From d9834d60d07c5d9737319044d554d2939d94c97a Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Fri, 1 Nov 2024 19:06:16 -0400 Subject: [PATCH] update endpoint --- .../src/app/api/v1/targets/route.ts | 12 +-- openapi.v1.yaml | 82 +++++++++++++++++++ packages/job-dispatch/src/target.ts | 23 +++++- 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/apps/webservice/src/app/api/v1/targets/route.ts b/apps/webservice/src/app/api/v1/targets/route.ts index ab78944b..51c20116 100644 --- a/apps/webservice/src/app/api/v1/targets/route.ts +++ b/apps/webservice/src/app/api/v1/targets/route.ts @@ -37,15 +37,15 @@ const patchBodySchema = z.object({ ), }); -export const PATCH = request() +export const POST = request() .use(authn) .use(parseBody(patchBodySchema)) .use( - authz(({ can, ctx }) => - can + authz(({ can, ctx }) => { + return can .perform(Permission.TargetUpdate) - .on({ type: "workspace", id: ctx.body.workspaceId }), - ), + .on({ type: "workspace", id: ctx.body.workspaceId }); + }), ) .handle<{ user: schema.User; body: z.infer }>( async (ctx) => { @@ -55,6 +55,8 @@ export const PATCH = request() { status: 400 }, ); + console.log("ctx.body.targets", ctx.body.targets); + const targets = await upsertTargets( db, ctx.body.targets.map((t) => ({ diff --git a/openapi.v1.yaml b/openapi.v1.yaml index 574f7fd3..0d35e940 100644 --- a/openapi.v1.yaml +++ b/openapi.v1.yaml @@ -3,6 +3,88 @@ info: title: Ctrlplane API version: 1.0.0 paths: + /v1/targets: + post: + summary: Create or update multiple targets + operationId: upsertTargets + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - workspaceId + - targets + properties: + workspaceId: + type: string + format: uuid + targets: + type: array + items: + type: object + required: + - name + - kind + - identifier + - version + - config + properties: + name: + type: string + kind: + type: string + identifier: + type: string + version: + type: string + config: + type: object + metadata: + type: object + additionalProperties: + type: string + variables: + type: array + items: + type: object + required: + - key + - value + properties: + key: + type: string + value: + type: [string, number, boolean] + sensitive: + type: boolean + responses: + "200": + description: Targets created successfully + content: + application/json: + schema: + type: object + required: + - count + properties: + count: + type: integer + description: Number of targets created/updated + "400": + description: Bad request - No targets provided or invalid input + content: + application/json: + schema: + type: object + properties: + error: + type: string + "401": + description: Unauthorized + "403": + description: Permission denied /v1/targets/{targetId}: get: summary: Get a target diff --git a/packages/job-dispatch/src/target.ts b/packages/job-dispatch/src/target.ts index ff0a339a..0c60cb7b 100644 --- a/packages/job-dispatch/src/target.ts +++ b/packages/job-dispatch/src/target.ts @@ -8,6 +8,7 @@ import { eq, inArray, isNotNull, + or, } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import { @@ -212,13 +213,33 @@ export const upsertTargets = async ( >, ) => { try { + // Get existing targets from the database, grouped by providerId. + // - For targets without a providerId, look them up by workspaceId and + // identifier. + // - For targets with a providerId, get all targets for that provider. + log.info("Upserting targets", { + targetsToInsertCount: targetsToInsert.length, + }); const targetsBeforeInsertPromises = _.chain(targetsToInsert) .groupBy((t) => t.providerId) .filter((t) => t[0]?.providerId != null) .map(async (targets) => { const providerId = targets[0]?.providerId; + return providerId == null - ? [] + ? db + .select() + .from(target) + .where( + or( + ...targets.map((t) => + and( + eq(target.workspaceId, t.workspaceId), + eq(target.identifier, t.identifier), + ), + ), + ), + ) : getExistingTargetsForProvider(tx, providerId); }) .value();