Skip to content

Commit 3b3f9a7

Browse files
authored
refacot is null schema check (#19873)
1 parent 2354d40 commit 3b3f9a7

5 files changed

Lines changed: 193 additions & 72 deletions

File tree

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

Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -951,68 +951,7 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
951951
return result;
952952
}
953953

954-
/**
955-
* Check if the schema is of type 'null' or schema itself is pointing to null
956-
* <p>
957-
* Return true if the schema's type is 'null' or not specified
958-
*
959-
* @param schema Schema
960-
* @param openAPI OpenAPI
961-
*
962-
* @return true if schema is null type
963-
*/
964-
public boolean isNullTypeSchema(OpenAPI openAPI, Schema schema) {
965-
if (schema == null) {
966-
return true;
967-
}
968-
969-
// dereference the schema
970-
schema = ModelUtils.getReferencedSchema(openAPI, schema);
971-
972-
// allOf/anyOf/oneOf
973-
if (ModelUtils.hasAllOf(schema) || ModelUtils.hasOneOf(schema) || ModelUtils.hasAnyOf(schema)) {
974-
return false;
975-
}
976-
977-
// schema with properties
978-
if (schema.getProperties() != null) {
979-
return false;
980-
}
981-
982-
// convert referenced enum of null only to `nullable:true`
983-
if (schema.getEnum() != null && schema.getEnum().size() == 1) {
984-
if ("null".equals(String.valueOf(schema.getEnum().get(0)))) {
985-
return true;
986-
}
987-
}
988-
989-
if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
990-
// 3.1 spec
991-
if (schema.getTypes().size() == 1) { // 1 type only
992-
String type = (String) schema.getTypes().iterator().next();
993-
return type == null || "null".equals(type);
994-
} else { // more than 1 type so must not be just null
995-
return false;
996-
}
997-
}
998954

999-
if (schema instanceof JsonSchema) { // 3.1 spec
1000-
if (Boolean.TRUE.equals(schema.getNullable())) {
1001-
return true;
1002-
}
1003-
1004-
// for `type: null`
1005-
if (schema.getTypes() == null && schema.get$ref() == null) {
1006-
return true;
1007-
}
1008-
} else { // 3.0.x or 2.x spec
1009-
if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
1010-
return true;
1011-
}
1012-
}
1013-
1014-
return false;
1015-
}
1016955

1017956
/**
1018957
* If the schema is oneOf and the sub-schemas is null, set `nullable: true`
@@ -1056,7 +995,7 @@ private Schema processSimplifyOneOf(Schema schema) {
1056995
}
1057996
}
1058997

1059-
if (oneOfSchemas.removeIf(oneOf -> isNullTypeSchema(openAPI, oneOf))) {
998+
if (oneOfSchemas.removeIf(oneOf -> ModelUtils.isNullTypeSchema(openAPI, oneOf))) {
1060999
schema.setNullable(true);
10611000

10621001
// if only one element left, simplify to just the element (schema)
@@ -1192,7 +1131,7 @@ private Schema processSimplifyAnyOf(Schema schema) {
11921131
}
11931132
}
11941133

1195-
if (anyOfSchemas.removeIf(anyOf -> isNullTypeSchema(openAPI, anyOf))) {
1134+
if (anyOfSchemas.removeIf(anyOf -> ModelUtils.isNullTypeSchema(openAPI, anyOf))) {
11961135
schema.setNullable(true);
11971136
}
11981137

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,69 @@ public static Schema cloneSchema(Schema schema, boolean openapi31) {
21992199
}
22002200
}
22012201

2202+
/**
2203+
* Check if the schema is of type 'null' or schema itself is pointing to null
2204+
* <p>
2205+
* Return true if the schema's type is 'null' or not specified
2206+
*
2207+
* @param schema Schema
2208+
* @param openAPI OpenAPI
2209+
*
2210+
* @return true if schema is null type
2211+
*/
2212+
public static boolean isNullTypeSchema(OpenAPI openAPI, Schema schema) {
2213+
if (schema == null) {
2214+
return true;
2215+
}
2216+
2217+
// dereference the schema
2218+
schema = ModelUtils.getReferencedSchema(openAPI, schema);
2219+
2220+
// allOf/anyOf/oneOf
2221+
if (ModelUtils.hasAllOf(schema) || ModelUtils.hasOneOf(schema) || ModelUtils.hasAnyOf(schema)) {
2222+
return false;
2223+
}
2224+
2225+
// schema with properties
2226+
if (schema.getProperties() != null) {
2227+
return false;
2228+
}
2229+
2230+
// convert referenced enum of null only to `nullable:true`
2231+
if (schema.getEnum() != null && schema.getEnum().size() == 1) {
2232+
if ("null".equals(String.valueOf(schema.getEnum().get(0)))) {
2233+
return true;
2234+
}
2235+
}
2236+
2237+
if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
2238+
// 3.1 spec
2239+
if (schema.getTypes().size() == 1) { // 1 type only
2240+
String type = (String) schema.getTypes().iterator().next();
2241+
return type == null || "null".equals(type);
2242+
} else { // more than 1 type so must not be just null
2243+
return false;
2244+
}
2245+
}
2246+
2247+
if (schema instanceof JsonSchema) { // 3.1 spec
2248+
if (Boolean.TRUE.equals(schema.getNullable())) {
2249+
return true;
2250+
}
2251+
2252+
// for `type: null`
2253+
if (schema.getTypes() == null && schema.get$ref() == null) {
2254+
return true;
2255+
}
2256+
} else { // 3.0.x or 2.x spec
2257+
if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
2258+
return true;
2259+
}
2260+
}
2261+
2262+
return false;
2263+
}
2264+
22022265
@FunctionalInterface
22032266
private interface OpenAPISchemaVisitor {
22042267

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,6 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOfStringAndEnumString() {
107107
assertTrue(schema3.getEnum().size() > 0);
108108
}
109109

110-
@Test
111-
public void isNullTypeSchemaTest() {
112-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
113-
Map<String, String> options = new HashMap<>();
114-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
115-
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
116-
assertFalse(openAPINormalizer.isNullTypeSchema(openAPI, schema));
117-
}
118-
119110
@Test
120111
public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
121112
// to test the rule SIMPLIFY_ONEOF_ANYOF

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
import io.swagger.v3.oas.models.parameters.Parameter;
2323
import io.swagger.v3.oas.models.parameters.RequestBody;
2424
import io.swagger.v3.oas.models.responses.ApiResponse;
25+
import org.openapitools.codegen.OpenAPINormalizer;
2526
import org.openapitools.codegen.TestUtils;
2627
import org.testng.Assert;
2728
import org.testng.annotations.Test;
2829

2930
import java.math.BigDecimal;
3031
import java.util.*;
3132

33+
import static org.testng.Assert.assertFalse;
34+
import static org.testng.Assert.assertTrue;
35+
3236
public class ModelUtilsTest {
3337

3438
@Test
@@ -469,4 +473,32 @@ public void testGetSchemaItemsWith31Spec() {
469473
Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("with_prefixitems")));
470474
Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("without_items")));
471475
}
476+
477+
@Test
478+
public void isNullTypeSchemaTest() {
479+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml");
480+
Map<String, String> options = new HashMap<>();
481+
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
482+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
483+
484+
schema = openAPI.getComponents().getSchemas().get("IntegerRef");
485+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
486+
487+
schema = openAPI.getComponents().getSchemas().get("OneOfAnyType");
488+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
489+
490+
schema = openAPI.getComponents().getSchemas().get("AnyOfAnyType");
491+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
492+
493+
schema = openAPI.getComponents().getSchemas().get("Parent");
494+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
495+
// the dummy property is a ref to integer
496+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getProperties().get("dummy")));
497+
498+
schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
499+
assertFalse(ModelUtils.isNullTypeSchema(openAPI, schema));
500+
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(1)));
501+
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(2)));
502+
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(3)));
503+
}
472504
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
openapi: 3.0.1
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+
Parent:
55+
type: object
56+
properties:
57+
dummy:
58+
$ref: '#/components/schemas/IntegerRef'
59+
number:
60+
anyOf:
61+
- $ref: '#/components/schemas/Number'
62+
AnyOfStringArrayOfString:
63+
anyOf:
64+
- type: string
65+
- type: array
66+
items:
67+
type: string
68+
AnyOfAnyType:
69+
anyOf:
70+
- type: boolean
71+
- type: array
72+
items: {}
73+
- type: object
74+
- type: string
75+
- type: number
76+
- type: integer
77+
AnyOfAnyTypeWithRef:
78+
anyOf:
79+
- type: boolean
80+
- type: array
81+
items: { }
82+
- type: object
83+
- type: string
84+
- type: number
85+
- $ref: '#/components/schemas/IntegerRef'
86+
IntegerRef:
87+
type: integer
88+
OneOfAnyType:
89+
oneOf:
90+
- type: object
91+
- type: boolean
92+
- type: number
93+
- type: string
94+
- type: integer
95+
- type: array
96+
items: {}

0 commit comments

Comments
 (0)