124 lines · 4.3 KB · TypeScript
RawDownload
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";import { Schema } from "effect";import { ScopeId } from "@executor-js/sdk/core";import { InternalError } from "@executor-js/api"; import {  GraphqlIntrospectionError,  GraphqlExtractionError,} from "../sdk/errors";import { GraphqlSourceAuth, HeaderValue } from "../sdk/types"; // StoredGraphqlSource shape as an HTTP response schema. Kept local to the// api layer because the sdk-side `StoredGraphqlSource` is a plain interface.export const StoredSourceSchema = Schema.Struct({  namespace: Schema.String,  name: Schema.String,  endpoint: Schema.String,  headers: Schema.Record(Schema.String, HeaderValue),  queryParams: Schema.Record(Schema.String, HeaderValue),  auth: GraphqlSourceAuth,}); // ---------------------------------------------------------------------------// Params// --------------------------------------------------------------------------- const ScopeParams = {  scopeId: ScopeId,}; const SourceParams = {  scopeId: ScopeId,  namespace: Schema.String,}; // ---------------------------------------------------------------------------// Payloads// --------------------------------------------------------------------------- const AddSourcePayload = Schema.Struct({  endpoint: Schema.String,  name: Schema.optional(Schema.String),  introspectionJson: Schema.optional(Schema.String),  namespace: Schema.optional(Schema.String),  headers: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),  queryParams: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),  auth: Schema.optional(GraphqlSourceAuth),}); const UpdateSourcePayload = Schema.Struct({  name: Schema.optional(Schema.String),  endpoint: Schema.optional(Schema.String),  headers: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),  queryParams: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),  auth: Schema.optional(GraphqlSourceAuth),}); const UpdateSourceResponse = Schema.Struct({  updated: Schema.Boolean,}); // ---------------------------------------------------------------------------// Responses// --------------------------------------------------------------------------- const AddSourceResponse = Schema.Struct({  toolCount: Schema.Number,  namespace: Schema.String,}); // ---------------------------------------------------------------------------// Errors with HTTP status// --------------------------------------------------------------------------- const IntrospectionError = GraphqlIntrospectionError.annotate(  { httpApiStatus: 400 },);const ExtractionError = GraphqlExtractionError.annotate(  { httpApiStatus: 400 },); // ---------------------------------------------------------------------------// Group//// Plugin SDK errors (GraphqlIntrospectionError etc.) are declared once at// the group level via `.addError(...)` — every endpoint inherits them. The// errors themselves carry their HTTP status via `HttpApiSchema.annotations`// above, so handlers just `return yield* ext.foo(...)` and the schema// encodes whatever it gets.//// 5xx is handled at the API level: `.addError(InternalError)` adds a// single shared opaque-by-schema 500 surface translated from `StorageError`// by `withCapture` at the HTTP edge. No per-handler wrapping, no// per-plugin InternalError.// --------------------------------------------------------------------------- const GraphqlErrors = [InternalError, IntrospectionError, ExtractionError] as const; export const GraphqlGroup = HttpApiGroup.make("graphql")  .add(    HttpApiEndpoint.post("addSource", "/scopes/:scopeId/graphql/sources", {      params: ScopeParams,      payload: AddSourcePayload,      success: AddSourceResponse,      error: GraphqlErrors,    }),  )  .add(    HttpApiEndpoint.get("getSource", "/scopes/:scopeId/graphql/sources/:namespace", {      params: SourceParams,      success: Schema.NullOr(StoredSourceSchema),      error: GraphqlErrors,    }),  )  .add(    HttpApiEndpoint.patch("updateSource", "/scopes/:scopeId/graphql/sources/:namespace", {      params: SourceParams,      payload: UpdateSourcePayload,      success: UpdateSourceResponse,      error: GraphqlErrors,    }),  )  // Plugin domain errors carry their own HTTP status (4xx);  // `InternalError` is the shared opaque 500 translated at the HTTP edge.