diff --git a/core/src/main/java/io/smallrye/openapi/runtime/io/media/SchemaIO.java b/core/src/main/java/io/smallrye/openapi/runtime/io/media/SchemaIO.java index c220bd8c4..fa96802b6 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/io/media/SchemaIO.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/io/media/SchemaIO.java @@ -191,7 +191,7 @@ private void populateSchemaObject30(Schema schema, O node) { } // Detect {$ref=....,nullable=true} and convert to anyOf[{$ref=...}, {type=null}] - if (schema.getRef() != null && schema.getType() == null && SchemaSupport.getNullable(schema) == Boolean.TRUE) { + if (schema.getRef() != null && SchemaSupport.getNullable(schema) == Boolean.TRUE) { List newAnyOfSchemas = new ArrayList<>(); newAnyOfSchemas.add(OASFactory.createSchema().ref(schema.getRef())); newAnyOfSchemas.add(SchemaSupport.nullSchema()); @@ -201,7 +201,11 @@ private void populateSchemaObject30(Schema schema, O node) { schema.addAllOf(OASFactory.createSchema().anyOf(newAnyOfSchemas)); } schema.setRef(null); - SchemaSupport.setNullable(schema, null); + if (schema.getType() == null) { + SchemaSupport.setNullable(schema, null); + } else { + SchemaSupport.setNullable(schema, Boolean.TRUE); + } } // Detect {enum=[null]} and convert to {type=null} @@ -389,7 +393,7 @@ private ReplacementFields compute30ReplacementFields(Schema schema31) { } // If we have anyOf = [{type=null}, {$ref=...}], remove it and set nullable and allOf = [{$ref=...}] - if (result.anyOf != null && result.anyOf.size() == 2 && result.ref == null && result.type == null) { + if (result.anyOf != null && result.anyOf.size() == 2 && result.ref == null) { Optional typeNullSchema = result.anyOf.stream().filter(s -> isSoloTypeNull(s)).findFirst(); Optional refSchema = result.anyOf.stream().filter(s -> isSoloRef(s)).findFirst(); if (typeNullSchema.isPresent() && refSchema.isPresent()) { diff --git a/core/src/test/java/io/smallrye/openapi/runtime/scanner/SchemaPropertyTest.java b/core/src/test/java/io/smallrye/openapi/runtime/scanner/SchemaPropertyTest.java index bb18a8056..eafe630cc 100644 --- a/core/src/test/java/io/smallrye/openapi/runtime/scanner/SchemaPropertyTest.java +++ b/core/src/test/java/io/smallrye/openapi/runtime/scanner/SchemaPropertyTest.java @@ -93,4 +93,29 @@ static class Turtle extends Reptile { String shellPattern; Speed speed; } + + @Test + void testNullableFieldReference() throws Exception { + Index index = indexOf(FieldTarget.class, FieldReferrer.class); + SmallRyeOpenAPI result1 = SmallRyeOpenAPI.builder() + .withConfig(config(Collections.emptyMap())) + .withIndex(index) + .defaultRequiredProperties(false) + .build(); + printToConsole(result1.model()); + assertJsonEquals("components.schemas.schemaproperty-nullable.json", result1.model()); + } + + @org.eclipse.microprofile.openapi.annotations.media.Schema + public static class FieldTarget { + } + + @org.eclipse.microprofile.openapi.annotations.media.Schema + public static class FieldReferrer { + @org.eclipse.microprofile.openapi.annotations.media.Schema(nullable = true) + private FieldTarget a; + @org.eclipse.microprofile.openapi.annotations.media.Schema(nullable = true, description = "b") + private FieldTarget b; + } + } diff --git a/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference.json b/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference.json index beaa95af9..9510d0e82 100644 --- a/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference.json +++ b/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference.json @@ -13,6 +13,18 @@ } ] }, + "NullRefWithAnnotations": { + "description": "Description is an annotation. Scanner generates type as well here.", + "type": ["object", "null"], + "anyOf": [ + { + "$ref": "#/components/schemas/mySchema" + }, + { + "type": "null" + } + ] + }, "NullRefWithAnyOf": { "allOf": [ { diff --git a/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference30.json b/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference30.json index d5de5f61d..cd7c08606 100644 --- a/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference30.json +++ b/core/src/test/resources/io/smallrye/openapi/runtime/io/schemas-with-nullable-reference30.json @@ -11,6 +11,16 @@ } ] }, + "NullRefWithAnnotations": { + "description": "Description is an annotation. Scanner generates type as well here.", + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/mySchema" + } + ], + "nullable": true + }, "NullRefWithAnyOf": { "allOf": [ { diff --git a/core/src/test/resources/io/smallrye/openapi/runtime/scanner/components.schemas.schemaproperty-nullable.json b/core/src/test/resources/io/smallrye/openapi/runtime/scanner/components.schemas.schemaproperty-nullable.json new file mode 100644 index 000000000..b526e0b28 --- /dev/null +++ b/core/src/test/resources/io/smallrye/openapi/runtime/scanner/components.schemas.schemaproperty-nullable.json @@ -0,0 +1,31 @@ +{ + "openapi" : "3.1.0", + "components" : { + "schemas" : { + "FieldReferrer" : { + "type" : "object", + "properties" : { + "a" : { + "anyOf" : [ { + "$ref" : "#/components/schemas/FieldTarget" + }, { + "type" : "null" + } ] + }, + "b" : { + "description" : "b", + "type" : "object", + "anyOf" : [ { + "$ref" : "#/components/schemas/FieldTarget" + }, { + "type" : "null" + } ] + } + } + }, + "FieldTarget" : { + "type" : "object" + } + } + } +} \ No newline at end of file