History

Move source auth handling into plugins

Rhys Sullivan committed 7 months ago

Commit db9b84d1 parent 02d549c15 files · +98 −125Browse files

Showing the changes to plugins/openapi (4 of 15 files). Show all

Unified · Split

4 files
  1. plugins/openapi/react/index.tsx +36 −14
  2. plugins/openapi/sdk/index.ts +13 −21
  3. plugins/openapi/shared/index.ts +3 −2
  4. plugins/openapi/shared/package.json +1 −0
plugins/openapi/react/index.tsx+36−14
@@ -38,7 +38,7 @@ import { · 37 unmodified lines
38 type SourceToolExplorerSearch,38 type SourceToolExplorerSearch,
39 useSourcePluginNavigation,39 useSourcePluginNavigation,
40 useSourcePluginSearch,40 useSourcePluginSearch,
41} from "@executor/react/source-plugins";41} from "@executor/react/plugins";
42import {42import {
43 openApiHttpApiExtension,43 openApiHttpApiExtension,
44} from "@executor/plugin-openapi-http";44} from "@executor/plugin-openapi-http";
@@ -169,6 +169,31 @@ const inputFromConfig = ( · 124 unmodified lines
169 auth: config.auth,169 auth: config.auth,
170});170});
171 171
172const secretValue = (input: OpenApiConnectInput["auth"]): string =>
173 input.kind === "bearer"
174 ? JSON.stringify(input.tokenSecretRef)
175 : "";
176
177const authFromSecretValue = (
178 authKind: OpenApiConnectInput["auth"]["kind"],
179 value: string,
180): OpenApiConnectInput["auth"] => {
181 if (authKind === "none") {
182 return {
183 kind: "none",
184 };
185 }
186
187 if (!value) {
188 throw new Error("Select a secret for bearer auth.");
189 }
190
191 return {
192 kind: "bearer",
193 tokenSecretRef: JSON.parse(value) as OpenApiConnectInput["auth"] & { tokenSecretRef: never }["tokenSecretRef"],
194 };
195};
196
172const useAvailableSecrets = (197const useAvailableSecrets = (
173 openApiHttpClient: ReturnType<typeof getOpenApiHttpClient>,198 openApiHttpClient: ReturnType<typeof getOpenApiHttpClient>,
174) => {199) => {
@@ -205,9 +230,7 @@ function OpenApiSourceForm(props: { · 30 unmodified lines
205 props.initialValue.auth.kind,230 props.initialValue.auth.kind,
206 );231 );
207 const [tokenSecretRef, setTokenSecretRef] = useState(232 const [tokenSecretRef, setTokenSecretRef] = useState(
208 props.initialValue.auth.kind === "bearer"233 secretValue(props.initialValue.auth),
209 ? props.initialValue.auth.tokenSecretRef
210 : "",
211 );234 );
212 const [error, setError] = useState<string | null>(null);235 const [error, setError] = useState<string | null>(null);
213 const [preview, setPreview] = useState<OpenApiPreviewResponse | null>(null);236 const [preview, setPreview] = useState<OpenApiPreviewResponse | null>(null);
@@ -309,19 +332,12 @@ function OpenApiSourceForm(props: { · 95 unmodified lines
309 }332 }
310 333
311 try {334 try {
335 const auth = authFromSecretValue(authKind, tokenSecretRef);
312 await submitMutation.mutateAsync({336 await submitMutation.mutateAsync({
313 name: trimmedName,337 name: trimmedName,
314 specUrl: trimmedSpecUrl,338 specUrl: trimmedSpecUrl,
315 baseUrl: trimmedBaseUrl || null,339 baseUrl: trimmedBaseUrl || null,
316 auth:340 auth,
317 authKind === "bearer"
318 ? {
319 kind: "bearer",
320 tokenSecretRef: tokenSecretRef.trim(),
321 }
322 : {
323 kind: "none",
324 },
325 });341 });
326 } catch (cause) {342 } catch (cause) {
327 setError(cause instanceof Error ? cause.message : "Failed saving source.");343 setError(cause instanceof Error ? cause.message : "Failed saving source.");
@@ -398,7 +414,13 @@ function OpenApiSourceForm(props: { · 70 unmodified lines
398 >414 >
399 <option value="">Select a secret</option>415 <option value="">Select a secret</option>
400 {availableSecrets.map((secret) => (416 {availableSecrets.map((secret) => (
401 <option key={secret.id} value={secret.id}>417 <option
418 key={`${secret.providerId}:${secret.id}`}
419 value={JSON.stringify({
420 providerId: secret.providerId,
421 handle: secret.id,
422 })}
423 >
402 {secret.name || secret.id}424 {secret.name || secret.id}
403 </option>425 </option>
404 ))}426 ))}
plugins/openapi/sdk/index.ts+13−21
@@ -13,6 +13,9 @@ import type { · 12 unmodified lines
13 ExecutorSourceConnector,13 ExecutorSourceConnector,
14 SourcePluginRuntime,14 SourcePluginRuntime,
15} from "@executor/platform-sdk/plugins";15} from "@executor/platform-sdk/plugins";
16import {
17 SecretMaterialResolverService,
18} from "@executor/platform-sdk/runtime";
16import {19import {
17 OpenApiConnectionAuthSchema,20 OpenApiConnectionAuthSchema,
18 deriveOpenApiNamespace,21 deriveOpenApiNamespace,
@@ -64,12 +67,6 @@ export type OpenApiSourceStorage = { · 45 unmodified lines
64 }) => Effect.Effect<void, Error, never>;67 }) => Effect.Effect<void, Error, never>;
65};68};
66 69
67export type OpenApiSecrets = {
68 resolve: (input: {
69 ref: string;
70 }) => Effect.Effect<string, Error, never>;
71};
72
73export type OpenApiSdk = {70export type OpenApiSdk = {
74 previewDocument: (71 previewDocument: (
75 input: OpenApiPreviewRequest,72 input: OpenApiPreviewRequest,
@@ -93,7 +90,6 @@ export type OpenApiSdk = { · 17 unmodified lines
93 90
94export type OpenApiSdkPluginOptions = {91export type OpenApiSdkPluginOptions = {
95 storage: OpenApiSourceStorage;92 storage: OpenApiSourceStorage;
96 secrets?: OpenApiSecrets;
97};93};
98 94
99const OpenApiExecutorAddInputSchema = Schema.Struct({95const OpenApiExecutorAddInputSchema = Schema.Struct({
@@ -414,22 +410,18 @@ const decodeResponseBody = async (response: Response): Promise<unknown> => { · 314 unmodified lines
414};410};
415 411
416const resolveBearerToken = (412const resolveBearerToken = (
417 options: OpenApiSdkPluginOptions,
418 stored: OpenApiStoredSourceData,413 stored: OpenApiStoredSourceData,
419): Effect.Effect<string | null, Error, never> => {414): Effect.Effect<string | null, Error, any> => {
420 if (stored.auth.kind === "none") {415 const { auth } = stored;
416 if (auth.kind === "none") {
421 return Effect.succeed(null);417 return Effect.succeed(null);
422 }418 }
423 419
424 if (!options.secrets) {420 return Effect.flatMap(SecretMaterialResolverService, (resolveSecretMaterial) =>
425 return Effect.fail(421 resolveSecretMaterial({
426 new Error("OpenAPI bearer auth is configured, but no secret resolver is available."),422 ref: auth.tokenSecretRef,
427 );423 }).pipe(Effect.map((token) => token.trim()))
428 }424 );
429
430 return options.secrets.resolve({
431 ref: stored.auth.tokenSecretRef,
432 }).pipe(Effect.map((token) => token.trim()));
433};425};
434 426
435const openApiDocumentHeaders = (input: {427const openApiDocumentHeaders = (input: {
@@ -550,7 +542,7 @@ const createOpenApiSourceRuntime = ( · 114 unmodified lines
550 });542 });
551 }543 }
552 544
553 const bearerToken = yield* resolveBearerToken(options, stored);545 const bearerToken = yield* resolveBearerToken(stored);
554 const fetched = yield* fetchOpenApiDocument({546 const fetched = yield* fetchOpenApiDocument({
555 stored,547 stored,
556 bearerToken,548 bearerToken,
@@ -686,7 +678,7 @@ const createOpenApiSourceRuntime = ( · 129 unmodified lines
686 }678 }
687 }679 }
688 680
689 const bearerToken = yield* resolveBearerToken(options, stored);681 const bearerToken = yield* resolveBearerToken(stored);
690 if (bearerToken && bearerToken.length > 0) {682 if (bearerToken && bearerToken.length > 0) {
691 headers.authorization = `Bearer ${bearerToken}`;683 headers.authorization = `Bearer ${bearerToken}`;
692 }684 }
plugins/openapi/shared/index.ts+3−2
@@ -3,8 +3,9 @@ import * as Schema from "effect/Schema"; · 2 unmodified lines
3import YAML from "yaml";3import YAML from "yaml";
4 4
5import {5import {
6 SecretRefSchema,
6 StringMapSchema,7 StringMapSchema,
7} from "@executor/source-core";8} from "@executor/platform-sdk/schema";
8 9
9export const OpenApiConnectionAuthSchema = Schema.Union(10export const OpenApiConnectionAuthSchema = Schema.Union(
10 Schema.Struct({11 Schema.Struct({
@@ -12,7 +13,7 @@ export const OpenApiConnectionAuthSchema = Schema.Union( · 1 unmodified line
12 }),13 }),
13 Schema.Struct({14 Schema.Struct({
14 kind: Schema.Literal("bearer"),15 kind: Schema.Literal("bearer"),
15 tokenSecretRef: Schema.String,16 tokenSecretRef: SecretRefSchema,
16 }),17 }),
17);18);
18 19
plugins/openapi/shared/package.json+1−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
13 },13 },
14 "dependencies": {14 "dependencies": {
15 "@executor/platform-sdk": "workspace:*",
15 "@executor/source-core": "workspace:*",16 "@executor/source-core": "workspace:*",
16 "effect": "catalog:",17 "effect": "catalog:",
17 "yaml": "^2.8.1"18 "yaml": "^2.8.1"