fix(codegen/gcp): route query-location params in action methods and CRUD update (#1990) #263
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/1990-query-location-params"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
domainPropertiesandupdatePropertyNames, mirroring the existing insert query-param pass. This makes method-level query params likevalueInputOptionavailable inGlobalArgsSchemaso the CRUD update codegen routes them to the URL automatically.action.config.parameters, add them to the action's arguments schema, and route them toparams(notbody). This fixes methods like Sheetsvalues.appendwherevalueInputOptionis required by the API but was previously unreachable.model/gcp/sheets/to pick up the fix. Other GCP models will pick up the fix in the next scheduled full regeneration.Sibling of #1274 (path params missing from GlobalArgsSchema) and #1562 (query routing in CRUD update codegen).
Closes #1990
Test plan
rangesnow in arguments)deno task generate:gcp sheetsproduces intended diffcodegen/andmodel/gcp/sheets/🤖 Generated with Claude Code
Code Review
Blocking Issues
None.
Suggestions
String()coercion for array-valued query params (extensionModelGenerator.ts,pipeline.ts): Therangesparameter onbatch_get(and similar array-typed query params in Google APIs) is meant to be repeated —?ranges=A1&ranges=B2. The generated code emitsparams["ranges"] = String(args["ranges"]), which for an array produces"A1,B2"instead. This is a pre-existing framework limitation (the same pattern is used in existing insert query-param routing), but the SheetsbatchGetendpoint is a likely real-world pain point sincerangesis almost always an array. No change needed to block merge, but worth a follow-up.pathParamNamesvariable name is slightly misleading (extensionModelGenerator.ts, line ~1575): It's built fromaction.config.parameterOrder, which is the list of required ordered params — in practice always path params for Google APIs, but not strictly enforced by the type. The name implies it contains only path params. Since the filterparamDef.location !== "query"already handles real path params, thepathParamNamesguard is defensive and harmless, just cosmetically misnamed.New unit test uses
assert(output.includes(...))but the existing pattern is snapshots: The new test inextensionModelGenerator_test.tsasserts on specific substrings rather than adding a snapshot entry. The existing test suite uses@std/testing/snapshotfor full-output verification. The substring approach is readable for targeted assertions but won't catch unexpected regressions in surrounding generated code. Not blocking — the snapshot forbatch_getin__snapshots__/extensionModelGenerator_test.ts.snapdoes cover the equivalent generated output from the test fixture.Overall the change is clean: the update/patch query-param pass correctly mirrors the insert pass and excludes
updateMaskandvalidateOnly, the action-method query-param routing correctly separates path params (viaparameterOrder) from query params, and the model changes are consistent with the codegen output.Adversarial Review
Critical / High
None found.
Medium
Missing
deprecatedfilter on action method query params —codegen/gcp/extensionModelGenerator.ts:1577-1584The action method query-param collection loop does not check
paramDef.deprecated === true, while the parallel code inpipeline.ts:1466-1470(for CRUD update/patch) andpipeline.ts:1447(for insert) both filter deprecated params. If a GCP API marks an action query parameter as deprecated, it will still be emitted into the generated arguments schema and routing code.Breaking example: A GCP service deprecates a query parameter (e.g.,
responseValueRenderOption) on an action method. The generated model exposes it as an argument and routes it to the URL, potentially sending an unsupported parameter that the API rejects or ignores with a warning.Suggested fix: Add
if (paramDef.deprecated === true) continue;after thelocationcheck at line 1581, consistent with both pipeline.ts passes.String()coercion flattens array-valued query params —codegen/gcp/extensionModelGenerator.ts:1735-1736Query params like
ranges(SheetsbatchGet) accept repeated values. The generated code doesparams["ranges"] = String(args["ranges"]). If the caller passes["Sheet1!A1:B2", "Sheet2!C3"], JavaScript'sString()on an array produces"Sheet1!A1:B2,Sheet2!C3"— a single comma-joined string. GCP APIs that expect repeated query params (?ranges=X&ranges=Y) would receive the wrong encoding.This is a pre-existing pattern (the CRUD insert query-param routing at line 529 does the same
String()coercion via globalArgs), so it's not new to this PR. But the action method change widens the surface area —rangesonbatchGetis the first generated action param where this matters.Suggested fix (non-blocking): Consider handling array values by iterating and adding multiple entries, or document that callers must pass pre-formatted strings. This could be addressed in a follow-up.
Low
No
validateOnlyfilter on action method query params —codegen/gcp/extensionModelGenerator.ts:1577-1584The CRUD passes in
pipeline.tsskipvalidateOnly(a dry-run flag), but the action method query-param loop doesn't. If an action method declares avalidateOnlyquery parameter, it would be exposed as an argument. This may be intentional —validateOnlycould be useful for action methods — but it's an inconsistency worth noting.Verdict
PASS — The core logic is correct: query-location parameters on action methods are properly split into the arguments schema and routed to URL params instead of the request body, and the pipeline correctly mirrors the insert pass for update/patch methods. The
updateMaskexclusion in the second commit is necessary and correct. Test coverage validates the golden path. The medium findings are edge cases around schema inconsistencies (deprecated params) and data type handling (arrays), neither of which block the merge.Code Review
Blocking Issues
None.
Suggestions
No explicit test for CRUD update query-param routing (pipeline side)
pipeline.tslines 1463–1479 add a new pass that collects query-location parameters fromupdate/patchmethod configs intodomainPropertiesandupdatePropertyNames. The routing of those collected params in the CRUD update body (already present inextensionModelGenerator.ts) is exercised indirectly by the model diffs, but there is no unit test inextensionModelGenerator_test.tsthat provides a resource with an update method query param and asserts it lands inparamsrather thanbody. The analogous action-method path now has a dedicated assertion test (generateGcpExtensionModel - action method query params routed to params); a similar test for the CRUD update path would make the coverage symmetric and prevent regression.Sanitization mismatch in
updateParameters[propName]?.locationlookup (pre-existing pattern, carried into new code)In the CRUD update body loop (
extensionModelGenerator.ts~line 1052),propNameis a sanitized key fromresource.domainProperties, butupdateParameters(fromextractMethodConfig) stores raw parameter names. For any GCP update/patch query param whose name contains a dot, hyphen, or slash (e.g., a hypothetical"foo.bar") the sanitized lookupupdateParameters["foo_bar"]returnsundefined, so the property silently falls into the request body instead of the URL query string. The same mismatch exists in the pre-existing insert code path. In practice, GCP query-parameter names appear to be plain camelCase identifiers, so this is unlikely to cause observable failures, but it is worth noting since the newpipeline.tscollection pass follows the same pattern. Consider sanitizing the keys inextractMethodConfig(or adding asafeIdentwrapper at the lookup site) to make the system robust by construction.Minor:
domainProperties[paramName]duplicate-guard uses raw name against mixed-case-key mapIn the new update/patch query-param collection loop (
pipeline.tsline 1468:if (domainProperties[paramName]) continue;),paramNameis the raw parameter name, butdomainPropertiesmay already contain an entry under the sanitized version of that name (populated by the earliergcpSchemaToCfPropertiescalls). For standard alphanumeric param names this is a non-issue, but for param names with dots/hyphens the guard could fail to detect an existing sanitized entry and add a second raw entry. This is another instance of the same pre-existing pattern; same note as above applies.Adversarial Review
Critical / High
No critical or high severity findings.
Medium
codegen/gcp/extensionModelGenerator.ts:1741—String()coercion silently flattens array-valued query params.GCP Discovery Documents sometimes declare query parameters that accept repeated values (e.g.,
rangesonsheets.spreadsheets.values.batchGet). The generated code coerces them withString(args["ranges"]), which turns["Sheet1!A1:B2", "Sheet2!A1"]into"Sheet1!A1:B2,Sheet2!A1". GCP APIs expect repeated query params asranges=Sheet1!A1:B2&ranges=Sheet2!A1.Breaking example: A user calls the
batch_getaction method withranges: ["Sheet1!A1:B2", "Sheet2!C3"]. The generated code sends?ranges=Sheet1!A1:B2,Sheet2!C3(one param, comma-joined) instead of?ranges=Sheet1!A1:B2&ranges=Sheet2!C3(two params). The API may reject or misinterpret this.Note: This is a pre-existing pattern — the list method query param routing (
extensionModelGenerator.ts:1514) and CRUD query param routing have the sameString()coercion. This PR didn't introduce it, just propagated the same pattern to action methods. Flagging because it's now exercised on a concrete new path (Sheetsranges). Not blocking since it's consistent with the existing codebase.Low
codegen/gcp/extensionModelGenerator.ts:1598-1603— Query params on action methods are always.optional()even when potentially required by the API.All query params added to the action method's arguments schema are generated as
z.any().optional(). If a GCP API requires a query param (e.g.,valueInputOptionis effectively required for Sheetsvalues.append), the client won't validate this — the user gets a runtime 400 from GCP instead of a local validation error.Mitigation: This matches the existing behavior for body properties on actions (which use
z.any()without propagating type info from the Discovery schema). The GCP error message is usually clear. Low impact.codegen/gcp/pipeline.ts:1460-1479— Update/patch query param collection doesn't filter byparameterOrdermembership, unlike insert.The insert query param collection (lines 1444-1456) adds ALL query params from the insert method. The new update/patch collection mirrors this. Both skip
validateOnlyand (for update/patch)updateMask. However, neither checks whether the param is actually inparameterOrder— they collect all method-level query params. This is correct behavior (query params typically aren't inparameterOrdersince that's for path params), just noting the design intent is clear and consistent.Verdict
PASS — The changes correctly route query-location parameters in action methods to URL params instead of the request body, with proper dedup against request body properties. The pipeline change consistently collects update/patch query params into
domainProperties. Test coverage is adequate (direct assertion test + snapshot). The design doc is updated. No blocking issues found.