diff --git a/packages/plugins/google-discovery/src/api/group.ts b/packages/plugins/google-discovery/src/api/group.ts index 632e0014ce..c6ddd4e156 100644 --- a/packages/plugins/google-discovery/src/api/group.ts +++ b/packages/plugins/google-discovery/src/api/group.ts @@ -2,9 +2,12 @@ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; import { Schema } from "effect"; import { ScopeId } from "@executor/sdk"; +import { GoogleDiscoveryStoredSourceSchema } from "../sdk/stored-source"; + export { HttpApiSchema }; const scopeIdParam = HttpApiSchema.param("scopeId", ScopeId); +const namespaceParam = HttpApiSchema.param("namespace", Schema.String); const AuthPayload = Schema.Union( Schema.Struct({ @@ -125,4 +128,9 @@ export class GoogleDiscoveryGroup extends HttpApiGroup.make("googleDiscovery") .addSuccess(HtmlResponse) .addError(ApiError), ) + .add( + HttpApiEndpoint.get("getSource")`/scopes/${scopeIdParam}/google-discovery/sources/${namespaceParam}` + .addSuccess(Schema.NullOr(GoogleDiscoveryStoredSourceSchema)) + .addError(ApiError), + ) {} diff --git a/packages/plugins/google-discovery/src/api/handlers.ts b/packages/plugins/google-discovery/src/api/handlers.ts index f846ace4b2..917517673e 100644 --- a/packages/plugins/google-discovery/src/api/handlers.ts +++ b/packages/plugins/google-discovery/src/api/handlers.ts @@ -105,6 +105,12 @@ export const GoogleDiscoveryHandlers = HttpApiBuilder.group( }); }).pipe(Effect.orDie), ) + .handle("getSource", ({ path }) => + Effect.gen(function* () { + const ext = yield* GoogleDiscoveryExtensionService; + return yield* ext.getSource(path.namespace); + }).pipe(Effect.orDie), + ) .handle("oauthCallback", ({ urlParams }) => Effect.gen(function* () { const ext = yield* GoogleDiscoveryExtensionService; diff --git a/packages/plugins/google-discovery/src/react/EditGoogleDiscoverySource.tsx b/packages/plugins/google-discovery/src/react/EditGoogleDiscoverySource.tsx index 6ccf440d3a..eaf7dd6397 100644 --- a/packages/plugins/google-discovery/src/react/EditGoogleDiscoverySource.tsx +++ b/packages/plugins/google-discovery/src/react/EditGoogleDiscoverySource.tsx @@ -1,6 +1,10 @@ +import { useAtomValue, Result } from "@effect-atom/atom-react"; +import { useScope } from "@executor/react/api/scope-context"; import { Badge } from "@executor/react/components/badge"; import { Button } from "@executor/react/components/button"; +import { googleDiscoverySourceAtom } from "./atoms"; + export default function EditGoogleDiscoverySource({ sourceId, onSave, @@ -8,6 +12,13 @@ export default function EditGoogleDiscoverySource({ readonly sourceId: string; readonly onSave: () => void; }) { + const scopeId = useScope(); + const sourceResult = useAtomValue(googleDiscoverySourceAtom(scopeId, sourceId)); + + const source = Result.isSuccess(sourceResult) ? sourceResult.value : null; + const config = source?.config; + const authKind = config?.auth.kind ?? "none"; + return (
@@ -15,21 +26,53 @@ export default function EditGoogleDiscoverySource({ Edit Google Discovery Source

- This source is managed from its Discovery document and can be refreshed from the source header. + View configuration for this Google API source. To change authentication, + remove and re-add the source with updated OAuth credentials.

- {sourceId} + {source?.name ?? sourceId}

+ {config?.discoveryUrl && ( +

+ {config.discoveryUrl} +

+ )}
Google Discovery
+ {config && ( +
+
+
+

Service

+

+ {config.service} +

+
+
+

Version

+

+ {config.version} +

+
+
+ +
+

Authentication

+

+ {authKind === "oauth2" ? "OAuth 2.0" : authKind} +

+
+
+ )} +
diff --git a/packages/plugins/google-discovery/src/react/atoms.ts b/packages/plugins/google-discovery/src/react/atoms.ts index 0715eecee2..ea43b381e3 100644 --- a/packages/plugins/google-discovery/src/react/atoms.ts +++ b/packages/plugins/google-discovery/src/react/atoms.ts @@ -1,5 +1,15 @@ +import type { ScopeId } from "@executor/sdk"; import { GoogleDiscoveryClient } from "./client"; +export const googleDiscoverySourceAtom = ( + scopeId: ScopeId, + namespace: string, +) => + GoogleDiscoveryClient.query("googleDiscovery", "getSource", { + path: { scopeId, namespace }, + timeToLive: "15 seconds", + }); + export const probeGoogleDiscovery = GoogleDiscoveryClient.mutation( "googleDiscovery", "probeDiscovery", diff --git a/packages/plugins/google-discovery/src/sdk/binding-store.ts b/packages/plugins/google-discovery/src/sdk/binding-store.ts index 1c4dbe08e3..36fc9fbb74 100644 --- a/packages/plugins/google-discovery/src/sdk/binding-store.ts +++ b/packages/plugins/google-discovery/src/sdk/binding-store.ts @@ -55,6 +55,9 @@ export interface GoogleDiscoveryBindingStore { readonly putSource: (source: GoogleDiscoveryStoredSource) => Effect.Effect; readonly removeSource: (namespace: string) => Effect.Effect; readonly listSources: () => Effect.Effect; + readonly getSource: ( + namespace: string, + ) => Effect.Effect; readonly getSourceConfig: ( namespace: string, ) => Effect.Effect; @@ -120,6 +123,14 @@ const makeStore = ( return entries.map((e) => JSON.parse(e.value) as GoogleDiscoveryStoredSource); }), + getSource: (namespace) => + Effect.gen(function* () { + const raw = yield* sources.get(namespace); + if (!raw) return null; + // @effect-diagnostics-next-line preferSchemaOverJson:off + return JSON.parse(raw) as GoogleDiscoveryStoredSource; + }), + getSourceConfig: (namespace) => Effect.gen(function* () { const raw = yield* sources.get(namespace); diff --git a/packages/plugins/google-discovery/src/sdk/plugin.ts b/packages/plugins/google-discovery/src/sdk/plugin.ts index fc57d6e033..485277a6b8 100644 --- a/packages/plugins/google-discovery/src/sdk/plugin.ts +++ b/packages/plugins/google-discovery/src/sdk/plugin.ts @@ -15,6 +15,7 @@ import { import type { GoogleDiscoveryBindingStore, + GoogleDiscoveryStoredSource, } from "./binding-store"; import { makeInMemoryBindingStore } from "./binding-store"; import { extractGoogleDiscoveryManifest } from "./document"; @@ -110,6 +111,9 @@ export interface GoogleDiscoveryPluginExtension { readonly completeOAuth: ( input: GoogleDiscoveryOAuthCompleteInput, ) => Effect.Effect; + readonly getSource: ( + namespace: string, + ) => Effect.Effect; } const DISCOVERY_SERVICE_HOST = "https://www.googleapis.com/discovery/v1/apis"; @@ -293,6 +297,7 @@ export const googleDiscoveryPlugin = (options?: { runtime: false, canRemove: true, canRefresh: true, + canEdit: true, }), ), ), @@ -558,6 +563,9 @@ export const googleDiscoveryPlugin = (options?: { scopes: [...session.scopes], }; }), + + getSource: (namespace: string) => + bindingStore.getSource(namespace), }; return { diff --git a/packages/plugins/google-discovery/src/sdk/stored-source.ts b/packages/plugins/google-discovery/src/sdk/stored-source.ts new file mode 100644 index 0000000000..c59ecd292d --- /dev/null +++ b/packages/plugins/google-discovery/src/sdk/stored-source.ts @@ -0,0 +1,19 @@ +import { Schema } from "effect"; + +import { GoogleDiscoveryStoredSourceData } from "./types"; + +// --------------------------------------------------------------------------- +// Stored source — the shape persisted by the binding store and exposed +// via the getSource HTTP endpoint. +// --------------------------------------------------------------------------- + +export class GoogleDiscoveryStoredSourceSchema extends Schema.Class( + "GoogleDiscoveryStoredSource", +)({ + namespace: Schema.String, + name: Schema.String, + config: GoogleDiscoveryStoredSourceData, +}) {} + +export type GoogleDiscoveryStoredSourceSchemaType = + typeof GoogleDiscoveryStoredSourceSchema.Type;