diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 246e34a..39e6fd4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,36 @@ = Changelog +== 8.x Series is based on OpenAPI 7.x+ + +- The OpenAPI base now shifts to version 7 and Java 11. +- It also shifts to a more strict interpretation of nullable (a field can have a null value) vs required (a field +is required, but can be null or non null). A field is REQUIRED is it is marked as required, or if it is *not nullable* +and has no default (otherwise Dart doesn't know what to do with it). +- We drop support for pre-Dart 2.12 code (i.e. non-null safe code) +- We add the ability to drop deprecated APIs (additional property `filter-deprecated-apis`) +- We add the ability to drop deprecated Models (additional property `filter-deprecated-models`) + + +If you want to support something like Person? then you need something like this: + +---- + SomeClass: + properties: + person: # do not include a description or the API changes code generation + nullable: true + allOf: + - $ref: "#/components/schemas/Person" +---- + +This text snippet does not change the API, you get a `Person? person` field. If you add a description, +OpenAPI will automatically generate a new class which is outside of our control, and you will get a new +class likely called `SomeClassPerson` so it can hold the description. + +If you put `nullable: true` on the definition of the object itself, it inherently turns the object to +always be nullable, so you get things like `List people`. + + + == 7.x Series is based on OpenAPI 6.2+ The OpenAPI crew broke the API in weird ways in 6.2, so we go to a new major diff --git a/README.adoc b/README.adoc index 0fe27c0..dde1fe0 100644 --- a/README.adoc +++ b/README.adoc @@ -3,7 +3,7 @@ This plugin was originally designed for use by the OpenAPi v3 Maven Plugin, but works with the command line generator and the Gradle plugin. it generates _excellent_ Dart code and uses Dio. -NOTE: we are currently using 6.4.0 of the OpenAPI Maven Plugin. +NOTE: we are currently using 7.0.1 of the OpenAPI Maven Plugin. == Sponsors diff --git a/pom.xml b/pom.xml index 763f53b..56f28f1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,10 +4,10 @@ openapi-dart-generator jar openapi-dart-generator - 7.3-SNAPSHOT + 8.2-SNAPSHOT - dart2 generator from openapi 3.x spec files. Changing to openapitools dependencies. + dart2 generator from openapi 3.x spec files. https://www.bluetrainsoftware.com @@ -34,14 +34,14 @@ - 6.4.0 + 7.0.1 1.11 1.11 1.8.10 - 3.5 + 3.8 diff --git a/sample-app/SampleRunner/test/api_tests.dart b/sample-app/SampleRunner/test/api_tests.dart index 40a400e..977f413 100644 --- a/sample-app/SampleRunner/test/api_tests.dart +++ b/sample-app/SampleRunner/test/api_tests.dart @@ -23,8 +23,11 @@ main() { print(w); print(x); print("codes ${w.hashCode} vs ${x.hashCode}"); + expect(true, w.hashCode > 0, reason: '${w.hashCode} should be > 0'); print("empty array -> ${[].hashCode}"); - expect(true, w.hashCode == x.hashCode); + expect(true, w.hashCode == x.hashCode, + reason: + 'w hashcode: ${w.hashCode} should equal x hashcode: ${x.hashCode}'); var z = w.copyWith(types: [GeocodedWaypointTypesEnum.ADDRESS]); expect(false, w == z); expect(false, w.hashCode == z.hashCode); @@ -54,12 +57,8 @@ main() { test( 'hashing an object which has two fields of the same type is still different', () { - var ht = HashTest() - ..fieldOne = false - ..fieldTwo = true; - var ht1 = HashTest() - ..fieldOne = true - ..fieldTwo = false; + var ht = HashTest(fieldOne: false, fieldTwo: true); + var ht1 = HashTest(fieldTwo: false, fieldOne: true); expect(false, ht.hashCode == ht1.hashCode); }); @@ -67,6 +66,7 @@ main() { const addProp = { 'discrim': 'fred', 'readings': {'one': 1, 'two': 2.3}, + 'extra': {}, 'dependencies': { 'deps1': ['a', 34.2, true], 'deps2': [17.8, false, 'b'] @@ -97,24 +97,24 @@ main() { var ap = AddProps3.fromJson(addProp); expect(ap.discrim, 'fred'); - expect(ap.readings.length, 2); - expect(ap.readings['one'], 1); - expect(ap.readings['two'], 2.3); - expect(ap.dependencies['deps1'], ['a', 34.2, true]); - expect(ap.dependencies['deps2'], [17.8, false, 'b']); - expect(ap.otherDeps['name'], ['tom', 'dick', 'harry']); - expect(ap.otherDeps['height'], [1.7, 1.3, 1.4]); - expect(ap.otherDeps['info'], 'this is top secret'); - expect(ap.yetMoreAdditional['sList'], ['a', 'b', 'c']); + expect(ap.readings!.length, 2); + expect(ap.readings!['one'], 1); + expect(ap.readings!['two'], 2.3); + expect(ap.dependencies!['deps1'], ['a', 34.2, true]); + expect(ap.dependencies!['deps2'], [17.8, false, 'b']); + expect(ap.otherDeps!['name'], ['tom', 'dick', 'harry']); + expect(ap.otherDeps!['height'], [1.7, 1.3, 1.4]); + expect(ap.otherDeps!['info'], 'this is top secret'); + expect(ap.yetMoreAdditional!['sList'], ['a', 'b', 'c']); expect( - ap.mapWithComplexObject['c1']?[0], - Event() - ..status = EventStatus.STREAMING - ..id = 'xx' - ..title = 'Scully' - ..img = 'img' - ..imageUrl = 'http://blah'); - expect(ap.mapWithEnums['statuses'], + ap.mapWithComplexObject!['c1']?[0], + Event( + status: EventStatus.STREAMING, + id: 'xx', + title: 'Scully', + img: 'img', + imageUrl: 'http://blah')); + expect(ap.mapWithEnums!['statuses'], [EventStatus.STREAMING, EventStatus.CLOSED]); }); @@ -136,6 +136,7 @@ main() { 'basicInt': 2.6, 'basicDouble': 1, 'intList': [1, 2.6], + 'int64Int': 33, 'intMap': {'one': 1, 'two': 2.7}, 'doubleList': [1, 2.6], 'doubleMap': {'one': 1, 'two': 2.7}, @@ -145,16 +146,26 @@ main() { expect(data.basicDouble, 1.0); expect(data.basicInt, 2); expect(data.intList, [1, 2]); + expect(data.int64Int, 33); expect(data.intMap, {'one': 1, 'two': 2}); expect(data.doubleList, [1.0, 2.6]); expect(data.doubleMap, {'one': 1.0, 'two': 2.7}); }); test('data serialisation', () { final data = DoubleAndIntConversion( - basicInt: 43, basicDouble: 26.2, intList: [], doubleMap: {}); + int64Int: 0, + intMap: {}, + basicInt: 43, + basicDouble: 26.2, + intList: [], + doubleMap: {}); expect(data.toJson(), { - 'basicInt': 43, 'basicDouble': 26.2, 'intList': [], - 'doubleList': [], // because we can't tell between null and empty + 'basicInt': 43, + 'int64Int': 0, + 'basicDouble': 26.2, + 'intList': [], + 'intMap': {}, + 'doubleList': [], // because it is not nullable 'doubleMap': {} }); }); diff --git a/src/it/k8s_null/lib/int_or_string.dart b/src/it/k8s_null/lib/int_or_string.dart index bd4bc1d..6034b78 100644 --- a/src/it/k8s_null/lib/int_or_string.dart +++ b/src/it/k8s_null/lib/int_or_string.dart @@ -14,7 +14,7 @@ class IntOrString { return IntOrString()..value = this.value; } - dynamic? toJson() { + dynamic toJson() { return this.value; } diff --git a/src/it/k8s_null/pom.xml b/src/it/k8s_null/pom.xml index 462c87e..f4d61ab 100644 --- a/src/it/k8s_null/pom.xml +++ b/src/it/k8s_null/pom.xml @@ -51,7 +51,7 @@ org.openapitools openapi-generator-maven-plugin - 6.4.0 + 7.0.1 com.bluetrainsoftware.maven diff --git a/src/it/k8s_null/test.yaml b/src/it/k8s_null/test.yaml index 1fe3c5d..2d99a68 100644 --- a/src/it/k8s_null/test.yaml +++ b/src/it/k8s_null/test.yaml @@ -377,6 +377,7 @@ components: int64Int: type: integer format: int64 + default: 0 basicDouble: type: number format: double @@ -390,6 +391,7 @@ components: type: integer doubleList: type: array + default: [] items: type: number format: double diff --git a/src/it/k8s_null/test/api_tests.dart b/src/it/k8s_null/test/api_tests.dart index 2c4e791..4ce0315 100644 --- a/src/it/k8s_null/test/api_tests.dart +++ b/src/it/k8s_null/test/api_tests.dart @@ -23,8 +23,9 @@ main() { print(w); print(x); print("codes ${w.hashCode} vs ${x.hashCode}"); + expect(true, w.hashCode > 0, reason: '${w.hashCode} should be > 0'); print("empty array -> ${[].hashCode}"); - expect(true, w.hashCode == x.hashCode); + expect(true, w.hashCode == x.hashCode, reason: 'w hashcode: ${w.hashCode} should equal x hashcode: ${x.hashCode}'); var z = w.copyWith(types: [GeocodedWaypointTypesEnum.ADDRESS]); expect(false, w == z); expect(false, w.hashCode == z.hashCode); @@ -33,13 +34,29 @@ main() { expect(encodeDecode, w); }); + test('basic inheritance', () { + final wld = WithListDerived.fromJson({ + "list": [ + {"id": 1, "name": "one"}, + {"id": 2, "name": "two"} + ], + "id": 7, + "name": "entity", + "nullableList": [] + }); + + expect(wld.id, 7); + expect(wld.name, "entity"); + expect(wld.list.length, 2); + }); + // previous hashing mechanism if you swapped the adjacent field values // it wouldn't change the hash test( 'hashing an object which has two fields of the same type is still different', () { var ht = HashTest(fieldOne: false, fieldTwo: true); - var ht1 = HashTest(fieldOne: true, fieldTwo: false); + var ht1 = HashTest(fieldTwo: false, fieldOne: true); expect(false, ht.hashCode == ht1.hashCode); }); @@ -47,6 +64,7 @@ main() { const addProp = { 'discrim': 'fred', 'readings': {'one': 1, 'two': 2.3}, + 'extra': {}, 'dependencies': { 'deps1': ['a', 34.2, true], 'deps2': [17.8, false, 'b'] @@ -77,24 +95,24 @@ main() { var ap = AddProps3.fromJson(addProp); expect(ap.discrim, 'fred'); - expect(ap.readings?.length, 2); - expect(ap.readings?['one'], 1); - expect(ap.readings?['two'], 2.3); - expect(ap.dependencies?['deps1'], ['a', 34.2, true]); - expect(ap.dependencies?['deps2'], [17.8, false, 'b']); - expect(ap.otherDeps?['name'], ['tom', 'dick', 'harry']); - expect(ap.otherDeps?['height'], [1.7, 1.3, 1.4]); - expect(ap.otherDeps?['info'], 'this is top secret'); - expect(ap.yetMoreAdditional?['sList'], ['a', 'b', 'c']); + expect(ap.readings!.length, 2); + expect(ap.readings!['one'], 1); + expect(ap.readings!['two'], 2.3); + expect(ap.dependencies!['deps1'], ['a', 34.2, true]); + expect(ap.dependencies!['deps2'], [17.8, false, 'b']); + expect(ap.otherDeps!['name'], ['tom', 'dick', 'harry']); + expect(ap.otherDeps!['height'], [1.7, 1.3, 1.4]); + expect(ap.otherDeps!['info'], 'this is top secret'); + expect(ap.yetMoreAdditional!['sList'], ['a', 'b', 'c']); expect( - ap.mapWithComplexObject?['c1']?[0], + ap.mapWithComplexObject!['c1']?[0], Event( status: EventStatus.STREAMING, id: 'xx', title: 'Scully', img: 'img', imageUrl: 'http://blah')); - expect(ap.mapWithEnums?['statuses'], + expect(ap.mapWithEnums!['statuses'], [EventStatus.STREAMING, EventStatus.CLOSED]); }); @@ -116,6 +134,7 @@ main() { 'basicInt': 2.6, 'basicDouble': 1, 'intList': [1, 2.6], + 'int64Int': 33, 'intMap': {'one': 1, 'two': 2.7}, 'doubleList': [1, 2.6], 'doubleMap': {'one': 1, 'two': 2.7}, @@ -125,35 +144,33 @@ main() { expect(data.basicDouble, 1.0); expect(data.basicInt, 2); expect(data.intList, [1, 2]); + expect(data.int64Int, 33); expect(data.intMap, {'one': 1, 'two': 2}); expect(data.doubleList, [1.0, 2.6]); expect(data.doubleMap, {'one': 1.0, 'two': 2.7}); }); - - test('basic inheritance', () { - final wld = WithListDerived.fromJson({ - "list": [ {"id": 1, "name": "one"}, {"id": 2, "name": "two"} ], - "id": 7, "name": "entity", - "nullableList": [] - }); - - expect(wld.id, 7); - expect(wld.name, "entity"); - expect(wld.list.length, 2); - }); - test('data serialisation', () { final data = DoubleAndIntConversion( - basicInt: 43, basicDouble: 26.2, intList: [], doubleMap: {}); - expect(data.toJson(), - {'basicInt': 43, 'basicDouble': 26.2, 'intList': [], 'doubleMap': {}}); + int64Int: 0, + intMap: {}, + basicInt: 43, + basicDouble: 26.2, + intList: [], + doubleMap: {}); + expect(data.toJson(), { + 'basicInt': 43, + 'int64Int': 0, + 'basicDouble': 26.2, + 'intList': [], + 'intMap': {}, + 'doubleList': [], // because it is not nullable + 'doubleMap': {} + }); }); - test("int enums being generated with correct type", () { expect(IntTypeEnum.number1.toJson(), 1); expect(IntTypeEnum.number1, IntTypeEnumExtension.fromJson(1)); }); - test( "enums included in a model via allOf with reference will be treated as" "enums and generate valid code ", () { @@ -162,7 +179,6 @@ main() { expect(testO.name, "foobar"); expect(testO.enumFieldAllOf, NumericAndSpacedEnum.n667); }); - test("generating 2d array in a correct way", () { const json = { "coordinates": [ @@ -172,10 +188,10 @@ main() { }; final geometry = PointGeometry.fromJson(json); expect(geometry.coordinates, hasLength(2)); - final firstPair = geometry.coordinates?.first; + final firstPair = geometry.coordinates.first; expect(firstPair, hasLength(2)); - expect(firstPair?[0], -27.6307582); - expect(firstPair?[1], 153.0401564); + expect(firstPair[0], -27.6307582); + expect(firstPair[1], 153.0401564); }); } diff --git a/src/main/kotlin/com/bluetrainsoftware/openapi/DartV3ApiGenerator.kt b/src/main/kotlin/com/bluetrainsoftware/openapi/DartV3ApiGenerator.kt index 86b68f8..bfa4b34 100644 --- a/src/main/kotlin/com/bluetrainsoftware/openapi/DartV3ApiGenerator.kt +++ b/src/main/kotlin/com/bluetrainsoftware/openapi/DartV3ApiGenerator.kt @@ -30,18 +30,15 @@ import java.util.regex.Pattern import java.util.stream.Collectors class DartV3ApiGenerator : DartClientCodegen() { - private var arraysThatHaveADefaultAreNullSafe = false - private var isNullSafeEnabled = false - private var extraAnyOfClasses: MutableMap = HashMap() private var extraAnyParts: MutableSet = HashSet() private var listAnyOf = false - private var use5xStyleNullable = false + private var filterDeprecatedApis = false + private var filterDeprecatedModels = false companion object { private const val LIBRARY_NAME = "dart2-api" private const val DART2_TEMPLATE_FOLDER = "dart2-v3template" - private const val ARRAYS_WITH_DEFAULT_VALUES_ARE_NULLSAFE = "nullSafe-array-default" private const val LIST_ANY_OF = "listAnyOf" private val log = LoggerFactory.getLogger(DartV3ApiGenerator::class.java) @@ -97,11 +94,10 @@ class DartV3ApiGenerator : DartClientCodegen() { supportingFiles.add(SupportingFile("apilib.mustache", libFolder, "api.dart")) additionalProperties["x-internal-enums"] = extraInternalEnumProperties additionalProperties["x-external-formatters"] = extraPropertiesThatUseExternalFormatters - arraysThatHaveADefaultAreNullSafe = additionalProperties.containsKey(ARRAYS_WITH_DEFAULT_VALUES_ARE_NULLSAFE) - isNullSafeEnabled = additionalProperties.containsKey("nullSafe") additionalProperties["x-dart-anyparts"] = extraAnyParts + filterDeprecatedApis = additionalProperties["filter-deprecated-apis"] != null + filterDeprecatedModels = additionalProperties["filter-deprecated-models"] != null listAnyOf = additionalProperties.containsKey(LIST_ANY_OF) - use5xStyleNullable = additionalProperties.containsKey("x-use-5x-nullable") } /** @@ -144,7 +140,7 @@ class DartV3ApiGenerator : DartClientCodegen() { } } - // don't just add stuff to the end of the name willy nilly, check it is a reserved word first + // don't just add stuff to the end of the name willy-nilly, check it is a reserved word first override fun escapeReservedWord(name: String): String { return if (isReservedWord(name)) name + "_" else name } @@ -175,7 +171,7 @@ class DartV3ApiGenerator : DartClientCodegen() { return name } - override fun toModelFilename(name: String): String? { + override fun toModelFilename(name: String): String { return if (name.contains(".")) { val lastIndex = name.lastIndexOf(".") val path = name.substring(0, lastIndex).replace(".", File.separator).replace('-', '_') @@ -188,12 +184,12 @@ class DartV3ApiGenerator : DartClientCodegen() { // use this one on the INSIDE of a List - where X is the inside type passed here private fun propertyListOrMapSuffix(cp: CodegenProperty): String { - return if (cp.isNullable && isNullSafeEnabled && "dynamic" != cp.dataType) "?" else "" + return if (cp.isNullable && "dynamic" != cp.dataType) "?" else "" } // use this one on the OUTSIDE of a List? where the outside type is passed here (not X) private fun listOrMapSuffix(cp: CodegenProperty): String { - return if (nullable(cp) && isNullSafeEnabled && "dynamic" != cp.dataType) "?" else "" + return if (cp.isNullable && "dynamic" != cp.dataType) "?" else "" } private fun correctPropertyForBinary(model: CodegenModel, cp: CodegenProperty, classLevelField: Boolean): Boolean { @@ -217,12 +213,6 @@ class DartV3ApiGenerator : DartClientCodegen() { return cp.isNullable } - // in arraysThatHaveADefaultAreNullSafe we force arrays to be non-null if they - // are optional - this does not affect required/optional serialisation - if ((cp.isArray || cp.isMap) && arraysThatHaveADefaultAreNullSafe) { - return false - } - return !cp.required || cp.isNullable } @@ -258,7 +248,7 @@ class DartV3ApiGenerator : DartClientCodegen() { // if the var is binary, fix it to be a MultipartFile if (correctPropertyForBinary(model, cp, classLevelField)) { - cp.vendorExtensions.put("x-var-is-binary", "true") + cp.vendorExtensions["x-var-is-binary"] = "true" } val isComposedEnum = if (cp.isModel) { @@ -273,13 +263,13 @@ class DartV3ApiGenerator : DartClientCodegen() { cp.enumName = model.classname + cp.nameInCamelCase + "Enum" if (cp.isArray) { cp.dataType = "List<" + cp.enumName + propertyListOrMapSuffix(cp.items) + ">" + listOrMapSuffix(cp) - if (isNullSafeEnabled && nullable(cp) && cp.items != null) { + if (cp.isNullable && cp.items != null) { cp.items.vendorExtensions["x-list-null"] = "true" } } else if (cp.isMap) { cp.dataType = "Map" + listOrMapSuffix(cp) } else { - cp.dataType = cp.enumName + if (isNullSafeEnabled) (if (nullable(cp)) "?" else "") else "" + cp.dataType = cp.enumName + propertyListOrMapSuffix(cp) if (cp.defaultValue != null) { if (cp.defaultValue.startsWith("'") && cp.defaultValue.endsWith("'")) { cp.defaultValue = cp.defaultValue.substring(1, cp.defaultValue.length - 1) @@ -294,9 +284,7 @@ class DartV3ApiGenerator : DartClientCodegen() { } } else { cp.enumName = cp.complexType - if (isNullSafeEnabled) { - cp.dataType = cp.enumName + if (nullable(cp)) "?" else "" - } + cp.dataType = cp.enumName + if (cp.isNullable) "?" else "" } cp.datatypeWithEnum = cp.dataType cp.isEnum = true @@ -313,11 +301,11 @@ class DartV3ApiGenerator : DartClientCodegen() { val inner = nullGenChild(cp.items) cp.dataType = "List<" + inner + ">" + listOrMapSuffix(cp) - if (isNullSafeEnabled && nullable(cp) && cp.items != null) { + if (cp.isNullable && cp.items != null) { cp.items.vendorExtensions["x-list-null"] = "true" } - } else if (nullable(cp) && isNullSafeEnabled && "dynamic" != cp.dataType) { - cp.dataType = cp.dataType + "?" + } else { + cp.dataType = cp.dataType + propertyListOrMapSuffix(cp) } cp.vendorExtensions["x-innerType"] = cp.dataType } @@ -333,33 +321,13 @@ class DartV3ApiGenerator : DartClientCodegen() { } } - if ((cp.defaultValue == "[]" && cp.isArray) || (cp.defaultValue == "{}" && cp.isMap)) { - cp.defaultValue = "const " + cp.defaultValue - } - - // now allow arrays to be non nullable by making them empty. Only operates on 1st level because - // it affects the constructor and defaults of top level fields - if (classLevelField && cp.isArray && arraysThatHaveADefaultAreNullSafe && cp.defaultValue != null) { - cp.defaultValue = "const []" - cp.vendorExtensions["x-ns-default-val"] = java.lang.Boolean.TRUE - if (cp.items != null) { // it should be not null, its an array - cp.items.vendorExtensions["x-ns-default-val"] = java.lang.Boolean.TRUE - } - var props: MutableList? // type-unsafe upstream api - = model.vendorExtensions["x-ns-default-vals"] as MutableList? - - if (props == null) { - props = mutableListOf() - model.vendorExtensions["x-ns-default-vals"] = props - model.vendorExtensions["x-has-ns-default-vals"] = java.lang.Boolean.TRUE - } + cp.vendorExtensions["original-default"] = cp.defaultValue - props.add(cp) + if (cp.required || (cp.defaultValue == null && !cp.isNullable)) { + cp.required = true } - if ((cp.required || cp.defaultValue == null) && !nullable(cp)) { - cp.vendorExtensions["x-dart-required"] = "true" - } + cp.vendorExtensions["required-owned"] = (cp.required && !cp.isInherited) } // detect whether the schema follows the pattern: @@ -387,12 +355,14 @@ class DartV3ApiGenerator : DartClientCodegen() { val extractLastPathComponent = Pattern.compile("#/components/schemas/([a-zA-Z\\d.\\-_]+)") .matcher(singleRef) if (extractLastPathComponent.matches()) { - val referencedEnum = openAPI.components.schemas[extractLastPathComponent.group(1)]!!.enum - if (referencedEnum != null && referencedEnum.isNotEmpty()) { - isComposedEnum = true - cp.isModel = false - cp.isEnum = true - cp.isAnyType = false + openAPI.components.schemas[extractLastPathComponent.group(1)]?.let { schema -> + val referencedEnum = schema.enum + if (referencedEnum != null && referencedEnum.isNotEmpty()) { + isComposedEnum = true + cp.isModel = false + cp.isEnum = true + cp.isAnyType = false + } } } } @@ -409,20 +379,17 @@ class DartV3ApiGenerator : DartClientCodegen() { } private fun nullGenChild(cp: CodegenProperty): String { - val `val`: String - if (cp.isMap) { - `val` = "Map" + listOrMapSuffix(cp) + val value: String = if (cp.isMap) { + "Map" + listOrMapSuffix(cp) } else if (cp.isArray) { - `val` = - "List<" + nullGenChild(cp.items) + ">" + if (isNullSafeEnabled) (if (!nullable(cp) || arraysThatHaveADefaultAreNullSafe) "" else "?") else "" - if (!isNullSafeEnabled) { - cp.vendorExtensions["x-list-null"] = java.lang.Boolean.TRUE - } + "List<" + nullGenChild(cp.items) + ">" + listOrMapSuffix(cp) } else { - `val` = cp.dataType + propertyListOrMapSuffix(cp) + cp.dataType + propertyListOrMapSuffix(cp) } - cp.vendorExtensions["x-innerType"] = `val` - return `val` + + cp.vendorExtensions["x-innerType"] = value + + return value } // this allows us to keep track of files to remove at the end @@ -447,41 +414,24 @@ class DartV3ApiGenerator : DartClientCodegen() { originalModels.values.forEach { modelData: ModelsMap -> if (modelData.models != null) { - updateModelMaps(modelData.models, allModels) + determineInterestingModels(modelData.models, allModels) } } - + // we need this one first updateModelsWithAllOfInheritance(allModels) + // this requires knowledge of inheritance + correctInternalsOfModels(allModels) + filterNullableArraysByNonInheritedProperties(allModels) return originalModels } - private fun updateModelMaps(models: List, allModels: MutableMap) { - models.forEach { modelMap: ModelMap -> - val cm = modelMap.model ?: return@forEach - - // there is no way way to detect these classes other than by name - if (cm.name.endsWith("_allOf") || cm.vendorExtensions.containsKey("x-skip-generation")) { - return@forEach // skip this one, it isn't used at all - } - - // if NOT an inline object we will definitely use it. Only BODY based inline - // objects will be otherwise used. - if (!cm.getName().startsWith("inline_object") && cm.classFilename != null) { - cm.vendorExtensions["isUsedModel"] = "true" - modelMap["isUsedModel"] = "true" - modelFilesNotToDeleted.add(cm.classFilename + ".dart") - } - - if (!cm.getName().endsWith("_allOf")) { - allModels[cm.getName()] = cm - } - + private fun correctInternalsOfModels(allModels: MutableMap) { + allModels.values.forEach { cm -> cm.vendorExtensions["dartClassName"] = StringUtils.camelize(cm.getClassname()) - if (cm.vars != null) { - val optionalArrayOrMaps = mutableListOf() + val arraysWithDefaults = mutableListOf() cm.vars.forEach { cp: CodegenProperty -> var correctingSettings: CodegenProperty? = cp @@ -510,28 +460,54 @@ class DartV3ApiGenerator : DartClientCodegen() { correctingSettings = correctingSettings.items } - if ((cp.isArray || cp.isMap) && !cp.required && varIsOwnedByThisClass(cp)) { - optionalArrayOrMaps.add(cp) + if ((cp.isArray || cp.isMap) && !cp.required && !cp.isInherited && cp.defaultValue != null) { + arraysWithDefaults.add(cp) } } - if (optionalArrayOrMaps.isNotEmpty()) { - cm.vendorExtensions["x-opt-arrays"] = optionalArrayOrMaps - cm.vendorExtensions["x-has-opt-arrays"] = "true" + if (arraysWithDefaults.isNotEmpty()) { + cm.vendorExtensions["x-nullable-arrays-with-defaults"] = arraysWithDefaults + cm.vendorExtensions["x-has-nullable-arrays-with-defaults"] = "true" } } } } + private fun determineInterestingModels(models: List, allModels: MutableMap) { + models.forEach { modelMap: ModelMap -> + val cm = modelMap.model ?: return@forEach + + // there is no way to detect these classes other than by name + if (cm.name.endsWith("_allOf") || cm.vendorExtensions.containsKey("x-skip-generation")) { + return@forEach // skip this one, it isn't used at all + } + + // if NOT an inline object we will definitely use it. Only BODY based inline + // objects will be otherwise used. + if (!cm.getName().startsWith("inline_object") && cm.classFilename != null) { + cm.vendorExtensions["isUsedModel"] = "true" + modelMap["isUsedModel"] = "true" + modelFilesNotToDeleted.add(cm.classFilename + ".dart") + } + + if (!cm.name.endsWith("_allOf")) { + allModels[cm.getName()] = cm + } + } + } + private fun updateModelsWithAllOfInheritance(models: Map) { // Workaround for https://github.com/OpenAPITools/openapi-generator/issues/11846 for (cm in models.values) { - var parent: CodegenModel? = cm.parentModel + if (cm.name == "Entity2") { + println("blah") + } - val properties = cm.vars.map { obj: CodegenProperty -> obj.getName()!! }.toMutableSet() + var parent = cm.parentModel - while (parent != null) { + val properties = cm.vars.map { it.name }.toMutableSet() + while (parent != null) { for (cp in parent.vars) { if (!properties.contains(cp.getName())) { properties.add(cp.getName()) @@ -545,7 +521,7 @@ class DartV3ApiGenerator : DartClientCodegen() { } for (cm in models.values) { - if (cm.getParent() != null) { + if (cm.parent != null) { val parent = models[cm.getParent()] if (parent == null) { log.info("Cannot find parent for class {}:{}", cm.getName(), cm.getParent()) @@ -556,56 +532,65 @@ class DartV3ApiGenerator : DartClientCodegen() { cm.vars.forEach { property -> val matchingName = props[property.getName()] if (matchingName != null) { - property.vendorExtensions["x-dart-inherited"] = java.lang.Boolean.TRUE + property.vendorExtensions["x-dart-inherited"] = true + property.isInherited = true } } val cmp = Comparator.comparing { cp: CodegenProperty -> - cp.vendorExtensions.containsKey( - "x-dart-inherited" - ) + cp.isInherited } cm.vars.sortWith(cmp.reversed()) } + } + } - val ownVars = cm.vars.filter(::varIsOwnedByThisClass) + /** + * This removes any inherited properties from the nullable arrays + */ + private fun filterNullableArraysByNonInheritedProperties(models: Map) { + for (cm in models.values) { + val ownVars = cm.vars.filter { v -> !v.isInherited } cm.vendorExtensions["x-dart-ownVars"] = ownVars cm.vendorExtensions["x-dart-hasOwnVars"] = ownVars.isNotEmpty() - if (cm.vendorExtensions.containsKey("x-has-opt-arrays")) { - val optArrays = (cm.vendorExtensions["x-opt-arrays"] as List) + if (cm.vendorExtensions.containsKey("x-has-nullable-arrays-with-defaults")) { + val optArrays = (cm.vendorExtensions["x-nullable-arrays-with-defaults"] as List) .filter { cp -> ownVars.find { ov -> cp.name == ov.name } != null } .toList() if (optArrays.isEmpty()) { // none left - cm.vendorExtensions.remove("x-has-opt-arrays") - cm.vendorExtensions.remove("x-opt-arrays") + cm.vendorExtensions.remove("x-has-nullable-arrays-with-defaults") + cm.vendorExtensions.remove("x-nullable-arrays-with-defaults") } else { - cm.vendorExtensions["x-opt-arrays"] = optArrays + cm.vendorExtensions["x-nullable-arrays-with-defaults"] = optArrays } } } } - private fun varIsOwnedByThisClass(cp: CodegenProperty): Boolean { - return !cp.vendorExtensions.containsKey( - "x-dart-inherited" - ) - } - // for debugging inevitable weirdness in the operations generated. DO NOT MODIFY THE MODEL - it has already been // generated to the file // system - override fun postProcessOperationsWithModels(operationsMap: OperationsMap, models: List): OperationsMap? { + override fun postProcessOperationsWithModels(operationsMap: OperationsMap, models: MutableList): OperationsMap? { processImportMappings() processPubspecMappings() val som = super.postProcessOperationsWithModels(operationsMap, models) + val ops = operationsMap.operations.operation + if (filterDeprecatedApis) { + ops.removeIf { it.isDeprecated } + } + + if (filterDeprecatedModels) { + models.removeIf { it.model.isDeprecated } + } + // at this point, all the model files have actually already been generated to disk, that horse has bolted. What // we need to do is figured out which models are "form" based and are not required. Basically anything that is - // directly used by by a form post + // directly used by a form post val modelMap: MutableMap = HashMap() val modelMetaMap: MutableMap = HashMap() models.forEach(Consumer { m: ModelMap -> @@ -631,6 +616,12 @@ class DartV3ApiGenerator : DartClientCodegen() { } } + co.formParams.forEach { p -> + if (!p.required) { + p.isNullable = true // because it is by default, it has to be + } + } + co.allParams.forEach(Consumer { p: CodegenParameter -> if (p.isFile || p.isBinary || p.isBodyParam && bodyIsFile) { if (p.isArray) { @@ -640,6 +631,7 @@ class DartV3ApiGenerator : DartClientCodegen() { } p.baseType = p.dataType } + }) } return som @@ -648,9 +640,9 @@ class DartV3ApiGenerator : DartClientCodegen() { override fun toDefaultValue(schema: Schema<*>): String? { // default inherited one uses const, and we can't use that anymore as they are inmodifiable return if (ModelUtils.isMapSchema(schema)) { - "{}" + schema.default?.toString() } else if (ModelUtils.isArraySchema(schema)) { - "[]" + schema.default?.toString() } else if (schema.default != null) { val s = if (ModelUtils.isStringSchema(schema)) "'" + schema.default.toString() .replace("'", "\\'") + "'" else schema.default.toString() @@ -662,7 +654,7 @@ class DartV3ApiGenerator : DartClientCodegen() { override fun addAdditionPropertiesToCodeGenModel(codegenModel: CodegenModel, schema: Schema<*>?) { //super.addAdditionPropertiesToCodeGenModel(codegenModel, schema); - codegenModel.additionalPropertiesType = getSchemaType(ModelUtils.getAdditionalProperties(openAPI, schema)) + codegenModel.additionalPropertiesType = getSchemaType(ModelUtils.getAdditionalProperties(schema)) addImport(codegenModel, codegenModel.additionalPropertiesType) } @@ -783,23 +775,22 @@ class DartV3ApiGenerator : DartClientCodegen() { } } - override fun toAnyOfName(originalNames: List, composedSchema: ComposedSchema): String? { + override fun toAnyOfName(originalNames: MutableList?, composedSchema: Schema<*>?): String { val names = ArrayList(originalNames) if (listAnyOf) { // create models for List classes that have discriminator - if (composedSchema.discriminator != null) { + if (composedSchema?.discriminator != null) { names.sort() - val namesFilename = names.stream().map { name: String -> - toModelFilename( - name - ) - }.collect(Collectors.toList()) - val namesCapitalized = names.stream().map { str: String? -> + val namesFilename = names.map { name -> + toModelFilename(name) + }.toList() + + val namesCapitalized = names.map { str: String? -> org.apache.commons.lang3.StringUtils.capitalize( str ) - }.collect(Collectors.toList()) + } val className = "AnyOf" + java.lang.String.join("", namesCapitalized) val enumClassName = "AnyOfDiscriminator" + java.lang.String.join("", namesCapitalized) val fileName = "any_of_" + java.lang.String.join("_", namesFilename) @@ -820,9 +811,9 @@ class DartV3ApiGenerator : DartClientCodegen() { val fileName: String, private val className: String, private val enumClassName: String, - private val composedSchema: ComposedSchema + private val composedSchema: Schema<*>? ) { - private val discriminatorProperty: Discriminator? = composedSchema.discriminator + private val discriminatorProperty: Discriminator? = composedSchema?.discriminator init { assert(discriminatorProperty != null) @@ -855,15 +846,15 @@ class DartV3ApiGenerator : DartClientCodegen() { val model = ModelMap() model.model = toCodegenModel() // model.put("isUsedModel", "false") - model.put("importPath", "model.${className}") + model["importPath"] = "model.${className}" data["models"] = mutableListOf(model) - composedSchema.anyOf.forEach(Consumer { schema: Schema<*> -> + composedSchema?.anyOf?.forEach(Consumer { schema: Schema<*> -> val ref = schema.`$ref` val type = generator.getTypeDeclaration(schema) collectedTypes.add(type) var discriminatorValue = ModelUtils.getSimpleRef(ref) - // lookup discriminator mappings if there any + // lookup discriminator mappings if there are any if (discriminatorProperty.mapping != null) { for ((key, value) in discriminatorProperty.mapping) { if (value == ref) { diff --git a/src/main/resources/dart2-v3template/_any_of_class.mustache b/src/main/resources/dart2-v3template/_any_of_class.mustache index b9a5176..53670de 100644 --- a/src/main/resources/dart2-v3template/_any_of_class.mustache +++ b/src/main/resources/dart2-v3template/_any_of_class.mustache @@ -1,7 +1,7 @@ class {{classname}} { {{classname}}(); - {{#nullSafe}}late {{/nullSafe}}dynamic _value; + late dynamic _value; dynamic get value => _value; void set value(dynamic v) { {{#innerTypes}} @@ -14,7 +14,7 @@ class {{classname}} { throw ArgumentError("${v.runtimeType} cannot be a value of AnyOf<{{anyOfTemplate}}>"); } - {{#nullSafe}}late {{/nullSafe}}{{enumClassname}} _discriminator; + late {{enumClassname}} _discriminator; {{enumClassname}} get discriminator => _discriminator; @override @@ -22,7 +22,7 @@ class {{classname}} { return 'AnyOf<{{anyOfTemplate}}>[value=$_value]'; } - fromJson(Map{{#nullSafe}}?{{/nullSafe}} json) { + fromJson(Map? json) { if (json == null) return; final discriminatorStr = json[r'{{discriminatorProperty}}']; @@ -41,7 +41,7 @@ class {{classname}} { } } - {{classname}}.fromJson(Map{{#nullSafe}}?{{/nullSafe}} json) { + {{classname}}.fromJson(Map? json) { fromJson(json); // allows child classes to call } @@ -68,12 +68,7 @@ class {{classname}} { hashCode = hashCode * 31 + _value.hashCode; } - {{#nullSafe}}hashCode = hashCode * 31 + _discriminator.hashCode;{{/nullSafe}} - {{^nullSafe}} - if (_discriminator != null) { - hashCode = hashCode * 31 + _discriminator.hashCode; - } - {{/nullSafe}} + hashCode = hashCode * 31 + _discriminator.hashCode; return hashCode; } @@ -87,7 +82,7 @@ class {{classname}} { } {{/disableCopyWith}} - static List<{{classname}}> listFromJson(List{{#nullSafe}}?{{/nullSafe}} json) { + static List<{{classname}}> listFromJson(List? json) { return json == null ? <{{classname}}>[] : json.map((value) => {{classname}}.fromJson(value)).toList(); } diff --git a/src/main/resources/dart2-v3template/_array_copy.mustache b/src/main/resources/dart2-v3template/_array_copy.mustache index bdb85b7..f623038 100644 --- a/src/main/resources/dart2-v3template/_array_copy.mustache +++ b/src/main/resources/dart2-v3template/_array_copy.mustache @@ -1,18 +1,19 @@ +/* nullable: {{isNullable}}, vend: {{vendorExtentions}} */ +data{{#isNullable}}?{{/isNullable}}. {{#items}} - data{{#vendorExtensions.x-list-null}}?{{/vendorExtensions.x-list-null}}. {{#isArray}} map((data) { return (({{{dataType}}} data) { - return {{>_copy}};})(data); }){{^nullSafe}}{{#vendorExtensions.x-list-null}}?{{/vendorExtensions.x-list-null}}{{/nullSafe}}.toList() + return {{>_copy}};})(data); }){{#isNullable}}?{{/isNullable}}.toList() {{/isArray}} {{#isMap}} - map((data) { ((data) { return {{>_copy}};})(data); }).toList() + map((data) { ((data) { return {{>_copy}};})(data); }){{#isNullable}}?{{/isNullable}}.toList() {{/isMap}} {{^items}} {{^isModel}} toList() {{/isModel}} {{#isModel}} - map((data) => {{>_copy}}).toList() + map((data) => {{>_copy}}){{#isNullable}}?{{/isNullable}}.toList() {{/isModel}} {{/items}} {{/items}} diff --git a/src/main/resources/dart2-v3template/_array_from_json.mustache b/src/main/resources/dart2-v3template/_array_from_json.mustache index 32e43c9..b760339 100644 --- a/src/main/resources/dart2-v3template/_array_from_json.mustache +++ b/src/main/resources/dart2-v3template/_array_from_json.mustache @@ -1,9 +1,9 @@ {{#items}} {{#isArray}} - (data as List){{#nullSafe}}.fromNull(){{/nullSafe}}.map((data) { return ((dynamic data) { return {{>_from_json}};})(data); }).toList() + (data as List).fromNull().map((data) { return ((dynamic data) { return {{>_from_json}};})(data); }).toList() {{/isArray}} {{#isMap}} - (data as List){{#nullSafe}}.fromNull(){{/nullSafe}}.map((data) { ((dynamic data) { return {{>_from_json}};})(data); }).toList() + (data as List).fromNull().map((data) { ((dynamic data) { return {{>_from_json}};})(data); }).toList() {{/isMap}} {{^items}} {{#isEnum}} @@ -26,12 +26,7 @@ ).toList() {{/isNumeric}} {{^isNumeric}} - {{^nullSafe}} - (data as List)?.cast<{{{dataType}}}>() - {{/nullSafe}} - {{#nullSafe}} - (data as List{{#x-dart-nullable}}?{{/x-dart-nullable}}){{#x-dart-nullable}}?{{/x-dart-nullable}}.cast<{{{dataType}}}>(){{#x-dart-nullable}} ?? <{{{dataType}}}>[]{{/x-dart-nullable}} - {{/nullSafe}} + (data as List{{#x-dart-nullable}}?{{/x-dart-nullable}}){{#x-dart-nullable}}?{{/x-dart-nullable}}.cast<{{{dataType}}}>(){{#x-dart-nullable}} ?? <{{{dataType}}}>[]{{/x-dart-nullable}} {{/isNumeric}} {{/isPrimitiveType}} {{#isAnyType}}data{{/isAnyType}} diff --git a/src/main/resources/dart2-v3template/_class_to_json.mustache b/src/main/resources/dart2-v3template/_class_to_json.mustache index ed87c7b..acbf2fc 100644 --- a/src/main/resources/dart2-v3template/_class_to_json.mustache +++ b/src/main/resources/dart2-v3template/_class_to_json.mustache @@ -1,69 +1,50 @@ final json = {{#parent}}super.toJson();{{/parent}}{{^parent}}{};{{/parent}} {{#vars}} {{^vendorExtensions.x-var-is-binary}} - {{^vendorExtensions.x-dart-inherited}} - {{#isArray}} - {{^required}} - {{#vendorExtensions.x-ns-default-val}} - if ({{{name}}}{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.isNotEmpty{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}} == true{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}) { - {{/vendorExtensions.x-ns-default-val}} - {{/required}} - json[r'{{baseName}}'] = ((v) => {{> _to_json_serialise }} )({{{name}}}); - {{^required}} - {{#vendorExtensions.x-ns-default-val}} - }{{> __class_to_json_required_null }} - {{/vendorExtensions.x-ns-default-val}} - {{/required}} - {{/isArray}} - {{#isMap}} - {{#vendorExtensions.x-dart-nullable}}if ({{{name}}}{{#nullSafe}}?{{/nullSafe}}.isNotEmpty{{#nullSafe}} == true{{/nullSafe}}) { {{/vendorExtensions.x-dart-nullable}} - {{^required}}{{^vendorExtensions.x-dart-nullable}}if ({{{name}}}.isNotEmpty) { {{/vendorExtensions.x-dart-nullable}}{{/required}} - json[r'{{baseName}}'] = ((v) => {{> _to_json_serialise }} )({{{name}}}); - {{#vendorExtensions.x-dart-nullable}} - }{{> __class_to_json_required_null }} - {{/vendorExtensions.x-dart-nullable}} - {{^required}}{{^vendorExtensions.x-dart-nullable}} - }{{> __class_to_json_required_null }} - {{/vendorExtensions.x-dart-nullable}}{{/required}} - {{/isMap}} - {{^items}} - {{^required}} - {{#vendorExtensions.x-dart-nullable}} - {{^generateNullValuesToJson}} + {{^isInherited}} + {{#isNullable}} + {{^required}}{{! don't send it if it isn't required and its null}} if ({{{name}}} != null) { - {{/generateNullValuesToJson}} - {{/vendorExtensions.x-dart-nullable}} - {{/required}} - json[r'{{baseName}}'] = - {{#isModel}} - {{{name}}}{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toJson() - {{/isModel}} - {{#isPrimitiveType}} - {{{name}}} - {{/isPrimitiveType}} - {{#isEnum}} - {{{name}}}{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toJson() - {{/isEnum}} - {{#isDate}} - {{{name}}}{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toUtc() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.toIso8601String() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.substring(0, 10) - {{/isDate}} - {{#isDateTime}} - {{{name}}}{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toUtc() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.toIso8601String() - {{/isDateTime}} - {{#isAnyType}}{{{name}}}{{/isAnyType}} - ; - {{^required}} - {{#vendorExtensions.x-dart-nullable}} - {{^generateNullValuesToJson}} - } - {{/generateNullValuesToJson}} - {{/vendorExtensions.x-dart-nullable}} - {{/required}} - {{/items}} - {{/vendorExtensions.x-dart-inherited}} + {{/required}} + {{#required}} + if ({{{name}}} == null) { + json[r'{{baseName}}'] = null; + } else { + {{/required}} + {{/isNullable}} + {{#isArray}} + json[r'{{baseName}}'] = ((v) => {{> _to_json_serialise }} )({{{name}}}); + {{/isArray}} + {{#isMap}} + json[r'{{baseName}}'] = ((v) => {{> _to_json_serialise }} )({{{name}}}); + {{/isMap}} + {{^items}} + json[r'{{baseName}}'] = + {{#isModel}} + {{{name}}}{{#isNullable}}?{{/isNullable}}.toJson() + {{/isModel}} + {{#isPrimitiveType}} + {{{name}}} + {{/isPrimitiveType}} + {{#isEnum}} + {{{name}}}{{#isNullable}}?{{/isNullable}}.toJson() + {{/isEnum}} + {{#isDate}} + {{{name}}}{{#isNullable}}?{{/isNullable}}.toUtc() + .toIso8601String() + .substring(0, 10) + {{/isDate}} + {{#isDateTime}} + {{{name}}}{{#isNullable}}?{{/isNullable}}.toUtc() + .toIso8601String() + {{/isDateTime}} + {{#isAnyType}}{{{name}}}{{/isAnyType}} + ; + {{/items}} + {{#isNullable}} + } + {{/isNullable}} + {{/isInherited}} {{/vendorExtensions.x-var-is-binary}} {{/vars}} return json; diff --git a/src/main/resources/dart2-v3template/_complex_copy.mustache b/src/main/resources/dart2-v3template/_complex_copy.mustache index b747519..7a33fc0 100644 --- a/src/main/resources/dart2-v3template/_complex_copy.mustache +++ b/src/main/resources/dart2-v3template/_complex_copy.mustache @@ -1 +1 @@ -((this.{{{name}}} == null) ? {{#defaultValue}}{{{defaultValue}}}{{#nullSafe}} as {{{dataType}}}{{/nullSafe}}{{^nullSafe}}{{^isAnyType}} as {{{dataType}}}{{/isAnyType}}{{/nullSafe}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} : (({{{dataType}}} data) { return {{>_copy}}; }(this.{{{name}}}))); +((this.{{{name}}} == null) ? {{#defaultValue}}{{{defaultValue}}} as {{{dataType}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} : (({{{dataType}}} data) { return {{>_copy}}; }(this.{{{name}}}))); diff --git a/src/main/resources/dart2-v3template/_copy.mustache b/src/main/resources/dart2-v3template/_copy.mustache index 6564f60..01b0acd 100644 --- a/src/main/resources/dart2-v3template/_copy.mustache +++ b/src/main/resources/dart2-v3template/_copy.mustache @@ -9,8 +9,8 @@ {{#isDateTime}}{{>_copy_date}}{{/isDateTime}} {{#isDate}}{{>_copy_date}}{{/isDate}} {{#isModel}} - {{^vendorExtensions.x-dart-nullable}}data.copyWith(){{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}}data == null ? null : data.copyWith(){{/vendorExtensions.x-dart-nullable}} + {{^isNullable}}data.copyWith(){{/isNullable}} + {{#isNullable}}data == null ? null : data.copyWith(){{/isNullable}} {{/isModel}} {{#isPrimitiveType}}data{{/isPrimitiveType}} {{#isAnyType}}data{{/isAnyType}} diff --git a/src/main/resources/dart2-v3template/_copy_date.mustache b/src/main/resources/dart2-v3template/_copy_date.mustache index 1f3d716..aa5f61e 100644 --- a/src/main/resources/dart2-v3template/_copy_date.mustache +++ b/src/main/resources/dart2-v3template/_copy_date.mustache @@ -1,2 +1,2 @@ -{{^vendorExtensions.x-dart-nullable}}data.add(Duration()){{/vendorExtensions.x-dart-nullable}} -{{#vendorExtensions.x-dart-nullable}}data == null ? null : data.add(Duration()){{/vendorExtensions.x-dart-nullable}} +{{^isNullable}}data.add(Duration()){{/isNullable}} +{{#isNullable}}data == null ? null : data.add(Duration()){{/isNullable}} diff --git a/src/main/resources/dart2-v3template/_from_json.mustache b/src/main/resources/dart2-v3template/_from_json.mustache index 2ca1273..c93a5b3 100644 --- a/src/main/resources/dart2-v3template/_from_json.mustache +++ b/src/main/resources/dart2-v3template/_from_json.mustache @@ -9,40 +9,28 @@ {{enumName}}Extension.fromJson(data) {{/isEnum}} {{#isDateTime}} - {{^vendorExtensions.x-dart-nullable}}{{#nullSafe}} + {{^isNullable}} DateTime.parse(data!) - {{/nullSafe}} - {{^nullSafe}} + {{/isNullable}} + {{#isNullable}} data == null ? null : DateTime.parse(data) - {{/nullSafe}} - {{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}} - data == null ? null : DateTime.parse(data) - {{/vendorExtensions.x-dart-nullable}} + {{/isNullable}} {{/isDateTime}} {{#isDate}} - {{^vendorExtensions.x-dart-nullable}}{{#nullSafe}} + {{^isNullable}} DateTime.parse(data! + 'T00:00:00.000Z') - {{/nullSafe}} - {{^nullSafe}} - data == null ? null : DateTime.parse(data + 'T00:00:00.000Z') - {{/nullSafe}} - {{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}} + {{/isNullable}} + {{#isNullable}} data == null ? null : DateTime.parse(data + 'T00:00:00.000Z') - {{/vendorExtensions.x-dart-nullable}} + {{/isNullable}} {{/isDate}} {{#isModel}} - {{^vendorExtensions.x-dart-nullable}}{{#nullSafe}} + {{^isNullable}} {{{complexType}}}.fromJson(data) - {{/nullSafe}} - {{^nullSafe}} - data == null ? {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} : {{{complexType}}}.fromJson(data) - {{/nullSafe}} - {{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}} + {{/isNullable}} + {{#isNullable}} data == null ? {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} : {{{complexType}}}.fromJson(data) - {{/vendorExtensions.x-dart-nullable}} + {{/isNullable}} {{/isModel}} {{#isPrimitiveType}} {{#isInteger}}data.toInt(){{/isInteger}} diff --git a/src/main/resources/dart2-v3template/_map_copy.mustache b/src/main/resources/dart2-v3template/_map_copy.mustache index b6dc0ac..38368d1 100644 --- a/src/main/resources/dart2-v3template/_map_copy.mustache +++ b/src/main/resources/dart2-v3template/_map_copy.mustache @@ -1,4 +1,4 @@ -data{{#nullSafe}}{{#vendorExtensions.x-dart-nullable}}{{^vendorExtensions.x-ns-default-val}}?{{/vendorExtensions.x-ns-default-val}}{{/vendorExtensions.x-dart-nullable}}{{/nullSafe}}.map((String key, +data{{#isNullable}}?{{/isNullable}}.map((String key, {{#items}} {{#isMap}} {{{vendorExtensions.x-innerType}}} data) => MapEntry(key, (dynamic data) { return {{>_copy}}; }(data) )) diff --git a/src/main/resources/dart2-v3template/_map_from_json.mustache b/src/main/resources/dart2-v3template/_map_from_json.mustache index 64ff002..8402ee5 100644 --- a/src/main/resources/dart2-v3template/_map_from_json.mustache +++ b/src/main/resources/dart2-v3template/_map_from_json.mustache @@ -1,36 +1,36 @@ {{#items}} {{#isMap}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, (dynamic data) { return {{>_from_json}}; }(data) )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, (dynamic data) { return {{>_from_json}}; }(data) )) {{/isMap}} {{#isArray}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, (dynamic data) { return {{>_from_json}}; }(data) )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, (dynamic data) { return {{>_from_json}}; }(data) )) {{/isArray}} {{^items}} {{#isEnum}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) {{/isEnum}} {{#isDateTime}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) {{/isDateTime}} {{#isDate}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) {{/isDate}} {{#isModel}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) + (data as Map).cast().fromNull().map((String key, dynamic data) => MapEntry(key, {{>_from_json}} )) {{/isModel}} {{#isPrimitiveType}} {{#isNumeric}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}}.map((key, value) => MapEntry(key, (value as num). + (data as Map).cast().fromNull().map((key, value) => MapEntry(key, (value as num). {{#isInteger}}toInt(){{/isInteger}} {{^isInteger}}toDouble(){{/isInteger}} )) {{/isNumeric}} {{^isNumeric}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}} + (data as Map).cast().fromNull() {{/isNumeric}} {{/isPrimitiveType}} {{#isAnyType}} - (data as Map).cast(){{#nullSafe}}.fromNull(){{/nullSafe}} + (data as Map).cast().fromNull() {{/isAnyType}} {{/items}} {{/items}} diff --git a/src/main/resources/dart2-v3template/_simple_from_json.mustache b/src/main/resources/dart2-v3template/_simple_from_json.mustache index 72584d7..4cf2f9b 100644 --- a/src/main/resources/dart2-v3template/_simple_from_json.mustache +++ b/src/main/resources/dart2-v3template/_simple_from_json.mustache @@ -9,7 +9,7 @@ return DateTime.parse(json[r'{{{baseName}}}']!) {{/isDateTime}} {{#isDate}} - DateTime.parse(json[r'{{{baseName}}}']{{#nullSafe}}!{{/nullSafe}} + 'T00:00:00.000Z') + DateTime.parse(json[r'{{{baseName}}}']! + 'T00:00:00.000Z') {{/isDate}} {{#isModel}} {{{complexType}}}.fromJson(json[r'{{{baseName}}}']) diff --git a/src/main/resources/dart2-v3template/_simple_from_json_required.mustache b/src/main/resources/dart2-v3template/_simple_from_json_required.mustache index 5ac1f92..5991f01 100644 --- a/src/main/resources/dart2-v3template/_simple_from_json_required.mustache +++ b/src/main/resources/dart2-v3template/_simple_from_json_required.mustache @@ -1,10 +1,10 @@ -{{#isEnum}}{{^vendorExtensions.x-dart-nullable}} +{{#isEnum}}{{^isNullable}} final _enumVal = {{enumName}}Extension.fromJson(_jsonData); -if (_enumVal == null) {{> _deserialisation_error }}{{/vendorExtensions.x-dart-nullable}} +if (_enumVal == null) {{> _deserialisation_error }}{{/isNullable}} {{/isEnum}} return {{#isEnum}} - {{^vendorExtensions.x-dart-nullable}}_enumVal;{{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}}{{enumName}}Extension.fromJson(_jsonData);{{/vendorExtensions.x-dart-nullable}} + {{^isNullable}}_enumVal;{{/isNullable}} + {{#isNullable}}{{enumName}}Extension.fromJson(_jsonData);{{/isNullable}} {{/isEnum}} {{#isDateTime}}DateTime.parse(_jsonData);{{/isDateTime}} {{#isDate}}DateTime.parse(_jsonData + 'T00:00:00.000Z');{{/isDate}} diff --git a/src/main/resources/dart2-v3template/_to_json_serialise.mustache b/src/main/resources/dart2-v3template/_to_json_serialise.mustache index 70448ce..9939808 100644 --- a/src/main/resources/dart2-v3template/_to_json_serialise.mustache +++ b/src/main/resources/dart2-v3template/_to_json_serialise.mustache @@ -1,9 +1,9 @@ {{#isArray}} - v{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.map((v) => {{#items}}{{> _to_json_serialise }} {{/items}} ){{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toList() + v{{#isNullable}}?{{/isNullable}}.map({{#items}}({{{dataType}}} v) => {{> _to_json_serialise }} {{/items}} ){{#isNullable}}?{{/isNullable}}.toList() {{/isArray}} {{#isMap}} - Map.fromIterables({{{name}}}{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}}!{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.keys, - {{{name}}}{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}}!{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.values.map((v) => {{#items}} {{> _to_json_serialise }} {{/items}} )) + Map.fromIterables({{{name}}}{{#isNullable}}!{{/isNullable}}.keys, + {{{name}}}{{#isNullable}}!{{/isNullable}}.values.map({{#items}}({{{dataType}}} v) => {{> _to_json_serialise }} {{/items}} )) {{/isMap}} {{^items}} {{#isModel}} @@ -13,16 +13,16 @@ v {{/isPrimitiveType}} {{#isEnum}} - (v as {{{dataType}}}){{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toJson() + v{{#isNullable}}?{{/isNullable}}.toJson() {{/isEnum}} {{#isDate}} - v{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toUtc() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.toIso8601String() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.substring(0, 10) + v{{#isNullable}}?{{/isNullable}}.toUtc() + {{#isNullable}}?{{/isNullable}}.toIso8601String() + {{#isNullable}}?{{/isNullable}}.substring(0, 10) {{/isDate}} {{#isDateTime}} - v{{#vendorExtensions.x-dart-nullable}}?{{/vendorExtensions.x-dart-nullable}}.toUtc() - {{#vendorExtensions.x-dart-nullable}}{{^nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.toIso8601String() + v{{#isNullable}}?{{/isNullable}}.toUtc() + {{#isNullable}}?{{/isNullable}}.toIso8601String() {{/isDateTime}} {{#isAnyType}}v{{/isAnyType}} {{^isModel}} diff --git a/src/main/resources/dart2-v3template/api.mustache b/src/main/resources/dart2-v3template/api.mustache index c4f82d3..9e5c4ee 100644 --- a/src/main/resources/dart2-v3template/api.mustache +++ b/src/main/resources/dart2-v3template/api.mustache @@ -8,7 +8,7 @@ part of {{pubName}}.api; class {{classname}} { final {{classname}}Delegate apiDelegate; - {{classname}}(ApiClient apiClient) : {{^nullSafe}}assert(apiClient != null), {{/nullSafe}}apiDelegate = {{classname}}Delegate(apiClient); + {{classname}}(ApiClient apiClient) : apiDelegate = {{classname}}Delegate(apiClient); {{#operation}} @@ -26,7 +26,7 @@ part of {{pubName}}.api; {{#returnType}}Future {{/returnType}}{{^returnType}}Future {{/returnType}} {{/vendorExtensions.x-dart-produces-raw}} {{/vendorExtensions.x-dart-produces-json}} - {{nickname}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options{{#nullSafe}}?{{/nullSafe}} options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}{{#nullSafe}}?{{/nullSafe}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options{{#nullSafe}}?{{/nullSafe}} options}{{/hasOptionalParams}}) async { + {{nickname}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options? options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}? {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options? options}{{/hasOptionalParams}}) async { final response = await apiDelegate.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}} options: options, {{#hasOptionalParams}}{{#hasParams}}{{/hasParams}}{{#allParams}}{{^required}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{/hasOptionalParams}}); @@ -42,7 +42,7 @@ part of {{pubName}}.api; final __body = response.body; if(response.statusCode >= 400) { - throw ApiException(response.statusCode, __body == null ? null : await decodeBodyBytes({{#nullSafe}}__body{{/nullSafe}}{{^nullSafe}}response{{/nullSafe}})); + throw ApiException(response.statusCode, __body == null ? null : await decodeBodyBytes(__body)); } {{^returnType}} @@ -58,7 +58,7 @@ part of {{pubName}}.api; return response; {{/vendorExtensions.x-dart-produces-raw}} {{^vendorExtensions.x-dart-produces-raw}} - return await apiDelegate.{{nickname}}_decode({{#nullSafe}}__body{{/nullSafe}}{{^nullSafe}}response{{/nullSafe}}); + return await apiDelegate.{{nickname}}_decode(__body); {{/vendorExtensions.x-dart-produces-raw}} {{/returnType}} } @@ -73,20 +73,11 @@ part of {{pubName}}.api; {{^vendorExtensions.x-dart-produces-json}} Future {{/vendorExtensions.x-dart-produces-json}} - {{vendorExtensions.x-dart-extension-name}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options{{#nullSafe}}?{{/nullSafe}} options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}{{#nullSafe}}?{{/nullSafe}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options{{#nullSafe}}?{{/nullSafe}} options}{{/hasOptionalParams}}) async { + {{vendorExtensions.x-dart-extension-name}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options? options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}? {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options? options}{{/hasOptionalParams}}) async { final response = await apiDelegate.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}} options: options, {{#hasOptionalParams}}{{#hasParams}}{{/hasParams}}{{#allParams}}{{^required}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{/hasOptionalParams}}); - {{#nullSafe}} return ApiResponse(response.statusCode, response.headers, response.body); - {{/nullSafe}} - {{^nullSafe}} - return ApiResponse() - ..statusCode = response.statusCode - ..headers = response.headers - ..body = response.body; - {{/nullSafe}} - } {{/vendorExtensions.x-dart-extension-name}} {{/operation}} @@ -96,28 +87,20 @@ part of {{pubName}}.api; class {{classname}}Delegate { final ApiClient apiClient; - {{classname}}Delegate(this.apiClient){{^nullSafe}} : assert(apiClient != null){{/nullSafe}}; + {{classname}}Delegate(this.apiClient); {{#operation}} + {{#allParams}} + // {{paramName}} required: {{required}} nullable: {{isNullable}} ext: {{{vendorExtensions}}} + {{/allParams}} Future - {{nickname}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options{{#nullSafe}}?{{/nullSafe}} options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}{{#nullSafe}}?{{/nullSafe}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options{{#nullSafe}}?{{/nullSafe}} options}{{/hasOptionalParams}}) async { - {{#bodyParams}}Object{{#nullSafe}}{{^required}}?{{/required}}{{/nullSafe}} postBody = {{paramName}};{{/bodyParams}} - - {{^nullSafe}} - // verify required params are set - {{#allParams}} - {{#required}} - if({{paramName}} == null) { - throw ApiException(400, 'Missing required param: {{paramName}}'); - } - {{/required}} - {{/allParams}} - {{/nullSafe}} + {{nickname}}({{#allParams}}{{#required}}{{{dataType}}}{{#isNullable}}?{{/isNullable}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{Options? options{{#hasParams}}, {{/hasParams}}{{#allParams}}{{^required}}{{{dataType}}}? {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}{{^hasOptionalParams}}{{#hasParams}}, {{/hasParams}}{Options? options}{{/hasOptionalParams}}) async { + {{#bodyParams}}Object{{#isNullable}}?{{/isNullable}} postBody = {{paramName}};{{/bodyParams}} // create path and map variables final __path = '{{{path}}}'{{#pathParams}}.replaceAll('{' + '{{baseName}}' + '}', {{#isDateTime}}openApiDateTimeParameterToString{{/isDateTime}}{{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}{{/isDateTime}}( - {{{paramName}}}){{#nullSafe}}{{^isDateTime}}{{^isDate}}!{{/isDate}}{{/isDateTime}}{{/nullSafe}}){{/pathParams}}; + {{{paramName}}}){{^isDateTime}}{{^isDate}}!{{/isDate}}{{/isDateTime}}){{/pathParams}}; // query params final queryParams = []; @@ -138,7 +121,7 @@ part of {{pubName}}.api; {{/required}} queryParams.addAll(convertParametersForCollectionFormat( (p) => - {{#isDateTime}}openApiDateTimeParameterToString(p, false){{/isDateTime}}{{^isDateTime}}{{#isDate}}openApiDateParameterToString(p, false){{/isDate}}{{^isDate}}LocalApiClient.parameterToString(p){{#nullSafe}}!{{/nullSafe}}{{/isDate}}{{/isDateTime}}, + {{#isDateTime}}openApiDateTimeParameterToString(p, false){{/isDateTime}}{{^isDateTime}}{{#isDate}}openApiDateParameterToString(p, false){{/isDate}}{{^isDate}}LocalApiClient.parameterToString(p)!{{/isDate}}{{/isDateTime}}, '{{collectionFormat}}', '{{baseName}}', {{paramName}})); {{^required}} } @@ -158,19 +141,12 @@ part of {{pubName}}.api; headerParams['Content-Type'] = contentTypes[0]; } {{#bodyParam}} - {{^nullSafe}} - if (postBody != null) { - postBody = LocalApiClient.serialize(postBody); - } - {{/nullSafe}} - {{#nullSafe}} {{#required}} postBody = LocalApiClient.serialize(postBody); {{/required}} {{^required}} postBody = postBody == null ? null : LocalApiClient.serialize(postBody); {{/required}} - {{/nullSafe}} {{/bodyParam}} {{/vendorExtensions.x-dart-consumes-json}} {{#vendorExtensions.x-dart-consumes-form}} @@ -181,13 +157,14 @@ part of {{pubName}}.api; final postBody = { {{#formParams}} {{^isFile}} {{^isArray}} - '{{baseName}}': {{#isDateTime}}{{paramName}}?.toUtc(){{^nullSafe}}?{{/nullSafe}}.toIso8601String(){{/isDateTime}} + '{{baseName}}': {{#isDateTime}}{{paramName}}{{#isNullable}}?{{/isNullable}}.toUtc().toIso8601String(){{/isDateTime}} {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}({{paramName}}){{/isDateTime}}, {{/isArray}} {{#isArray}} - '{{baseName}}': {{paramName}}{{^required}}?{{/required}}{{#nullSafe}}.whereNotNull(){{/nullSafe}}{{^nullSafe}}.where((p) => p != null){{/nullSafe}} - {{^nullSafe}}?{{/nullSafe}}.map({{#isDateTime}}{{paramName}}?.toUtc().toIso8601String(){{/isDateTime}} - {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}{{/isDateTime}}){{^nullSafe}}?{{/nullSafe}}.toList(), + + '{{baseName}}': {{paramName}}{{#isNullable}}?{{/isNullable}}.whereNotNull() + .map({{#isDateTime}}{{paramName}}.toUtc().toIso8601String(){{/isDateTime}} + {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}{{/isDateTime}}).toList(), {{/isArray}} {{/isFile}} {{/formParams}} }.entries.where((me) => me.value != null).map((me) => me.key + "=" + Uri.encodeComponent(me.value)).join("&"); @@ -206,9 +183,9 @@ part of {{pubName}}.api; {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}({{paramName}}){{/isDateTime}}, {{/isArray}} {{#isArray}} - '{{baseName}}': {{paramName}}{{^required}}?{{/required}}{{#nullSafe}}.whereNotNull(){{/nullSafe}}{{^nullSafe}}.where((p) => p != null){{/nullSafe}} - {{^nullSafe}}?{{/nullSafe}}.map({{#isDateTime}}{{paramName}}?.toUtc().toIso8601String(){{/isDateTime}} - {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}{{/isDateTime}}){{^nullSafe}}?{{/nullSafe}}.toList(), + '{{baseName}}': {{paramName}}{{#isNullable}}?{{/isNullable}}.whereNotNull() + .map({{#isDateTime}}{{paramName}}.toUtc().toIso8601String(){{/isDateTime}} + {{^isDateTime}}{{#isDate}}openApiDateParameterToString{{/isDate}}{{^isDate}}LocalApiClient.parameterToString{{/isDate}}{{/isDateTime}}).toList(), {{/isArray}} {{/isFile}} {{#isFile}} @@ -233,17 +210,17 @@ part of {{pubName}}.api; {{^vendorExtensions.x-dart-produces-json}} {{#returnType}}Future {{/returnType}}{{^returnType}}Future {{/returnType}} {{/vendorExtensions.x-dart-produces-json}} - {{nickname}}_decode({{#nullSafe}}Stream> body{{/nullSafe}}{{^nullSafe}}ApiResponse response{{/nullSafe}}) async { + {{nickname}}_decode(Stream> body) async { {{#vendorExtensions.x-dart-produces-json}} {{#isArray}} {{#returnType}} - return (LocalApiClient.deserializeFromString(await utf8.decodeStream({{#nullSafe}}body{{/nullSafe}}{{^nullSafe}}response.body{{/nullSafe}}), '{{{returnType}}}') as List).map((item) => item as {{returnBaseType}}).toList(); + return (LocalApiClient.deserializeFromString(await utf8.decodeStream(body), '{{{returnType}}}') as List).map((item) => item as {{returnBaseType}}).toList(); {{/returnType}} {{/isArray}} {{^isArray}} {{#isMap}} {{#returnType}} - return {{{returnType}}}.from(LocalApiClient.deserializeFromString(await utf8.decodeStream({{#nullSafe}}body{{/nullSafe}}{{^nullSafe}}response.body{{/nullSafe}}), '{{{returnType}}}')); + return {{{returnType}}}.from(LocalApiClient.deserializeFromString(await utf8.decodeStream(body), '{{{returnType}}}')); {{/returnType}}; {{/isMap}} {{^isMap}} @@ -252,7 +229,7 @@ part of {{pubName}}.api; return response; {{/vendorExtensions.x-dart-produces-raw}} {{^vendorExtensions.x-dart-produces-raw}} - return LocalApiClient.deserializeFromString(await utf8.decodeStream({{#nullSafe}}body{{/nullSafe}}{{^nullSafe}}response.body{{/nullSafe}}), '{{{returnType}}}') as {{{returnType}}}; + return LocalApiClient.deserializeFromString(await utf8.decodeStream(body), '{{{returnType}}}') as {{{returnType}}}; {{/vendorExtensions.x-dart-produces-raw}} {{/returnType}} {{/isMap}} @@ -260,7 +237,7 @@ part of {{pubName}}.api; {{/vendorExtensions.x-dart-produces-json}} {{^vendorExtensions.x-dart-produces-json}} {{^vendorExtensions.x-dart-produces-raw}} - return await utf8.decodeStream({{#nullSafe}}body{{/nullSafe}}{{^nullSafe}}response.body{{/nullSafe}}); + return await utf8.decodeStream(body); {{/vendorExtensions.x-dart-produces-raw}} {{/vendorExtensions.x-dart-produces-json}} } diff --git a/src/main/resources/dart2-v3template/api_client.mustache b/src/main/resources/dart2-v3template/api_client.mustache index 5abc274..9ed2371 100644 --- a/src/main/resources/dart2-v3template/api_client.mustache +++ b/src/main/resources/dart2-v3template/api_client.mustache @@ -3,11 +3,7 @@ part of {{pubName}}.api; class LocalApiClient { -{{^nullSafe}} - static final _regList = RegExp(r'^List<(.*)>$'); - static final _regMap = RegExp(r'^Map$'); -{{/nullSafe}} - static dynamic serialize(Object{{#nullSafe}}?{{/nullSafe}} value) { + static dynamic serialize(Object? value) { try { if (value == null) { return null; @@ -51,7 +47,7 @@ return value.toString(); } } - static dynamic deserializeFromString(String{{#nullSafe}}?{{/nullSafe}} json, String targetType) { + static dynamic deserializeFromString(String? json, String targetType) { if (json == null) { // HTTP Code 204 return null; } @@ -99,24 +95,7 @@ return value.toString(); return {{{enumName}}}Extension.fromJson(value); {{/x-internal-enums}} default: -{{^nullSafe}} - { - Match match; - if (value is List && - (match = _regList.firstMatch(targetType)) != null) { - var newTargetType = match[1]; - return value.map((v) => deserialize(v, newTargetType)).toList(); - } else if (value is Map && - (match = _regMap.firstMatch(targetType)) != null) { - var newTargetType = match[1]; - return Map.fromIterables(value.keys, - value.values.map((v) => deserialize(v, newTargetType))); - } - } -{{/nullSafe}} -{{#nullSafe}} return matchLeftovers(value, targetType, (v, t) => deserialize(v, t)); -{{/nullSafe}} } } on Exception catch (e, stack) { throw ApiException.withInner(500, 'Exception during deserialization.', e, stack); @@ -124,7 +103,7 @@ return value.toString(); } /// Format the given parameter object into string. - static String{{#nullSafe}}?{{/nullSafe}} parameterToString(dynamic value) { + static String? parameterToString(dynamic value) { if (value == null) { return null; } else if (value is DateTime) { // shouldn't ever be used, replaced by direct call diff --git a/src/main/resources/dart2-v3template/api_test.mustache b/src/main/resources/dart2-v3template/api_test.mustache index 1eeb637..1b3eb8c 100644 --- a/src/main/resources/dart2-v3template/api_test.mustache +++ b/src/main/resources/dart2-v3template/api_test.mustache @@ -18,7 +18,7 @@ void main() { // {{{.}}} // {{/notes}} - //{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{operationId}}({{#allParams}}{{^vendorExtensions.x-dart-nullable}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-dart-nullable}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{#vendorExtensions.x-dart-nullable}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-dart-nullable}}{{/allParams}} }{{/hasOptionalParams}}) async + //{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{operationId}}({{#allParams}}{{^isNullable}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/isNullable}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{#isNullable}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/isNullable}}{{/allParams}} }{{/hasOptionalParams}}) async test('test {{operationId}}', () async { // TODO }); diff --git a/src/main/resources/dart2-v3template/class.mustache b/src/main/resources/dart2-v3template/class.mustache index c4613ee..e2b24c9 100644 --- a/src/main/resources/dart2-v3template/class.mustache +++ b/src/main/resources/dart2-v3template/class.mustache @@ -1,10 +1,10 @@ // {{{name}}} class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{#vars}} - {{^vendorExtensions.x-dart-inherited}} + {{^isInherited}} {{#description}}/* {{{description}}} */{{/description}} {{^allowableValues}} - {{{dataType}}} {{{name}}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}; + {{^isNullable}}{{^required}}late {{/required}}{{/isNullable}}{{{dataType}}} {{{name}}}; {{/allowableValues}} {{#allowableValues}} {{#isEnum}} @@ -18,38 +18,48 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{{classname}}}{{nameInCamelCase}}Enum {{{name}}}{{#defaultValue}} = {{{classname}}}{{nameInCamelCase}}Enum.{{{defaultValue}}}{{/defaultValue}}; {{/complexType}} {{#complexType}} - {{{dataType}}} {{{name}}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}; + {{^isNullable}}{{^required}}late {{/required}}{{/isNullable}}{{{dataType}}}{{{name}}}; {{/complexType}} {{/isEnum}} {{/allowableValues}} - {{/vendorExtensions.x-dart-inherited}} + {{/isInherited}} {{/vars}} {{^hasVars}}{{^parent}} - {{^vendorExtensions.x-dart-nullable}}{{#nullSafe}}late {{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}dynamic json; + {{^isNullable}}late {{/isNullable}}dynamic json; {{/parent}}{{/hasVars}} +{{#vars}} + // {{name}} required: {{required}} nullable: {{isNullable}} default: {{vendorExtensions.original-default}} inherited: {{isInherited}} container {{isContainer}} +{{/vars}} {{{classname}}}( {{#hasVars}}{ {{#vars}} - {{#nullSafe}}{{#vendorExtensions.x-dart-required}}required {{/vendorExtensions.x-dart-required}}{{/nullSafe}} - {{^vendorExtensions.x-dart-required}} - {{#isArray}}{{{dataType}}}{{#nullSafe}}?{{/nullSafe}} {{{name}}}{{/isArray}} - {{#isMap}}{{{dataType}}}{{#nullSafe}}?{{/nullSafe}} {{{name}}}{{/isMap}} - {{^isArray}} - {{^isMap}} - {{#vendorExtensions.x-dart-inherited}}{{{dataType}}} {{/vendorExtensions.x-dart-inherited}}{{^vendorExtensions.x-dart-inherited}}this.{{/vendorExtensions.x-dart-inherited}}{{{name}}}{{^vendorExtensions.x-dart-required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/vendorExtensions.x-dart-required}} - {{/isMap}} - {{/isArray}} - {{/vendorExtensions.x-dart-required}} - {{#vendorExtensions.x-dart-required}} - this.{{{name}}}{{^vendorExtensions.x-dart-required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/vendorExtensions.x-dart-required}} - {{/vendorExtensions.x-dart-required}} + {{#required}}required {{/required}} + {{#vendorExtensions.required-owned}} + this.{{{name}}}{{^required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}} + {{/vendorExtensions.required-owned}} + {{^vendorExtensions.required-owned}} + {{! now we just deal with arrays and maps }} + {{#isContainer}} + {{#isInherited}}{{{dataType}}} {{{name}}}{{/isInherited}} + {{^isInherited}} + {{#defaultValue}}{{{dataType}}}{{^isNullable}}?{{/isNullable}} {{{name}}}{{/defaultValue}} + {{^defaultValue}}this.{{{name}}}{{/defaultValue}} + {{/isInherited}} + {{/isContainer}} + {{^isContainer}} + {{#isInherited}}{{{dataType}}} {{{name}}}{{/isInherited}} + {{^isInherited}} + this.{{{name}}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} + {{/isInherited}} + {{/isContainer}} + {{/vendorExtensions.required-owned}} {{^-last}}, {{/-last}} {{/vars}} }{{/hasVars}} ) -{{#vendorExtensions.x-has-opt-arrays}}:{{/vendorExtensions.x-has-opt-arrays}} -{{^vendorExtensions.x-has-opt-arrays}}{{#parent}}:{{/parent}}{{/vendorExtensions.x-has-opt-arrays}} -{{#vendorExtensions.x-opt-arrays}}this.{{{name}}} = {{{name}}} ?? {{#isArray}}[]{{/isArray}}{{#isMap}}{}{{/isMap}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-opt-arrays}} -{{#parent}}{{#vendorExtensions.x-has-opt-arrays}},{{/vendorExtensions.x-has-opt-arrays}} super({{#vars}}{{#vendorExtensions.x-dart-inherited}}{{name}}: {{name}},{{/vendorExtensions.x-dart-inherited}}{{/vars}}) {{/parent}} +{{#vendorExtensions.x-has-nullable-arrays-with-defaults}}:{{/vendorExtensions.x-has-nullable-arrays-with-defaults}} +{{^vendorExtensions.x-has-nullable-arrays-with-defaults}}{{#parent}}:{{/parent}}{{/vendorExtensions.x-has-nullable-arrays-with-defaults}} +{{#vendorExtensions.x-nullable-arrays-with-defaults}}{{#defaultValue}}this.{{{name}}} = {{{name}}} ?? {{{defaultValue}}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-nullable-arrays-with-defaults}} +{{#parent}}{{#vendorExtensions.x-has-nullable-arrays-with-defaults}},{{/vendorExtensions.x-has-nullable-arrays-with-defaults}} super({{#vars}}{{#isInherited}}{{name}}: {{name}}{{#defaultValue}} ?? {{{defaultValue}}}{{/defaultValue}},{{/isInherited}}{{/vars}}) {{/parent}} ; @override @@ -70,11 +80,10 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{#hasVars}} {{#vars}} - {{^vendorExtensions.x-dart-inherited}} + {{^isInherited}} static {{{dataType}}} fromJson_{{name}}(Map json) { - {{#nullSafe}} {{^vendorExtensions.x-var-is-binary}} - {{^vendorExtensions.x-dart-nullable}} + {{^isNullable}} final _jsonData = json[r'{{{baseName}}}']; if (_jsonData == null) { {{#required}} @@ -89,31 +98,25 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{#isArray}}{{>_complex_from_json_required}}{{/isArray}} {{#isMap}}{{>_complex_from_json_required}}{{/isMap}} {{^items}}{{>_simple_from_json_required}}{{/items}} - {{/vendorExtensions.x-dart-nullable}} + {{/isNullable}} - {{#vendorExtensions.x-dart-nullable}} + {{#isNullable}} {{#required}} // required but nullable, has to be passed if (!json.containsKey(r'{{{baseName}}}')) { {{> _deserialisation_error }} } {{/required}} {{#isArray}}{{>_complex_from_json}}{{/isArray}} {{#isMap}}{{>_complex_from_json}}{{/isMap}} - {{^items}}{{>_simple_from_json}}{{/items}}{{/vendorExtensions.x-dart-nullable}} + {{^items}}{{>_simple_from_json}}{{/items}}{{/isNullable}} {{/vendorExtensions.x-var-is-binary}} - {{/nullSafe}} - {{^nullSafe}} - {{^vendorExtensions.x-var-is-binary}} - {{#isArray}}{{>_complex_from_json}}{{/isArray}}{{#isMap}}{{>_complex_from_json}}{{/isMap}}{{^items}}{{>_simple_from_json}}{{/items}} - {{/vendorExtensions.x-var-is-binary}} - {{/nullSafe}} {{#vendorExtensions.x-var-is-binary}} - {{^vendorExtensions.x-dart-nullable}} - return {{#isArray}}const []{{/isArray}}{{#isMap}}const {}{{/isMap}}{{^isArray}}{{^isMap}}MultipartFile.fromBytes([]){{/isMap}}{{/isArray}}; - {{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}}return null;{{/vendorExtensions.x-dart-nullable}} + {{^isNullable}} + return {{#isArray}}[]{{/isArray}}{{#isMap}}{}{{/isMap}}{{^isArray}}{{^isMap}}MultipartFile.fromBytes([]){{/isMap}}{{/isArray}}; + {{/isNullable}} + {{#isNullable}}return null;{{/isNullable}} {{/vendorExtensions.x-var-is-binary}} } - {{/vendorExtensions.x-dart-inherited}} + {{/isInherited}} {{/vars}} {{{classname}}}.fromJson(Map json) : @@ -125,11 +128,11 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{> _class_to_json }} } {{/hasVars}} - static List<{{{classname}}}> listFromJson(List{{#nullSafe}}?{{/nullSafe}} json) { + static List<{{{classname}}}> listFromJson(List? json) { return json == null ? <{{{classname}}}>[] : json.map((value) => {{{classname}}}.fromJson(value)).toList(); } - static Map mapFromJson(Map{{#nullSafe}}?{{/nullSafe}} json) { + static Map mapFromJson(Map? json) { final map = {}; if (json != null && json.isNotEmpty) { json.forEach((String key, dynamic value) => map[key] = {{{classname}}}.fromJson(value)); @@ -138,7 +141,7 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { } @override - bool operator ==(Object{{#nullSafe}}?{{/nullSafe}} __other) { + bool operator ==(Object? __other) { if (identical(this, __other)) { return true; } @@ -184,17 +187,10 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { var hashCode = {{#parent}}super.hashCode{{/parent}}{{^parent}}runtimeType.hashCode{{/parent}}; {{#hasVars}} - {{#vars}}{{^vendorExtensions.x-dart-inherited}} - {{#vendorExtensions.x-ns-default-val}} - if ({{{name}}}{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}.isNotEmpty{{#vendorExtensions.x-dart-nullable}}{{#nullSafe}} == true{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}) { - {{/vendorExtensions.x-ns-default-val}} - {{^vendorExtensions.x-ns-default-val}} - {{#nullSafe}} - {{#vendorExtensions.x-dart-nullable}} + {{#vars}}{{^isInherited}} + {{#isNullable}} if ({{{name}}} != null) { - {{/vendorExtensions.x-dart-nullable}} - {{/nullSafe}} - {{/vendorExtensions.x-ns-default-val}} + {{/isNullable}} {{#isArray}} hashCode = hashCode * 31 + const ListEquality().hash({{{name}}}); {{/isArray}} @@ -204,18 +200,12 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{^items}} hashCode = hashCode * 31 + {{{name}}}.hashCode; {{/items}} - {{#vendorExtensions.x-ns-default-val}} - } - {{/vendorExtensions.x-ns-default-val}} - {{^vendorExtensions.x-ns-default-val}} - {{#nullSafe}} - {{#vendorExtensions.x-dart-nullable}} + + {{#isNullable}} } - {{/vendorExtensions.x-dart-nullable}} - {{/nullSafe}} - {{/vendorExtensions.x-ns-default-val}} + {{/isNullable}} - {{/vendorExtensions.x-dart-inherited}}{{/vars}}{{/hasVars}}{{^hasVars}}{{^parent}}if (json != null) { hashCode = hashCode ^ json.hashCode; }{{/parent}}{{/hasVars}} + {{/isInherited}}{{/vars}}{{/hasVars}}{{^hasVars}}{{^parent}}if (json != null) { hashCode = hashCode ^ json.hashCode; }{{/parent}}{{/hasVars}} return hashCode; } @@ -223,39 +213,33 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{^disableCopyWith}} {{{classname}}} copyWith({{#hasVars}}{ {{#vars}} - {{{dataType}}}{{^vendorExtensions.x-simpledynamic}}{{^vendorExtensions.x-dart-nullable}}{{#nullSafe}}?{{/nullSafe}}{{/vendorExtensions.x-dart-nullable}}{{/vendorExtensions.x-simpledynamic}} {{{name}}}, + {{{dataType}}}{{^vendorExtensions.x-simpledynamic}}{{^isNullable}}?{{/isNullable}}{{/vendorExtensions.x-simpledynamic}} {{{name}}}, {{/vars}} }{{/hasVars}}) { {{#vars}} final _copy_{{{name}}} = {{{name}}} ?? - {{#nullSafe}} - {{^vendorExtensions.x-dart-nullable}} + + {{^isNullable}} {{#isArray}}{{>_complex_copy_required}}{{/isArray}}{{#isMap}}{{>_complex_copy_required}}{{/isMap}}{{^items}}{{^isModel}}this.{{{name}}}{{/isModel}}{{#isModel}}this.{{{name}}}.copyWith(){{/isModel}};{{/items}} - {{/vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-dart-nullable}} - {{#vendorExtensions.x-ns-default-val}} - {{#isArray}}{{>_complex_copy_required}}{{/isArray}}{{#isMap}}{{>_complex_copy_required}}{{/isMap}}{{^items}}{{^isModel}}this.{{{name}}}{{/isModel}}{{#isModel}}this.{{{name}}}.copyWith(){{/isModel}};{{/items}} - {{/vendorExtensions.x-ns-default-val}} - {{^vendorExtensions.x-ns-default-val}} + {{/isNullable}} + {{#isNullable}} {{#isArray}}{{>_complex_copy}}{{/isArray}}{{#isMap}}{{>_complex_copy}}{{/isMap}}{{^items}}{{^isModel}}this.{{{name}}}{{/isModel}}{{#isModel}}this.{{{name}}}?.copyWith(){{/isModel}};{{/items}} - {{/vendorExtensions.x-ns-default-val}} - {{/vendorExtensions.x-dart-nullable}} - {{/nullSafe}} - {{^nullSafe}} - {{#isArray}}{{>_complex_copy}}{{/isArray}}{{#isMap}}{{>_complex_copy}}{{/isMap}}{{^items}}{{^isModel}}this.{{{name}}}{{/isModel}}{{#isModel}}this.{{{name}}}?.copyWith(){{/isModel}};{{/items}} - {{/nullSafe}} + {{/isNullable}} {{/vars}} - return {{{classname}}}( - {{#vars}}{{{name}}}: _copy_{{{name}}},{{/vars}}); + return {{{classname}}}( + {{#vars}} + {{{name}}}: _copy_{{{name}}}, + {{/vars}} + ); } {{/disableCopyWith}} } {{#vars}} - {{^vendorExtensions.x-dart-inherited}} + {{^isInherited}} {{^complexType}} {{^min}} {{#allowableValues}} @@ -273,16 +257,16 @@ class {{{classname}}}{{#parent}} extends {{parent}}{{/parent}} { {{#enumVars}}{{{enumName}}}.{{{name}}}:{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}} {{/allowableValues}} }; - String{{#nullSafe}}?{{/nullSafe}} toJson() => toMap[this]; - static {{{enumName}}}{{#nullSafe}}?{{/nullSafe}} fromJson(String key) => {{^nullSafe}}key == null ? null : {{/nullSafe}}fromMap[key]; + String? toJson() => toMap[this]; + static {{{enumName}}}? fromJson(String key) => fromMap[key]; - static List<{{{enumName}}}> listFromJson(List{{#nullSafe}}?{{/nullSafe}} json) => - json == null ? <{{{enumName}}}>[] : json.map((value) => fromJson(value)).toList(){{#nullSafe}}.fromNull(){{/nullSafe}}; + static List<{{{enumName}}}> listFromJson(List? json) => + json == null ? <{{{enumName}}}>[] : json.map((value) => fromJson(value)).toList().fromNull(); static {{{enumName}}} copyWith({{{enumName}}} instance) => instance; } {{/allowableValues}} {{/min}} {{/complexType}} - {{/vendorExtensions.x-dart-inherited}} + {{/isInherited}} {{/vars}} diff --git a/src/main/resources/dart2-v3template/copy-with.mustache b/src/main/resources/dart2-v3template/copy-with.mustache index d6a94a0..a849adb 100644 --- a/src/main/resources/dart2-v3template/copy-with.mustache +++ b/src/main/resources/dart2-v3template/copy-with.mustache @@ -12,7 +12,7 @@ {{/isPrimitiveType}} {{^isPrimitiveType}} {{#isContainer}} - function(v{{#nullSafe}}?{{/nullSafe}}) { + function(v?) { var newVal; final fld = v; {{#items}} diff --git a/src/main/resources/dart2-v3template/enum.mustache b/src/main/resources/dart2-v3template/enum.mustache index 75ffd62..7b50b83 100644 --- a/src/main/resources/dart2-v3template/enum.mustache +++ b/src/main/resources/dart2-v3template/enum.mustache @@ -6,10 +6,10 @@ enum {{classname}} { } extension {{classname}}Extension on {{classname}} { - {{#isString}}String{{#nullSafe}}?{{/nullSafe}} get name => toMap[this]{{/isString}}{{#isInteger}}int{{#nullSafe}}?{{/nullSafe}} get asInt => toMap[this]{{/isInteger}}; + {{#isString}}String? get name => toMap[this]{{/isString}}{{#isInteger}}int? get asInt => toMap[this]{{/isInteger}}; // you have to call this extension class to use this as this is not yet supported - static {{classname}}{{#nullSafe}}?{{/nullSafe}} type({{dataType}} name) => fromMap[name]; + static {{classname}}? type({{dataType}} name) => fromMap[name]; static Map<{{dataType}}, {{classname}}> fromMap = { {{#allowableValues}} {{#enumVars}}{{{value}}}:{{classname}}.{{{name}}}{{^-last}}, {{/-last}}{{/enumVars}} @@ -18,18 +18,18 @@ extension {{classname}}Extension on {{classname}} { {{#enumVars}}{{classname}}.{{{name}}}:{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}} {{/allowableValues}} }; - static {{classname}}{{#nullSafe}}?{{/nullSafe}} fromJson(dynamic data) => data == null ? null : fromMap[data]; + static {{classname}}? fromJson(dynamic data) => data == null ? null : fromMap[data]; dynamic toJson() => toMap[this]; - static List<{{classname}}> listFromJson(List{{#nullSafe}}?{{/nullSafe}} json) => - json == null ? <{{classname}}>[] : json.map((value) => fromJson(value)).toList(){{#nullSafe}}.fromNull(){{/nullSafe}}; + static List<{{classname}}> listFromJson(List? json) => + json == null ? <{{classname}}>[] : json.map((value) => fromJson(value)).toList().fromNull(); {{^disableCopyWith}} static {{classname}} copyWith({{classname}} instance) => instance; {{/disableCopyWith}} - static Map mapFromJson(Map{{#nullSafe}}?{{/nullSafe}} json) { + static Map mapFromJson(Map? json) { final map = {}; if (json != null && json.isNotEmpty) { json.forEach((String key, dynamic value) { diff --git a/src/main/resources/dart2-v3template/pubspec.mustache b/src/main/resources/dart2-v3template/pubspec.mustache index e6a9f02..cebfd28 100644 --- a/src/main/resources/dart2-v3template/pubspec.mustache +++ b/src/main/resources/dart2-v3template/pubspec.mustache @@ -1,6 +1,9 @@ name: {{pubName}} version: {{pubVersion}} description: {{pubDescription}} +{{#localDev}} +publish_to: none +{{/localDev}} {{#pubRepository}} repository: {{{.}}} {{/pubRepository}} @@ -8,47 +11,23 @@ repository: {{{.}}} homepage: {{{.}}} {{/pubHomepage}} environment: -{{#nullSafe}} - sdk: '>=2.15.0 <3.0.0' -{{/nullSafe}} -{{^nullSafe}} - sdk: '>=2.7.0 <3.0.0' -{{/nullSafe}} + sdk: '>=2.15.0 <3.99.0' dependencies: -{{#nullSafe}} {{#localDev}} openapi_dart_common: path: {{{localDev}}} {{/localDev}} {{^localDev}} -{{#useDio5}} dio: '^5.0.1' - openapi_dart_common: ^5.0.0 -{{/useDio5}} -{{^useDio5}} - dio: '^4.0.0' - openapi_dart_common: ^4.1.1 -{{/useDio5}} + openapi_dart_common: ^5.1.0 {{/localDev}} collection: '^1.16.0' -{{/nullSafe}} -{{^nullSafe}} - dio: '>=3.0.10 <4.0.0' - openapi_dart_common: ^3.3.1 - collection: ^1.14.11 -{{/nullSafe}} {{#x-dart-deps}} {{{.}}} {{/x-dart-deps}} dev_dependencies: -{{#nullSafe}} test: ^1.23.1 flutter_lints: ^2.0.1 -{{/nullSafe}} -{{^nullSafe}} - test: ^1.3.0 - pedantic: ^1.9.0 -{{/nullSafe}} {{#x-dart-devdeps}} {{{.}}} {{/x-dart-devdeps}} diff --git a/src/test/java/cd/connect/openapi/SampleRunner.java b/src/test/java/cd/connect/openapi/SampleRunner.java index 6de6ca7..700407d 100644 --- a/src/test/java/cd/connect/openapi/SampleRunner.java +++ b/src/test/java/cd/connect/openapi/SampleRunner.java @@ -26,8 +26,6 @@ public void runGenerator() { // "--import-mappings", "IntOrString=./int_or_string.dart", // "--global-property", "skipFormModel=false", // "--additional-properties", "x-use-5x-nullable=true", - "--additional-properties", "nullSafe=true", - "--additional-properties", "nullSafe-array-default=true", "--additional-properties", "localDev=/Users/richard/projects/dart/openapi_dart_common", "--additional-properties", "listAnyOf=true", "--output", "sample-app/" + getClass().getSimpleName()) diff --git a/src/test/resources/api_tests.dart b/src/test/resources/api_tests.dart index 6fa375e..eece08f 100644 --- a/src/test/resources/api_tests.dart +++ b/src/test/resources/api_tests.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:sample_app/api.dart'; import 'package:test/test.dart'; @@ -21,8 +23,9 @@ main() { print(w); print(x); print("codes ${w.hashCode} vs ${x.hashCode}"); + expect(true, w.hashCode > 0, reason: '${w.hashCode} should be > 0'); print("empty array -> ${[].hashCode}"); - expect(true, w.hashCode == x.hashCode); + expect(true, w.hashCode == x.hashCode, reason: 'w hashcode: ${w.hashCode} should equal x hashcode: ${x.hashCode}'); var z = w.copyWith(types: [GeocodedWaypointTypesEnum.ADDRESS]); expect(false, w == z); expect(false, w.hashCode == z.hashCode); @@ -31,17 +34,29 @@ main() { expect(encodeDecode, w); }); + test('basic inheritance', () { + final wld = WithListDerived.fromJson({ + "list": [ + {"id": 1, "name": "one"}, + {"id": 2, "name": "two"} + ], + "id": 7, + "name": "entity", + "nullableList": [] + }); + + expect(wld.id, 7); + expect(wld.name, "entity"); + expect(wld.list.length, 2); + }); + // previous hashing mechanism if you swapped the adjacent field values // it wouldn't change the hash test( 'hashing an object which has two fields of the same type is still different', () { - var ht = HashTest() - ..fieldOne = false - ..fieldTwo = true; - var ht1 = HashTest() - ..fieldOne = true - ..fieldTwo = false; + var ht = HashTest(fieldOne: false, fieldTwo: true); + var ht1 = HashTest(fieldTwo: false, fieldOne: true); expect(false, ht.hashCode == ht1.hashCode); }); @@ -49,6 +64,7 @@ main() { const addProp = { 'discrim': 'fred', 'readings': {'one': 1, 'two': 2.3}, + 'extra': {}, 'dependencies': { 'deps1': ['a', 34.2, true], 'deps2': [17.8, false, 'b'] @@ -79,23 +95,115 @@ main() { var ap = AddProps3.fromJson(addProp); expect(ap.discrim, 'fred'); - expect(ap.readings.length, 2); - expect(ap.readings['one'], 1); - expect(ap.readings['two'], 2.3); - expect(ap.dependencies['deps1'], ['a', 34.2, true]); - expect(ap.dependencies['deps2'], [17.8, false, 'b']); - expect(ap.otherDeps['name'], ['tom', 'dick', 'harry']); - expect(ap.otherDeps['height'], [1.7, 1.3, 1.4]); - expect(ap.otherDeps['info'], 'this is top secret'); - expect(ap.yetMoreAdditional['sList'], ['a', 'b', 'c']); + expect(ap.readings!.length, 2); + expect(ap.readings!['one'], 1); + expect(ap.readings!['two'], 2.3); + expect(ap.dependencies!['deps1'], ['a', 34.2, true]); + expect(ap.dependencies!['deps2'], [17.8, false, 'b']); + expect(ap.otherDeps!['name'], ['tom', 'dick', 'harry']); + expect(ap.otherDeps!['height'], [1.7, 1.3, 1.4]); + expect(ap.otherDeps!['info'], 'this is top secret'); + expect(ap.yetMoreAdditional!['sList'], ['a', 'b', 'c']); expect( - ap.mapWithComplexObject['c1'][0], - Event() - ..status = EventStatus.STREAMING - ..id = 'xx' - ..title = 'Scully' - ..img = 'img' - ..imageUrl = 'http://blah'); - expect(ap.mapWithEnums['statuses'], [EventStatus.STREAMING, EventStatus.CLOSED]); + ap.mapWithComplexObject!['c1']?[0], + Event( + status: EventStatus.STREAMING, + id: 'xx', + title: 'Scully', + img: 'img', + imageUrl: 'http://blah')); + expect(ap.mapWithEnums!['statuses'], + [EventStatus.STREAMING, EventStatus.CLOSED]); + }); + + test("List> - parsing json array with discriminator", + () { + final items = + AnyOfMyAppleMyBanana.listFromJson(jsonDecode(_dummyDiscriminatorJson)); + + expect(items, hasLength(2)); + expect(items[0].discriminator, AnyOfDiscriminatorMyAppleMyBanana.MyApple); + expect(items[0].asMyApple().type, "apple"); + expect(items[0].asMyApple().kind, "Foxwhelp"); + expect(items[1].discriminator, AnyOfDiscriminatorMyAppleMyBanana.MyBanana); + expect(items[1].asMyBanana().type, "banana"); + expect(items[1].asMyBanana().count, 42); + }); + test('double with ints in them tests and vs versa', () { + const testData = { + 'basicInt': 2.6, + 'basicDouble': 1, + 'intList': [1, 2.6], + 'int64Int': 33, + 'intMap': {'one': 1, 'two': 2.7}, + 'doubleList': [1, 2.6], + 'doubleMap': {'one': 1, 'two': 2.7}, + }; + + final data = DoubleAndIntConversion.fromJson(testData); + expect(data.basicDouble, 1.0); + expect(data.basicInt, 2); + expect(data.intList, [1, 2]); + expect(data.int64Int, 33); + expect(data.intMap, {'one': 1, 'two': 2}); + expect(data.doubleList, [1.0, 2.6]); + expect(data.doubleMap, {'one': 1.0, 'two': 2.7}); + }); + test('data serialisation', () { + final data = DoubleAndIntConversion( + int64Int: 0, + intMap: {}, + basicInt: 43, + basicDouble: 26.2, + intList: [], + doubleMap: {}); + expect(data.toJson(), { + 'basicInt': 43, + 'int64Int': 0, + 'basicDouble': 26.2, + 'intList': [], + 'intMap': {}, + 'doubleList': [], // because it is not nullable + 'doubleMap': {} + }); + }); + test("int enums being generated with correct type", () { + expect(IntTypeEnum.number1.toJson(), 1); + expect(IntTypeEnum.number1, IntTypeEnumExtension.fromJson(1)); + }); + test( + "enums included in a model via allOf with reference will be treated as" + "enums and generate valid code ", () { + final testO = ObjectContainingEnum.fromJson( + {"name": "foobar", "enumFieldAllOf": "667"}); + expect(testO.name, "foobar"); + expect(testO.enumFieldAllOf, NumericAndSpacedEnum.n667); + }); + test("generating 2d array in a correct way", () { + const json = { + "coordinates": [ + [-27.6307582, 153.0401564], + [37.4220656, -122.0862784], + ] + }; + final geometry = PointGeometry.fromJson(json); + expect(geometry.coordinates, hasLength(2)); + final firstPair = geometry.coordinates.first; + expect(firstPair, hasLength(2)); + expect(firstPair[0], -27.6307582); + expect(firstPair[1], 153.0401564); }); } + +const _dummyDiscriminatorJson = r""" +[ + { + "type": "apple", + "kind": "Foxwhelp" + }, + { + "type": "banana", + "count": 42 + } +] +"""; diff --git a/src/test/resources/integration_test.yaml b/src/test/resources/integration_test.yaml index 1fe3c5d..fb9d259 100644 --- a/src/test/resources/integration_test.yaml +++ b/src/test/resources/integration_test.yaml @@ -270,6 +270,24 @@ paths: $ref: "#/components/schemas/com.bluetrainsoftware.AddProps2" components: schemas: + ObbjectWithDeliberatelyNullableArray: + type: object + properties: + myNullableInt: + type: integer + nullable: true + myInt: + type: integer + nullable: true + myNullableArray: + type: array + nullable: true + items: + type: string + myArray: + type: array + items: + type: string GeocodedWaypoint: type: object description: GeocodedWaypoint @@ -377,6 +395,7 @@ components: int64Int: type: integer format: int64 + default: 0 basicDouble: type: number format: double @@ -390,6 +409,7 @@ components: type: integer doubleList: type: array + default: [] items: type: number format: double