@tsed/bullmq
Version:
BullMQ integration for Ts.ED
116 lines • 4.4 kB
JavaScript
import { constant, DIContext, inject, injectable, injectMany, injector, logger, ProviderType, runInContext } from "@tsed/di";
import { getComputedType } from "@tsed/schema";
import { v4 } from "uuid";
import { BullMQTypes } from "./constants/BullMQTypes.js";
import { BULLMQ } from "./constants/constants.js";
import { JobDispatcher } from "./dispatchers/index.js";
import { createQueueProvider } from "./utils/createQueueProvider.js";
import { createWorkerProvider } from "./utils/createWorkerProvider.js";
import { getFallbackJobToken, getJobToken } from "./utils/getJobToken.js";
import { mapQueueOptions } from "./utils/mapQueueOptions.js";
import { mapWorkerOptions } from "./utils/mapWorkerOptions.js";
export class BullMQModule {
constructor() {
this.dispatcher = inject(JobDispatcher);
this.onProcess = async (job) => {
const jobService = this.getJob(job.name, job.queueName);
if (!jobService) {
logger().warn({
event: "BULLMQ_JOB_NOT_FOUND",
message: `Job ${job.name} ${job.queueName} not found`
});
return;
}
const $ctx = new DIContext({
id: job.id || v4().split("-").join(""),
additionalProps: {
logType: "bullmq",
name: job.name,
queue: job.queueName,
attempt: job.attemptsMade
}
});
$ctx.set("BULLMQ_JOB", job);
try {
return await runInContext($ctx, () => {
$ctx.logger.info("Processing job");
try {
return jobService.handle(job.data, job);
}
finally {
$ctx.logger.info("Finished processing job");
}
});
}
catch (er) {
$ctx.logger.error({
event: "BULLMQ_JOB_ERROR",
message: er.message,
stack: er.stack
});
throw er;
}
finally {
await $ctx.destroy();
}
};
// build providers allow @Inject(queue) usage in JobController instance
if (this.isEnabled()) {
const queues = [...this.getUniqQueueNames()];
this.buildQueues(queues);
if (!this.isWorkerEnabled()) {
const workers = this.config.workerQueues?.length ? this.config.workerQueues : queues;
this.buildWorkers(workers);
}
}
}
get config() {
return constant("bullmq");
}
$onInit() {
if (this.isEnabled()) {
injectMany(BullMQTypes.CRON).map((job) => this.dispatcher.dispatch(getComputedType(job)));
}
}
async $onDestroy() {
if (!this.isEnabled()) {
return;
}
await Promise.all(injectMany(BullMQTypes.QUEUE).map((queue) => queue.close()));
await Promise.all(injectMany(BullMQTypes.WORKER).map((worker) => worker.close()));
}
isEnabled() {
return !!this.config;
}
isWorkerEnabled() {
return this.config.disableWorker;
}
buildQueues(queues) {
queues.forEach((queue) => {
const opts = mapQueueOptions(queue, this.config);
createQueueProvider(queue, opts);
});
}
buildWorkers(workers) {
workers.forEach((worker) => {
const opts = mapWorkerOptions(worker, this.config);
createWorkerProvider(worker, this.onProcess, opts);
});
}
/**
* Auto discover queue names from provider and merge it with queue names from global configuration.
* @private
*/
getUniqQueueNames() {
return new Set(injector()
.getProviders([BullMQTypes.JOB, BullMQTypes.CRON, BullMQTypes.FALLBACK_JOB])
.map((provider) => provider.store.get(BULLMQ)?.queue)
.concat(this.config.queues)
.filter(Boolean));
}
getJob(name, queueName) {
return inject(getJobToken(queueName, name)) || inject(getFallbackJobToken(queueName)) || inject(getFallbackJobToken());
}
}
injectable(BullMQModule).type(ProviderType.MODULE);
//# sourceMappingURL=BullMQModule.js.map