From d5c37284f5dbb9b6e65858c25d55c1aa2969f8a2 Mon Sep 17 00:00:00 2001 From: frantuma Date: Tue, 26 Mar 2024 17:18:10 +0100 Subject: [PATCH] refs #4474 - fix NPE while resolving container types --- .../v3/core/jackson/ModelResolver.java | 15 +- .../swagger/v3/core/util/ReflectionUtils.java | 6 +- .../v3/core/resolving/Ticket4474Test.java | 164 ++++++++++++++++++ 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4474Test.java diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index fc86e2184c..8abdcbad4b 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -34,7 +34,6 @@ import io.swagger.v3.core.util.AnnotationsUtils; import io.swagger.v3.core.util.Constants; import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Json31; import io.swagger.v3.core.util.ObjectMapperFactory; import io.swagger.v3.core.util.ReferenceTypeUtils; import io.swagger.v3.core.util.PrimitiveType; @@ -438,7 +437,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context if (keyType != null && valueType != null) { - if (ReflectionUtils.isSystemType(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) { + if (ReflectionUtils.isSystemTypeNotArray(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) { context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(valueType).jsonViewAnnotation(annotatedType.getJsonViewAnnotation())); return null; } @@ -470,7 +469,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context mapModel.name(name); model = mapModel; } else if (valueType != null) { - if (ReflectionUtils.isSystemType(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) { + if (ReflectionUtils.isSystemTypeNotArray(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) { context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(valueType).jsonViewAnnotation(annotatedType.getJsonViewAnnotation())); return null; } @@ -924,10 +923,12 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context .collect(Collectors.toList()); anyOfFiltered.forEach(c -> { Schema anyOfRef = context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation())); - if (StringUtils.isNotBlank(anyOfRef.getName())) { - composedSchema.addAnyOfItem(new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + anyOfRef.getName())); - } else { - composedSchema.addAnyOfItem(anyOfRef); + if (anyOfRef != null) { + if (StringUtils.isNotBlank(anyOfRef.getName())) { + composedSchema.addAnyOfItem(new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + anyOfRef.getName())); + } else { + composedSchema.addAnyOfItem(anyOfRef); + } } // remove shared properties defined in the parent if (isSubtype(beanDesc.getClassInfo(), c)) { diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReflectionUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReflectionUtils.java index 307058ad53..f8b0c08a7d 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReflectionUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReflectionUtils.java @@ -440,6 +440,10 @@ public static boolean isVoid(Type type) { } public static boolean isSystemType(JavaType type) { + return isSystemTypeNotArray(type) ? true : type.isArrayType(); + } + + public static boolean isSystemTypeNotArray(JavaType type) { // used while resolving container types to skip resolving system types; possibly extend by checking classloader // and/or other packages for (String systemPrefix: PrimitiveType.systemPrefixes()) { @@ -450,7 +454,7 @@ public static boolean isSystemType(JavaType type) { } } } - return type.isArrayType(); + return false; } /** diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4474Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4474Test.java new file mode 100644 index 0000000000..d9b6383a65 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4474Test.java @@ -0,0 +1,164 @@ +package io.swagger.v3.core.resolving; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Ticket4474Test extends SwaggerTestBase { + + @AfterMethod + public void afterTest() { + ModelResolver.enumsAsRef = false; + } + + @Test + public void testAnyOf() throws Exception { + ModelResolver.enumsAsRef = true; + + final ModelResolver modelResolver = new ModelResolver(mapper()); + + final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + final Schema model = context + .resolve(new AnnotatedType(Document.class)); + + SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "Document:\n" + + " type: object\n" + + " properties:\n" + + " data:\n" + + " type: object\n" + + " additionalProperties:\n" + + " type: object\n" + + " anyOf:\n" + + " - type: array\n" + + " items:\n" + + " type: boolean\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int32\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int64\n" + + " - type: array\n" + + " items:\n" + + " type: number\n" + + " format: double\n" + + " - type: array\n" + + " items:\n" + + " type: string\n" + + " - type: boolean\n" + + " - type: integer\n" + + " format: int32\n" + + " - type: integer\n" + + " format: int64\n" + + " listData:\n" + + " type: array\n" + + " items:\n" + + " type: object\n" + + " anyOf:\n" + + " - type: array\n" + + " items:\n" + + " type: boolean\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int32\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int64\n" + + " - type: array\n" + + " items:\n" + + " type: number\n" + + " format: double\n" + + " - type: array\n" + + " items:\n" + + " type: string\n" + + " - type: boolean\n" + + " - type: integer\n" + + " format: int32\n" + + " - type: integer\n" + + " format: int64\n" + + " itemData:\n" + + " type: object\n" + + " anyOf:\n" + + " - type: array\n" + + " items:\n" + + " type: boolean\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int32\n" + + " - type: array\n" + + " items:\n" + + " type: integer\n" + + " format: int64\n" + + " - type: array\n" + + " items:\n" + + " type: number\n" + + " format: double\n" + + " - type: array\n" + + " items:\n" + + " type: string\n" + + " - type: boolean\n" + + " - type: integer\n" + + " format: int32\n" + + " - type: integer\n" + + " format: int64"); + } + + static class Document { + @io.swagger.v3.oas.annotations.media.Schema( + anyOf = { + Boolean[].class, + Integer[].class, + Long[].class, + Double[].class, + String[].class, + Boolean.class, + Integer.class, + Long.class, + Map.class, + }) + public Map data = new HashMap<>(); + + @io.swagger.v3.oas.annotations.media.Schema( + anyOf = { + Boolean[].class, + Integer[].class, + Long[].class, + Double[].class, + String[].class, + Boolean.class, + Integer.class, + Long.class, + Map.class, + }) + public List listData = new ArrayList<>(); + + @io.swagger.v3.oas.annotations.media.Schema( + anyOf = { + Boolean[].class, + Integer[].class, + Long[].class, + Double[].class, + String[].class, + Boolean.class, + Integer.class, + Long.class, + Map.class, + }) + public Object itemData = new Object(); + } +}