Skip to content

Commit 54309ca

Browse files
committed
fix(dart): preserve nullable nested array item types and decoding
1 parent d6c2905 commit 54309ca

13 files changed

Lines changed: 123 additions & 32 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,17 @@ public String toDefaultValue(Schema schema) {
524524

525525
@Override
526526
public String getTypeDeclaration(Schema p) {
527+
return getTypeDeclaration(p, false);
528+
}
529+
530+
private String getTypeDeclaration(Schema p, boolean includeNullableSuffix) {
527531
Schema<?> schema = unaliasSchema(p);
528532
Schema<?> target = ModelUtils.isGenerateAliasAsModel() ? p : schema;
533+
String typeDeclaration;
529534
if (ModelUtils.isArraySchema(target)) {
530535
Schema<?> items = ModelUtils.getSchemaItems(schema);
531-
return getSchemaType(target) + "<" + getTypeDeclaration(items) + ">";
532-
}
533-
if (ModelUtils.isMapSchema(target)) {
536+
typeDeclaration = getSchemaType(target) + "<" + getTypeDeclaration(items, true) + ">";
537+
} else if (ModelUtils.isMapSchema(target)) {
534538
// Note: ModelUtils.isMapSchema(p) returns true when p is a composed schema that also defines
535539
// additionalproperties: true
536540
Schema<?> inner = ModelUtils.getAdditionalProperties(target);
@@ -539,9 +543,16 @@ public String getTypeDeclaration(Schema p) {
539543
inner = new StringSchema().description("TODO default missing map inner type to string");
540544
p.setAdditionalProperties(inner);
541545
}
542-
return getSchemaType(target) + "<String, " + getTypeDeclaration(inner) + ">";
546+
typeDeclaration = getSchemaType(target) + "<String, " + getTypeDeclaration(inner, true) + ">";
547+
} else {
548+
typeDeclaration = super.getTypeDeclaration(p);
543549
}
544-
return super.getTypeDeclaration(p);
550+
551+
if (includeNullableSuffix && ModelUtils.isNullable(schema)) {
552+
return typeDeclaration + "?";
553+
}
554+
555+
return typeDeclaration;
545556
}
546557

547558
@Override

modules/openapi-generator/src/main/resources/dart2/serialization/native/native_class.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,10 @@ class {{{classname}}} {
142142
{{{name}}}: json[r'{{{baseName}}}'] is List
143143
? (json[r'{{{baseName}}}'] as List).map((e) =>
144144
{{#items.complexType}}
145-
{{items.complexType}}.listFromJson(json[r'{{{baseName}}}']){{#uniqueItems}}.toSet(){{/uniqueItems}}
145+
{{items.complexType}}.listFromJson(e){{#uniqueItems}}.toSet(){{/uniqueItems}}
146146
{{/items.complexType}}
147147
{{^items.complexType}}
148-
e == null ? {{#items.isNullable}}null{{/items.isNullable}}{{^items.isNullable}}const <{{items.items.dataType}}>[]{{/items.isNullable}} : (e as List).cast<{{items.items.dataType}}>()
148+
e == null ? {{#items.isNullable}}null{{/items.isNullable}}{{^items.isNullable}}const <{{items.items.dataType}}{{#items.items.isNullable}}?{{/items.items.isNullable}}>[]{{/items.isNullable}} : (e as List).map((value) => value as {{items.items.dataType}}{{#items.items.isNullable}}?{{/items.items.isNullable}}).toList(growable: false)
149149
{{/items.complexType}}
150150
).toList()
151151
: {{#isNullable}}null{{/isNullable}}{{^isNullable}}const []{{/isNullable}},
@@ -163,7 +163,7 @@ class {{{classname}}} {
163163
: {{items.complexType}}.mapListFromJson(json[r'{{{baseName}}}']),
164164
{{/items.complexType}}
165165
{{^items.complexType}}
166-
: (json[r'{{{baseName}}}'] as Map<String, dynamic>).map((k, v) => MapEntry(k, v == null ? {{#items.isNullable}}null{{/items.isNullable}}{{^items.isNullable}}const <{{items.items.dataType}}>[]{{/items.isNullable}} : (v as List).cast<{{items.items.dataType}}>())),
166+
: (json[r'{{{baseName}}}'] as Map<String, dynamic>).map((k, v) => MapEntry(k, v == null ? {{#items.isNullable}}null{{/items.isNullable}}{{^items.isNullable}}const <{{items.items.dataType}}{{#items.items.isNullable}}?{{/items.items.isNullable}}>[]{{/items.isNullable}} : (v as List).map((value) => value as {{items.items.dataType}}{{#items.items.isNullable}}?{{/items.items.isNullable}}).toList(growable: false))),
167167
{{/items.complexType}}
168168
{{/items.isArray}}
169169
{{^items.isArray}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,28 @@ public void dateTest() {
564564
Assert.assertEquals(op.returnType, "DateTime");
565565
Assert.assertEquals(op.bodyParam.dataType, "DateTime");
566566
}
567+
568+
@Test(description = "array items can be nullable")
569+
public void arrayItemsCanBeNullable() {
570+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/array-nullable-items.yaml");
571+
final DefaultCodegen codegen = new DartClientCodegen();
572+
codegen.setOpenAPI(openAPI);
573+
final ArraySchema schema = (ArraySchema) openAPI.getComponents().getSchemas().get("ArrayWithNullableItemsModel")
574+
.getProperties()
575+
.get("foo");
576+
577+
Assert.assertEquals(codegen.getTypeDeclaration(schema), "List<String?>");
578+
}
579+
580+
@Test(description = "nested array items can be nullable")
581+
public void nestedArrayItemsCanBeNullable() {
582+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/nested-array-nullable-items.yaml");
583+
final DefaultCodegen codegen = new DartClientCodegen();
584+
codegen.setOpenAPI(openAPI);
585+
final ArraySchema schema = (ArraySchema) openAPI.getComponents().getSchemas().get("NestedArrayWithNullableItemsModel")
586+
.getProperties()
587+
.get("foo");
588+
589+
Assert.assertEquals(codegen.getTypeDeclaration(schema), "List<List<String?>>");
590+
}
567591
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/dio/DartDioModelTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,4 +492,32 @@ public void dateDefaultValues() {
492492
Assert.assertEquals(dateTimeDefault.name, "dateTime");
493493
Assert.assertNull(dateTimeDefault.defaultValue);
494494
}
495+
496+
@Test(description = "array items can be nullable")
497+
public void arrayItemsCanBeNullable() {
498+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/array-nullable-items.yaml");
499+
final DartDioClientCodegen codegen = new DartDioClientCodegen();
500+
codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE);
501+
codegen.processOpts();
502+
codegen.setOpenAPI(openAPI);
503+
final ArraySchema schema = (ArraySchema) openAPI.getComponents().getSchemas().get("ArrayWithNullableItemsModel")
504+
.getProperties()
505+
.get("foo");
506+
507+
Assert.assertEquals(codegen.getTypeDeclaration(schema), "BuiltList<String?>");
508+
}
509+
510+
@Test(description = "nested array items can be nullable")
511+
public void nestedArrayItemsCanBeNullable() {
512+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/nested-array-nullable-items.yaml");
513+
final DartDioClientCodegen codegen = new DartDioClientCodegen();
514+
codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE);
515+
codegen.processOpts();
516+
codegen.setOpenAPI(openAPI);
517+
final ArraySchema schema = (ArraySchema) openAPI.getComponents().getSchemas().get("NestedArrayWithNullableItemsModel")
518+
.getProperties()
519+
.get("foo");
520+
521+
Assert.assertEquals(codegen.getTypeDeclaration(schema), "BuiltList<BuiltList<String?>>");
522+
}
495523
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Nested array nullable items
4+
version: latest
5+
paths:
6+
'/':
7+
get:
8+
operationId: operation
9+
responses:
10+
'200':
11+
description: Success
12+
content:
13+
application/json:
14+
schema:
15+
$ref: '#/components/schemas/NestedArrayWithNullableItemsModel'
16+
components:
17+
schemas:
18+
NestedArrayWithNullableItemsModel:
19+
required:
20+
- foo
21+
properties:
22+
foo:
23+
type: array
24+
items:
25+
type: array
26+
items:
27+
type: string
28+
nullable: true

samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/doc/NullableClass.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ Name | Type | Description | Notes
1515
**dateProp** | [**DateTime**](DateTime.md) | | [optional]
1616
**datetimeProp** | [**DateTime**](DateTime.md) | | [optional]
1717
**arrayNullableProp** | **List&lt;Object&gt;** | | [optional]
18-
**arrayAndItemsNullableProp** | **List&lt;Object&gt;** | | [optional]
19-
**arrayItemsNullable** | **List&lt;Object&gt;** | | [optional]
18+
**arrayAndItemsNullableProp** | **List&lt;Object?&gt;** | | [optional]
19+
**arrayItemsNullable** | **List&lt;Object?&gt;** | | [optional]
2020
**objectNullableProp** | **Map&lt;String, Object&gt;** | | [optional]
21-
**objectAndItemsNullableProp** | **Map&lt;String, Object&gt;** | | [optional]
22-
**objectItemsNullable** | **Map&lt;String, Object&gt;** | | [optional]
21+
**objectAndItemsNullableProp** | **Map&lt;String, Object?&gt;** | | [optional]
22+
**objectItemsNullable** | **Map&lt;String, Object?&gt;** | | [optional]
2323

2424
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
2525

samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake-json_serializable/lib/src/model/nullable_class.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class NullableClass {
137137
)
138138

139139

140-
final List<Object>? arrayAndItemsNullableProp;
140+
final List<Object?>? arrayAndItemsNullableProp;
141141

142142

143143

@@ -149,7 +149,7 @@ class NullableClass {
149149
)
150150

151151

152-
final List<Object>? arrayItemsNullable;
152+
final List<Object?>? arrayItemsNullable;
153153

154154

155155

@@ -173,7 +173,7 @@ class NullableClass {
173173
)
174174

175175

176-
final Map<String, Object>? objectAndItemsNullableProp;
176+
final Map<String, Object?>? objectAndItemsNullableProp;
177177

178178

179179

@@ -185,7 +185,7 @@ class NullableClass {
185185
)
186186

187187

188-
final Map<String, Object>? objectItemsNullable;
188+
final Map<String, Object?>? objectItemsNullable;
189189

190190

191191

samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/doc/NullableClass.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ Name | Type | Description | Notes
1515
**dateProp** | [**Date**](Date.md) | | [optional]
1616
**datetimeProp** | [**DateTime**](DateTime.md) | | [optional]
1717
**arrayNullableProp** | [**BuiltList&lt;JsonObject&gt;**](JsonObject.md) | | [optional]
18-
**arrayAndItemsNullableProp** | [**BuiltList&lt;JsonObject&gt;**](JsonObject.md) | | [optional]
19-
**arrayItemsNullable** | [**BuiltList&lt;JsonObject&gt;**](JsonObject.md) | | [optional]
18+
**arrayAndItemsNullableProp** | [**BuiltList&lt;JsonObject?&gt;**](JsonObject.md) | | [optional]
19+
**arrayItemsNullable** | [**BuiltList&lt;JsonObject?&gt;**](JsonObject.md) | | [optional]
2020
**objectNullableProp** | [**BuiltMap&lt;String, JsonObject&gt;**](JsonObject.md) | | [optional]
21-
**objectAndItemsNullableProp** | [**BuiltMap&lt;String, JsonObject&gt;**](JsonObject.md) | | [optional]
22-
**objectItemsNullable** | [**BuiltMap&lt;String, JsonObject&gt;**](JsonObject.md) | | [optional]
21+
**objectAndItemsNullableProp** | [**BuiltMap&lt;String, JsonObject?&gt;**](JsonObject.md) | | [optional]
22+
**objectItemsNullable** | [**BuiltMap&lt;String, JsonObject?&gt;**](JsonObject.md) | | [optional]
2323

2424
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
2525

samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/doc/NullableClass.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ Name | Type | Description | Notes
1515
**dateProp** | [**DateTime**](DateTime.md) | | [optional]
1616
**datetimeProp** | [**DateTime**](DateTime.md) | | [optional]
1717
**arrayNullableProp** | [**List<Object>**](Object.md) | | [optional] [default to const []]
18-
**arrayAndItemsNullableProp** | [**List<Object>**](Object.md) | | [optional] [default to const []]
19-
**arrayItemsNullable** | [**List<Object>**](Object.md) | | [optional] [default to const []]
18+
**arrayAndItemsNullableProp** | [**List<Object?>**](Object.md) | | [optional] [default to const []]
19+
**arrayItemsNullable** | [**List<Object?>**](Object.md) | | [optional] [default to const []]
2020
**objectNullableProp** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
21-
**objectAndItemsNullableProp** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
22-
**objectItemsNullable** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
21+
**objectAndItemsNullableProp** | [**Map<String, Object?>**](Object.md) | | [optional] [default to const {}]
22+
**objectItemsNullable** | [**Map<String, Object?>**](Object.md) | | [optional] [default to const {}]
2323

2424
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
2525

samples/openapi3/client/petstore/dart2/petstore_client_lib_fake/lib/model/additional_properties_class.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class AdditionalPropertiesClass {
7171
mapOfMapProperty: mapCastOfType<String, dynamic>(json, r'map_of_map_property') ?? const {},
7272
mapOfArrayInteger: json[r'map_of_array_integer'] == null
7373
? const {}
74-
: (json[r'map_of_array_integer'] as Map<String, dynamic>).map((k, v) => MapEntry(k, v == null ? const <int>[] : (v as List).cast<int>())),
74+
: (json[r'map_of_array_integer'] as Map<String, dynamic>).map((k, v) => MapEntry(k, v == null ? const <int>[] : (v as List).map((value) => value as int).toList(growable: false))),
7575
);
7676
}
7777
return null;

0 commit comments

Comments
 (0)