diff --git a/plugins/webhooks/server/api/schema.ts b/plugins/webhooks/server/api/schema.ts new file mode 100644 index 000000000..5d00a4d5c --- /dev/null +++ b/plugins/webhooks/server/api/schema.ts @@ -0,0 +1,38 @@ +import { z } from "zod"; + +export const WebhookSubscriptionsCreateSchema = z.object({ + body: z.object({ + name: z.string(), + url: z.string().url(), + secret: z.string().optional(), + events: z.array(z.string()), + }), +}); + +export type WebhookSubscriptionsCreateReq = z.infer< + typeof WebhookSubscriptionsCreateSchema +>; + +export const WebhookSubscriptionsUpdateSchema = z.object({ + body: z.object({ + id: z.string().uuid(), + name: z.string(), + url: z.string().url(), + secret: z.string().optional(), + events: z.array(z.string()), + }), +}); + +export type WebhookSubscriptionsUpdateReq = z.infer< + typeof WebhookSubscriptionsUpdateSchema +>; + +export const WebhookSubscriptionsDeleteSchema = z.object({ + body: z.object({ + id: z.string().uuid(), + }), +}); + +export type WebhookSubscriptionsDeleteReq = z.infer< + typeof WebhookSubscriptionsDeleteSchema +>; diff --git a/plugins/webhooks/server/api/webhookSubscriptions.ts b/plugins/webhooks/server/api/webhookSubscriptions.ts index 47332ecc6..4c5337a0c 100644 --- a/plugins/webhooks/server/api/webhookSubscriptions.ts +++ b/plugins/webhooks/server/api/webhookSubscriptions.ts @@ -1,14 +1,14 @@ import Router from "koa-router"; import compact from "lodash/compact"; import isEmpty from "lodash/isEmpty"; -import { ValidationError } from "@server/errors"; import auth from "@server/middlewares/authentication"; +import validate from "@server/middlewares/validate"; import { WebhookSubscription, Event } from "@server/models"; import { authorize } from "@server/policies"; import pagination from "@server/routes/api/middlewares/pagination"; import { WebhookSubscriptionEvent, APIContext } from "@server/types"; -import { assertArray, assertPresent, assertUuid } from "@server/validation"; import presentWebhookSubscription from "../presenters/webhookSubscription"; +import * as T from "./schema"; const router = new Router(); @@ -38,18 +38,13 @@ router.post( router.post( "webhookSubscriptions.create", auth({ admin: true }), - async (ctx: APIContext) => { + validate(T.WebhookSubscriptionsCreateSchema), + async (ctx: APIContext) => { const { user } = ctx.state.auth; authorize(user, "createWebhookSubscription", user.team); - const { name, url, secret } = ctx.request.body; - const events: string[] = compact(ctx.request.body.events); - assertPresent(name, "name is required"); - assertPresent(url, "url is required"); - assertArray(events, "events is required"); - if (events.length === 0) { - throw ValidationError("events are required"); - } + const { name, url, secret } = ctx.input.body; + const events: string[] = compact(ctx.input.body.events); const webhookSubscription = await WebhookSubscription.create({ name, @@ -84,9 +79,9 @@ router.post( router.post( "webhookSubscriptions.delete", auth({ admin: true }), - async (ctx: APIContext) => { - const { id } = ctx.request.body; - assertUuid(id, "id is required"); + validate(T.WebhookSubscriptionsDeleteSchema), + async (ctx: APIContext) => { + const { id } = ctx.input.body; const { user } = ctx.state.auth; const webhookSubscription = await WebhookSubscription.findByPk(id); @@ -117,21 +112,14 @@ router.post( router.post( "webhookSubscriptions.update", auth({ admin: true }), - async (ctx: APIContext) => { - const { id } = ctx.request.body; - assertUuid(id, "id is required"); + validate(T.WebhookSubscriptionsUpdateSchema), + async (ctx: APIContext) => { + const { id, name, url, secret } = ctx.input.body; const { user } = ctx.state.auth; - - const { name, url, secret } = ctx.request.body; - const events: string[] = compact(ctx.request.body.events); - assertPresent(name, "name is required"); - assertPresent(url, "url is required"); - assertArray(events, "events is required"); - if (events.length === 0) { - throw ValidationError("events are required"); - } - - const webhookSubscription = await WebhookSubscription.findByPk(id); + const events: string[] = compact(ctx.input.body.events); + const webhookSubscription = await WebhookSubscription.findByPk(id, { + rejectOnEmpty: true, + }); authorize(user, "update", webhookSubscription); diff --git a/server/routes/api/index.ts b/server/routes/api/index.ts index 0622bda50..edca8ba81 100644 --- a/server/routes/api/index.ts +++ b/server/routes/api/index.ts @@ -60,8 +60,11 @@ glob .forEach((filePath: string) => { // eslint-disable-next-line @typescript-eslint/no-var-requires const pkg: Router = require(path.join(process.cwd(), filePath)).default; - router.use("/", pkg.routes()); - Logger.debug("lifecycle", `Registered API routes for ${filePath}`); + + if (pkg && "routes" in pkg) { + router.use("/", pkg.routes()); + Logger.debug("lifecycle", `Registered API routes for ${filePath}`); + } }); // routes