Conversation
A query parameter whose schema is an object and whose style/explode are left at
their defaults — style: form, explode: true — must go on the wire as one
parameter per entry, keyed by the property name alone. The dart client handed
the whole map to _queryParams, which stringified it with Map.toString() into a
single parameter (filter={tld: com, createdDate:gte: 2023-01-01}).
The generated api now iterates an exploded map entry by entry at the call
site; _queryParams is left alone, since it never receives style or explode
and is still the right fallback for every other parameter. A free-form
object is typed Object in dart and needs a cast to Map; a declared map does
not. deepObject and explode: false objects keep their previous wire format,
byte for byte.
The new test fixture covers the four style/explode combinations that decide
the wire format; the test fails without the template change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ
…d object An exploded form-style object query parameter handed each entry to _queryParams with an empty collection format, which defaults to csv, so a list value went out comma joined (tld=com%2Cnet) instead of once per element (tld=com&tld=net). A Set went out as its toString(), since _queryParams only expands a List, and a null element went out as an empty value. Each entry is now passed with the 'multi' collection format, and an Iterable value is turned into a List with its null elements dropped first, so the key repeats once per non-null element and an empty collection adds nothing. The closure parameter is typed dynamic so the Iterable check still compiles for a map with a declared value type. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/main/resources/dart2/api.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/dart2/api.mustache:71">
P2: When `explode` is omitted for an object query parameter, this template does not enter the new per-entry branch. Update codegen metadata to represent the OpenAPI form-object default as exploded, while preserving explicit `explode: false`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if ({{{paramName}}} != null) { | ||
| {{/required}} | ||
| {{#isMap}} | ||
| {{#isExplode}} |
There was a problem hiding this comment.
P2: When explode is omitted for an object query parameter, this template does not enter the new per-entry branch. Update codegen metadata to represent the OpenAPI form-object default as exploded, while preserving explicit explode: false.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/dart2/api.mustache, line 71:
<comment>When `explode` is omitted for an object query parameter, this template does not enter the new per-entry branch. Update codegen metadata to represent the OpenAPI form-object default as exploded, while preserving explicit `explode: false`.</comment>
<file context>
@@ -67,7 +67,24 @@ class {{{classname}}} {
if ({{{paramName}}} != null) {
{{/required}}
+ {{#isMap}}
+ {{#isExplode}}
+ {{^isDeepObject}}
+ // form style explodes an object into one query parameter per entry, keyed by the property name alone;
</file context>
There was a problem hiding this comment.
The default is already applied upstream of this template: swagger-parser fills in explode for form style (OpenAPIDeserializer sets explode = true when the style is form and the spec leaves it out), so isExplode is true by the time the template runs.
Checked with a jar built from this branch: a parameter with neither style nor explode, and one with only style: form, both generate the per-entry forEach branch. Explicit explode: false stays a single parameter.
The shipped comment is one line now, without the implementation note. Fix the fixture description, which claimed four style/explode combinations where it covers three, and retitle the test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bug
A query parameter whose schema is an object, with
style/explodeleft at their defaults (form,true), must go on the wire as one parameter per entry, keyed by the property name alone. Thedartclient stringified the whole map withMap.toString()into a single parameter instead.called with
{"category": "books", "createdDate:gte": "2023-01-01"}:category=books&createdDate%3Agte=2023-01-01filter=%7Bcategory%3A+books%2C+createdDate%3Agte%3A+2023-01-01%7DSeries
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
dart2/api.mustachehanded every query parameter whole to_queryParams, which knows only a collection format and falls through totoString(). Like the python port (#24802), the generated api now iterates an exploded map entry by entry at the call site;_queryParamsitself is untouched.Objectand gets aMapcast; a declared map needs none (dart analyzeon the generated fixture client: no issues)._queryParamswith'multi', after dropping null items and turning anyIterableinto aList, so the key repeats per element (tld=com&tld=net)._queryParamsalready did.deepObjectandexplode: falseparameters produce the line they did before.Verified
A client generated from the fixture, against a server that echoes its raw query string:
filterortypedFilterwith{"category": "books", "createdDate:gte": "2023-01-01"}category=books&createdDate%3Agte=2023-01-01filterwith{"anyList": ["x", null, 2]}anyList=x&anyList=2page=declaredplusfilterwith{"page": "fromMap"}page=declared&page=fromMap(both kept)deepFilter(deepObject) orflatFilter(explode: false)Map.toString(), as on master (see Known gaps)DartClientCodegenTest#testExplodedObjectQueryParameterfails without the fix.Known gaps
$ref,isModelrather thanisMap) still go on the wire whole; iterating one would send the dart property names rather than the wire names.deepObjectandexplode: falsekeep their singleMap.toString()parameter, as on master. Both are wrong per the spec (deepFilter[category]=books,flatFilter=category,books,…).toString(); form style defines no encoding for them.TypeErrorat the cast instead of going out as itstoString(), as python raises for a non-dict.PR checklist
./bin/generate-samples.sh bin/configs/dart*.yaml): 1 file,petstore_client_lib_fake/lib/api/fake_api.dart, from the petstore fixture'slanguageparameter (a declared map with the default style). The dart-dio samples have their own templates and are untouched.Generated with Claude Code