UNPKG

webhook-openapi

Version:

Helps you create you own webhook server

193 lines (188 loc) 4.97 kB
import { OptionalKind } from '@sinclair/typebox'; export * from '@sinclair/typebox'; function mapProperties(to, schema) { return Object.entries(schema.properties ?? []).map(([key, value]) => { const { type, description, examples, ...otherProps } = value; return { in: to, name: key, schema: { type, ...otherProps }, required: schema.required?.includes(key) ?? false }; }); } function mapTypeContents(mimeTypes, schema) { const responses = {}; for (const type of mimeTypes) { responses[type] = { schema }; } return responses; } class WebhookEvent { openapi = {}; _ = { method: "post", body: null, bodyHeaders: null, response: {} //OpenAPIV3_1.ResponsesObject, }; // method<Method extends HTTPMethods>(method: Method) { // this._.method = method.toLocaleLowerCase() as Lowercase<typeof method>; // return this; // } bodyHeaders(schema) { this._.bodyHeaders = schema; return this; } body(contentTypeOrSchema, schema) { this._.body = // biome-ignore lint/style/noNonNullAssertion: <explanation> typeof contentTypeOrSchema === "object" ? contentTypeOrSchema : schema; return this; } response(schema, options) { this._.response = schema; return this; } } class Webhook { openapi; mimeTypes = { "application/json": { serialization: JSON.stringify, deserialization: (response) => response.json() }, "text/plain": { serialization: (data) => data, deserialization: (response) => response.text() } }; hooks = { beforeRequest: [], afterResponse: [], sendError: [] }; constructor() { this.openapi = { openapi: "3.1.0", info: { title: "Webhook-openapi", version: "0.0.1" }, webhooks: {}, paths: {} }; } extend(webhook) { this.hooks.afterResponse.push(...webhook.hooks.afterResponse); this.hooks.beforeRequest.push(...webhook.hooks.beforeRequest); this.hooks.sendError.push(...webhook.hooks.sendError); return this; } async runHooks(name, args) { for (const hook of this.hooks[name]) { hook(args); } } async runMutationHooks(name, args) { let data = args; for await (const hook of this.hooks[name]) { data = await hook(args); } return data; } onBeforeRequest(hook) { this.hooks.beforeRequest.push(hook); return this; } onAfterResponse(hook) { this.hooks.afterResponse.push(hook); return this; } onSendError(hook) { this.hooks.sendError.push(hook); return this; } mimeType(mimeType, options) { this.mimeTypes[mimeType] = options; return this; } event(name, event, params) { const webhookEvent = event(new WebhookEvent()); this.openapi.webhooks[name] = { ...params, [webhookEvent._.method]: { parameters: webhookEvent._.bodyHeaders ? mapProperties("header", webhookEvent._.bodyHeaders) : void 0, requestBody: { content: mapTypeContents( Object.keys(this.mimeTypes), webhookEvent._.body ?? void 0 ), required: !(OptionalKind in (webhookEvent?._?.body ?? {})), description: webhookEvent._.body?.description }, responses: { "200": { description: webhookEvent._.response?.description ?? "", content: mapTypeContents( Object.keys(this.mimeTypes), webhookEvent._.response ?? void 0 ) } } } }; return this; } async call(url, event, params, requestOptions) { const custom = requestOptions?.custom ?? {}; const requestInit = { method: "POST", headers: new Headers({ "Content-Type": requestOptions?.mimeType ?? "application/json" }), ...requestOptions }; await this.runMutationHooks("beforeRequest", { request: requestInit, data: params, event, url, custom, webhook: this }); if (!requestInit.body) requestInit.body = await this.mimeTypes[requestOptions?.mimeType ?? "application/json"].serialization(params); try { const response = await fetch(url, requestInit); const mimeType = response.headers.get("content-type")?.split(";")[0].trim(); let data; if (mimeType) data = await this.mimeTypes[mimeType ?? "application/text"]?.deserialization( response ); this.runHooks("afterResponse", { response, request: requestInit, url, body: params, data, event, custom, webhook: this }); } catch (error) { if (error instanceof Error) await this.runHooks("sendError", { request: requestInit, data: params, url, event, webhook: this, error }); } return null; } } export { Webhook, WebhookEvent };