Skip to content

Commit

Permalink
feat: Refactor enum schema generation
Browse files Browse the repository at this point in the history
Refactor the method for generating enum schemas to better handle type-specific schemas, such as IntegerSchema. This change ensures that integer-based enums are correctly represented in the generated schemas.
  • Loading branch information
T3rm1 committed Oct 25, 2024
1 parent dc8785e commit 3bd0ba0
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 38 deletions.
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;
}

0 comments on commit 3bd0ba0

Please sign in to comment.