Rhys Sullivanfmtc92c57d6 months agoHistory
107 lines · 3.5 KB · TypeScript
RawDownload
import type { ConnectionId, ScopeId } from "@executor-js/sdk/core"; import { oauth2ClientSecretSlot } from "./store";import type { ConfiguredHeaderValue, OpenApiSourceBindingValue } from "./types"; export type BindingRowForCredentialStatus = {  readonly slot: string;  readonly scopeId: ScopeId;  readonly value: OpenApiSourceBindingValue;}; export type SourceForCredentialStatus = {  readonly config: {    readonly headers?: Record<string, ConfiguredHeaderValue>;    readonly oauth2?: {      readonly securitySchemeName: string;      readonly flow: "authorizationCode" | "clientCredentials";      readonly clientIdSlot: string;      readonly clientSecretSlot: string | null;      readonly connectionSlot: string;    };  };}; const scopeRank = (ranks: ReadonlyMap<string, number>, scopeId: ScopeId): number =>  ranks.get(scopeId) ?? Number.MAX_SAFE_INTEGER; export const effectiveBindingForScope = (  rows: readonly BindingRowForCredentialStatus[],  slot: string,  targetScope: ScopeId,  ranks: ReadonlyMap<string, number>,): BindingRowForCredentialStatus | null =>  rows    .filter(      (row) => row.slot === slot && scopeRank(ranks, row.scopeId) >= scopeRank(ranks, targetScope),    )    .sort((a, b) => scopeRank(ranks, a.scopeId) - scopeRank(ranks, b.scopeId))[0] ?? null; const hasSecretBinding = (  rows: readonly BindingRowForCredentialStatus[],  slot: string,  targetScope: ScopeId,  ranks: ReadonlyMap<string, number>,) => effectiveBindingForScope(rows, slot, targetScope, ranks)?.value.kind === "secret"; const hasConnectionBinding = (  rows: readonly BindingRowForCredentialStatus[],  slot: string,  targetScope: ScopeId,  ranks: ReadonlyMap<string, number>,  liveConnectionIds?: ReadonlySet<string>,) => {  const binding = effectiveBindingForScope(rows, slot, targetScope, ranks);  if (binding?.value.kind !== "connection") return false;  return liveConnectionIds ? liveConnectionIds.has(binding.value.connectionId) : true;}; const effectiveClientSecretSlot = (oauth2: {  readonly securitySchemeName: string;  readonly clientSecretSlot: string | null;}): string => oauth2.clientSecretSlot ?? oauth2ClientSecretSlot(oauth2.securitySchemeName); export function missingCredentialLabels(  source: SourceForCredentialStatus,  bindings: readonly BindingRowForCredentialStatus[],  targetScope: ScopeId,  ranks: ReadonlyMap<string, number>,  options?: {    readonly liveConnectionIds?: ReadonlySet<string> | readonly ConnectionId[];  },): string[] {  const missing: string[] = [];  const rawLiveConnectionIds = options?.liveConnectionIds;  const liveConnectionIds = rawLiveConnectionIds    ? rawLiveConnectionIds instanceof Set      ? rawLiveConnectionIds      : new Set(rawLiveConnectionIds)    : undefined;   for (const [headerName, value] of Object.entries(source.config.headers ?? {})) {    if (typeof value === "string") continue;    if (!hasSecretBinding(bindings, value.slot, targetScope, ranks)) {      missing.push(headerName);    }  }   const oauth2 = source.config.oauth2;  if (!oauth2) return missing;   if (!hasSecretBinding(bindings, oauth2.clientIdSlot, targetScope, ranks)) {    missing.push("Client ID");  }   const clientSecretSlot = effectiveClientSecretSlot(oauth2);  if (!hasSecretBinding(bindings, clientSecretSlot, targetScope, ranks)) {    missing.push("Client Secret");  }   if (    !hasConnectionBinding(bindings, oauth2.connectionSlot, targetScope, ranks, liveConnectionIds)  ) {    missing.push(oauth2.flow === "clientCredentials" ? "OAuth client connection" : "OAuth sign-in");  }   return missing;}