import { useState } from "react"; import { AuthTemplateSlug, IntegrationSlug, OAuthClientSlug, type Connection, type AuthTemplateSlug as AuthTemplateSlugType, } from "@executor-js/sdk/shared"; import { OAuthSignInButton, useOAuthPopupFlow, type OAuthCompletionPayload, } from "@executor-js/react/plugins/oauth-sign-in"; import { ConnectionOwnerDropdown, useConnectionOwner, } from "@executor-js/react/plugins/connection-owner"; import { useOwnerDisplay } from "@executor-js/react/api/owner-display"; import { graphqlConnectionName } from "./defaults"; // v2 OAuth sign-in. `oauth.start` runs a registered OAuth client for one // integration + template, minting an owner-scoped connection (the redirect is // driven through the popup; `state` correlates it). There is no per-source // connection slot or `oauthConnectionId` anymore — the connection is owned by // the chosen owner and identified by (owner, integration, name). const GRAPHQL_OAUTH_CLIENT = "graphql-oauth"; export default function GraphqlSignInButton(props: { readonly slug: IntegrationSlug; readonly template: AuthTemplateSlugType; readonly displayName: string; readonly existing: readonly Connection[]; }) { const { connectionOwner, setConnectionOwner, connectionOwnerOptions } = useConnectionOwner(); const oauth = useOAuthPopupFlow({ popupName: "graphql-oauth", startErrorMessage: "Failed to start OAuth", }); const ownerDisplay = useOwnerDisplay(); const [connectedOwner, setConnectedOwner] = useState(null); const existingForOwner = props.existing.find( (connection) => connection.owner === connectionOwner && connection.template === props.template, ); const isConnected = existingForOwner !== undefined || connectedOwner === connectionOwner; const handleSignIn = (): void => { setConnectedOwner(null); void oauth.start({ payload: { client: OAuthClientSlug.make(GRAPHQL_OAUTH_CLIENT), // GraphQL manages its own client per owner — the app and connection // share one owner. clientOwner: connectionOwner, owner: connectionOwner, name: graphqlConnectionName(String(props.slug), connectionOwner), integration: IntegrationSlug.make(String(props.slug)), template: AuthTemplateSlug.make(String(props.template)), identityLabel: `${props.displayName} OAuth`, }, onSuccess: (payload: OAuthCompletionPayload) => { // Touch the minted connection name to satisfy the success contract; the // connection list re-reads via reactivity keys after the flow completes. void payload.name; setConnectedOwner(connectionOwner); }, }); }; return (
{isConnected && (

Connected in {ownerDisplay.label(connectionOwner)} {existingForOwner?.identityLabel ? ` as ${existingForOwner.identityLabel}` : ""}.

)} { setConnectionOwner(owner); setConnectedOwner(null); }} label="Connection saved to" help="Choose who can use the OAuth connection." />
); }