536 lines · 18.5 KB · TypeScript
RawDownload
import { sha256Hex } from "@executor/codemode-core"; import type {  CreateSourcePayload,  UpdateSourcePayload,} from "#api";import type {  AccountId,  AuthArtifact,  CredentialSlot,  Source,  SourceAuth,  SourceImportAuthPolicy,  SourceCatalogAdapterKey,  SourceCatalogId,  SourceCatalogKind,  SourceCatalogRevisionId,  StoredSourceRecord,  StoredSourceCatalogRecord,  StoredSourceCatalogRevisionRecord,  WorkspaceId,} from "#schema";import {  AuthArtifactIdSchema,  ProviderAuthGrantIdSchema,  SourceCatalogIdSchema,  SourceCatalogRevisionIdSchema,} from "#schema";import * as Effect from "effect/Effect"; import { getSourceAdapter, getSourceAdapterForSource } from "./source-adapters";import {  authArtifactFromSourceAuth,  sourceAuthFromAuthArtifact,} from "./auth-artifacts"; const trimOrNull = (value: string | null | undefined): string | null => {  if (value === null || value === undefined) {    return null;  }   const trimmed = value.trim();  return trimmed.length > 0 ? trimmed : null;}; type SourceCatalogSourceConfig = Record<string, unknown>; const sourceConfigFromSource = (source: Source): SourceCatalogSourceConfig =>  getSourceAdapterForSource(source).sourceConfigFromSource(source); const sourceCatalogKindFromSource = (source: Source): SourceCatalogKind => {  const adapter = getSourceAdapterForSource(source);  return adapter.family;}; const sourceCatalogAdapterKeyFromSource = (source: Source): SourceCatalogAdapterKey => {  return getSourceAdapterForSource(source).key;}; const sourceCatalogProviderKeyFromSource = (source: Source): string => {  return getSourceAdapterForSource(source).providerKey;}; const stableHash = (value: string): string =>  sha256Hex(value).slice(0, 24); const sourceCatalogSignature = (source: Source): string =>  JSON.stringify({    catalogKind: sourceCatalogKindFromSource(source),    adapterKey: sourceCatalogAdapterKeyFromSource(source),    providerKey: sourceCatalogProviderKeyFromSource(source),    sourceConfig: sourceConfigFromSource(source),  }); export const sourceConfigSignature = (source: Source): string =>  JSON.stringify(sourceConfigFromSource(source)); export const stableSourceCatalogId = (source: Source): SourceCatalogId =>  SourceCatalogIdSchema.make(`src_catalog_${stableHash(sourceCatalogSignature(source))}`); export const stableSourceCatalogRevisionId = (  source: Source,): SourceCatalogRevisionId =>  SourceCatalogRevisionIdSchema.make(`src_catalog_rev_${stableHash(sourceConfigSignature(source))}`); const normalizeAuth = (  auth: SourceAuth | undefined,): Effect.Effect<SourceAuth, Error, never> =>  Effect.gen(function* () {    if (auth === undefined || auth.kind === "none") {      return { kind: "none" } satisfies SourceAuth;    }     if (auth.kind === "bearer") {      const headerName = trimOrNull(auth.headerName) ?? "Authorization";      const prefix = auth.prefix ?? "Bearer ";      const providerId = trimOrNull(auth.token.providerId);      const handle = trimOrNull(auth.token.handle);      if (providerId === null || handle === null) {        return yield* Effect.fail(new Error("Bearer auth requires a token secret ref"));      }       return {        kind: "bearer",        headerName,        prefix,        token: {          providerId,          handle,        },      } satisfies SourceAuth;    }     if (auth.kind === "oauth2_authorized_user") {      const headerName = trimOrNull(auth.headerName) ?? "Authorization";      const prefix = auth.prefix ?? "Bearer ";      const refreshProviderId = trimOrNull(auth.refreshToken.providerId);      const refreshHandle = trimOrNull(auth.refreshToken.handle);      if (refreshProviderId === null || refreshHandle === null) {        return yield* Effect.fail(          new Error("OAuth2 authorized-user auth requires a refresh token secret ref"),        );      }       let clientSecret: { providerId: string; handle: string } | null = null;      if (auth.clientSecret !== null) {        const clientSecretProviderId = trimOrNull(auth.clientSecret.providerId);        const clientSecretHandle = trimOrNull(auth.clientSecret.handle);        if (clientSecretProviderId === null || clientSecretHandle === null) {          return yield* Effect.fail(            new Error("OAuth2 authorized-user client secret ref must include providerId and handle"),          );        }        clientSecret = {          providerId: clientSecretProviderId,          handle: clientSecretHandle,        };      }       const tokenEndpoint = trimOrNull(auth.tokenEndpoint);      const clientId = trimOrNull(auth.clientId);      if (tokenEndpoint === null || clientId === null) {        return yield* Effect.fail(          new Error("OAuth2 authorized-user auth requires tokenEndpoint and clientId"),        );      }       return {        kind: "oauth2_authorized_user",        headerName,        prefix,        tokenEndpoint,        clientId,        clientAuthentication: auth.clientAuthentication,        clientSecret,        refreshToken: {          providerId: refreshProviderId,          handle: refreshHandle,        },        grantSet: auth.grantSet ?? null,      } satisfies SourceAuth;    }     if (auth.kind === "provider_grant_ref") {      const headerName = trimOrNull(auth.headerName) ?? "Authorization";      const prefix = auth.prefix ?? "Bearer ";      const grantId = trimOrNull(auth.grantId);      if (grantId === null) {        return yield* Effect.fail(          new Error("Provider grant auth requires a grantId"),        );      }       return {        kind: "provider_grant_ref",        grantId: ProviderAuthGrantIdSchema.make(grantId),        providerKey: trimOrNull(auth.providerKey) ?? "",        requiredScopes: auth.requiredScopes          .map((scope) => scope.trim())          .filter((scope) => scope.length > 0),        headerName,        prefix,      } satisfies SourceAuth;    }     if (auth.kind === "mcp_oauth") {      const redirectUri = trimOrNull(auth.redirectUri);      const accessProviderId = trimOrNull(auth.accessToken.providerId);      const accessHandle = trimOrNull(auth.accessToken.handle);      if (redirectUri === null || accessProviderId === null || accessHandle === null) {        return yield* Effect.fail(          new Error("MCP OAuth auth requires redirectUri and access token secret ref"),        );      }       let refreshToken: { providerId: string; handle: string } | null = null;      if (auth.refreshToken !== null) {        const refreshProviderId = trimOrNull(auth.refreshToken.providerId);        const refreshHandle = trimOrNull(auth.refreshToken.handle);        if (refreshProviderId === null || refreshHandle === null) {          return yield* Effect.fail(            new Error("MCP OAuth refresh token ref must include providerId and handle"),          );        }         refreshToken = {          providerId: refreshProviderId,          handle: refreshHandle,        };      }       const tokenType = trimOrNull(auth.tokenType) ?? "Bearer";       return {        kind: "mcp_oauth",        redirectUri,        accessToken: {          providerId: accessProviderId,          handle: accessHandle,        },        refreshToken,        tokenType,        expiresIn: auth.expiresIn ?? null,        scope: trimOrNull(auth.scope),        resourceMetadataUrl: trimOrNull(auth.resourceMetadataUrl),        authorizationServerUrl: trimOrNull(auth.authorizationServerUrl),        resourceMetadataJson: trimOrNull(auth.resourceMetadataJson),        authorizationServerMetadataJson: trimOrNull(auth.authorizationServerMetadataJson),        clientInformationJson: trimOrNull(auth.clientInformationJson),      } satisfies SourceAuth;    }     const headerName = trimOrNull(auth.headerName) ?? "Authorization";    const prefix = auth.prefix ?? "Bearer ";    const accessProviderId = trimOrNull(auth.accessToken.providerId);    const accessHandle = trimOrNull(auth.accessToken.handle);    if (accessProviderId === null || accessHandle === null) {      return yield* Effect.fail(new Error("OAuth2 auth requires an access token secret ref"));    }     let refreshToken: { providerId: string; handle: string } | null = null;    if (auth.refreshToken !== null) {      const refreshProviderId = trimOrNull(auth.refreshToken.providerId);      const refreshHandle = trimOrNull(auth.refreshToken.handle);      if (refreshProviderId === null || refreshHandle === null) {        return yield* Effect.fail(          new Error("OAuth2 refresh token ref must include providerId and handle"),        );      }       refreshToken = {        providerId: refreshProviderId,        handle: refreshHandle,      };    }     return {      kind: "oauth2",      headerName,      prefix,      accessToken: {        providerId: accessProviderId,        handle: accessHandle,      },      refreshToken,    } satisfies SourceAuth;  }); const normalizeImportAuthPolicy = (  sourceKind: Source["kind"],  policy: SourceImportAuthPolicy | undefined,): SourceImportAuthPolicy => policy ?? getSourceAdapter(sourceKind).defaultImportAuthPolicy; const validateSourceImportAuth = (source: Source): Effect.Effect<Source, Error, never> =>  Effect.gen(function* () {    if (source.importAuthPolicy !== "separate" && source.importAuth.kind !== "none") {      return yield* Effect.fail(        new Error("importAuth must be none unless importAuthPolicy is separate"),      );    }     return source;  }); const validateSourceByKind = (source: Source): Effect.Effect<Source, Error, never> =>  Effect.flatMap(    validateSourceImportAuth(source),    (validated) => getSourceAdapterForSource(validated).validateSource(validated),  ); export const createSourceFromPayload = (input: {  workspaceId: WorkspaceId;  sourceId: Source["id"];  payload: CreateSourcePayload;  now: number;}): Effect.Effect<Source, Error, never> =>  Effect.gen(function* () {    const auth = yield* normalizeAuth(input.payload.auth);    const importAuth = yield* normalizeAuth(input.payload.importAuth);    const importAuthPolicy = normalizeImportAuthPolicy(      input.payload.kind,      input.payload.importAuthPolicy,    );     return yield* validateSourceByKind({      id: input.sourceId,      workspaceId: input.workspaceId,      name: input.payload.name.trim(),      kind: input.payload.kind,      endpoint: input.payload.endpoint.trim(),      status: input.payload.status ?? "draft",      enabled: input.payload.enabled ?? true,      namespace: trimOrNull(input.payload.namespace),      bindingVersion: getSourceAdapter(input.payload.kind).bindingConfigVersion,      binding: input.payload.binding ?? {},      importAuthPolicy,      importAuth,      auth,      sourceHash: trimOrNull(input.payload.sourceHash),      lastError: trimOrNull(input.payload.lastError),      createdAt: input.now,      updatedAt: input.now,    });  }); export const updateSourceFromPayload = (input: {  source: Source;  payload: UpdateSourcePayload;  now: number;}): Effect.Effect<Source, Error, never> =>  Effect.gen(function* () {    const nextAuth = input.payload.auth === undefined      ? input.source.auth      : yield* normalizeAuth(input.payload.auth);    const nextImportAuth = input.payload.importAuth === undefined      ? input.source.importAuth      : yield* normalizeAuth(input.payload.importAuth);    const nextImportAuthPolicy = normalizeImportAuthPolicy(      input.source.kind,      input.payload.importAuthPolicy ?? input.source.importAuthPolicy,    );     return yield* validateSourceByKind({      ...input.source,      name: input.payload.name !== undefined ? input.payload.name.trim() : input.source.name,      endpoint:        input.payload.endpoint !== undefined          ? input.payload.endpoint.trim()          : input.source.endpoint,      status: input.payload.status ?? input.source.status,      enabled: input.payload.enabled ?? input.source.enabled,      namespace: input.payload.namespace !== undefined        ? trimOrNull(input.payload.namespace)        : input.source.namespace,      bindingVersion: input.payload.binding !== undefined        ? getSourceAdapter(input.source.kind).bindingConfigVersion        : input.source.bindingVersion,      binding: input.payload.binding !== undefined        ? input.payload.binding        : input.source.binding,      importAuthPolicy: nextImportAuthPolicy,      importAuth: nextImportAuth,      auth: nextAuth,      sourceHash: input.payload.sourceHash !== undefined        ? trimOrNull(input.payload.sourceHash)        : input.source.sourceHash,      lastError: input.payload.lastError !== undefined        ? trimOrNull(input.payload.lastError)        : input.source.lastError,      updatedAt: input.now,    });  }); export const createSourceCatalogRecord = (input: {  source: Source;  catalogId?: SourceCatalogId | null;  latestRevisionId: SourceCatalogRevisionId;}): StoredSourceCatalogRecord => ({  id: input.catalogId ?? stableSourceCatalogId(input.source),  kind: sourceCatalogKindFromSource(input.source),  adapterKey: sourceCatalogAdapterKeyFromSource(input.source),  providerKey: sourceCatalogProviderKeyFromSource(input.source),  name: input.source.name,  summary: null,  visibility: "workspace",  latestRevisionId: input.latestRevisionId,  createdAt: input.source.createdAt,  updatedAt: input.source.updatedAt,}); export const createSourceCatalogRevisionRecord = (input: {  source: Source;  catalogId: SourceCatalogId;  catalogRevisionId?: SourceCatalogRevisionId | null;  revisionNumber: number;  importMetadataJson?: string | null;  importMetadataHash?: string | null;  snapshotHash?: string | null;}): StoredSourceCatalogRevisionRecord => ({  id:    input.catalogRevisionId    ?? stableSourceCatalogRevisionId(input.source),  catalogId: input.catalogId,  revisionNumber: input.revisionNumber,  sourceConfigJson: sourceConfigSignature(input.source),  importMetadataJson: input.importMetadataJson ?? null,  importMetadataHash: input.importMetadataHash ?? null,  snapshotHash: input.snapshotHash ?? null,  createdAt: input.source.createdAt,  updatedAt: input.source.updatedAt,}); export const splitSourceForStorage = (input: {  source: Source;  catalogId: SourceCatalogId;  catalogRevisionId: SourceCatalogRevisionId;  actorAccountId?: AccountId | null;  existingRuntimeAuthArtifactId?: AuthArtifact["id"] | null;  existingImportAuthArtifactId?: AuthArtifact["id"] | null;}): {  sourceRecord: StoredSourceRecord;  runtimeAuthArtifact: AuthArtifact | null;  importAuthArtifact: AuthArtifact | null;} => {  const sourceRecord: StoredSourceRecord = {    id: input.source.id,    workspaceId: input.source.workspaceId,    catalogId: input.catalogId,    catalogRevisionId: input.catalogRevisionId,    name: input.source.name,    kind: input.source.kind,    endpoint: input.source.endpoint,    status: input.source.status,    enabled: input.source.enabled,    namespace: input.source.namespace,    importAuthPolicy: input.source.importAuthPolicy,    bindingConfigJson: getSourceAdapterForSource(input.source).serializeBindingConfig(input.source),    sourceHash: input.source.sourceHash,    lastError: input.source.lastError,    createdAt: input.source.createdAt,    updatedAt: input.source.updatedAt,  };   return {    sourceRecord,    runtimeAuthArtifact: authArtifactFromSourceAuth({      source: input.source,      auth: input.source.auth,      slot: "runtime",      actorAccountId: input.actorAccountId,      existingAuthArtifactId: input.existingRuntimeAuthArtifactId        ?? AuthArtifactIdSchema.make(`auth_art_${crypto.randomUUID()}`),    }),    importAuthArtifact: input.source.importAuthPolicy === "separate"      ? authArtifactFromSourceAuth({          source: input.source,          auth: input.source.importAuth,          slot: "import",          actorAccountId: input.actorAccountId,          existingAuthArtifactId: input.existingImportAuthArtifactId            ?? AuthArtifactIdSchema.make(`auth_art_${crypto.randomUUID()}`),        })      : null,  };}; export const projectSourceFromStorage = (input: {  sourceRecord: StoredSourceRecord;  runtimeAuthArtifact: AuthArtifact | null;  importAuthArtifact: AuthArtifact | null;}): Effect.Effect<Source, Error, never> =>  Effect.gen(function* () {    const adapter = getSourceAdapter(input.sourceRecord.kind);    const bindingConfig = yield* adapter.deserializeBindingConfig({      id: input.sourceRecord.id,      bindingConfigJson: input.sourceRecord.bindingConfigJson,    });     return {      id: input.sourceRecord.id,      workspaceId: input.sourceRecord.workspaceId,      name: input.sourceRecord.name,      kind: input.sourceRecord.kind,      endpoint: input.sourceRecord.endpoint,      status: input.sourceRecord.status,      enabled: input.sourceRecord.enabled,      namespace: input.sourceRecord.namespace,      bindingVersion: bindingConfig.version,      binding: bindingConfig.payload,      importAuthPolicy: input.sourceRecord.importAuthPolicy,      importAuth:        input.sourceRecord.importAuthPolicy === "separate"          ? sourceAuthFromAuthArtifact(input.importAuthArtifact)          : { kind: "none" },      auth: sourceAuthFromAuthArtifact(input.runtimeAuthArtifact),      sourceHash: input.sourceRecord.sourceHash,      lastError: input.sourceRecord.lastError,      createdAt: input.sourceRecord.createdAt,      updatedAt: input.sourceRecord.updatedAt,    } satisfies Source;

Lines 1–500 of 536.