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