[BUG][Dart] Incorrectly handling of `type: string` combined with `format: decimal`
Bug Report Checklist
- [x] Have you provided a full/minimal spec to reproduce the issue?
- [x] Have you validated the input using an OpenAPI validator (example)?
- [x] Have you tested with the latest master to confirm the issue still exists?
- [x] Have you searched for related issues/PRs?
- [x] What's the actual output vs expected output?
- [ ] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
The Dart generator maps properties with type: string and format: decimal as doubles when parsing JSON. The API returns a string, as described in the schema. Given the type mismatch, the mapValueOfType helper method sets the field to null.
If the field is required, a runtime error occurs because the fromJson method uses the bang operator (!) to assert the field is not null but mapValueOfType returns null.
openapi-generator version
The incorrect mapping of decimal to double has existed since at least 5.4.0. The runtime issue appeared in 6.0.0 with the introduction of the bang operator (!).
I am currently using 6.2.1.
OpenAPI declaration file content or url
https://raw.githubusercontent.com/alpacahq/bkdocs/564d49f52bc91bb180b15b2b900253a1e7c53846/assets/openapi.yaml
Generation Details
java -jar openapi-generator-cli.jar generate -i https://raw.githubusercontent.com/alpacahq/bkdocs/564d49f52bc91bb180b15b2b900253a1e7c53846/assets/openapi.yaml -g dart --global-property apiDocs=false,modelDocs=false -o tmp
Steps to reproduce
- Generate the client library.
- Note the use of
lastEquity: mapValueOfType<double>(json, r'last_equity')intmp/lib/model/account_extended.dart. - Call
AccountExtended.fromJson()withlast_equityset to a string value. - Observe that the return value for
lastEquityisnull.
Related issues/PRs
https://github.com/OpenAPITools/openapi-generator/issues/10836
Suggest a fix
Consider using an actual Decimal type when parsing decimal values: https://pub.dev/packages/decimal.
@jaumard (2018/09) @josh-burton (2019/12) @amondnet (2019/12) @sbu-WBT (2020/12) @kuhnroyal (2020/12) @agilob (2020/12) @ahmednfwela (2021/08)
reproduced as well.
If a field has type: string and format: decimal, then the resulting struct field gets type float64 assigned.
This was blocking me on a project but I managed to get it mostly working. There's a lingering bug in how it handles required decimal fields -- the generated code uses Decimal.from_json(...)! but the ! is unnecessary (it's easy enough to remove manually). I'm not sure I'll have time to finish out a formal PR but wanted to share the patch at least for others who come across this so they can get something workable.
index 5bf7fd5df30..06e23c383c0 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java
@@ -148,7 +148,7 @@ public abstract class AbstractDartCodegen extends DefaultCodegen {
typeMapping.put("number", "num");
typeMapping.put("float", "double");
typeMapping.put("double", "double");
- typeMapping.put("decimal", "double");
+ typeMapping.put("decimal", "Decimal");
typeMapping.put("integer", "int");
typeMapping.put("Date", "DateTime");
typeMapping.put("date", "DateTime");
@@ -168,6 +168,7 @@ public abstract class AbstractDartCodegen extends DefaultCodegen {
"int",
"num",
"double",
+ "Decimal",
"List",
"Set",
"Map",
@@ -183,6 +184,7 @@ public abstract class AbstractDartCodegen extends DefaultCodegen {
imports.put("List", "dart:core");
imports.put("Set", "dart:core");
imports.put("Map", "dart:core");
+ imports.put("Decimal", "package:decimal/decimal.dart");
imports.put("DateTime", "dart:core");
imports.put("Object", "dart:core");
imports.put("MultipartFile", "package:http/http.dart");
diff --git a/modules/openapi-generator/src/main/resources/dart2/apilib.mustache b/modules/openapi-generator/src/main/resources/dart2/apilib.mustache
index 1b1898d88dd..c796ea1a82a 100644
--- a/modules/openapi-generator/src/main/resources/dart2/apilib.mustache
+++ b/modules/openapi-generator/src/main/resources/dart2/apilib.mustache
@@ -6,6 +6,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
+import 'package:decimal/decimal.dart';
import 'package:http/http.dart';
import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
diff --git a/modules/openapi-generator/src/main/resources/dart2/pubspec.mustache b/modules/openapi-generator/src/main/resources/dart2/pubspec.mustache
index af522867b03..b77b10615b0 100644
--- a/modules/openapi-generator/src/main/resources/dart2/pubspec.mustache
+++ b/modules/openapi-generator/src/main/resources/dart2/pubspec.mustache
@@ -19,6 +19,7 @@ dependencies:
http: '>=0.13.0 <0.14.0'
intl: any
meta: '^1.1.8'
+ decimal: '^2.3.3'
dev_dependencies:
test: '>=1.21.6 <1.22.0'
{{#json_serializable}}
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java
index 03848b4f802..b31c0ac2596 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java
@@ -100,8 +100,8 @@ public class DartModelTest {
final CodegenProperty property6 = cm.vars.get(5);
Assert.assertEquals(property6.baseName, "decimal");
- Assert.assertEquals(property6.dataType, "double");
- Assert.assertEquals(property6.baseType, "double");
+ Assert.assertEquals(property6.dataType, "Decimal");
+ Assert.assertEquals(property6.baseType, "Decimal");
}
@Test(description = "convert a model with list property")