Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support other types than string in enum schemas #4769

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Enum<?>> rawEnumClass = (Class<Enum<?>>) type.getRawClass();
model = _createSchemaForEnum(rawEnumClass);
isPrimitive = true;
}
if (model == null) {
Expand Down Expand Up @@ -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<Enum<?>> enumClass) {
boolean useIndex = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX);
boolean useToString = _mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

Optional<Method> jsonValueMethod = Arrays.stream(propClass.getDeclaredMethods())
Optional<Method> jsonValueMethod = Arrays.stream(enumClass.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(JsonValue.class))
.filter(m -> m.getAnnotation(JsonValue.class).value())
.findFirst();

Optional<Field> jsonValueField = Arrays.stream(propClass.getDeclaredFields())
Optional<Field> 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<Enum<?>> enumClass = (Class<Enum<?>>) 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) {
Expand All @@ -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<String> propertiesToIgnore) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
/**
* 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),
@Hidden HIDDEN(-1);

private final int value;

JacksonNumberValueEnum(int value) {
JacksonIntegerValueEnum(int value) {
this.value = value;
}

@JsonValue
public Number getValue() {
public Integer getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -15,7 +15,7 @@ public enum JacksonNumberValueFieldEnum {
@JsonValue
private final int value;

JacksonNumberValueFieldEnum(int value) {
JacksonIntegerValueFieldEnum(int value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}