diff --git a/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts index fcf271b21..7d6f10456 100644 --- a/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts +++ b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts @@ -1,13 +1,8 @@ import { ActionArgs, json } from "@remix-run/server-runtime"; -import { - EndpointIndexErrorSchema, - GetEndpointIndexResponse, - GetEndpointIndexResponseSchema, -} from "@trigger.dev/core"; +import { GetEndpointIndexResponse, GetEndpointIndexResponseSchema } from "@trigger.dev/core"; import { z } from "zod"; import { prisma } from "~/db.server"; import { authenticateApiRequest } from "~/services/apiAuth.server"; -import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server"; import { logger } from "~/services/logger.server"; const ParamsSchema = z.object({ diff --git a/apps/webapp/app/services/endpoints/performEndpointIndexService.ts b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts index 3457fa466..556a8526a 100644 --- a/apps/webapp/app/services/endpoints/performEndpointIndexService.ts +++ b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts @@ -13,6 +13,7 @@ import { EndpointIndexError } from "@trigger.dev/core"; import { safeBodyFromResponse } from "~/utils/json"; import { fromZodError } from "zod-validation-error"; import { IndexEndpointStats } from "@trigger.dev/core"; +import { RegisterHttpEndpointService } from "../triggers/registerHttpEndpoint.server"; export class PerformEndpointIndexService { #prismaClient: PrismaClient; @@ -22,6 +23,7 @@ export class PerformEndpointIndexService { #registerSourceServiceV2 = new RegisterSourceServiceV2(); #registerDynamicTriggerService = new RegisterDynamicTriggerService(); #registerDynamicScheduleService = new RegisterDynamicScheduleService(); + #registerHttpEndpointService = new RegisterHttpEndpointService(); constructor(prismaClient: PrismaClient = prisma) { this.#prismaClient = prismaClient; @@ -126,7 +128,7 @@ export class PerformEndpointIndexService { }); } - const { jobs, sources, dynamicTriggers, dynamicSchedules } = bodyResult.data; + const { jobs, sources, dynamicTriggers, dynamicSchedules, httpEndpoints } = bodyResult.data; const { "trigger-version": triggerVersion, "trigger-sdk-version": triggerSdkVersion } = headerResult.data; const { endpoint } = endpointIndex; @@ -152,6 +154,7 @@ export class PerformEndpointIndexService { dynamicTriggers: 0, dynamicSchedules: 0, disabledJobs: 0, + httpEndpoints: 0, }; const existingJobs = await this.#prismaClient.job.findMany({ @@ -300,6 +303,21 @@ export class PerformEndpointIndexService { } } + if (httpEndpoints) { + for (const httpEndpoint of httpEndpoints) { + try { + await this.#registerHttpEndpointService.call(endpoint, httpEndpoint); + indexStats.httpEndpoints++; + } catch (error) { + logger.error("Failed to register http endpoint", { + endpointId: endpoint.id, + httpEndpoint, + error, + }); + } + } + } + logger.debug("Endpoint indexing complete", { endpointId: endpoint.id, indexStats, @@ -320,6 +338,7 @@ export class PerformEndpointIndexService { sources, dynamicTriggers, dynamicSchedules, + httpEndpoints, }, }, }); diff --git a/apps/webapp/app/services/triggers/registerHttpEndpoint.server.ts b/apps/webapp/app/services/triggers/registerHttpEndpoint.server.ts new file mode 100644 index 000000000..6979d8c69 --- /dev/null +++ b/apps/webapp/app/services/triggers/registerHttpEndpoint.server.ts @@ -0,0 +1,68 @@ +import { HttpEndpointMetadata } from "@trigger.dev/core"; +import { $transaction, PrismaClientOrTransaction, prisma } from "~/db.server"; +import { ExtendedEndpoint, findEndpoint } from "~/models/endpoint.server"; + +export class RegisterHttpEndpointService { + #prismaClient: PrismaClientOrTransaction; + + constructor(prismaClient: PrismaClientOrTransaction = prisma) { + this.#prismaClient = prismaClient; + } + + public async call( + endpointIdOrEndpoint: string | ExtendedEndpoint, + httpEndpointMetadata: HttpEndpointMetadata + ) { + const endpoint = + typeof endpointIdOrEndpoint === "string" + ? await findEndpoint(endpointIdOrEndpoint) + : endpointIdOrEndpoint; + + //what should the scope of secrets be? and the http endpoints? + //todo will a user just enter the URL and secret once for all environments? + const secretKey = `httpendpoint:${endpoint.projectId}:${httpEndpointMetadata.id}`; + + //todo association between EventRecord and httpEndpoint + //todo wildcards at the end of the URLs, just one triggerHttpEndpoint + + return await $transaction(this.#prismaClient, async (tx) => { + const existingHttpEndpoint = await tx.triggerHttpEndpoint.findUnique({ + where: { + key_environmentId: { + key: httpEndpointMetadata.id, + environmentId: endpoint.environmentId, + }, + }, + }); + + const httpEndpoint = await tx.triggerHttpEndpoint.upsert({ + where: { + key_environmentId: { + key: httpEndpointMetadata.id, + environmentId: endpoint.environmentId, + }, + }, + create: { + key: httpEndpointMetadata.id, + active: true, + immediateResponseFilter: httpEndpointMetadata.immediateResponseFilter, + title: httpEndpointMetadata.title, + icon: httpEndpointMetadata.icon, + properties: httpEndpointMetadata.properties, + secretReference: { + connectOrCreate: { + key: "TRIGGER_API_KEY", + }, + }, + environment: { + connect: { + id: endpoint.environment.id, + }, + }, + project: { connect: { id: endpoint.projectId } }, + }, + update: {}, + }); + }); + } +} diff --git a/packages/core/src/schemas/api.ts b/packages/core/src/schemas/api.ts index 8c8ae8818..dd0dc0224 100644 --- a/packages/core/src/schemas/api.ts +++ b/packages/core/src/schemas/api.ts @@ -17,6 +17,7 @@ import { CachedTaskSchema, ServerTaskSchema, TaskSchema } from "./tasks"; import { EventSpecificationSchema, TriggerMetadataSchema } from "./triggers"; import { RunStatusSchema } from "./runs"; import { JobRunStatusRecordSchema } from "./statuses"; +import { RequestFilterSchema } from "./requestFilter"; export const UpdateTriggerSourceBodyV1Schema = z.object({ registeredEvents: z.array(z.string()), @@ -292,7 +293,11 @@ export type DynamicTriggerEndpointMetadata = z.infer; @@ -320,6 +325,7 @@ const IndexEndpointStatsSchema = z.object({ dynamicTriggers: z.number(), dynamicSchedules: z.number(), disabledJobs: z.number().default(0), + httpEndpoints: z.number().default(0), }); export type IndexEndpointStats = z.infer; diff --git a/packages/trigger-sdk/src/httpEndpoint.ts b/packages/trigger-sdk/src/httpEndpoint.ts index e026e01db..4b7c2a719 100644 --- a/packages/trigger-sdk/src/httpEndpoint.ts +++ b/packages/trigger-sdk/src/httpEndpoint.ts @@ -15,6 +15,7 @@ import { formatSchemaErrors } from "./utils/formatSchemaErrors"; type HttpEndpointOptions> = { id: string; event: TEventSpecification; + immediateResponseFilter?: RequestFilter; }; export type RequestOptions = { @@ -41,6 +42,7 @@ export class HttpEndpoint> { id: this.options.id, version: "1", event: this.options.event, + immediateResponseFilter: this.options.immediateResponseFilter, }; } } @@ -62,6 +64,7 @@ class HttpTrigger> title: this.options.endpointId, properties: this.options.event.properties, rule: { + //should this be prefixed so it doesn't clash with integrations? e.g. `httpendpoint-${this.options.endpointId}` event: this.options.endpointId, payload: this.options.filter ?? {}, source: this.options.event.source,