-
Notifications
You must be signed in to change notification settings - Fork 152
Expand file tree
/
Copy pathGoogleAccountsPanel.tsx
More file actions
118 lines (107 loc) · 4.24 KB
/
Copy pathGoogleAccountsPanel.tsx
File metadata and controls
118 lines (107 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { useCallback, useMemo } from "react";
import { useAtomValue, useAtomSet } from "@effect/atom-react";
import * as Exit from "effect/Exit";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import { AuthTemplateSlug, IntegrationSlug } from "@executor-js/sdk/shared";
import type { IntegrationAccountHandoff } from "@executor-js/sdk/client";
import { TriangleAlert } from "lucide-react";
import { AccountsSection } from "@executor-js/react/components/accounts-section";
import { Alert, AlertDescription, AlertTitle } from "@executor-js/react/components/alert";
import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
import type { AuthMethod, Placement } from "@executor-js/react/lib/auth-placements";
import {
useCustomMethodActions,
type AuthMethodsCodec,
type ConfigureAuthMethods,
} from "@executor-js/react/lib/custom-auth-methods";
import {
authMethodsFromConfig,
openApiWireAuthInput,
templateFromPlacements,
} from "@executor-js/plugin-openapi/react";
import type { Authentication } from "@executor-js/plugin-openapi";
import { googleConfigAtom, googleConfigure } from "./atoms";
import { googleAudienceWarningMessagesForUrls } from "../sdk/presets";
const NO_AUTH_METHOD: AuthMethod = {
id: "none",
label: "No authentication",
kind: "none",
source: "spec",
template: AuthTemplateSlug.make("none"),
placements: [],
};
export default function GoogleAccountsPanel(props: {
readonly sourceId: string;
readonly integrationName: string;
readonly accountHandoff?: IntegrationAccountHandoff | null;
}) {
const { sourceId, integrationName, accountHandoff } = props;
const slug = IntegrationSlug.make(sourceId);
const configResult = useAtomValue(googleConfigAtom(slug));
const doConfigure = useAtomSet(googleConfigure, { mode: "promiseExit" });
const existingTemplate = useMemo<readonly Authentication[]>(() => {
if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];
return (configResult.value.authenticationTemplate ?? []) as readonly Authentication[];
}, [configResult]);
const methods = useMemo<readonly AuthMethod[]>(() => {
const declared = authMethodsFromConfig(existingTemplate);
return declared.length > 0 ? declared : [NO_AUTH_METHOD];
}, [existingTemplate]);
const configure = useCallback<ConfigureAuthMethods<Authentication>>(
async (input) => {
const exit = await doConfigure({
params: { slug },
payload: {
authenticationTemplate: input.authenticationTemplate.map(openApiWireAuthInput),
...(input.mode ? { mode: input.mode } : {}),
},
reactivityKeys: integrationWriteKeys,
});
return Exit.map(exit, (result) => result.authenticationTemplate as readonly Authentication[]);
},
[doConfigure, slug],
);
const codec = useMemo<AuthMethodsCodec<Authentication>>(
() => ({
toAuthMethods: authMethodsFromConfig,
templatesFromPlacements: (placements: readonly Placement[]) => [
templateFromPlacements(placements),
],
slugOf: (template: Authentication) => String(template.slug),
}),
[],
);
const { createCustomMethod, removeCustomMethod } = useCustomMethodActions({
existing: existingTemplate,
codec,
configure,
});
const audienceWarnings = useMemo<readonly string[]>(() => {
if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];
const urls = configResult.value.googleDiscoveryUrls ?? [];
return googleAudienceWarningMessagesForUrls(urls);
}, [configResult]);
return (
<div className="mx-auto max-w-3xl space-y-8 px-6 py-8">
{audienceWarnings.length > 0 && (
<Alert variant="destructive">
<TriangleAlert />
<AlertTitle>Some Google APIs need special consent</AlertTitle>
<AlertDescription>
{audienceWarnings.map((message: string) => (
<p key={message}>{message}</p>
))}
</AlertDescription>
</Alert>
)}
<AccountsSection
integration={slug}
integrationName={integrationName}
methods={methods}
accountHandoff={accountHandoff}
createCustomMethod={createCustomMethod}
removeCustomMethod={removeCustomMethod}
/>
</div>
);
}