mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
feat: make jobs client type safe
This commit is contained in:
@ -2,36 +2,47 @@ import { z } from 'zod';
|
||||
|
||||
import type { Json } from './json';
|
||||
|
||||
export const ZTriggerJobOptionsSchema = z.object({
|
||||
export type SimpleTriggerJobOptions = {
|
||||
id?: string;
|
||||
name: string;
|
||||
payload: unknown;
|
||||
timestamp?: number;
|
||||
};
|
||||
|
||||
export const ZSimpleTriggerJobOptionsSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string(),
|
||||
payload: z.unknown().refine((x) => x !== undefined, { message: 'payload is required' }),
|
||||
timestamp: z.number().optional(),
|
||||
});
|
||||
|
||||
// The Omit is a temporary workaround for a "bug" in the zod library
|
||||
// @see: https://github.com/colinhacks/zod/issues/2966
|
||||
export type TriggerJobOptions = Omit<z.infer<typeof ZTriggerJobOptionsSchema>, 'payload'> & {
|
||||
payload: unknown;
|
||||
};
|
||||
// Map the array to create a union of objects we may accept
|
||||
export type TriggerJobOptions<Definitions extends Array<JobDefinition> = []> = {
|
||||
[K in keyof Definitions]: {
|
||||
id?: string;
|
||||
name: Definitions[K]['trigger']['name'];
|
||||
payload: Definitions[K]['trigger']['schema'] extends z.ZodType<infer Shape> ? Shape : unknown;
|
||||
timestamp?: number;
|
||||
};
|
||||
}[number];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type JobDefinition<T = any> = {
|
||||
export type JobDefinition<Name extends string = string, Schema = any> = {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
enabled?: boolean;
|
||||
trigger: {
|
||||
name: string;
|
||||
schema?: z.ZodSchema<T>;
|
||||
name: Name;
|
||||
schema?: z.ZodType<Schema>;
|
||||
};
|
||||
handler: (options: { payload: T; io: JobRunIO }) => Promise<Json | void>;
|
||||
handler: (options: { payload: Schema; io: JobRunIO }) => Promise<Json | void>;
|
||||
};
|
||||
|
||||
export interface JobRunIO {
|
||||
// stableRun<T extends Json | void>(cacheKey: string, callback: (io: JobRunIO) => T | Promise<T>): Promise<T>;
|
||||
runTask<T extends Json | void>(cacheKey: string, callback: () => Promise<T>): Promise<T>;
|
||||
triggerJob(cacheKey: string, options: TriggerJobOptions): Promise<unknown>;
|
||||
triggerJob(cacheKey: string, options: SimpleTriggerJobOptions): Promise<unknown>;
|
||||
wait(cacheKey: string, ms: number): Promise<void>;
|
||||
logger: {
|
||||
info(...args: unknown[]): void;
|
||||
@ -41,3 +52,7 @@ export interface JobRunIO {
|
||||
log(...args: unknown[]): void;
|
||||
};
|
||||
}
|
||||
|
||||
export const defineJob = <N extends string, T = unknown>(
|
||||
job: JobDefinition<N, T>,
|
||||
): JobDefinition<N, T> => job;
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import type { JobDefinition, TriggerJobOptions } from './_internal/job';
|
||||
import type { JobDefinition, SimpleTriggerJobOptions } from './_internal/job';
|
||||
|
||||
export abstract class BaseJobProvider {
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async triggerJob(_options: TriggerJobOptions): Promise<void> {
|
||||
public async triggerJob(_options: SimpleTriggerJobOptions): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public defineJob<T>(_job: JobDefinition<T>): void {
|
||||
public defineJob<N extends string, T>(_job: JobDefinition<N, T>): void {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
|
||||
@ -3,12 +3,12 @@ import type { BaseJobProvider as JobClientProvider } from './base';
|
||||
import { LocalJobProvider } from './local';
|
||||
import { TriggerJobProvider } from './trigger';
|
||||
|
||||
export class JobClient {
|
||||
export class JobClient<T extends Array<JobDefinition> = []> {
|
||||
private static _instance: JobClient;
|
||||
|
||||
private _provider: JobClientProvider;
|
||||
|
||||
private constructor() {
|
||||
public constructor(definitions: T) {
|
||||
if (process.env.NEXT_PRIVATE_JOBS_PROVIDER === 'trigger') {
|
||||
this._provider = TriggerJobProvider.getInstance();
|
||||
|
||||
@ -16,23 +16,27 @@ export class JobClient {
|
||||
}
|
||||
|
||||
this._provider = LocalJobProvider.getInstance();
|
||||
|
||||
definitions.forEach((definition) => {
|
||||
this._provider.defineJob(definition);
|
||||
});
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this._instance) {
|
||||
this._instance = new JobClient();
|
||||
}
|
||||
// public static getInstance() {
|
||||
// if (!this._instance) {
|
||||
// this._instance = new JobClient();
|
||||
// }
|
||||
|
||||
return this._instance;
|
||||
}
|
||||
// return this._instance;
|
||||
// }
|
||||
|
||||
public async triggerJob(options: TriggerJobOptions) {
|
||||
public async triggerJob(options: TriggerJobOptions<T>) {
|
||||
return this._provider.triggerJob(options);
|
||||
}
|
||||
|
||||
public defineJob<T>(job: JobDefinition<T>) {
|
||||
return this._provider.defineJob(job);
|
||||
}
|
||||
// public defineJob<N extends string, T>(job: JobDefinition<N, T>) {
|
||||
// return this._provider.defineJob(job);
|
||||
// }
|
||||
|
||||
public getApiHandler() {
|
||||
return this._provider.getApiHandler();
|
||||
|
||||
@ -12,8 +12,8 @@ import { verify } from '../../server-only/crypto/verify';
|
||||
import {
|
||||
type JobDefinition,
|
||||
type JobRunIO,
|
||||
type TriggerJobOptions,
|
||||
ZTriggerJobOptionsSchema,
|
||||
type SimpleTriggerJobOptions,
|
||||
ZSimpleTriggerJobOptionsSchema,
|
||||
} from './_internal/job';
|
||||
import type { Json } from './_internal/json';
|
||||
import { BaseJobProvider } from './base';
|
||||
@ -35,14 +35,14 @@ export class LocalJobProvider extends BaseJobProvider {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
public defineJob<T>(definition: JobDefinition<T>) {
|
||||
public defineJob<N extends string, T>(definition: JobDefinition<N, T>) {
|
||||
this._jobDefinitions[definition.id] = {
|
||||
...definition,
|
||||
enabled: definition.enabled ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
public async triggerJob(options: TriggerJobOptions) {
|
||||
public async triggerJob(options: SimpleTriggerJobOptions) {
|
||||
console.log({ jobDefinitions: this._jobDefinitions });
|
||||
|
||||
const eligibleJobs = Object.values(this._jobDefinitions).filter(
|
||||
@ -87,9 +87,9 @@ export class LocalJobProvider extends BaseJobProvider {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const options = await json(req)
|
||||
.then(async (data) => ZTriggerJobOptionsSchema.parseAsync(data))
|
||||
.then(async (data) => ZSimpleTriggerJobOptionsSchema.parseAsync(data))
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
.then((data) => data as TriggerJobOptions)
|
||||
.then((data) => data as SimpleTriggerJobOptions)
|
||||
.catch(() => null);
|
||||
|
||||
if (!options) {
|
||||
@ -224,7 +224,7 @@ export class LocalJobProvider extends BaseJobProvider {
|
||||
private async submitJobToEndpoint(options: {
|
||||
jobId: string;
|
||||
jobDefinitionId: string;
|
||||
data: TriggerJobOptions;
|
||||
data: SimpleTriggerJobOptions;
|
||||
isRetry?: boolean;
|
||||
}) {
|
||||
const { jobId, jobDefinitionId, data, isRetry } = options;
|
||||
|
||||
@ -4,7 +4,7 @@ import { createPagesRoute } from '@trigger.dev/nextjs';
|
||||
import type { IO } from '@trigger.dev/sdk';
|
||||
import { TriggerClient, eventTrigger } from '@trigger.dev/sdk';
|
||||
|
||||
import type { JobDefinition, JobRunIO, TriggerJobOptions } from './_internal/job';
|
||||
import type { JobDefinition, JobRunIO, SimpleTriggerJobOptions } from './_internal/job';
|
||||
import { BaseJobProvider } from './base';
|
||||
|
||||
export class TriggerJobProvider extends BaseJobProvider {
|
||||
@ -32,7 +32,7 @@ export class TriggerJobProvider extends BaseJobProvider {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
public defineJob<T>(job: JobDefinition<T>): void {
|
||||
public defineJob<N extends string, T>(job: JobDefinition<N, T>): void {
|
||||
this._client.defineJob({
|
||||
id: job.id,
|
||||
name: job.name,
|
||||
@ -45,12 +45,12 @@ export class TriggerJobProvider extends BaseJobProvider {
|
||||
});
|
||||
}
|
||||
|
||||
public async triggerJob(_options: TriggerJobOptions): Promise<void> {
|
||||
public async triggerJob(options: SimpleTriggerJobOptions): Promise<void> {
|
||||
await this._client.sendEvent({
|
||||
id: _options.id,
|
||||
name: _options.name,
|
||||
payload: _options.payload,
|
||||
timestamp: _options.timestamp ? new Date(_options.timestamp) : undefined,
|
||||
id: options.id,
|
||||
name: options.name,
|
||||
payload: options.payload,
|
||||
timestamp: options.timestamp ? new Date(options.timestamp) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user