@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
JavaScript
;
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,
},
};