From a61e05ca8024c208091da93efd0e43e0a277a180 Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 08:39:45 +0200 Subject: [PATCH 01/13] [go] fix: explode object query parameters (#24797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [go] explode object query parameters 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 go client bracketed it as filter[category]=books instead. parameterAddToHeaderOrQuery in go/client.mustache bracketed every map whatever style it was handed. The reflect.Slice case immediately above it already branches on style == "deepObject", so the map case looks like an oversight. The generated api passes the style correctly; nothing downstream read it. The flattening applies to the top level only. The recursive call no longer carries the style down, so anything nested inside an entry keeps the path it has accumulated: {profile: {name: "x"}} is profile[name]=x, and two siblings holding the same property name stay distinct — {a: {x: 1}, b: {x: 2}} is a[x]=1&b[x]=2 rather than a pair of colliding x= entries. Nested slices are unaffected, since the slice branch only special cases deepObject and so behaves identically for an empty style and for form. * fix: [go] an array element does not inherit the form flattening A form style query parameter whose value is an array of maps reached the map branch one level down with the style still set, so the entries were keyed by their property names alone and the accumulated parent path was dropped. Reset the style when descending into array elements, mirroring the reset the map branch already does for its entries: only a map that is the parameter's direct value is exploded to bare property names. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Go3AyndcGwv5tFTwo9aBfy * test: [go] cover the array of objects the element reset exists for The fixture declared four object parameters and no array, so the assertions added for the element reset in 470b7e0 only matched template text that every generated client.go carries: they held whatever the fixture contained. Add the array of objects, and deliberately not exploded. That is the only shape that reaches the array branch of parameterAddToHeaderOrQuery: with explode: true the api template unrolls the slice itself and hands each element over as a map, which enters the map branch instead, so an exploded array would have left the branch as uncovered as no array at all. Not exploded, the whole slice arrives as one form style value: parameterAddToHeaderOrQuery(localVarQueryParams, "arrayFilter", r.arrayFilter, "form", "csv") which is asserted, and is derived from the spec rather than from the template. The behaviour itself lives in generated go rather than in the generator, so it is not reachable from a codegen test. Exercising the helper in a generated client directly, with a []map[string]interface{} under style form: filter[a]=1 with the reset, a=1 without it, while a map that is the parameter's own value stays a=1 either way. mvn -pl modules/openapi-generator -Dtest=GoClientCodegenTest test: 28 tests, 0 failures. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01894FEkx3sDA5wTqjeEo33D * chore: [go] drop the stray go.mod and go.sum changes Running the go toolchain in the issue_20079_go_regex_wrongly_translated sample rewrote its go.mod and go.sum, and the rewrite was swept into the array element commit. bin/generate-samples.sh emits neither, so the samples up to date job reverted both files and failed on the dirty tree. Restore them to what the generator produces. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JHEXQ9zWNyGwVnWPJJnzSo * fix: [go] skip nil entries of an exploded object query parameter A nil entry of a form style exploded map went on the wire as the literal "null" (nullValue=null), and so did a nil element of a list an entry holds (anyList=x&anyList=null&anyList=2). Both are now left out, after unwrapping interfaces and pointers, matching the kotlin port. A typed nil pointer, such as (*string)(nil), panicked with "reflect.Value.Interface on zero Value" because the pointer branch called Elem().Interface() without a nil check. It is now left out as well; that branch is shared with deepObject, where it turns the same panic into an omitted value. deepObject is otherwise unchanged. Co-Authored-By: Claude Opus 5 (1M context) * fix: [go] skip nil entries under deepObject too The previous commit left nil entries out of a form style exploded map but not out of a deepObject one, so the same map went on the wire two ways: a nil entry was dropped under form and sent as filter[k]=null under deepObject. deepObject was also inconsistent with itself, because the nil check added to the pointer branch already dropped a typed nil pointer there. Move the entry check ahead of the form branch, so it applies whatever the style, and skip a nil element in the array branch as well. The elements that survive keep the index they had, so filter[l][0] and filter[l][2] stay where they are rather than closing the gap; the index names a position in the array the caller passed, and renumbering would quietly move the remaining values. Nothing changes for a non-nil value, and a nil parameter passed at the top level still goes out as key=null. Co-Authored-By: Claude Fable 5.1 * revert: [go] keep deepObject's null for a nil entry This reverts cd901bce4bb. deepObject sending [key]=null for a nil entry is behaviour the petstore sample pins in TestQueryDeepObject, and it is the same on master, so it stays out of a change about form style. Form style keeps skipping nil entries, and the nil check in the pointer case stays. Co-Authored-By: Claude Fable 5.1 * test: [go] pin the nil-entry skip inside the form branch The assertion on parameterValueIndirect alone also matched the reverted layout that skipped nil for every style. Pinning what follows the skip fails if it is ever moved ahead of the style branch again, which would break deepObject's [key]=null that TestQueryDeepObject expects. Co-Authored-By: Claude Opus 5 (1M context) * chore: [go] trim the exploded object comments, test and fixture The shipped comments shrink to one line each, parameterValueIndirect returns true once it has unwrapped a non-nil value, the codegen test keeps only the assertions that fail without each fix, and arrayFilter leaves the shared fixture so it is byte-identical across the ports again. The fixture's description now names the three combinations it covers. Co-Authored-By: Claude Opus 5.5 (1M context) * fix: [go] keep the validity check in parameterValueIndirect The constant true reported an invalid reflect.Value as usable. No caller passes one today, but the check guards v.Interface() against a panic and costs nothing. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../src/main/resources/go/client.mustache | 43 ++++++++++++++++++- .../codegen/go/GoClientCodegenTest.java | 29 +++++++++++++ .../echo_api/go-external-refs/client.go | 43 ++++++++++++++++++- samples/client/echo_api/go/client.go | 43 ++++++++++++++++++- .../client.go | 43 ++++++++++++++++++- .../client.go | 43 ++++++++++++++++++- .../others/go/oneof-anyof-required/client.go | 43 ++++++++++++++++++- .../go/oneof-discriminator-lookup/client.go | 43 ++++++++++++++++++- .../client/petstore/go/go-petstore/client.go | 43 ++++++++++++++++++- .../x-auth-id-alias/go-experimental/client.go | 43 ++++++++++++++++++- .../client.go | 43 ++++++++++++++++++- .../petstore/go-petstore-withXml/client.go | 43 ++++++++++++++++++- .../go/go-petstore-aws-signature/client.go | 43 ++++++++++++++++++- .../client/petstore/go/go-petstore/client.go | 43 ++++++++++++++++++- 14 files changed, 562 insertions(+), 26 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/go/client.mustache b/modules/openapi-generator/src/main/resources/go/client.mustache index c0fea7cafc3d..6accea635955 100644 --- a/modules/openapi-generator/src/main/resources/go/client.mustache +++ b/modules/openapi-generator/src/main/resources/go/client.mustache @@ -191,10 +191,14 @@ func parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix stri for i:=0;i files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + TestUtils.assertFileContains(Paths.get(output + "/client.go"), + "keyPrefixForMapEntry = k.String()", + "if !ok { continue } if entry.Kind() == reflect.Slice {", + "case reflect.Ptr: if v.IsNil() { return }", + "styleForElement = \"\""); + + // the api passes the declared style through + Path api = Paths.get(output + "/api_default.go"); + TestUtils.assertFileContains(api, + "parameterAddToHeaderOrQuery(localVarQueryParams, \"filter\", r.filter, \"form\", \"\")", + "parameterAddToHeaderOrQuery(localVarQueryParams, \"typedFilter\", r.typedFilter, \"form\", \"\")", + "parameterAddToHeaderOrQuery(localVarQueryParams, \"deepFilter\", r.deepFilter, \"deepObject\", \"\")", + "parameterAddToHeaderOrQuery(localVarQueryParams, \"flatFilter\", r.flatFilter, \"form\", \"\")"); + } + @Test public void testNullableComposition() throws IOException { File output = Files.createTempDirectory("test").toFile(); diff --git a/samples/client/echo_api/go-external-refs/client.go b/samples/client/echo_api/go-external-refs/client.go index 83d93205607b..b4f4979079a5 100644 --- a/samples/client/echo_api/go-external-refs/client.go +++ b/samples/client/echo_api/go-external-refs/client.go @@ -195,10 +195,14 @@ func parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix stri for i:=0;i Date: Mon, 28 Sep 2026 08:47:55 +0200 Subject: [PATCH 02/13] [python] fix: explode object query parameters (#24802) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [python] explode object query parameters 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 python client JSON-encoded the whole object into a single parameter instead. python/api.mustache appended the object whole, and it was JSON encoded by ApiClient.parameters_to_url_query, which sees no style or explode. Exploded maps are now appended entry by entry. parameters_to_url_query's dict branch is left alone: it is shared by every call site and is still the right fallback for a parameter that is not exploded. python/api_client.mustache quoted values and never quoted names. That was harmless while every name came from baseName in the document, but an exploded object takes its names from the object, so the names became runtime data for the first time and a name carrying & or = would break the URL. Names are now quoted too. * fix: [python] do not apply a collection format to an exploded object entry An exploded object query parameter takes its names from the object, so a property name can collide with the name of a sibling array parameter that declares a collection format. parameters_to_url_query looked the name up in collection_formats without checking that the value was a collection, so the scalar was iterated character by character. In the petstore fixture, testQueryParameterCollectionFormat declares context: multi alongside the exploded map language. Calling it with language={"context": "en"} produced context=e&context=n, and a csv sibling turned https://x.test into h,t,t,p,s,%3A,/,/,x,.,t,e,s,t. The collection format now applies only when the value really is a list or a tuple. Every genuine array parameter is unaffected — multi, csv, ssv and pipes all serialize byte for byte as before. Reported by cubic on #24802. * test: [python] exercise the exploded-name collision guard at runtime Two runtime tests on parameters_to_url_query in the hand-maintained petstore sample tests: a scalar entry named like a declared array parameter goes out as itself, and the declared array parameter still gets its collection format. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Go3AyndcGwv5tFTwo9aBfy * fix: [python] explode object query parameters with declared properties An object with declared properties becomes a model rather than a dict, so it was flagged isModel, missed the isMap branch and was still json encoded as a single parameter. With form style and explode a model is now serialized with sanitize_for_serialization first, so its entries carry the names the properties have on the wire and unset properties are left out, and then exploded like a map. A oneOf or anyOf model holding a primitive does not serialize to a dict and stays a single parameter; deepObject and explode: false are unchanged. An exploded entry holding a list now repeats its name per item (photoUrls=a&photoUrls=b), as java, go and typescript-axios do, instead of going out as the str() of the list. Co-Authored-By: Claude Opus 5 (1M context) * fix: [python] skip null entries and explode a oneOf holding a list A null map value went on the wire as k=None and a null list item as l=None, because parameters_to_url_query quotes str(None). Both exploded branches now skip a null value and a null item, as kotlin, dart and go do. The model branch also appends the serialized value rather than the model. For a oneOf or anyOf holding a primitive that changes nothing on the wire, since param_serialize runs sanitize_for_serialization over every query parameter before parameters_to_url_query, but one holding a list now repeats the parameter name per item instead of sending the list's repr. Adds a runtime test for the quoting of exploded property names, which only the generated text covered until now, and one for a model whose optional properties are left unset. Co-Authored-By: Claude Fable 5.1 * refactor: [python] explode query objects in one ApiClient helper The map and model branches repeated the same loop in every generated operation. ApiClient.explode_query_object now serializes the value and returns one pair per entry, so each branch is a single call, and the helper gets direct unit tests. Comments shrink to one line, the shared fixture's description names the three combinations it covers, and the test titles say what they check. Co-Authored-By: Claude Opus 5.5 (1M context) * chore: [python] regenerate the python-httpx2 samples They landed on master after this branch was cut, so they did not carry the exploded query object changes yet. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../src/main/resources/python/api.mustache | 30 ++++++++ .../main/resources/python/api_client.mustache | 17 +++-- .../python/PythonClientCodegenTest.java | 68 +++++++++++++++++++ .../python/exploded-model-query-param.yaml | 66 ++++++++++++++++++ .../openapi_client/api/query_api.py | 6 +- .../openapi_client/api_client.py | 17 +++-- .../python/openapi_client/api/query_api.py | 6 +- .../python/openapi_client/api_client.py | 17 +++-- .../echo_api/python/tests/test_manual.py | 26 +++++++ .../legacy_model_dict_client/api_client.py | 17 +++-- .../petstore_api/api/fake_api.py | 2 +- .../python-aiohttp/petstore_api/api_client.py | 17 +++-- .../petstore_api/api/fake_api.py | 2 +- .../petstore_api/api_client.py | 17 +++-- .../python-httpx/petstore_api/api/fake_api.py | 2 +- .../python-httpx/petstore_api/api_client.py | 17 +++-- .../petstore_api/api/fake_api.py | 2 +- .../petstore_api/api_client.py | 17 +++-- .../petstore_api/api/fake_api.py | 2 +- .../python-httpx2/petstore_api/api_client.py | 17 +++-- .../petstore_api/api/fake_api.py | 2 +- .../petstore_api/api_client.py | 17 +++-- .../python/petstore_api/api/fake_api.py | 2 +- .../python/petstore_api/api_client.py | 17 +++-- .../petstore/python/tests/test_api_client.py | 41 +++++++++++ 25 files changed, 387 insertions(+), 57 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/python/exploded-model-query-param.yaml diff --git a/modules/openapi-generator/src/main/resources/python/api.mustache b/modules/openapi-generator/src/main/resources/python/api.mustache index 514be3b69a85..3cee5c88c1b8 100644 --- a/modules/openapi-generator/src/main/resources/python/api.mustache +++ b/modules/openapi-generator/src/main/resources/python/api.mustache @@ -347,7 +347,37 @@ https://github.com/OpenAPITools/openapi-generator/blob/c84b949df1a9ec04ba75989cb _query_params.append(('{{baseName}}', {{paramName}})) {{/isDate}} {{^isDateTime}}{{^isDate}} + {{#isMap}} + {{#isExplode}} + {{#isDeepObject}} + _query_params.append(('{{baseName}}', {{paramName}})) + {{/isDeepObject}} + {{^isDeepObject}} + _query_params.extend(self.api_client.explode_query_object('{{baseName}}', {{paramName}})) + {{/isDeepObject}} + {{/isExplode}} + {{^isExplode}} + _query_params.append(('{{baseName}}', {{paramName}})) + {{/isExplode}} + {{/isMap}} + {{#isModel}} + {{#isExplode}} + {{#isDeepObject}} + _query_params.append(('{{baseName}}', {{paramName}})) + {{/isDeepObject}} + {{^isDeepObject}} + _query_params.extend(self.api_client.explode_query_object('{{baseName}}', {{paramName}})) + {{/isDeepObject}} + {{/isExplode}} + {{^isExplode}} + _query_params.append(('{{baseName}}', {{paramName}})) + {{/isExplode}} + {{/isModel}} + {{^isMap}} + {{^isModel}} _query_params.append(('{{baseName}}', {{paramName}}{{#isEnumRef}}.value{{/isEnumRef}})) + {{/isModel}} + {{/isMap}} {{/isDate}}{{/isDateTime}} {{/queryParams}} # process the header parameters diff --git a/modules/openapi-generator/src/main/resources/python/api_client.mustache b/modules/openapi-generator/src/main/resources/python/api_client.mustache index 874d9cd3efef..973a8de03b2b 100644 --- a/modules/openapi-generator/src/main/resources/python/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/python/api_client.mustache @@ -671,6 +671,13 @@ https://github.com/OpenAPITools/openapi-generator/blob/c84b949df1a9ec04ba75989cb new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -689,11 +696,12 @@ https://github.com/OpenAPITools/openapi-generator/blob/c84b949df1a9ec04ba75989cb if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -706,12 +714,13 @@ https://github.com/OpenAPITools/openapi-generator/blob/c84b949df1a9ec04ba75989cb else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java index 5f6272a7a11b..5b7f8c33b8c6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java @@ -803,6 +803,74 @@ public void testInitFileImportsExportsWithCustomApiPackage() throws IOException assertFileContains(apiInitFile.toPath(), "from my_pkg.my_api.pet_api import PetApi"); } + @Test(description = "Verify a form style, exploded map query parameter goes on the wire one entry per parameter") + public void testExplodedObjectQueryParameter() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("python") + .setInputSpec("src/test/resources/3_0/exploded-object-query-param.yaml") + .setOutputDir(output.getAbsolutePath()); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + Path api = Paths.get(output.getAbsolutePath(), "openapi_client", "api", "default_api.py"); + + TestUtils.assertFileContains(api, + "_query_params.extend(self.api_client.explode_query_object('filter', filter))", + "_query_params.extend(self.api_client.explode_query_object('typedFilter', typed_filter))"); + TestUtils.assertFileNotContains(api, "_query_params.append(('filter', filter))"); + + // deepObject and form without explode both keep a single parameter + TestUtils.assertFileContains(api, + "_query_params.append(('deepFilter', deep_filter))", + "_query_params.append(('flatFilter', flat_filter))"); + TestUtils.assertFileNotContains(api, + "explode_query_object('deepFilter', deep_filter)", + "explode_query_object('flatFilter', flat_filter)"); + + // a collection format applies only to a list, so an exploded "context": "en" next to a context: multi array stays context=en + Path apiClient = Paths.get(output.getAbsolutePath(), "openapi_client", "api_client.py"); + TestUtils.assertFileContains(apiClient, + "def explode_query_object(self, name, obj):", + "if k in collection_formats and isinstance(v, (list, tuple)):"); + } + + @Test + public void testExplodedModelQueryParameter() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("python") + .setInputSpec("src/test/resources/3_0/python/exploded-model-query-param.yaml") + .setOutputDir(output.getAbsolutePath()); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + Path api = Paths.get(output.getAbsolutePath(), "openapi_client", "api", "default_api.py"); + + // a model is serialized first (wire names), then exploded; a oneOf holding a primitive stays one parameter, one holding a list repeats the name + TestUtils.assertFileContains(api, + "_query_params.extend(self.api_client.explode_query_object('refFilter', ref_filter))", + "_query_params.extend(self.api_client.explode_query_object('inlineFilter', inline_filter))", + "_query_params.extend(self.api_client.explode_query_object('oneOfFilter', one_of_filter))"); + TestUtils.assertFileNotContains(api, "_query_params.append(('oneOfFilter', one_of_filter))"); + + // deepObject and form without explode both keep a single parameter + TestUtils.assertFileContains(api, + "_query_params.append(('deepFilter', deep_filter))", + "_query_params.append(('flatFilter', flat_filter))"); + TestUtils.assertFileNotContains(api, + "explode_query_object('deepFilter', deep_filter)", + "explode_query_object('flatFilter', flat_filter)"); + } + @Test(description = "Verify default license format uses object notation when poetry1 is false") public void testLicenseFormatInPyprojectToml() throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); diff --git a/modules/openapi-generator/src/test/resources/3_0/python/exploded-model-query-param.yaml b/modules/openapi-generator/src/test/resources/3_0/python/exploded-model-query-param.yaml new file mode 100644 index 000000000000..e7a68518b4a0 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/python/exploded-model-query-param.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.3 +info: + title: Exploded model query parameters + description: > + Query parameters whose schema is an object with declared properties, so the python + generator turns them into models rather than dicts. With the form/true defaults a model + must be exploded like a map, keyed by the names its properties carry on the wire. + version: 1.0.0 +servers: + - url: localhost:8080 +paths: + /items: + get: + operationId: listItems + parameters: + # a $ref to a component schema, with style and explode left at their defaults + - in: query + name: refFilter + schema: + $ref: '#/components/schemas/Filter' + # an inline object with declared properties, which becomes a model of its own + - in: query + name: inlineFilter + schema: + type: object + properties: + category: + type: string + # a oneOf model may hold a primitive or a list, neither of which has properties to explode + - in: query + name: oneOfFilter + schema: + $ref: '#/components/schemas/FilterOrTerm' + # deepObject and form without explode both keep a single parameter + - in: query + name: deepFilter + style: deepObject + explode: true + schema: + $ref: '#/components/schemas/Filter' + - in: query + name: flatFilter + style: form + explode: false + schema: + $ref: '#/components/schemas/Filter' + responses: + '200': + description: a list of items +components: + schemas: + Filter: + type: object + properties: + category: + type: string + # the wire name is not a python identifier, so the model attribute is renamed + createdDate:gte: + type: string + FilterOrTerm: + oneOf: + - $ref: '#/components/schemas/Filter' + - type: string + - type: array + items: + type: string diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api/query_api.py b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api/query_api.py index ac967a796ab2..05837d93cbee 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api/query_api.py +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api/query_api.py @@ -2204,7 +2204,7 @@ def _test_query_style_form_explode_true_array_string_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters @@ -2466,7 +2466,7 @@ def _test_query_style_form_explode_true_object_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters @@ -2728,7 +2728,7 @@ def _test_query_style_form_explode_true_object_all_of_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py index 17c439964cee..77721a58cb85 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py @@ -520,6 +520,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -538,11 +545,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -555,12 +563,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/client/echo_api/python/openapi_client/api/query_api.py b/samples/client/echo_api/python/openapi_client/api/query_api.py index ac967a796ab2..05837d93cbee 100644 --- a/samples/client/echo_api/python/openapi_client/api/query_api.py +++ b/samples/client/echo_api/python/openapi_client/api/query_api.py @@ -2204,7 +2204,7 @@ def _test_query_style_form_explode_true_array_string_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters @@ -2466,7 +2466,7 @@ def _test_query_style_form_explode_true_object_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters @@ -2728,7 +2728,7 @@ def _test_query_style_form_explode_true_object_all_of_serialize( # process the query parameters if query_object is not None: - _query_params.append(('query_object', query_object)) + _query_params.extend(self.api_client.explode_query_object('query_object', query_object)) # process the header parameters # process the form parameters diff --git a/samples/client/echo_api/python/openapi_client/api_client.py b/samples/client/echo_api/python/openapi_client/api_client.py index 17c439964cee..77721a58cb85 100644 --- a/samples/client/echo_api/python/openapi_client/api_client.py +++ b/samples/client/echo_api/python/openapi_client/api_client.py @@ -520,6 +520,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -538,11 +545,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -555,12 +563,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/client/echo_api/python/tests/test_manual.py b/samples/client/echo_api/python/tests/test_manual.py index b63f5b36dcb1..b6d2a30e7c67 100644 --- a/samples/client/echo_api/python/tests/test_manual.py +++ b/samples/client/echo_api/python/tests/test_manual.py @@ -92,6 +92,32 @@ def test_query_style_form_explode_false_array_string_test(self): e = EchoServerResponseParser(api_response) self.assertEqual(e.path, "/query/style_form/explode_false/array_string?query_object=Oh%2C%20hello%20world,abc,DEF") + def test_query_style_form_explode_true_object_test(self): + # form style with explode puts every property of a model on the wire under its own + # name, the name it carries on the wire (photoUrls, not photo_urls), and repeats + # that name for every item of a list + api_instance = openapi_client.QueryApi() + pet = openapi_client.Pet(id=12345, name="Hello World", photoUrls=["http://a.com", "http://b.com"], status="available") + api_response = api_instance.test_query_style_form_explode_true_object(pet) + e = EchoServerResponseParser(api_response) + self.assertEqual(e.path, "/query/style_form/explode_true/object?id=12345&name=Hello%20World&photoUrls=http%3A//a.com&photoUrls=http%3A//b.com&status=available") + + def test_query_style_form_explode_true_object_leaves_out_unset_properties(self): + # a property that was never set contributes nothing + api_instance = openapi_client.QueryApi() + pet = openapi_client.Pet(name="Hello World", photoUrls=["http://a.com"]) + api_response = api_instance.test_query_style_form_explode_true_object(pet) + e = EchoServerResponseParser(api_response) + self.assertEqual(e.path, "/query/style_form/explode_true/object?name=Hello%20World&photoUrls=http%3A//a.com") + + def test_query_style_form_explode_true_object_all_of_test(self): + # an allOf model is exploded the same way; the var_date attribute goes on the wire as date + api_instance = openapi_client.QueryApi() + query = openapi_client.DataQuery(id=1, outcomes=["SUCCESS", "FAILURE"], text="Some text", date=datetime.datetime(2020, 1, 2, 3, 4, 5)) + api_response = api_instance.test_query_style_form_explode_true_object_all_of(query) + e = EchoServerResponseParser(api_response) + self.assertEqual(e.path, "/query/style_form/explode_true/object/allOf?id=1&outcomes=SUCCESS&outcomes=FAILURE&text=Some%20text&date=2020-01-02T03%3A04%3A05") + def testDateTimeQueryWithDateTimeFormat(self): api_instance = openapi_client.QueryApi() datetime_format_backup = api_instance.api_client.configuration.datetime_format # backup dateime_format diff --git a/samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/api_client.py b/samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/api_client.py index baab728d4dd5..dffac725656d 100644 --- a/samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/api_client.py +++ b/samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/api_client.py @@ -628,6 +628,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -646,11 +653,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -663,12 +671,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api/fake_api.py index 3cc84bcc7af2..f5034ea9753a 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api/fake_api.py @@ -9534,7 +9534,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py index 49b32fc8edd6..50741490c29c 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py @@ -519,6 +519,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -537,11 +544,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -554,12 +562,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api/fake_api.py index fda246402ac5..c22c0ab04bda 100644 --- a/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api/fake_api.py @@ -13194,7 +13194,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api_client.py index b7eb3a9170cb..44b7a286135f 100644 --- a/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-httpx-sync/petstore_api/api_client.py @@ -522,6 +522,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -540,11 +547,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -557,12 +565,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-httpx/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-httpx/petstore_api/api/fake_api.py index 9e895f7806ee..6adfebde84fb 100644 --- a/samples/openapi3/client/petstore/python-httpx/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-httpx/petstore_api/api/fake_api.py @@ -9515,7 +9515,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-httpx/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-httpx/petstore_api/api_client.py index b7eb3a9170cb..44b7a286135f 100644 --- a/samples/openapi3/client/petstore/python-httpx/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-httpx/petstore_api/api_client.py @@ -522,6 +522,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -540,11 +547,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -557,12 +565,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api/fake_api.py index fda246402ac5..c22c0ab04bda 100644 --- a/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api/fake_api.py @@ -13194,7 +13194,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api_client.py index b7eb3a9170cb..44b7a286135f 100644 --- a/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-httpx2-sync/petstore_api/api_client.py @@ -522,6 +522,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -540,11 +547,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -557,12 +565,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-httpx2/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-httpx2/petstore_api/api/fake_api.py index 9e895f7806ee..6adfebde84fb 100644 --- a/samples/openapi3/client/petstore/python-httpx2/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-httpx2/petstore_api/api/fake_api.py @@ -9515,7 +9515,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-httpx2/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-httpx2/petstore_api/api_client.py index b7eb3a9170cb..44b7a286135f 100644 --- a/samples/openapi3/client/petstore/python-httpx2/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-httpx2/petstore_api/api_client.py @@ -522,6 +522,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -540,11 +547,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -557,12 +565,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api/fake_api.py index da7188dcf14f..feba662440e8 100644 --- a/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api/fake_api.py @@ -9535,7 +9535,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api_client.py index 8dcae18708cf..eeb694ac158b 100644 --- a/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-lazyImports/petstore_api/api_client.py @@ -519,6 +519,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -537,11 +544,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -554,12 +562,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python/petstore_api/api/fake_api.py index 2373d1680c58..0605972caae5 100755 --- a/samples/openapi3/client/petstore/python/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python/petstore_api/api/fake_api.py @@ -9515,7 +9515,7 @@ def _test_query_parameter_collection_format_serialize( if language is not None: - _query_params.append(('language', language)) + _query_params.extend(self.api_client.explode_query_object('language', language)) if allow_empty is not None: diff --git a/samples/openapi3/client/petstore/python/petstore_api/api_client.py b/samples/openapi3/client/petstore/python/petstore_api/api_client.py index 899983e443d6..c98eb486e651 100755 --- a/samples/openapi3/client/petstore/python/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python/petstore_api/api_client.py @@ -519,6 +519,13 @@ def parameters_to_tuples(self, params, collection_formats): new_params.append((k, v)) return new_params + def explode_query_object(self, name, obj): + """form style, explode: one query parameter per entry, keyed by the property name; a list repeats the name, None is left out""" + obj = self.sanitize_for_serialization(obj) + if not isinstance(obj, dict): + obj = {name: obj} + return [(k, item) for k, v in obj.items() for item in (v if isinstance(v, (list, tuple)) else [v]) if item is not None] + def parameters_to_url_query(self, params, collection_formats): """Get parameters as list of tuples, formatting collections. @@ -537,11 +544,12 @@ def parameters_to_url_query(self, params, collection_formats): if isinstance(v, dict): v = json.dumps(v) - if k in collection_formats: + # a collection format applies only to a list; an exploded entry may share a declared array parameter's name + if k in collection_formats and isinstance(v, (list, tuple)): collection_format = collection_formats[k] if collection_format == 'multi': new_params.extend( - (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + (quote(str(k)), quote(str(value).lower() if isinstance(value, bool) else str(value))) for value in v ) else: @@ -554,12 +562,13 @@ def parameters_to_url_query(self, params, collection_formats): else: # csv is the default delimiter = ',' new_params.append( - (k, delimiter.join( + (quote(str(k)), delimiter.join( quote(str(value).lower() if isinstance(value, bool) else str(value)) for value in v)) ) else: - new_params.append((k, quote(str(v)))) + # names are quoted too: an exploded object's names are runtime data + new_params.append((quote(str(k)), quote(str(v)))) return "&".join(["=".join(map(str, item)) for item in new_params]) diff --git a/samples/openapi3/client/petstore/python/tests/test_api_client.py b/samples/openapi3/client/petstore/python/tests/test_api_client.py index cb0ed9080f51..cea1e548dd31 100644 --- a/samples/openapi3/client/petstore/python/tests/test_api_client.py +++ b/samples/openapi3/client/petstore/python/tests/test_api_client.py @@ -290,6 +290,47 @@ def test_parameters_to_url_query_boolean_value(self): result = self.api_client.parameters_to_url_query([('boolean', True)], {}) self.assertEqual(result, "boolean=true") + def test_parameters_to_url_query_exploded_name_does_not_collide_with_collection_format(self): + # an exploded object query parameter contributes entries under its own property + # names. A scalar entry whose name happens to match a declared array parameter + # must not be joined or repeated as if it were that parameter's list. + params = self.api_client.parameters_to_url_query( + params=[('language', 'nl'), ('context', 'abc')], + collection_formats={'context': 'multi'}) + self.assertEqual(params, "language=nl&context=abc") + + def test_parameters_to_url_query_collection_format_still_applies_to_lists(self): + # the declared array parameter itself still gets its collection format + params = self.api_client.parameters_to_url_query( + params=[('language', 'nl'), ('context', ['a', 'b'])], + collection_formats={'context': 'multi'}) + self.assertEqual(params, "language=nl&context=a&context=b") + + def test_parameters_to_url_query_quotes_reserved_characters_in_names(self): + # an exploded object contributes entries under its own property names, which are + # whatever the spec declares and need not be url safe. The name is quoted just + # like the value, so a property called "createdDate:gte" does not read as a + # separator on the wire. + params = self.api_client.parameters_to_url_query( + params=[('createdDate:gte', '2023-01-01'), ('a&b', 'c')], + collection_formats={}) + self.assertEqual(params, "createdDate%3Agte=2023-01-01&a%26b=c") + + def test_explode_query_object(self): + # one pair per entry; a list repeats the name, None is left out, values are serialized + params = self.api_client.explode_query_object('filter', {'a': 'b', 'k': None, 'l': ['x', None, 2], 'd': parse('2020-01-02').date()}) + self.assertEqual(params, [('a', 'b'), ('l', 'x'), ('l', 2), ('d', '2020-01-02')]) + + def test_explode_query_object_model(self): + # a model explodes under its wire names, and unset properties contribute nothing + params = self.api_client.explode_query_object('pet', petstore_api.Pet(name='doggie', photoUrls=['a', 'b'])) + self.assertEqual(params, [('name', 'doggie'), ('photoUrls', 'a'), ('photoUrls', 'b')]) + + def test_explode_query_object_not_a_dict(self): + # a value that does not serialize to a dict stays under the parameter name + self.assertEqual(self.api_client.explode_query_object('q', 'x'), [('q', 'x')]) + self.assertEqual(self.api_client.explode_query_object('q', ['x', None, 'y']), [('q', 'x'), ('q', 'y')]) + def test_parameters_to_url_query_list_value(self): params = self.api_client.parameters_to_url_query(params=[('list', [1, 2, 3])], collection_formats={'list': 'multi'}) From aa783ed44e2dba7a699a6fcf4ed2f33e475943d7 Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 08:53:36 +0200 Subject: [PATCH 03/13] [csharp] fix: conditional serialization invents value-type defaults on all-optional models (#24877) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [csharp] conditionalSerialization must not flag value-typed properties the caller never set With conditionalSerialization=true, an all-optional model's public constructor took every value-typed property as a plain value type and then guarded the serialization flag with `if (this.AutoRenew != null)` — always true for a non-nullable bool/int/enum, so every value-typed property was marked as set the moment the model was constructed. A partial-update command built as `new UpdateThingCommand(note: "renamed")` went on the wire as {"autoRenew":false,"note":"renamed"}: an invented false on the exact operation where it does the most damage. (The C# compiler flags the old guard itself: CS0472 'the result of the expression is always true'.) Optional value-typed constructor parameters without a default value are now nullable, and the flag is only raised when the argument was actually provided: if (autoRenew != null) { this._AutoRenew = autoRenew.Value; this._flagAutoRenew = true; } Required properties, properties with a spec default, reference types, and the public property surface (plain value-typed properties with flag-raising setters) are all generated byte-for-byte as before; only the optional value-type constructor path changes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ * test: [csharp] cover optional enums and nullable value types in the conditionalSerialization constructor Extends the conditional-serialization-value-types fixture with an optional inline enum, a nullable enum and a nullable integer. The test now asserts that enums take the guarded `.Value` path, that `nullable: true` value types keep their existing null check, and that no parameter type is emitted as `T??`. Co-Authored-By: Claude Opus 5 (1M context) * test: [csharp] drop conditionalSerialization assertions the remaining ones already imply Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../resources/csharp/modelGeneric.mustache | 11 ++- .../CSharpClientCodegenTest.java | 72 +++++++++++++++++++ ...conditional-serialization-value-types.yaml | 63 ++++++++++++++++ .../src/Org.OpenAPITools/Model/ApiResponse.cs | 6 +- .../src/Org.OpenAPITools/Model/AppleReq.cs | 6 +- .../src/Org.OpenAPITools/Model/Banana.cs | 6 +- .../src/Org.OpenAPITools/Model/BananaReq.cs | 6 +- .../src/Org.OpenAPITools/Model/Cat.cs | 6 +- .../src/Org.OpenAPITools/Model/Category.cs | 6 +- .../Org.OpenAPITools/Model/DateOnlyClass.cs | 6 +- .../src/Org.OpenAPITools/Model/EnumArrays.cs | 4 +- .../src/Org.OpenAPITools/Model/EnumTest.cs | 32 ++++----- .../src/Org.OpenAPITools/Model/FormatTest.cs | 66 ++++++++--------- ...dPropertiesAndAdditionalPropertiesClass.cs | 14 ++-- .../Model/Model200Response.cs | 6 +- .../src/Org.OpenAPITools/Model/NumberOnly.cs | 6 +- .../Model/ObjectWithDeprecatedFields.cs | 6 +- .../src/Org.OpenAPITools/Model/Order.cs | 22 +++--- .../Org.OpenAPITools/Model/OuterComposite.cs | 10 +-- .../src/Org.OpenAPITools/Model/Pet.cs | 10 +-- .../Org.OpenAPITools/Model/RequiredClass.cs | 54 +++++++------- .../src/Org.OpenAPITools/Model/Return.cs | 6 +- .../Model/RolesReportsHash.cs | 6 +- .../Model/SpecialModelName.cs | 6 +- .../src/Org.OpenAPITools/Model/Tag.cs | 6 +- .../src/Org.OpenAPITools/Model/TestResult.cs | 4 +- .../src/Org.OpenAPITools/Model/User.cs | 10 +-- .../src/Org.OpenAPITools/Model/Whale.cs | 10 +-- .../src/Org.OpenAPITools/Model/Zebra.cs | 4 +- .../Model/ZeroBasedEnumClass.cs | 4 +- 30 files changed, 309 insertions(+), 165 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/csharp/conditional-serialization-value-types.yaml diff --git a/modules/openapi-generator/src/main/resources/csharp/modelGeneric.mustache b/modules/openapi-generator/src/main/resources/csharp/modelGeneric.mustache index 5a95adaa11fd..384fddd919bf 100644 --- a/modules/openapi-generator/src/main/resources/csharp/modelGeneric.mustache +++ b/modules/openapi-generator/src/main/resources/csharp/modelGeneric.mustache @@ -140,7 +140,7 @@ {{#hasOnlyReadOnly}} [JsonConstructorAttribute] {{/hasOnlyReadOnly}} - public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{#defaultValue}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{defaultValue}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/defaultValue}}{{^defaultValue}}default{{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}){{#parent}} : base({{#parentVars}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{^-last}}, {{/-last}}{{/parentVars}}){{/parent}} + public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{#conditionalSerialization}}{{#vendorExtensions.x-csharp-value-type}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/vendorExtensions.x-csharp-value-type}}{{/conditionalSerialization}}{{/isEnum}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{#defaultValue}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{defaultValue}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/defaultValue}}{{^defaultValue}}default{{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}){{#parent}} : base({{#parentVars}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{^-last}}, {{/-last}}{{/parentVars}}){{/parent}} { {{#vars}} {{^isInherited}} @@ -196,11 +196,20 @@ this.{{name}} = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}; {{/conditionalSerialization}} {{#conditionalSerialization}} + {{#vendorExtensions.x-csharp-value-type}} + if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} != null) + { + this._{{name}} = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.Value; + this._flag{{name}} = true; + } + {{/vendorExtensions.x-csharp-value-type}} + {{^vendorExtensions.x-csharp-value-type}} this._{{name}} = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}; if (this.{{name}} != null) { this._flag{{name}} = true; } + {{/vendorExtensions.x-csharp-value-type}} {{/conditionalSerialization}} {{/defaultValue}} {{/required}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpClientCodegenTest.java index f457707d1336..55e7cb4d720f 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpClientCodegenTest.java @@ -605,6 +605,78 @@ public void testMapResponse() throws Exception { Assert.assertTrue(cr1.isMap); } + @Test(description = "conditionalSerialization: an all-optional model's public constructor must not raise the serialization flag for value-typed properties the caller never passed") + public void testConditionalSerializationValueTypeConstructorFlags() throws IOException { + Map files = generateConditionalSerializationValueTypeModels(true); + + File updateCommand = files.get("UpdateThingCommand.cs"); + assertNotNull(updateCommand); + // value-typed parameters become nullable so "not passed" is representable ... + assertFileContains(updateCommand.toPath(), + "public UpdateThingCommand(bool? autoRenew = default, int? period = default, int? graceDays = default, StatusEnum? status = default, RenewalModeEnum? renewalMode = default, string note = default)"); + // ... and the flag is only raised for arguments that were actually provided + assertFileContains(updateCommand.toPath(), + "if (autoRenew != null)\n" + + " {\n" + + " this._AutoRenew = autoRenew.Value;\n" + + " this._flagAutoRenew = true;\n" + + " }"); + // optional enums take the same guarded path + assertFileContains(updateCommand.toPath(), + "if (status != null)\n" + + " {\n" + + " this._Status = status.Value;\n" + + " this._flagStatus = true;\n" + + " }"); + // a nullable value type already had a meaningful null check and keeps it + assertFileContains(updateCommand.toPath(), + "this._GraceDays = graceDays;\n" + + " if (this.GraceDays != null)\n" + + " {\n" + + " this._flagGraceDays = true;\n" + + " }"); + // the public property surface is untouched: plain value types, flag-raising setters + assertFileContains(updateCommand.toPath(), "public bool AutoRenew", "public int Period"); + assertFileNotContains(updateCommand.toPath(), "public bool? AutoRenew", "public int? Period"); + + // required value-typed properties keep their non-nullable parameter and unconditional assignment + File createCommand = files.get("CreateThingCommand.cs"); + assertNotNull(createCommand); + assertFileContains(createCommand.toPath(), + "public CreateThingCommand(string name = default, bool autoRenew = default, string note = default)"); + assertFileContains(createCommand.toPath(), "this._AutoRenew = autoRenew;"); + assertFileNotContains(createCommand.toPath(), "autoRenew.Value"); + } + + @Test(description = "without conditionalSerialization the constructor keeps plain value-typed parameters") + public void testValueTypeConstructorUnchangedWithoutConditionalSerialization() throws IOException { + Map files = generateConditionalSerializationValueTypeModels(false); + + File updateCommand = files.get("UpdateThingCommand.cs"); + assertNotNull(updateCommand); + assertFileContains(updateCommand.toPath(), + "public UpdateThingCommand(bool autoRenew = default, int period = default, int? graceDays = default, StatusEnum? status = default, RenewalModeEnum? renewalMode = default, string note = default)"); + assertFileNotContains(updateCommand.toPath(), "_flagAutoRenew"); + } + + private Map generateConditionalSerializationValueTypeModels(boolean conditionalSerialization) throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/csharp/conditional-serialization-value-types.yaml"); + final DefaultGenerator defaultGenerator = new DefaultGenerator(); + final ClientOptInput clientOptInput = new ClientOptInput(); + clientOptInput.openAPI(openAPI); + CSharpClientCodegen cSharpClientCodegen = new CSharpClientCodegen(); + cSharpClientCodegen.setLibrary("httpclient"); + cSharpClientCodegen.setOutputDir(output.getAbsolutePath()); + cSharpClientCodegen.additionalProperties().put(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION, String.valueOf(conditionalSerialization)); + clientOptInput.config(cSharpClientCodegen); + defaultGenerator.opts(clientOptInput); + + return defaultGenerator.generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity(), (first, second) -> first)); + } + private Map generateIssue23046Models(boolean nonPublicApi) throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); output.deleteOnExit(); diff --git a/modules/openapi-generator/src/test/resources/3_0/csharp/conditional-serialization-value-types.yaml b/modules/openapi-generator/src/test/resources/3_0/csharp/conditional-serialization-value-types.yaml new file mode 100644 index 000000000000..e5c83202fb31 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/csharp/conditional-serialization-value-types.yaml @@ -0,0 +1,63 @@ +openapi: 3.0.3 +info: + title: conditional serialization value-type flags + version: 1.0.0 +paths: + /things: + post: + operationId: createThing + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateThingCommand' + responses: + '200': + description: created + patch: + operationId: updateThing + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateThingCommand' + responses: + '200': + description: updated +components: + schemas: + UpdateThingCommand: + type: object + description: an all-optional partial-update command; its only constructor is the public one + properties: + autoRenew: + type: boolean + period: + type: integer + graceDays: + type: integer + nullable: true + status: + type: string + enum: [active, suspended] + renewalMode: + type: string + enum: [manual, automatic] + nullable: true + note: + type: string + CreateThingCommand: + type: object + description: required value type keeps its non-nullable constructor parameter + required: + - name + - autoRenew + properties: + name: + type: string + autoRenew: + type: boolean + note: + type: string diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ApiResponse.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ApiResponse.cs index 616d4f5a451c..4e4ea7a29af6 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ApiResponse.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ApiResponse.cs @@ -38,11 +38,11 @@ public partial class ApiResponse : IEquatable, IValidatableObject /// code. /// type. /// message. - public ApiResponse(int code = default, string type = default, string message = default) + public ApiResponse(int? code = default, string type = default, string message = default) { - this._Code = code; - if (this.Code != null) + if (code != null) { + this._Code = code.Value; this._flagCode = true; } this._Type = type; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/AppleReq.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/AppleReq.cs index 071709bc6254..3c6e86b9dd16 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/AppleReq.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/AppleReq.cs @@ -42,7 +42,7 @@ protected AppleReq() { } /// /// cultivar (required). /// mealy. - public AppleReq(string cultivar = default, bool mealy = default) + public AppleReq(string cultivar = default, bool? mealy = default) { // to ensure "cultivar" is required (not null) if (cultivar == null) @@ -50,9 +50,9 @@ public AppleReq(string cultivar = default, bool mealy = default) throw new ArgumentNullException("cultivar is a required property for AppleReq and cannot be null"); } this._Cultivar = cultivar; - this._Mealy = mealy; - if (this.Mealy != null) + if (mealy != null) { + this._Mealy = mealy.Value; this._flagMealy = true; } } diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Banana.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Banana.cs index 40effe24e769..8fa191bb07d9 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Banana.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Banana.cs @@ -36,11 +36,11 @@ public partial class Banana : IEquatable, IValidatableObject /// Initializes a new instance of the class. /// /// lengthCm. - public Banana(decimal lengthCm = default) + public Banana(decimal? lengthCm = default) { - this._LengthCm = lengthCm; - if (this.LengthCm != null) + if (lengthCm != null) { + this._LengthCm = lengthCm.Value; this._flagLengthCm = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/BananaReq.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/BananaReq.cs index ef540c9d2a5b..c77084eec25a 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/BananaReq.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/BananaReq.cs @@ -42,12 +42,12 @@ protected BananaReq() { } /// /// lengthCm (required). /// sweet. - public BananaReq(decimal lengthCm = default, bool sweet = default) + public BananaReq(decimal lengthCm = default, bool? sweet = default) { this._LengthCm = lengthCm; - this._Sweet = sweet; - if (this.Sweet != null) + if (sweet != null) { + this._Sweet = sweet.Value; this._flagSweet = true; } } diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Cat.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Cat.cs index 6b5189557252..eda5df130c9a 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Cat.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Cat.cs @@ -47,11 +47,11 @@ protected Cat() /// declawed. /// className (required) (default to "Cat"). /// color (default to "red"). - public Cat(bool declawed = default, string className = @"Cat", string color = @"red") : base(className, color) + public Cat(bool? declawed = default, string className = @"Cat", string color = @"red") : base(className, color) { - this._Declawed = declawed; - if (this.Declawed != null) + if (declawed != null) { + this._Declawed = declawed.Value; this._flagDeclawed = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Category.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Category.cs index c15741a47c8f..4a38856efa12 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Category.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Category.cs @@ -45,7 +45,7 @@ protected Category() /// /// id. /// name (required) (default to "default-name"). - public Category(long id = default, string name = @"default-name") + public Category(long? id = default, string name = @"default-name") { // to ensure "name" is required (not null) if (name == null) @@ -53,9 +53,9 @@ public Category(long id = default, string name = @"default-name") throw new ArgumentNullException("name is a required property for Category and cannot be null"); } this._Name = name; - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/DateOnlyClass.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/DateOnlyClass.cs index cbf300737196..3923331b6697 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/DateOnlyClass.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/DateOnlyClass.cs @@ -36,11 +36,11 @@ public partial class DateOnlyClass : IEquatable, IValidatableObje /// Initializes a new instance of the class. /// /// dateOnlyProperty. - public DateOnlyClass(DateTime dateOnlyProperty = default) + public DateOnlyClass(DateTime? dateOnlyProperty = default) { - this._DateOnlyProperty = dateOnlyProperty; - if (this.DateOnlyProperty != null) + if (dateOnlyProperty != null) { + this._DateOnlyProperty = dateOnlyProperty.Value; this._flagDateOnlyProperty = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumArrays.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumArrays.cs index 774f416dcaec..aa75b5a918b7 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumArrays.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumArrays.cs @@ -103,9 +103,9 @@ public enum ArrayEnumEnum /// arrayEnum. public EnumArrays(JustSymbolEnum? justSymbol = default, List arrayEnum = default) { - this._JustSymbol = justSymbol; - if (this.JustSymbol != null) + if (justSymbol != null) { + this._JustSymbol = justSymbol.Value; this._flagJustSymbol = true; } this._ArrayEnum = arrayEnum; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumTest.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumTest.cs index e3fd5593c813..6158d41ee7cc 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumTest.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/EnumTest.cs @@ -450,44 +450,44 @@ protected EnumTest() public EnumTest(EnumStringEnum? enumString = default, EnumStringRequiredEnum enumStringRequired = default, EnumIntegerEnum? enumInteger = default, EnumIntegerOnlyEnum? enumIntegerOnly = default, EnumNumberEnum? enumNumber = default, OuterEnum? outerEnum = default, OuterEnumInteger? outerEnumInteger = default, OuterEnumDefaultValue? outerEnumDefaultValue = default, OuterEnumIntegerDefaultValue? outerEnumIntegerDefaultValue = default) { this._EnumStringRequired = enumStringRequired; - this._EnumString = enumString; - if (this.EnumString != null) + if (enumString != null) { + this._EnumString = enumString.Value; this._flagEnumString = true; } - this._EnumInteger = enumInteger; - if (this.EnumInteger != null) + if (enumInteger != null) { + this._EnumInteger = enumInteger.Value; this._flagEnumInteger = true; } - this._EnumIntegerOnly = enumIntegerOnly; - if (this.EnumIntegerOnly != null) + if (enumIntegerOnly != null) { + this._EnumIntegerOnly = enumIntegerOnly.Value; this._flagEnumIntegerOnly = true; } - this._EnumNumber = enumNumber; - if (this.EnumNumber != null) + if (enumNumber != null) { + this._EnumNumber = enumNumber.Value; this._flagEnumNumber = true; } - this._OuterEnum = outerEnum; - if (this.OuterEnum != null) + if (outerEnum != null) { + this._OuterEnum = outerEnum.Value; this._flagOuterEnum = true; } - this._OuterEnumInteger = outerEnumInteger; - if (this.OuterEnumInteger != null) + if (outerEnumInteger != null) { + this._OuterEnumInteger = outerEnumInteger.Value; this._flagOuterEnumInteger = true; } - this._OuterEnumDefaultValue = outerEnumDefaultValue; - if (this.OuterEnumDefaultValue != null) + if (outerEnumDefaultValue != null) { + this._OuterEnumDefaultValue = outerEnumDefaultValue.Value; this._flagOuterEnumDefaultValue = true; } - this._OuterEnumIntegerDefaultValue = outerEnumIntegerDefaultValue; - if (this.OuterEnumIntegerDefaultValue != null) + if (outerEnumIntegerDefaultValue != null) { + this._OuterEnumIntegerDefaultValue = outerEnumIntegerDefaultValue.Value; this._flagOuterEnumIntegerDefaultValue = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/FormatTest.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/FormatTest.cs index cb78ccd5bd53..09fae29f3c7e 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/FormatTest.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/FormatTest.cs @@ -71,7 +71,7 @@ protected FormatTest() /// stringFormattedAsDecimalRequired (required). /// duplicatePropertyName2. /// duplicatePropertyName. - public FormatTest(int integer = default, int int32 = default, int int32Range = default, int int64Positive = default, int int64Negative = default, int int64PositiveExclusive = default, int int64NegativeExclusive = default, uint unsignedInteger = default, long int64 = default, ulong unsignedLong = default, decimal number = default, float varFloat = default, double varDouble = default, decimal varDecimal = default, string varString = default, byte[] varByte = default, System.IO.Stream binary = default, DateTime date = default, DateTime dateTime = default, Guid uuid = default, string password = default, string patternWithDigits = default, string patternWithDigitsAndDelimiter = default, string patternWithBackslash = default, decimal stringFormattedAsDecimal = default, decimal stringFormattedAsDecimalRequired = default, string duplicatePropertyName2 = default, string duplicatePropertyName = default) + public FormatTest(int? integer = default, int? int32 = default, int? int32Range = default, int? int64Positive = default, int? int64Negative = default, int? int64PositiveExclusive = default, int? int64NegativeExclusive = default, uint? unsignedInteger = default, long? int64 = default, ulong? unsignedLong = default, decimal number = default, float? varFloat = default, double? varDouble = default, decimal? varDecimal = default, string varString = default, byte[] varByte = default, System.IO.Stream binary = default, DateTime date = default, DateTime? dateTime = default, Guid? uuid = default, string password = default, string patternWithDigits = default, string patternWithDigitsAndDelimiter = default, string patternWithBackslash = default, decimal? stringFormattedAsDecimal = default, decimal stringFormattedAsDecimalRequired = default, string duplicatePropertyName2 = default, string duplicatePropertyName = default) { this._Number = number; // to ensure "varByte" is required (not null) @@ -88,69 +88,69 @@ public FormatTest(int integer = default, int int32 = default, int int32Range = d } this._Password = password; this._StringFormattedAsDecimalRequired = stringFormattedAsDecimalRequired; - this._Integer = integer; - if (this.Integer != null) + if (integer != null) { + this._Integer = integer.Value; this._flagInteger = true; } - this._Int32 = int32; - if (this.Int32 != null) + if (int32 != null) { + this._Int32 = int32.Value; this._flagInt32 = true; } - this._Int32Range = int32Range; - if (this.Int32Range != null) + if (int32Range != null) { + this._Int32Range = int32Range.Value; this._flagInt32Range = true; } - this._Int64Positive = int64Positive; - if (this.Int64Positive != null) + if (int64Positive != null) { + this._Int64Positive = int64Positive.Value; this._flagInt64Positive = true; } - this._Int64Negative = int64Negative; - if (this.Int64Negative != null) + if (int64Negative != null) { + this._Int64Negative = int64Negative.Value; this._flagInt64Negative = true; } - this._Int64PositiveExclusive = int64PositiveExclusive; - if (this.Int64PositiveExclusive != null) + if (int64PositiveExclusive != null) { + this._Int64PositiveExclusive = int64PositiveExclusive.Value; this._flagInt64PositiveExclusive = true; } - this._Int64NegativeExclusive = int64NegativeExclusive; - if (this.Int64NegativeExclusive != null) + if (int64NegativeExclusive != null) { + this._Int64NegativeExclusive = int64NegativeExclusive.Value; this._flagInt64NegativeExclusive = true; } - this._UnsignedInteger = unsignedInteger; - if (this.UnsignedInteger != null) + if (unsignedInteger != null) { + this._UnsignedInteger = unsignedInteger.Value; this._flagUnsignedInteger = true; } - this._Int64 = int64; - if (this.Int64 != null) + if (int64 != null) { + this._Int64 = int64.Value; this._flagInt64 = true; } - this._UnsignedLong = unsignedLong; - if (this.UnsignedLong != null) + if (unsignedLong != null) { + this._UnsignedLong = unsignedLong.Value; this._flagUnsignedLong = true; } - this._Float = varFloat; - if (this.Float != null) + if (varFloat != null) { + this._Float = varFloat.Value; this._flagFloat = true; } - this._Double = varDouble; - if (this.Double != null) + if (varDouble != null) { + this._Double = varDouble.Value; this._flagDouble = true; } - this._Decimal = varDecimal; - if (this.Decimal != null) + if (varDecimal != null) { + this._Decimal = varDecimal.Value; this._flagDecimal = true; } this._String = varString; @@ -163,14 +163,14 @@ public FormatTest(int integer = default, int int32 = default, int int32Range = d { this._flagBinary = true; } - this._DateTime = dateTime; - if (this.DateTime != null) + if (dateTime != null) { + this._DateTime = dateTime.Value; this._flagDateTime = true; } - this._Uuid = uuid; - if (this.Uuid != null) + if (uuid != null) { + this._Uuid = uuid.Value; this._flagUuid = true; } this._PatternWithDigits = patternWithDigits; @@ -188,9 +188,9 @@ public FormatTest(int integer = default, int int32 = default, int int32Range = d { this._flagPatternWithBackslash = true; } - this._StringFormattedAsDecimal = stringFormattedAsDecimal; - if (this.StringFormattedAsDecimal != null) + if (stringFormattedAsDecimal != null) { + this._StringFormattedAsDecimal = stringFormattedAsDecimal.Value; this._flagStringFormattedAsDecimal = true; } this._DuplicatePropertyName2 = duplicatePropertyName2; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/MixedPropertiesAndAdditionalPropertiesClass.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/MixedPropertiesAndAdditionalPropertiesClass.cs index 6da43bec2431..187bca7b07fb 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/MixedPropertiesAndAdditionalPropertiesClass.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/MixedPropertiesAndAdditionalPropertiesClass.cs @@ -39,21 +39,21 @@ public partial class MixedPropertiesAndAdditionalPropertiesClass : IEquatableuuid. /// dateTime. /// map. - public MixedPropertiesAndAdditionalPropertiesClass(Guid uuidWithPattern = default, Guid uuid = default, DateTime dateTime = default, Dictionary map = default) + public MixedPropertiesAndAdditionalPropertiesClass(Guid? uuidWithPattern = default, Guid? uuid = default, DateTime? dateTime = default, Dictionary map = default) { - this._UuidWithPattern = uuidWithPattern; - if (this.UuidWithPattern != null) + if (uuidWithPattern != null) { + this._UuidWithPattern = uuidWithPattern.Value; this._flagUuidWithPattern = true; } - this._Uuid = uuid; - if (this.Uuid != null) + if (uuid != null) { + this._Uuid = uuid.Value; this._flagUuid = true; } - this._DateTime = dateTime; - if (this.DateTime != null) + if (dateTime != null) { + this._DateTime = dateTime.Value; this._flagDateTime = true; } this._Map = map; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Model200Response.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Model200Response.cs index a04ab5a4b683..482a70ef3e78 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Model200Response.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Model200Response.cs @@ -37,11 +37,11 @@ public partial class Model200Response : IEquatable, IValidatab /// /// name. /// varClass. - public Model200Response(int name = default, string varClass = default) + public Model200Response(int? name = default, string varClass = default) { - this._Name = name; - if (this.Name != null) + if (name != null) { + this._Name = name.Value; this._flagName = true; } this._Class = varClass; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/NumberOnly.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/NumberOnly.cs index d5eeb4624871..c5143c1a5a62 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/NumberOnly.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/NumberOnly.cs @@ -39,11 +39,11 @@ public partial class NumberOnly : IEquatable, IValidatableObject /// Initializes a new instance of the class. /// /// justNumber. - public NumberOnly(decimal justNumber = default) + public NumberOnly(decimal? justNumber = default) { - this._JustNumber = justNumber; - if (this.JustNumber != null) + if (justNumber != null) { + this._JustNumber = justNumber.Value; this._flagJustNumber = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ObjectWithDeprecatedFields.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ObjectWithDeprecatedFields.cs index 411cdf8a15af..2fe23672496a 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ObjectWithDeprecatedFields.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ObjectWithDeprecatedFields.cs @@ -39,16 +39,16 @@ public partial class ObjectWithDeprecatedFields : IEquatableid. /// deprecatedRef. /// bars. - public ObjectWithDeprecatedFields(string uuid = default, decimal id = default, DeprecatedObject deprecatedRef = default, List bars = default) + public ObjectWithDeprecatedFields(string uuid = default, decimal? id = default, DeprecatedObject deprecatedRef = default, List bars = default) { this._Uuid = uuid; if (this.Uuid != null) { this._flagUuid = true; } - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } this._DeprecatedRef = deprecatedRef; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Order.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Order.cs index c660fee22884..ff5a01987383 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Order.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Order.cs @@ -94,31 +94,31 @@ public bool ShouldSerializeStatus() /// shipDate. /// Order Status. /// complete (default to false). - public Order(long id = default, long petId = default, int quantity = default, DateTime shipDate = default, StatusEnum? status = default, bool complete = false) + public Order(long? id = default, long? petId = default, int? quantity = default, DateTime? shipDate = default, StatusEnum? status = default, bool complete = false) { - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } - this._PetId = petId; - if (this.PetId != null) + if (petId != null) { + this._PetId = petId.Value; this._flagPetId = true; } - this._Quantity = quantity; - if (this.Quantity != null) + if (quantity != null) { + this._Quantity = quantity.Value; this._flagQuantity = true; } - this._ShipDate = shipDate; - if (this.ShipDate != null) + if (shipDate != null) { + this._ShipDate = shipDate.Value; this._flagShipDate = true; } - this._Status = status; - if (this.Status != null) + if (status != null) { + this._Status = status.Value; this._flagStatus = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/OuterComposite.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/OuterComposite.cs index a73853ef32ec..406c8c52f0e8 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/OuterComposite.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/OuterComposite.cs @@ -38,11 +38,11 @@ public partial class OuterComposite : IEquatable, IValidatableOb /// myNumber. /// myString. /// myBoolean. - public OuterComposite(decimal myNumber = default, string myString = default, bool myBoolean = default) + public OuterComposite(decimal? myNumber = default, string myString = default, bool? myBoolean = default) { - this._MyNumber = myNumber; - if (this.MyNumber != null) + if (myNumber != null) { + this._MyNumber = myNumber.Value; this._flagMyNumber = true; } this._MyString = myString; @@ -50,9 +50,9 @@ public OuterComposite(decimal myNumber = default, string myString = default, boo { this._flagMyString = true; } - this._MyBoolean = myBoolean; - if (this.MyBoolean != null) + if (myBoolean != null) { + this._MyBoolean = myBoolean.Value; this._flagMyBoolean = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Pet.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Pet.cs index beff340cf26e..8b1bd25ba47d 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Pet.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Pet.cs @@ -102,7 +102,7 @@ protected Pet() /// photoUrls (required). /// tags. /// pet status in the store. - public Pet(long id = default, Category category = default, string name = default, List photoUrls = default, List tags = default, StatusEnum? status = default) + public Pet(long? id = default, Category category = default, string name = default, List photoUrls = default, List tags = default, StatusEnum? status = default) { // to ensure "name" is required (not null) if (name == null) @@ -116,9 +116,9 @@ public Pet(long id = default, Category category = default, string name = default throw new ArgumentNullException("photoUrls is a required property for Pet and cannot be null"); } this._PhotoUrls = photoUrls; - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } this._Category = category; @@ -131,9 +131,9 @@ public Pet(long id = default, Category category = default, string name = default { this._flagTags = true; } - this._Status = status; - if (this.Status != null) + if (status != null) { + this._Status = status.Value; this._flagStatus = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RequiredClass.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RequiredClass.cs index eafc6ff0b6cd..db4dbf576bcc 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RequiredClass.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RequiredClass.cs @@ -853,7 +853,7 @@ protected RequiredClass() /// requiredNotnullableArrayOfString (required). /// notrequiredNullableArrayOfString. /// notrequiredNotnullableArrayOfString. - public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNotnullableintegerProp = default, int? notRequiredNullableIntegerProp = default, int notRequiredNotnullableintegerProp = default, string requiredNullableStringProp = default, string requiredNotnullableStringProp = default, string notrequiredNullableStringProp = default, string notrequiredNotnullableStringProp = default, bool? requiredNullableBooleanProp = default, bool requiredNotnullableBooleanProp = default, bool? notrequiredNullableBooleanProp = default, bool notrequiredNotnullableBooleanProp = default, DateTime? requiredNullableDateProp = default, DateTime requiredNotNullableDateProp = default, DateTime? notRequiredNullableDateProp = default, DateTime notRequiredNotnullableDateProp = default, DateTime requiredNotnullableDatetimeProp = default, DateTime? requiredNullableDatetimeProp = default, DateTime? notrequiredNullableDatetimeProp = default, DateTime notrequiredNotnullableDatetimeProp = default, RequiredNullableEnumIntegerEnum requiredNullableEnumInteger = default, RequiredNotnullableEnumIntegerEnum requiredNotnullableEnumInteger = default, NotrequiredNullableEnumIntegerEnum? notrequiredNullableEnumInteger = default, NotrequiredNotnullableEnumIntegerEnum? notrequiredNotnullableEnumInteger = default, RequiredNullableEnumIntegerOnlyEnum requiredNullableEnumIntegerOnly = default, RequiredNotnullableEnumIntegerOnlyEnum requiredNotnullableEnumIntegerOnly = default, NotrequiredNullableEnumIntegerOnlyEnum? notrequiredNullableEnumIntegerOnly = default, NotrequiredNotnullableEnumIntegerOnlyEnum? notrequiredNotnullableEnumIntegerOnly = default, RequiredNotnullableEnumStringEnum requiredNotnullableEnumString = default, RequiredNullableEnumStringEnum requiredNullableEnumString = default, NotrequiredNullableEnumStringEnum? notrequiredNullableEnumString = default, NotrequiredNotnullableEnumStringEnum? notrequiredNotnullableEnumString = default, OuterEnumDefaultValue requiredNullableOuterEnumDefaultValue = default, OuterEnumDefaultValue requiredNotnullableOuterEnumDefaultValue = default, OuterEnumDefaultValue? notrequiredNullableOuterEnumDefaultValue = default, OuterEnumDefaultValue? notrequiredNotnullableOuterEnumDefaultValue = default, Guid? requiredNullableUuid = default, Guid requiredNotnullableUuid = default, Guid? notrequiredNullableUuid = default, Guid notrequiredNotnullableUuid = default, List requiredNullableArrayOfString = default, List requiredNotnullableArrayOfString = default, List notrequiredNullableArrayOfString = default, List notrequiredNotnullableArrayOfString = default) + public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNotnullableintegerProp = default, int? notRequiredNullableIntegerProp = default, int? notRequiredNotnullableintegerProp = default, string requiredNullableStringProp = default, string requiredNotnullableStringProp = default, string notrequiredNullableStringProp = default, string notrequiredNotnullableStringProp = default, bool? requiredNullableBooleanProp = default, bool requiredNotnullableBooleanProp = default, bool? notrequiredNullableBooleanProp = default, bool? notrequiredNotnullableBooleanProp = default, DateTime? requiredNullableDateProp = default, DateTime requiredNotNullableDateProp = default, DateTime? notRequiredNullableDateProp = default, DateTime? notRequiredNotnullableDateProp = default, DateTime requiredNotnullableDatetimeProp = default, DateTime? requiredNullableDatetimeProp = default, DateTime? notrequiredNullableDatetimeProp = default, DateTime? notrequiredNotnullableDatetimeProp = default, RequiredNullableEnumIntegerEnum requiredNullableEnumInteger = default, RequiredNotnullableEnumIntegerEnum requiredNotnullableEnumInteger = default, NotrequiredNullableEnumIntegerEnum? notrequiredNullableEnumInteger = default, NotrequiredNotnullableEnumIntegerEnum? notrequiredNotnullableEnumInteger = default, RequiredNullableEnumIntegerOnlyEnum requiredNullableEnumIntegerOnly = default, RequiredNotnullableEnumIntegerOnlyEnum requiredNotnullableEnumIntegerOnly = default, NotrequiredNullableEnumIntegerOnlyEnum? notrequiredNullableEnumIntegerOnly = default, NotrequiredNotnullableEnumIntegerOnlyEnum? notrequiredNotnullableEnumIntegerOnly = default, RequiredNotnullableEnumStringEnum requiredNotnullableEnumString = default, RequiredNullableEnumStringEnum requiredNullableEnumString = default, NotrequiredNullableEnumStringEnum? notrequiredNullableEnumString = default, NotrequiredNotnullableEnumStringEnum? notrequiredNotnullableEnumString = default, OuterEnumDefaultValue requiredNullableOuterEnumDefaultValue = default, OuterEnumDefaultValue requiredNotnullableOuterEnumDefaultValue = default, OuterEnumDefaultValue? notrequiredNullableOuterEnumDefaultValue = default, OuterEnumDefaultValue? notrequiredNotnullableOuterEnumDefaultValue = default, Guid? requiredNullableUuid = default, Guid requiredNotnullableUuid = default, Guid? notrequiredNullableUuid = default, Guid? notrequiredNotnullableUuid = default, List requiredNullableArrayOfString = default, List requiredNotnullableArrayOfString = default, List notrequiredNullableArrayOfString = default, List notrequiredNotnullableArrayOfString = default) { // to ensure "requiredNullableIntegerProp" is required (not null) if (requiredNullableIntegerProp == null) @@ -927,9 +927,9 @@ public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNot { this._flagNotRequiredNullableIntegerProp = true; } - this._NotRequiredNotnullableintegerProp = notRequiredNotnullableintegerProp; - if (this.NotRequiredNotnullableintegerProp != null) + if (notRequiredNotnullableintegerProp != null) { + this._NotRequiredNotnullableintegerProp = notRequiredNotnullableintegerProp.Value; this._flagNotRequiredNotnullableintegerProp = true; } this._NotrequiredNullableStringProp = notrequiredNullableStringProp; @@ -947,9 +947,9 @@ public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNot { this._flagNotrequiredNullableBooleanProp = true; } - this._NotrequiredNotnullableBooleanProp = notrequiredNotnullableBooleanProp; - if (this.NotrequiredNotnullableBooleanProp != null) + if (notrequiredNotnullableBooleanProp != null) { + this._NotrequiredNotnullableBooleanProp = notrequiredNotnullableBooleanProp.Value; this._flagNotrequiredNotnullableBooleanProp = true; } this._NotRequiredNullableDateProp = notRequiredNullableDateProp; @@ -957,9 +957,9 @@ public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNot { this._flagNotRequiredNullableDateProp = true; } - this._NotRequiredNotnullableDateProp = notRequiredNotnullableDateProp; - if (this.NotRequiredNotnullableDateProp != null) + if (notRequiredNotnullableDateProp != null) { + this._NotRequiredNotnullableDateProp = notRequiredNotnullableDateProp.Value; this._flagNotRequiredNotnullableDateProp = true; } this._NotrequiredNullableDatetimeProp = notrequiredNullableDatetimeProp; @@ -967,49 +967,49 @@ public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNot { this._flagNotrequiredNullableDatetimeProp = true; } - this._NotrequiredNotnullableDatetimeProp = notrequiredNotnullableDatetimeProp; - if (this.NotrequiredNotnullableDatetimeProp != null) + if (notrequiredNotnullableDatetimeProp != null) { + this._NotrequiredNotnullableDatetimeProp = notrequiredNotnullableDatetimeProp.Value; this._flagNotrequiredNotnullableDatetimeProp = true; } - this._NotrequiredNullableEnumInteger = notrequiredNullableEnumInteger; - if (this.NotrequiredNullableEnumInteger != null) + if (notrequiredNullableEnumInteger != null) { + this._NotrequiredNullableEnumInteger = notrequiredNullableEnumInteger.Value; this._flagNotrequiredNullableEnumInteger = true; } - this._NotrequiredNotnullableEnumInteger = notrequiredNotnullableEnumInteger; - if (this.NotrequiredNotnullableEnumInteger != null) + if (notrequiredNotnullableEnumInteger != null) { + this._NotrequiredNotnullableEnumInteger = notrequiredNotnullableEnumInteger.Value; this._flagNotrequiredNotnullableEnumInteger = true; } - this._NotrequiredNullableEnumIntegerOnly = notrequiredNullableEnumIntegerOnly; - if (this.NotrequiredNullableEnumIntegerOnly != null) + if (notrequiredNullableEnumIntegerOnly != null) { + this._NotrequiredNullableEnumIntegerOnly = notrequiredNullableEnumIntegerOnly.Value; this._flagNotrequiredNullableEnumIntegerOnly = true; } - this._NotrequiredNotnullableEnumIntegerOnly = notrequiredNotnullableEnumIntegerOnly; - if (this.NotrequiredNotnullableEnumIntegerOnly != null) + if (notrequiredNotnullableEnumIntegerOnly != null) { + this._NotrequiredNotnullableEnumIntegerOnly = notrequiredNotnullableEnumIntegerOnly.Value; this._flagNotrequiredNotnullableEnumIntegerOnly = true; } - this._NotrequiredNullableEnumString = notrequiredNullableEnumString; - if (this.NotrequiredNullableEnumString != null) + if (notrequiredNullableEnumString != null) { + this._NotrequiredNullableEnumString = notrequiredNullableEnumString.Value; this._flagNotrequiredNullableEnumString = true; } - this._NotrequiredNotnullableEnumString = notrequiredNotnullableEnumString; - if (this.NotrequiredNotnullableEnumString != null) + if (notrequiredNotnullableEnumString != null) { + this._NotrequiredNotnullableEnumString = notrequiredNotnullableEnumString.Value; this._flagNotrequiredNotnullableEnumString = true; } - this._NotrequiredNullableOuterEnumDefaultValue = notrequiredNullableOuterEnumDefaultValue; - if (this.NotrequiredNullableOuterEnumDefaultValue != null) + if (notrequiredNullableOuterEnumDefaultValue != null) { + this._NotrequiredNullableOuterEnumDefaultValue = notrequiredNullableOuterEnumDefaultValue.Value; this._flagNotrequiredNullableOuterEnumDefaultValue = true; } - this._NotrequiredNotnullableOuterEnumDefaultValue = notrequiredNotnullableOuterEnumDefaultValue; - if (this.NotrequiredNotnullableOuterEnumDefaultValue != null) + if (notrequiredNotnullableOuterEnumDefaultValue != null) { + this._NotrequiredNotnullableOuterEnumDefaultValue = notrequiredNotnullableOuterEnumDefaultValue.Value; this._flagNotrequiredNotnullableOuterEnumDefaultValue = true; } this._NotrequiredNullableUuid = notrequiredNullableUuid; @@ -1017,9 +1017,9 @@ public RequiredClass(int? requiredNullableIntegerProp = default, int requiredNot { this._flagNotrequiredNullableUuid = true; } - this._NotrequiredNotnullableUuid = notrequiredNotnullableUuid; - if (this.NotrequiredNotnullableUuid != null) + if (notrequiredNotnullableUuid != null) { + this._NotrequiredNotnullableUuid = notrequiredNotnullableUuid.Value; this._flagNotrequiredNotnullableUuid = true; } this._NotrequiredNullableArrayOfString = notrequiredNullableArrayOfString; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Return.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Return.cs index b14f39f9b6ba..997837d01848 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Return.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Return.cs @@ -47,7 +47,7 @@ protected Return() /// varLock (required). /// varAbstract (required). /// varUnsafe. - public Return(int varReturn = default, string varLock = default, string varAbstract = default, string varUnsafe = default) + public Return(int? varReturn = default, string varLock = default, string varAbstract = default, string varUnsafe = default) { // to ensure "varLock" is required (not null) if (varLock == null) @@ -61,9 +61,9 @@ public Return(int varReturn = default, string varLock = default, string varAbstr throw new ArgumentNullException("varAbstract is a required property for Return and cannot be null"); } this._Abstract = varAbstract; - this._VarReturn = varReturn; - if (this.VarReturn != null) + if (varReturn != null) { + this._VarReturn = varReturn.Value; this._flagVarReturn = true; } this._Unsafe = varUnsafe; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RolesReportsHash.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RolesReportsHash.cs index f63539ea26e2..0077a271cc21 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RolesReportsHash.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/RolesReportsHash.cs @@ -37,11 +37,11 @@ public partial class RolesReportsHash : IEquatable, IValidatab /// /// roleUuid. /// role. - public RolesReportsHash(Guid roleUuid = default, RolesReportsHashRole role = default) + public RolesReportsHash(Guid? roleUuid = default, RolesReportsHashRole role = default) { - this._RoleUuid = roleUuid; - if (this.RoleUuid != null) + if (roleUuid != null) { + this._RoleUuid = roleUuid.Value; this._flagRoleUuid = true; } this._Role = role; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/SpecialModelName.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/SpecialModelName.cs index bfb952dcd355..e0408e63a083 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/SpecialModelName.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/SpecialModelName.cs @@ -37,11 +37,11 @@ public partial class SpecialModelName : IEquatable, IValidatab /// /// specialPropertyName. /// varSpecialModelName. - public SpecialModelName(long specialPropertyName = default, string varSpecialModelName = default) + public SpecialModelName(long? specialPropertyName = default, string varSpecialModelName = default) { - this._SpecialPropertyName = specialPropertyName; - if (this.SpecialPropertyName != null) + if (specialPropertyName != null) { + this._SpecialPropertyName = specialPropertyName.Value; this._flagSpecialPropertyName = true; } this._VarSpecialModelName = varSpecialModelName; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Tag.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Tag.cs index 71370874b768..877c8249222f 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Tag.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Tag.cs @@ -37,11 +37,11 @@ public partial class Tag : IEquatable, IValidatableObject /// /// id. /// name. - public Tag(long id = default, string name = default) + public Tag(long? id = default, string name = default) { - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } this._Name = name; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/TestResult.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/TestResult.cs index a91f85d19849..b8ebba133514 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/TestResult.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/TestResult.cs @@ -66,9 +66,9 @@ public bool ShouldSerializeCode() /// list of named parameters for current message. public TestResult(TestResultCode? code = default, string uuid = default, Dictionary data = default) { - this._Code = code; - if (this.Code != null) + if (code != null) { + this._Code = code.Value; this._flagCode = true; } this._Uuid = uuid; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/User.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/User.cs index 0d5b93e58ffa..203166eeeec3 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/User.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/User.cs @@ -47,11 +47,11 @@ public partial class User : IEquatable, IValidatableObject /// test code generation for nullable objects. Value must be a map of strings to values or the 'null' value.. /// test code generation for any type Here the 'type' attribute is not specified, which means the value can be anything, including the null value, string, number, boolean, array or object. See https://github.com/OAI/OpenAPI-Specification/issues/1389. /// test code generation for any type Here the 'type' attribute is not specified, which means the value can be anything, including the null value, string, number, boolean, array or object. The 'nullable' attribute does not change the allowed values.. - public User(long id = default, string username = default, string firstName = default, string lastName = default, string email = default, string password = default, string phone = default, int userStatus = default, Object objectWithNoDeclaredProps = default, Object objectWithNoDeclaredPropsNullable = default, Object anyTypeProp = default, Object anyTypePropNullable = default) + public User(long? id = default, string username = default, string firstName = default, string lastName = default, string email = default, string password = default, string phone = default, int? userStatus = default, Object objectWithNoDeclaredProps = default, Object objectWithNoDeclaredPropsNullable = default, Object anyTypeProp = default, Object anyTypePropNullable = default) { - this._Id = id; - if (this.Id != null) + if (id != null) { + this._Id = id.Value; this._flagId = true; } this._Username = username; @@ -84,9 +84,9 @@ public User(long id = default, string username = default, string firstName = def { this._flagPhone = true; } - this._UserStatus = userStatus; - if (this.UserStatus != null) + if (userStatus != null) { + this._UserStatus = userStatus.Value; this._flagUserStatus = true; } this._ObjectWithNoDeclaredProps = objectWithNoDeclaredProps; diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Whale.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Whale.cs index 1455bffc70d3..f5e732af315a 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Whale.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Whale.cs @@ -46,7 +46,7 @@ protected Whale() /// hasBaleen. /// hasTeeth. /// className (required). - public Whale(bool hasBaleen = default, bool hasTeeth = default, string className = default) + public Whale(bool? hasBaleen = default, bool? hasTeeth = default, string className = default) { // to ensure "className" is required (not null) if (className == null) @@ -54,14 +54,14 @@ public Whale(bool hasBaleen = default, bool hasTeeth = default, string className throw new ArgumentNullException("className is a required property for Whale and cannot be null"); } this._ClassName = className; - this._HasBaleen = hasBaleen; - if (this.HasBaleen != null) + if (hasBaleen != null) { + this._HasBaleen = hasBaleen.Value; this._flagHasBaleen = true; } - this._HasTeeth = hasTeeth; - if (this.HasTeeth != null) + if (hasTeeth != null) { + this._HasTeeth = hasTeeth.Value; this._flagHasTeeth = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Zebra.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Zebra.cs index 9717a1ace7d4..de06d726d8f7 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Zebra.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/Zebra.cs @@ -104,9 +104,9 @@ public Zebra(TypeEnum? type = default, string className = default) throw new ArgumentNullException("className is a required property for Zebra and cannot be null"); } this._ClassName = className; - this._Type = type; - if (this.Type != null) + if (type != null) { + this._Type = type.Value; this._flagType = true; } this.AdditionalProperties = new Dictionary(); diff --git a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ZeroBasedEnumClass.cs b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ZeroBasedEnumClass.cs index 262a2a49c3f9..9804d01afe37 100644 --- a/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ZeroBasedEnumClass.cs +++ b/samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/src/Org.OpenAPITools/Model/ZeroBasedEnumClass.cs @@ -83,9 +83,9 @@ public bool ShouldSerializeZeroBasedEnum() /// zeroBasedEnum. public ZeroBasedEnumClass(ZeroBasedEnumEnum? zeroBasedEnum = default) { - this._ZeroBasedEnum = zeroBasedEnum; - if (this.ZeroBasedEnum != null) + if (zeroBasedEnum != null) { + this._ZeroBasedEnum = zeroBasedEnum.Value; this._flagZeroBasedEnum = true; } this.AdditionalProperties = new Dictionary(); From af3a9f778620442d7a978c8eb334c720f3e665a6 Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 08:58:04 +0200 Subject: [PATCH 04/13] [kotlin] fix: explode object query parameters (#24867) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [kotlin] explode object query parameters in the jvm-okhttp client 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 jvm-okhttp client serialized the whole map with toString() into a single parameter instead (filter={tld=com, ...}), and a declared map came out as concatenated Pair.toString()s. The jvm-ktor library already explodes these correctly. The query building in api.mustache now iterates an exploded map entry by entry, the same way jvm-ktor does. 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. No sample changes: no kotlin sample spec declares a free-form or map-typed query parameter with the default style, the echo api's object query parameters are all model-typed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ * fix: [kotlin] skip nulls, repeat collections and append exploded query entries Follow-up to the review of the exploded object query parameters in the jvm-okhttp client: - a null map entry is left out instead of going on the wire as the literal "null" (Any?.toString() returns "null" rather than throwing) - a collection value repeats the key once per element instead of being sent as the collection's toString(); scalar values and elements go through parameterToString, so dates get the same format as other query parameters - an exploded entry named like another query parameter no longer overwrites it, or gets overwritten by it: the exploded parameters are now emitted after every declared query parameter and append to an existing entry, so both values end up on the wire To place the exploded parameters after the declared ones, the codegen flags them with x-kotlin-explode-form-object (jvm-okhttp libraries only) and api.mustache emits them in a second pass; the three duplicated required/optional/nullable branches of the first version are gone. deepObject and explode: false parameters are unchanged, and no sample changes. Co-Authored-By: Claude Opus 5 (1M context) * chore: [kotlin] trim the exploded query comment and simplify the entry loop One comment line above the loop instead of three; the when now only picks the elements and filterNotNull/parameterToString run once. Drop the defensive !isModel check, which no other port has: a map-typed parameter is never isModel. 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) * test: [kotlin] keep the shared fixture byte-identical with the other ports Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../languages/KotlinClientCodegen.java | 8 ++++ .../libraries/jvm-okhttp/api.mustache | 18 ++++++++ .../kotlin/KotlinClientCodegenApiTest.java | 42 +++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java index 90b3c8ad9bcf..8db7c83669b2 100755 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java @@ -1157,6 +1157,14 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List>() .apply { {{#queryParams}} + {{^vendorExtensions.x-kotlin-explode-form-object}} {{^required}} if ({{{paramName}}} != null) { {{#isModel}} @@ -251,6 +252,23 @@ import {{packageName}}.infrastructure.Serializer {{/isModel}} {{/isNullable}} {{/required}} + {{/vendorExtensions.x-kotlin-explode-form-object}} + {{/queryParams}} + {{#queryParams}} + {{#vendorExtensions.x-kotlin-explode-form-object}} + // form style, explode: one query parameter per entry, keyed by the property name, appended after the declared ones; null is left out, a collection repeats the key + ({{{paramName}}} as? kotlin.collections.Map<*, *>)?.forEach { (key, value) -> + val values = when (value) { + is kotlin.collections.Iterable<*> -> value.toList() + is kotlin.Array<*> -> value.toList() + else -> listOf(value) + }.filterNotNull().map { parameterToString(it) } + if (key != null && values.isNotEmpty()) { + val name = key.toString() + put(name, getOrElse(name) { emptyList() } + values) + } + } + {{/vendorExtensions.x-kotlin-explode-form-object}} {{/queryParams}} } {{/hasQueryParams}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenApiTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenApiTest.java index 1bad07e5acf0..da4faa356004 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenApiTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenApiTest.java @@ -15,6 +15,7 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; @@ -253,6 +254,47 @@ public void testJvmKtorQueryParamWithTypeObject() throws IOException { assertFileNotContains(defaultApi.toPath(), "mapDeep?.apply {"); } + @Test(description = "Verify a form style, exploded map query parameter goes on the wire one entry per parameter") + public void testExplodedObjectQueryParameterJvmOkhttp() throws IOException { + OpenAPI openAPI = readOpenAPI("src/test/resources/3_0/exploded-object-query-param.yaml"); + + KotlinClientCodegen codegen = createCodegen(ClientLibrary.JVM_OKHTTP4); + DefaultGenerator generator = new DefaultGenerator(); + enableOnlyApiGeneration(generator); + + List files = generator.opts(createClientOptInput(openAPI, codegen)).generate(); + File defaultApi = files.stream().filter(file -> file.getName().equals("DefaultApi.kt")).findAny().orElseThrow(); + + String content = new String(Files.readAllBytes(defaultApi.toPath()), StandardCharsets.UTF_8); + + // form style with explode - the default - puts every entry on the wire under its own + // property name. Serializing the whole map with toString() is what used to happen. + assertFileContains(defaultApi.toPath(), "(filter as? kotlin.collections.Map<*, *>)?.forEach { (key, value) ->"); + assertFileNotContains(defaultApi.toPath(), "put(\"filter\", listOf(filter.toString()))"); + + // a declared map behaves the same way + assertFileContains(defaultApi.toPath(), "(typedFilter as? kotlin.collections.Map<*, *>)?.forEach { (key, value) ->"); + assertFileNotContains(defaultApi.toPath(), "put(\"typedFilter\""); + + // a null entry is left out rather than sent as "null", a collection repeats the key + // once per element rather than going out as its toString(), and an entry is appended + // rather than replacing a query parameter of the same name + assertFileContains(defaultApi.toPath(), + "is kotlin.collections.Iterable<*> -> value.toList()", + "}.filterNotNull().map { parameterToString(it) }", + "put(name, getOrElse(name) { emptyList() } + values)"); + + // deepObject and form without explode both keep a single parameter + assertFileContains(defaultApi.toPath(), + "put(\"deepFilter\", listOf(deepFilter.toString()))", + "put(\"flatFilter\", listOf(flatFilter.toString()))"); + + // the exploded entries are added after every declared parameter, so a declared + // parameter's put cannot overwrite an entry that happens to share its name + Assert.assertTrue(content.indexOf("(filter as? kotlin.collections.Map") > content.indexOf("put(\"flatFilter\""), + "exploded entries must be added after the declared query parameters"); + } + private static void assertFileContainsLine(List lines, String line) { Assert.assertListContains(lines, s -> s.equals(line), line); } From 9f97fc4c5b6d9d209f516655854ea7f62131c871 Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 08:59:31 +0200 Subject: [PATCH 05/13] [php] fix: enumUnknownDefaultCase declares the unknown-default member but never deserializes to it (#24878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [php] enumUnknownDefaultCase must reach deserialization, not just declare the member With enumUnknownDefaultCase=true the generator adds UNKNOWN_DEFAULT_OPEN_API to every enum class, but ObjectSerializer::deserialize still threw InvalidArgumentException for any wire value outside the list — the error message even advertises 'unknown_default_open_api' as an allowed value while refusing to produce it. The flag changed nothing observable: one new enum value from the server still destroyed the whole response. ObjectSerializer::deserialize now returns the enum's declared unknown-default member for an unrecognized value, guarded twice: the branch is only generated when enumUnknownDefaultCase=true (clients generated without the flag are byte-identical to before), and at runtime it only fires for enum classes that actually declare UNKNOWN_DEFAULT_OPEN_API. Java's fromValue behavior under the same flag is the reference. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ * test: [php] one assertion per enumUnknownDefaultCase serializer case Co-Authored-By: Claude Opus 5.5 (1M context) * test: [php] keep the assertion on the UNKNOWN_DEFAULT_OPEN_API defined() guard Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../resources/php/ObjectSerializer.mustache | 6 ++++++ .../codegen/php/PhpClientCodegenTest.java | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache b/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache index 780ac7e05c03..425bcdde9a7a 100644 --- a/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache +++ b/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache @@ -485,6 +485,12 @@ class ObjectSerializer if (method_exists($class, 'getAllowableEnumValues')) { if (!in_array($data, $class::getAllowableEnumValues(), true)) { + {{#enumUnknownDefaultCase}} + if (defined("$class::UNKNOWN_DEFAULT_OPEN_API")) { + return constant("$class::UNKNOWN_DEFAULT_OPEN_API"); + } + + {{/enumUnknownDefaultCase}} $imploded = implode("', '", $class::getAllowableEnumValues()); throw new \InvalidArgumentException("Invalid value for enum '$class', must be one of: '$imploded'"); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpClientCodegenTest.java index 7ffd48a1fd09..803a460aea0a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpClientCodegenTest.java @@ -136,6 +136,15 @@ public void testEnumUnknownDefaultCaseDeserializationEnabled() throws Exception Assert.assertListContains(modelContent, a -> a.equals("$color = self::COLOR_UNKNOWN_DEFAULT_OPEN_API;"), ""); Assert.assertListNotContains(modelContent, a -> a.equals("\"Invalid value '%s' for 'color', must be one of '%s'\","), ""); + + List serializerContent = Files + .readAllLines(files.get("ObjectSerializer.php").toPath()) + .stream() + .map(String::trim) + .collect(Collectors.toList()); + + Assert.assertListContains(serializerContent, a -> a.equals("if (defined(\"$class::UNKNOWN_DEFAULT_OPEN_API\")) {"), ""); + Assert.assertListContains(serializerContent, a -> a.equals("return constant(\"$class::UNKNOWN_DEFAULT_OPEN_API\");"), ""); } @Test @@ -164,6 +173,14 @@ public void testEnumUnknownDefaultCaseDeserializationDisabled() throws Exception Assert.assertListNotContains(modelContent, a -> a.equals("$color = self::COLOR_UNKNOWN_DEFAULT_OPEN_API;"), ""); Assert.assertListContains(modelContent, a -> a.equalsIgnoreCase("\"Invalid value '%s' for 'color', must be one of '%s'\","), ""); + + List serializerContent = Files + .readAllLines(files.get("ObjectSerializer.php").toPath()) + .stream() + .map(String::trim) + .collect(Collectors.toList()); + + Assert.assertListNotContains(serializerContent, a -> a.equals("return constant(\"$class::UNKNOWN_DEFAULT_OPEN_API\");"), ""); } @Test public void testDateTimeLengthValidationIsNotGenerated() throws Exception { From 7bee48f1dc36b6bb1a9f0edb8442b00b982fa40d Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 10:23:55 +0200 Subject: [PATCH 06/13] [ruby] fix: enumUnknownDefaultCase declares the unknown-default member but never deserializes to it (#24879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: [ruby] enumUnknownDefaultCase must reach build_from_hash, not just declare the member With enumUnknownDefaultCase=true the generator adds UNKNOWN_DEFAULT_OPEN_API to every enum class's constants and all_vars, but the enum's build_from_hash still raised 'Invalid ENUM value' for any wire value outside the list — the flag changed nothing observable, and one new enum value from the server still destroyed the whole response. build_from_hash now falls back to the enum's own unknown-default member — the last declared enum var, which under the flag is always the appended UNKNOWN_DEFAULT_OPEN_API. The tolerance lives in each generated enum class; without the flag the generated code is byte-identical to before and still raises. Java's fromValue behavior under the same flag is the reference. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ * test: [ruby] drop enumUnknownDefaultCase assertions the build_from_hash checks imply Co-Authored-By: Claude Opus 5.5 (1M context) * test: [ruby] keep the enumUnknownDefaultCase member value assertions Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 Co-authored-by: William Cheng --- .../partial_model_enum_class.mustache | 5 ++ .../codegen/ruby/RubyClientCodegenTest.java | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/modules/openapi-generator/src/main/resources/ruby-client/partial_model_enum_class.mustache b/modules/openapi-generator/src/main/resources/ruby-client/partial_model_enum_class.mustache index d2ddbff19777..1c3276ec1254 100644 --- a/modules/openapi-generator/src/main/resources/ruby-client/partial_model_enum_class.mustache +++ b/modules/openapi-generator/src/main/resources/ruby-client/partial_model_enum_class.mustache @@ -18,6 +18,11 @@ # @return [String] The enum value def build_from_hash(value) return value if {{classname}}.all_vars.include?(value) + {{#enumUnknownDefaultCase}} + {{#allowableValues}}{{#enumVars}}{{#-last}}{{{name}}}{{/-last}}{{/enumVars}}{{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{^enumUnknownDefaultCase}} raise "Invalid ENUM value #{value} for class #{{{classname}}}" + {{/enumUnknownDefaultCase}} end end \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/ruby/RubyClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/ruby/RubyClientCodegenTest.java index a9914a9b8acf..c688b531eec8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/ruby/RubyClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/ruby/RubyClientCodegenTest.java @@ -858,4 +858,58 @@ public void testAliasModelDoesNotDelegateToItsBuiltInSuperclass() throws Excepti "def self.acceptable_openapi_nullable\n openapi_nullable\n end"); TestUtils.assertFileNotContains(arrayAlias, "superclass.", "super(attributes)"); } + + @Test(description = "enumUnknownDefaultCase=true makes build_from_hash fall back to the unknown default member") + public void testEnumUnknownDefaultCaseBuildFromHashFallback() throws Exception { + final File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/enum.yaml"); + CodegenConfig codegenConfig = new RubyClientCodegen(); + codegenConfig.additionalProperties().put(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "true"); + codegenConfig.setOutputDir(output.getAbsolutePath()); + + ClientOptInput clientOptInput = new ClientOptInput().openAPI(openAPI).config(codegenConfig); + new DefaultGenerator().opts(clientOptInput).generate(); + + Path stringEnum = new File(output, "lib/openapi_client/models/type.rb").toPath(); + TestUtils.assertFileContains(stringEnum, "UNKNOWN_DEFAULT_OPEN_API = \"unknown_default_open_api\".freeze"); + TestUtils.assertFileContains(stringEnum, + "def build_from_hash(value)\n" + + " return value if Type.all_vars.include?(value)\n" + + " UNKNOWN_DEFAULT_OPEN_API\n" + + " end"); + TestUtils.assertFileNotContains(stringEnum, "raise \"Invalid ENUM value"); + + // the integer enum's unknown-default member keeps its (pre-existing) numeric-prefixed name + Path integerEnum = new File(output, "lib/openapi_client/models/integer_enum.rb").toPath(); + TestUtils.assertFileContains(integerEnum, "Nunknown_default_open_api = 11184809.freeze"); + TestUtils.assertFileContains(integerEnum, + "def build_from_hash(value)\n" + + " return value if IntegerEnum.all_vars.include?(value)\n" + + " Nunknown_default_open_api\n" + + " end"); + TestUtils.assertFileNotContains(integerEnum, "raise \"Invalid ENUM value"); + } + + @Test(description = "without enumUnknownDefaultCase build_from_hash still raises on unknown values") + public void testEnumBuildFromHashRaisesByDefault() throws Exception { + final File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/enum.yaml"); + CodegenConfig codegenConfig = new RubyClientCodegen(); + codegenConfig.setOutputDir(output.getAbsolutePath()); + + ClientOptInput clientOptInput = new ClientOptInput().openAPI(openAPI).config(codegenConfig); + new DefaultGenerator().opts(clientOptInput).generate(); + + Path stringEnum = new File(output, "lib/openapi_client/models/type.rb").toPath(); + TestUtils.assertFileNotContains(stringEnum, "UNKNOWN_DEFAULT_OPEN_API"); + TestUtils.assertFileContains(stringEnum, + "def build_from_hash(value)\n" + + " return value if Type.all_vars.include?(value)\n" + + " raise \"Invalid ENUM value #{value} for class #Type\"\n" + + " end"); + } } From 11221eb547d094b636742d2fee2dd6f761e7a521 Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 10:30:02 +0200 Subject: [PATCH 07/13] [kotlin] fix: enumUnknownDefaultCase's moshi fallback adapters break nullable enum fields (#24894) * [kotlin] fix: make the enumUnknownDefaultCase moshi fallback adapters null-safe SerializerHelper registers a moshi EnumJsonAdapter with withUnknownFallback per enum, but EnumJsonAdapter is not null-safe and it is registered bare: any model with a nullable enum property throws "value was null! Wrap in .nullSafe() to write nullable values" on a null value, reading and writing alike. The flag traded unknown-value tolerance for a regression on every optional enum field. Append .nullSafe() in both branches. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz * test: [kotlin] shorten the null-safe adapter test comment Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../SerializerHelper.kt.mustache | 4 +-- .../kotlin/KotlinClientCodegenModelTest.java | 27 +++++++++++++++++++ .../client/infrastructure/SerializerHelper.kt | 10 +++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/jvm-common/infrastructure/SerializerHelper.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/jvm-common/infrastructure/SerializerHelper.kt.mustache index 7e0fb08639d8..3b30175d9945 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/jvm-common/infrastructure/SerializerHelper.kt.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/jvm-common/infrastructure/SerializerHelper.kt.mustache @@ -19,7 +19,7 @@ import com.squareup.moshi.adapters.EnumJsonAdapter {{#enumVars}} {{#-last}} .add({{modelPackage}}.{{classname}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}::class.java) - .withUnknownFallback({{modelPackage}}.{{classname}}.{{&name}})) + .withUnknownFallback({{modelPackage}}.{{classname}}.{{&name}}).nullSafe()) {{/-last}} {{/enumVars}} {{/allowableValues}} @@ -33,7 +33,7 @@ import com.squareup.moshi.adapters.EnumJsonAdapter {{#enumVars}} {{#-last}} .add({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java) - .withUnknownFallback({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}.{{&name}})) + .withUnknownFallback({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}.{{&name}}).nullSafe()) {{/-last}} {{/enumVars}} {{/allowableValues}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java index dad52b7ae72b..353754b2bc00 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -990,6 +990,33 @@ public void testJacksonEnumsExposeDecodeOrNullHelper() throws IOException { "Expected exactly one @JsonCreator annotation in the generated enum, found " + jsonCreatorCount); } + @Test + public void testMoshiEnumUnknownDefaultCaseAdaptersAreNullSafe() throws IOException { + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(KOTLIN_GENERATOR) + .setLibrary("jvm-okhttp4") + .setAdditionalProperties(new HashMap<>() {{ + put(CodegenConstants.SERIALIZATION_LIBRARY, "moshi"); + put(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "true"); + }}) + .setInputSpec("src/test/resources/3_0/enum.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(); + + generator.opts(clientOptInput).generate(); + + final Path helperKt = Paths.get(output + "/src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt"); + + // EnumJsonAdapter is not null-safe, so every registered fallback adapter must be wrapped + TestUtils.assertFileContains(helperKt, ".nullSafe())"); + TestUtils.assertFileNotContains(helperKt, "unknown_default_open_api))"); + } + @Test public void testJacksonEnumsWithUnknownDefaultCase() throws IOException { File output = Files.createTempDirectory("test").toFile(); diff --git a/samples/client/petstore/kotlin-enum-default-value/src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt b/samples/client/petstore/kotlin-enum-default-value/src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt index 420b3c3c22b8..35b6efb435ba 100644 --- a/samples/client/petstore/kotlin-enum-default-value/src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt +++ b/samples/client/petstore/kotlin-enum-default-value/src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt @@ -7,14 +7,14 @@ object SerializerHelper { fun addEnumUnknownDefaultCase(moshiBuilder: Moshi.Builder): Moshi.Builder { return moshiBuilder .add(org.openapitools.client.models.ComplexEnum::class.java, EnumJsonAdapter.create(org.openapitools.client.models.ComplexEnum::class.java) - .withUnknownFallback(org.openapitools.client.models.ComplexEnum.unknown_default_open_api)) + .withUnknownFallback(org.openapitools.client.models.ComplexEnum.unknown_default_open_api).nullSafe()) .add(org.openapitools.client.models.ModelWithEnumPropertyHavingDefault.PropertyName::class.java, EnumJsonAdapter.create(org.openapitools.client.models.ModelWithEnumPropertyHavingDefault.PropertyName::class.java) - .withUnknownFallback(org.openapitools.client.models.ModelWithEnumPropertyHavingDefault.PropertyName.unknown_default_open_api)) + .withUnknownFallback(org.openapitools.client.models.ModelWithEnumPropertyHavingDefault.PropertyName.unknown_default_open_api).nullSafe()) .add(org.openapitools.client.models.PropertyOfDay.DaysOfWeek::class.java, EnumJsonAdapter.create(org.openapitools.client.models.PropertyOfDay.DaysOfWeek::class.java) - .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.DaysOfWeek.unknown_default_open_api)) + .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.DaysOfWeek.unknown_default_open_api).nullSafe()) .add(org.openapitools.client.models.PropertyOfDay.MonthOfYear::class.java, EnumJsonAdapter.create(org.openapitools.client.models.PropertyOfDay.MonthOfYear::class.java) - .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.MonthOfYear.unknown_default_open_api)) + .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.MonthOfYear.unknown_default_open_api).nullSafe()) .add(org.openapitools.client.models.PropertyOfDay.HolidayTypes::class.java, EnumJsonAdapter.create(org.openapitools.client.models.PropertyOfDay.HolidayTypes::class.java) - .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.HolidayTypes.unknown_default_open_api)) + .withUnknownFallback(org.openapitools.client.models.PropertyOfDay.HolidayTypes.unknown_default_open_api).nullSafe()) } } From ab136df1d6696f6eeac9499136cbfd6df9c64112 Mon Sep 17 00:00:00 2001 From: Roemer Vlasveld Date: Mon, 28 Sep 2026 10:36:45 +0200 Subject: [PATCH 08/13] [kotlin] do not HTML-escape property names in generated KDoc @param (#25028) Kotlin wraps a property name that is not a valid identifier in back-ticks, for example `2ndField` for a schema property named "2nd_field". The model templates rendered that name with a double-brace Mustache tag, which HTML escapes it, so the generated KDoc read: @param `2ndField` An optional property ... Switch the two affected templates to a triple-brace tag, matching what kotlin-client, kotlin-server, ktor2 and jaxrs-spec already do. No committed sample changes, because no existing kotlin-spring or kotlin-vertx sample has a property name that needs back-ticks. The new test uses param-json-property.yaml, which does. Claude-Session: https://claude.ai/code/session_01VBn7VWUxZL6mTUbg21xNjm Co-authored-by: Claude Opus 5 (1M context) --- .../resources/kotlin-spring/dataClass.mustache | 4 ++-- .../kotlin-vertx-server/data_class.mustache | 2 +- .../spring/KotlinSpringServerCodegenTest.java | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache index 45f39c691392..261ddc00d115 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache @@ -1,10 +1,10 @@ /** * {{{description}}} {{#requiredVars}} - * @param {{name}} {{{description}}} + * @param {{{name}}} {{{description}}} {{/requiredVars}} {{#optionalVars}} - * @param {{name}} {{{description}}} + * @param {{{name}}} {{{description}}} {{/optionalVars}} */{{#discriminator}} {{>typeInfoAnnotation}}{{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-vertx-server/data_class.mustache b/modules/openapi-generator/src/main/resources/kotlin-vertx-server/data_class.mustache index 338856586b27..c676209311da 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-vertx-server/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-vertx-server/data_class.mustache @@ -9,7 +9,7 @@ import com.fasterxml.jackson.annotation.JsonInclude /** * {{{description}}} {{#vars}} - * @param {{name}} {{{description}}} + * @param {{{name}}} {{{description}}} {{/vars}} */ {{#parcelizeModels}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 74a72e8680a3..8164d64ab2f6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -7797,6 +7797,20 @@ public void paramJsonPropertyAnnotationWithDigitStartingPropertyName() throws IO ); } + @Test(description = "KDoc @param must name the property literally, without HTML-escaping its back-ticks") + public void kdocParamNameIsNotHtmlEscaped() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/kotlin/param-json-property.yaml" + ); + + File itemFile = files.get("Item.kt"); + assertThat(itemFile).isNotNull(); + // "2nd_field" is not a valid Kotlin identifier, so the property is declared as `2ndField`. + // The KDoc must carry those back-ticks as written rather than as HTML entities. + assertFileNotContains(itemFile.toPath(), "`"); + assertFileContains(itemFile.toPath(), "@param `2ndField` "); + } + /** * Regression test for https://github.com/OpenAPITools/openapi-generator/issues/24139 From 801dbe663d387d56c2a328643f16e590f2663d5d Mon Sep 17 00:00:00 2001 From: Wiebren Braakman Date: Mon, 28 Sep 2026 11:03:28 +0200 Subject: [PATCH 09/13] [rust] fix: exploded deepObject free-form query parameters do not compile (#24899) * [rust] fix: exploded deepObject free-form query parameters do not compile The exploded deepObject branch walks every map-flagged parameter with .len()/.iter() - fine for a HashMap-typed map (an object schema with declared additionalProperties), but a bare free-form object is a serde_json::Value, which has neither, so the generated crate did not compile. Split the branch on isContainer and walk the Value through as_object(): a non-object Value sends nothing, matching the parameter's declared object shape. The second of the two follow-ups promised in #24866's body. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz * [rust] rework: route exploded deepObject maps through parse_deep_object Review pointed out the branch emitted unprefixed json-quoted pairs - not the deepObject wire format the style asks for - and that was true of the pre-existing HashMap iteration too. Both shapes (typed map and free-form serde_json::Value) now take crate::apis::parse_deep_object, the route the non-explode branch already uses: name[key]=value on the wire, one code path, and the Value shape compiles because to_value accepts both. The fixture gains a required-nullable parameter so all patched sites are exercised, and the test registers the generated tree for cleanup. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz * [rust] tidy: drop narrating template comments, pin the typed-map wire shape The four comment copies repeated what the identical non-explode block already says. The test is renamed for what it checks, and the fixture's `filter` becomes a typed map (`additionalProperties: {type: string}`), the shape whose wire format this change affects. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Fable 5 --- .../resources/rust/reqwest-trait/api.mustache | 14 +++----- .../main/resources/rust/reqwest/api.mustache | 14 +++----- .../codegen/rust/RustClientCodegenTest.java | 25 +++++++++++++ .../deep-object-free-form-query-param.yaml | 36 +++++++++++++++++++ 4 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache index ff3d54ddcc2d..c11fed0ac474 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache @@ -206,11 +206,8 @@ impl {{classname}} for {{classname}}Client { {{/isModel}} {{#isMap}} if let Some(ref param_value) = {{{paramName}}} { - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - local_var_req_builder = local_var_req_builder.query(&query_params); + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + local_var_req_builder = local_var_req_builder.query(¶ms); } {{/isMap}} {{/isExplode}} @@ -256,11 +253,8 @@ impl {{classname}} for {{classname}}Client { local_var_req_builder = local_var_req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - local_var_req_builder = local_var_req_builder.query(&query_params); + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + local_var_req_builder = local_var_req_builder.query(¶ms); {{/isMap}} {{/isExplode}} {{/isDeepObject}} diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache index 5224b0c4d9b5..14e99143c12b 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache @@ -180,11 +180,8 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: req_builder = req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - req_builder = req_builder.query(&query_params); + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); {{/isMap}} {{/isExplode}} }; @@ -251,11 +248,8 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: req_builder = req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - req_builder = req_builder.query(&query_params); + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); {{/isMap}} {{/isExplode}} {{/isDeepObject}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java index 4ca3f4c70c91..aa734299d91a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java @@ -350,6 +350,31 @@ public void testFreeFormObjectParams() throws IOException { } } + @Test + public void testExplodedDeepObjectMapQueryParams() throws IOException { + // exploded deepObject maps (typed or free-form) go through parse_deep_object like the non-explode branch + for (String library : new String[] {"reqwest", "reqwest-trait"}) { + Path target = Files.createTempDirectory("test"); + target.toFile().deleteOnExit(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("rust") + .setLibrary(library) + .setInputSpec("src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString().replace("\\", "/")); + List files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + Path outputPath = Path.of(target.toString(), "/src/apis/default_api.rs"); + TestUtils.assertFileExists(outputPath); + // optional typed map, optional free-form object, required-nullable typed map + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"filter\""); + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"extra\""); + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"scope\""); + TestUtils.assertFileNotContains(outputPath, "param_value.len()"); + TestUtils.assertFileNotContains(outputPath, "param_value.iter()"); + } + } + @Test public void testArrayWithObjectEnumValues() throws IOException { Path target = Files.createTempDirectory("test"); diff --git a/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml b/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml new file mode 100644 index 000000000000..0a2c992091a4 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml @@ -0,0 +1,36 @@ +openapi: 3.0.3 +info: + title: deep object params + version: 1.0.0 +paths: + /items: + get: + operationId: listItems + parameters: + - name: filter + in: query + style: deepObject + explode: true + schema: + type: object + additionalProperties: + type: string + - name: extra + in: query + style: deepObject + explode: true + schema: + type: object + - name: scope + in: query + required: true + style: deepObject + explode: true + schema: + type: object + nullable: true + additionalProperties: + type: string + responses: + '200': + description: ok From a3438179febe15d43c15f2d3b7d1d41def98c501 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 28 Sep 2026 17:43:59 +0800 Subject: [PATCH 10/13] add tests for deep object free form query parameters for rust (reqwest) (#25029) --- ...est-deep-object-free-form-query-param.yaml | 8 ++ .../.gitignore | 3 + .../.openapi-generator-ignore | 23 ++++ .../.openapi-generator/FILES | 11 ++ .../.openapi-generator/VERSION | 1 + .../.travis.yml | 1 + .../Cargo.toml | 20 +++ .../README.md | 45 +++++++ .../docs/DefaultApi.md | 39 ++++++ .../git_push.sh | 54 ++++++++ .../src/apis/configuration.rs | 52 ++++++++ .../src/apis/default_api.rs | 67 ++++++++++ .../src/apis/mod.rs | 116 ++++++++++++++++++ .../src/lib.rs | 11 ++ .../src/models/mod.rs | 0 15 files changed, 451 insertions(+) create mode 100644 bin/configs/rust-reqwest-deep-object-free-form-query-param.yaml create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/.gitignore create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator-ignore create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/FILES create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/VERSION create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/.travis.yml create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/Cargo.toml create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/README.md create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/docs/DefaultApi.md create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/git_push.sh create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/configuration.rs create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/default_api.rs create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/mod.rs create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/lib.rs create mode 100644 samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/models/mod.rs diff --git a/bin/configs/rust-reqwest-deep-object-free-form-query-param.yaml b/bin/configs/rust-reqwest-deep-object-free-form-query-param.yaml new file mode 100644 index 000000000000..c30fd0a8eb7b --- /dev/null +++ b/bin/configs/rust-reqwest-deep-object-free-form-query-param.yaml @@ -0,0 +1,8 @@ +generatorName: rust +outputDir: samples/client/others/rust/reqwest-deep-object-free-form-query-param +library: reqwest +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml +templateDir: modules/openapi-generator/src/main/resources/rust +additionalProperties: + supportAsync: false + packageName: deep-object-free-form-query-param diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.gitignore b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.gitignore new file mode 100644 index 000000000000..6aa106405a4b --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.gitignore @@ -0,0 +1,3 @@ +/target/ +**/*.rs.bk +Cargo.lock diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator-ignore b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/FILES b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/FILES new file mode 100644 index 000000000000..20457f1a6d03 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/FILES @@ -0,0 +1,11 @@ +.gitignore +.travis.yml +Cargo.toml +README.md +docs/DefaultApi.md +git_push.sh +src/apis/configuration.rs +src/apis/default_api.rs +src/apis/mod.rs +src/lib.rs +src/models/mod.rs diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/VERSION b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/VERSION new file mode 100644 index 000000000000..32a8cfaceeb9 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.26.0-SNAPSHOT diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.travis.yml b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.travis.yml new file mode 100644 index 000000000000..22761ba7ee19 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/.travis.yml @@ -0,0 +1 @@ +language: rust diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/Cargo.toml b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/Cargo.toml new file mode 100644 index 000000000000..5e2c888fe315 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "deep-object-free-form-query-param" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" +# Override this license by providing a License Object in the OpenAPI. +license = "Unlicense" +edition = "2021" + +[dependencies] +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" +serde_repr = "^0.1" +url = "^2.5" +reqwest = { version = "^0.13", default-features = false, features = ["json", "blocking", "multipart", "query", "form"] } + +[features] +default = ["native-tls"] +native-tls = ["reqwest/native-tls"] +rustls = ["reqwest/rustls"] diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/README.md b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/README.md new file mode 100644 index 000000000000..4e888aa80f0f --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/README.md @@ -0,0 +1,45 @@ +# Rust API client for deep-object-free-form-query-param + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + +## Overview + +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. + +- API version: 1.0.0 +- Package version: 1.0.0 +- Generator version: 7.26.0-SNAPSHOT +- Build package: `org.openapitools.codegen.languages.RustClientCodegen` + +## Installation + +Put the package under your project folder in a directory named `deep-object-free-form-query-param` and add the following to `Cargo.toml` under `[dependencies]`: + +``` +deep-object-free-form-query-param = { path = "./deep-object-free-form-query-param" } +``` + +## Documentation for API Endpoints + +All URIs are relative to *http://localhost* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*DefaultApi* | [**list_items**](docs/DefaultApi.md#list_items) | **GET** /items | + + +## Documentation For Models + + + +To get access to the crate's generated documentation, use: + +``` +cargo doc --open +``` + +## Author + + + diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/docs/DefaultApi.md b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/docs/DefaultApi.md new file mode 100644 index 000000000000..6ed7780ee76f --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/docs/DefaultApi.md @@ -0,0 +1,39 @@ +# \DefaultApi + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**list_items**](DefaultApi.md#list_items) | **GET** /items | + + + +## list_items + +> list_items(scope, filter, extra) + + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**scope** | Option<[**std::collections::HashMap**](String.md)> | | [required] | +**filter** | Option<[**std::collections::HashMap**](String.md)> | | | +**extra** | Option<**serde_json::Value**> | | | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/git_push.sh b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/git_push.sh new file mode 100644 index 000000000000..a35991cd51a1 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/git_push.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ -z "${git_host}" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \${git_host} to ${git_host}" +fi + +if [ -z "${git_user_id}" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \${git_user_id} to ${git_user_id}" +fi + +if [ -z "${git_repo_id}" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \${git_repo_id} to ${git_repo_id}" +fi + +if [ -z "${release_note}" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \${release_note} to ${release_note}" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "${release_note}" + +# Sets the new remote +if [ -z "$(git remote)" ]; then # git remote not defined + if [ -z "${GIT_TOKEN:-}" ]; then + echo "[INFO] \${GIT_TOKEN} (environment variable) is not set. Using the git credential in your environment." + git remote add origin "https://${git_host}/${git_user_id}/${git_repo_id}.git" + else + git remote add origin "https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git" + fi +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/configuration.rs b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/configuration.rs new file mode 100644 index 000000000000..d9be07bb2c4d --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/configuration.rs @@ -0,0 +1,52 @@ +/* + * deep object params + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * Generated by: https://openapi-generator.tech + */ + + +pub use reqwest::blocking::Client; + +#[derive(Debug, Clone)] +pub struct Configuration { + pub base_path: String, + pub user_agent: Option, + pub client: reqwest::blocking::Client, + pub basic_auth: Option, + pub oauth_access_token: Option, + pub bearer_access_token: Option, + pub api_key: Option, +} + +pub type BasicAuth = (String, Option); + +#[derive(Debug, Clone)] +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} + + +impl Configuration { + pub fn new() -> Configuration { + Configuration::default() + } +} + +impl Default for Configuration { + fn default() -> Self { + Configuration { + base_path: "http://localhost".to_owned(), + user_agent: Some("OpenAPI-Generator/1.0.0/rust".to_owned()), + client: reqwest::blocking::Client::new(), + basic_auth: None, + oauth_access_token: None, + bearer_access_token: None, + api_key: None, + } + } +} diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/default_api.rs b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/default_api.rs new file mode 100644 index 000000000000..f9d4bf1e9e23 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/default_api.rs @@ -0,0 +1,67 @@ +/* + * deep object params + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; +use serde::{Deserialize, Serialize, de::Error as _}; +use crate::{apis::ResponseContent, models}; +use super::{Error, configuration, ContentType}; + + +/// struct for typed errors of method [`list_items`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ListItemsError { + UnknownValue(serde_json::Value), +} + + +pub fn list_items(configuration: &configuration::Configuration, scope: Option>, filter: Option>, extra: Option) -> Result<(), Error> { + // add a prefix to parameters to efficiently prevent name collisions + let p_query_scope = scope; + let p_query_filter = filter; + let p_query_extra = extra; + + let uri_str = format!("{}/items", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + if let Some(ref param_value) = p_query_filter { + let params = crate::apis::parse_deep_object("filter", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); + } + if let Some(ref param_value) = p_query_extra { + let params = crate::apis::parse_deep_object("extra", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); + } + if let Some(ref param_value) = p_query_scope { + let params = crate::apis::parse_deep_object("scope", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); + }; + if let Some(ref param_value) = p_query_scope { + req_builder = req_builder.query(&[("scope", &serde_json::to_string(param_value)?)]); + }; + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req)?; + + let status = resp.status(); + + if !status.is_client_error() && !status.is_server_error() { + Ok(()) + } else { + let content = resp.text()?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/mod.rs b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/mod.rs new file mode 100644 index 000000000000..fdcc89b36066 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/apis/mod.rs @@ -0,0 +1,116 @@ +use std::error; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct ResponseContent { + pub status: reqwest::StatusCode, + pub content: String, + pub entity: Option, +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + Serde(serde_json::Error), + Io(std::io::Error), + ResponseError(ResponseContent), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (module, e) = match self { + Error::Reqwest(e) => ("reqwest", e.to_string()), + Error::Serde(e) => ("serde", e.to_string()), + Error::Io(e) => ("IO", e.to_string()), + Error::ResponseError(e) => ("response", format!("status code {}", e.status)), + }; + write!(f, "error in {}: {}", module, e) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self { + Error::Reqwest(e) => e, + Error::Serde(e) => e, + Error::Io(e) => e, + Error::ResponseError(_) => return None, + }) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Reqwest(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub fn urlencode>(s: T) -> String { + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() +} + +pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { + if let serde_json::Value::Object(object) = value { + let mut params = vec![]; + + for (key, value) in object { + match value { + serde_json::Value::Object(_) => params.append(&mut parse_deep_object( + &format!("{}[{}]", prefix, key), + value, + )), + serde_json::Value::Array(array) => { + for (i, value) in array.iter().enumerate() { + params.append(&mut parse_deep_object( + &format!("{}[{}][{}]", prefix, key, i), + value, + )); + } + }, + serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), + } + } + + return params; + } + + unimplemented!("Only objects are supported with style=deepObject") +} + +/// Internal use only +/// A content type supported by this client. +#[allow(dead_code)] +enum ContentType { + Json, + Text, + Unsupported(String) +} + +impl From<&str> for ContentType { + fn from(content_type: &str) -> Self { + if content_type.starts_with("application") && content_type.contains("json") { + return Self::Json; + } else if content_type.starts_with("text/plain") { + return Self::Text; + } else { + return Self::Unsupported(content_type.to_string()); + } + } +} + +pub mod default_api; + +pub mod configuration; diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/lib.rs b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/lib.rs new file mode 100644 index 000000000000..e1520628d762 --- /dev/null +++ b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(unused_imports)] +#![allow(clippy::too_many_arguments)] + +extern crate serde_repr; +extern crate serde; +extern crate serde_json; +extern crate url; +extern crate reqwest; + +pub mod apis; +pub mod models; diff --git a/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/models/mod.rs b/samples/client/others/rust/reqwest-deep-object-free-form-query-param/src/models/mod.rs new file mode 100644 index 000000000000..e69de29bb2d1 From 2f13add76e6314fcb451ef52cb769fcf840fdb75 Mon Sep 17 00:00:00 2001 From: Roemer Vlasveld Date: Mon, 28 Sep 2026 13:27:05 +0200 Subject: [PATCH 11/13] [kotlin-spring] seal allOf discriminator parent interfaces by default (#23953) A model with a discriminator is rendered as an interface. Because the generator knows every subtype at generation time, that interface can be sealed, which lets callers use exhaustive `when` matching and stops external code from adding implementors that the discriminator mapping does not know about. Adds `useSealedDiscriminatorInterfaces` (default true). Set it to false to restore the plain `interface` rendering, which is needed when the generated interface is implemented from outside the generated package. Also declares SchemaSupportFeature.allOf, which the generator supports but did not advertise. Claude-Session: https://claude.ai/code/session_01VBn7VWUxZL6mTUbg21xNjm Co-authored-by: Claude Opus 5 (1M context) --- docs/generators/kotlin-spring.md | 3 +- .../languages/KotlinSpringServerCodegen.java | 18 +++- .../kotlin-spring/dataClass.mustache | 2 +- .../spring/KotlinSpringServerCodegenTest.java | 88 +++++++++++++++++++ .../kotlin/org/openapitools/model/Animal.kt | 2 +- .../main/kotlin/org/openapitools/model/Pet.kt | 2 +- .../kotlin/org/openapitools/model/Animal.kt | 2 +- .../main/kotlin/org/openapitools/model/Pet.kt | 2 +- 8 files changed, 112 insertions(+), 7 deletions(-) diff --git a/docs/generators/kotlin-spring.md b/docs/generators/kotlin-spring.md index 015a6370b3e7..26bb22126a6b 100644 --- a/docs/generators/kotlin-spring.md +++ b/docs/generators/kotlin-spring.md @@ -74,6 +74,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |useFlowForArrayReturnType|Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.| |true| |useJackson3|Use Jackson 3 dependencies (tools.jackson package). Only available with `useSpringBoot4`. Defaults to true when `useSpringBoot4` is enabled.| |false| |useResponseEntity|Whether (when false) to return actual type (e.g. List<Fruit>) and handle non-happy path responses via exceptions flow or (when true) return entire ResponseEntity (e.g. ResponseEntity<List<Fruit>>). If disabled, method are annotated using a @ResponseStatus annotation, which has the status of the first response declared in the Api definition| |true| +|useSealedDiscriminatorInterfaces|Generate sealed interfaces instead of plain interfaces for allOf discriminator parent models. When true (default), discriminator parents rendered as `sealed interface`, enabling exhaustive `when` matching and preventing external implementors (which cannot know all subtypes). Set to false to restore the legacy plain `interface` behavior, e.g. when you implement the generated interface from a module outside the generated package.| |true| |useSealedResponseInterfaces|Generate sealed interfaces for endpoint responses that all possible response types implement. Allows controllers to return any valid response type in a type-safe manner (e.g., sealed interface CreateUserResponse implemented by User, ConflictResponse, ErrorResponse)| |false| |useSpringBoot3|Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false| |useSpringBoot4|Generate code and provide dependencies for use with Spring Boot 4.x. Enabling this option will also enable `useJakartaEe`.| |false| @@ -325,7 +326,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |Composite|✓|OAS2,OAS3 |Polymorphism|✓|OAS2,OAS3 |Union|✗|OAS3 -|allOf|✗|OAS2,OAS3 +|allOf|✓|OAS2,OAS3 |anyOf|✗|OAS3 |oneOf|✓|OAS3 |not|✗|OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 6d17dc0b302e..62a3557c031a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -115,6 +115,7 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen public static final String GENERATE_PAGEABLE_CONSTRAINT_VALIDATION = "generatePageableConstraintValidation"; public static final String SUBSTITUTE_GENERIC_PAGED_MODEL = "substituteGenericPagedModel"; public static final String USE_SEALED_RESPONSE_INTERFACES = "useSealedResponseInterfaces"; + public static final String USE_SEALED_DISCRIMINATOR_INTERFACES = "useSealedDiscriminatorInterfaces"; public static final String COMPANION_OBJECT = "companionObject"; public static final String SUSPEND_FUNCTIONS = "suspendFunctions"; @@ -201,6 +202,7 @@ public void setAutoXSpringPaginated(boolean autoXSpringPaginated) { @Setter private boolean generatePageableConstraintValidation = false; @Setter private boolean substituteGenericPagedModel = false; @Setter private boolean useSealedResponseInterfaces = false; + @Setter private boolean useSealedDiscriminatorInterfaces = true; @Setter private boolean companionObject = false; @Setter private boolean useEnumValueInterface = false; private String valuedEnumClassName = "ValuedEnum"; @@ -262,7 +264,8 @@ public KotlinSpringServerCodegen() { ) .includeSchemaSupportFeatures( SchemaSupportFeature.Polymorphism, - SchemaSupportFeature.oneOf + SchemaSupportFeature.oneOf, + SchemaSupportFeature.allOf ) .includeParameterFeatures( ParameterFeature.Cookie @@ -329,6 +332,13 @@ public KotlinSpringServerCodegen() { addSwitch(USE_SEALED_RESPONSE_INTERFACES, "Generate sealed interfaces for endpoint responses that all possible response types implement. Allows controllers to return any valid response type in a type-safe manner (e.g., sealed interface CreateUserResponse implemented by User, ConflictResponse, ErrorResponse)", useSealedResponseInterfaces); + addSwitch(USE_SEALED_DISCRIMINATOR_INTERFACES, + "Generate sealed interfaces instead of plain interfaces for allOf discriminator parent models. " + + "When true (default), discriminator parents rendered as `sealed interface`, enabling exhaustive " + + "`when` matching and preventing external implementors (which cannot know all subtypes). " + + "Set to false to restore the legacy plain `interface` behavior, e.g. when you implement " + + "the generated interface from a module outside the generated package.", + useSealedDiscriminatorInterfaces); addOption(X_KOTLIN_IMPLEMENTS_SKIP, "A list of fully qualified interfaces that should NOT be implemented despite their presence in vendor extension `x-kotlin-implements`. Example: yaml `xKotlinImplementsSkip: [com.some.pack.WithPhotoUrls]` skips implementing the interface in any schema", "empty list"); addOption(X_KOTLIN_IMPLEMENTS_FIELDS_SKIP, "A list of fields per schema name that should NOT be created with `override` keyword despite their presence in vendor extension `x-kotlin-implements-fields` for the schema. Example: yaml `xKotlinImplementsFieldsSkip: Pet: [photoUrls]` skips `override` for `photoUrls` in schema `Pet`", "empty map"); addOption(SCHEMA_IMPLEMENTS, "A map of single interface or a list of interfaces per schema name that should be implemented (serves similar purpose as `x-kotlin-implements`, but is fully decoupled from the api spec). Example: yaml `schemaImplements: {Pet: com.some.pack.WithId, Category: [com.some.pack.CategoryInterface], Dog: [com.some.pack.Canine, com.some.pack.OtherInterface]}` implements interfaces in schemas `Pet` (interface `com.some.pack.WithId`), `Category` (interface `com.some.pack.CategoryInterface`), `Dog`(interfaces `com.some.pack.Canine`, `com.some.pack.OtherInterface`)", "empty map"); @@ -652,6 +662,12 @@ public void processOpts() { } writePropertyBack(USE_SEALED_RESPONSE_INTERFACES, useSealedResponseInterfaces); + if (additionalProperties.containsKey(USE_SEALED_DISCRIMINATOR_INTERFACES)) { + this.setUseSealedDiscriminatorInterfaces( + Boolean.parseBoolean(additionalProperties.get(USE_SEALED_DISCRIMINATOR_INTERFACES).toString())); + } + writePropertyBack(USE_SEALED_DISCRIMINATOR_INTERFACES, useSealedDiscriminatorInterfaces); + if (additionalProperties.containsKey(COMPANION_OBJECT)) { this.setCompanionObject(convertPropertyToBooleanAndWriteBack(COMPANION_OBJECT)); } else { diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache index 261ddc00d115..b57036ba3af3 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache @@ -14,7 +14,7 @@ {{#vendorExtensions.x-class-extra-annotation}} {{{.}}} {{/vendorExtensions.x-class-extra-annotation}} -{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#vendorExtensions.x-is-open-class}}open {{/vendorExtensions.x-is-open-class}}{{^vendorExtensions.x-is-open-class}}{{#hasVars}}data {{/hasVars}}{{/vendorExtensions.x-is-open-class}}class {{classname}}( +{{#discriminator}}{{#useSealedDiscriminatorInterfaces}}sealed {{/useSealedDiscriminatorInterfaces}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#vendorExtensions.x-is-open-class}}open {{/vendorExtensions.x-is-open-class}}{{^vendorExtensions.x-is-open-class}}{{#hasVars}}data {{/hasVars}}{{/vendorExtensions.x-is-open-class}}class {{classname}}( {{#requiredVars}} {{>dataClassReqVar}}{{^-last}}, {{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}}, diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 8164d64ab2f6..9386932bab2f 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -8105,4 +8105,92 @@ public void testMultiLevelAllOfWithEscapedPropertyNames() throws IOException { assertFileContains(mid, "\"2ndField=\" + `2ndField`"); assertFileNotContains(mid, "$`2ndField`"); } + + // ==================== allOf sealed interface tests ==================== + + @Test(description = "allOf discriminator parent generates sealed interface by default") + public void testAllOfDiscriminatorParentIsSealedInterfaceByDefault() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + new DefaultGenerator().opts(new ClientOptInput() + .openAPI(new OpenAPIParser().readLocation("src/test/resources/3_1/polymorphism-allof-and-discriminator.yaml", null, new ParseOptions()).getOpenAPI()) + .config(new KotlinSpringServerCodegen() {{ + setOutputDir(output.getAbsolutePath()); + }})) + .generate(); + + String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/model"; + + // Pet should be a sealed interface with Jackson annotations + assertFileContains(Paths.get(outputPath + "/Pet.kt"), + "sealed interface Pet", + "@JsonTypeInfo", "property = \"petType\"", "visible = true", + "@JsonSubTypes" + ); + // Children must remain data classes — sealing only applies to the discriminator parent + assertFileContains(Paths.get(outputPath + "/Dog.kt"), "data class Dog"); + assertFileContains(Paths.get(outputPath + "/Cat.kt"), "data class Cat"); + assertFileNotContains(Paths.get(outputPath + "/Dog.kt"), "sealed"); + assertFileNotContains(Paths.get(outputPath + "/Cat.kt"), "sealed"); + } + + @Test(description = "allOf discriminator parent generates plain interface when useSealedDiscriminatorInterfaces=false") + public void testAllOfDiscriminatorOptOutPlainInterface() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + new DefaultGenerator().opts(new ClientOptInput() + .openAPI(new OpenAPIParser().readLocation("src/test/resources/3_1/polymorphism-allof-and-discriminator.yaml", null, new ParseOptions()).getOpenAPI()) + .config(new KotlinSpringServerCodegen() {{ + setOutputDir(output.getAbsolutePath()); + additionalProperties().put(USE_SEALED_DISCRIMINATOR_INTERFACES, "false"); + }})) + .generate(); + + String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/model"; + + // Opt-out: plain interface, not sealed + assertFileContains(Paths.get(outputPath + "/Pet.kt"), "interface Pet"); + assertFileNotContains(Paths.get(outputPath + "/Pet.kt"), "sealed interface Pet"); + } + + @Test(description = "allOf sealed interface correctly composes external x-kotlin-implements supertypes") + public void testSealedInterfaceWithExternalSupertypes() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + new DefaultGenerator().opts(new ClientOptInput() + .openAPI(new OpenAPIParser().readLocation("src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml", null, new ParseOptions()).getOpenAPI()) + .config(new KotlinSpringServerCodegen() {{ + setOutputDir(output.getAbsolutePath()); + }})) + .generate(); + + String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/model"; + + // Pet has x-kotlin-implements external supertypes; sealed should come before those + assertFileContains(Paths.get(outputPath + "/Pet.kt"), + "sealed interface Pet : com.some.pack.Named" + ); + } + + @Test(description = "oneOf sealed interface stays sealed when useSealedDiscriminatorInterfaces=false (different template path)") + public void testOneOfInterfaceStillSealedWhenAllOfFlagOff() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + new DefaultGenerator().opts(new ClientOptInput() + .openAPI(new OpenAPIParser().readLocation("src/test/resources/3_0/kotlin/polymorphism-oneof-discriminator.yaml", null, new ParseOptions()).getOpenAPI()) + .config(new KotlinSpringServerCodegen() {{ + setOutputDir(output.getAbsolutePath()); + additionalProperties().put(USE_SEALED_DISCRIMINATOR_INTERFACES, "false"); + }})) + .generate(); + + String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/model"; + + // oneOf parent uses oneof_interface.mustache, not dataClass.mustache — flag has no effect on it + assertFileContains(Paths.get(outputPath + "/Animal.kt"), "sealed interface Animal"); + } } diff --git a/samples/server/others/kotlin-springboot/allOf-multilevel/src/main/kotlin/org/openapitools/model/Animal.kt b/samples/server/others/kotlin-springboot/allOf-multilevel/src/main/kotlin/org/openapitools/model/Animal.kt index dd324f44902d..74388c908ffc 100644 --- a/samples/server/others/kotlin-springboot/allOf-multilevel/src/main/kotlin/org/openapitools/model/Animal.kt +++ b/samples/server/others/kotlin-springboot/allOf-multilevel/src/main/kotlin/org/openapitools/model/Animal.kt @@ -31,7 +31,7 @@ import jakarta.validation.Valid JsonSubTypes.Type(value = Dog::class, name = "Dog") ) -interface Animal { +sealed interface Animal { val className: kotlin.String diff --git a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/model/Pet.kt b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/model/Pet.kt index 14b070fef3c8..eb4ef6b9192a 100644 --- a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/model/Pet.kt +++ b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/model/Pet.kt @@ -44,7 +44,7 @@ import io.swagger.annotations.ApiModelProperty JsonSubTypes.Type(value = Dog::class, name = "Dog") ) -interface Pet : com.some.pack.Named, com.some.pack.WithCategory, com.some.pack.WithDefaultMethods, com.some.pack.WithPhotoUrls, java.io.Serializable { +sealed interface Pet : com.some.pack.Named, com.some.pack.WithCategory, com.some.pack.WithDefaultMethods, com.some.pack.WithPhotoUrls, java.io.Serializable { @get:ApiModelProperty(required = true, value = "") override val name: kotlin.String diff --git a/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/model/Animal.kt b/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/model/Animal.kt index 49d3e8d0cdb9..94e91b90f15d 100644 --- a/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/model/Animal.kt +++ b/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/model/Animal.kt @@ -34,7 +34,7 @@ import io.swagger.v3.oas.annotations.media.Schema JsonSubTypes.Type(value = Dog::class, name = "DOG") ) -interface Animal { +sealed interface Animal { @get:Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "") val className: kotlin.String diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/model/Pet.kt b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/model/Pet.kt index 21e90ed770f6..76ba37c947fa 100644 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/model/Pet.kt +++ b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/model/Pet.kt @@ -44,7 +44,7 @@ import io.swagger.annotations.ApiModelProperty JsonSubTypes.Type(value = Dog::class, name = "Dog") ) -interface Pet : com.some.pack.Named, com.some.pack.WithCategory, com.some.pack.WithDefaultMethods, com.some.pack.WithId, java.io.Serializable { +sealed interface Pet : com.some.pack.Named, com.some.pack.WithCategory, com.some.pack.WithDefaultMethods, com.some.pack.WithId, java.io.Serializable { @get:ApiModelProperty(required = true, value = "") override val name: kotlin.String From ec4430edc292e9a27dcbe167c5f013a1b687e47f Mon Sep 17 00:00:00 2001 From: Max Azatian <66359507+HardMax71@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:45:42 +0200 Subject: [PATCH 12/13] [rust] Log a sanitized identifier only when it differs from the cased original (#25015) * [rust] Log a sanitized identifier only when it differs from the cased original * [rust] Keep the rename warning test to its own log events --- .../languages/AbstractRustCodegen.java | 10 ++---- .../codegen/rust/AbstractRustCodegenTest.java | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java index 1698503abe89..8fa353a906e8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java @@ -201,16 +201,12 @@ public String sanitizeIdentifier(String name, CasingType casingType, String esca // Sanitize any other special characters that weren't replaced name = sanitizeName(name); - // Keep track of modifications prior to casing - boolean nameWasModified = !originalName.equals(name); - // Convert casing name = casingFunction.apply(name); // If word starts with number add a prefix // Note: this must be done after casing since CamelCase will strip leading underscores if (name.matches("^\\d.*")) { - nameWasModified = true; name = casingFunction.apply(escapePrefix + '_' + name); } @@ -218,7 +214,6 @@ public String sanitizeIdentifier(String name, CasingType casingType, String esca // There is currently a bug in Rust where this doesn't work for a few reserved words :( // https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094 if (isReservedWord(name)) { - nameWasModified = true; if (this.keywordsThatDoNotSupportRawIdentifiers.contains(name) || !allowRawIdentifiers) { name = casingFunction.apply(escapePrefix + '_' + name); } else { @@ -227,8 +222,9 @@ public String sanitizeIdentifier(String name, CasingType casingType, String esca } // If the name had to be modified (not just because of casing), log the change - if (nameWasModified) { - LOGGER.warn("{} cannot be used as a {} name. Renamed to {}", casingFunction.apply(originalName), type, name); + String casedOriginalName = casingFunction.apply(originalName); + if (!casedOriginalName.equals(name)) { + LOGGER.warn("{} cannot be used as a {} name. Renamed to {}", casedOriginalName, type, name); } return name; diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/AbstractRustCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/AbstractRustCodegenTest.java index 300e0b9566c3..a03659a9dd16 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/AbstractRustCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/AbstractRustCodegenTest.java @@ -1,7 +1,13 @@ package org.openapitools.codegen.rust; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.spi.FilterReply; import org.openapitools.codegen.CodegenProperty; import org.openapitools.codegen.languages.AbstractRustCodegen; +import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.Test; @@ -74,6 +80,32 @@ public void testSanitizeIdentifier() { Assert.assertEquals(sanitizeSnakeCase.apply(""), ""); } + @Test + public void testSanitizeIdentifierWarnsOnlyOnRename() { + Logger logger = (Logger) LoggerFactory.getLogger(AbstractRustCodegen.class); + ListAppender listAppender = new ListAppender<>(); + // Other test classes run in parallel and log on the same logger, so keep only this test's names + listAppender.addFilter(new Filter() { + @Override + public FilterReply decide(ILoggingEvent event) { + return event.getFormattedMessage().contains("SanitizeWarningProbe") ? FilterReply.NEUTRAL : FilterReply.DENY; + } + }); + listAppender.start(); + logger.addAppender(listAppender); + try { + codegen.sanitizeIdentifier("sanitize-warning-probe", AbstractRustCodegen.CasingType.CAMEL_CASE, "p", "model", true); + Assert.assertTrue(listAppender.list.isEmpty()); + + codegen.sanitizeIdentifier("sanitize/warning/probe", AbstractRustCodegen.CasingType.CAMEL_CASE, "p", "model", true); + Assert.assertEquals(listAppender.list.size(), 1); + Assert.assertEquals(listAppender.list.get(0).getFormattedMessage(), + "SanitizeWarningProbe cannot be used as a model name. Renamed to SanitizeSlashWarningSlashProbe"); + } finally { + logger.detachAppender(listAppender); + } + } + @Test public void testToVarName() { // Should be converted to snake case From 4c2d787fb145ff40ea766077be8a88ca4cd8a6be Mon Sep 17 00:00:00 2001 From: Max Azatian <66359507+HardMax71@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:55:19 +0200 Subject: [PATCH 13/13] [rust] Redact credentials in the reqwest Configuration and ApiKey Debug output (#25019) * [rust] Redact credentials in the reqwest Configuration and ApiKey Debug output * [rust] Redact the token source in Configuration Debug and test every credential --- .../rust/reqwest-trait/configuration.mustache | 37 +++++++++++++++++- .../rust/reqwest/configuration.mustache | 39 ++++++++++++++++++- .../codegen/rust/RustClientCodegenTest.java | 36 +++++++++++++++++ .../src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../composed-oneof/src/apis/configuration.rs | 29 +++++++++++++- .../emptyObject/src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../multipart-async/src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../oneOf-array-map/src/apis/configuration.rs | 29 +++++++++++++- .../oneOf-reuseRef/src/apis/configuration.rs | 29 +++++++++++++- .../reqwest/oneOf/src/apis/configuration.rs | 29 +++++++++++++- .../petstore/src/apis/configuration.rs | 29 +++++++++++++- .../name-mapping/src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 14 ++++++- .../petstore-async/src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 30 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../src/apis/configuration.rs | 29 +++++++++++++- .../petstore/src/apis/configuration.rs | 29 +++++++++++++- .../test-duplicates/src/apis/configuration.rs | 29 +++++++++++++- 25 files changed, 689 insertions(+), 47 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/configuration.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/configuration.mustache index 9a8fa5481ec7..c7f5bd854cc8 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/configuration.mustache @@ -13,7 +13,7 @@ use async_trait::async_trait; {{/supportTokenSource}} pub use {{#supportMiddleware}}reqwest_middleware::ClientWithMiddleware{{/supportMiddleware}}{{^supportMiddleware}}reqwest::Client{{/supportMiddleware}}; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -35,11 +35,20 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } + +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} {{/supportTokenSource}} {{#withAWSV4Signature}} @@ -81,6 +90,30 @@ impl AWSv4Key { } {{/withAWSV4Signature}} +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + {{^supportTokenSource}} + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + {{/supportTokenSource}} + {{#withAWSV4Signature}} + debug.field("aws_v4_key", &self.aws_v4_key); + {{/withAWSV4Signature}} + {{#supportTokenSource}} + debug.field("token_source", &"[REDACTED]"); + {{/supportTokenSource}} + debug.finish() + } +} + impl Configuration { pub fn new() -> Configuration { Configuration::default() diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest/configuration.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest/configuration.mustache index 4ff5b5caad98..4dc403de23aa 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest/configuration.mustache @@ -13,7 +13,7 @@ use async_trait::async_trait; {{/supportTokenSource}} pub use {{#supportMiddleware}}reqwest_middleware::ClientWithMiddleware{{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client{{/supportMiddleware}}; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -37,11 +37,20 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } + +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} {{/supportTokenSource}} {{#withAWSV4Signature}} @@ -83,6 +92,32 @@ impl AWSv4Key { } {{/withAWSV4Signature}} +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + {{^supportTokenSource}} + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + {{/supportTokenSource}} + {{#withAWSV4Signature}} + debug.field("aws_v4_key", &self.aws_v4_key); + {{/withAWSV4Signature}} + {{#supportAsync}} + {{#supportTokenSource}} + debug.field("token_source", &"[REDACTED]"); + {{/supportTokenSource}} + {{/supportAsync}} + debug.finish() + } +} + impl Configuration { pub fn new() -> Configuration { Configuration::default() diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java index aa734299d91a..9397d3f9e4a9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java @@ -413,4 +413,40 @@ public void testReqwestTraitUuidParamsUseNamedLifetimes() throws IOException { "async fn list_widget_items<'id, 'run_id>(&self, id: &'id str, run_id: Option<&'run_id str>)"); TestUtils.assertFileNotContains(outputPath, "Option<&str>"); } + + @Test + public void testReqwestConfigurationDebugRedactsCredentials() throws IOException { + for (String library : new String[]{"reqwest", "reqwest-trait"}) { + Path outputPath = generateReqwestConfiguration(library, false); + TestUtils.assertFileNotContains(outputPath, "#[derive(Debug, Clone)]"); + TestUtils.assertFileContains(outputPath, + ".field(\"basic_auth\", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| \"[REDACTED]\"))))", + ".field(\"oauth_access_token\", &self.oauth_access_token.as_ref().map(|_| \"[REDACTED]\"))", + ".field(\"bearer_access_token\", &self.bearer_access_token.as_ref().map(|_| \"[REDACTED]\"))", + ".field(\"api_key\", &self.api_key)", + ".field(\"key\", &\"[REDACTED]\")"); + + Path tokenSourcePath = generateReqwestConfiguration(library, true); + TestUtils.assertFileNotContains(tokenSourcePath, "#[derive(Debug, Clone)]", "&self.token_source"); + TestUtils.assertFileContains(tokenSourcePath, ".field(\"token_source\", &\"[REDACTED]\")"); + } + } + + private Path generateReqwestConfiguration(String library, boolean supportTokenSource) throws IOException { + Path target = Files.createTempDirectory("test"); + target.toFile().deleteOnExit(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("rust") + .setLibrary(library) + .addAdditionalProperty("supportAsync", true) + .addAdditionalProperty("supportTokenSource", supportTokenSource) + .setInputSpec("src/test/resources/3_0/rust/petstore.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString().replace("\\", "/")); + List files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + Path outputPath = Path.of(target.toString(), "/src/apis/configuration.rs"); + TestUtils.assertFileExists(outputPath); + return outputPath; + } } diff --git a/samples/client/others/rust/reqwest-regression-16119/src/apis/configuration.rs b/samples/client/others/rust/reqwest-regression-16119/src/apis/configuration.rs index e5420eed77b0..c9dad4bfd4fa 100644 --- a/samples/client/others/rust/reqwest-regression-16119/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest-regression-16119/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/api-with-ref-param/src/apis/configuration.rs b/samples/client/others/rust/reqwest/api-with-ref-param/src/apis/configuration.rs index 77e3439ee0f0..a135aa2dc0da 100644 --- a/samples/client/others/rust/reqwest/api-with-ref-param/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/api-with-ref-param/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/composed-oneof/src/apis/configuration.rs b/samples/client/others/rust/reqwest/composed-oneof/src/apis/configuration.rs index 70d170f44fd9..1ce1e81941ed 100644 --- a/samples/client/others/rust/reqwest/composed-oneof/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/composed-oneof/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/emptyObject/src/apis/configuration.rs b/samples/client/others/rust/reqwest/emptyObject/src/apis/configuration.rs index 674a68aa4a04..6038efb3ab67 100644 --- a/samples/client/others/rust/reqwest/emptyObject/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/emptyObject/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/enum-query-params/src/apis/configuration.rs b/samples/client/others/rust/reqwest/enum-query-params/src/apis/configuration.rs index 1659ab96afdd..ec4430bfdf5a 100644 --- a/samples/client/others/rust/reqwest/enum-query-params/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/enum-query-params/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/multipart-async/src/apis/configuration.rs b/samples/client/others/rust/reqwest/multipart-async/src/apis/configuration.rs index ffc218c8f93f..aa01a227bdf9 100644 --- a/samples/client/others/rust/reqwest/multipart-async/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/multipart-async/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/nullable-enum-query-params/src/apis/configuration.rs b/samples/client/others/rust/reqwest/nullable-enum-query-params/src/apis/configuration.rs index f5989abeab40..16e1e3922eeb 100644 --- a/samples/client/others/rust/reqwest/nullable-enum-query-params/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/nullable-enum-query-params/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/oneOf-array-map/src/apis/configuration.rs b/samples/client/others/rust/reqwest/oneOf-array-map/src/apis/configuration.rs index 9d11dd3c97a1..ced995ba8b95 100644 --- a/samples/client/others/rust/reqwest/oneOf-array-map/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/oneOf-array-map/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/oneOf-reuseRef/src/apis/configuration.rs b/samples/client/others/rust/reqwest/oneOf-reuseRef/src/apis/configuration.rs index 68d2697c86ea..bd180b77c105 100644 --- a/samples/client/others/rust/reqwest/oneOf-reuseRef/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/oneOf-reuseRef/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/others/rust/reqwest/oneOf/src/apis/configuration.rs b/samples/client/others/rust/reqwest/oneOf/src/apis/configuration.rs index 20441dda2e9a..ff1a19eac9d6 100644 --- a/samples/client/others/rust/reqwest/oneOf/src/apis/configuration.rs +++ b/samples/client/others/rust/reqwest/oneOf/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest-trait/petstore/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest-trait/petstore/src/apis/configuration.rs index d96450ab233f..f2047ccc5a1a 100644 --- a/samples/client/petstore/rust/reqwest-trait/petstore/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest-trait/petstore/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/name-mapping/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/name-mapping/src/apis/configuration.rs index 3ae7dbe56254..9b5e4d72387a 100644 --- a/samples/client/petstore/rust/reqwest/name-mapping/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/name-mapping/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/apis/configuration.rs index fb9eb258b86c..4ac874f55deb 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest_middleware::ClientWithMiddleware; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/apis/configuration.rs index c66c3d1c8ecb..e34cb8016bf2 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/apis/configuration.rs @@ -14,7 +14,7 @@ use google_cloud_token::TokenSource; use async_trait::async_trait; pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -23,6 +23,18 @@ pub struct Configuration { } +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug.field("token_source", &"[REDACTED]"); + debug.finish() + } +} + impl Configuration { pub fn new() -> Configuration { Configuration::default() diff --git a/samples/client/petstore/rust/reqwest/petstore-async/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-async/src/apis/configuration.rs index d96450ab233f..f2047ccc5a1a 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/apis/configuration.rs index d96450ab233f..f2047ccc5a1a 100644 --- a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/apis/configuration.rs index af280539b12f..c4fba6dce168 100644 --- a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/apis/configuration.rs @@ -15,7 +15,7 @@ use http; use secrecy::{SecretString, ExposeSecret}; pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -29,12 +29,21 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + #[derive(Debug, Clone)] pub struct AWSv4Key { pub access_key: String, @@ -72,6 +81,23 @@ impl AWSv4Key { } } +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.field("aws_v4_key", &self.aws_v4_key); + debug.finish() + } +} + impl Configuration { pub fn new() -> Configuration { Configuration::default() diff --git a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/apis/configuration.rs index e9f6c13cc86b..2df2fb4df8ef 100644 --- a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-no-chrono/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-no-chrono/src/apis/configuration.rs index e9f6c13cc86b..2df2fb4df8ef 100644 --- a/samples/client/petstore/rust/reqwest/petstore-no-chrono/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-no-chrono/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore-serde-path-to-error/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore-serde-path-to-error/src/apis/configuration.rs index d96450ab233f..f2047ccc5a1a 100644 --- a/samples/client/petstore/rust/reqwest/petstore-serde-path-to-error/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore-serde-path-to-error/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/petstore/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/petstore/src/apis/configuration.rs index e9f6c13cc86b..2df2fb4df8ef 100644 --- a/samples/client/petstore/rust/reqwest/petstore/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/petstore/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration { diff --git a/samples/client/petstore/rust/reqwest/test-duplicates/src/apis/configuration.rs b/samples/client/petstore/rust/reqwest/test-duplicates/src/apis/configuration.rs index 316cf7e0ce6d..05bd588eddca 100644 --- a/samples/client/petstore/rust/reqwest/test-duplicates/src/apis/configuration.rs +++ b/samples/client/petstore/rust/reqwest/test-duplicates/src/apis/configuration.rs @@ -11,7 +11,7 @@ pub use reqwest::blocking::Client; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Configuration { pub base_path: String, pub user_agent: Option, @@ -24,12 +24,37 @@ pub struct Configuration { pub type BasicAuth = (String, Option); -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ApiKey { pub prefix: Option, pub key: String, } +impl std::fmt::Debug for ApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApiKey") + .field("prefix", &self.prefix) + .field("key", &"[REDACTED]") + .finish() + } +} + + +impl std::fmt::Debug for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("Configuration"); + debug + .field("base_path", &self.base_path) + .field("user_agent", &self.user_agent) + .field("client", &self.client); + debug + .field("basic_auth", &self.basic_auth.as_ref().map(|(username, password)| (username, password.as_ref().map(|_| "[REDACTED]")))) + .field("oauth_access_token", &self.oauth_access_token.as_ref().map(|_| "[REDACTED]")) + .field("bearer_access_token", &self.bearer_access_token.as_ref().map(|_| "[REDACTED]")) + .field("api_key", &self.api_key); + debug.finish() + } +} impl Configuration { pub fn new() -> Configuration {