@executor-js/plugin-openapi 1.4.32 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AddOpenApiSource-7M52SRUX.js +893 -0
- package/dist/AddOpenApiSource-7M52SRUX.js.map +1 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js +68 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js.map +1 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js +112 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js.map +1 -0
- package/dist/api/group.d.ts +104 -225
- package/dist/api/index.d.ts +127 -271
- package/dist/{chunk-BB5IAKRG.js → chunk-AQ7JDDRM.js} +12 -2
- package/dist/chunk-AQ7JDDRM.js.map +1 -0
- package/dist/chunk-BSLE6HCE.js +181 -0
- package/dist/chunk-BSLE6HCE.js.map +1 -0
- package/dist/chunk-MZWZQ24W.js +226 -0
- package/dist/chunk-MZWZQ24W.js.map +1 -0
- package/dist/chunk-OSIFYIQP.js +623 -0
- package/dist/chunk-OSIFYIQP.js.map +1 -0
- package/dist/chunk-QSSRVK6M.js +139 -0
- package/dist/chunk-QSSRVK6M.js.map +1 -0
- package/dist/chunk-V7VCHYOY.js +1544 -0
- package/dist/chunk-V7VCHYOY.js.map +1 -0
- package/dist/{chunk-AN4HJFNP.js → chunk-YVRI7KRC.js} +162 -186
- package/dist/chunk-YVRI7KRC.js.map +1 -0
- package/dist/client.js +9 -8
- package/dist/client.js.map +1 -1
- package/dist/core.js +28 -11
- package/dist/index.js +11 -4
- package/dist/react/AddOpenApiSource.d.ts +2 -13
- package/dist/react/GoogleProductPicker.d.ts +9 -0
- package/dist/react/OpenApiAccountsPanel.d.ts +6 -0
- package/dist/react/OpenApiSourceDetailsFields.d.ts +3 -2
- package/dist/react/atoms.d.ts +177 -192
- package/dist/react/auth-method-config.d.ts +15 -0
- package/dist/react/client.d.ts +103 -224
- package/dist/react/index.d.ts +2 -2
- package/dist/react/source-plugin.d.ts +2 -2
- package/dist/sdk/config.d.ts +75 -0
- package/dist/sdk/configure.test.d.ts +1 -0
- package/dist/sdk/describe-auth-methods.test.d.ts +1 -0
- package/dist/sdk/errors.d.ts +4 -6
- package/dist/sdk/extract.d.ts +7 -5
- package/dist/sdk/google-bundle.test.d.ts +1 -0
- package/dist/sdk/google-discovery.d.ts +43 -0
- package/dist/sdk/google-discovery.test.d.ts +1 -0
- package/dist/sdk/google-oauth-batches.d.ts +13 -0
- package/dist/sdk/google-oauth-batches.test.d.ts +1 -0
- package/dist/sdk/google-oauth-scopes.d.ts +3 -0
- package/dist/sdk/google-oauth-scopes.test.d.ts +1 -0
- package/dist/sdk/google-presets.d.ts +16 -0
- package/dist/sdk/google-presets.test.d.ts +1 -0
- package/dist/sdk/google-product-picker-scopes.test.d.ts +1 -0
- package/dist/sdk/index.d.ts +7 -5
- package/dist/sdk/invoke.d.ts +6 -9
- package/dist/sdk/openapi-utils.d.ts +1 -0
- package/dist/sdk/plugin.d.ts +74 -231
- package/dist/sdk/presets.d.ts +2 -1
- package/dist/sdk/preview.d.ts +20 -15
- package/dist/sdk/query-serialization.test.d.ts +1 -0
- package/dist/sdk/store.d.ts +14 -41
- package/dist/sdk/types.d.ts +23 -51
- package/dist/testing/index.d.ts +49 -38
- package/dist/testing.js +46 -18
- package/dist/testing.js.map +1 -1
- package/package.json +6 -4
- package/dist/AddOpenApiSource-NSCULGTM.js +0 -19
- package/dist/AddOpenApiSource-NSCULGTM.js.map +0 -1
- package/dist/EditOpenApiSource-MV7NYTRP.js +0 -774
- package/dist/EditOpenApiSource-MV7NYTRP.js.map +0 -1
- package/dist/OpenApiSourceSummary-7JBS7PUV.js +0 -122
- package/dist/OpenApiSourceSummary-7JBS7PUV.js.map +0 -1
- package/dist/chunk-2ZKKZYZH.js +0 -1181
- package/dist/chunk-2ZKKZYZH.js.map +0 -1
- package/dist/chunk-AN4HJFNP.js.map +0 -1
- package/dist/chunk-BB5IAKRG.js.map +0 -1
- package/dist/chunk-PS5XP3DG.js +0 -2086
- package/dist/chunk-PS5XP3DG.js.map +0 -1
- package/dist/chunk-X5JX3KTA.js +0 -201
- package/dist/chunk-X5JX3KTA.js.map +0 -1
- package/dist/react/OpenApiSourceSummary.d.ts +0 -5
- package/dist/sdk/credential-status.d.ts +0 -23
- package/dist/sdk/source-contracts.d.ts +0 -55
- /package/dist/{sdk/credential-status.test.d.ts → react/auth-method-config.test.d.ts} +0 -0
package/dist/chunk-2ZKKZYZH.js
DELETED
|
@@ -1,1181 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
addOpenApiSpecOptimistic,
|
|
3
|
-
previewOpenApiSpec
|
|
4
|
-
} from "./chunk-X5JX3KTA.js";
|
|
5
|
-
import {
|
|
6
|
-
OAuth2SourceConfig,
|
|
7
|
-
expandServerUrlOptions,
|
|
8
|
-
headerBindingSlot,
|
|
9
|
-
oauth2ClientIdSlot,
|
|
10
|
-
oauth2ClientSecretSlot,
|
|
11
|
-
oauth2ConnectionSlot,
|
|
12
|
-
queryParamBindingSlot,
|
|
13
|
-
specFetchHeaderBindingSlot,
|
|
14
|
-
specFetchQueryParamBindingSlot
|
|
15
|
-
} from "./chunk-AN4HJFNP.js";
|
|
16
|
-
|
|
17
|
-
// src/react/AddOpenApiSource.tsx
|
|
18
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
19
|
-
import { useAtomSet } from "@effect/atom-react";
|
|
20
|
-
import * as Effect from "effect/Effect";
|
|
21
|
-
import * as Exit from "effect/Exit";
|
|
22
|
-
import * as Match from "effect/Match";
|
|
23
|
-
import * as Option from "effect/Option";
|
|
24
|
-
import * as Schema from "effect/Schema";
|
|
25
|
-
import {
|
|
26
|
-
ConnectionId,
|
|
27
|
-
ScopeId,
|
|
28
|
-
SecretId,
|
|
29
|
-
SetSourceCredentialBindingInput
|
|
30
|
-
} from "@executor-js/sdk/shared";
|
|
31
|
-
import { setSourceCredentialBinding, startOAuth } from "@executor-js/react/api/atoms";
|
|
32
|
-
import { useScope, useScopeStack } from "@executor-js/react/api/scope-context";
|
|
33
|
-
import { connectionWriteKeys, sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";
|
|
34
|
-
import { HeadersList } from "@executor-js/react/plugins/headers-list";
|
|
35
|
-
import {
|
|
36
|
-
HttpCredentialsEditor,
|
|
37
|
-
emptyHttpCredentials,
|
|
38
|
-
serializeHttpCredentials
|
|
39
|
-
} from "@executor-js/react/plugins/http-credentials";
|
|
40
|
-
import {
|
|
41
|
-
oauthCallbackUrl,
|
|
42
|
-
useOAuthPopupFlow
|
|
43
|
-
} from "@executor-js/react/plugins/oauth-sign-in";
|
|
44
|
-
import {
|
|
45
|
-
CreatableSecretPicker,
|
|
46
|
-
matchPresetKey
|
|
47
|
-
} from "@executor-js/react/plugins/secret-header-auth";
|
|
48
|
-
import { CredentialScopeDropdown } from "@executor-js/react/plugins/credential-target-scope";
|
|
49
|
-
import { slugifyNamespace, useSourceIdentity } from "@executor-js/react/plugins/source-identity";
|
|
50
|
-
import { useSecretPickerSecrets } from "@executor-js/react/plugins/use-secret-picker-secrets";
|
|
51
|
-
import { Button } from "@executor-js/react/components/button";
|
|
52
|
-
import { CopyButton } from "@executor-js/react/components/copy-button";
|
|
53
|
-
import {
|
|
54
|
-
Collapsible,
|
|
55
|
-
CollapsibleContent,
|
|
56
|
-
CollapsibleTrigger
|
|
57
|
-
} from "@executor-js/react/components/collapsible";
|
|
58
|
-
import {
|
|
59
|
-
CardStack as CardStack2,
|
|
60
|
-
CardStackContent as CardStackContent2,
|
|
61
|
-
CardStackEntryField as CardStackEntryField2
|
|
62
|
-
} from "@executor-js/react/components/card-stack";
|
|
63
|
-
import { FieldLabel } from "@executor-js/react/components/field";
|
|
64
|
-
import { FloatActions } from "@executor-js/react/components/float-actions";
|
|
65
|
-
import { HelpTooltip } from "@executor-js/react/components/help-tooltip";
|
|
66
|
-
import { Label } from "@executor-js/react/components/label";
|
|
67
|
-
import { Textarea } from "@executor-js/react/components/textarea";
|
|
68
|
-
import { Checkbox } from "@executor-js/react/components/checkbox";
|
|
69
|
-
import { RadioGroup, RadioGroupItem } from "@executor-js/react/components/radio-group";
|
|
70
|
-
import { IOSSpinner, Spinner } from "@executor-js/react/components/spinner";
|
|
71
|
-
|
|
72
|
-
// src/react/OpenApiSourceDetailsFields.tsx
|
|
73
|
-
import {
|
|
74
|
-
CardStack,
|
|
75
|
-
CardStackContent,
|
|
76
|
-
CardStackEntry,
|
|
77
|
-
CardStackEntryContent,
|
|
78
|
-
CardStackEntryDescription,
|
|
79
|
-
CardStackEntryField,
|
|
80
|
-
CardStackEntryTitle
|
|
81
|
-
} from "@executor-js/react/components/card-stack";
|
|
82
|
-
import {
|
|
83
|
-
FreeformCombobox
|
|
84
|
-
} from "@executor-js/react/components/combobox";
|
|
85
|
-
import { Input } from "@executor-js/react/components/input";
|
|
86
|
-
import { SourceFavicon } from "@executor-js/react/components/source-favicon";
|
|
87
|
-
import {
|
|
88
|
-
SourceIdentityFieldRows
|
|
89
|
-
} from "@executor-js/react/plugins/source-identity";
|
|
90
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
91
|
-
function OpenApiSourceDetailsFields(props) {
|
|
92
|
-
const baseUrlOptions = props.baseUrlOptions ?? [];
|
|
93
|
-
return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
|
|
94
|
-
/* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
95
|
-
props.faviconUrl && /* @__PURE__ */ jsx(SourceFavicon, { url: props.faviconUrl, size: 16 }),
|
|
96
|
-
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
97
|
-
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.title }),
|
|
98
|
-
props.description && /* @__PURE__ */ jsx(CardStackEntryDescription, { children: props.description })
|
|
99
|
-
] }),
|
|
100
|
-
props.saveState && props.saveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: props.saveState === "saving" ? "Saving\u2026" : "Saved" })
|
|
101
|
-
] }),
|
|
102
|
-
/* @__PURE__ */ jsx(
|
|
103
|
-
SourceIdentityFieldRows,
|
|
104
|
-
{
|
|
105
|
-
identity: props.identity,
|
|
106
|
-
namespaceReadOnly: props.namespaceReadOnly
|
|
107
|
-
}
|
|
108
|
-
),
|
|
109
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2", children: [
|
|
110
|
-
/* @__PURE__ */ jsxs(CardStackEntryField, { label: "Base URL", children: [
|
|
111
|
-
baseUrlOptions.length > 0 ? /* @__PURE__ */ jsx(
|
|
112
|
-
FreeformCombobox,
|
|
113
|
-
{
|
|
114
|
-
value: props.baseUrl,
|
|
115
|
-
onValueChange: props.onBaseUrlChange,
|
|
116
|
-
options: baseUrlOptions,
|
|
117
|
-
placeholder: "https://api.example.com",
|
|
118
|
-
className: "w-full",
|
|
119
|
-
inputClassName: "font-mono text-sm"
|
|
120
|
-
}
|
|
121
|
-
) : /* @__PURE__ */ jsx(
|
|
122
|
-
Input,
|
|
123
|
-
{
|
|
124
|
-
value: props.baseUrl,
|
|
125
|
-
onChange: (e) => props.onBaseUrlChange(e.target.value),
|
|
126
|
-
placeholder: "https://api.example.com",
|
|
127
|
-
className: "font-mono text-sm"
|
|
128
|
-
}
|
|
129
|
-
),
|
|
130
|
-
props.baseUrlMissingMessage && !props.baseUrl && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-amber-600 dark:text-amber-400", children: props.baseUrlMissingMessage })
|
|
131
|
-
] }),
|
|
132
|
-
props.specUrl !== void 0 && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec URL", children: /* @__PURE__ */ jsx(
|
|
133
|
-
Input,
|
|
134
|
-
{
|
|
135
|
-
value: props.specUrl,
|
|
136
|
-
onChange: (e) => props.onSpecUrlChange?.(e.target.value),
|
|
137
|
-
placeholder: "https://api.example.com/openapi.json",
|
|
138
|
-
className: "font-mono text-sm",
|
|
139
|
-
disabled: props.specUrlDisabled
|
|
140
|
-
}
|
|
141
|
-
) })
|
|
142
|
-
] }),
|
|
143
|
-
props.footer && /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.footer }) }) })
|
|
144
|
-
] }) });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// src/react/AddOpenApiSource.tsx
|
|
148
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
149
|
-
var addSpecWriteKeys = [...sourceWriteKeys, ...connectionWriteKeys];
|
|
150
|
-
var bindingWriteKeys = [...sourceWriteKeys, ...connectionWriteKeys];
|
|
151
|
-
var OPENAPI_OAUTH_POPUP_NAME = "openapi-oauth";
|
|
152
|
-
var OPENAPI_OAUTH_CALLBACK_PATH = "/api/oauth/callback";
|
|
153
|
-
var ErrorMessage = Schema.Struct({ message: Schema.String });
|
|
154
|
-
var decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);
|
|
155
|
-
var errorMessageFromExit = (exit, fallback) => Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), {
|
|
156
|
-
onNone: () => fallback,
|
|
157
|
-
onSome: ({ message }) => message
|
|
158
|
-
});
|
|
159
|
-
var openApiOAuthConnectionId = (namespaceSlug, flow) => flow === "clientCredentials" ? `openapi-oauth2-app-${namespaceSlug || "default"}` : `openapi-oauth2-user-${namespaceSlug || "default"}`;
|
|
160
|
-
function resolveOAuthUrl(url, baseUrl) {
|
|
161
|
-
if (!url) return url;
|
|
162
|
-
try {
|
|
163
|
-
new URL(url);
|
|
164
|
-
return url;
|
|
165
|
-
} catch {
|
|
166
|
-
if (!baseUrl) return url;
|
|
167
|
-
try {
|
|
168
|
-
return new URL(url, baseUrl).toString();
|
|
169
|
-
} catch {
|
|
170
|
-
return url;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function inferOAuthIssuerUrl(authorizationUrl) {
|
|
175
|
-
try {
|
|
176
|
-
return new URL(authorizationUrl).origin;
|
|
177
|
-
} catch {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
var specInputForAdd = (input) => {
|
|
182
|
-
const value2 = input.trim();
|
|
183
|
-
const parsed = Effect.runSyncExit(
|
|
184
|
-
Effect.try({
|
|
185
|
-
try: () => new URL(value2),
|
|
186
|
-
catch: () => null
|
|
187
|
-
})
|
|
188
|
-
);
|
|
189
|
-
return Exit.isSuccess(parsed) ? { kind: "url", url: value2 } : { kind: "blob", value: value2 };
|
|
190
|
-
};
|
|
191
|
-
var serializeStrategy = (s) => Match.value(s).pipe(
|
|
192
|
-
Match.when({ kind: "none" }, () => "none"),
|
|
193
|
-
Match.when({ kind: "custom" }, () => "custom"),
|
|
194
|
-
Match.when({ kind: "header" }, (sel) => `header:${sel.presetIndex}`),
|
|
195
|
-
Match.when({ kind: "oauth2" }, (sel) => `oauth2:${sel.presetIndex}`),
|
|
196
|
-
Match.exhaustive
|
|
197
|
-
);
|
|
198
|
-
var parseStrategy = (value2) => {
|
|
199
|
-
if (value2 === "none") return { kind: "none" };
|
|
200
|
-
if (value2 === "custom") return { kind: "custom" };
|
|
201
|
-
if (value2.startsWith("header:")) {
|
|
202
|
-
return {
|
|
203
|
-
kind: "header",
|
|
204
|
-
presetIndex: Number(value2.slice("header:".length))
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
if (value2.startsWith("oauth2:")) {
|
|
208
|
-
return {
|
|
209
|
-
kind: "oauth2",
|
|
210
|
-
presetIndex: Number(value2.slice("oauth2:".length))
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
return { kind: "none" };
|
|
214
|
-
};
|
|
215
|
-
function prefixForHeader(preset, headerName) {
|
|
216
|
-
const label = preset.label.toLowerCase();
|
|
217
|
-
if (headerName.toLowerCase() === "authorization") {
|
|
218
|
-
if (label.includes("bearer")) return "Bearer ";
|
|
219
|
-
if (label.includes("basic")) return "Basic ";
|
|
220
|
-
}
|
|
221
|
-
return void 0;
|
|
222
|
-
}
|
|
223
|
-
function entriesFromSpecPreset(preset) {
|
|
224
|
-
return preset.secretHeaders.map((headerName) => {
|
|
225
|
-
const prefix = prefixForHeader(preset, headerName);
|
|
226
|
-
return {
|
|
227
|
-
name: headerName,
|
|
228
|
-
secretId: null,
|
|
229
|
-
prefix,
|
|
230
|
-
presetKey: matchPresetKey(headerName, prefix),
|
|
231
|
-
fromPreset: true
|
|
232
|
-
};
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
var secretStorageDescription = (label) => label === "Personal" ? "Only you can use this secret." : "Everyone in the organization can use this secret.";
|
|
236
|
-
function AddOpenApiSource(props) {
|
|
237
|
-
const [specUrl, setSpecUrl] = useState(props.initialUrl ?? "");
|
|
238
|
-
const [analyzing, setAnalyzing] = useState(false);
|
|
239
|
-
const [analyzeError, setAnalyzeError] = useState(null);
|
|
240
|
-
const [preview, setPreview] = useState(null);
|
|
241
|
-
const [baseUrl, setBaseUrl] = useState("");
|
|
242
|
-
const identity = useSourceIdentity({
|
|
243
|
-
fallbackName: preview ? Option.getOrElse(preview.title, () => "") : "",
|
|
244
|
-
fallbackNamespace: props.initialNamespace
|
|
245
|
-
});
|
|
246
|
-
const [strategy, setStrategy] = useState({ kind: "none" });
|
|
247
|
-
const [customHeaders, setCustomHeaders] = useState([]);
|
|
248
|
-
const [specFetchCredentials, setSpecFetchCredentials] = useState(
|
|
249
|
-
() => emptyHttpCredentials()
|
|
250
|
-
);
|
|
251
|
-
const [specFetchCredentialsOpen, setSpecFetchCredentialsOpen] = useState(false);
|
|
252
|
-
const [runtimeCredentials, setRuntimeCredentials] = useState(
|
|
253
|
-
() => emptyHttpCredentials()
|
|
254
|
-
);
|
|
255
|
-
const [oauth2ClientIdSecretId, setOauth2ClientIdSecretId] = useState(null);
|
|
256
|
-
const [oauth2ClientSecretSecretId, setOauth2ClientSecretSecretId] = useState(null);
|
|
257
|
-
const [oauth2ClientIdScope, setOauth2ClientIdScope] = useState(null);
|
|
258
|
-
const [oauth2ClientSecretScope, setOauth2ClientSecretScope] = useState(null);
|
|
259
|
-
const [oauth2SelectedScopes, setOauth2SelectedScopes] = useState(/* @__PURE__ */ new Set());
|
|
260
|
-
const [oauth2AuthState, setOauth2AuthState] = useState(null);
|
|
261
|
-
const [startingOAuth, setStartingOAuth] = useState(false);
|
|
262
|
-
const [oauth2Error, setOauth2Error] = useState(null);
|
|
263
|
-
const [adding, setAdding] = useState(false);
|
|
264
|
-
const [addError, setAddError] = useState(null);
|
|
265
|
-
const scopeId = useScope();
|
|
266
|
-
const scopeStack = useScopeStack();
|
|
267
|
-
const credentialScopeOptions = useMemo(
|
|
268
|
-
() => scopeStack.map((entry, index) => ({
|
|
269
|
-
scopeId: entry.id,
|
|
270
|
-
label: index === 0 ? "Personal" : entry.name || "Organization",
|
|
271
|
-
description: secretStorageDescription(
|
|
272
|
-
index === 0 ? "Personal" : entry.name || "Organization"
|
|
273
|
-
)
|
|
274
|
-
})),
|
|
275
|
-
[scopeStack]
|
|
276
|
-
);
|
|
277
|
-
const defaultOAuthTokenTargetScope = credentialScopeOptions[0]?.scopeId ?? scopeId;
|
|
278
|
-
const [oauthTokenTargetScope, setOAuthTokenTargetScope] = useState(
|
|
279
|
-
defaultOAuthTokenTargetScope
|
|
280
|
-
);
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
if (!credentialScopeOptions.some((option) => option.scopeId === oauthTokenTargetScope)) {
|
|
283
|
-
setOAuthTokenTargetScope(defaultOAuthTokenTargetScope);
|
|
284
|
-
}
|
|
285
|
-
}, [credentialScopeOptions, defaultOAuthTokenTargetScope, oauthTokenTargetScope]);
|
|
286
|
-
useEffect(() => {
|
|
287
|
-
if (oauth2ClientIdScope && !credentialScopeOptions.some((option) => option.scopeId === oauth2ClientIdScope)) {
|
|
288
|
-
setOauth2ClientIdScope(null);
|
|
289
|
-
setOauth2ClientIdSecretId(null);
|
|
290
|
-
setOauth2AuthState(null);
|
|
291
|
-
}
|
|
292
|
-
if (oauth2ClientSecretScope && !credentialScopeOptions.some((option) => option.scopeId === oauth2ClientSecretScope)) {
|
|
293
|
-
setOauth2ClientSecretScope(null);
|
|
294
|
-
setOauth2ClientSecretSecretId(null);
|
|
295
|
-
setOauth2AuthState(null);
|
|
296
|
-
}
|
|
297
|
-
}, [credentialScopeOptions, oauth2ClientIdScope, oauth2ClientSecretScope]);
|
|
298
|
-
const doPreview = useAtomSet(previewOpenApiSpec, { mode: "promiseExit" });
|
|
299
|
-
const doAdd = useAtomSet(addOpenApiSpecOptimistic(scopeId), {
|
|
300
|
-
mode: "promiseExit"
|
|
301
|
-
});
|
|
302
|
-
const doStartOAuth = useAtomSet(startOAuth, { mode: "promiseExit" });
|
|
303
|
-
const doSetBinding = useAtomSet(setSourceCredentialBinding, {
|
|
304
|
-
mode: "promiseExit"
|
|
305
|
-
});
|
|
306
|
-
const secretList = useSecretPickerSecrets();
|
|
307
|
-
const oauth = useOAuthPopupFlow({
|
|
308
|
-
popupName: OPENAPI_OAUTH_POPUP_NAME,
|
|
309
|
-
popupBlockedMessage: "OAuth popup was blocked by the browser",
|
|
310
|
-
popupClosedMessage: "OAuth cancelled - popup was closed before completing the flow.",
|
|
311
|
-
startErrorMessage: "Failed to start OAuth"
|
|
312
|
-
});
|
|
313
|
-
const handleAnalyzeRef = useRef(() => {
|
|
314
|
-
});
|
|
315
|
-
useEffect(() => {
|
|
316
|
-
const trimmed = specUrl.trim();
|
|
317
|
-
if (!trimmed) return;
|
|
318
|
-
if (preview) return;
|
|
319
|
-
const handle = setTimeout(() => {
|
|
320
|
-
handleAnalyzeRef.current();
|
|
321
|
-
}, 400);
|
|
322
|
-
return () => clearTimeout(handle);
|
|
323
|
-
}, [specUrl, preview]);
|
|
324
|
-
const expandServerOptions = (server) => {
|
|
325
|
-
return expandServerUrlOptions(server).map((value2) => ({
|
|
326
|
-
value: value2,
|
|
327
|
-
label: value2
|
|
328
|
-
}));
|
|
329
|
-
};
|
|
330
|
-
const servers = preview?.servers ?? [];
|
|
331
|
-
const baseUrlOptions = Array.from(
|
|
332
|
-
new Map(servers.flatMap(expandServerOptions).map((option) => [option.value, option])).values()
|
|
333
|
-
);
|
|
334
|
-
const resolvedBaseUrl = baseUrl.trim();
|
|
335
|
-
const sourceScope = ScopeId.make(scopeId);
|
|
336
|
-
const configuredHeaders = {};
|
|
337
|
-
const headerBindings = [];
|
|
338
|
-
const configuredQueryParams = {};
|
|
339
|
-
const queryParamBindings = [];
|
|
340
|
-
for (const ch of customHeaders) {
|
|
341
|
-
if (!ch.name.trim()) continue;
|
|
342
|
-
const slot = headerBindingSlot(ch.name.trim());
|
|
343
|
-
configuredHeaders[ch.name.trim()] = { kind: "secret", prefix: ch.prefix };
|
|
344
|
-
if (ch.secretId) {
|
|
345
|
-
const targetScope = ch.targetScope ?? sourceScope;
|
|
346
|
-
headerBindings.push({
|
|
347
|
-
slot,
|
|
348
|
-
secretId: ch.secretId,
|
|
349
|
-
scope: targetScope,
|
|
350
|
-
secretScope: ch.secretScope ?? targetScope
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
for (const param of runtimeCredentials.queryParams) {
|
|
355
|
-
const name = param.name.trim();
|
|
356
|
-
if (!name) continue;
|
|
357
|
-
if (param.secretId) {
|
|
358
|
-
const slot = queryParamBindingSlot(name);
|
|
359
|
-
const targetScope = param.targetScope ?? sourceScope;
|
|
360
|
-
configuredQueryParams[name] = { kind: "secret", prefix: param.prefix };
|
|
361
|
-
queryParamBindings.push({
|
|
362
|
-
slot,
|
|
363
|
-
secretId: param.secretId,
|
|
364
|
-
scope: targetScope,
|
|
365
|
-
secretScope: param.secretScope ?? targetScope
|
|
366
|
-
});
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
if (param.literalValue?.trim()) {
|
|
370
|
-
configuredQueryParams[name] = param.literalValue.trim();
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
const configuredSpecFetchHeaders = {};
|
|
374
|
-
const configuredSpecFetchQueryParams = {};
|
|
375
|
-
const specFetchBindings = [];
|
|
376
|
-
for (const header of specFetchCredentials.headers) {
|
|
377
|
-
const name = header.name.trim();
|
|
378
|
-
if (!name || !header.secretId) continue;
|
|
379
|
-
const targetScope = header.targetScope ?? sourceScope;
|
|
380
|
-
configuredSpecFetchHeaders[name] = { kind: "secret", prefix: header.prefix };
|
|
381
|
-
specFetchBindings.push({
|
|
382
|
-
slot: specFetchHeaderBindingSlot(name),
|
|
383
|
-
secretId: header.secretId,
|
|
384
|
-
scope: targetScope,
|
|
385
|
-
secretScope: header.secretScope ?? targetScope
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
for (const param of specFetchCredentials.queryParams) {
|
|
389
|
-
const name = param.name.trim();
|
|
390
|
-
if (!name) continue;
|
|
391
|
-
if (param.secretId) {
|
|
392
|
-
const targetScope = param.targetScope ?? sourceScope;
|
|
393
|
-
configuredSpecFetchQueryParams[name] = { kind: "secret", prefix: param.prefix };
|
|
394
|
-
specFetchBindings.push({
|
|
395
|
-
slot: specFetchQueryParamBindingSlot(name),
|
|
396
|
-
secretId: param.secretId,
|
|
397
|
-
scope: targetScope,
|
|
398
|
-
secretScope: param.secretScope ?? targetScope
|
|
399
|
-
});
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
if (param.literalValue?.trim()) {
|
|
403
|
-
configuredSpecFetchQueryParams[name] = param.literalValue.trim();
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
const configuredSpecFetchCredentials = Object.keys(configuredSpecFetchHeaders).length > 0 || Object.keys(configuredSpecFetchQueryParams).length > 0 ? {
|
|
407
|
-
...Object.keys(configuredSpecFetchHeaders).length > 0 ? { headers: configuredSpecFetchHeaders } : {},
|
|
408
|
-
...Object.keys(configuredSpecFetchQueryParams).length > 0 ? { queryParams: configuredSpecFetchQueryParams } : {}
|
|
409
|
-
} : null;
|
|
410
|
-
const oauth2Presets = preview?.oauth2Presets ?? [];
|
|
411
|
-
const oauth2RedirectUrl = oauthCallbackUrl(OPENAPI_OAUTH_CALLBACK_PATH);
|
|
412
|
-
const resolvedSourceId = slugifyNamespace(identity.namespace) || (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
|
|
413
|
-
const selectedOAuth2Preset = strategy.kind === "oauth2" ? oauth2Presets[strategy.presetIndex] ?? null : null;
|
|
414
|
-
const selectedOAuth2Fingerprint = selectedOAuth2Preset ? [
|
|
415
|
-
resolvedSourceId,
|
|
416
|
-
resolvedBaseUrl,
|
|
417
|
-
selectedOAuth2Preset.securitySchemeName,
|
|
418
|
-
selectedOAuth2Preset.flow,
|
|
419
|
-
selectedOAuth2Preset.tokenUrl,
|
|
420
|
-
Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => "")
|
|
421
|
-
].join("\n") : "";
|
|
422
|
-
const oauth2Auth = oauth2AuthState?.fingerprint === selectedOAuth2Fingerprint ? oauth2AuthState.auth : null;
|
|
423
|
-
const configuredOAuth2 = strategy.kind === "oauth2" && selectedOAuth2Preset ? OAuth2SourceConfig.make({
|
|
424
|
-
kind: "oauth2",
|
|
425
|
-
securitySchemeName: selectedOAuth2Preset.securitySchemeName,
|
|
426
|
-
flow: selectedOAuth2Preset.flow,
|
|
427
|
-
tokenUrl: resolveOAuthUrl(selectedOAuth2Preset.tokenUrl, resolvedBaseUrl),
|
|
428
|
-
authorizationUrl: selectedOAuth2Preset.flow === "authorizationCode" ? resolveOAuthUrl(
|
|
429
|
-
Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => ""),
|
|
430
|
-
resolvedBaseUrl
|
|
431
|
-
) || null : null,
|
|
432
|
-
clientIdSlot: oauth2ClientIdSlot(selectedOAuth2Preset.securitySchemeName),
|
|
433
|
-
// Authorization-code specs can still be confidential clients
|
|
434
|
-
// (Spotify is one example). Persist the slot even when the value is
|
|
435
|
-
// deferred so the edit screen can collect the secret later.
|
|
436
|
-
clientSecretSlot: oauth2ClientSecretSlot(selectedOAuth2Preset.securitySchemeName),
|
|
437
|
-
connectionSlot: oauth2ConnectionSlot(selectedOAuth2Preset.securitySchemeName),
|
|
438
|
-
scopes: [...oauth2SelectedScopes]
|
|
439
|
-
}) : null;
|
|
440
|
-
const hasHeaders = Object.keys(configuredHeaders).length > 0;
|
|
441
|
-
const oauth2Busy = startingOAuth || oauth.busy;
|
|
442
|
-
const canConnectOAuth2 = Boolean(oauth2ClientIdSecretId) && resolvedBaseUrl.length > 0;
|
|
443
|
-
const hasIncompleteHeaderCredentials = strategy.kind !== "none" && strategy.kind !== "oauth2" && customHeaders.some((header) => header.name.trim() && !header.secretId);
|
|
444
|
-
const hasIncompleteQueryCredentials = runtimeCredentials.queryParams.some(
|
|
445
|
-
(param) => param.name.trim() && !param.secretId && !param.literalValue?.trim()
|
|
446
|
-
);
|
|
447
|
-
const hasIncompleteSpecFetchCredentials = specFetchCredentials.headers.some((header) => header.name.trim() && !header.secretId) || specFetchCredentials.queryParams.some(
|
|
448
|
-
(param) => param.name.trim() && !param.secretId && !param.literalValue?.trim()
|
|
449
|
-
);
|
|
450
|
-
const willAddWithoutInitialCredentials = Boolean(selectedOAuth2Preset && !oauth2Auth) || hasIncompleteSpecFetchCredentials || hasIncompleteHeaderCredentials || hasIncompleteQueryCredentials;
|
|
451
|
-
const canAdd = preview !== null && resolvedBaseUrl.length > 0;
|
|
452
|
-
const handleAnalyze = async () => {
|
|
453
|
-
setAnalyzing(true);
|
|
454
|
-
setAnalyzeError(null);
|
|
455
|
-
setAddError(null);
|
|
456
|
-
const credentials = serializeHttpCredentials(specFetchCredentials);
|
|
457
|
-
const exit = await doPreview({
|
|
458
|
-
params: { scopeId },
|
|
459
|
-
payload: {
|
|
460
|
-
spec: specUrl,
|
|
461
|
-
specFetchCredentials: credentials
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
if (Exit.isFailure(exit)) {
|
|
465
|
-
setAnalyzeError(errorMessageFromExit(exit, "Failed to parse spec"));
|
|
466
|
-
setAnalyzing(false);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
const result = exit.value;
|
|
470
|
-
setPreview(result);
|
|
471
|
-
const firstServer = result.servers[0];
|
|
472
|
-
setBaseUrl(firstServer ? expandServerOptions(firstServer)[0]?.value ?? "" : "");
|
|
473
|
-
const firstPreset = result.headerPresets[0];
|
|
474
|
-
if (firstPreset) {
|
|
475
|
-
setStrategy({ kind: "header", presetIndex: 0 });
|
|
476
|
-
setCustomHeaders(entriesFromSpecPreset(firstPreset));
|
|
477
|
-
} else if (result.oauth2Presets[0]) {
|
|
478
|
-
setStrategy({ kind: "oauth2", presetIndex: 0 });
|
|
479
|
-
setCustomHeaders([]);
|
|
480
|
-
setOauth2SelectedScopes(new Set(Object.keys(result.oauth2Presets[0].scopes)));
|
|
481
|
-
} else {
|
|
482
|
-
setStrategy({ kind: "custom" });
|
|
483
|
-
setCustomHeaders([]);
|
|
484
|
-
}
|
|
485
|
-
setAnalyzing(false);
|
|
486
|
-
};
|
|
487
|
-
handleAnalyzeRef.current = handleAnalyze;
|
|
488
|
-
const selectStrategy = (next) => {
|
|
489
|
-
setStrategy(next);
|
|
490
|
-
if (next.kind !== "oauth2") {
|
|
491
|
-
setOauth2AuthState(null);
|
|
492
|
-
setOauth2Error(null);
|
|
493
|
-
}
|
|
494
|
-
Match.value(next).pipe(
|
|
495
|
-
Match.when({ kind: "none" }, () => {
|
|
496
|
-
setCustomHeaders([]);
|
|
497
|
-
}),
|
|
498
|
-
Match.when({ kind: "custom" }, () => {
|
|
499
|
-
const userHeaders = customHeaders.filter((h) => !h.fromPreset);
|
|
500
|
-
setCustomHeaders(userHeaders.length > 0 ? userHeaders : []);
|
|
501
|
-
}),
|
|
502
|
-
Match.when({ kind: "header" }, (n) => {
|
|
503
|
-
const preset = preview?.headerPresets[n.presetIndex];
|
|
504
|
-
if (!preset) return;
|
|
505
|
-
const userHeaders = customHeaders.filter((h) => !h.fromPreset);
|
|
506
|
-
setCustomHeaders([...entriesFromSpecPreset(preset), ...userHeaders]);
|
|
507
|
-
}),
|
|
508
|
-
Match.when({ kind: "oauth2" }, (n) => {
|
|
509
|
-
setCustomHeaders([]);
|
|
510
|
-
const preset = preview?.oauth2Presets[n.presetIndex];
|
|
511
|
-
if (preset) {
|
|
512
|
-
setOauth2SelectedScopes(new Set(Object.keys(preset.scopes)));
|
|
513
|
-
}
|
|
514
|
-
}),
|
|
515
|
-
Match.exhaustive
|
|
516
|
-
);
|
|
517
|
-
};
|
|
518
|
-
const handleHeadersChange = (next) => {
|
|
519
|
-
setCustomHeaders(next);
|
|
520
|
-
if (strategy.kind === "header" && next.every((h) => !h.fromPreset)) {
|
|
521
|
-
setStrategy(next.length === 0 ? { kind: "none" } : { kind: "custom" });
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
const toggleOAuth2Scope = (scope) => {
|
|
525
|
-
setOauth2SelectedScopes((prev) => {
|
|
526
|
-
const copy = new Set(prev);
|
|
527
|
-
if (copy.has(scope)) copy.delete(scope);
|
|
528
|
-
else copy.add(scope);
|
|
529
|
-
return copy;
|
|
530
|
-
});
|
|
531
|
-
setOauth2AuthState(null);
|
|
532
|
-
};
|
|
533
|
-
const handleConnectOAuth2 = useCallback(async () => {
|
|
534
|
-
if (!selectedOAuth2Preset || !oauth2ClientIdSecretId || !preview) return;
|
|
535
|
-
oauth.cancel();
|
|
536
|
-
setOauth2Error(null);
|
|
537
|
-
const displayName = identity.name.trim() || selectedOAuth2Preset.securitySchemeName;
|
|
538
|
-
const tokenUrl = resolveOAuthUrl(selectedOAuth2Preset.tokenUrl, resolvedBaseUrl);
|
|
539
|
-
const clientIdSecretScope = oauth2ClientIdScope ?? sourceScope;
|
|
540
|
-
const clientSecretSecretScope = oauth2ClientSecretScope ?? sourceScope;
|
|
541
|
-
if (selectedOAuth2Preset.flow === "clientCredentials") {
|
|
542
|
-
if (!oauth2ClientSecretSecretId) {
|
|
543
|
-
setOauth2Error("client_credentials requires a client secret");
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
setStartingOAuth(true);
|
|
547
|
-
const connectionId = openApiOAuthConnectionId(resolvedSourceId, selectedOAuth2Preset.flow);
|
|
548
|
-
const exit = await doStartOAuth({
|
|
549
|
-
params: { scopeId: oauthTokenTargetScope },
|
|
550
|
-
payload: {
|
|
551
|
-
endpoint: tokenUrl,
|
|
552
|
-
redirectUrl: tokenUrl,
|
|
553
|
-
connectionId,
|
|
554
|
-
tokenScope: oauthTokenTargetScope,
|
|
555
|
-
strategy: {
|
|
556
|
-
kind: "client-credentials",
|
|
557
|
-
tokenEndpoint: tokenUrl,
|
|
558
|
-
clientIdSecretId: oauth2ClientIdSecretId,
|
|
559
|
-
clientIdSecretScopeId: String(clientIdSecretScope),
|
|
560
|
-
clientSecretSecretId: oauth2ClientSecretSecretId,
|
|
561
|
-
clientSecretSecretScopeId: String(clientSecretSecretScope),
|
|
562
|
-
scopes: [...oauth2SelectedScopes]
|
|
563
|
-
},
|
|
564
|
-
pluginId: "openapi",
|
|
565
|
-
identityLabel: `${displayName} OAuth`
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
setStartingOAuth(false);
|
|
569
|
-
if (Exit.isFailure(exit)) {
|
|
570
|
-
setOauth2Error(errorMessageFromExit(exit, "Failed to start OAuth"));
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const response = exit.value;
|
|
574
|
-
if (!response.completedConnection) {
|
|
575
|
-
setOauth2Error("client_credentials flow did not mint a connection");
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
setOauth2AuthState({
|
|
579
|
-
fingerprint: selectedOAuth2Fingerprint,
|
|
580
|
-
auth: { connectionId: response.completedConnection.connectionId }
|
|
581
|
-
});
|
|
582
|
-
setOauth2Error(null);
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
const authorizationUrl = resolveOAuthUrl(
|
|
586
|
-
Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => ""),
|
|
587
|
-
resolvedBaseUrl
|
|
588
|
-
);
|
|
589
|
-
const issuerUrl = inferOAuthIssuerUrl(authorizationUrl);
|
|
590
|
-
await oauth.openAuthorization({
|
|
591
|
-
tokenScope: oauthTokenTargetScope,
|
|
592
|
-
run: async () => {
|
|
593
|
-
const exit = await doStartOAuth({
|
|
594
|
-
params: { scopeId: oauthTokenTargetScope },
|
|
595
|
-
payload: {
|
|
596
|
-
endpoint: authorizationUrl,
|
|
597
|
-
connectionId: openApiOAuthConnectionId(resolvedSourceId, selectedOAuth2Preset.flow),
|
|
598
|
-
tokenScope: oauthTokenTargetScope,
|
|
599
|
-
redirectUrl: oauth2RedirectUrl,
|
|
600
|
-
strategy: {
|
|
601
|
-
kind: "authorization-code",
|
|
602
|
-
authorizationEndpoint: authorizationUrl,
|
|
603
|
-
tokenEndpoint: tokenUrl,
|
|
604
|
-
issuerUrl,
|
|
605
|
-
clientIdSecretId: oauth2ClientIdSecretId,
|
|
606
|
-
clientIdSecretScopeId: String(clientIdSecretScope),
|
|
607
|
-
clientSecretSecretId: oauth2ClientSecretSecretId ?? null,
|
|
608
|
-
clientSecretSecretScopeId: oauth2ClientSecretSecretId ? String(clientSecretSecretScope) : null,
|
|
609
|
-
scopes: [...oauth2SelectedScopes]
|
|
610
|
-
},
|
|
611
|
-
pluginId: "openapi",
|
|
612
|
-
identityLabel: `${displayName} OAuth`
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
|
-
if (Exit.isFailure(exit)) {
|
|
616
|
-
throw new Error(errorMessageFromExit(exit, "Failed to start OAuth"));
|
|
617
|
-
}
|
|
618
|
-
const response = exit.value;
|
|
619
|
-
if (response.authorizationUrl === null) {
|
|
620
|
-
throw new Error("Unexpected response flow from server");
|
|
621
|
-
}
|
|
622
|
-
return {
|
|
623
|
-
sessionId: response.sessionId,
|
|
624
|
-
authorizationUrl: response.authorizationUrl
|
|
625
|
-
};
|
|
626
|
-
},
|
|
627
|
-
onSuccess: (result) => {
|
|
628
|
-
setOauth2AuthState({
|
|
629
|
-
fingerprint: selectedOAuth2Fingerprint,
|
|
630
|
-
auth: { connectionId: result.connectionId }
|
|
631
|
-
});
|
|
632
|
-
setOauth2Error(null);
|
|
633
|
-
},
|
|
634
|
-
onError: (message) => {
|
|
635
|
-
setStartingOAuth(false);
|
|
636
|
-
setOauth2Error(message);
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
}, [
|
|
640
|
-
selectedOAuth2Preset,
|
|
641
|
-
oauth2ClientIdSecretId,
|
|
642
|
-
oauth2ClientSecretSecretId,
|
|
643
|
-
oauth2SelectedScopes,
|
|
644
|
-
oauth2RedirectUrl,
|
|
645
|
-
resolvedBaseUrl,
|
|
646
|
-
preview,
|
|
647
|
-
doStartOAuth,
|
|
648
|
-
identity.name,
|
|
649
|
-
resolvedSourceId,
|
|
650
|
-
selectedOAuth2Fingerprint,
|
|
651
|
-
oauth,
|
|
652
|
-
oauthTokenTargetScope,
|
|
653
|
-
oauth2ClientIdScope,
|
|
654
|
-
oauth2ClientSecretScope,
|
|
655
|
-
sourceScope
|
|
656
|
-
]);
|
|
657
|
-
const handleCancelOAuth2 = useCallback(() => {
|
|
658
|
-
oauth.cancel();
|
|
659
|
-
setStartingOAuth(false);
|
|
660
|
-
setOauth2Error(null);
|
|
661
|
-
}, [oauth]);
|
|
662
|
-
const handleAdd = async () => {
|
|
663
|
-
setAdding(true);
|
|
664
|
-
setAddError(null);
|
|
665
|
-
const namespace = resolvedSourceId;
|
|
666
|
-
const displayName = identity.name.trim() || (preview ? Option.getOrElse(preview.title, () => namespace) : namespace);
|
|
667
|
-
const exit = await doAdd({
|
|
668
|
-
params: { scopeId },
|
|
669
|
-
payload: {
|
|
670
|
-
spec: specInputForAdd(specUrl),
|
|
671
|
-
name: displayName,
|
|
672
|
-
namespace,
|
|
673
|
-
baseUrl: resolvedBaseUrl,
|
|
674
|
-
...configuredSpecFetchCredentials ? { specFetchCredentials: configuredSpecFetchCredentials } : {},
|
|
675
|
-
...hasHeaders ? { headers: configuredHeaders } : {},
|
|
676
|
-
...Object.keys(configuredQueryParams).length > 0 ? { queryParams: configuredQueryParams } : {},
|
|
677
|
-
...configuredOAuth2 ? { oauth2: configuredOAuth2 } : {}
|
|
678
|
-
},
|
|
679
|
-
reactivityKeys: addSpecWriteKeys
|
|
680
|
-
});
|
|
681
|
-
if (Exit.isFailure(exit)) {
|
|
682
|
-
setAddError(errorMessageFromExit(exit, "Failed to add source"));
|
|
683
|
-
setAdding(false);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
const sourceId = exit.value.namespace;
|
|
687
|
-
const oauthTokenBindingScope = ScopeId.make(oauthTokenTargetScope);
|
|
688
|
-
const clientIdBindingScope = oauth2ClientIdScope ?? sourceScope;
|
|
689
|
-
const clientSecretBindingScope = oauth2ClientSecretScope ?? sourceScope;
|
|
690
|
-
for (const binding of headerBindings) {
|
|
691
|
-
const bindingExit = await doSetBinding({
|
|
692
|
-
params: { scopeId },
|
|
693
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
694
|
-
source: { id: sourceId, scope: sourceScope },
|
|
695
|
-
scope: binding.scope,
|
|
696
|
-
slotKey: binding.slot,
|
|
697
|
-
value: {
|
|
698
|
-
kind: "secret",
|
|
699
|
-
secretId: SecretId.make(binding.secretId),
|
|
700
|
-
secretScopeId: binding.secretScope
|
|
701
|
-
}
|
|
702
|
-
}),
|
|
703
|
-
reactivityKeys: bindingWriteKeys
|
|
704
|
-
});
|
|
705
|
-
if (Exit.isFailure(bindingExit)) {
|
|
706
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
707
|
-
setAdding(false);
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
for (const binding of queryParamBindings) {
|
|
712
|
-
const bindingExit = await doSetBinding({
|
|
713
|
-
params: { scopeId },
|
|
714
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
715
|
-
source: { id: sourceId, scope: sourceScope },
|
|
716
|
-
scope: binding.scope,
|
|
717
|
-
slotKey: binding.slot,
|
|
718
|
-
value: {
|
|
719
|
-
kind: "secret",
|
|
720
|
-
secretId: SecretId.make(binding.secretId),
|
|
721
|
-
secretScopeId: binding.secretScope
|
|
722
|
-
}
|
|
723
|
-
}),
|
|
724
|
-
reactivityKeys: bindingWriteKeys
|
|
725
|
-
});
|
|
726
|
-
if (Exit.isFailure(bindingExit)) {
|
|
727
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
728
|
-
setAdding(false);
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
for (const binding of specFetchBindings) {
|
|
733
|
-
const bindingExit = await doSetBinding({
|
|
734
|
-
params: { scopeId },
|
|
735
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
736
|
-
source: { id: sourceId, scope: sourceScope },
|
|
737
|
-
scope: binding.scope,
|
|
738
|
-
slotKey: binding.slot,
|
|
739
|
-
value: {
|
|
740
|
-
kind: "secret",
|
|
741
|
-
secretId: SecretId.make(binding.secretId),
|
|
742
|
-
secretScopeId: binding.secretScope
|
|
743
|
-
}
|
|
744
|
-
}),
|
|
745
|
-
reactivityKeys: bindingWriteKeys
|
|
746
|
-
});
|
|
747
|
-
if (Exit.isFailure(bindingExit)) {
|
|
748
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
749
|
-
setAdding(false);
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
if (configuredOAuth2 && oauth2ClientIdSecretId) {
|
|
754
|
-
const bindingExit = await doSetBinding({
|
|
755
|
-
params: { scopeId },
|
|
756
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
757
|
-
source: { id: sourceId, scope: sourceScope },
|
|
758
|
-
scope: clientIdBindingScope,
|
|
759
|
-
slotKey: configuredOAuth2.clientIdSlot,
|
|
760
|
-
value: {
|
|
761
|
-
kind: "secret",
|
|
762
|
-
secretId: SecretId.make(oauth2ClientIdSecretId),
|
|
763
|
-
secretScopeId: clientIdBindingScope
|
|
764
|
-
}
|
|
765
|
-
}),
|
|
766
|
-
reactivityKeys: bindingWriteKeys
|
|
767
|
-
});
|
|
768
|
-
if (Exit.isFailure(bindingExit)) {
|
|
769
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
770
|
-
setAdding(false);
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (configuredOAuth2?.clientSecretSlot && oauth2ClientSecretSecretId) {
|
|
775
|
-
const bindingExit = await doSetBinding({
|
|
776
|
-
params: { scopeId },
|
|
777
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
778
|
-
source: { id: sourceId, scope: sourceScope },
|
|
779
|
-
scope: clientSecretBindingScope,
|
|
780
|
-
slotKey: configuredOAuth2.clientSecretSlot,
|
|
781
|
-
value: {
|
|
782
|
-
kind: "secret",
|
|
783
|
-
secretId: SecretId.make(oauth2ClientSecretSecretId),
|
|
784
|
-
secretScopeId: clientSecretBindingScope
|
|
785
|
-
}
|
|
786
|
-
}),
|
|
787
|
-
reactivityKeys: bindingWriteKeys
|
|
788
|
-
});
|
|
789
|
-
if (Exit.isFailure(bindingExit)) {
|
|
790
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
791
|
-
setAdding(false);
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
if (configuredOAuth2 && oauth2Auth) {
|
|
796
|
-
const bindingExit = await doSetBinding({
|
|
797
|
-
params: { scopeId },
|
|
798
|
-
payload: SetSourceCredentialBindingInput.make({
|
|
799
|
-
source: { id: sourceId, scope: sourceScope },
|
|
800
|
-
scope: oauthTokenBindingScope,
|
|
801
|
-
slotKey: configuredOAuth2.connectionSlot,
|
|
802
|
-
value: {
|
|
803
|
-
kind: "connection",
|
|
804
|
-
connectionId: ConnectionId.make(oauth2Auth.connectionId)
|
|
805
|
-
}
|
|
806
|
-
}),
|
|
807
|
-
reactivityKeys: bindingWriteKeys
|
|
808
|
-
});
|
|
809
|
-
if (Exit.isFailure(bindingExit)) {
|
|
810
|
-
setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
|
|
811
|
-
setAdding(false);
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
props.onComplete();
|
|
816
|
-
};
|
|
817
|
-
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-6", children: [
|
|
818
|
-
/* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2("h1", { className: "text-xl font-semibold text-foreground", children: "Add OpenAPI Source" }) }),
|
|
819
|
-
!preview && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
820
|
-
/* @__PURE__ */ jsx2(CardStack2, { children: /* @__PURE__ */ jsx2(CardStackContent2, { className: "border-t-0", children: /* @__PURE__ */ jsx2(
|
|
821
|
-
CardStackEntryField2,
|
|
822
|
-
{
|
|
823
|
-
label: "OpenAPI Spec",
|
|
824
|
-
hint: "Paste a URL or raw JSON/YAML content.",
|
|
825
|
-
children: /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
826
|
-
/* @__PURE__ */ jsx2(
|
|
827
|
-
Textarea,
|
|
828
|
-
{
|
|
829
|
-
value: specUrl,
|
|
830
|
-
onChange: (e) => {
|
|
831
|
-
setSpecUrl(e.target.value);
|
|
832
|
-
},
|
|
833
|
-
placeholder: "https://api.example.com/openapi.json",
|
|
834
|
-
rows: 3,
|
|
835
|
-
maxRows: 10,
|
|
836
|
-
className: "font-mono text-sm"
|
|
837
|
-
}
|
|
838
|
-
),
|
|
839
|
-
analyzing && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute right-2 top-2", children: /* @__PURE__ */ jsx2(IOSSpinner, { className: "size-4" }) })
|
|
840
|
-
] })
|
|
841
|
-
}
|
|
842
|
-
) }) }),
|
|
843
|
-
/* @__PURE__ */ jsxs2(
|
|
844
|
-
Collapsible,
|
|
845
|
-
{
|
|
846
|
-
open: specFetchCredentialsOpen,
|
|
847
|
-
onOpenChange: setSpecFetchCredentialsOpen,
|
|
848
|
-
className: "space-y-3",
|
|
849
|
-
children: [
|
|
850
|
-
/* @__PURE__ */ jsx2(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(Button, { variant: "outline", size: "sm", className: "self-start", children: specFetchCredentialsOpen ? "Hide spec credentials" : "Add spec credentials" }) }),
|
|
851
|
-
/* @__PURE__ */ jsx2(CollapsibleContent, { children: /* @__PURE__ */ jsx2(
|
|
852
|
-
HttpCredentialsEditor,
|
|
853
|
-
{
|
|
854
|
-
credentials: specFetchCredentials,
|
|
855
|
-
onChange: setSpecFetchCredentials,
|
|
856
|
-
existingSecrets: secretList,
|
|
857
|
-
sourceName: identity.name,
|
|
858
|
-
targetScope: sourceScope,
|
|
859
|
-
credentialScopeOptions,
|
|
860
|
-
bindingScopeOptions: credentialScopeOptions,
|
|
861
|
-
labels: {
|
|
862
|
-
headers: "Spec fetch headers",
|
|
863
|
-
queryParams: "Spec fetch query parameters"
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
) })
|
|
867
|
-
]
|
|
868
|
-
}
|
|
869
|
-
)
|
|
870
|
-
] }),
|
|
871
|
-
preview ? /* @__PURE__ */ jsx2(
|
|
872
|
-
OpenApiSourceDetailsFields,
|
|
873
|
-
{
|
|
874
|
-
title: Option.getOrElse(preview.title, () => "API"),
|
|
875
|
-
description: `${Option.getOrElse(preview.version, () => "")}${Option.isSome(preview.version) ? " \xB7 " : ""}${preview.operationCount} operation${preview.operationCount !== 1 ? "s" : ""}${preview.tags.length > 0 ? ` \xB7 ${preview.tags.length} tag${preview.tags.length !== 1 ? "s" : ""}` : ""}`,
|
|
876
|
-
identity,
|
|
877
|
-
baseUrl: resolvedBaseUrl,
|
|
878
|
-
onBaseUrlChange: setBaseUrl,
|
|
879
|
-
baseUrlOptions,
|
|
880
|
-
specUrl,
|
|
881
|
-
onSpecUrlChange: (value2) => {
|
|
882
|
-
setSpecUrl(value2);
|
|
883
|
-
setPreview(null);
|
|
884
|
-
setBaseUrl("");
|
|
885
|
-
setCustomHeaders([]);
|
|
886
|
-
setStrategy({ kind: "none" });
|
|
887
|
-
setOauth2AuthState(null);
|
|
888
|
-
setOauth2Error(null);
|
|
889
|
-
},
|
|
890
|
-
faviconUrl: resolvedBaseUrl,
|
|
891
|
-
baseUrlMissingMessage: "A base URL is required to make requests."
|
|
892
|
-
}
|
|
893
|
-
) : null,
|
|
894
|
-
analyzeError && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: analyzeError }) }),
|
|
895
|
-
preview && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
896
|
-
/* @__PURE__ */ jsxs2("section", { className: "space-y-2.5", children: [
|
|
897
|
-
/* @__PURE__ */ jsx2(FieldLabel, { children: "Authentication method" }),
|
|
898
|
-
/* @__PURE__ */ jsxs2(
|
|
899
|
-
RadioGroup,
|
|
900
|
-
{
|
|
901
|
-
value: serializeStrategy(strategy),
|
|
902
|
-
onValueChange: (value2) => selectStrategy(parseStrategy(value2)),
|
|
903
|
-
className: "gap-1.5",
|
|
904
|
-
children: [
|
|
905
|
-
preview.headerPresets.map((preset, i) => {
|
|
906
|
-
const selected = strategy.kind === "header" && strategy.presetIndex === i;
|
|
907
|
-
return /* @__PURE__ */ jsxs2(
|
|
908
|
-
Label,
|
|
909
|
-
{
|
|
910
|
-
className: `flex items-start gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${selected ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
|
|
911
|
-
children: [
|
|
912
|
-
/* @__PURE__ */ jsx2(RadioGroupItem, { value: `header:${i}`, className: "mt-0.5" }),
|
|
913
|
-
/* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
|
|
914
|
-
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium text-foreground", children: preset.label }),
|
|
915
|
-
preset.secretHeaders.length > 0 && /* @__PURE__ */ jsx2("div", { className: "mt-0.5 font-mono text-[10px] text-muted-foreground", children: preset.secretHeaders.join(" \xB7 ") })
|
|
916
|
-
] })
|
|
917
|
-
]
|
|
918
|
-
},
|
|
919
|
-
`header-${i}`
|
|
920
|
-
);
|
|
921
|
-
}),
|
|
922
|
-
oauth2Presets.map((preset, i) => {
|
|
923
|
-
const selected = strategy.kind === "oauth2" && strategy.presetIndex === i;
|
|
924
|
-
const scopeCount = Object.keys(preset.scopes).length;
|
|
925
|
-
return /* @__PURE__ */ jsxs2(
|
|
926
|
-
Label,
|
|
927
|
-
{
|
|
928
|
-
className: `flex items-start gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${selected ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
|
|
929
|
-
children: [
|
|
930
|
-
/* @__PURE__ */ jsx2(RadioGroupItem, { value: `oauth2:${i}`, className: "mt-0.5" }),
|
|
931
|
-
/* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
|
|
932
|
-
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium text-foreground", children: preset.label }),
|
|
933
|
-
/* @__PURE__ */ jsxs2("div", { className: "mt-0.5 text-[10px] text-muted-foreground", children: [
|
|
934
|
-
scopeCount,
|
|
935
|
-
" scope",
|
|
936
|
-
scopeCount === 1 ? "" : "s"
|
|
937
|
-
] })
|
|
938
|
-
] })
|
|
939
|
-
]
|
|
940
|
-
},
|
|
941
|
-
`oauth2-${i}`
|
|
942
|
-
);
|
|
943
|
-
}),
|
|
944
|
-
/* @__PURE__ */ jsxs2(
|
|
945
|
-
Label,
|
|
946
|
-
{
|
|
947
|
-
className: `flex items-center gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${strategy.kind === "custom" ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
|
|
948
|
-
children: [
|
|
949
|
-
/* @__PURE__ */ jsx2(RadioGroupItem, { value: "custom" }),
|
|
950
|
-
/* @__PURE__ */ jsx2("span", { className: "text-xs font-medium text-foreground", children: "Custom" })
|
|
951
|
-
]
|
|
952
|
-
}
|
|
953
|
-
),
|
|
954
|
-
/* @__PURE__ */ jsxs2(
|
|
955
|
-
Label,
|
|
956
|
-
{
|
|
957
|
-
className: `flex items-center gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${strategy.kind === "none" ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
|
|
958
|
-
children: [
|
|
959
|
-
/* @__PURE__ */ jsx2(RadioGroupItem, { value: "none" }),
|
|
960
|
-
/* @__PURE__ */ jsx2("span", { className: "text-xs font-medium text-foreground", children: "None" })
|
|
961
|
-
]
|
|
962
|
-
}
|
|
963
|
-
)
|
|
964
|
-
]
|
|
965
|
-
}
|
|
966
|
-
),
|
|
967
|
-
strategy.kind !== "none" && strategy.kind !== "oauth2" && /* @__PURE__ */ jsx2("div", { className: "space-y-3", children: /* @__PURE__ */ jsx2(
|
|
968
|
-
HeadersList,
|
|
969
|
-
{
|
|
970
|
-
headers: customHeaders,
|
|
971
|
-
onHeadersChange: handleHeadersChange,
|
|
972
|
-
existingSecrets: secretList,
|
|
973
|
-
sourceName: identity.name,
|
|
974
|
-
targetScope: sourceScope,
|
|
975
|
-
credentialScopeOptions,
|
|
976
|
-
bindingScopeOptions: credentialScopeOptions,
|
|
977
|
-
emptyLabel: "No credentials yet. Add the header value this method should use."
|
|
978
|
-
}
|
|
979
|
-
) }),
|
|
980
|
-
/* @__PURE__ */ jsx2(
|
|
981
|
-
HttpCredentialsEditor,
|
|
982
|
-
{
|
|
983
|
-
credentials: runtimeCredentials,
|
|
984
|
-
onChange: setRuntimeCredentials,
|
|
985
|
-
existingSecrets: secretList,
|
|
986
|
-
sourceName: identity.name,
|
|
987
|
-
targetScope: sourceScope,
|
|
988
|
-
credentialScopeOptions,
|
|
989
|
-
bindingScopeOptions: credentialScopeOptions,
|
|
990
|
-
sections: { headers: false, queryParams: true },
|
|
991
|
-
labels: { queryParams: "Runtime query parameters" }
|
|
992
|
-
}
|
|
993
|
-
),
|
|
994
|
-
selectedOAuth2Preset && /* @__PURE__ */ jsx2("div", { className: "space-y-3 rounded-lg border border-border/60 bg-muted/10 p-3", children: /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
|
|
995
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
996
|
-
/* @__PURE__ */ jsxs2(FieldLabel, { className: "text-[11px]", children: [
|
|
997
|
-
"Redirect URL",
|
|
998
|
-
" ",
|
|
999
|
-
/* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "\xB7 add this to your OAuth app's allowed redirects" })
|
|
1000
|
-
] }),
|
|
1001
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1 rounded-md border border-border bg-background/50 px-2.5 py-1.5 font-mono text-[11px]", children: [
|
|
1002
|
-
/* @__PURE__ */ jsx2("span", { className: "truncate flex-1 text-foreground", children: oauth2RedirectUrl }),
|
|
1003
|
-
/* @__PURE__ */ jsx2(CopyButton, { value: oauth2RedirectUrl })
|
|
1004
|
-
] })
|
|
1005
|
-
] }),
|
|
1006
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1007
|
-
/* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Client ID secret" }),
|
|
1008
|
-
/* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
|
|
1009
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1010
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
|
|
1011
|
-
/* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Secret" }),
|
|
1012
|
-
/* @__PURE__ */ jsx2(HelpTooltip, { label: "Client ID secret", children: "Select or create the OAuth client ID secret." })
|
|
1013
|
-
] }),
|
|
1014
|
-
/* @__PURE__ */ jsx2(
|
|
1015
|
-
CreatableSecretPicker,
|
|
1016
|
-
{
|
|
1017
|
-
value: oauth2ClientIdSecretId,
|
|
1018
|
-
onSelect: (id, secretScopeId) => {
|
|
1019
|
-
setOauth2ClientIdSecretId(id);
|
|
1020
|
-
setOauth2ClientIdScope(secretScopeId ?? sourceScope);
|
|
1021
|
-
setOauth2AuthState(null);
|
|
1022
|
-
},
|
|
1023
|
-
secrets: secretList,
|
|
1024
|
-
sourceName: identity.name,
|
|
1025
|
-
secretLabel: "Client ID",
|
|
1026
|
-
targetScope: oauth2ClientIdScope ?? sourceScope,
|
|
1027
|
-
credentialScopeOptions,
|
|
1028
|
-
onCreatedScope: setOauth2ClientIdScope
|
|
1029
|
-
}
|
|
1030
|
-
)
|
|
1031
|
-
] }),
|
|
1032
|
-
/* @__PURE__ */ jsx2(
|
|
1033
|
-
CredentialScopeDropdown,
|
|
1034
|
-
{
|
|
1035
|
-
value: oauth2ClientIdScope ?? sourceScope,
|
|
1036
|
-
options: credentialScopeOptions,
|
|
1037
|
-
onChange: (targetScope) => {
|
|
1038
|
-
setOauth2ClientIdScope(targetScope);
|
|
1039
|
-
setOauth2ClientIdSecretId(null);
|
|
1040
|
-
setOauth2AuthState(null);
|
|
1041
|
-
},
|
|
1042
|
-
label: "Used by",
|
|
1043
|
-
help: "Choose where this OAuth client ID credential lives."
|
|
1044
|
-
}
|
|
1045
|
-
)
|
|
1046
|
-
] })
|
|
1047
|
-
] }),
|
|
1048
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1049
|
-
/* @__PURE__ */ jsxs2(FieldLabel, { className: "text-[11px]", children: [
|
|
1050
|
-
"Client secret",
|
|
1051
|
-
" ",
|
|
1052
|
-
/* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "\xB7 optional for public clients with PKCE" })
|
|
1053
|
-
] }),
|
|
1054
|
-
/* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
|
|
1055
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1056
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
|
|
1057
|
-
/* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Secret" }),
|
|
1058
|
-
/* @__PURE__ */ jsx2(HelpTooltip, { label: "Client secret", children: "Select or create the OAuth client secret." })
|
|
1059
|
-
] }),
|
|
1060
|
-
/* @__PURE__ */ jsx2(
|
|
1061
|
-
CreatableSecretPicker,
|
|
1062
|
-
{
|
|
1063
|
-
value: oauth2ClientSecretSecretId,
|
|
1064
|
-
onSelect: (id, secretScopeId) => {
|
|
1065
|
-
setOauth2ClientSecretSecretId(id);
|
|
1066
|
-
setOauth2ClientSecretScope(secretScopeId ?? sourceScope);
|
|
1067
|
-
setOauth2AuthState(null);
|
|
1068
|
-
},
|
|
1069
|
-
secrets: secretList,
|
|
1070
|
-
sourceName: identity.name,
|
|
1071
|
-
secretLabel: "Client Secret",
|
|
1072
|
-
targetScope: oauth2ClientSecretScope ?? sourceScope,
|
|
1073
|
-
credentialScopeOptions,
|
|
1074
|
-
onCreatedScope: setOauth2ClientSecretScope
|
|
1075
|
-
}
|
|
1076
|
-
)
|
|
1077
|
-
] }),
|
|
1078
|
-
/* @__PURE__ */ jsx2(
|
|
1079
|
-
CredentialScopeDropdown,
|
|
1080
|
-
{
|
|
1081
|
-
value: oauth2ClientSecretScope ?? sourceScope,
|
|
1082
|
-
options: credentialScopeOptions,
|
|
1083
|
-
onChange: (targetScope) => {
|
|
1084
|
-
setOauth2ClientSecretScope(targetScope);
|
|
1085
|
-
setOauth2ClientSecretSecretId(null);
|
|
1086
|
-
setOauth2AuthState(null);
|
|
1087
|
-
},
|
|
1088
|
-
label: "Used by",
|
|
1089
|
-
help: "Choose where this OAuth client secret credential lives."
|
|
1090
|
-
}
|
|
1091
|
-
)
|
|
1092
|
-
] })
|
|
1093
|
-
] }),
|
|
1094
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1095
|
-
/* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Scopes" }),
|
|
1096
|
-
/* @__PURE__ */ jsx2("div", { className: "space-y-1 rounded-md border border-border/50 bg-background/50 p-2", children: Object.keys(selectedOAuth2Preset.scopes).length === 0 ? /* @__PURE__ */ jsx2("div", { className: "text-[11px] italic text-muted-foreground", children: "No scopes declared by the spec." }) : Object.entries(selectedOAuth2Preset.scopes).map(([scope, description]) => /* @__PURE__ */ jsxs2(Label, { className: "flex items-start gap-2 cursor-pointer py-1", children: [
|
|
1097
|
-
/* @__PURE__ */ jsx2(
|
|
1098
|
-
Checkbox,
|
|
1099
|
-
{
|
|
1100
|
-
checked: oauth2SelectedScopes.has(scope),
|
|
1101
|
-
onCheckedChange: () => toggleOAuth2Scope(scope)
|
|
1102
|
-
}
|
|
1103
|
-
),
|
|
1104
|
-
/* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
|
|
1105
|
-
/* @__PURE__ */ jsx2("div", { className: "font-mono text-[11px] text-foreground", children: scope }),
|
|
1106
|
-
description && /* @__PURE__ */ jsx2("div", { className: "text-[10px] text-muted-foreground", children: description })
|
|
1107
|
-
] })
|
|
1108
|
-
] }, scope)) })
|
|
1109
|
-
] }),
|
|
1110
|
-
oauth2Auth ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between rounded-md border border-green-500/30 bg-green-500/5 px-3 py-2", children: [
|
|
1111
|
-
/* @__PURE__ */ jsxs2("div", { className: "text-[11px] text-green-700 dark:text-green-400", children: [
|
|
1112
|
-
"Connected \xB7 ",
|
|
1113
|
-
oauth2SelectedScopes.size,
|
|
1114
|
-
" scope",
|
|
1115
|
-
oauth2SelectedScopes.size === 1 ? "" : "s",
|
|
1116
|
-
" granted"
|
|
1117
|
-
] }),
|
|
1118
|
-
/* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "sm", onClick: () => setOauth2AuthState(null), children: "Disconnect" })
|
|
1119
|
-
] }) : oauth2Busy ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
1120
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 items-center gap-2 rounded-md border border-border/60 bg-background/50 px-3 py-2 text-[11px] text-muted-foreground", children: [
|
|
1121
|
-
/* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
|
|
1122
|
-
"Waiting for OAuth\u2026 complete the flow in the popup, or cancel to retry."
|
|
1123
|
-
] }),
|
|
1124
|
-
/* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "sm", onClick: handleCancelOAuth2, children: "Cancel" }),
|
|
1125
|
-
/* @__PURE__ */ jsx2(Button, { variant: "secondary", size: "sm", onClick: handleConnectOAuth2, children: "Retry" })
|
|
1126
|
-
] }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-1.5", children: /* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
|
|
1127
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
|
|
1128
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
|
|
1129
|
-
/* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "OAuth sign-in" }),
|
|
1130
|
-
/* @__PURE__ */ jsx2(HelpTooltip, { label: "OAuth sign-in", children: "Start the provider OAuth flow." })
|
|
1131
|
-
] }),
|
|
1132
|
-
/* @__PURE__ */ jsx2(
|
|
1133
|
-
Button,
|
|
1134
|
-
{
|
|
1135
|
-
variant: "secondary",
|
|
1136
|
-
onClick: handleConnectOAuth2,
|
|
1137
|
-
disabled: !canConnectOAuth2,
|
|
1138
|
-
className: canConnectOAuth2 ? "w-full border border-green-500/30 bg-green-600 text-white hover:bg-green-700 focus-visible:ring-green-500/30 dark:bg-green-500 dark:text-white dark:hover:bg-green-600" : "w-full",
|
|
1139
|
-
children: "Connect via OAuth"
|
|
1140
|
-
}
|
|
1141
|
-
)
|
|
1142
|
-
] }),
|
|
1143
|
-
/* @__PURE__ */ jsx2(
|
|
1144
|
-
CredentialScopeDropdown,
|
|
1145
|
-
{
|
|
1146
|
-
value: oauthTokenTargetScope,
|
|
1147
|
-
options: credentialScopeOptions,
|
|
1148
|
-
onChange: (targetScope) => {
|
|
1149
|
-
setOAuthTokenTargetScope(targetScope);
|
|
1150
|
-
setOauth2AuthState(null);
|
|
1151
|
-
},
|
|
1152
|
-
label: "Token saved to",
|
|
1153
|
-
help: "Choose who can use the signed-in OAuth token."
|
|
1154
|
-
}
|
|
1155
|
-
)
|
|
1156
|
-
] }) }),
|
|
1157
|
-
oauth2Error && /* @__PURE__ */ jsx2("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-destructive", children: oauth2Error }) })
|
|
1158
|
-
] }) })
|
|
1159
|
-
] }),
|
|
1160
|
-
addError && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: addError }) })
|
|
1161
|
-
] }),
|
|
1162
|
-
/* @__PURE__ */ jsxs2(FloatActions, { children: [
|
|
1163
|
-
/* @__PURE__ */ jsx2(Button, { variant: "ghost", onClick: props.onCancel, disabled: adding, children: "Cancel" }),
|
|
1164
|
-
preview && /* @__PURE__ */ jsxs2(Button, { onClick: handleAdd, disabled: !canAdd || adding, children: [
|
|
1165
|
-
adding && /* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
|
|
1166
|
-
adding ? "Adding\u2026" : willAddWithoutInitialCredentials ? "Add without credentials" : "Add source"
|
|
1167
|
-
] })
|
|
1168
|
-
] })
|
|
1169
|
-
] });
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
export {
|
|
1173
|
-
OpenApiSourceDetailsFields,
|
|
1174
|
-
OPENAPI_OAUTH_POPUP_NAME,
|
|
1175
|
-
OPENAPI_OAUTH_CALLBACK_PATH,
|
|
1176
|
-
openApiOAuthConnectionId,
|
|
1177
|
-
resolveOAuthUrl,
|
|
1178
|
-
inferOAuthIssuerUrl,
|
|
1179
|
-
AddOpenApiSource
|
|
1180
|
-
};
|
|
1181
|
-
//# sourceMappingURL=chunk-2ZKKZYZH.js.map
|