126 lines · 4.7 KB · TypeScript
RawDownload
import { HttpApiBuilder } from "effect/unstable/httpapi";import { Context, Effect } from "effect"; import { addGroup, capture } from "@executor-js/api";import type { OpenApiPluginExtension } from "../sdk/plugin";import { OpenApiGroup } from "./group"; // ---------------------------------------------------------------------------// Service tag//// Holds the `Captured` shape — every method's `StorageFailure`// channel has been swapped for `InternalError({ traceId })`. The cloud// app provides an already-wrapped extension via// `Layer.succeed(OpenApiExtensionService, withCapture(executor.openapi))`.// Handlers see `InternalError` in the error union, which matches// `.addError(InternalError)` on the group — no per-handler translation.// --------------------------------------------------------------------------- export class OpenApiExtensionService extends Context.Service<  OpenApiExtensionService,  OpenApiPluginExtension>()("OpenApiExtensionService") {} // ---------------------------------------------------------------------------// Composed API — core + openapi group// --------------------------------------------------------------------------- const ExecutorApiWithOpenApi = addGroup(OpenApiGroup); // ---------------------------------------------------------------------------// Handlers//// Each handler is exactly: yield the extension service, call the method,// return. Plugin SDK errors flow through the typed channel and are// schema-encoded to 4xx by HttpApi (see group.ts errors). Defects bubble up// and are captured + downgraded to `InternalError(traceId)` by the API-level// observability middleware.// --------------------------------------------------------------------------- export const OpenApiHandlers = HttpApiBuilder.group(ExecutorApiWithOpenApi, "openapi", (handlers) =>  handlers    .handle("previewSpec", ({ payload }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          return yield* ext.previewSpec({ spec: payload.spec });        }),      ),    )    .handle("addSpec", ({ payload }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          return yield* ext.addSpec({            spec: payload.spec,            slug: payload.slug,            description: payload.description,            baseUrl: payload.baseUrl,            headers: payload.headers ? { ...payload.headers } : undefined,            queryParams: payload.queryParams ? { ...payload.queryParams } : undefined,            authenticationTemplate: payload.authenticationTemplate,          });        }),      ),    )    .handle("getIntegration", ({ params }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          const integration = yield* ext.getIntegration(params.slug);          return integration            ? {                slug: integration.slug,                description: integration.description,                kind: integration.kind,                canRemove: integration.canRemove,                canRefresh: integration.canRefresh,              }            : null;        }),      ),    )    .handle("getConfig", ({ params }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          const config = yield* ext.getConfig(params.slug);          return config            ? {                spec: config.spec,                sourceUrl: config.sourceUrl,                googleDiscoveryUrls: config.googleDiscoveryUrls                  ? [...config.googleDiscoveryUrls]                  : undefined,                baseUrl: config.baseUrl,                headers: config.headers ? { ...config.headers } : undefined,                queryParams: config.queryParams ? { ...config.queryParams } : undefined,                authenticationTemplate: config.authenticationTemplate                  ? [...config.authenticationTemplate]                  : undefined,              }            : null;        }),      ),    )    .handle("configure", ({ params, payload }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          const authenticationTemplate = yield* ext.configure(params.slug, {            authenticationTemplate: payload.authenticationTemplate,            mode: payload.mode ?? "merge",          });          return { authenticationTemplate: [...authenticationTemplate] };        }),      ),    )    .handle("removeSpec", ({ params }) =>      capture(        Effect.gen(function* () {          const ext = yield* OpenApiExtensionService;          yield* ext.removeSpec(params.slug);        }),      ),    ),);