From 78a10f154bd04be4a5474d44268385698deaac73 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Tue, 5 May 2026 18:09:24 -0700 Subject: [PATCH] Clean up Google Discovery invocation failures --- .../google-discovery/src/sdk/invoke.ts | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/packages/plugins/google-discovery/src/sdk/invoke.ts b/packages/plugins/google-discovery/src/sdk/invoke.ts index cac95144d..d2dd671e7 100644 --- a/packages/plugins/google-discovery/src/sdk/invoke.ts +++ b/packages/plugins/google-discovery/src/sdk/invoke.ts @@ -46,19 +46,34 @@ const replacePathParameters = (input: { pathTemplate: string; args: Record; parameters: readonly GoogleDiscoveryParameter[]; -}): string => - input.pathTemplate.replaceAll(/\{([^}]+)\}/g, (_, name: string) => { - const parameter = input.parameters.find( - (entry) => entry.location === "path" && entry.name === name, - ); - const values = stringValuesFromParameter(input.args[name], false); - if (values.length === 0) { - if (parameter?.required) { - throw new Error(`Missing required path parameter: ${name}`); +}): Effect.Effect => + Effect.gen(function* () { + let output = ""; + let cursor = 0; + const pathParameterPattern = /\{([^}]+)\}/g; + + for (const match of input.pathTemplate.matchAll(pathParameterPattern)) { + const name = match[1]!; + output += input.pathTemplate.slice(cursor, match.index); + cursor = match.index + match[0].length; + + const parameter = input.parameters.find( + (entry) => entry.location === "path" && entry.name === name, + ); + const values = stringValuesFromParameter(input.args[name], false); + if (values.length === 0) { + if (parameter?.required) { + return yield* new GoogleDiscoveryInvocationError({ + message: `Missing required path parameter: ${name}`, + statusCode: Option.none(), + }); + } + continue; } - return ""; + output += encodeURIComponent(values[0]!); } - return encodeURIComponent(values[0]!); + + return output + input.pathTemplate.slice(cursor); }); const resolveBaseUrl = (source: GoogleDiscoveryStoredSourceData): string => @@ -87,7 +102,7 @@ const performRequest = Effect.fn("GoogleDiscovery.invoke")(function* (input: { }) { const client = yield* HttpClient.HttpClient; - const resolvedPath = replacePathParameters({ + const resolvedPath = yield* replacePathParameters({ pathTemplate: input.pathTemplate, args: input.args, parameters: input.parameters, @@ -138,7 +153,7 @@ const performRequest = Effect.fn("GoogleDiscovery.invoke")(function* (input: { Effect.mapError( (err) => new GoogleDiscoveryInvocationError({ - message: `HTTP request failed: ${err.message}`, + message: "HTTP request failed", statusCode: Option.none(), cause: err, }), @@ -147,9 +162,9 @@ const performRequest = Effect.fn("GoogleDiscovery.invoke")(function* (input: { const contentType = response.headers["content-type"] ?? null; const mapBodyError = Effect.mapError( - (err: { readonly message?: string }) => + (err) => new GoogleDiscoveryInvocationError({ - message: `Failed to read response body: ${err.message ?? String(err)}`, + message: "Failed to read response body", statusCode: Option.some(response.status), cause: err, }), @@ -191,21 +206,17 @@ export const invokeGoogleDiscoveryTool = (input: { Effect.gen(function* () { const entry = yield* input.ctx.storage.getBinding(input.toolId, input.toolScope); if (!entry) { - return yield* Effect.fail( - new GoogleDiscoveryInvocationError({ - message: `No Google Discovery operation found for tool "${input.toolId}"`, - statusCode: Option.none(), - }), - ); + return yield* new GoogleDiscoveryInvocationError({ + message: `No Google Discovery operation found for tool "${input.toolId}"`, + statusCode: Option.none(), + }); } const stored = yield* input.ctx.storage.getSource(entry.namespace, input.toolScope); if (!stored) { - return yield* Effect.fail( - new GoogleDiscoveryInvocationError({ - message: `No Google Discovery source found for "${entry.namespace}"`, - statusCode: Option.none(), - }), - ); + return yield* new GoogleDiscoveryInvocationError({ + message: `No Google Discovery source found for "${entry.namespace}"`, + statusCode: Option.none(), + }); } const source = stored.config; @@ -213,9 +224,9 @@ export const invokeGoogleDiscoveryTool = (input: { source.auth.kind === "oauth2" ? `Bearer ${yield* input.ctx.connections.accessToken(source.auth.connectionId).pipe( Effect.mapError( - (err) => + () => new GoogleDiscoveryOAuthError({ - message: "message" in err ? (err as { message: string }).message : String(err), + message: "Failed to resolve Google Discovery OAuth access token", }), ), )}`