Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ public AbstractDartCodegen() {
"bool",
"int",
"num",
"double"
"double",
"Object"
);

typeMapping = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,14 @@ class {{{classname}}} {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "{{{classname}}}[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "{{{classname}}}[$key]" has a null value in JSON.');
});
{{#vars}}
{{#required}}
assert(json.containsKey(r'{{{baseName}}}'), 'Required key "{{{classname}}}[{{{baseName}}}]" is missing from JSON.');
{{^isNullable}}
assert(json[r'{{{baseName}}}'] != null, 'Required key "{{{classname}}}[{{{baseName}}}]" has a null value in JSON.');
{{/isNullable}}
{{/required}}
{{/vars}}
return true;
}());

Expand Down Expand Up @@ -221,7 +225,7 @@ class {{{classname}}} {
{{{name}}}: mapValueOfType<{{{datatypeWithEnum}}}>(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}},
{{/isEnum}}
{{#isEnum}}
{{{name}}}: {{{enumName}}}.fromJson(json[r'{{{baseName}}}']){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}},
{{{name}}}: {{{enumName}}}.fromJson(json[r'{{{baseName}}}']){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? const {{{enumName}}}._({{{.}}}){{/defaultValue}}{{/required}},
{{/isEnum}}
{{/isNumber}}
{{/isMap}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@

package org.openapitools.codegen.dart;

import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.languages.DartClientCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -96,4 +99,74 @@ public void testEnumPropertyWithQuotes() {
Assert.assertEquals(codegen.toEnumValue("1.0", "number"), "1.0");
Assert.assertEquals(codegen.toEnumValue("1", "int"), "1");
}

private List<File> generateDartNativeFromSpec(String specPath) throws Exception {
File output = Files.createTempDirectory("dart-native-test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("dart")
.setInputSpec(specPath)
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));

ClientOptInput opts = configurator.toClientOptInput();
DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(opts).generate();
files.forEach(File::deleteOnExit);
return files;
}

@Test(description = "Object-typed arrays should not produce Object.listFromJson()")
public void testObjectArrayDoesNotUseListFromJson() throws Exception {
List<File> files = generateDartNativeFromSpec(
"src/test/resources/3_0/dart/dart-native-deserialization-bugs.yaml");

File modelFile = files.stream()
.filter(f -> f.getName().equals("object_array_model.dart"))
.findFirst()
.orElseThrow(() -> new AssertionError("object_array_model.dart not found in generated files"));

// Object is a primitive type and has no listFromJson — should use cast<Object>() instead
TestUtils.assertFileNotContains(modelFile.toPath(), "Object.listFromJson");
TestUtils.assertFileContains(modelFile.toPath(), "cast<Object>");
}

@Test(description = "Enum properties with defaults should emit enum constructor, not string literal")
public void testEnumDefaultUsesEnumConstructor() throws Exception {
List<File> files = generateDartNativeFromSpec(
"src/test/resources/3_0/dart/dart-native-deserialization-bugs.yaml");

File modelFile = files.stream()
.filter(f -> f.getName().equals("enum_default_model.dart"))
.findFirst()
.orElseThrow(() -> new AssertionError("enum_default_model.dart not found in generated files"));

// Default should use const EnumType._('value'), not a bare string literal
TestUtils.assertFileNotContains(modelFile.toPath(), "?? 'active'");
TestUtils.assertFileContains(modelFile.toPath(),
"?? const EnumDefaultModelStatusEnum._('active')");
}

@Test(description = "Required+nullable fields should not assert non-null")
public void testRequiredNullableFieldsDoNotAssertNonNull() throws Exception {
List<File> files = generateDartNativeFromSpec(
"src/test/resources/3_0/dart/dart-native-deserialization-bugs.yaml");

File modelFile = files.stream()
.filter(f -> f.getName().equals("nullable_required_model.dart"))
.findFirst()
.orElseThrow(() -> new AssertionError("nullable_required_model.dart not found in generated files"));

// Required key 'name' (non-nullable) should assert both presence and non-null
TestUtils.assertFileContains(modelFile.toPath(),
"json.containsKey(r'name')");
TestUtils.assertFileContains(modelFile.toPath(),
"json[r'name'] != null");

// Required key 'nickname' (nullable) should assert presence but NOT non-null
TestUtils.assertFileContains(modelFile.toPath(),
"json.containsKey(r'nickname')");
TestUtils.assertFileNotContains(modelFile.toPath(),
"json[r'nickname'] != null");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
openapi: 3.0.0
info:
title: Dart Native Deserialization Bugs
description: Test spec for verifying fixes to Dart native serialization codegen bugs.
version: 1.0.0
paths: {}
components:
schemas:
ObjectArrayModel:
type: object
properties:
items:
type: array
items: {}
EnumDefaultModel:
type: object
properties:
status:
type: string
enum:
- active
- inactive
default: active
NullableRequiredModel:
type: object
required:
- name
- nickname
properties:
name:
type: string
nickname:
type: string
nullable: true
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,6 @@ class ApiResponse {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ApiResponse[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ApiResponse[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ class Category {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Category[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Category[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ class Order {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Order[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Order[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ class Pet {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Pet[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Pet[$key]" has a null value in JSON.');
});
assert(json.containsKey(r'name'), 'Required key "Pet[name]" is missing from JSON.');
assert(json[r'name'] != null, 'Required key "Pet[name]" has a null value in JSON.');
assert(json.containsKey(r'photoUrls'), 'Required key "Pet[photoUrls]" is missing from JSON.');
assert(json[r'photoUrls'] != null, 'Required key "Pet[photoUrls]" has a null value in JSON.');
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ class Tag {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Tag[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Tag[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,6 @@ class User {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "User[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "User[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ Name | Type | Description | Notes
**stringProp** | **String** | | [optional]
**dateProp** | [**DateTime**](DateTime.md) | | [optional]
**datetimeProp** | [**DateTime**](DateTime.md) | | [optional]
**arrayNullableProp** | [**List<Object>**](Object.md) | | [optional] [default to const []]
**arrayAndItemsNullableProp** | [**List<Object>**](Object.md) | | [optional] [default to const []]
**arrayItemsNullable** | [**List<Object>**](Object.md) | | [optional] [default to const []]
**objectNullableProp** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
**objectAndItemsNullableProp** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
**objectItemsNullable** | [**Map<String, Object>**](Object.md) | | [optional] [default to const {}]
**arrayNullableProp** | **List<Object>** | | [optional] [default to const []]
**arrayAndItemsNullableProp** | **List<Object>** | | [optional] [default to const []]
**arrayItemsNullable** | **List<Object>** | | [optional] [default to const []]
**objectNullableProp** | **Map<String, Object>** | | [optional] [default to const {}]
**objectAndItemsNullableProp** | **Map<String, Object>** | | [optional] [default to const {}]
**objectItemsNullable** | **Map<String, Object>** | | [optional] [default to const {}]

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ class AdditionalPropertiesClass {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "AdditionalPropertiesClass[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "AdditionalPropertiesClass[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ class AllOfWithSingleRef {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "AllOfWithSingleRef[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "AllOfWithSingleRef[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ class Animal {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Animal[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Animal[$key]" has a null value in JSON.');
});
assert(json.containsKey(r'className'), 'Required key "Animal[className]" is missing from JSON.');
assert(json[r'className'] != null, 'Required key "Animal[className]" has a null value in JSON.');
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,6 @@ class ApiResponse {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ApiResponse[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ApiResponse[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ class ArrayOfArrayOfNumberOnly {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ArrayOfArrayOfNumberOnly[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ArrayOfArrayOfNumberOnly[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ class ArrayOfNumberOnly {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ArrayOfNumberOnly[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ArrayOfNumberOnly[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ class ArrayTest {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ArrayTest[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ArrayTest[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,6 @@ class Capitalization {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Capitalization[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Capitalization[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ class Cat {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Cat[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Cat[$key]" has a null value in JSON.');
});
assert(json.containsKey(r'className'), 'Required key "Cat[className]" is missing from JSON.');
assert(json[r'className'] != null, 'Required key "Cat[className]" has a null value in JSON.');
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@ class Category {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "Category[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "Category[$key]" has a null value in JSON.');
});
assert(json.containsKey(r'name'), 'Required key "Category[name]" is missing from JSON.');
assert(json[r'name'] != null, 'Required key "Category[name]" has a null value in JSON.');
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ class ChildWithNullable {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ChildWithNullable[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ChildWithNullable[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ class ClassModel {
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "ClassModel[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "ClassModel[$key]" has a null value in JSON.');
});
return true;
}());

Expand Down
Loading
Loading