From ee98cc2d3b9836b6d0a75e4c882ecd33c4632504 Mon Sep 17 00:00:00 2001 From: T3rm1 Date: Fri, 25 Oct 2024 12:04:35 +0200 Subject: [PATCH] feat: Support other types than string in enum schemas --- .../v3/core/jackson/ModelResolver.java | 55 +++++++++++-------- .../v3/core/converting/EnumPropertyTest.java | 17 +++--- ...Enum.java => JacksonIntegerValueEnum.java} | 6 +- ...java => JacksonIntegerValueFieldEnum.java} | 4 +- .../oas/models/ModelWithJacksonEnumField.java | 4 +- 5 files changed, 48 insertions(+), 38 deletions(-) rename modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/{JacksonNumberValueEnum.java => JacksonIntegerValueEnum.java} (80%) rename modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/{JacksonNumberValueFieldEnum.java => JacksonIntegerValueFieldEnum.java} (82%) 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 58181bcdd7..c6266cdf16 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 @@ -56,7 +56,6 @@ import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.JsonSchema; import io.swagger.v3.oas.models.media.MapSchema; -import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; @@ -306,8 +305,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context } if (model == null && type.isEnumType()) { - model = new StringSchema(); - _addEnumProps(type.getRawClass(), model); + @SuppressWarnings("unchecked") + Class> rawEnumClass = (Class>) type.getRawClass(); + model = _createSchemaForEnum(rawEnumClass); isPrimitive = true; } if (model == null) { @@ -1164,46 +1164,57 @@ protected boolean _isOptionalType(JavaType propType) { /** * Adds each enum property value to the model schema * - * @param propClass the enum class for which to add properties - * @param property the schema to add properties to + * @param enumClass the enum class for which to add properties */ - protected void _addEnumProps(Class propClass, Schema property) { - final boolean useIndex = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX); - final boolean useToString = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + protected Schema _createSchemaForEnum(Class> enumClass) { + boolean useIndex = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX); + boolean useToString = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); - Optional jsonValueMethod = Arrays.stream(propClass.getDeclaredMethods()) + Optional jsonValueMethod = Arrays.stream(enumClass.getDeclaredMethods()) .filter(m -> m.isAnnotationPresent(JsonValue.class)) .filter(m -> m.getAnnotation(JsonValue.class).value()) .findFirst(); - Optional jsonValueField = Arrays.stream(propClass.getDeclaredFields()) + Optional jsonValueField = Arrays.stream(enumClass.getDeclaredFields()) .filter(f -> f.isAnnotationPresent(JsonValue.class)) .filter(f -> f.getAnnotation(JsonValue.class).value()) .findFirst(); - jsonValueMethod.ifPresent(m -> m.setAccessible(true)); - jsonValueField.ifPresent(m -> m.setAccessible(true)); - @SuppressWarnings("unchecked") - Class> enumClass = (Class>) propClass; + Schema schema = null; + if (jsonValueField.isPresent()) { + jsonValueField.get().setAccessible(true); + PrimitiveType primitiveType = PrimitiveType.fromType(jsonValueField.get().getType()); + if (primitiveType != null) { + schema = primitiveType.createProperty(); + } + } else if (jsonValueMethod.isPresent()) { + jsonValueMethod.get().setAccessible(true); + PrimitiveType primitiveType = PrimitiveType.fromType(jsonValueMethod.get().getReturnType()); + if (primitiveType != null) { + schema = primitiveType.createProperty(); + } + } + if (schema == null) { + schema = new StringSchema(); + } Enum[] enumConstants = enumClass.getEnumConstants(); if (enumConstants != null) { - String[] enumValues = _intr.findEnumValues(propClass, enumConstants, + String[] enumValues = _intr.findEnumValues(enumClass, enumConstants, new String[enumConstants.length]); for (Enum en : enumConstants) { - String n; - Field enumField = ReflectionUtils.findField(en.name(), enumClass); if (null != enumField && enumField.isAnnotationPresent(Hidden.class)) { continue; } String enumValue = enumValues[en.ordinal()]; - String methodValue = jsonValueMethod.flatMap(m -> ReflectionUtils.safeInvoke(m, en)).map(Object::toString).orElse(null); - String fieldValue = jsonValueField.flatMap(f -> ReflectionUtils.safeGet(f, en)).map(Object::toString).orElse(null); + Object methodValue = jsonValueMethod.flatMap(m -> ReflectionUtils.safeInvoke(m, en)).orElse(null); + Object fieldValue = jsonValueField.flatMap(f -> ReflectionUtils.safeGet(f, en)).orElse(null); + Object n; if (methodValue != null) { n = methodValue; } else if (fieldValue != null) { @@ -1217,12 +1228,10 @@ protected void _addEnumProps(Class propClass, Schema property) { } else { n = _intr.findEnumValue(en); } - if (property instanceof StringSchema) { - StringSchema sp = (StringSchema) property; - sp.addEnumItem(n); - } + schema.addEnumItemObject(n); } } + return schema; } protected boolean ignore(final Annotated member, final XmlAccessorType xmlAccessorTypeAnnotation, final String propName, final Set propertiesToIgnore) { diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/EnumPropertyTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/EnumPropertyTest.java index 0893929fbf..26ff8c7718 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/EnumPropertyTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/EnumPropertyTest.java @@ -12,6 +12,7 @@ import io.swagger.v3.core.oas.models.ModelWithEnumProperty; import io.swagger.v3.core.oas.models.ModelWithEnumRefProperty; import io.swagger.v3.core.oas.models.ModelWithJacksonEnumField; +import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; import org.testng.annotations.AfterTest; @@ -216,23 +217,23 @@ public void testExtractJacksonEnumFields() { assertEquals(secondStringProperty.getEnum(), Arrays.asList("one", "two", "three")); final Schema thirdEnumProperty = (Schema) model.getProperties().get("thirdEnumValue"); - assertTrue(thirdEnumProperty instanceof StringSchema); - final StringSchema thirdStringProperty = (StringSchema) thirdEnumProperty; - assertEquals(thirdStringProperty.getEnum(), Arrays.asList("2", "4", "6")); + assertTrue(thirdEnumProperty instanceof IntegerSchema); + final IntegerSchema thirdStringProperty = (IntegerSchema) thirdEnumProperty; + assertEquals(thirdStringProperty.getEnum(), Arrays.asList(2, 4, 6)); final Schema fourthEnumProperty = (Schema) model.getProperties().get("fourthEnumValue"); assertTrue(fourthEnumProperty instanceof StringSchema); final StringSchema fourthStringProperty = (StringSchema) fourthEnumProperty; - assertEquals(fourthEnumProperty.getEnum(), Arrays.asList("one", "two", "three")); + assertEquals(fourthStringProperty.getEnum(), Arrays.asList("one", "two", "three")); final Schema fifthEnumProperty = (Schema) model.getProperties().get("fifthEnumValue"); - assertTrue(fifthEnumProperty instanceof StringSchema); - final StringSchema fifthStringProperty = (StringSchema) fifthEnumProperty; - assertEquals(fifthEnumProperty.getEnum(), Arrays.asList("2", "4", "6")); + assertTrue(fifthEnumProperty instanceof IntegerSchema); + final IntegerSchema fifthStringProperty = (IntegerSchema) fifthEnumProperty; + assertEquals(fifthStringProperty.getEnum(), Arrays.asList(2, 4, 6)); final Schema sixthEnumProperty = (Schema) model.getProperties().get("sixthEnumValue"); assertTrue(sixthEnumProperty instanceof StringSchema); final StringSchema sixthStringProperty = (StringSchema) sixthEnumProperty; - assertEquals(sixthEnumProperty.getEnum(), Arrays.asList("one", "two", "three")); + assertEquals(sixthStringProperty.getEnum(), Arrays.asList("one", "two", "three")); } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueEnum.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueEnum.java similarity index 80% rename from modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueEnum.java rename to modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueEnum.java index a5c29b9ff8..f5a927fb56 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueEnum.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueEnum.java @@ -6,7 +6,7 @@ /** * Enum holds values different from names. Schema model will derive Integer value from jackson annotation JsonValue on public method. */ -public enum JacksonNumberValueEnum { +public enum JacksonIntegerValueEnum { FIRST(2), SECOND(4), THIRD(6), @@ -14,12 +14,12 @@ public enum JacksonNumberValueEnum { private final int value; - JacksonNumberValueEnum(int value) { + JacksonIntegerValueEnum(int value) { this.value = value; } @JsonValue - public Number getValue() { + public Integer getValue() { return value; } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueFieldEnum.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueFieldEnum.java similarity index 82% rename from modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueFieldEnum.java rename to modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueFieldEnum.java index 6e94628953..591ba9b3fb 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonNumberValueFieldEnum.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/JacksonIntegerValueFieldEnum.java @@ -6,7 +6,7 @@ /** * Enum holds values different from names. Schema model will derive Integer value from jackson annotation JsonValue on private field. */ -public enum JacksonNumberValueFieldEnum { +public enum JacksonIntegerValueFieldEnum { FIRST(2), SECOND(4), THIRD(6), @@ -15,7 +15,7 @@ public enum JacksonNumberValueFieldEnum { @JsonValue private final int value; - JacksonNumberValueFieldEnum(int value) { + JacksonIntegerValueFieldEnum(int value) { this.value = value; } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/ModelWithJacksonEnumField.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/ModelWithJacksonEnumField.java index d9e154085b..e7e6cd0246 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/ModelWithJacksonEnumField.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/oas/models/ModelWithJacksonEnumField.java @@ -6,8 +6,8 @@ public class ModelWithJacksonEnumField { public JacksonPropertyEnum firstEnumValue; public JacksonValueEnum secondEnumValue; - public JacksonNumberValueEnum thirdEnumValue; + public JacksonIntegerValueEnum thirdEnumValue; public JacksonValueFieldEnum fourthEnumValue; - public JacksonNumberValueFieldEnum fifthEnumValue; + public JacksonIntegerValueFieldEnum fifthEnumValue; public JacksonValuePrivateEnum sixthEnumValue; }