Initial server side work

This commit is contained in:
Matt Aitken
2023-10-24 13:26:25 +01:00
parent 734624371b
commit d1b4057f8d
5 changed files with 98 additions and 7 deletions
@@ -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: {},
});
});
}
}
+6
View File
@@ -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>;
+3
View File
@@ -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,