Skip to content

Commit 03af25c

Browse files
authored
Fix null check in 3.1 spec (#18353)
* fix null check in 3.1 spec * clean up
1 parent ff8fa40 commit 03af25c

File tree

5 files changed

+229
-6
lines changed

5 files changed

+229
-6
lines changed

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ public boolean isNullTypeSchema(Schema schema) {
890890

891891
if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
892892
// 3.1 spec
893-
if (schema.getTypes().size() ==1) { // 1 type only
893+
if (schema.getTypes().size() == 1) { // 1 type only
894894
String type = (String) schema.getTypes().iterator().next();
895895
return type == null || "null".equals(type);
896896
} else { // more than 1 type so must not be just null
@@ -902,6 +902,11 @@ public boolean isNullTypeSchema(Schema schema) {
902902
if (Boolean.TRUE.equals(schema.getNullable())) {
903903
return true;
904904
}
905+
906+
// for `type: null`
907+
if (schema.getTypes() == null && schema.get$ref() == null) {
908+
return true;
909+
}
905910
} else { // 3.0.x or 2.x spec
906911
if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
907912
return true;
@@ -938,7 +943,7 @@ private Schema processSimplifyOneOf(Schema schema) {
938943
if (oneOfSchemas.size() == 6) {
939944
TreeSet<String> ts = new TreeSet<>();
940945
for (Schema s: oneOfSchemas) {
941-
ts.add(s.getType());
946+
ts.add(ModelUtils.getType(s));
942947
}
943948

944949
if (ts.equals(anyTypeTreeSet)) {
@@ -1063,7 +1068,7 @@ private Schema processSimplifyAnyOf(Schema schema) {
10631068
if (anyOfSchemas.size() == 6) {
10641069
TreeSet<String> ts = new TreeSet<>();
10651070
for (Schema s: anyOfSchemas) {
1066-
ts.add(s.getType());
1071+
ts.add(ModelUtils.getType(s));
10671072
}
10681073

10691074
if (ts.equals(anyTypeTreeSet)) {

modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java

+19
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,25 @@ public static boolean hasAnyOf(Schema schema) {
21262126
return false;
21272127
}
21282128

2129+
/**
2130+
* Returns schema type.
2131+
* For 3.1 spec, return the first one.
2132+
*
2133+
* @param schema the schema
2134+
* @return schema type
2135+
*/
2136+
public static String getType(Schema schema) {
2137+
if (schema == null) {
2138+
return null;
2139+
}
2140+
2141+
if (schema instanceof JsonSchema) {
2142+
return String.valueOf(schema.getTypes().iterator().next());
2143+
} else {
2144+
return schema.getType();
2145+
}
2146+
}
2147+
21292148
/**
21302149
* Returns true if any of the common attributes of the schema (e.g. readOnly, default, maximum, etc) is defined.
21312150
*

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

+83-3
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
158158
assertEquals(schema2.getOneOf().size(), 4);
159159
assertNull(schema2.getNullable());
160160

161+
Schema schema2b = openAPI.getComponents().getSchemas().get("OneOfTest2");
162+
assertEquals(schema2b.getOneOf().size(), 2);
163+
assertNull(schema2b.getNullable());
164+
161165
Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
162166
assertEquals(schema5.getOneOf().size(), 3);
163167
assertNull(schema5.getNullable());
@@ -189,6 +193,11 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
189193
assertTrue(schema4 instanceof IntegerSchema);
190194
assertTrue(schema4.getNullable());
191195

196+
Schema schema4b = openAPI.getComponents().getSchemas().get("OneOfTest2");
197+
assertNull(schema4b.getOneOf());
198+
assertTrue(schema4b instanceof StringSchema);
199+
assertTrue(schema4b.getNullable());
200+
192201
Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
193202
assertEquals(schema6.getOneOf().size(), 2);
194203
assertTrue(schema6.getNullable());
@@ -532,7 +541,7 @@ public void testSetContainerToNullable() {
532541
@Test
533542
public void testSetPrimitiveTypesToNullable() {
534543
// test `string|integer|number|boolean`
535-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0//setPrimitiveTypesToNullable_test.yaml");
544+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/setPrimitiveTypesToNullable_test.yaml");
536545

537546
Schema schema = openAPI.getComponents().getSchemas().get("Person");
538547
assertEquals(((Schema) schema.getProperties().get("lastName")).getNullable(), null);
@@ -552,7 +561,7 @@ public void testSetPrimitiveTypesToNullable() {
552561
assertEquals(((Schema) schema2.getProperties().get("first_boolean")).getNullable(), true);
553562

554563
// test `number` only
555-
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0//setPrimitiveTypesToNullable_test.yaml");
564+
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0/setPrimitiveTypesToNullable_test.yaml");
556565

557566
Schema schema3 = openAPI2.getComponents().getSchemas().get("Person");
558567
assertEquals(((Schema) schema3.getProperties().get("lastName")).getNullable(), null);
@@ -572,7 +581,7 @@ public void testSetPrimitiveTypesToNullable() {
572581
}
573582

574583
@Test
575-
public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
584+
public void testOpenAPINormalizerSimplifyOneOfAnyOf31SpecForIssue18184 () {
576585
// to test the rule SIMPLIFY_ONEOF_ANYOF in 3.1 spec
577586
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_18184.yaml");
578587
// test spec contains anyOf with a ref to enum and another scheme type is null
@@ -636,6 +645,77 @@ public void testOpenAPINormalizerProcessingArraySchema31Spec() {
636645
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getItems().getTypes().contains("string"), true);
637646
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getItems().getType(), "string");
638647
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getType(), "array");
648+
}
649+
650+
@Test
651+
public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
652+
// to test the rule SIMPLIFY_ONEOF_ANYOF with 3.1 spec
653+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml");
654+
655+
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
656+
assertEquals(schema.getAnyOf().size(), 4);
657+
assertNull(schema.getNullable());
658+
659+
Schema schema2 = openAPI.getComponents().getSchemas().get("OneOfTest");
660+
assertEquals(schema2.getOneOf().size(), 4);
661+
assertNull(schema2.getNullable());
662+
663+
Schema schema2b = openAPI.getComponents().getSchemas().get("OneOfTest2");
664+
assertEquals(schema2b.getOneOf().size(), 2);
665+
assertNull(schema2b.getNullable());
666+
667+
Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
668+
assertEquals(schema5.getOneOf().size(), 3);
669+
assertNull(schema5.getNullable());
670+
671+
Schema schema7 = openAPI.getComponents().getSchemas().get("Parent");
672+
assertEquals(((Schema) schema7.getProperties().get("number")).getAnyOf().size(), 1);
673+
674+
Schema schema9 = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
675+
assertEquals(schema9.getAnyOf().size(), 2);
676+
677+
Schema schema11 = openAPI.getComponents().getSchemas().get("AnyOfAnyType");
678+
assertEquals(schema11.getAnyOf().size(), 6);
679+
680+
Schema schema13 = openAPI.getComponents().getSchemas().get("OneOfAnyType");
681+
assertEquals(schema13.getOneOf().size(), 6);
639682

683+
Map<String, String> options = new HashMap<>();
684+
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
685+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
686+
openAPINormalizer.normalize();
687+
688+
Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
689+
assertNull(schema3.getAnyOf());
690+
assertEquals(ModelUtils.getType(schema3), "string");
691+
assertTrue(schema3.getNullable());
692+
693+
Schema schema4 = openAPI.getComponents().getSchemas().get("OneOfTest");
694+
assertNull(schema4.getOneOf());
695+
assertEquals(ModelUtils.getType(schema4), "integer");
696+
assertTrue(schema4.getNullable());
697+
698+
Schema schema4b = openAPI.getComponents().getSchemas().get("OneOfTest2");
699+
assertNull(schema4b.getOneOf());
700+
assertEquals(ModelUtils.getType(schema4b), "string");
701+
assertTrue(schema4b.getNullable());
702+
703+
Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
704+
assertEquals(schema6.getOneOf().size(), 2);
705+
assertTrue(schema6.getNullable());
706+
707+
Schema schema8 = openAPI.getComponents().getSchemas().get("Parent");
708+
assertEquals(((Schema) schema8.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
709+
710+
Schema schema10 = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
711+
assertEquals(schema10.getAnyOf().size(), 2);
712+
713+
Schema schema12 = openAPI.getComponents().getSchemas().get("AnyOfAnyType");
714+
assertEquals(schema12.getAnyOf(), null);
715+
assertEquals(schema12.getType(), null);
716+
717+
Schema schema14 = openAPI.getComponents().getSchemas().get("OneOfAnyType");
718+
assertEquals(schema14.getOneOf(), null);
719+
assertEquals(schema14.getType(), null);
640720
}
641721
}

modules/openapi-generator/src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ components:
4040
- type: 'null'
4141
- type: null
4242
- $ref: null
43+
OneOfTest2:
44+
description: to test oneOf
45+
oneOf:
46+
- type: string
47+
- type: 'null'
4348
OneOfNullableTest:
4449
description: to test oneOf nullable
4550
oneOf:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
openapi: 3.1.0
2+
info:
3+
version: 1.0.0
4+
title: Example
5+
license:
6+
name: MIT
7+
servers:
8+
- url: http://api.example.xyz/v1
9+
paths:
10+
/person/display/{personId}:
11+
get:
12+
parameters:
13+
- name: personId
14+
in: path
15+
required: true
16+
description: The id of the person to retrieve
17+
schema:
18+
type: string
19+
operationId: list
20+
responses:
21+
'200':
22+
description: OK
23+
content:
24+
application/json:
25+
schema:
26+
$ref: "#/components/schemas/AnyOfTest"
27+
components:
28+
schemas:
29+
AnyOfTest:
30+
description: to test anyOf
31+
anyOf:
32+
- type: string
33+
- type: 'null'
34+
- type: null
35+
- $ref: null
36+
OneOfTest:
37+
description: to test oneOf
38+
oneOf:
39+
- type: integer
40+
- type: 'null'
41+
- type: null
42+
- $ref: null
43+
OneOfTest2:
44+
description: to test oneOf
45+
oneOf:
46+
- type: string
47+
- type: 'null'
48+
OneOfNullableTest:
49+
description: to test oneOf nullable
50+
oneOf:
51+
- type: integer
52+
- type: string
53+
- $ref: null
54+
SingleAnyOfTest:
55+
description: to test anyOf (enum string only)
56+
anyOf:
57+
- type: string
58+
enum:
59+
- A
60+
- B
61+
Parent:
62+
type: object
63+
properties:
64+
number:
65+
anyOf:
66+
- $ref: '#/components/schemas/Number'
67+
ParentWithOneOfProperty:
68+
type: object
69+
properties:
70+
number:
71+
oneOf:
72+
- $ref: '#/components/schemas/Number'
73+
ParentWithPluralOneOfProperty:
74+
type: object
75+
properties:
76+
number:
77+
oneOf:
78+
- $ref: '#/components/schemas/Number'
79+
- $ref: '#/components/schemas/Number2'
80+
Number:
81+
enum:
82+
- one
83+
- two
84+
- three
85+
type: string
86+
Number2:
87+
enum:
88+
- one
89+
- two
90+
type: string
91+
AnyOfStringArrayOfString:
92+
anyOf:
93+
- type: string
94+
- type: array
95+
items:
96+
type: string
97+
AnyOfAnyType:
98+
anyOf:
99+
- type: boolean
100+
- type: array
101+
items: {}
102+
- type: object
103+
- type: string
104+
- type: number
105+
- type: integer
106+
OneOfAnyType:
107+
oneOf:
108+
- type: object
109+
- type: boolean
110+
- type: number
111+
- type: string
112+
- type: integer
113+
- type: array
114+
items: {}

0 commit comments

Comments
 (0)