Initial server side work
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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: {},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<typeof DynamicTriggerEndpoi
|
||||
const HttpEndpointMetadataSchema = z.object({
|
||||
id: z.string(),
|
||||
version: z.string(),
|
||||
title: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
properties: z.array(DisplayPropertySchema).optional(),
|
||||
event: EventSpecificationSchema,
|
||||
immediateResponseFilter: RequestFilterSchema.optional(),
|
||||
});
|
||||
|
||||
export type HttpEndpointMetadata = z.infer<typeof HttpEndpointMetadataSchema>;
|
||||
@@ -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<typeof IndexEndpointStatsSchema>;
|
||||
|
||||
@@ -15,6 +15,7 @@ import { formatSchemaErrors } from "./utils/formatSchemaErrors";
|
||||
type HttpEndpointOptions<TEventSpecification extends EventSpecification<any>> = {
|
||||
id: string;
|
||||
event: TEventSpecification;
|
||||
immediateResponseFilter?: RequestFilter;
|
||||
};
|
||||
|
||||
export type RequestOptions = {
|
||||
@@ -41,6 +42,7 @@ export class HttpEndpoint<TEventSpecification extends EventSpecification<any>> {
|
||||
id: this.options.id,
|
||||
version: "1",
|
||||
event: this.options.event,
|
||||
immediateResponseFilter: this.options.immediateResponseFilter,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -62,6 +64,7 @@ class HttpTrigger<TEventSpecification extends EventSpecification<any>>
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user