[go] fix: explode object query parameters
Bug
A query parameter whose schema is an object, with style/explode left at their defaults (form, true), must go on the wire as one parameter per entry, keyed by the property name alone. The go client bracketed it instead.
parameters:
- in: query
name: filter
schema:
type: object
called with {"category": "books", "createdDate:gte": "2023-01-01"}:
| on the wire | |
|---|---|
| expected | category=books&createdDate%3Agte=2023-01-01 |
| go before | filter[category]=books&filter[createdDate%3Agte]=2023-01-01 |
Series
One of six per-language PRs for the same bug: #24797 go, #24802 python, #24803 typescript-fetch, #24867 kotlin, #24868 dart, #24869 ruby. No shared main/ code; each adds the same fixture, 3_0/exploded-object-query-param.yaml.
Fix
go/client.mustache, parameterAddToHeaderOrQuery:
- The map branch now honours
form: each entry becomes its own parameter, keyed by the property name. It used to bracket every map whatever style it was handed. - Only the top level is flattened. Anything nested inside an entry keeps its bracketed path, so
{a: {x: 1}, b: {x: 2}}isa[x]=1&b[x]=2, not two collidingx=entries. - An array element keeps master's path too: the slice branch resets the style for its elements under
form, so a map inside an array staysarrayFilter[a]=1.
Nil handling
Under form, a nil entry and a nil item of a list entry are left out rather than sent as null, as the kotlin, python and dart ports do. A typed nil pointer, which panicked in Elem().Interface() on master, is left out under any style.
input (filter) | form | deepObject |
|---|---|---|
{"k": nil, "a": "b"} | a=b | filter[a]=b&filter[k]=null (as on master) |
{"k": (*string)(nil), "a": "b"} | a=b (master: panic) | filter[a]=b (master: panic) |
{"l": ["x", nil, 2]} | l=x&l=2 | filter[l][0]=x&filter[l][1]=null&filter[l][2]=2 (as on master) |
deepObject keeps [k]=null on purpose: the petstore sample's TestQueryDeepObject pins inputOptions[F3]=null.
Verified
A client generated from the fixture, against a server that echoes its raw query string:
| parameter | on the wire |
|---|---|
filter (object, defaults) | category=books |
typedFilter (map, defaults) | category=books |
deepFilter (style: deepObject) | deepFilter[category]=books (as on master) |
flatFilter (explode: false) | category=books (see Known gaps) |
GoClientCodegenTest#testExplodedObjectQueryParameter fails without the fix. No sample operation has a form-style map query parameter, so no sample test reaches the new branch; TestQueryDeepObject in samples/openapi3/client/petstore/go/fake_api_test.go passes unchanged.
Known gaps
The go templates pass style to the runtime but not explode, so a form-style object with explode: false is now exploded; it was bracketed before, and the spec wants flatFilter=category,books. Passing explode changes parameterAddToHeaderOrQuery's signature and all 12 call sites in go/api.mustache; happy to do that here if you'd rather.
PR checklist
- Read the contribution guidelines.
- Built the project and updated samples (
./bin/generate-samples.sh bin/configs/go*.yaml): 12 sampleclient.gofiles. - Technical committee: @antihax @grokify @kemokemo @jirikuncar @ph4r5h4d @lwj5
Generated with Claude Code