diff --git a/.changeset/brave-parents-roll.md b/.changeset/brave-parents-roll.md new file mode 100644 index 00000000000..b89668ce1ff --- /dev/null +++ b/.changeset/brave-parents-roll.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Multiple eventname support in eventDispatcher diff --git a/apps/webapp/app/services/events/deliverEvent.server.ts b/apps/webapp/app/services/events/deliverEvent.server.ts index f3745a49b04..04df77fe5a2 100644 --- a/apps/webapp/app/services/events/deliverEvent.server.ts +++ b/apps/webapp/app/services/events/deliverEvent.server.ts @@ -33,7 +33,9 @@ export class DeliverEventService { const possibleEventDispatchers = await tx.eventDispatcher.findMany({ where: { environmentId: eventRecord.environmentId, - event: eventRecord.name, + event: { + has: eventRecord.name, + }, source: eventRecord.source, enabled: true, manual: false, diff --git a/apps/webapp/app/services/jobs/registerJob.server.ts b/apps/webapp/app/services/jobs/registerJob.server.ts index 2e6d2df3ce8..36d3c207f37 100644 --- a/apps/webapp/app/services/jobs/registerJob.server.ts +++ b/apps/webapp/app/services/jobs/registerJob.server.ts @@ -404,7 +404,10 @@ export class RegisterJobService { }, }, create: { - event: trigger.rule.event, + event: + typeof trigger.rule.event === "string" + ? [trigger.rule.event] + : trigger.rule.event, source: trigger.rule.source, payloadFilter: trigger.rule.payload, contextFilter: trigger.rule.context, @@ -417,7 +420,10 @@ export class RegisterJobService { dispatchableId: job.id, }, update: { - event: trigger.rule.event, + event: + typeof trigger.rule.event === "string" + ? [trigger.rule.event] + : trigger.rule.event, source: trigger.rule.source, payloadFilter: trigger.rule.payload, contextFilter: trigger.rule.context, diff --git a/apps/webapp/app/services/jobs/testJob.server.ts b/apps/webapp/app/services/jobs/testJob.server.ts index 183f04e2893..501ab8ccf54 100644 --- a/apps/webapp/app/services/jobs/testJob.server.ts +++ b/apps/webapp/app/services/jobs/testJob.server.ts @@ -18,64 +18,73 @@ export class TestJobService { versionId: string; payload: any; }) { - return await $transaction(this.#prismaClient, async (tx) => { - //get the environment with orgId and projectId - const environment = await tx.runtimeEnvironment.findUniqueOrThrow({ - include: { - organization: true, - project: true, - }, - where: { - id: environmentId, - }, - }); + return await $transaction( + this.#prismaClient, + async (tx) => { + //get the environment with orgId and projectId + const environment = await tx.runtimeEnvironment.findUniqueOrThrow({ + include: { + organization: true, + project: true, + }, + where: { + id: environmentId, + }, + }); - const version = await tx.jobVersion.findUniqueOrThrow({ - include: { - job: true, - }, - where: { - id: versionId, - }, - }); + const version = await tx.jobVersion.findUniqueOrThrow({ + include: { + job: true, + }, + where: { + id: versionId, + }, + }); - const event = EventSpecificationSchema.parse(version.eventSpecification); + const event = EventSpecificationSchema.parse( + version.eventSpecification + ); + const eventName = Array.isArray(event.name) + ? event.name[0] + : event.name; - const eventLog = await this.#prismaClient.eventRecord.create({ - data: { - organization: { - connect: { - id: environment.organizationId, + const eventLog = await this.#prismaClient.eventRecord.create({ + data: { + organization: { + connect: { + id: environment.organizationId, + }, }, - }, - project: { - connect: { - id: environment.projectId, + project: { + connect: { + id: environment.projectId, + }, }, - }, - environment: { - connect: { - id: environment.id, + environment: { + connect: { + id: environment.id, + }, }, + eventId: `test:${eventName}:${new Date().getTime()}`, + name: eventName, + timestamp: new Date(), + payload: payload ?? {}, + context: {}, + source: event.source ?? "trigger.dev", + isTest: true, }, - eventId: `test:${event.name}:${new Date().getTime()}`, - name: event.name, - timestamp: new Date(), - payload: payload ?? {}, - context: {}, - source: event.source ?? "trigger.dev", - isTest: true, - }, - }); + }); - const createRunService = new CreateRunService(tx); + const createRunService = new CreateRunService(tx); - return await createRunService.call({ - environment, - eventId: eventLog.id, - job: version.job, - version, - }); - }, { timeout: 10000 }); + return await createRunService.call({ + environment, + eventId: eventLog.id, + job: version.job, + version, + }); + }, + { timeout: 10000 } + ); } } diff --git a/examples/nextjs-example/src/jobs/events.ts b/examples/nextjs-example/src/jobs/events.ts index 136409ee0ca..4ef4071864e 100644 --- a/examples/nextjs-example/src/jobs/events.ts +++ b/examples/nextjs-example/src/jobs/events.ts @@ -44,3 +44,17 @@ client.defineJob({ } }, }); + +client.defineJob({ + id: "test-multiple-events", + name: "Test Multiple Events", + version: "0.0.1", + logLevel: "debug", + trigger: eventTrigger({ + name: ["test.event.1", "test.event.2"], + examples: [{ id: "test", name: "Test", payload: { name: "test" } }], + }), + run: async (payload, io, ctx) => { + await io.logger.log(`Triggered by the ${ctx.event.name} event`, { ctx }); + }, +}); diff --git a/packages/database/prisma/migrations/20230724074140_changed/migration.sql b/packages/database/prisma/migrations/20230724074140_changed/migration.sql new file mode 100644 index 00000000000..9b1129cae16 --- /dev/null +++ b/packages/database/prisma/migrations/20230724074140_changed/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - The `event` column on the `EventDispatcher` table would be dropped and recreated. This will lead to data loss if there is data in the column. + +*/ +-- AlterTable +-- Step 1: Create temporary column +ALTER TABLE "EventDispatcher" +ADD COLUMN temp_event TEXT[]; + +-- Step 2: Update temporary column +UPDATE "EventDispatcher" +SET temp_event = ARRAY[event]; + +-- Step 3: Drop original column +ALTER TABLE "EventDispatcher" +DROP COLUMN "event"; + +-- Step 4: Rename temporary column +ALTER TABLE "EventDispatcher" +RENAME COLUMN temp_event TO "event"; + diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 117d81fb41a..066260c4472 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -583,12 +583,12 @@ enum DynamicTriggerType { } model EventDispatcher { - id String @id @default(cuid()) - event String + id String @id @default(cuid()) + event String[] source String payloadFilter Json? contextFilter Json? - manual Boolean @default(false) + manual Boolean @default(false) dispatchableId String dispatchable Json diff --git a/packages/internal/src/schemas/eventFilter.ts b/packages/internal/src/schemas/eventFilter.ts index b0499f19b0b..4ef40a7dfa3 100644 --- a/packages/internal/src/schemas/eventFilter.ts +++ b/packages/internal/src/schemas/eventFilter.ts @@ -19,7 +19,7 @@ export const EventFilterSchema: z.ZodType = z.lazy(() => ); export const EventRuleSchema = z.object({ - event: z.string(), + event: z.string().or(z.array(z.string())), source: z.string(), payload: EventFilterSchema.optional(), context: EventFilterSchema.optional(), diff --git a/packages/internal/src/schemas/triggers.ts b/packages/internal/src/schemas/triggers.ts index 2ef8402bcd1..edc5256bc6e 100644 --- a/packages/internal/src/schemas/triggers.ts +++ b/packages/internal/src/schemas/triggers.ts @@ -13,7 +13,7 @@ export const EventExampleSchema = z.object({ export type EventExample = z.infer; export const EventSpecificationSchema = z.object({ - name: z.string(), + name: z.string().or(z.array(z.string())), title: z.string(), source: z.string(), icon: z.string(), @@ -30,7 +30,7 @@ export const DynamicTriggerMetadataSchema = z.object({ export const StaticTriggerMetadataSchema = z.object({ type: z.literal("static"), - title: z.string(), + title: z.union([z.string(), z.array(z.string())]), properties: z.array(DisplayPropertySchema).optional(), rule: EventRuleSchema, }); diff --git a/packages/trigger-sdk/src/triggerClient.ts b/packages/trigger-sdk/src/triggerClient.ts index e5f9e54bd9e..3bd46e640af 100644 --- a/packages/trigger-sdk/src/triggerClient.ts +++ b/packages/trigger-sdk/src/triggerClient.ts @@ -513,7 +513,12 @@ export class TriggerClient { } registeredSource.events = Array.from( - new Set([...registeredSource.events, options.event.name]) + new Set([ + ...registeredSource.events, + ...(typeof options.event.name === "string" + ? [options.event.name] + : options.event.name), + ]) ); this.#registeredSources[options.key] = registeredSource; diff --git a/packages/trigger-sdk/src/triggers/dynamic.ts b/packages/trigger-sdk/src/triggers/dynamic.ts index 1f6245f6431..f98a138d4e1 100644 --- a/packages/trigger-sdk/src/triggers/dynamic.ts +++ b/packages/trigger-sdk/src/triggers/dynamic.ts @@ -92,7 +92,10 @@ export class DynamicTrigger< key, channel: this.source.channel, params, - events: [this.event.name], + events: + typeof this.event.name === "string" + ? [this.event.name] + : this.event.name, integration: { id: this.source.integration.id, metadata: this.source.integration.metadata, diff --git a/packages/trigger-sdk/src/triggers/eventTrigger.ts b/packages/trigger-sdk/src/triggers/eventTrigger.ts index 3951271a05e..8942fa1f38d 100644 --- a/packages/trigger-sdk/src/triggers/eventTrigger.ts +++ b/packages/trigger-sdk/src/triggers/eventTrigger.ts @@ -6,12 +6,16 @@ import { import { z } from "zod"; import { Job } from "../job"; import { TriggerClient } from "../triggerClient"; -import { EventSpecification, Trigger } from "../types"; +import { + EventSpecification, + EventSpecificationExample, + Trigger, +} from "../types"; type EventTriggerOptions> = { event: TEventSpecification; - name?: string; + name?: string | string[]; source?: string; filter?: EventFilter; }; @@ -56,8 +60,8 @@ export class EventTrigger> /** Configuration options for an EventTrigger */ type TriggerOptions = { - /** The name of the event you are subscribing to. Must be an exact match (case sensitive). */ - name: string; + /** The name of the event you are subscribing to. Must be an exact match (case sensitive). To trigger on multiple possible events, pass in an array of event names */ + name: string | string[]; /** A [Zod](https://trigger.dev/docs/documentation/guides/zod) schema that defines the shape of the event payload. * The default is `z.any()` which is `any`. * */ @@ -84,6 +88,8 @@ type TriggerOptions = { * ``` */ filter?: EventFilter; + + examples?: EventSpecificationExample[]; }; /** `eventTrigger()` is set as a [Job's trigger](https://trigger.dev/docs/sdk/job) to subscribe to an event a Job from [a sent event](https://trigger.dev/docs/sdk/triggerclient/instancemethods/sendevent) @@ -100,6 +106,7 @@ export function eventTrigger( title: "Event", source: options.source ?? "trigger.dev", icon: "custom-event", + examples: options.examples, parsePayload: (rawPayload: any) => { if (options.schema) { return options.schema.parse(rawPayload); diff --git a/packages/trigger-sdk/src/types.ts b/packages/trigger-sdk/src/types.ts index 1b9010563c5..eb291f543a3 100644 --- a/packages/trigger-sdk/src/types.ts +++ b/packages/trigger-sdk/src/types.ts @@ -81,7 +81,7 @@ export type EventSpecificationExample = { }; export interface EventSpecification { - name: string; + name: string | string[]; title: string; source: string; icon: string;