// ---------------------------------------------------------------------------// Connections HTTP API — the v2 credential surface.//// A connection IS the credential: owner-scoped (org | user), bound 1:1 to an// integration, resolving its value through a `CredentialProvider`. Identified by// `(owner, integration, name)`. No scope segments, no token-secret-ids, no// identity-override-by-scope — those v1 concepts are gone.// --------------------------------------------------------------------------- import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";import { Predicate, Schema } from "effect"; import { AuthTemplateSlug, ConnectionAddress, ConnectionName, ConnectionNotFoundError, CredentialProviderNotRegisteredError, IntegrationNotFoundError, IntegrationSlug, InternalError, InvalidConnectionInputError, OAuthClientSlug, Owner, ProviderItemId, ProviderKey,} from "@executor-js/sdk/shared"; // ---------------------------------------------------------------------------// Params — a connection is identified by (owner, integration, name).// --------------------------------------------------------------------------- const ConnectionParams = { owner: Owner, integration: IntegrationSlug, name: ConnectionName,}; // ---------------------------------------------------------------------------// Response schemas — mirrors the SDK's `Connection`.// --------------------------------------------------------------------------- const ConnectionResponse = Schema.Struct({ owner: Owner, name: ConnectionName, integration: IntegrationSlug, template: AuthTemplateSlug, provider: ProviderKey, address: ConnectionAddress, identityLabel: Schema.NullOr(Schema.String), description: Schema.NullOr(Schema.String), expiresAt: Schema.NullOr(Schema.Number), // The OAuth app that minted this connection (its `oauth_client` slug), or null // for static credentials. Lets the UI map a connection back to its app. Just a // slug — never a secret. oauthClient: Schema.NullOr(OAuthClientSlug), oauthClientOwner: Schema.NullOr(Owner), oauthScope: Schema.NullOr(Schema.String),}); const ToolResponse = Schema.Struct({ address: Schema.String, owner: Owner, integration: IntegrationSlug, connection: ConnectionName, name: Schema.String, pluginId: Schema.String, description: Schema.String,}); // ---------------------------------------------------------------------------// Payload schemas// --------------------------------------------------------------------------- // A connection picks exactly one origin: a single pasted `value` (sugar for the// `token` input), a `values` map (one per named input, e.g. both of Datadog's// keys), or an external `from` reference.const CommonCreateFields = { owner: Owner, name: ConnectionName, integration: IntegrationSlug, template: AuthTemplateSlug, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), description: Schema.optional(Schema.NullOr(Schema.String)),} as const; // User-curated metadata edits. Absent field = unchanged; null = cleared.const UpdateConnectionPayload = Schema.Struct({ description: Schema.optional(Schema.NullOr(Schema.String)), identityLabel: Schema.optional(Schema.NullOr(Schema.String)),}); const CreateConnectionPayload = Schema.Struct({ ...CommonCreateFields, value: Schema.optional(Schema.String), values: Schema.optional(Schema.Record(Schema.String, Schema.String)), from: Schema.optional( Schema.Struct({ provider: ProviderKey, id: ProviderItemId, }), ),}).check( Schema.makeFilter((payload) => [payload.value, payload.values, payload.from].filter(Predicate.isNotUndefined).length === 1 ? undefined : "Expected exactly one credential origin", ),); // ---------------------------------------------------------------------------// Query — optional list filters.// --------------------------------------------------------------------------- const ListConnectionsQuery = Schema.Struct({ integration: Schema.optional(IntegrationSlug), owner: Schema.optional(Owner),}); // ---------------------------------------------------------------------------// Error schemas with HTTP status annotations// --------------------------------------------------------------------------- const ConnectionNotFound = ConnectionNotFoundError.annotate({ httpApiStatus: 404,});const IntegrationNotFound = IntegrationNotFoundError.annotate({ httpApiStatus: 404,});const CredentialProviderNotRegistered = CredentialProviderNotRegisteredError.annotate({ httpApiStatus: 409,});const InvalidConnectionInput = InvalidConnectionInputError.annotate({ httpApiStatus: 400,}); // ---------------------------------------------------------------------------// Group// --------------------------------------------------------------------------- export const ConnectionsApi = HttpApiGroup.make("connections") .add( HttpApiEndpoint.get("list", "/connections", { query: ListConnectionsQuery, success: Schema.Array(ConnectionResponse), error: InternalError, }), ) .add( HttpApiEndpoint.post("create", "/connections", { payload: CreateConnectionPayload, success: ConnectionResponse, error: [ InternalError, IntegrationNotFound, CredentialProviderNotRegistered, InvalidConnectionInput, ], }), ) .add( HttpApiEndpoint.get("get", "/connections/:owner/:integration/:name", { params: ConnectionParams, success: ConnectionResponse, error: [InternalError, ConnectionNotFound], }), ) .add( HttpApiEndpoint.patch("update", "/connections/:owner/:integration/:name", { params: ConnectionParams, payload: UpdateConnectionPayload, success: ConnectionResponse, error: [InternalError, ConnectionNotFound], }), ) .add( HttpApiEndpoint.delete("remove", "/connections/:owner/:integration/:name", { params: ConnectionParams, success: Schema.Struct({ removed: Schema.Boolean }), error: [InternalError, ConnectionNotFound], }), ) .add( HttpApiEndpoint.post("refresh", "/connections/:owner/:integration/:name/refresh", { params: ConnectionParams, success: Schema.Array(ToolResponse), error: [InternalError, ConnectionNotFound, IntegrationNotFound], }), );
// ---------------------------------------------------------------------------
// Connections HTTP API — the v2 credential surface.
//
// A connection IS the credential: owner-scoped (org | user), bound 1:1 to an
// integration, resolving its value through a `CredentialProvider`. Identified by
// `(owner, integration, name)`. No scope segments, no token-secret-ids, no
// identity-override-by-scope — those v1 concepts are gone.
// ---------------------------------------------------------------------------
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
import { Predicate, Schema } from "effect";
import {
AuthTemplateSlug,
ConnectionAddress,
ConnectionName,
ConnectionNotFoundError,
CredentialProviderNotRegisteredError,
IntegrationNotFoundError,
IntegrationSlug,
InternalError,
InvalidConnectionInputError,
OAuthClientSlug,
Owner,
ProviderItemId,
ProviderKey,
} from "@executor-js/sdk/shared";
// ---------------------------------------------------------------------------
// Params — a connection is identified by (owner, integration, name).
// ---------------------------------------------------------------------------
const ConnectionParams = {
owner: Owner,
integration: IntegrationSlug,
name: ConnectionName,
};
// ---------------------------------------------------------------------------
// Response schemas — mirrors the SDK's `Connection`.
// ---------------------------------------------------------------------------
const ConnectionResponse = Schema.Struct({
owner: Owner,
name: ConnectionName,
integration: IntegrationSlug,
template: AuthTemplateSlug,
provider: ProviderKey,
address: ConnectionAddress,
identityLabel: Schema.NullOr(Schema.String),
description: Schema.NullOr(Schema.String),
expiresAt: Schema.NullOr(Schema.Number),
// The OAuth app that minted this connection (its `oauth_client` slug), or null
// for static credentials. Lets the UI map a connection back to its app. Just a
// slug — never a secret.
oauthClient: Schema.NullOr(OAuthClientSlug),
oauthClientOwner: Schema.NullOr(Owner),
oauthScope: Schema.NullOr(Schema.String),
});
const ToolResponse = Schema.Struct({
address: Schema.String,
owner: Owner,
integration: IntegrationSlug,
connection: ConnectionName,
name: Schema.String,
pluginId: Schema.String,
description: Schema.String,
});
// ---------------------------------------------------------------------------
// Payload schemas
// ---------------------------------------------------------------------------
// A connection picks exactly one origin: a single pasted `value` (sugar for the
// `token` input), a `values` map (one per named input, e.g. both of Datadog's
// keys), or an external `from` reference.
const CommonCreateFields = {
owner: Owner,
name: ConnectionName,
integration: IntegrationSlug,
template: AuthTemplateSlug,
identityLabel: Schema.optional(Schema.NullOr(Schema.String)),
description: Schema.optional(Schema.NullOr(Schema.String)),
} as const;
// User-curated metadata edits. Absent field = unchanged; null = cleared.
const UpdateConnectionPayload = Schema.Struct({
description: Schema.optional(Schema.NullOr(Schema.String)),
identityLabel: Schema.optional(Schema.NullOr(Schema.String)),
});
const CreateConnectionPayload = Schema.Struct({
...CommonCreateFields,
value: Schema.optional(Schema.String),
values: Schema.optional(Schema.Record(Schema.String, Schema.String)),
from: Schema.optional(
Schema.Struct({
provider: ProviderKey,
id: ProviderItemId,
}),
),
}).check(
Schema.makeFilter((payload) =>
[payload.value, payload.values, payload.from].filter(Predicate.isNotUndefined).length === 1
? undefined
: "Expected exactly one credential origin",
),
);
// ---------------------------------------------------------------------------
// Query — optional list filters.
// ---------------------------------------------------------------------------
const ListConnectionsQuery = Schema.Struct({
integration: Schema.optional(IntegrationSlug),
owner: Schema.optional(Owner),
});
// ---------------------------------------------------------------------------
// Error schemas with HTTP status annotations
// ---------------------------------------------------------------------------
const ConnectionNotFound = ConnectionNotFoundError.annotate({
httpApiStatus: 404,
});
const IntegrationNotFound = IntegrationNotFoundError.annotate({
httpApiStatus: 404,
});
const CredentialProviderNotRegistered = CredentialProviderNotRegisteredError.annotate({
httpApiStatus: 409,
});
const InvalidConnectionInput = InvalidConnectionInputError.annotate({
httpApiStatus: 400,
});
// ---------------------------------------------------------------------------
// Group
// ---------------------------------------------------------------------------
export const ConnectionsApi = HttpApiGroup.make("connections")
.add(
HttpApiEndpoint.get("list", "/connections", {
query: ListConnectionsQuery,
success: Schema.Array(ConnectionResponse),
error: InternalError,
}),
)
.add(
HttpApiEndpoint.post("create", "/connections", {
payload: CreateConnectionPayload,
success: ConnectionResponse,
error: [
InternalError,
IntegrationNotFound,
CredentialProviderNotRegistered,
InvalidConnectionInput,
],
}),
)
.add(
HttpApiEndpoint.get("get", "/connections/:owner/:integration/:name", {
params: ConnectionParams,
success: ConnectionResponse,
error: [InternalError, ConnectionNotFound],
}),
)
.add(
HttpApiEndpoint.patch("update", "/connections/:owner/:integration/:name", {
params: ConnectionParams,
payload: UpdateConnectionPayload,
success: ConnectionResponse,
error: [InternalError, ConnectionNotFound],
}),
)
.add(
HttpApiEndpoint.delete("remove", "/connections/:owner/:integration/:name", {
params: ConnectionParams,
success: Schema.Struct({ removed: Schema.Boolean }),
error: [InternalError, ConnectionNotFound],
}),
)
.add(
HttpApiEndpoint.post("refresh", "/connections/:owner/:integration/:name/refresh", {
params: ConnectionParams,
success: Schema.Array(ToolResponse),
error: [InternalError, ConnectionNotFound, IntegrationNotFound],
}),
);