175 lines · 6.6 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// Integrations HTTP API — the v2 catalog surface (was `sources`).//// An integration is the tenant-shared catalog identity (slug + description +// which plugin owns it). The executor is bound to its `{ tenant, subject }` from// the request auth, so integration routes carry no scope segment — the catalog// is tenant-level. Connections (owner-scoped credentials) live in their own// group; credential-binding endpoints are gone (folded into connections).// --------------------------------------------------------------------------- import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";import { Schema } from "effect";import {  HealthCheckCandidate,  HealthCheckSpec,  IntegrationDetectionResult,  IntegrationNotFoundError,  IntegrationRemovalNotAllowedError,  IntegrationSlug,  InternalError,} from "@executor-js/sdk/shared"; // ---------------------------------------------------------------------------// Params// --------------------------------------------------------------------------- const IntegrationParams = { slug: IntegrationSlug }; // ---------------------------------------------------------------------------// Response / payload schemas// --------------------------------------------------------------------------- /** Where a credential value is carried — mirrors the SDK's *  `AuthPlacementDescriptor`. */const PlacementDescriptor = Schema.Struct({  carrier: Schema.Literals(["header", "query", "env"]),  name: Schema.String,  prefix: Schema.String,  /** Input variable this placement renders from (absent ⇒ `token`). Without   *  it the client cannot derive per-variable credential inputs for   *  multi-input methods. */  variable: Schema.optional(Schema.String),  /** Static value rendered verbatim (no credential input). */  literal: Schema.optional(Schema.String),}); /** OAuth specifics — mirrors the SDK's `AuthMethodOAuthDescriptor`. */const OAuthDescriptor = Schema.Struct({  discoveryUrl: Schema.optional(Schema.String),  authorizationUrl: Schema.optional(Schema.String),  tokenUrl: Schema.optional(Schema.String),  resource: Schema.optional(Schema.NullOr(Schema.String)),  scopes: Schema.optional(Schema.Array(Schema.String)),  registrationEndpoint: Schema.optional(Schema.String),  supportsDynamicRegistration: Schema.optional(Schema.Boolean),  supportsClientIdMetadataDocument: Schema.optional(Schema.Boolean),}); /** A single declared auth method — mirrors the SDK's `AuthMethodDescriptor`. */const AuthMethodDescriptorSchema = Schema.Struct({  id: Schema.String,  label: Schema.String,  kind: Schema.Literals(["oauth", "apikey", "header", "none"]),  template: Schema.String,  placements: Schema.optional(Schema.Array(PlacementDescriptor)),  oauth: Schema.optional(OAuthDescriptor),}); /** Public projection of an integration — mirrors the SDK's `Integration`. */const IntegrationResponse = Schema.Struct({  slug: IntegrationSlug,  /** Display name. */  name: Schema.String,  description: Schema.String,  /** The plugin that owns this integration kind (e.g. "openapi", "mcp"). */  kind: Schema.String,  canRemove: Schema.Boolean,  canRefresh: Schema.Boolean,  /** Declared auth methods derived from the owning plugin's stored config.   *  Always present (possibly empty) so the client never handles absence. */  authMethods: Schema.Array(AuthMethodDescriptorSchema),  /** Non-secret URL derived from opaque integration config for favicons. */  displayUrl: Schema.optional(Schema.String),}); const UpdateIntegrationPayload = Schema.Struct({  name: Schema.optional(Schema.String),  description: Schema.optional(Schema.String),}); const DetectRequest = Schema.Struct({  url: Schema.String.check(Schema.isMaxLength(2_048)),}); // Set (or clear, with null) the integration's declared health check. The spec// lives inside the owning plugin's opaque config; core only routes it.const SetHealthCheckPayload = Schema.Struct({  spec: Schema.NullOr(HealthCheckSpec),}); // ---------------------------------------------------------------------------// Error schemas with HTTP status annotations// --------------------------------------------------------------------------- const IntegrationNotFound = IntegrationNotFoundError.annotate({ httpApiStatus: 404 });const IntegrationRemovalNotAllowed = IntegrationRemovalNotAllowedError.annotate({  httpApiStatus: 409,}); // ---------------------------------------------------------------------------// Group// --------------------------------------------------------------------------- export const IntegrationsApi = HttpApiGroup.make("integrations")  .add(    HttpApiEndpoint.get("list", "/integrations", {      success: Schema.Array(IntegrationResponse),      error: InternalError,    }),  )  .add(    HttpApiEndpoint.get("get", "/integrations/:slug", {      params: IntegrationParams,      success: IntegrationResponse,      error: [InternalError, IntegrationNotFound],    }),  )  .add(    HttpApiEndpoint.patch("update", "/integrations/:slug", {      params: IntegrationParams,      payload: UpdateIntegrationPayload,      success: IntegrationResponse,      error: [InternalError, IntegrationNotFound],    }),  )  .add(    HttpApiEndpoint.delete("remove", "/integrations/:slug", {      params: IntegrationParams,      success: Schema.Struct({ removed: Schema.Boolean }),      error: [InternalError, IntegrationRemovalNotAllowed],    }),  )  .add(    HttpApiEndpoint.post("detect", "/integrations/detect", {      payload: DetectRequest,      success: Schema.Array(IntegrationDetectionResult),      error: InternalError,    }),  )  // The integration's currently declared health check (null when none is set).  .add(    HttpApiEndpoint.get("healthCheckGet", "/integrations/:slug/health-check", {      params: IntegrationParams,      success: Schema.NullOr(HealthCheckSpec),      error: InternalError,    }),  )  // Operations the user can pick as the health check, ranked non-destructive +  // fewest-required-args first so the obvious identity endpoint floats to the top.  .add(    HttpApiEndpoint.get("healthCheckCandidates", "/integrations/:slug/health-check/candidates", {      params: IntegrationParams,      success: Schema.Array(HealthCheckCandidate),      error: [InternalError, IntegrationNotFound],    }),  )  // Persist (or clear) the integration's health check.  .add(    HttpApiEndpoint.put("healthCheckSet", "/integrations/:slug/health-check", {      params: IntegrationParams,      payload: SetHealthCheckPayload,      success: Schema.Struct({ ok: Schema.Boolean }),      error: [InternalError, IntegrationNotFound],    }),  );