Skip to content

Commit 357d4f0

Browse files
authored
OpenAPI optionals/nulls/num casting (#85)
* phase 3 - 1 optionals are null again by default * phase 3 - 2 ensure required fields act properly * phase 3 - complete test fixing and minor issue with non-nullsafe Dart * phase 3 - beginning of dealing with num casting this is to fix #70 * phase 3 - maps of double/int now supported further fixes for #70 * phase 3 - maps of double/int now supported basic types now convert for ints and doubles
1 parent e8f26d4 commit 357d4f0

26 files changed

+551
-105
lines changed

README.adoc

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
This plugin was originally designed for use by the OpenAPi v3 Maven Plugin, but works with the
44
command line generator and the Gradle plugin. it generates _excellent_ Dart code and uses Dio.
55

6-
NOTE: we are currently using 5.2.1 of the OpenAPI Maven Plugin and have no plans to update until an issue that is specific to the core crops up that we cannot resolve. The OpenAPI Maven generator updates primarily because it is the only way they can release new versions of individual generators, which we think is not the right way to deal with language support.
6+
NOTE: we are currently using 6.0.1 of the OpenAPI Maven Plugin.
77

88
== Sponsors
99

10-
This project is sponsored by https://www.featurehub.io[FeatureHub].
10+
This project is part of the Open Source project https://www.featurehub.io[FeatureHub], please consider sponsoring that project to support this generator if you use it.
1111

1212
== How to use
1313

@@ -395,12 +395,21 @@ mvn clean verify
395395
The source for the tests is located in src/k8s** folders. The generated test output will be in target/it/k8s**.
396396

397397
==== Changelog
398-
- 6.1 - migrate to the *6.0.1* version of OpenAPI, move to Java 11, swap to Kotlin. This is advertised as a breaking change and has led to some classes changing their names, adding new classes which didn't exist before.
398+
- 6.1 - migrate to the *6.0.1* version of OpenAPI, move to Java 11, swap to Kotlin. This is advertised as a breaking change and has led to some classes changing their names, adding new classes which didn't exist before. This is
399+
a significant rewrite and retest of the functionality so we encourage people
400+
to report bugs and issues to us.
399401
* fields that are marked as `required` in the OpenAPI but have default values are not required in constructors (Dart fields with default values cannot be required)
400-
* previously `required` = non-null, now nullable = non-null, required can be null, it just means it has
401-
to be sent in the data. To revert to 5.x behaviour where !required = nullable, add an additional property
402-
of `x-use-5x-nullable=true`. For compatibility with other languages, we recommend you do not stick
403-
with this for long.
402+
* previously `required` = non-null, !required = non-null. This behaviour
403+
has been refined. In Dart terms, it does not make sense for optional fields
404+
to not be nullable, so that remains, but required fields can now be
405+
nullable as long as they have a default value (or are a list or map).
406+
* required fields are now _required_ - so if they aren't in the incoming JSON
407+
data we will throw a serialisation error. Optional fields if they are empty
408+
or null will not be sent (unless the `generateNullValuesToJson` additional
409+
property is used, in which case fields will always be be added).
410+
* numeric fields are no longer "cast", they are always converted to their
411+
respective variants, toInt() or toDouble() - down through lists and so forth.
412+
404413
- 5.13 - ability to disable the copyWith generation (see above)
405414
- 5.12 - contributed fixes for inherited types (via https://github.com/roald-di)
406415
- 5.11 - fix date/datetime strings in queries to not be encoded. Updated to use 5.2.1 of OpenAPI. Fixed a bunch

sample-app/SampleRunner/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.idea
2+
.openapi-generator-ignore
3+
.openapi-generator
4+
lib
5+
pubspec.yaml
6+
.packages
7+
pubspec.lock
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import 'dart:convert';
2+
3+
import 'package:sample_app/api.dart';
4+
import 'package:test/test.dart';
5+
6+
main() {
7+
test('import conversion and compare works for issue-19', () {
8+
const waypoint = {
9+
"geocoderStatus": "OK",
10+
"partialMatch": true,
11+
"placeId": "place-id",
12+
"types": ["STREET_ADDRESS", "STREET_NUMBER"]
13+
};
14+
var w = GeocodedWaypoint.fromJson(waypoint);
15+
expect(w.geocoderStatus, GeocodedWaypointGeocoderStatusEnum.OK);
16+
expect(w.partialMatch, true);
17+
expect(w.placeId, "place-id");
18+
expect(w.types,
19+
[GeocodedWaypointTypesEnum.ADDRESS, GeocodedWaypointTypesEnum.NUMBER]);
20+
var x = w.copyWith();
21+
expect(w, x);
22+
expect(true, w == x);
23+
print(w);
24+
print(x);
25+
print("codes ${w.hashCode} vs ${x.hashCode}");
26+
print("empty array -> ${[].hashCode}");
27+
expect(true, w.hashCode == x.hashCode);
28+
var z = w.copyWith(types: [GeocodedWaypointTypesEnum.ADDRESS]);
29+
expect(false, w == z);
30+
expect(false, w.hashCode == z.hashCode);
31+
var encodeDecode = LocalApiClient.deserialize(
32+
LocalApiClient.serialize(w), 'GeocodedWaypoint');
33+
expect(encodeDecode, w);
34+
});
35+
36+
// previous hashing mechanism if you swapped the adjacent field values
37+
// it wouldn't change the hash
38+
test(
39+
'hashing an object which has two fields of the same type is still different',
40+
() {
41+
var ht = HashTest()
42+
..fieldOne = false
43+
..fieldTwo = true;
44+
var ht1 = HashTest()
45+
..fieldOne = true
46+
..fieldTwo = false;
47+
expect(false, ht.hashCode == ht1.hashCode);
48+
});
49+
50+
test('additional properties mappings', () {
51+
const addProp = {
52+
'discrim': 'fred',
53+
'readings': {'one': 1, 'two': 2.3},
54+
'dependencies': {
55+
'deps1': ['a', 34.2, true],
56+
'deps2': [17.8, false, 'b']
57+
},
58+
'otherDeps': {
59+
'name': ['tom', 'dick', 'harry'],
60+
'height': [1.7, 1.3, 1.4],
61+
'info': 'this is top secret'
62+
},
63+
'yetMoreAdditional': {
64+
'sList': ['a', 'b', 'c']
65+
},
66+
'mapWithComplexObject': {
67+
'c1': [
68+
{
69+
'status': 'STREAMING',
70+
'id': 'xx',
71+
'title': 'Scully',
72+
'img': 'img',
73+
'imageUrl': 'http://blah'
74+
}
75+
]
76+
},
77+
'mapWithEnums': {
78+
"statuses": ['STREAMING', 'CLOSED']
79+
},
80+
};
81+
82+
var ap = AddProps3.fromJson(addProp);
83+
expect(ap.discrim, 'fred');
84+
expect(ap.readings.length, 2);
85+
expect(ap.readings['one'], 1);
86+
expect(ap.readings['two'], 2.3);
87+
expect(ap.dependencies['deps1'], ['a', 34.2, true]);
88+
expect(ap.dependencies['deps2'], [17.8, false, 'b']);
89+
expect(ap.otherDeps['name'], ['tom', 'dick', 'harry']);
90+
expect(ap.otherDeps['height'], [1.7, 1.3, 1.4]);
91+
expect(ap.otherDeps['info'], 'this is top secret');
92+
expect(ap.yetMoreAdditional['sList'], ['a', 'b', 'c']);
93+
expect(
94+
ap.mapWithComplexObject['c1'][0],
95+
Event()
96+
..status = EventStatus.STREAMING
97+
..id = 'xx'
98+
..title = 'Scully'
99+
..img = 'img'
100+
..imageUrl = 'http://blah');
101+
expect(ap.mapWithEnums['statuses'],
102+
[EventStatus.STREAMING, EventStatus.CLOSED]);
103+
});
104+
105+
test("List<AnyOf<MyApple,MyBanana>> - parsing json array with discriminator",
106+
() {
107+
final items =
108+
AnyOfMyAppleMyBanana.listFromJson(jsonDecode(_dummyDiscriminatorJson));
109+
110+
expect(items, hasLength(2));
111+
expect(items[0].discriminator, AnyOfDiscriminatorMyAppleMyBanana.MyApple);
112+
expect(items[0].asMyApple().type, "apple");
113+
expect(items[0].asMyApple().kind, "Foxwhelp");
114+
expect(items[1].discriminator, AnyOfDiscriminatorMyAppleMyBanana.MyBanana);
115+
expect(items[1].asMyBanana().type, "banana");
116+
expect(items[1].asMyBanana().count, 42);
117+
});
118+
test('double with ints in them tests and vs versa', () {
119+
const testData = {
120+
'basicInt': 2.6,
121+
'basicDouble': 1,
122+
'intList': [1, 2.6],
123+
'intMap': {'one': 1, 'two': 2.7},
124+
'doubleList': [1, 2.6],
125+
'doubleMap': {'one': 1, 'two': 2.7},
126+
};
127+
128+
final data = DoubleAndIntConversion.fromJson(testData);
129+
expect(data.basicDouble, 1.0);
130+
expect(data.basicInt, 2);
131+
expect(data.intList, [1, 2]);
132+
expect(data.intMap, {'one': 1, 'two': 2});
133+
expect(data.doubleList, [1.0, 2.6]);
134+
expect(data.doubleMap, {'one': 1.0, 'two': 2.7});
135+
});
136+
test("int enums being generated with correct type", () {
137+
expect(IntTypeEnum.number1.toJson(), 1);
138+
expect(IntTypeEnum.number1, IntTypeEnumExtension.fromJson(1));
139+
});
140+
test(
141+
"enums included in a model via allOf with reference will be treated as"
142+
"enums and generate valid code ", () {
143+
final testO = ObjectContainingEnum.fromJson(
144+
{"name": "foobar", "enumFieldAllOf": "667"});
145+
expect(testO.name, "foobar");
146+
expect(testO.enumFieldAllOf, NumericAndSpacedEnum.n667);
147+
});
148+
test("generating 2d array in a correct way", () {
149+
const json = {
150+
"coordinates": [
151+
[-27.6307582, 153.0401564],
152+
[37.4220656, -122.0862784],
153+
]
154+
};
155+
final geometry = PointGeometry.fromJson(json);
156+
expect(geometry.coordinates, hasLength(2));
157+
final firstPair = geometry.coordinates.first;
158+
expect(firstPair, hasLength(2));
159+
expect(firstPair[0], -27.6307582);
160+
expect(firstPair[1], 153.0401564);
161+
});
162+
}
163+
164+
const _dummyDiscriminatorJson = r"""
165+
[
166+
{
167+
"type": "apple",
168+
"kind": "Foxwhelp"
169+
},
170+
{
171+
"type": "banana",
172+
"count": 42
173+
}
174+
]
175+
""";

src/it/k8s/test.yaml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,35 @@ components:
345345
- CLOSED
346346
- PENDING
347347
- ARCHIVING
348+
DoubleAndIntConversion:
349+
type: object
350+
required:
351+
- basicDouble
352+
- basicInt
353+
properties:
354+
basicInt:
355+
type: integer
356+
basicDouble:
357+
type: number
358+
format: double
359+
intList:
360+
type: array
361+
items:
362+
type: integer
363+
intMap:
364+
type: object
365+
additionalProperties:
366+
type: integer
367+
doubleList:
368+
type: array
369+
items:
370+
type: number
371+
format: double
372+
doubleMap:
373+
type: object
374+
additionalProperties:
375+
type: number
376+
format: double
348377
"com.bluetrainsoftware.AddProps1":
349378
properties:
350379
extra:
@@ -360,6 +389,15 @@ components:
360389
items:
361390
type: integer
362391
type: array
392+
mapOfIntegers:
393+
type: object
394+
additionalProperties:
395+
type: integer
396+
mapOfDoubles:
397+
type: object
398+
additionalProperties:
399+
type: number
400+
format: double
363401
dependencies:
364402
type: object
365403
additionalProperties:
@@ -571,8 +609,8 @@ components:
571609
nullable: true
572610
enum:
573611
- Ahoy There
574-
- 18 Spaces Here
575612
- application+json
613+
- 18 Spaces Here
576614
- 667
577615
IntTypeEnum:
578616
type: integer

src/it/k8s/test/api_tests.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,24 @@ main() {
115115
expect(items[1].asMyBanana().type, "banana");
116116
expect(items[1].asMyBanana().count, 42);
117117
});
118+
test('double with ints in them tests and vs versa', () {
119+
const testData = {
120+
'basicInt': 2.6,
121+
'basicDouble': 1,
122+
'intList': [1, 2.6],
123+
'intMap': {'one': 1, 'two': 2.7},
124+
'doubleList': [1, 2.6],
125+
'doubleMap': {'one': 1, 'two': 2.7},
126+
};
127+
128+
final data = DoubleAndIntConversion.fromJson(testData);
129+
expect(data.basicDouble, 1.0);
130+
expect(data.basicInt, 2);
131+
expect(data.intList, [1, 2]);
132+
expect(data.intMap, {'one': 1, 'two': 2});
133+
expect(data.doubleList, [1.0, 2.6]);
134+
expect(data.doubleMap, {'one': 1.0, 'two': 2.7});
135+
});
118136
test("int enums being generated with correct type", () {
119137
expect(IntTypeEnum.number1.toJson(), 1);
120138
expect(IntTypeEnum.number1, IntTypeEnumExtension.fromJson(1));

src/it/k8s_null/test.yaml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,35 @@ components:
345345
- CLOSED
346346
- PENDING
347347
- ARCHIVING
348+
DoubleAndIntConversion:
349+
type: object
350+
required:
351+
- basicDouble
352+
- basicInt
353+
properties:
354+
basicInt:
355+
type: integer
356+
basicDouble:
357+
type: number
358+
format: double
359+
intList:
360+
type: array
361+
items:
362+
type: integer
363+
intMap:
364+
type: object
365+
additionalProperties:
366+
type: integer
367+
doubleList:
368+
type: array
369+
items:
370+
type: number
371+
format: double
372+
doubleMap:
373+
type: object
374+
additionalProperties:
375+
type: number
376+
format: double
348377
"com.bluetrainsoftware.AddProps1":
349378
properties:
350379
extra:
@@ -360,6 +389,15 @@ components:
360389
items:
361390
type: integer
362391
type: array
392+
mapOfIntegers:
393+
type: object
394+
additionalProperties:
395+
type: integer
396+
mapOfDoubles:
397+
type: object
398+
additionalProperties:
399+
type: number
400+
format: double
363401
dependencies:
364402
type: object
365403
additionalProperties:
@@ -571,8 +609,8 @@ components:
571609
nullable: true
572610
enum:
573611
- Ahoy There
574-
- 18 Spaces Here
575612
- application+json
613+
- 18 Spaces Here
576614
- 667
577615
IntTypeEnum:
578616
type: integer

0 commit comments

Comments
 (0)