### Configure Tenant Filter for Event Processing Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md Example of configuring a tenant filter callback for event processing using JavaScript. ```javascript const { config } = require("@cap-js-community/event-queue"); // Define a callback function to determine if a tenant's events should be processed config.tenantIdFilterEventProcessing = async (tenantId) => { // Replace with your custom logic to decide whether to process the tenant return await checkIfTenantShouldBeProcessedOnInstance(tenantId); }; ``` -------------------------------- ### Start Server Source: https://github.com/cap-js-community/event-queue/blob/main/example-cap-server/README.md Command to start the CAP server. ```bash npm start ``` -------------------------------- ### Subscribe Tenants Source: https://github.com/cap-js-community/event-queue/blob/main/example-cap-server/README.md Command to subscribe tenants to the CAP service. ```bash npm run subscribeTenants ``` -------------------------------- ### Configure Redis Connection (Option 2) Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md Alternative JSON configuration to override Redis connection options for hybrid testing. ```json { "cds": { "requires": { "redis-eventQueue": { "[hybrid]": { "options": { "socket": { "host": "localhost", "rejectUnauthorized": false } } } } } } } ``` -------------------------------- ### Configure Redis Connection (Option 1) Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md JSON configuration to override Redis connection options to connect to localhost for hybrid testing. ```json { "cds": { "eventQueue": { "[hybrid]": { "redisOptions": { "socket": { "host": "localhost", "rejectUnauthorized": false } } } } } } ``` -------------------------------- ### Extract Redis Host and Port Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md Command to extract Redis host and port from service binding for local testing. ```bash cds bind --exec -- node -e 'console.log(process.env.VCAP_SERVICES)' ``` -------------------------------- ### Bind Redis Service Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md Command to bind a Redis service to the app for event-queue capabilities. ```bash cds bind --to ``` -------------------------------- ### Local Development Commands Source: https://github.com/cap-js-community/event-queue/blob/main/docs/README.md Commands to install dependencies and serve the documentation locally. ```bash bundle install bundle exec jekyll serve ``` -------------------------------- ### Minimal Configuration for CAP Queue Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md This JSON configuration enables the event-queue to be used as a CAP Queue, providing a robust foundation with default settings for features like periodic events and load balancing. ```json { "cds": { "eventQueue": { "useAsCAPQueue": true } } } ``` -------------------------------- ### JSON Configuration Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example of event queue configuration using JSON format within `cds.env`. ```json { "cds": { "eventQueue": { "events": { "Notification/Email": { "impl": "./srv/util/mail-service/EventQueueNotificationProcessor", "load": 1, "parallelEventProcessing": 5 } }, "periodicEvents": { "HealthCheck/DB": { "impl": "./test/asset/EventQueueHealthCheckDb", "load": 1, "transactionMode": "alwaysRollback", "interval": 30 } } } } } ``` -------------------------------- ### Event Chaining Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Example demonstrating how to register successor handlers for different event outcomes (`#succeeded`, `#failed`, `#done`) and generic fallbacks. ```javascript class MyService extends cds.Service { async init() { await super.init(); // Primary handler this.on("orderCreated", async (req) => { // ... business logic return EventProcessingStatus.Done; }); // Runs on success — event-specific this.on("orderCreated/#succeeded", async (req) => { // req.eventQueue.triggerEvent is available here (see below) }); // Runs on failure — event-specific this.on("orderCreated/#failed", async (req) => { // req.data.error contains the serialised error message }); // Runs unconditionally — event-specific this.on("orderCreated/#done", async (req) => { // always runs; req.data.error is set when the parent failed }); // Generic fallbacks — run for any event without a dedicated handler this.on("#succeeded", async (req) => { /* ... */ }); this.on("#failed", async (req) => { /* ... */ }); this.on("#done", async (req) => { /* ... */ }); } } ``` -------------------------------- ### Changing Initialization Configuration at Runtime Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md JavaScript example demonstrating how to change the 'runInterval' configuration parameter at runtime. ```javascript const { config } = require("@cap-js-community/event-queue"); config.runInterval = 5 * 60 * 1000; // 5 minutes ``` -------------------------------- ### YAML Ad-Hoc Event Configuration Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example of ad-hoc event configuration using YAML format. ```yaml events: - type: Notification subType: Email impl: ./srv/util/mail-service/EventQueueNotificationProcessor load: 1 parallelEventProcessing: 5 - type: Attachment subType: Compress impl: ./srv/common/process/EventQueueClosingTaskSync load: 3 parallelEventProcessing: 2 selectMaxChunkSize: 5 transactionMode: alwaysRollback retryAttempts: 1 ``` -------------------------------- ### YAML Periodic Event Configuration Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example of periodic event configuration using YAML format, including interval settings. ```yaml periodicEvents: - type: HealthCheck subType: DB impl: ./test/asset/EventQueueHealthCheckDb load: 1 transactionMode: alwaysRollback interval: 30 ``` -------------------------------- ### Unit Test Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/unit-testing/index.md An example of a unit test for processing events in the event queue. ```javascript const cds = require("@sap/cds"); const { processEventQueue } = require("@cap-js-community/event-queue"); it("should process an queued service", async () => { // Arrange connect to service and queue it const service = await cds.connect.to("NotificationService"); const queuedService = cds.queued(service); await queuedService.send("sendFiori", { to: "to", subject: "subject", body: "body", }); // Act - process the event await processEventQueue({}, "CAP_OUTBOX", "NotificationService"); // parameters are cds context, type, and subType // Assert tx = cds.tx({}); const publishedEvents = await tx.run(SELECT.from("sap.eventqueue.Event")); expect(publishedEvent[0]).toMatchObject({ type: "CAP_OUTBOX", subType: "NotificationService", status: EventProcessingStatus.Done, }); expect(service).sendFioriActionCalled(); // this is pseudo code }); ``` -------------------------------- ### beforeProcessingEvents Function Example Source: https://github.com/cap-js-community/event-queue/blob/main/docs/implement-event/index.md This example demonstrates the usage of the `beforeProcessingEvents` function to load data in bulk required for processing all selected events. It's invoked once with all events. ```javascript "use strict"; const { EventQueueProcessorBase } = require("@cap-js-community/event-queue"); class EventQueueMinimalistic extends EventQueueProcessorBase { constructor(context, eventType, eventSubType, config) { super(context, eventType, eventSubType, config); } async beforeProcessingEvents() { this.__cache = await loadCache(this.eventProcessingMap); } } module.exports = EventQueueMinimalistic; ``` -------------------------------- ### Open SSH Tunnel Source: https://github.com/cap-js-community/event-queue/blob/main/docs/setup/index.md Command to open an SSH tunnel to a deployed CF app for connecting to a remote Redis instance. ```bash cf ssh -L :: ``` -------------------------------- ### Blocking Events Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example of how to block specific events from executing for all or certain tenants. ```javascript const { config } = require("@cap-js-community/event-queue"); config.isEventBlockedCb = async (type, subType, isPeriodicEvent, tenant) => { // Perform custom check and return true or false }; ``` -------------------------------- ### Changing Event Configuration at Runtime Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md JavaScript example demonstrating how to retrieve and modify the configuration for a specific event ('HealthCheck', 'DB', 'default'). ```javascript const { config } = require("@cap-js-community/event-queue"); const eventConfig = config.getEventConfig("HealthCheck", "DB", "default"); eventConfig.load = 5; ``` -------------------------------- ### YAML Event Configuration with Retries Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example demonstrating how to implement retries for both ad-hoc and periodic events using YAML. ```yaml events: - type: MasterData subType: Sync impl: ./test/asset/EventQueuePeriodicWithRetries load: 34 # two MD Syncs in parallel - but leave room for smaller other events retryAttempts: 5 periodicEvents: - type: MasterData subType: Sync impl: ./test/asset/EventQueuePeriodicWithRetries load: 1 cron: 0 0 * * * # Runs at midnight every day ``` -------------------------------- ### Service or Event Level Namespace Configuration Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Example configuration defining a namespace for a service ('StandardService' to 'namespaceA') and a specific event ('timeBucketAction' to 'namespaceC'). ```json { "cds": { "requires": { "StandardService": { "impl": "...", "queued": { "kind": "persistent-queue", "namespace": "namespaceA", "events": { "timeBucketAction": { "namespace": "namespaceC" } } } } } } } ``` -------------------------------- ### Unsubscribe Handler Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Code example for registering callbacks to react to tenant unsubscribe events across all application instances. ```javascript const { config } = require("@cap-js-community/event-queue"); config.attachUnsubscribeHandler(async (tenantId) => { try { cds.log("server").info("received unsubscribe event via event-queue", { tenantId }); await cds.db.disconnect(tenantId); } catch (err) { logger.error("disconnect db failed!", { tenantId }, err); } }); ``` -------------------------------- ### Publishing an Ad-hoc Event Source: https://github.com/cap-js-community/event-queue/blob/main/docs/publish-event/index.md Example of how to publish an ad-hoc event using the `publishEvent` function. ```javascript "use strict"; const { publishEvent } = require("@cap-js-community/event-queue"); await publishEvent(tx, { type: "Notifications", subType: "Tasks", payload: JSON.stringify({ recipients: ["alice@wonder.land"], }), }); ``` -------------------------------- ### Publishing a Delayed Event Source: https://github.com/cap-js-community/event-queue/blob/main/docs/publish-event/index.md Example of how to publish a delayed event by specifying a future `startAfter` timestamp. ```javascript await publishEvent(tx, { type: "Notifications", subType: "Tasks", startAfter: new Date("2023-12-31T12:00:00"), // Future timestamp payload: JSON.stringify({ recipients: ["alice@wonder.land"], }), }); ``` -------------------------------- ### Application Service with cds.ApplicationService - Manual Queuing Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Example demonstrating how to manually queue an action for an ApplicationService (e.g., odata-v4) by connecting to the service and using cds.queued. ```javascript const service = await cds.connect.to("task-service"); const queuedService = cds.queued(service, { kind: "persistent-queue", transactionMode: "alwaysRollback", }); await queuedService.send("process", { ID: 1, comment: "done", }); ``` -------------------------------- ### Action-specific clustering for sendMail action Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Example demonstrating how to define specific clustering logic for a particular action ('sendMail') within the event-queue. ```javascript this.on("eventQueueCluster.sendMail", (req) => { return req.eventQueue.clusterByDataProperty("to", (clusterKey, clusterEntries) => { // clusterKey is the value of the property "req.data.to" // clusterEntries is an array of all entries with the same "req.data.to" value return { recipients: clusterKey, mails: clusterEntries }; }); }); ``` -------------------------------- ### Delaying queued service calls Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md This example shows how to delay the processing of a queued service call by including the 'x-eventqueue-startAfter' header attribute with a Date object representing 4 minutes from the current time. ```javascript const queuedService = await cds.connect.to("task-service"); await queuedService.send( "process", { ID: 1, comment: "done", }, // delay the processing 4 minutes { "x-eventqueue-startAfter": new Date(Date.now() + 4 * 60 * 1000).toISOString() } ); ``` -------------------------------- ### Using Last Successful Run Timestamp for Periodic Events Source: https://github.com/cap-js-community/event-queue/blob/main/docs/implement-event/index.md This example shows how to use the `getLastSuccessfulRunTimestamp` function to retrieve the timestamp of the last successful run for a periodic event, which can be used for delta processing or choosing the next chunk. ```javascript "use strict"; const { EventQueueProcessorBase } = require("@cap-js-community/event-queue"); class EventQueueMinimalistic extends EventQueueProcessorBase { constructor(context, eventType, eventSubType, config) { super(context, eventType, eventSubType, config); } async processPeriodicEvent(processContext, key, eventEntry) { try { const tsLastRun = await this.getLastSuccessfulRunTimestamp(); // 2023-12-07T09:15:44.237 await doHeavyProcessing(queueEntries, payload); } catch { this.logger.error("Error during processing periodic event!", err); } } } module.exports = EventQueueMinimalistic; ``` -------------------------------- ### Accessing Trigger Event Context Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Example demonstrating how to access `req.eventQueue.triggerEvent` in a successor handler to get context from the parent event, including `triggerEventResult` and the parent `ID`. ```javascript this.on("orderCreated/#succeeded", async (req) => { const { triggerEventResult, ID } = req.eventQueue.triggerEvent; // triggerEventResult is exactly what the parent handler returned console.log(triggerEventResult); // → { status: 2, nextData: { orderId: "..." } } // ID is the UUID of the parent queue entry console.log(ID); // → "3f2e1a..." }); ``` -------------------------------- ### Enabling Queueing via cds.env.requires in package.json Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md This JSON configuration shows how to enable queuing for a service by specifying its implementation path and queuing parameters in package.json. ```json { "cds": { "requires": { "task-service": { "impl": "./srv/PATH_SERVICE/taskService.js", "queued": { "kind": "persistent-queue", "transactionMode": "alwaysRollback", "maxAttempts": 5, "parallelEventProcessing": 5 } } } } } ``` -------------------------------- ### Applying the Apache License Source: https://github.com/cap-js-community/event-queue/blob/main/LICENSES/Apache-2.0.txt This snippet shows how to apply the Apache License to your work by including a boilerplate notice. ```text Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. ``` -------------------------------- ### Enable event-queue as CAP queue Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md This JSON configuration snippet shows how to enable the event-queue to act as a CAP queue by setting the `useAsCAPQueue` initialization parameter. ```json { "cds": { "eventQueue": { "useAsCAPQueue": true } } } ``` -------------------------------- ### Logging-Service Configuration Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Configuration for a Logging-Service to process events from multiple namespaces ('orders', 'inventory', 'default'). ```json { "cds": { "eventQueue": { "processingNamespaces": ["orders", "inventory", "default"] } } } ``` -------------------------------- ### Clustering multiple queue events by data property Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Example of implementing eventQueueCluster in a CAP service to cluster events based on a data property, such as 'to' for emails. ```javascript this.on("eventQueueCluster", (req) => { return req.eventQueue.clusterByDataProperty("to", (clusterKey, clusterEntries) => { // clusterKey is the value of the property "req.data.to" // clusterEntries is an array of all entries with the same "req.data.to" value return { recipients: clusterKey, mails: clusterEntries }; }); }); ``` -------------------------------- ### Accessing Event-Queue properties within a service handler Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md This example demonstrates how to access various event-queue properties such as processor, key, queueEntries, and payload within a service handler by destructuring them from req.eventQueue. ```javascript class TaskService extends cds.Service { async init() { await super.init(); this.on("send", (req) => { const { processor, queueEntries, payload, key } = req.eventQueue; }); } } ``` -------------------------------- ### Configure a queued service with event-queue parameters Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md This JSON configuration demonstrates how to queue a service, such as `@cap-js/audit-logging`, using the event-queue. It specifies parameters like `kind`, `transactionMode`, `maxAttempts`, `checkForNextChunk`, and `parallelEventProcessing` within the `requires` section. ```json { "cds": { "requires": { "audit-log": { "queued": { "kind": "persistent-queue", "transactionMode": "alwaysRollback", "maxAttempts": 5, "checkForNextChunk": false, "parallelEventProcessing": 5 } } } } } ``` -------------------------------- ### Overriding Transaction Mode with setShouldRollbackTransaction Source: https://github.com/cap-js-community/event-queue/blob/main/docs/transaction-handling/index.md Example demonstrating how to use `setShouldRollbackTransaction` to force a rollback, even when the default behavior would be to commit (e.g., in `alwaysCommit` mode or when `processEvent` returns 'Done' in `isolated` mode). ```javascript class EventQueueMinimalistic extends EventQueueProcessorBase { constructor(context, eventType, eventSubType, config) { super(context, eventType, eventSubType, config); } async processEvent(processContext, key, eventEntries, payload) { let eventStatus = EventProcessingStatus.Done; try { await sendNotification(queueEntries, payload); } catch { eventStatus = EventProcessingStatus.Error; } this.setShouldRollbackTransaction(key); // leads to always rollback the transaction return eventEntries.map((eventEntry) => [eventEntry.ID, eventStatus]); } } ``` -------------------------------- ### Service-A Configuration Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Configuration for Service-A (Orders) to publish and process events within the 'orders' namespace. ```json { "cds": { "eventQueue": { "namespace": "orders", "processingNamespaces": ["orders"] } } } ``` -------------------------------- ### Testing the Processing of Events Source: https://github.com/cap-js-community/event-queue/blob/main/docs/unit-testing/index.md This JavaScript code demonstrates how to test the processing of events. It first arranges for an event to be published and then acts by processing the event using `processEventQueue`. ```javascript const cds = require("@sap/cds"); const { processEventQueue } = require("@cap-js-community/event-queue"); it("should process an event", async () => { // Arrange - triggerProcessA publishes an event for process B await triggerProcessA(); // Act - process the event await processEventQueue({}, "task", "processB"); // parameters are cds context, type, and subType // Assert const tx = cds.tx({}); const publishedEvents = await tx.run(SELECT.from("sap.eventqueue.Event")); await tx.rollback(); expect(publishedEvents).toHaveLength(1); expect(publishedEvent[0]).toMatchObject({ type: "task", subType: "processB", status: EventProcessingStatus.Done, }); }); ``` -------------------------------- ### Minimal implementation for ad-hoc events Source: https://github.com/cap-js-community/event-queue/blob/main/docs/implement-event/index.md This code snippet demonstrates the most basic implementation of an event processor for ad-hoc events. It extends `EventQueueProcessorBase` and overrides the `processEvent` method to handle incoming events. The `processEvent` method processes the provided `queueEntries` and returns an array of tuples, each containing the event entry ID and its processing status (either `Done` or `Error`). ```javascript "use strict"; const { EventQueueProcessorBase, EventProcessingStatus } = require("@cap-js-community/event-queue"); class EventQueueMinimalistic extends EventQueueProcessorBase { constructor(context, eventType, eventSubType, config) { super(context, eventType, eventSubType, config); } async processEvent(processContext, key, queueEntries, payload) { let eventStatus = EventProcessingStatus.Done; try { await doHeavyProcessing(queueEntries, payload); } catch { eventStatus = EventProcessingStatus.Error; } return queueEntries.map((queueEntry) => [queueEntry.ID, eventStatus]); } } module.exports = EventQueueMinimalistic; ``` -------------------------------- ### Service-B Configuration Source: https://github.com/cap-js-community/event-queue/blob/main/docs/configure-event/index.md Configuration for Service-B (Inventory) to publish and process events within the 'inventory' namespace. ```json { "cds": { "eventQueue": { "namespace": "inventory", "processingNamespaces": ["inventory"] } } } ``` -------------------------------- ### Passing Data to Successor Source: https://github.com/cap-js-community/event-queue/blob/main/docs/use-as-cap-outbox/index.md Demonstrates returning a `nextData` property from a primary handler to forward arbitrary data to the successor's `req.data`. ```javascript this.on("orderCreated", async (req) => { const orderId = await createOrder(req.data); return { status: EventProcessingStatus.Done, nextData: { orderId }, // available as req.data.orderId in the successor }; }); ```