diff --git a/modules/openapi-generator/src/main/resources/dart2/api.mustache b/modules/openapi-generator/src/main/resources/dart2/api.mustache index 1c995009e018..015bfb9ac1bd 100644 --- a/modules/openapi-generator/src/main/resources/dart2/api.mustache +++ b/modules/openapi-generator/src/main/resources/dart2/api.mustache @@ -67,7 +67,23 @@ class {{{classname}}} { {{^required}} if ({{{paramName}}} != null) { {{/required}} + {{#isMap}} + {{#isExplode}} + {{^isDeepObject}} + // form style, explode: one query parameter per entry, keyed by the property name; a collection repeats the key per non-null element + {{#isFreeFormObject}}({{{paramName}}} as Map){{/isFreeFormObject}}{{^isFreeFormObject}}{{{paramName}}}{{/isFreeFormObject}}.forEach((entryKey, dynamic entryValue) => queryParams.addAll(_queryParams('multi', entryKey.toString(), entryValue is Iterable ? entryValue.where((e) => e != null).toList() : entryValue))); + {{/isDeepObject}} + {{#isDeepObject}} queryParams.addAll(_queryParams('{{{collectionFormat}}}', '{{{baseName}}}', {{{paramName}}})); + {{/isDeepObject}} + {{/isExplode}} + {{^isExplode}} + queryParams.addAll(_queryParams('{{{collectionFormat}}}', '{{{baseName}}}', {{{paramName}}})); + {{/isExplode}} + {{/isMap}} + {{^isMap}} + queryParams.addAll(_queryParams('{{{collectionFormat}}}', '{{{baseName}}}', {{{paramName}}})); + {{/isMap}} {{^required}} } {{/required}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartClientCodegenTest.java index 2215908d3d65..301193c8fa63 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartClientCodegenTest.java @@ -137,6 +137,34 @@ public void testObjectArrayDoesNotUseListFromJson() throws Exception { TestUtils.assertFileContains(modelFile.toPath(), "cast"); } + @Test(description = "Verify a form style, exploded map query parameter goes on the wire one entry per parameter") + public void testExplodedObjectQueryParameter() throws Exception { + List files = generateDartNativeFromSpec( + "src/test/resources/3_0/exploded-object-query-param.yaml"); + + File apiFile = files.stream() + .filter(f -> f.getName().equals("default_api.dart")) + .findFirst() + .orElseThrow(() -> new AssertionError("default_api.dart not found in generated files")); + + // form style with explode - the default - puts every entry on the wire under its own + // property name. Handing the whole map to _queryParams stringifies it with + // Map.toString(), which is what used to happen. A collection value repeats the key per + // non-null element ('multi'). + TestUtils.assertFileContains(apiFile.toPath(), + "(filter as Map).forEach((entryKey, dynamic entryValue) => queryParams.addAll(_queryParams('multi', entryKey.toString(), entryValue is Iterable ? entryValue.where((e) => e != null).toList() : entryValue)));"); + TestUtils.assertFileNotContains(apiFile.toPath(), "_queryParams('', 'filter', filter)"); + + // a declared map behaves the same way, and needs no cast + TestUtils.assertFileContains(apiFile.toPath(), + "typedFilter.forEach((entryKey, dynamic entryValue) => queryParams.addAll(_queryParams('multi', entryKey.toString(), entryValue is Iterable ? entryValue.where((e) => e != null).toList() : entryValue)));"); + + // deepObject and form without explode both keep a single parameter + TestUtils.assertFileContains(apiFile.toPath(), + "_queryParams('', 'deepFilter', deepFilter)", + "_queryParams('', 'flatFilter', flatFilter)"); + } + @Test(description = "Enum properties with defaults should emit enum constructor, not string literal") public void testEnumDefaultUsesEnumConstructor() throws Exception { List files = generateDartNativeFromSpec( diff --git a/samples/client/wildcard-accept-header/java/build.gradle b/samples/client/wildcard-accept-header/java/build.gradle index 181c3a201e08..ce09e960be4d 100644 --- a/samples/client/wildcard-accept-header/java/build.gradle +++ b/samples/client/wildcard-accept-header/java/build.gradle @@ -24,6 +24,10 @@ sourceSets { main.java.srcDirs = ['src/main/java'] } +ext { + jakarta_annotation_version = "1.3.5" +} + if(hasProperty('target') && target == 'android') { apply plugin: 'com.android.library' @@ -103,10 +107,6 @@ if(hasProperty('target') && target == 'android') { } } -ext { - jakarta_annotation_version = "1.3.5" -} - dependencies { implementation "com.google.code.findbugs:jsr305:3.0.2" implementation 'com.squareup.okhttp3:okhttp:4.12.0' diff --git a/samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/lib/api/fake_api.dart b/samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/lib/api/fake_api.dart index c61373a87937..ab0713d278f6 100644 --- a/samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/lib/api/fake_api.dart +++ b/samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/lib/api/fake_api.dart @@ -1382,7 +1382,8 @@ class FakeApi { queryParams.addAll(_queryParams('csv', 'url', url)); queryParams.addAll(_queryParams('multi', 'context', context)); if (language != null) { - queryParams.addAll(_queryParams('', 'language', language)); + // form style, explode: one query parameter per entry, keyed by the property name; a collection repeats the key per non-null element + language.forEach((entryKey, dynamic entryValue) => queryParams.addAll(_queryParams('multi', entryKey.toString(), entryValue is Iterable ? entryValue.where((e) => e != null).toList() : entryValue))); } queryParams.addAll(_queryParams('', 'allowEmpty', allowEmpty));