From 4ae2634b5efc7fa64e7213a9f7de38b192fd1e79 Mon Sep 17 00:00:00 2001 From: dpeger Date: Wed, 26 Jan 2022 17:19:38 +0100 Subject: [PATCH 1/2] #4103 Support for Jackson reference types In addition to the hard coded `Optional` reference types the type information from jackson is used to unwrap additional reference types (e.g. `AtomicReference`) --- .../converter/ModelConverterContextImpl.java | 4 +- .../v3/core/jackson/ModelResolver.java | 4 +- ...onalUtils.java => ReferenceTypeUtils.java} | 15 ++--- .../v3/core/util/ReferenceTypeUtilsTest.java | 66 +++++++++++++++++++ 4 files changed, 77 insertions(+), 12 deletions(-) rename modules/swagger-core/src/main/java/io/swagger/v3/core/util/{OptionalUtils.java => ReferenceTypeUtils.java} (80%) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/util/ReferenceTypeUtilsTest.java diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverterContextImpl.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverterContextImpl.java index aaa55e66c2..99cdda9126 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverterContextImpl.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverterContextImpl.java @@ -1,6 +1,6 @@ package io.swagger.v3.core.converter; -import io.swagger.v3.core.util.OptionalUtils; +import io.swagger.v3.core.util.ReferenceTypeUtils; import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -76,7 +76,7 @@ public Map getDefinedModels() { @Override public Schema resolve(AnnotatedType type) { - AnnotatedType aType = OptionalUtils.unwrapOptional(type); + AnnotatedType aType = ReferenceTypeUtils.unwrapReference(type); if (aType != null) { return resolve(aType); } 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 e14ab2a38c..5809b3201b 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,7 @@ import io.swagger.v3.core.util.Constants; import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.ObjectMapperFactory; -import io.swagger.v3.core.util.OptionalUtils; +import io.swagger.v3.core.util.ReferenceTypeUtils; import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.core.util.ReflectionUtils; import io.swagger.v3.oas.annotations.Hidden; @@ -497,7 +497,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context .type("object") .name(name); } else { - AnnotatedType aType = OptionalUtils.unwrapOptional(annotatedType); + AnnotatedType aType = ReferenceTypeUtils.unwrapReference(annotatedType); if (aType != null) { model = context.resolve(aType); return model; diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OptionalUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java similarity index 80% rename from modules/swagger-core/src/main/java/io/swagger/v3/core/util/OptionalUtils.java rename to modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java index 1e4c8c02d0..ef21a95efb 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OptionalUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java @@ -7,23 +7,22 @@ import java.util.Arrays; -public abstract class OptionalUtils { +public abstract class ReferenceTypeUtils { - private static Logger LOGGER = LoggerFactory.getLogger(OptionalUtils.class); + private static Logger LOGGER = LoggerFactory.getLogger(ReferenceTypeUtils.class); - public static boolean _isOptionalType(JavaType jtype) { + public static boolean _isReferenceType(JavaType jtype) { - return Arrays.asList("com.google.common.base.Optional", "java.util.Optional") + return Arrays.asList("com.google.common.base.Optional", "java.util.Optional", "java.util.concurrent.atomic.AtomicReference") .contains(jtype.getRawClass().getCanonicalName()); } /** - * check if type is an Optional type, returns the unwrapped type in case, otherwise null + * check if type is a reference type, returns the unwrapped type in case, otherwise null * * @param type - * */ - public static AnnotatedType unwrapOptional(AnnotatedType type) { + public static AnnotatedType unwrapReference(AnnotatedType type) { if (type == null) { return type; @@ -36,7 +35,7 @@ public static AnnotatedType unwrapOptional(AnnotatedType type) { jtype = Json.mapper().constructType(type.getType()); } - if (_isOptionalType(jtype)) { + if (_isReferenceType(jtype)) { AnnotatedType aType = new AnnotatedType() .type(jtype.containedType(0)) .name(type.getName()) diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ReferenceTypeUtilsTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ReferenceTypeUtilsTest.java new file mode 100644 index 0000000000..cbaa15e06a --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ReferenceTypeUtilsTest.java @@ -0,0 +1,66 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import io.swagger.v3.core.converter.AnnotatedType; +import org.checkerframework.checker.units.qual.A; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Unit test cases for {@link ReferenceTypeUtils} + */ +public class ReferenceTypeUtilsTest { + + @Test(description = "AtomicReference should be reference type") + public void testIsReferenceTypeWithAtomicReference() { + final JavaType referredType = TypeFactory.defaultInstance().constructType(String.class); + final Class rawType = AtomicReference.class; + final JavaType atomicReferenceType = TypeFactory.defaultInstance().constructReferenceType(rawType, referredType); + + final boolean actualIsReferenceType = ReferenceTypeUtils._isReferenceType(atomicReferenceType); + + Assert.assertEquals(actualIsReferenceType, true, rawType.getCanonicalName() + " should be reference type but was not."); + } + + @Test(description = "AtomicReference JavaType should be unwrapped") + public void testUnwrapWithAtomicReferenceAndJavaType() { + final JavaType expectedReferredType = TypeFactory.defaultInstance().constructType(String.class); + + final Class rawType = AtomicReference.class; + final JavaType atomicReferenceType = TypeFactory.defaultInstance().constructReferenceType(rawType, expectedReferredType); + + final AnnotatedType actualUnwrappedType = ReferenceTypeUtils.unwrapReference(new AnnotatedType(atomicReferenceType)); + + Assert.assertEquals(actualUnwrappedType.getType(), expectedReferredType, rawType.getCanonicalName() + "Reference type not correctly unwrapped"); + } + + @Test(description = "AtomicReference should be unwrapped when read from Java bean") + public void testUnwrapWithAtomicReferenceMemberFromJavaBean() throws Exception { + final JavaType expectedReferredType = TypeFactory.defaultInstance().constructType(BigDecimal.class); + + final Type genericType = TypeWithAtomicReferenceMember.class.getDeclaredField("member").getGenericType(); + final AnnotatedType actualUnwrappedType = ReferenceTypeUtils.unwrapReference(new AnnotatedType(genericType)); + + Assert.assertEquals(actualUnwrappedType.getType(), expectedReferredType, genericType.getTypeName() + "Reference type not correctly unwrapped"); + } + + @SuppressWarnings("unused") + private static final class TypeWithAtomicReferenceMember { + AtomicReference member; + + public AtomicReference getMember() { + return member; + } + + public void setMember(AtomicReference member) { + this.member = member; + } + } + +} From 37ba7e501741eb6a8769bb6f4789d8719dbe810e Mon Sep 17 00:00:00 2001 From: dpeger Date: Wed, 26 Jan 2022 20:18:45 +0100 Subject: [PATCH 2/2] #4103 Support for Jackson reference types In addition to the hard coded `Optional` reference types the type information from jackson is used to unwrap additional reference types (e.g. `AtomicReference`) --- .../main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java index ef21a95efb..af33c177d1 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ReferenceTypeUtils.java @@ -13,8 +13,8 @@ public abstract class ReferenceTypeUtils { public static boolean _isReferenceType(JavaType jtype) { - return Arrays.asList("com.google.common.base.Optional", "java.util.Optional", "java.util.concurrent.atomic.AtomicReference") - .contains(jtype.getRawClass().getCanonicalName()); + return Arrays.asList("com.google.common.base.Optional", "java.util.Optional") + .contains(jtype.getRawClass().getCanonicalName()) || jtype.isReferenceType(); } /**