[go] fix: explode object query parameters

Merged
WI
1mo ago
+611-26

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
expectedcategory=books&createdDate%3Agte=2023-01-01
go beforefilter[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}} is a[x]=1&b[x]=2, not two colliding x= 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 stays arrayFilter[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)formdeepObject
{"k": nil, "a": "b"}a=bfilter[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=2filter[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:

parameteron 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 sample client.go files.
  • Technical committee: @antihax @grokify @kemokemo @jirikuncar @ph4r5h4d @lwj5

Generated with Claude Code