import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";import { Schema } from "effect";import { ScopeId, SecretBackedValue } from "@executor-js/sdk/core";import { InternalError } from "@executor-js/api"; import { OpenApiParseError, OpenApiExtractionError, OpenApiOAuthError } from "../sdk/errors";import { SpecPreview } from "../sdk/preview";import { StoredSourceSchema } from "../sdk/store";import { OAuth2Auth, OAuth2SourceConfig, OpenApiSourceBindingInputSchema, OpenApiSourceBindingRef,} from "../sdk/types"; // ---------------------------------------------------------------------------// Params// --------------------------------------------------------------------------- const DomainErrors = [ InternalError, OpenApiParseError, OpenApiExtractionError, OpenApiOAuthError,] as const; const ScopeIdParam = { scopeId: ScopeId,}; const SourceParams = { scopeId: ScopeId, namespace: Schema.String,}; const SourceBindingParams = { scopeId: ScopeId, namespace: Schema.String, sourceScopeId: ScopeId,}; const SpecFetchCredentialsPayload = Schema.Struct({ headers: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)), queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)),}); // ---------------------------------------------------------------------------// Payloads// --------------------------------------------------------------------------- const AddSpecPayload = Schema.Struct({ spec: Schema.String, specFetchCredentials: Schema.optional(SpecFetchCredentialsPayload), name: Schema.optional(Schema.String), baseUrl: 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, SecretBackedValue)), oauth2: Schema.optional(Schema.Union([OAuth2Auth, OAuth2SourceConfig])),}); const PreviewSpecPayload = Schema.Struct({ spec: Schema.String, specFetchCredentials: Schema.optional(SpecFetchCredentialsPayload),}); const UpdateSourcePayload = Schema.Struct({ name: Schema.optional(Schema.String), baseUrl: Schema.optional(Schema.String), headers: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)), // Set after a successful re-authenticate to refresh the source's // stored OAuth2 metadata. oauth2: Schema.optional(Schema.Union([OAuth2Auth, OAuth2SourceConfig])),}); const UpdateSourceResponse = Schema.Struct({ updated: Schema.Boolean,}); const RemoveBindingPayload = Schema.Struct({ sourceId: Schema.String, sourceScope: ScopeId, slot: Schema.String, scope: ScopeId,}); // ---------------------------------------------------------------------------// Responses// --------------------------------------------------------------------------- const AddSpecResponse = Schema.Struct({ toolCount: Schema.Number, namespace: Schema.String,}); // HTTP status on the three domain errors lives on their class// declarations in `../sdk/errors.ts` — see the comment there. // ---------------------------------------------------------------------------// Group//// Plugin SDK errors (OpenApiParseError, OpenApiExtractionError,// OpenApiOAuthError) 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 comes out.//// 5xx is handled at the API level: `.addError(InternalError)` adds the// shared opaque 500 surface. Defects are captured + downgraded to it by// an HttpApiBuilder middleware (see apps/cloud/src/observability.ts).// StorageError → InternalError translation happens at service wiring// time via `withCapture(executor)`.// --------------------------------------------------------------------------- export const OpenApiGroup = HttpApiGroup.make("openapi") .add( HttpApiEndpoint.post("previewSpec", "/scopes/:scopeId/openapi/preview", { params: ScopeIdParam, payload: PreviewSpecPayload, success: SpecPreview, error: DomainErrors, }), ) .add( HttpApiEndpoint.post("addSpec", "/scopes/:scopeId/openapi/specs", { params: ScopeIdParam, payload: AddSpecPayload, success: AddSpecResponse, error: DomainErrors, }), ) .add( HttpApiEndpoint.get("getSource", "/scopes/:scopeId/openapi/sources/:namespace", { params: SourceParams, success: Schema.NullOr(StoredSourceSchema), error: DomainErrors, }), ) .add( HttpApiEndpoint.patch("updateSource", "/scopes/:scopeId/openapi/sources/:namespace", { params: SourceParams, payload: UpdateSourcePayload, success: UpdateSourceResponse, error: DomainErrors, }), ) .add( HttpApiEndpoint.get( "listSourceBindings", "/scopes/:scopeId/openapi/sources/:namespace/base/:sourceScopeId/bindings", { params: SourceBindingParams, success: Schema.Array(OpenApiSourceBindingRef), error: DomainErrors, }, ), ) .add( HttpApiEndpoint.post("setSourceBinding", "/scopes/:scopeId/openapi/source-bindings", { params: ScopeIdParam, payload: OpenApiSourceBindingInputSchema, success: OpenApiSourceBindingRef, error: DomainErrors, }), ) .add( HttpApiEndpoint.post( "removeSourceBinding", "/scopes/:scopeId/openapi/source-bindings/remove", { params: ScopeIdParam, payload: RemoveBindingPayload, success: Schema.Struct({ removed: Schema.Boolean }), error: DomainErrors, }, ), );
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
import { Schema } from "effect";
import { ScopeId, SecretBackedValue } from "@executor-js/sdk/core";
import { InternalError } from "@executor-js/api";
import { OpenApiParseError, OpenApiExtractionError, OpenApiOAuthError } from "../sdk/errors";
import { SpecPreview } from "../sdk/preview";
import { StoredSourceSchema } from "../sdk/store";
import {
OAuth2Auth,
OAuth2SourceConfig,
OpenApiSourceBindingInputSchema,
OpenApiSourceBindingRef,
} from "../sdk/types";
// ---------------------------------------------------------------------------
// Params
// ---------------------------------------------------------------------------
const DomainErrors = [
InternalError,
OpenApiParseError,
OpenApiExtractionError,
OpenApiOAuthError,
] as const;
const ScopeIdParam = {
scopeId: ScopeId,
};
const SourceParams = {
scopeId: ScopeId,
namespace: Schema.String,
};
const SourceBindingParams = {
scopeId: ScopeId,
namespace: Schema.String,
sourceScopeId: ScopeId,
};
const SpecFetchCredentialsPayload = Schema.Struct({
headers: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)),
queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)),
});
// ---------------------------------------------------------------------------
// Payloads
// ---------------------------------------------------------------------------
const AddSpecPayload = Schema.Struct({
spec: Schema.String,
specFetchCredentials: Schema.optional(SpecFetchCredentialsPayload),
name: Schema.optional(Schema.String),
baseUrl: 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, SecretBackedValue)),
oauth2: Schema.optional(Schema.Union([OAuth2Auth, OAuth2SourceConfig])),
});
const PreviewSpecPayload = Schema.Struct({
spec: Schema.String,
specFetchCredentials: Schema.optional(SpecFetchCredentialsPayload),
});
const UpdateSourcePayload = Schema.Struct({
name: Schema.optional(Schema.String),
baseUrl: Schema.optional(Schema.String),
headers: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)),
// Set after a successful re-authenticate to refresh the source's
// stored OAuth2 metadata.
oauth2: Schema.optional(Schema.Union([OAuth2Auth, OAuth2SourceConfig])),
});
const UpdateSourceResponse = Schema.Struct({
updated: Schema.Boolean,
});
const RemoveBindingPayload = Schema.Struct({
sourceId: Schema.String,
sourceScope: ScopeId,
slot: Schema.String,
scope: ScopeId,
});
// ---------------------------------------------------------------------------
// Responses
// ---------------------------------------------------------------------------
const AddSpecResponse = Schema.Struct({
toolCount: Schema.Number,
namespace: Schema.String,
});
// HTTP status on the three domain errors lives on their class
// declarations in `../sdk/errors.ts` — see the comment there.
// ---------------------------------------------------------------------------
// Group
//
// Plugin SDK errors (OpenApiParseError, OpenApiExtractionError,
// OpenApiOAuthError) 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 comes out.
//
// 5xx is handled at the API level: `.addError(InternalError)` adds the
// shared opaque 500 surface. Defects are captured + downgraded to it by
// an HttpApiBuilder middleware (see apps/cloud/src/observability.ts).
// StorageError → InternalError translation happens at service wiring
// time via `withCapture(executor)`.
// ---------------------------------------------------------------------------
export const OpenApiGroup = HttpApiGroup.make("openapi")
.add(
HttpApiEndpoint.post("previewSpec", "/scopes/:scopeId/openapi/preview", {
params: ScopeIdParam,
payload: PreviewSpecPayload,
success: SpecPreview,
error: DomainErrors,
}),
)
.add(
HttpApiEndpoint.post("addSpec", "/scopes/:scopeId/openapi/specs", {
params: ScopeIdParam,
payload: AddSpecPayload,
success: AddSpecResponse,
error: DomainErrors,
}),
)
.add(
HttpApiEndpoint.get("getSource", "/scopes/:scopeId/openapi/sources/:namespace", {
params: SourceParams,
success: Schema.NullOr(StoredSourceSchema),
error: DomainErrors,
}),
)
.add(
HttpApiEndpoint.patch("updateSource", "/scopes/:scopeId/openapi/sources/:namespace", {
params: SourceParams,
payload: UpdateSourcePayload,
success: UpdateSourceResponse,
error: DomainErrors,
}),
)
.add(
HttpApiEndpoint.get(
"listSourceBindings",
"/scopes/:scopeId/openapi/sources/:namespace/base/:sourceScopeId/bindings",
{
params: SourceBindingParams,
success: Schema.Array(OpenApiSourceBindingRef),
error: DomainErrors,
},
),
)
.add(
HttpApiEndpoint.post("setSourceBinding", "/scopes/:scopeId/openapi/source-bindings", {
params: ScopeIdParam,
payload: OpenApiSourceBindingInputSchema,
success: OpenApiSourceBindingRef,
error: DomainErrors,
}),
)
.add(
HttpApiEndpoint.post(
"removeSourceBinding",
"/scopes/:scopeId/openapi/source-bindings/remove",
{
params: ScopeIdParam,
payload: RemoveBindingPayload,
success: Schema.Struct({ removed: Schema.Boolean }),
error: DomainErrors,
},
),
)
;