UNPKG

@cap-js-community/event-queue

Version:

An event queue that enables secure transactional processing of asynchronous and periodic events, featuring instant event processing with Redis Pub/Sub and load distribution across all application instances.

119 lines (107 loc) 3.42 kB
"use strict"; const cds = require("@sap/cds"); const redis = require("../shared/redis"); const config = require("../config"); const runnerHelper = require("../runner/runnerHelper"); const common = require("../shared/common"); const { TenantIdCheckTypes } = require("../constants"); const EVENT_MESSAGE_CHANNEL = "EVENT_QUEUE_MESSAGE_CHANNEL"; const COMPONENT_NAME = "/eventQueue/redisSub"; const initEventQueueRedisSubscribe = () => { if (initEventQueueRedisSubscribe._initDone || !config.redisEnabled) { return; } initEventQueueRedisSubscribe._initDone = true; config.processingNamespaces.forEach((namespace) => { redis.subscribeRedisChannel([namespace, EVENT_MESSAGE_CHANNEL].join("##"), _messageHandlerProcessEvents); }); }; const _messageHandlerProcessEvents = async (messageData) => { const logger = cds.log(COMPONENT_NAME); try { const { lockId, tenantId, type, subType, namespace } = JSON.parse(messageData); const tenantShouldBeProcessed = await common.isTenantIdValidCb(TenantIdCheckTypes.eventProcessing, tenantId); if (!tenantShouldBeProcessed) { return; } logger.debug("received redis event", { tenantId, type, subType, }); if (!config.isEventQueueActive) { cds.log(COMPONENT_NAME).info("Skipping processing because runner is deactivated!", { type, subType, }); return; } const { srvName, actionName } = config.normalizeSubType(type, subType); if (!config.getEventConfig(type, subType, namespace)) { if (config.isCapOutboxEvent(type)) { try { const service = await cds.connect.to(srvName); if (!service || actionName) { logger.warn("could not find CAP Service configuration to process event!", { type, subType, namespace, }); return; } config.addCAPServiceWithoutEnvConfig(subType, service); } catch (err) { logger.warn("could not connect to outboxed service", err, { type, subType, namespace, }); return; } } else { logger.warn("cannot find configuration for published event. Event won't be processed", { type, subType, }); return; } } if ( !( config.getEventConfig(type, subType, namespace) && config.shouldBeProcessedInThisApplication(type, subType, namespace) ) ) { logger.debug("event is not configured to be processed on this app-name", { tenantId, type, subType, }); return; } const user = await cds.tx({ tenant: tenantId }, async () => { const authInfo = await common.getAuthContext(tenantId); return new cds.User.Privileged({ id: config.userId, authInfo, tokenInfo: authInfo?.token }); }); const tenantContext = { tenant: tenantId, user, }; return await cds.tx(tenantContext, async ({ context }) => { return await runnerHelper.runEventCombinationForTenant(context, type, subType, namespace, { lockId, shouldTrace: true, }); }); } catch (err) { logger.error("could not parse event information", { messageData, }); } }; module.exports = { initEventQueueRedisSubscribe, __: { _messageHandlerProcessEvents, }, };