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 ;
}