diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/DartDioClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/DartDioClientCodegen.java index 91cf38518faef..a38eb7298b22f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/DartDioClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/DartDioClientCodegen.java @@ -78,6 +78,9 @@ public class DartDioClientCodegen extends AbstractDartCodegen { public static final String SKIP_COPY_WITH_DEFAULT_VALUE = "false"; private static final String CLIENT_NAME = "clientName"; + private static final String X_DISCRIMINATOR_MAPPED_MODELS_NONSELF = "x-discriminator-mapped-models-nonself"; + private static final String X_HAS_DISCRIMINATOR_SELF_MAPPING = "x-has-discriminator-self-mapping"; + private static final String X_DISCRIMINATOR_SELF_MAPPING_NAME = "x-discriminator-self-mapping-name"; @Getter @Setter private String dateLibrary; @@ -293,6 +296,7 @@ private void configureSerializationLibraryBuiltValue(String srcFolder) { private void configureSerializationLibraryJsonSerializable(String srcFolder) { supportingFiles.add(new SupportingFile("serialization/json_serializable/build.yaml.mustache", "" /* main project dir */, "build.yaml")); + supportingFiles.add(new SupportingFile("serialization/json_serializable/api_util.mustache", srcFolder, "api_util.dart")); supportingFiles.add(new SupportingFile("serialization/json_serializable/deserialize.mustache", srcFolder, "deserialize.dart")); @@ -583,36 +587,278 @@ private void adaptToDartInheritance(Map objs) { } } - /// override the default behavior of createDiscriminator - /// to remove extra mappings added as a side effect of setLegacyDiscriminatorBehavior(false) - /// this ensures 1-1 schema mapping instead of 1-many + /** + * Computes the maximum allOf inheritance distance from {@code schemaName} to + * {@code ancestorSchemaName}. + * + *

Returns {@code 0} when both schema names are equal, and {@code -1} when no + * inheritance path exists. The {@code visited} set prevents infinite recursion on + * cyclic graphs. + */ + private int getSchemaInheritanceDepth(String schemaName, String ancestorSchemaName, Set visited) { + if (schemaName == null || ancestorSchemaName == null) { + return -1; + } + if (schemaName.equals(ancestorSchemaName)) { + return 0; + } + + Schema currentSchema = ModelUtils.getSchema(openAPI, schemaName); + if (currentSchema == null || currentSchema.getAllOf() == null || currentSchema.getAllOf().isEmpty()) { + return -1; + } + + int maxDepth = -1; + for (Object parentObj : currentSchema.getAllOf()) { + if (!(parentObj instanceof Schema)) { + continue; + } + Schema parentSchema = (Schema) parentObj; + String parentRef = parentSchema.get$ref(); + if (parentRef == null) { + continue; + } + + String parentSchemaName = ModelUtils.getSimpleRef(parentRef); + if (ancestorSchemaName.equals(parentSchemaName)) { + maxDepth = Math.max(maxDepth, 1); + continue; + } + + if (parentSchemaName != null && visited.add(parentSchemaName)) { + int parentDepth = getSchemaInheritanceDepth(parentSchemaName, ancestorSchemaName, visited); + if (parentDepth >= 0) { + maxDepth = Math.max(maxDepth, parentDepth + 1); + } + visited.remove(parentSchemaName); + } + } + + return maxDepth; + } + + /** + * Builds discriminator metadata and removes implicit/over-broad mappings so Dart + * generation keeps a strict one-schema-per-discriminator-entry behavior. + * + *

For schema-local discriminators, only explicitly declared mappings are kept. + * For inherited discriminators, mappings are restricted to true allOf descendants + * of the current schema. + */ @Override protected CodegenDiscriminator createDiscriminator(String schemaName, Schema schema) { CodegenDiscriminator sub = super.createDiscriminator(schemaName, schema); - Discriminator originalDiscriminator = schema.getDiscriminator(); + if (sub == null) { + return null; + } + + if (sub.getMapping() != null) { + // Defensive copy: avoid mutating shared mapping objects from the parsed spec. + sub.setMapping(new LinkedHashMap<>(sub.getMapping())); + } + sub.setVendorExtensions(new LinkedHashMap<>(ObjectUtils.firstNonNull(sub.getVendorExtensions(), Collections.emptyMap()))); + + Discriminator originalDiscriminator = getSchemaLocalDiscriminator(schema); if (originalDiscriminator != null) { Map originalMapping = originalDiscriminator.getMapping(); if (originalMapping != null && !originalMapping.isEmpty()) { - //we already have a discriminator mapping, remove everything else - for (MappedModel currentMappings : new HashSet<>(sub.getMappedModels())) { - if (originalMapping.containsKey(currentMappings.getMappingName())) { - //all good - } else { - sub.getMapping().remove(currentMappings.getMappingName()); - sub.getMappedModels().remove(currentMappings); - } - } + // keep only explicitly declared mappings on the schema-local discriminator + filterMappedModels(sub, mappedModel -> originalMapping.containsKey(mappedModel.getMappingName())); } + orderMappedModelsBySchemaSpecificity(sub, schemaName); + prepareDiscriminatorTemplateData(sub, schemaName, toModelName(schemaName)); + return sub; + } + + // For inherited discriminators, keep real allOf descendants of this schema + // (e.g. Reptile keeps Crocodile/Turtle, but not Bird from Animal's mapping). + // Also preserve alternatives declared directly in this schema's oneOf/anyOf. + Set descendantSchemaNames = getAllOfDescendants(schemaName).stream() + .map(MappedModel::getSchemaName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Set declaredAlternatives = getComposedAlternativeSchemaNames(schema); + + if (ModelUtils.isComposedSchema(schema) && schema.getAllOf() != null) { + filterMappedModels(sub, mappedModel -> descendantSchemaNames.contains(mappedModel.getSchemaName()) + || declaredAlternatives.contains(mappedModel.getSchemaName()) + || schemaName.equals(mappedModel.getSchemaName())); } + + orderMappedModelsBySchemaSpecificity(sub, schemaName); + prepareDiscriminatorTemplateData(sub, schemaName, toModelName(schemaName)); return sub; } + /** + * Prepares discriminator vendor extensions consumed by Dart templates. + * + *

The method separates non-self mapped models and records whether the + * discriminator includes a self-mapping plus its mapping key. + */ + private void prepareDiscriminatorTemplateData(CodegenDiscriminator discriminator, String schemaName, String modelName) { + if (discriminator == null || discriminator.getMappedModels() == null) { + return; + } + + String selfMappingName = null; + List nonSelfMappedModels = new ArrayList<>(); + for (MappedModel mappedModel : discriminator.getMappedModels()) { + boolean isSelfMapping = Objects.equals(schemaName, mappedModel.getSchemaName()) + || Objects.equals(modelName, mappedModel.getModelName()); + if (isSelfMapping) { + selfMappingName = mappedModel.getMappingName(); + } else { + nonSelfMappedModels.add(mappedModel); + } + } + + Map discriminatorVendorExtensions = discriminator.getVendorExtensions(); + if (discriminatorVendorExtensions == null) { + discriminatorVendorExtensions = new LinkedHashMap<>(); + discriminator.setVendorExtensions(discriminatorVendorExtensions); + } + + discriminatorVendorExtensions.put(X_DISCRIMINATOR_MAPPED_MODELS_NONSELF, nonSelfMappedModels); + discriminatorVendorExtensions.put(X_HAS_DISCRIMINATOR_SELF_MAPPING, selfMappingName != null); + if (selfMappingName != null) { + discriminatorVendorExtensions.put(X_DISCRIMINATOR_SELF_MAPPING_NAME, selfMappingName); + } else { + discriminatorVendorExtensions.remove(X_DISCRIMINATOR_SELF_MAPPING_NAME); + } + } + + /** + * Orders discriminator mapped models by schema specificity relative to the owner + * schema (deepest descendants first). + * + *

When two mappings have the same depth, the original insertion order is + * preserved for deterministic output. + */ + private void orderMappedModelsBySchemaSpecificity(CodegenDiscriminator discriminator, String ownerSchemaName) { + if (discriminator.getMappedModels() == null || discriminator.getMappedModels().size() < 2) { + return; + } + + List ordered = new ArrayList<>(discriminator.getMappedModels()); + Map inheritanceDepthBySchema = new HashMap<>(); + Map originalOrder = new HashMap<>(); + for (int i = 0; i < ordered.size(); i++) { + MappedModel mappedModel = ordered.get(i); + originalOrder.put(mappedModel, i); + inheritanceDepthBySchema.computeIfAbsent( + mappedModel.getSchemaName(), + schemaName -> getSchemaInheritanceDepth(schemaName, ownerSchemaName, new HashSet<>()) + ); + } + + ordered.sort((left, right) -> { + int leftDepth = inheritanceDepthBySchema.getOrDefault(left.getSchemaName(), -1); + int rightDepth = inheritanceDepthBySchema.getOrDefault(right.getSchemaName(), -1); + if (leftDepth != rightDepth) { + return Integer.compare(rightDepth, leftDepth); + } + + return Integer.compare(originalOrder.get(left), originalOrder.get(right)); + }); + + discriminator.setMappedModels(new LinkedHashSet<>(ordered)); + } + + /** + * Removes discriminator mapped models that do not satisfy the provided predicate. + * + *

Both the mapped model set and the optional mapping-name lookup map are kept + * in sync. + */ + private void filterMappedModels(CodegenDiscriminator discriminator, java.util.function.Predicate keepPredicate) { + for (MappedModel mappedModel : new HashSet<>(discriminator.getMappedModels())) { + if (!keepPredicate.test(mappedModel)) { + if (discriminator.getMapping() != null) { + discriminator.getMapping().remove(mappedModel.getMappingName()); + } + discriminator.getMappedModels().remove(mappedModel); + } + } + } + + /** + * Returns the discriminator defined on the schema itself, including an inline + * allOf segment, but excluding discriminators inherited from parent schemas. + */ + private Discriminator getSchemaLocalDiscriminator(Schema schema) { + if (schema == null) { + return null; + } + + if (schema.getDiscriminator() != null) { + return schema.getDiscriminator(); + } + + if (ModelUtils.isComposedSchema(schema) && schema.getAllOf() != null) { + // Prefer inline allOf discriminator (child-local) over inherited parent discriminators. + for (Object allOfSchemaObj : schema.getAllOf()) { + if (!(allOfSchemaObj instanceof Schema)) { + continue; + } + Schema allOfSchema = (Schema) allOfSchemaObj; + if (allOfSchema.getDiscriminator() != null) { + return allOfSchema.getDiscriminator(); + } + } + } + + return null; + } + + /** + * Gets schema names referenced directly by oneOf/anyOf in the provided schema. + */ + private Set getComposedAlternativeSchemaNames(Schema schema) { + Set alternatives = new HashSet<>(); + if (schema == null || !ModelUtils.isComposedSchema(schema)) { + return alternatives; + } + + List oneOfSchemas = schema.getOneOf(); + if (oneOfSchemas != null) { + for (Schema oneOfSchema : oneOfSchemas) { + String ref = oneOfSchema != null ? oneOfSchema.get$ref() : null; + if (ref != null) { + alternatives.add(ModelUtils.getSimpleRef(ref)); + } + } + } + + List anyOfSchemas = schema.getAnyOf(); + if (anyOfSchemas != null) { + for (Schema anyOfSchema : anyOfSchemas) { + String ref = anyOfSchema != null ? anyOfSchema.get$ref() : null; + if (ref != null) { + alternatives.add(ModelUtils.getSimpleRef(ref)); + } + } + } + + return alternatives; + } + @Override public Map postProcessAllModels(Map objs) { objs = super.postProcessAllModels(objs); if (SERIALIZATION_LIBRARY_BUILT_VALUE.equals(library)) { adaptToDartInheritance(objs); syncRootTypesWithInnerVars(objs); + for (ModelsMap entry : objs.values()) { + for (ModelMap mo : entry.getModels()) { + CodegenModel cm = mo.getModel(); + if (cm != null && cm.discriminator != null) { + String ownerSchemaName = ObjectUtils.firstNonNull(cm.getSchemaName(), cm.getName(), cm.getClassname()); + orderMappedModelsBySchemaSpecificity(cm.discriminator, ownerSchemaName); + prepareDiscriminatorTemplateData(cm.discriminator, cm.getSchemaName(), cm.classname); + } + } + } } // loop through models to update the imports @@ -875,7 +1121,8 @@ private void processImports(List operationList, java.util.func } } - if (SERIALIZATION_LIBRARY_BUILT_VALUE.equals(library) && (op.getHasFormParams() || op.getHasQueryParams() || op.getHasPathParams())) { + if ((SERIALIZATION_LIBRARY_BUILT_VALUE.equals(library) || SERIALIZATION_LIBRARY_JSON_SERIALIZABLE.equals(library)) + && (op.getHasFormParams() || op.getHasQueryParams() || op.getHasPathParams())) { resultImports.add("package:" + pubName + "/" + sourceFolder + "/api_util.dart"); } diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/api.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/api.mustache index e86a2cfb005a4..8cad7e99c36e7 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/api.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/api.mustache @@ -51,7 +51,7 @@ class {{classname}} { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', {{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}.toString()){{/pathParams}}; + final _path = r'{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', {{#includeLibraryTemplate}}api/path_param{{/includeLibraryTemplate}}){{/pathParams}}; final _options = Options( method: r'{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}}', {{#isResponseFile}} @@ -86,7 +86,14 @@ class {{classname}} { {{#queryParams}} {{^required}}{{^isNullable}}if ({{{paramName}}} != null) {{/isNullable}}{{/required}}r'{{baseName}}': {{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}, {{/queryParams}} - };{{/hasQueryParams}}{{#hasBodyOrFormParams}} + }; + removeNullQueryParametersExcept( + _queryParameters, + { + {{#queryParams}}{{#required}}{{#isNullable}}r'{{baseName}}', + {{/isNullable}}{{/required}}{{/queryParams}} + }, + );{{/hasQueryParams}}{{#hasBodyOrFormParams}} dynamic _bodyData; diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/form_param.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/form_param.mustache new file mode 100644 index 0000000000000..f2d3b3ce76ab8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/form_param.mustache @@ -0,0 +1 @@ +{{#isContainer}}{{#isMap}}encodeFormParameter{{/isMap}}{{^isMap}}encodeCollectionFormParameter<{{{baseType}}}>{{/isMap}}{{/isContainer}}{{^isContainer}}encodeFormParameter{{/isContainer}}(_serializers, {{{paramName}}}, const FullType({{^isContainer}}{{{dataType}}}){{/isContainer}}{{#isContainer}}Built{{#isMap}}Map{{/isMap}}{{#isArray}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isArray}}, [{{#isMap}}FullType(String), {{/isMap}}FullType({{{baseType}}})]), {{#collectionFormat}}format: ListFormat.{{collectionFormat}},{{/collectionFormat}}{{/isContainer}}) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache new file mode 100644 index 0000000000000..7293d7d18859f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache @@ -0,0 +1 @@ +{{#isNullable}}{{{paramName}}} == null ? '' : {{{paramName}}}.toString(){{/isNullable}}{{^isNullable}}{{{paramName}}}.toString(){{/isNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/serialize.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/serialize.mustache index 6c4d370d86e94..5a9bab792ae2b 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/serialize.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/serialize.mustache @@ -9,7 +9,7 @@ {{^isMultipart}} _bodyData = { {{#formParams}} - {{^required}}{{^isNullable}}if ({{{paramName}}} != null) {{/isNullable}}{{/required}}r'{{{baseName}}}': {{>serialization/built_value/api/query_param}}, + {{^required}}{{^isNullable}}if ({{{paramName}}} != null) {{/isNullable}}{{/required}}r'{{{baseName}}}': {{>serialization/built_value/api/form_param}}, {{/formParams}} }; {{/isMultipart}} diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api_util.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api_util.mustache index 45432b0042dc8..4645de35885c6 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api_util.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api_util.mustache @@ -35,7 +35,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -49,7 +49,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -57,18 +57,26 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +{{>serialization/common/remove_null_query_parameters}} diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_discriminator.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_discriminator.mustache index f867345883b1f..a523fded48129 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_discriminator.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_discriminator.mustache @@ -1,20 +1,31 @@ extension {{classname}}DiscriminatorExt on {{classname}} { String? get discriminatorValue { - {{#mappedModels}} + {{#vendorExtensions.x-discriminator-mapped-models-nonself}} if (this is {{modelName}}) { return r'{{mappingName}}'; } - {{/mappedModels}} + {{/vendorExtensions.x-discriminator-mapped-models-nonself}} + {{#vendorExtensions.x-has-discriminator-self-mapping}} + return r'{{vendorExtensions.x-discriminator-self-mapping-name}}'; + {{/vendorExtensions.x-has-discriminator-self-mapping}} + {{^vendorExtensions.x-has-discriminator-self-mapping}} return null; + {{/vendorExtensions.x-has-discriminator-self-mapping}} } } extension {{classname}}BuilderDiscriminatorExt on {{classname}}Builder { String? get discriminatorValue { - {{#mappedModels}} + {{#vendorExtensions.x-discriminator-mapped-models-nonself}} if (this is {{modelName}}Builder) { return r'{{mappingName}}'; } - {{/mappedModels}} + {{/vendorExtensions.x-discriminator-mapped-models-nonself}} + {{#vendorExtensions.x-has-discriminator-self-mapping}} + return r'{{vendorExtensions.x-discriminator-self-mapping-name}}'; + {{/vendorExtensions.x-has-discriminator-self-mapping}} + {{^vendorExtensions.x-has-discriminator-self-mapping}} return null; + {{/vendorExtensions.x-has-discriminator-self-mapping}} } -} \ No newline at end of file +} + diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_serializer.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_serializer.mustache index 4e64f91904afa..9651e775ede9f 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_serializer.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_serializer.mustache @@ -91,11 +91,11 @@ class _${{classname}}Serializer implements PrimitiveSerializer<{{classname}}> { {{#hasDiscriminatorWithNonEmptyMapping}} {{#discriminator}} {{! handle discriminator }} - {{#mappedModels}} + {{#vendorExtensions.x-discriminator-mapped-models-nonself}} if (object is {{modelName}}) { return serializers.serialize(object, specifiedType: FullType({{modelName}}))!; } - {{/mappedModels}} + {{/vendorExtensions.x-discriminator-mapped-models-nonself}} {{/discriminator}} {{/hasDiscriminatorWithNonEmptyMapping}} return _serializeProperties(serializers, object, specifiedType: specifiedType).toList(); @@ -287,10 +287,10 @@ class _${{classname}}Serializer implements PrimitiveSerializer<{{classname}}> { final discIndex = serializedList.indexOf({{classname}}.discriminatorFieldName) + 1; final discValue = serializers.deserialize(serializedList[discIndex], specifiedType: FullType(String)) as String; switch (discValue) { - {{#mappedModels}} + {{#vendorExtensions.x-discriminator-mapped-models-nonself}} case r'{{mappingName}}': return serializers.deserialize(serialized, specifiedType: FullType({{modelName}})) as {{modelName}}; - {{/mappedModels}} + {{/vendorExtensions.x-discriminator-mapped-models-nonself}} default: return serializers.deserialize(serialized, specifiedType: FullType(${{classname}})) as ${{classname}}; } diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/serializers.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/serializers.mustache index 67ff3ea18cda9..43aef4e8a8dd5 100644 --- a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/serializers.mustache +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/serializers.mustache @@ -30,7 +30,7 @@ Serializers serializers = (_$serializers.toBuilder(){{#builtValueSerializers}} {{^fullTypeArgs}} {{#isArray}} const FullType(Built{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}, [FullType{{#isNullable}}.nullable{{/isNullable}}({{dataType}})]), - () => {{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}Builder<{{dataType}}>(), + () => {{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}Builder<{{dataType}}{{#isNullable}}?{{/isNullable}}>(), {{/isArray}} {{#isMap}} const FullType(BuiltMap, [FullType(String), FullType{{#isNullable}}.nullable{{/isNullable}}({{dataType}})]), diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/common/remove_null_query_parameters.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/common/remove_null_query_parameters.mustache new file mode 100644 index 0000000000000..8c395ffe35887 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/common/remove_null_query_parameters.mustache @@ -0,0 +1,6 @@ +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache new file mode 100644 index 0000000000000..7293d7d18859f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache @@ -0,0 +1 @@ +{{#isNullable}}{{{paramName}}} == null ? '' : {{{paramName}}}.toString(){{/isNullable}}{{^isNullable}}{{{paramName}}}.toString(){{/isNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api_util.mustache b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api_util.mustache new file mode 100644 index 0000000000000..69d4c33a95c9b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api_util.mustache @@ -0,0 +1,3 @@ +{{>header}} + +{{>serialization/common/remove_null_query_parameters}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioClientCodegenTest.java index 107ea58518021..541ecf99d78c4 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioClientCodegenTest.java @@ -351,4 +351,57 @@ public void testNestedAdditionalPropertiesGetBuilderFactories() throws IOExcepti "const FullType(BuiltMap, [FullType(String), FullType(BuiltList, [FullType(Widget)])]),", "() => MapBuilder>(),"); } + + /** + * Regression test for nullable collection item types in serializers.dart. + * + *

When a list or set property has a nullable item type (e.g. + * {@code items: { type: number, nullable: true }}), the generated + * {@code addBuilderFactory} call must use {@code ListBuilder()} + * (or {@code SetBuilder()}) to match the emitted + * {@code FullType.nullable(double)} / {@code FullType.nullable(String)}. + * + *

Before the fix, the builder instantiation was + * {@code ListBuilder()} — without the {@code ?} — causing a + * {@code FullType} mismatch at runtime. + */ + @Test + public void testNullableCollectionItemsGetNullableBuilderFactory() throws IOException { + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("dart-dio") + .setInputSpec("src/test/resources/3_0/dart-dio/built_value_nullable_collection_items.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + ClientOptInput opts = configurator.toClientOptInput(); + Generator generator = new DefaultGenerator().opts(opts); + List files = generator.generate(); + files.forEach(File::deleteOnExit); + + Path serializers = output.toPath().resolve("lib/src/serializers.dart"); + + // BuiltList with nullable double items: FullType must be .nullable and + // the builder factory must carry the ? on the type argument. + TestUtils.assertFileContains(serializers, + "const FullType(BuiltList, [FullType.nullable(double)]),", + "() => ListBuilder(),"); + + // BuiltSet with nullable String items. + TestUtils.assertFileContains(serializers, + "const FullType(BuiltSet, [FullType.nullable(String)]),", + "() => SetBuilder(),"); + + // BuiltMap with nullable String values. + TestUtils.assertFileContains(serializers, + "const FullType(BuiltMap, [FullType(String), FullType.nullable(String)]),", + "() => MapBuilder(),"); + + // Negative: non-nullable builder variants must NOT appear. + TestUtils.assertFileNotContains(serializers, + "() => ListBuilder(),"); + TestUtils.assertFileNotContains(serializers, + "() => SetBuilder(),"); + } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioModelTest.java index 0d602450214c4..6524dcd3efe6c 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioModelTest.java @@ -387,6 +387,124 @@ public void mapModelTest() { Assert.assertEquals(cm.vars.size(), 0); } + @Test(description = "uses schema-local discriminator mapping for dart-dio") + public void localDiscriminatorUsesDeclaredMappingsOnly() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/bugs/issue_15467.json"); + + final DefaultCodegen codegen = new DartDioClientCodegen(); + codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE); + codegen.processOpts(); + codegen.setOpenAPI(openAPI); + + final String modelName = "Animal"; + final Schema model = openAPI.getComponents().getSchemas().get(modelName); + final CodegenModel cm = codegen.fromModel(modelName, model); + + Assert.assertNotNull(cm.discriminator); + Assert.assertEquals(cm.discriminator.getMapping().size(), 4); + Assert.assertTrue(cm.discriminator.getMapping().containsKey("Bird")); + Assert.assertTrue(cm.discriminator.getMapping().containsKey("Reptile")); + Assert.assertTrue(cm.discriminator.getMapping().containsKey("Crocodile")); + Assert.assertTrue(cm.discriminator.getMapping().containsKey("Turtle")); + Assert.assertFalse(cm.discriminator.getMapping().containsKey("Lizard")); + } + + @Test(description = "does not leak sibling mappings into inherited discriminator for intermediate allOf models") + public void inheritedDiscriminatorKeepsOnlyDescendantsForIntermediateModel() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/bugs/issue_15467.json"); + + final DefaultCodegen codegen = new DartDioClientCodegen(); + codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE); + codegen.processOpts(); + codegen.setOpenAPI(openAPI); + + final Schema reptileSchema = openAPI.getComponents().getSchemas().get("Reptile"); + final CodegenModel reptileModel = codegen.fromModel("Reptile", reptileSchema); + + Assert.assertNotNull(reptileModel.discriminator); + Assert.assertNotNull(reptileModel.discriminator.getMapping()); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Crocodile")); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Turtle")); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Reptile")); + Assert.assertFalse(reptileModel.discriminator.getMapping().containsKey("Bird")); + + java.util.List reptileMappedModelOrder = reptileModel.discriminator.getMappedModels().stream() + .map(CodegenDiscriminator.MappedModel::getModelName) + .collect(java.util.stream.Collectors.toList()); + Assert.assertEquals(reptileMappedModelOrder.get(reptileMappedModelOrder.size() - 1), "Reptile"); + + Assert.assertEquals(reptileModel.discriminator.getVendorExtensions().get("x-has-discriminator-self-mapping"), Boolean.TRUE); + Assert.assertEquals(reptileModel.discriminator.getVendorExtensions().get("x-discriminator-self-mapping-name"), "Reptile"); + @SuppressWarnings("unchecked") + java.util.List reptileNonSelfMappedModels = + (java.util.List) reptileModel.discriminator.getVendorExtensions().get("x-discriminator-mapped-models-nonself"); + Assert.assertNotNull(reptileNonSelfMappedModels); + Assert.assertEquals( + reptileNonSelfMappedModels.stream() + .map(CodegenDiscriminator.MappedModel::getModelName) + .collect(java.util.stream.Collectors.toList()), + java.util.Arrays.asList("Crocodile", "Turtle")); + + final Schema animalSchema = openAPI.getComponents().getSchemas().get("Animal"); + final CodegenModel animalModel = codegen.fromModel("Animal", animalSchema); + Assert.assertNotNull(animalModel.discriminator); + Assert.assertNotNull(animalModel.discriminator.getMapping()); + Assert.assertTrue(animalModel.discriminator.getMapping().containsKey("Bird")); + Assert.assertTrue(animalModel.discriminator.getMapping().containsKey("Reptile")); + Assert.assertEquals(animalModel.discriminator.getVendorExtensions().get("x-has-discriminator-self-mapping"), Boolean.FALSE); + } + + @Test(description = "orders discriminator mappings so subclasses are checked before ancestor types") + public void discriminatorChecksSubclassesBeforeParentTypes() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/bugs/issue_15467.json"); + + final DefaultCodegen codegen = new DartDioClientCodegen(); + codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE); + codegen.processOpts(); + codegen.setOpenAPI(openAPI); + + final Schema animalSchema = openAPI.getComponents().getSchemas().get("Animal"); + final CodegenModel animalModel = codegen.fromModel("Animal", animalSchema); + + Assert.assertNotNull(animalModel.discriminator); + java.util.List mappedModelOrder = animalModel.discriminator.getMappedModels().stream() + .map(CodegenDiscriminator.MappedModel::getModelName) + .collect(java.util.stream.Collectors.toList()); + + Assert.assertTrue(mappedModelOrder.indexOf("Turtle") < mappedModelOrder.indexOf("Reptile")); + Assert.assertTrue(mappedModelOrder.indexOf("Crocodile") < mappedModelOrder.indexOf("Reptile")); + + @SuppressWarnings("unchecked") + java.util.List animalNonSelfMappedModels = + (java.util.List) animalModel.discriminator.getVendorExtensions().get("x-discriminator-mapped-models-nonself"); + Assert.assertNotNull(animalNonSelfMappedModels); + java.util.List animalNonSelfMappedModelOrder = animalNonSelfMappedModels.stream() + .map(CodegenDiscriminator.MappedModel::getModelName) + .collect(java.util.stream.Collectors.toList()); + Assert.assertTrue(animalNonSelfMappedModelOrder.indexOf("Turtle") < animalNonSelfMappedModelOrder.indexOf("Reptile")); + Assert.assertTrue(animalNonSelfMappedModelOrder.indexOf("Crocodile") < animalNonSelfMappedModelOrder.indexOf("Reptile")); + } + + @Test(description = "preserves declared oneOf alternatives on inherited discriminators") + public void inheritedDiscriminatorKeepsDeclaredOneOfAlternatives() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/bugs/issue_23717_inherited_discriminator_oneof.yaml"); + + final DefaultCodegen codegen = new DartDioClientCodegen(); + codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE); + codegen.processOpts(); + codegen.setOpenAPI(openAPI); + + final Schema reptileSchema = openAPI.getComponents().getSchemas().get("Reptile"); + final CodegenModel reptileModel = codegen.fromModel("Reptile", reptileSchema); + + Assert.assertNotNull(reptileModel.discriminator); + Assert.assertNotNull(reptileModel.discriminator.getMapping()); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Reptile")); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Lizard")); + Assert.assertTrue(reptileModel.discriminator.getMapping().containsKey("Snake")); + Assert.assertFalse(reptileModel.discriminator.getMapping().containsKey("Bird")); + } + @DataProvider(name = "modelNames") public static Object[][] modelNames() { return new Object[][]{ diff --git a/modules/openapi-generator/src/test/resources/3_0/dart-dio/built_value_nullable_collection_items.yaml b/modules/openapi-generator/src/test/resources/3_0/dart-dio/built_value_nullable_collection_items.yaml new file mode 100644 index 0000000000000..a9fc65f532abe --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/dart-dio/built_value_nullable_collection_items.yaml @@ -0,0 +1,46 @@ +openapi: "3.0.0" +info: + title: Built-value nullable collection item BuilderFactory test + version: "1.0.0" +paths: + /items: + get: + operationId: getItems + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Container" +components: + schemas: + Widget: + type: object + properties: + id: + type: integer + # A BuiltList whose item type is nullable (double?). + # Before the fix, serializers.dart emitted ListBuilder() instead + # of ListBuilder(), causing a FullType mismatch at runtime. + Container: + type: object + properties: + scores: + type: array + items: + type: number + format: double + nullable: true + tags: + type: array + uniqueItems: true + items: + type: string + nullable: true + lookup: + type: object + additionalProperties: + type: string + nullable: true + diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_15467.json b/modules/openapi-generator/src/test/resources/bugs/issue_15467.json new file mode 100644 index 0000000000000..d166013024dc8 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_15467.json @@ -0,0 +1,80 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Test API", + "version": "v1" + }, + "paths": {}, + "components": { + "schemas": { + "Animal": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "Bird": "#/components/schemas/Bird", + "Reptile": "#/components/schemas/Reptile", + "Crocodile": "#/components/schemas/Crocodile", + "Turtle": "#/components/schemas/Turtle" + } + }, + "additionalProperties": false, + "properties": { + "id": {"type": "string", "nullable": true}, + "name": {"type": "string", "nullable": true}, + "age": {"type": "integer", "format": "int32"}, + "type": {"type": "string", "nullable": true} + } + }, + "Bird": { + "allOf": [ + {"$ref": "#/components/schemas/Animal"}, + { + "type": "object", + "additionalProperties": false, + "properties": { + "wingSpan": {"type": "number", "format": "double"} + } + } + ] + }, + "Reptile": { + "allOf": [ + {"$ref": "#/components/schemas/Animal"}, + { + "type": "object", + "additionalProperties": false, + "properties": { + "scaleColor": {"type": "string", "nullable": true} + } + } + ] + }, + "Crocodile": { + "allOf": [ + {"$ref": "#/components/schemas/Reptile"}, + { + "type": "object", + "additionalProperties": false, + "properties": { + "numberOfTeeth": {"type": "integer", "format": "int32"} + } + } + ] + }, + "Turtle": { + "allOf": [ + {"$ref": "#/components/schemas/Reptile"}, + { + "type": "object", + "additionalProperties": false, + "properties": { + "shellDiameter": {"type": "number", "format": "double"} + } + } + ] + } + } + } +} + diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_23717_inherited_discriminator_oneof.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_23717_inherited_discriminator_oneof.yaml new file mode 100644 index 0000000000000..15c944d7f8be5 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_23717_inherited_discriminator_oneof.yaml @@ -0,0 +1,51 @@ +openapi: 3.0.0 +info: + title: Test API + version: v1 +paths: {} +components: + schemas: + Animal: + type: object + discriminator: + propertyName: type + mapping: + Bird: '#/components/schemas/Bird' + Reptile: '#/components/schemas/Reptile' + Lizard: '#/components/schemas/Lizard' + Snake: '#/components/schemas/Snake' + properties: + type: + type: string + Bird: + allOf: + - $ref: '#/components/schemas/Animal' + - type: object + properties: + wingSpan: + type: number + format: double + Reptile: + allOf: + - $ref: '#/components/schemas/Animal' + - type: object + properties: + scaleColor: + type: string + oneOf: + - $ref: '#/components/schemas/Lizard' + - $ref: '#/components/schemas/Snake' + Lizard: + allOf: + - $ref: '#/components/schemas/Animal' + - type: object + properties: + canRegrowTail: + type: boolean + Snake: + allOf: + - $ref: '#/components/schemas/Animal' + - type: object + properties: + venomous: + type: boolean diff --git a/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/docs/models/StringPatternsWithOneOf.md b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/docs/models/StringPatternsWithOneOf.md new file mode 100644 index 0000000000000..1c8837a76f960 --- /dev/null +++ b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/docs/models/StringPatternsWithOneOf.md @@ -0,0 +1,9 @@ +# Org.OpenAPITools.Model.StringPatternsWithOneOf + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md) + diff --git a/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools.Test/Model/StringPatternsWithOneOfTests.cs b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools.Test/Model/StringPatternsWithOneOfTests.cs new file mode 100644 index 0000000000000..1c994e15b475d --- /dev/null +++ b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools.Test/Model/StringPatternsWithOneOfTests.cs @@ -0,0 +1,56 @@ +/* + * Example + * + * 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://github.com/openapitools/openapi-generator.git + */ + + +using Xunit; + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; + +namespace Org.OpenAPITools.Test.Model +{ + ///

+ /// Class for testing StringPatternsWithOneOf + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class StringPatternsWithOneOfTests : IDisposable + { + // TODO uncomment below to declare an instance variable for StringPatternsWithOneOf + //private StringPatternsWithOneOf instance; + + public StringPatternsWithOneOfTests() + { + // TODO uncomment below to create an instance of StringPatternsWithOneOf + //instance = new StringPatternsWithOneOf(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of StringPatternsWithOneOf + /// + [Fact] + public void StringPatternsWithOneOfInstanceTest() + { + // TODO uncomment below to test "IsType" StringPatternsWithOneOf + //Assert.IsType(instance); + } + } +} diff --git a/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools/Model/StringPatternsWithOneOf.cs b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools/Model/StringPatternsWithOneOf.cs new file mode 100644 index 0000000000000..7a9ba3304ff4f --- /dev/null +++ b/samples/client/petstore/csharp/generichost/latest/AnnotatedEnum/src/Org.OpenAPITools/Model/StringPatternsWithOneOf.cs @@ -0,0 +1,162 @@ +// +/* + * Example + * + * 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://github.com/openapitools/openapi-generator.git + */ + +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.ComponentModel.DataAnnotations; +using Org.OpenAPITools.Client; + +namespace Org.OpenAPITools.Model +{ + /// + /// StringPatternsWithOneOf + /// + public partial class StringPatternsWithOneOf : IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// + internal StringPatternsWithOneOf(string @string) + { + String = @string; + OnCreated(); + } + + partial void OnCreated(); + + /// + /// Numeric identifier + /// + /// Numeric identifier + public string? String { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class StringPatternsWithOneOf {\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + yield break; + } + } + + /// + /// A Json converter for type + /// + public partial class StringPatternsWithOneOfJsonConverter : JsonConverter + { + partial void OnCreated(); + + /// + /// Initializes a new instance of the class. + /// + public StringPatternsWithOneOfJsonConverter() + { + OnCreated(); + } + + /// + /// Deserializes json to + /// + /// + /// + /// + /// + /// + public override StringPatternsWithOneOf Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) + { + int currentDepth = utf8JsonReader.CurrentDepth; + + if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray) + throw new JsonException(); + + JsonTokenType startingTokenType = utf8JsonReader.TokenType; + + string? varString = default; + + while (utf8JsonReader.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1) + { + string? localVarJsonPropertyName = utf8JsonReader.GetString(); + utf8JsonReader.Read(); + + switch (localVarJsonPropertyName) + { + default: + break; + } + } + } + + if (varString != null) + return new StringPatternsWithOneOf(varString); + + throw new JsonException(); + } + + /// + /// Serializes a + /// + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, StringPatternsWithOneOf stringPatternsWithOneOf, JsonSerializerOptions jsonSerializerOptions) + { + writer.WriteStartObject(); + + WriteProperties(writer, stringPatternsWithOneOf, jsonSerializerOptions); + writer.WriteEndObject(); + } + + /// + /// Serializes the properties of + /// + /// + /// + /// + /// + public void WriteProperties(Utf8JsonWriter writer, StringPatternsWithOneOf stringPatternsWithOneOf, JsonSerializerOptions jsonSerializerOptions) + { + + } + } +} diff --git a/samples/openapi3/client/petstore/dart-dio/anyof/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/anyof/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/anyof/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/anyof/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/binary_response/.openapi-generator/FILES b/samples/openapi3/client/petstore/dart-dio/binary_response/.openapi-generator/FILES index 809d286209731..fbf70217c43dd 100644 --- a/samples/openapi3/client/petstore/dart-dio/binary_response/.openapi-generator/FILES +++ b/samples/openapi3/client/petstore/dart-dio/binary_response/.openapi-generator/FILES @@ -6,6 +6,7 @@ doc/DefaultApi.md lib/openapi.dart lib/src/api.dart lib/src/api/default_api.dart +lib/src/api_util.dart lib/src/auth/api_key_auth.dart lib/src/auth/auth.dart lib/src/auth/basic_auth.dart diff --git a/samples/openapi3/client/petstore/dart-dio/binary_response/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/binary_response/lib/src/api_util.dart new file mode 100644 index 0000000000000..e139907e3020b --- /dev/null +++ b/samples/openapi3/client/petstore/dart-dio/binary_response/lib/src/api_util.dart @@ -0,0 +1,11 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// + + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/oneof/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/oneof/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/bar_ref_or_value.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/bar_ref_or_value.dart index da9d22c502e67..b9f6a6809b53f 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/bar_ref_or_value.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/bar_ref_or_value.dart @@ -71,6 +71,8 @@ extension BarRefOrValueBuilderDiscriminatorExt on BarRefOrValueBuilder { } } + + class _$BarRefOrValueSerializer implements PrimitiveSerializer { @override final Iterable types = const [BarRefOrValue, _$BarRefOrValue]; diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity.dart index 7965d016a2737..a4f81c45b5194 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity.dart @@ -29,12 +29,12 @@ abstract class Entity implements Addressable, Extensible { static const String discriminatorFieldName = r'@type'; static const Map discriminatorMapping = { + r'PizzaSpeziale': PizzaSpeziale, r'Bar': Bar, r'Bar_Create': BarCreate, r'Foo': Foo, r'Pasta': Pasta, r'Pizza': Pizza, - r'PizzaSpeziale': PizzaSpeziale, }; @BuiltValueSerializer(custom: true) @@ -43,6 +43,9 @@ abstract class Entity implements Addressable, Extensible { extension EntityDiscriminatorExt on Entity { String? get discriminatorValue { + if (this is PizzaSpeziale) { + return r'PizzaSpeziale'; + } if (this is Bar) { return r'Bar'; } @@ -58,14 +61,14 @@ extension EntityDiscriminatorExt on Entity { if (this is Pizza) { return r'Pizza'; } - if (this is PizzaSpeziale) { - return r'PizzaSpeziale'; - } return null; } } extension EntityBuilderDiscriminatorExt on EntityBuilder { String? get discriminatorValue { + if (this is PizzaSpezialeBuilder) { + return r'PizzaSpeziale'; + } if (this is BarBuilder) { return r'Bar'; } @@ -81,13 +84,12 @@ extension EntityBuilderDiscriminatorExt on EntityBuilder { if (this is PizzaBuilder) { return r'Pizza'; } - if (this is PizzaSpezialeBuilder) { - return r'PizzaSpeziale'; - } return null; } } + + class _$EntitySerializer implements PrimitiveSerializer { @override final Iterable types = const [Entity]; @@ -141,6 +143,9 @@ class _$EntitySerializer implements PrimitiveSerializer { Entity object, { FullType specifiedType = FullType.unspecified, }) { + if (object is PizzaSpeziale) { + return serializers.serialize(object, specifiedType: FullType(PizzaSpeziale))!; + } if (object is Bar) { return serializers.serialize(object, specifiedType: FullType(Bar))!; } @@ -156,9 +161,6 @@ class _$EntitySerializer implements PrimitiveSerializer { if (object is Pizza) { return serializers.serialize(object, specifiedType: FullType(Pizza))!; } - if (object is PizzaSpeziale) { - return serializers.serialize(object, specifiedType: FullType(PizzaSpeziale))!; - } return _serializeProperties(serializers, object, specifiedType: specifiedType).toList(); } @@ -172,6 +174,8 @@ class _$EntitySerializer implements PrimitiveSerializer { final discIndex = serializedList.indexOf(Entity.discriminatorFieldName) + 1; final discValue = serializers.deserialize(serializedList[discIndex], specifiedType: FullType(String)) as String; switch (discValue) { + case r'PizzaSpeziale': + return serializers.deserialize(serialized, specifiedType: FullType(PizzaSpeziale)) as PizzaSpeziale; case r'Bar': return serializers.deserialize(serialized, specifiedType: FullType(Bar)) as Bar; case r'Bar_Create': @@ -182,8 +186,6 @@ class _$EntitySerializer implements PrimitiveSerializer { return serializers.deserialize(serialized, specifiedType: FullType(Pasta)) as Pasta; case r'Pizza': return serializers.deserialize(serialized, specifiedType: FullType(Pizza)) as Pizza; - case r'PizzaSpeziale': - return serializers.deserialize(serialized, specifiedType: FullType(PizzaSpeziale)) as PizzaSpeziale; default: return serializers.deserialize(serialized, specifiedType: FullType($Entity)) as $Entity; } diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity_ref.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity_ref.dart index 9db50cb7d6acc..b95b32a1dfbcc 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity_ref.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/entity_ref.dart @@ -66,6 +66,8 @@ extension EntityRefBuilderDiscriminatorExt on EntityRefBuilder { } } + + class _$EntityRefSerializer implements PrimitiveSerializer { @override final Iterable types = const [EntityRef]; diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/foo_ref_or_value.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/foo_ref_or_value.dart index 5eb0e894671ed..bda694e6f8531 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/foo_ref_or_value.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/foo_ref_or_value.dart @@ -70,6 +70,8 @@ extension FooRefOrValueBuilderDiscriminatorExt on FooRefOrValueBuilder { } } + + class _$FooRefOrValueSerializer implements PrimitiveSerializer { @override final Iterable types = const [FooRefOrValue, _$FooRefOrValue]; diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart index 48651d978c235..132fb657567da 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart @@ -68,6 +68,8 @@ extension FruitBuilderDiscriminatorExt on FruitBuilder { } } + + class _$FruitSerializer implements PrimitiveSerializer { @override final Iterable types = const [Fruit, _$Fruit]; diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/pizza.dart b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/pizza.dart index 238e2ae15924b..7b9a0362743d7 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/pizza.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/pizza.dart @@ -51,6 +51,8 @@ extension PizzaBuilderDiscriminatorExt on PizzaBuilder { } } + + class _$PizzaSerializer implements PrimitiveSerializer { @override final Iterable types = const [Pizza]; diff --git a/samples/openapi3/client/petstore/dart-dio/oneof_primitive/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/oneof_primitive/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/oneof_primitive/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/oneof_primitive/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/pet_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/pet_api.dart index 8aaf646e3051a..7e1c73bb8edf4 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/pet_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/pet_api.dart @@ -146,7 +146,8 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', (encodeQueryParameter(_serializers, petId, const FullType(int)).toString()) +); final _options = Options( method: r'DELETE', headers: { @@ -220,6 +221,12 @@ class PetApi { final _queryParameters = { r'status': encodeCollectionQueryParameter(_serializers, status, const FullType(BuiltList, [FullType(String)]), format: ListFormat.csv,), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -306,6 +313,12 @@ class PetApi { final _queryParameters = { r'tags': encodeCollectionQueryParameter(_serializers, tags, const FullType(BuiltList, [FullType(String)]), format: ListFormat.csv,), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -370,7 +383,8 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', (encodeQueryParameter(_serializers, petId, const FullType(int)).toString()) +); final _options = Options( method: r'GET', headers: { @@ -558,7 +572,8 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', (encodeQueryParameter(_serializers, petId, const FullType(int)).toString()) +); final _options = Options( method: r'POST', headers: { @@ -581,8 +596,8 @@ class PetApi { try { _bodyData = { - if (name != null) r'name': encodeQueryParameter(_serializers, name, const FullType(String)), - if (status != null) r'status': encodeQueryParameter(_serializers, status, const FullType(String)), + if (name != null) r'name': encodeFormParameter(_serializers, name, const FullType(String)), + if (status != null) r'status': encodeFormParameter(_serializers, status, const FullType(String)), }; } catch(error, stackTrace) { @@ -636,7 +651,8 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', (encodeQueryParameter(_serializers, petId, const FullType(int)).toString()) +); final _options = Options( method: r'POST', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/store_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/store_api.dart index 5ad35b28d39a7..fec3deedda987 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/store_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/store_api.dart @@ -43,7 +43,8 @@ class StoreApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{orderId}'.replaceAll('{' r'orderId' '}', encodeQueryParameter(_serializers, orderId, const FullType(String)).toString()); + final _path = r'/store/order/{orderId}'.replaceAll('{' r'orderId' '}', (encodeQueryParameter(_serializers, orderId, const FullType(String)).toString()) +); final _options = Options( method: r'DELETE', headers: { @@ -170,7 +171,8 @@ class StoreApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{orderId}'.replaceAll('{' r'orderId' '}', encodeQueryParameter(_serializers, orderId, const FullType(int)).toString()); + final _path = r'/store/order/{orderId}'.replaceAll('{' r'orderId' '}', (encodeQueryParameter(_serializers, orderId, const FullType(int)).toString()) +); final _options = Options( method: r'GET', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/user_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/user_api.dart index c052b91ccf633..05af91b58a1fc 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/user_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api/user_api.dart @@ -266,7 +266,8 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', (encodeQueryParameter(_serializers, username, const FullType(String)).toString()) +); final _options = Options( method: r'DELETE', headers: { @@ -320,7 +321,8 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', (encodeQueryParameter(_serializers, username, const FullType(String)).toString()) +); final _options = Options( method: r'GET', headers: { @@ -414,6 +416,12 @@ class UserApi { r'username': encodeQueryParameter(_serializers, username, const FullType(String)), r'password': encodeQueryParameter(_serializers, password, const FullType(String)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -529,7 +537,8 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', (encodeQueryParameter(_serializers, username, const FullType(String)).toString()) +); final _options = Options( method: r'PUT', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore-timemachine/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/.openapi-generator/FILES b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/.openapi-generator/FILES index a3483714374f4..27b493adc77ba 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/.openapi-generator/FILES +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/.openapi-generator/FILES @@ -74,6 +74,7 @@ lib/src/api/fake_classname_tags123_api.dart lib/src/api/pet_api.dart lib/src/api/store_api.dart lib/src/api/user_api.dart +lib/src/api_util.dart lib/src/auth/api_key_auth.dart lib/src/auth/auth.dart lib/src/auth/basic_auth.dart diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/fake_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/fake_api.dart index 370401906dcd0..880cffb4a4bd2 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/fake_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/fake_api.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'package:openapi/src/deserialize.dart'; import 'package:dio/dio.dart'; +import 'package:openapi/src/api_util.dart'; import 'package:openapi/src/model/child_with_nullable.dart'; import 'package:openapi/src/model/fake_big_decimal_map200_response.dart'; import 'package:openapi/src/model/file_schema_test_class.dart'; @@ -289,6 +290,12 @@ _responseData = rawData == null ? null : deserialize{ if (query1 != null) r'query_1': query1, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; @@ -1016,6 +1023,12 @@ _responseData = rawData == null ? null : deserialize{ r'query': query, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; @@ -1314,6 +1327,12 @@ _responseData = rawData == null ? null : deserialize(r if (enumQueryDouble != null) r'enum_query_double': enumQueryDouble, if (enumQueryModelArray != null) r'enum_query_model_array': enumQueryModelArray, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; @@ -1409,6 +1428,12 @@ _responseData = rawData == null ? null : deserialize(r if (stringGroup != null) r'string_group': stringGroup, if (int64Group != null) r'int64_group': int64Group, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -1748,6 +1773,12 @@ _responseData = rawData == null ? null : deserialize(r if (language != null) r'language': language, r'allowEmpty': allowEmpty, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/pet_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/pet_api.dart index 60aaece869053..fe00762da4dec 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/pet_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/pet_api.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'package:openapi/src/deserialize.dart'; import 'package:dio/dio.dart'; +import 'package:openapi/src/api_util.dart'; import 'package:openapi/src/model/api_response.dart'; import 'package:openapi/src/model/pet.dart'; @@ -114,7 +115,8 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString() +); final _options = Options( method: r'DELETE', headers: { @@ -188,6 +190,12 @@ class PetApi { final _queryParameters = { r'status': status, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -271,6 +279,12 @@ _responseData = rawData == null ? null : deserialize, Pet>(rawData, 'L final _queryParameters = { r'tags': tags, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -332,7 +346,8 @@ _responseData = rawData == null ? null : deserialize, Pet>(rawData, 'Se ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString() +); final _options = Options( method: r'GET', headers: { @@ -486,7 +501,8 @@ _responseData = rawData == null ? null : deserialize(rawData, 'Pet', g ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString() +); final _options = Options( method: r'POST', headers: { @@ -564,7 +580,8 @@ _responseData = rawData == null ? null : deserialize(rawData, 'Pet', g ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', petId.toString()); + final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', petId.toString() +); final _options = Options( method: r'POST', headers: { @@ -667,7 +684,8 @@ _responseData = rawData == null ? null : deserialize(r ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/fake/{petId}/uploadImageWithRequiredFile'.replaceAll('{' r'petId' '}', petId.toString()); + final _path = r'/fake/{petId}/uploadImageWithRequiredFile'.replaceAll('{' r'petId' '}', petId.toString() +); final _options = Options( method: r'POST', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/store_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/store_api.dart index 272e45a0ef6cb..629a61b6de32d 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/store_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/store_api.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'package:openapi/src/deserialize.dart'; import 'package:dio/dio.dart'; +import 'package:openapi/src/api_util.dart'; import 'package:openapi/src/model/order.dart'; class StoreApi { @@ -40,7 +41,8 @@ class StoreApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString()); + final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString() +); final _options = Options( method: r'DELETE', headers: { @@ -164,7 +166,8 @@ _responseData = rawData == null ? null : deserialize, int>(rawD ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString()); + final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString() +); final _options = Options( method: r'GET', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/user_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/user_api.dart index 87ceeccc6b4a5..aefe942713434 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/user_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api/user_api.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'package:openapi/src/deserialize.dart'; import 'package:dio/dio.dart'; +import 'package:openapi/src/api_util.dart'; import 'package:openapi/src/model/user.dart'; class UserApi { @@ -238,7 +239,8 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString() +); final _options = Options( method: r'DELETE', headers: { @@ -285,7 +287,8 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString() +); final _options = Options( method: r'GET', headers: { @@ -376,6 +379,12 @@ _responseData = rawData == null ? null : deserialize(rawData, 'User' r'username': username, r'password': password, }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -484,7 +493,8 @@ _responseData = rawData == null ? null : deserialize(rawData, 'S ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString() +); final _options = Options( method: r'PUT', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api_util.dart new file mode 100644 index 0000000000000..e139907e3020b --- /dev/null +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/api_util.dart @@ -0,0 +1,11 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// + + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/fake_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/fake_api.dart index 2c26b06536d94..85929159abb46 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/fake_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/fake_api.dart @@ -304,6 +304,12 @@ class FakeApi { final _queryParameters = { if (query1 != null) r'query_1': encodeQueryParameter(_serializers, query1, const FullType(String)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; @@ -1042,6 +1048,12 @@ class FakeApi { final _queryParameters = { r'query': encodeQueryParameter(_serializers, query, const FullType(String)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; @@ -1243,20 +1255,20 @@ class FakeApi { try { _bodyData = { - if (integer != null) r'integer': encodeQueryParameter(_serializers, integer, const FullType(int)), - if (int32 != null) r'int32': encodeQueryParameter(_serializers, int32, const FullType(int)), - if (int64 != null) r'int64': encodeQueryParameter(_serializers, int64, const FullType(int)), - r'number': encodeQueryParameter(_serializers, number, const FullType(num)), - if (float != null) r'float': encodeQueryParameter(_serializers, float, const FullType(double)), - r'double': encodeQueryParameter(_serializers, double_, const FullType(double)), - if (string != null) r'string': encodeQueryParameter(_serializers, string, const FullType(String)), - r'pattern_without_delimiter': encodeQueryParameter(_serializers, patternWithoutDelimiter, const FullType(String)), - r'byte': encodeQueryParameter(_serializers, byte, const FullType(String)), - if (binary != null) r'binary': encodeQueryParameter(_serializers, binary, const FullType(Uint8List)), - if (date != null) r'date': encodeQueryParameter(_serializers, date, const FullType(Date)), - if (dateTime != null) r'dateTime': encodeQueryParameter(_serializers, dateTime, const FullType(DateTime)), - if (password != null) r'password': encodeQueryParameter(_serializers, password, const FullType(String)), - if (callback != null) r'callback': encodeQueryParameter(_serializers, callback, const FullType(String)), + if (integer != null) r'integer': encodeFormParameter(_serializers, integer, const FullType(int)), + if (int32 != null) r'int32': encodeFormParameter(_serializers, int32, const FullType(int)), + if (int64 != null) r'int64': encodeFormParameter(_serializers, int64, const FullType(int)), + r'number': encodeFormParameter(_serializers, number, const FullType(num)), + if (float != null) r'float': encodeFormParameter(_serializers, float, const FullType(double)), + r'double': encodeFormParameter(_serializers, double_, const FullType(double)), + if (string != null) r'string': encodeFormParameter(_serializers, string, const FullType(String)), + r'pattern_without_delimiter': encodeFormParameter(_serializers, patternWithoutDelimiter, const FullType(String)), + r'byte': encodeFormParameter(_serializers, byte, const FullType(String)), + if (binary != null) r'binary': encodeFormParameter(_serializers, binary, const FullType(Uint8List)), + if (date != null) r'date': encodeFormParameter(_serializers, date, const FullType(Date)), + if (dateTime != null) r'dateTime': encodeFormParameter(_serializers, dateTime, const FullType(DateTime)), + if (password != null) r'password': encodeFormParameter(_serializers, password, const FullType(String)), + if (callback != null) r'callback': encodeFormParameter(_serializers, callback, const FullType(String)), }; } catch(error, stackTrace) { @@ -1345,13 +1357,19 @@ class FakeApi { if (enumQueryDouble != null) r'enum_query_double': encodeQueryParameter(_serializers, enumQueryDouble, const FullType(double)), if (enumQueryModelArray != null) r'enum_query_model_array': encodeCollectionQueryParameter(_serializers, enumQueryModelArray, const FullType(BuiltList, [FullType(ModelEnumClass)]), format: ListFormat.multi,), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); dynamic _bodyData; try { _bodyData = { - if (enumFormStringArray != null) r'enum_form_string_array': encodeCollectionQueryParameter(_serializers, enumFormStringArray, const FullType(BuiltList, [FullType(String)]), format: ListFormat.csv,), - if (enumFormString != null) r'enum_form_string': encodeQueryParameter(_serializers, enumFormString, const FullType(String)), + if (enumFormStringArray != null) r'enum_form_string_array': encodeCollectionFormParameter(_serializers, enumFormStringArray, const FullType(BuiltList, [FullType(String)]), format: ListFormat.csv,), + if (enumFormString != null) r'enum_form_string': encodeFormParameter(_serializers, enumFormString, const FullType(String)), }; } catch(error, stackTrace) { @@ -1440,6 +1458,12 @@ class FakeApi { if (stringGroup != null) r'string_group': encodeQueryParameter(_serializers, stringGroup, const FullType(int)), if (int64Group != null) r'int64_group': encodeQueryParameter(_serializers, int64Group, const FullType(int)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -1630,8 +1654,8 @@ class FakeApi { try { _bodyData = { - r'param': encodeQueryParameter(_serializers, param, const FullType(String)), - r'param2': encodeQueryParameter(_serializers, param2, const FullType(String)), + r'param': encodeFormParameter(_serializers, param, const FullType(String)), + r'param2': encodeFormParameter(_serializers, param2, const FullType(String)), }; } catch(error, stackTrace) { @@ -1782,6 +1806,12 @@ class FakeApi { if (language != null) r'language': encodeQueryParameter(_serializers, language, const FullType(BuiltMap, [FullType(String), FullType(String)]), ), r'allowEmpty': encodeQueryParameter(_serializers, allowEmpty, const FullType(String)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/pet_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/pet_api.dart index a4da1c5ff6dbc..978e591ec2c2a 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/pet_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/pet_api.dart @@ -118,7 +118,7 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); final _options = Options( method: r'DELETE', headers: { @@ -192,6 +192,12 @@ class PetApi { final _queryParameters = { r'status': encodeCollectionQueryParameter(_serializers, status, const FullType(BuiltList, [FullType(String)]), format: ListFormat.csv,), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -278,6 +284,12 @@ class PetApi { final _queryParameters = { r'tags': encodeCollectionQueryParameter(_serializers, tags, const FullType(BuiltSet, [FullType(String)]), format: ListFormat.csv,), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -342,7 +354,7 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); final _options = Options( method: r'GET', headers: { @@ -500,7 +512,7 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', petId.toString()); final _options = Options( method: r'POST', headers: { @@ -523,8 +535,8 @@ class PetApi { try { _bodyData = { - if (name != null) r'name': encodeQueryParameter(_serializers, name, const FullType(String)), - if (status != null) r'status': encodeQueryParameter(_serializers, status, const FullType(String)), + if (name != null) r'name': encodeFormParameter(_serializers, name, const FullType(String)), + if (status != null) r'status': encodeFormParameter(_serializers, status, const FullType(String)), }; } catch(error, stackTrace) { @@ -578,7 +590,7 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/pet/{petId}/uploadImage'.replaceAll('{' r'petId' '}', petId.toString()); final _options = Options( method: r'POST', headers: { @@ -684,7 +696,7 @@ class PetApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/fake/{petId}/uploadImageWithRequiredFile'.replaceAll('{' r'petId' '}', encodeQueryParameter(_serializers, petId, const FullType(int)).toString()); + final _path = r'/fake/{petId}/uploadImageWithRequiredFile'.replaceAll('{' r'petId' '}', petId.toString()); final _options = Options( method: r'POST', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/store_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/store_api.dart index 6f519ff8ee518..45ab1d462b9d4 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/store_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/store_api.dart @@ -43,7 +43,7 @@ class StoreApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', encodeQueryParameter(_serializers, orderId, const FullType(String)).toString()); + final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString()); final _options = Options( method: r'DELETE', headers: { @@ -170,7 +170,7 @@ class StoreApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', encodeQueryParameter(_serializers, orderId, const FullType(int)).toString()); + final _path = r'/store/order/{order_id}'.replaceAll('{' r'order_id' '}', orderId.toString()); final _options = Options( method: r'GET', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/user_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/user_api.dart index 2a7e5132a60a6..aad4658c14835 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/user_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api/user_api.dart @@ -244,7 +244,7 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); final _options = Options( method: r'DELETE', headers: { @@ -291,7 +291,7 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); final _options = Options( method: r'GET', headers: { @@ -385,6 +385,12 @@ class UserApi { r'username': encodeQueryParameter(_serializers, username, const FullType(String)), r'password': encodeQueryParameter(_serializers, password, const FullType(String)), }; + removeNullQueryParametersExcept( + _queryParameters, + { + + }, + ); final _response = await _dio.request( _path, @@ -493,7 +499,7 @@ class UserApi { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final _path = r'/user/{username}'.replaceAll('{' r'username' '}', encodeQueryParameter(_serializers, username, const FullType(String)).toString()); + final _path = r'/user/{username}'.replaceAll('{' r'username' '}', username.toString()); final _options = Options( method: r'PUT', headers: { diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api_util.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api_util.dart index ed3bb12f25b81..0414453883220 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/api_util.dart @@ -38,7 +38,7 @@ dynamic encodeQueryParameter( FullType type, ) { if (value == null) { - return ''; + return null; } if (value is String || value is num || value is bool) { return value; @@ -52,7 +52,7 @@ dynamic encodeQueryParameter( specifiedType: type, ); if (serialized == null) { - return ''; + return null; } if (serialized is String) { return serialized; @@ -60,18 +60,31 @@ dynamic encodeQueryParameter( return serialized; } -ListParam encodeCollectionQueryParameter( +ListParam? encodeCollectionQueryParameter( Serializers serializers, dynamic value, FullType type, { ListFormat format = ListFormat.multi, }) { + if (value == null) { + return null; + } final serialized = serializers.serialize( value as Object, specifiedType: type, ); + if (serialized == null) { + return null; + } if (value is BuiltList || value is BuiltSet) { return ListParam(List.of((serialized as Iterable).cast()), format); } throw ArgumentError('Invalid value passed to encodeCollectionQueryParameter'); } + +void removeNullQueryParametersExcept( + Map queryParameters, + Set requiredParameters, +) { + queryParameters.removeWhere((key, value) => value == null && !requiredParameters.contains(key)); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/animal.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/animal.dart index 0eb3c9fe4fc24..d1c2dbe11b40a 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/animal.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/animal.dart @@ -57,6 +57,8 @@ extension AnimalBuilderDiscriminatorExt on AnimalBuilder { } } + + class _$AnimalSerializer implements PrimitiveSerializer { @override final Iterable types = const [Animal]; diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/parent_with_nullable.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/parent_with_nullable.dart index 92dd01f197522..373201d259aaf 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/parent_with_nullable.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/model/parent_with_nullable.dart @@ -49,6 +49,8 @@ extension ParentWithNullableBuilderDiscriminatorExt on ParentWithNullableBuilder } } + + class _$ParentWithNullableSerializer implements PrimitiveSerializer { @override final Iterable types = const [ParentWithNullable]; diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/serializers.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/serializers.dart index 4a1884a3eca2b..8a99a2fdf732f 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/serializers.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/src/serializers.dart @@ -202,7 +202,7 @@ Serializers serializers = (_$serializers.toBuilder() ) ..addBuilderFactory( const FullType(BuiltList, [FullType.nullable(JsonObject)]), - () => ListBuilder(), + () => ListBuilder(), ) ..addBuilderFactory( const FullType(BuiltList, [FullType(Tag)]), diff --git a/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/HttpServerVerticle.java b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/HttpServerVerticle.java new file mode 100644 index 0000000000000..9f8811724f179 --- /dev/null +++ b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/HttpServerVerticle.java @@ -0,0 +1,63 @@ +package org.openapitools.vertxweb.server; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.openapi.RouterBuilder; +import io.vertx.ext.web.openapi.RouterBuilderOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.openapitools.vertxweb.server.api.PetApiHandler; +import org.openapitools.vertxweb.server.api.PetApiImpl; +import org.openapitools.vertxweb.server.api.StoreApiHandler; +import org.openapitools.vertxweb.server.api.StoreApiImpl; +import org.openapitools.vertxweb.server.api.UserApiHandler; +import org.openapitools.vertxweb.server.api.UserApiImpl; + +public class HttpServerVerticle extends AbstractVerticle { + + private static final Logger logger = LoggerFactory.getLogger(HttpServerVerticle.class); + private static final String specFile = "src/main/resources/openapi.yaml"; + + + private final PetApiHandler petHandler = new PetApiHandler(new PetApiImpl()); + private final StoreApiHandler storeHandler = new StoreApiHandler(new StoreApiImpl()); + private final UserApiHandler userHandler = new UserApiHandler(new UserApiImpl()); + + @Override + public void start(Promise startPromise) { + RouterBuilder.create(vertx, specFile) + .map(builder -> { + builder.setOptions(new RouterBuilderOptions() + // For production use case, you need to enable this flag and provide the proper security handler + .setRequireSecurityHandlers(false) + ); + + petHandler.mount(builder); + storeHandler.mount(builder); + userHandler.mount(builder); + + Router router = builder.createRouter(); + router.errorHandler(400, this::validationFailureHandler); + + return router; + }) + .compose(router -> + vertx.createHttpServer() + .requestHandler(router) + .listen(8080) + ) + .onSuccess(server -> logger.info("Http verticle deploy successful")) + .onFailure(t -> logger.error("Http verticle failed to deploy", t)) + // Complete the start promise + .mapEmpty().onComplete(startPromise); + } + + private void validationFailureHandler(RoutingContext rc) { + rc.response().setStatusCode(400) + .end("Bad Request : " + rc.failure().getMessage()); + } +} diff --git a/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/PetApiImpl.java b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/PetApiImpl.java new file mode 100644 index 0000000000000..8743965e08aa9 --- /dev/null +++ b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/PetApiImpl.java @@ -0,0 +1,51 @@ +package org.openapitools.vertxweb.server.api; + +import io.vertx.ext.web.FileUpload; +import org.openapitools.vertxweb.server.model.ModelApiResponse; +import org.openapitools.vertxweb.server.model.Pet; + +import org.openapitools.vertxweb.server.ApiResponse; + +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.handler.HttpException; + +import java.util.List; +import java.util.Map; + +// Implement this class + +public class PetApiImpl implements PetApi { + public Future> addPet(Pet pet) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> deletePet(Long petId, String apiKey) { + return Future.failedFuture(new HttpException(501)); + } + + public Future>> findPetsByStatus(List status) { + return Future.failedFuture(new HttpException(501)); + } + + public Future>> findPetsByTags(List tags) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> getPetById(Long petId) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> updatePet(Pet pet) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> updatePetWithForm(Long petId, JsonObject formBody) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> uploadFile(Long petId, FileUpload _file) { + return Future.failedFuture(new HttpException(501)); + } + +} diff --git a/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApiImpl.java b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApiImpl.java new file mode 100644 index 0000000000000..1bc8d437a05d0 --- /dev/null +++ b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApiImpl.java @@ -0,0 +1,33 @@ +package org.openapitools.vertxweb.server.api; + +import org.openapitools.vertxweb.server.model.Order; + +import org.openapitools.vertxweb.server.ApiResponse; + +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.handler.HttpException; + +import java.util.List; +import java.util.Map; + +// Implement this class + +public class StoreApiImpl implements StoreApi { + public Future> deleteOrder(String orderId) { + return Future.failedFuture(new HttpException(501)); + } + + public Future>> getInventory() { + return Future.failedFuture(new HttpException(501)); + } + + public Future> getOrderById(Long orderId) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> placeOrder(Order order) { + return Future.failedFuture(new HttpException(501)); + } + +} diff --git a/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/UserApiImpl.java b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/UserApiImpl.java new file mode 100644 index 0000000000000..414f22e0de2ad --- /dev/null +++ b/samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/UserApiImpl.java @@ -0,0 +1,50 @@ +package org.openapitools.vertxweb.server.api; + +import java.time.OffsetDateTime; +import org.openapitools.vertxweb.server.model.User; + +import org.openapitools.vertxweb.server.ApiResponse; + +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.handler.HttpException; + +import java.util.List; +import java.util.Map; + +// Implement this class + +public class UserApiImpl implements UserApi { + public Future> createUser(User user) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> createUsersWithArrayInput(List user) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> createUsersWithListInput(List user) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> deleteUser(String username) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> getUserByName(String username) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> loginUser(String username, String password) { + return Future.failedFuture(new HttpException(501)); + } + + public Future> logoutUser() { + return Future.failedFuture(new HttpException(501)); + } + + public Future> updateUser(String username, User user) { + return Future.failedFuture(new HttpException(501)); + } + +}