Fix null check in 3.1 spec (#18353)

* fix null check in 3.1 spec

* clean up
This commit is contained in:
William Cheng 2024-04-11 16:52:31 +08:00 committed by GitHub
parent ff8fa40808
commit 03af25ce34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 229 additions and 6 deletions

View File

@ -890,7 +890,7 @@ public class OpenAPINormalizer {
if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
// 3.1 spec
if (schema.getTypes().size() ==1) { // 1 type only
if (schema.getTypes().size() == 1) { // 1 type only
String type = (String) schema.getTypes().iterator().next();
return type == null || "null".equals(type);
} else { // more than 1 type so must not be just null
@ -902,6 +902,11 @@ public class OpenAPINormalizer {
if (Boolean.TRUE.equals(schema.getNullable())) {
return true;
}
// for `type: null`
if (schema.getTypes() == null && schema.get$ref() == null) {
return true;
}
} else { // 3.0.x or 2.x spec
if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
return true;
@ -938,7 +943,7 @@ public class OpenAPINormalizer {
if (oneOfSchemas.size() == 6) {
TreeSet<String> ts = new TreeSet<>();
for (Schema s: oneOfSchemas) {
ts.add(s.getType());
ts.add(ModelUtils.getType(s));
}
if (ts.equals(anyTypeTreeSet)) {
@ -1063,7 +1068,7 @@ public class OpenAPINormalizer {
if (anyOfSchemas.size() == 6) {
TreeSet<String> ts = new TreeSet<>();
for (Schema s: anyOfSchemas) {
ts.add(s.getType());
ts.add(ModelUtils.getType(s));
}
if (ts.equals(anyTypeTreeSet)) {

View File

@ -2126,6 +2126,25 @@ public class ModelUtils {
return false;
}
/**
* Returns schema type.
* For 3.1 spec, return the first one.
*
* @param schema the schema
* @return schema type
*/
public static String getType(Schema schema) {
if (schema == null) {
return null;
}
if (schema instanceof JsonSchema) {
return String.valueOf(schema.getTypes().iterator().next());
} else {
return schema.getType();
}
}
/**
* Returns true if any of the common attributes of the schema (e.g. readOnly, default, maximum, etc) is defined.
*

View File

@ -158,6 +158,10 @@ public class OpenAPINormalizerTest {
assertEquals(schema2.getOneOf().size(), 4);
assertNull(schema2.getNullable());
Schema schema2b = openAPI.getComponents().getSchemas().get("OneOfTest2");
assertEquals(schema2b.getOneOf().size(), 2);
assertNull(schema2b.getNullable());
Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema5.getOneOf().size(), 3);
assertNull(schema5.getNullable());
@ -189,6 +193,11 @@ public class OpenAPINormalizerTest {
assertTrue(schema4 instanceof IntegerSchema);
assertTrue(schema4.getNullable());
Schema schema4b = openAPI.getComponents().getSchemas().get("OneOfTest2");
assertNull(schema4b.getOneOf());
assertTrue(schema4b instanceof StringSchema);
assertTrue(schema4b.getNullable());
Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema6.getOneOf().size(), 2);
assertTrue(schema6.getNullable());
@ -532,7 +541,7 @@ public class OpenAPINormalizerTest {
@Test
public void testSetPrimitiveTypesToNullable() {
// test `string|integer|number|boolean`
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0//setPrimitiveTypesToNullable_test.yaml");
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/setPrimitiveTypesToNullable_test.yaml");
Schema schema = openAPI.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema.getProperties().get("lastName")).getNullable(), null);
@ -552,7 +561,7 @@ public class OpenAPINormalizerTest {
assertEquals(((Schema) schema2.getProperties().get("first_boolean")).getNullable(), true);
// test `number` only
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0//setPrimitiveTypesToNullable_test.yaml");
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0/setPrimitiveTypesToNullable_test.yaml");
Schema schema3 = openAPI2.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema3.getProperties().get("lastName")).getNullable(), null);
@ -572,7 +581,7 @@ public class OpenAPINormalizerTest {
}
@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
public void testOpenAPINormalizerSimplifyOneOfAnyOf31SpecForIssue18184 () {
// to test the rule SIMPLIFY_ONEOF_ANYOF in 3.1 spec
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_18184.yaml");
// test spec contains anyOf with a ref to enum and another scheme type is null
@ -636,6 +645,77 @@ public class OpenAPINormalizerTest {
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getItems().getTypes().contains("string"), true);
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getItems().getType(), "string");
assertEquals(((Schema) schema6.getProperties().get("arrayOfStrings")).getType(), "array");
}
@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
// to test the rule SIMPLIFY_ONEOF_ANYOF with 3.1 spec
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml");
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 4);
assertNull(schema.getNullable());
Schema schema2 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertEquals(schema2.getOneOf().size(), 4);
assertNull(schema2.getNullable());
Schema schema2b = openAPI.getComponents().getSchemas().get("OneOfTest2");
assertEquals(schema2b.getOneOf().size(), 2);
assertNull(schema2b.getNullable());
Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema5.getOneOf().size(), 3);
assertNull(schema5.getNullable());
Schema schema7 = openAPI.getComponents().getSchemas().get("Parent");
assertEquals(((Schema) schema7.getProperties().get("number")).getAnyOf().size(), 1);
Schema schema9 = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
assertEquals(schema9.getAnyOf().size(), 2);
Schema schema11 = openAPI.getComponents().getSchemas().get("AnyOfAnyType");
assertEquals(schema11.getAnyOf().size(), 6);
Schema schema13 = openAPI.getComponents().getSchemas().get("OneOfAnyType");
assertEquals(schema13.getOneOf().size(), 6);
Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();
Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertNull(schema3.getAnyOf());
assertEquals(ModelUtils.getType(schema3), "string");
assertTrue(schema3.getNullable());
Schema schema4 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertNull(schema4.getOneOf());
assertEquals(ModelUtils.getType(schema4), "integer");
assertTrue(schema4.getNullable());
Schema schema4b = openAPI.getComponents().getSchemas().get("OneOfTest2");
assertNull(schema4b.getOneOf());
assertEquals(ModelUtils.getType(schema4b), "string");
assertTrue(schema4b.getNullable());
Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema6.getOneOf().size(), 2);
assertTrue(schema6.getNullable());
Schema schema8 = openAPI.getComponents().getSchemas().get("Parent");
assertEquals(((Schema) schema8.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
Schema schema10 = openAPI.getComponents().getSchemas().get("AnyOfStringArrayOfString");
assertEquals(schema10.getAnyOf().size(), 2);
Schema schema12 = openAPI.getComponents().getSchemas().get("AnyOfAnyType");
assertEquals(schema12.getAnyOf(), null);
assertEquals(schema12.getType(), null);
Schema schema14 = openAPI.getComponents().getSchemas().get("OneOfAnyType");
assertEquals(schema14.getOneOf(), null);
assertEquals(schema14.getType(), null);
}
}

View File

@ -40,6 +40,11 @@ components:
- type: 'null'
- type: null
- $ref: null
OneOfTest2:
description: to test oneOf
oneOf:
- type: string
- type: 'null'
OneOfNullableTest:
description: to test oneOf nullable
oneOf:

View File

@ -0,0 +1,114 @@
openapi: 3.1.0
info:
version: 1.0.0
title: Example
license:
name: MIT
servers:
- url: http://api.example.xyz/v1
paths:
/person/display/{personId}:
get:
parameters:
- name: personId
in: path
required: true
description: The id of the person to retrieve
schema:
type: string
operationId: list
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/AnyOfTest"
components:
schemas:
AnyOfTest:
description: to test anyOf
anyOf:
- type: string
- type: 'null'
- type: null
- $ref: null
OneOfTest:
description: to test oneOf
oneOf:
- type: integer
- type: 'null'
- type: null
- $ref: null
OneOfTest2:
description: to test oneOf
oneOf:
- type: string
- type: 'null'
OneOfNullableTest:
description: to test oneOf nullable
oneOf:
- type: integer
- type: string
- $ref: null
SingleAnyOfTest:
description: to test anyOf (enum string only)
anyOf:
- type: string
enum:
- A
- B
Parent:
type: object
properties:
number:
anyOf:
- $ref: '#/components/schemas/Number'
ParentWithOneOfProperty:
type: object
properties:
number:
oneOf:
- $ref: '#/components/schemas/Number'
ParentWithPluralOneOfProperty:
type: object
properties:
number:
oneOf:
- $ref: '#/components/schemas/Number'
- $ref: '#/components/schemas/Number2'
Number:
enum:
- one
- two
- three
type: string
Number2:
enum:
- one
- two
type: string
AnyOfStringArrayOfString:
anyOf:
- type: string
- type: array
items:
type: string
AnyOfAnyType:
anyOf:
- type: boolean
- type: array
items: {}
- type: object
- type: string
- type: number
- type: integer
OneOfAnyType:
oneOf:
- type: object
- type: boolean
- type: number
- type: string
- type: integer
- type: array
items: {}