Files
calendar/packages/lib/tasker/Tasker.ts
T
sean-brydonGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
442779d08d feat: monthly-proration-tasker (#26870)
* feat: monthly-proration-taskerh

* remove cronjob from tasker implementation

* feat: add DI for MonthlyProrationService and TriggerDevLoggerServiceModule

- Create TriggerDevLoggerServiceModule for DI injection of TriggerDevLogger
- Add tokens for TriggerDevLogger in shared.tokens.ts
- Create MonthlyProrationService DI module and container
- Update processMonthlyProrationBatch.ts to use DI container
- Use redactError in Tasker.ts to avoid logging sensitive information

Co-Authored-By: sean@cal.com <Sean@brydon.io>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-16 20:58:51 +00:00

96 lines
3.2 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import process from "node:process";
import { configure } from "@trigger.dev/sdk";
import { ENABLE_ASYNC_TASKER } from "../constants";
import { redactError } from "../redactError";
import type { ILogger } from "./types";
const isAsyncTaskerEnabled =
ENABLE_ASYNC_TASKER && process.env.TRIGGER_SECRET_KEY && process.env.TRIGGER_API_URL;
export abstract class Tasker<T> {
protected readonly asyncTasker: T;
protected readonly syncTasker: T;
protected readonly logger: ILogger;
constructor(dependencies: { asyncTasker: T; syncTasker: T; logger: ILogger }) {
this.logger = dependencies.logger;
if (!isAsyncTaskerEnabled) {
if (ENABLE_ASYNC_TASKER && (!process.env.TRIGGER_SECRET_KEY || !process.env.TRIGGER_API_URL)) {
this.logger.info(
"Missing env variables TRIGGER_SECRET_KEY or TRIGGER_API_URL, falling back to Sync tasker."
);
}
}
if (isAsyncTaskerEnabled) {
configure({
accessToken: process.env.TRIGGER_SECRET_KEY,
baseURL: process.env.TRIGGER_API_URL,
});
}
this.asyncTasker = isAsyncTaskerEnabled ? dependencies.asyncTasker : dependencies.syncTasker;
this.syncTasker = dependencies.syncTasker;
}
public async dispatch<K extends keyof T>(
taskName: K,
...args: T[K] extends (...args: any[]) => any ? Parameters<T[K]> : never
): Promise<T[K] extends (...args: any[]) => any ? Awaited<ReturnType<T[K]>> : never> {
this.logger.info(`Safely Dispatching task '${String(taskName)}'`, { args });
return this._safeDispatch(taskName, ...args);
}
private async _safeDispatch<K extends keyof T>(
taskName: K,
...args: T[K] extends (...args: any[]) => any ? Parameters<T[K]> : never
): Promise<T[K] extends (...args: any[]) => any ? Awaited<ReturnType<T[K]>> : never> {
try {
this.logger.info(
`${isAsyncTaskerEnabled ? "AsyncTasker" : "SyncTasker"} '${String(taskName)}' dispatched.`
);
const method = this.asyncTasker[taskName] as (...args: any[]) => any;
return await method.apply(this.asyncTasker, args);
} catch (err) {
const taskerLabel = isAsyncTaskerEnabled ? "AsyncTasker" : "SyncTasker";
const baseUrlInfo = isAsyncTaskerEnabled
? ` (baseURL: ${process.env.TRIGGER_API_URL ?? "unknown"})`
: "";
this.logger.error(
`${taskerLabel} failed for '${String(taskName)}'.${baseUrlInfo}`,
this.getErrorDetails(err)
);
if (this.asyncTasker === this.syncTasker) {
throw err;
}
this.logger.warn(`Trying again with SyncTasker for '${String(taskName)}'.`);
try {
const fallbackMethod = this.syncTasker[taskName] as (...args: any[]) => any;
return await fallbackMethod.apply(this.syncTasker, args);
} catch (err) {
this.logger.error(`SyncTasker failed for '${String(taskName)}'.`, this.getErrorDetails(err));
throw err;
}
}
}
private getErrorDetails(err: unknown) {
const redactedError = redactError(err);
if (redactedError instanceof Error) {
return {
name: redactedError.name,
message: redactedError.message,
stack: redactedError.stack,
};
}
return { message: String(redactedError) };
}
}