import { useAtomValue } from "@effect/atom-react"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { connectionsAtom, sourceAtom } from "@executor-js/react/api/atoms"; import { Badge } from "@executor-js/react/components/badge"; import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context"; import { ScopeId } from "@executor-js/sdk/core"; import { SourceCredentialNotice, SourceCredentialStatusBadge, missingSourceCredentialLabels, type SourceCredentialSlot, } from "@executor-js/react/plugins/source-credential-status"; import { openApiSourceAtom, openApiSourceBindingsAtom } from "./atoms"; import { effectiveBindingForScope } from "../sdk/credential-status"; import { oauth2ClientSecretSlot, type StoredSourceSchemaType } from "../sdk/store"; function OAuthBadge() { return OAuth; } function CheckingCredentialsBadge() { return ( Checking credentials ); } const effectiveClientSecretSlot = (oauth2: { readonly securitySchemeName: string; readonly clientSecretSlot: string | null; }): string => oauth2.clientSecretSlot ?? oauth2ClientSecretSlot(oauth2.securitySchemeName); const sourceCredentialSlots = (source: StoredSourceSchemaType): readonly SourceCredentialSlot[] => { const slots: SourceCredentialSlot[] = []; for (const [name, value] of Object.entries(source.config.headers ?? {})) { if (typeof value !== "string") slots.push({ kind: "secret", slot: value.slot, label: name }); } for (const [name, value] of Object.entries(source.config.queryParams ?? {})) { if (typeof value !== "string") slots.push({ kind: "secret", slot: value.slot, label: name }); } const oauth2 = source.config.oauth2; if (oauth2) { slots.push({ kind: "secret", slot: oauth2.clientIdSlot, label: "Client ID" }); slots.push({ kind: "secret", slot: effectiveClientSecretSlot(oauth2), label: "Client Secret", }); slots.push({ kind: "connection", slot: oauth2.connectionSlot, label: oauth2.flow === "clientCredentials" ? "OAuth client connection" : "OAuth sign-in", }); } return slots; }; // The entry row already renders name + id + kind, so this summary // component only contributes extras — specifically, an OAuth status // badge when the source has OAuth2 configured. Non-OAuth sources // render nothing. export default function OpenApiSourceSummary(props: { sourceId: string; variant?: "badge" | "panel"; onAction?: () => void; }) { const displayScope = useScope(); const userScope = useUserScope(); const scopeStack = useScopeStack(); const summaryResult = useAtomValue(sourceAtom(props.sourceId, displayScope)); const sourceScopeId = AsyncResult.isSuccess(summaryResult) && summaryResult.value?.scopeId ? summaryResult.value.scopeId : displayScope; const sourceResult = useAtomValue(openApiSourceAtom(ScopeId.make(sourceScopeId), props.sourceId)); const bindingsResult = useAtomValue( openApiSourceBindingsAtom(displayScope, props.sourceId, ScopeId.make(sourceScopeId)), ); const connectionsResult = useAtomValue(connectionsAtom(displayScope)); const source = AsyncResult.isSuccess(sourceResult) && sourceResult.value ? sourceResult.value : null; if (!source) return null; const oauth2 = source.config.oauth2; const bindingsLoaded = AsyncResult.isSuccess(bindingsResult); const connectionsLoaded = AsyncResult.isSuccess(connectionsResult); if (!bindingsLoaded) { return props.variant === "panel" ? null : ; } const bindings = AsyncResult.isSuccess(bindingsResult) ? bindingsResult.value : []; if (oauth2 && !connectionsLoaded) { return props.variant === "panel" ? null : ; } const connections = AsyncResult.isSuccess(connectionsResult) ? connectionsResult.value : []; const liveConnectionIds = new Set(connections.map((connection) => connection.id)); const scopeRanks = new Map(scopeStack.map((scope, index) => [scope.id, index] as const)); const credentialTargetScope = userScope; const missing = missingSourceCredentialLabels({ slots: sourceCredentialSlots(source), bindings, targetScope: credentialTargetScope, scopeRanks, liveConnectionIds, }); if (props.variant === "panel") { return ; } if (missing.length > 0) return ; if (!oauth2) return null; const connectionBinding = effectiveBindingForScope( bindings, oauth2.connectionSlot, credentialTargetScope, scopeRanks, ); const connectionId = connectionBinding && connectionBinding.value.kind === "connection" ? connectionBinding.value.connectionId : null; if (connectionId && connections.some((connection) => connection.id === connectionId)) { return ; } return ; }