98 lines · 3.7 KB · TypeScript
RawDownload
import { HttpApiBuilder } from "effect/unstable/httpapi";import { Context, Effect } from "effect"; import { addGroup, capture } from "@executor-js/api";import type {  GraphqlPluginExtension,  HeaderValue,  GraphqlUpdateSourceInput,} from "../sdk/plugin";import { GraphqlGroup } from "./group"; // ---------------------------------------------------------------------------// Service tag//// Holds the `Captured` shape — every method's `StorageError` channel has// been swapped for `InternalError({ traceId })`. The host app provides an// already-wrapped extension via// `Layer.succeed(GraphqlExtensionService, withCapture(executor.graphql))`.// Handlers see `InternalError` in the error union, which matches// `.addError(InternalError)` on the group — no per-handler translation.// --------------------------------------------------------------------------- export class GraphqlExtensionService extends Context.Service<GraphqlExtensionService, GraphqlPluginExtension>()("GraphqlExtensionService") {} // ---------------------------------------------------------------------------// Composed API — core + graphql group// --------------------------------------------------------------------------- const ExecutorApiWithGraphql = addGroup(GraphqlGroup); // ---------------------------------------------------------------------------// 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 `.addError(...)` calls).// Defects bubble up and are captured + downgraded to `InternalError(traceId)`// by the API-level observability middleware.// --------------------------------------------------------------------------- export const GraphqlHandlers = HttpApiBuilder.group(  ExecutorApiWithGraphql,  "graphql",  (handlers) =>    handlers      .handle("addSource", ({ params: path, payload }) =>        capture(          Effect.gen(function* () {            const ext = yield* GraphqlExtensionService;            const result = yield* ext.addSource({              endpoint: payload.endpoint,              scope: path.scopeId,              name: payload.name,              introspectionJson: payload.introspectionJson,              namespace: payload.namespace,              headers: payload.headers as                | Record<string, HeaderValue>                | undefined,              queryParams: payload.queryParams as                | Record<string, HeaderValue>                | undefined,              auth: payload.auth,            });            return {              toolCount: result.toolCount,              namespace: result.namespace,            };          }),        ),      )      .handle("getSource", ({ params: path }) =>        capture(          Effect.gen(function* () {            const ext = yield* GraphqlExtensionService;            return yield* ext.getSource(path.namespace, path.scopeId);          }),        ),      )      .handle("updateSource", ({ params: path, payload }) =>        capture(          Effect.gen(function* () {            const ext = yield* GraphqlExtensionService;            yield* ext.updateSource(path.namespace, path.scopeId, {              name: payload.name,              endpoint: payload.endpoint,              headers: payload.headers as                | Record<string, HeaderValue>                | undefined,              queryParams: payload.queryParams as                | Record<string, HeaderValue>                | undefined,              auth: payload.auth,            } as GraphqlUpdateSourceInput);            return { updated: true };          }),        ),      ),);