diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java new file mode 100644 index 0000000..20308aa --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java @@ -0,0 +1,20 @@ +package it.aboutbits.springboot.toolbox.swagger; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SwaggerMeta { + private String originalTypeFqn = null; + @JsonProperty("isIdentity") + private Boolean isIdentity = null; + @JsonProperty("isNestedStructure") + private Boolean isNestedStructure = null; + @JsonProperty("isMap") + private Boolean isMap = null; + private String mapKeyTypeFqn = null; +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMetaUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMetaUtil.java new file mode 100644 index 0000000..5332d0b --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMetaUtil.java @@ -0,0 +1,68 @@ +package it.aboutbits.springboot.toolbox.swagger; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.Nullable; + +@Slf4j +public final class SwaggerMetaUtil { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private SwaggerMetaUtil() { + } + + @SneakyThrows(JsonProcessingException.class) + public static String setMapKeyTypeFqn(@Nullable String currentMeta, @NonNull String value) { + var meta = getSwaggerMeta(currentMeta); + meta.setMapKeyTypeFqn(value); + + return OBJECT_MAPPER.writeValueAsString(meta); + } + + @SneakyThrows(JsonProcessingException.class) + public static String setIsMap(@Nullable String currentMeta, boolean value) { + var meta = getSwaggerMeta(currentMeta); + meta.setIsMap(!value ? null : true); + + return OBJECT_MAPPER.writeValueAsString(meta); + } + + @SneakyThrows(JsonProcessingException.class) + public static String setOriginalTypeFqn(@Nullable String currentMeta, @NonNull String value) { + var meta = getSwaggerMeta(currentMeta); + meta.setOriginalTypeFqn(value); + + return OBJECT_MAPPER.writeValueAsString(meta); + } + + @SneakyThrows(JsonProcessingException.class) + public static String setIsIdentity(@Nullable String currentMeta, boolean value) { + var meta = getSwaggerMeta(currentMeta); + meta.setIsIdentity(!value ? null : true); + + return OBJECT_MAPPER.writeValueAsString(meta); + } + + @SneakyThrows(JsonProcessingException.class) + public static String setIsNestedStructure(@Nullable String currentMeta, boolean value) { + var meta = getSwaggerMeta(currentMeta); + meta.setIsNestedStructure(!value ? null : true); + + return OBJECT_MAPPER.writeValueAsString(meta); + } + + private static SwaggerMeta getSwaggerMeta(String currentMeta) { + var meta = new SwaggerMeta(); + if (currentMeta != null) { + try { + meta = OBJECT_MAPPER.readValue(currentMeta, SwaggerMeta.class); + } catch (JsonProcessingException e) { + log.warn("error processing meta for swagger: {}", currentMeta); + } + } + return meta; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java index f0aa8cf..4f66df2 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java @@ -5,8 +5,10 @@ import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.oas.models.media.Schema; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.swagger.SwaggerMetaUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; import java.math.BigDecimal; import java.math.BigInteger; @@ -23,36 +25,52 @@ public Schema resolve( var type = annotatedType.getType(); - if (type instanceof Class clazz && CustomType.class.isAssignableFrom(clazz)) { - var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); - var wrappedType = constructor.getParameters()[0].getType(); + if (type instanceof Class clazz) { + Schema result = null; + if (CustomType.class.isAssignableFrom(clazz)) { + var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); + var wrappedType = constructor.getParameters()[0].getType(); - if (Short.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Short.TYPE)); - } - if (Integer.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Integer.TYPE)); - } - if (Long.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Long.TYPE)); - } - if (BigInteger.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Long.TYPE)); - } - if (Float.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Float.TYPE)); - } - if (Double.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (BigDecimal.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (String.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(String.class)); + if (Short.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Short.TYPE)); + } + if (Integer.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Integer.TYPE)); + } + if (Long.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Long.TYPE)); + } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Long.TYPE)); + } + if (Float.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Float.TYPE)); + } + if (Double.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Double.TYPE)); + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Double.TYPE)); + } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(Double.TYPE)); + } + if (String.class.isAssignableFrom(wrappedType)) { + result = context.resolve(new AnnotatedType(String.class)); + } + + if (result != null) { + var isIdentity = EntityId.class.isAssignableFrom(clazz); + + var description = SwaggerMetaUtil.setOriginalTypeFqn( + result.getDescription(), + ((Class) type).getName() + ); + description = SwaggerMetaUtil.setIsIdentity(description, isIdentity); + result.setDescription(description); + + return result; + } } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java index d34f3a5..bd58893 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java @@ -4,10 +4,10 @@ import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.oas.models.media.Schema; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.swagger.SwaggerMetaUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import it.aboutbits.springboot.toolbox.type.identity.EntityId; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.PropertyCustomizer; @@ -23,116 +23,103 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { if (type instanceof SimpleType simpleType) { var rawClass = simpleType.getRawClass(); - // We set the enum name as the description because swagger treats each usage as a new enum. - // This way we can preserve the information about the original enum type. - if (rawClass.isEnum()) { - var displayName = rawClass.getCanonicalName().replace(rawClass.getPackage().getName() + ".", ""); - property.setDescription(displayName); + if (rawClass.getName().contains(".") && !rawClass.getName().startsWith("java.")) { + property.setDescription(SwaggerMetaUtil.setOriginalTypeFqn( + property.getDescription(), + rawClass.getName() + )); } - } - - if (type instanceof SimpleType simpleType && CustomType.class.isAssignableFrom(simpleType.getRawClass())) { - var rawClass = simpleType.getRawClass(); - var displayName = rawClass.getSimpleName(); + if (CustomType.class.isAssignableFrom(simpleType.getRawClass())) { + Class wrappedType; + if (rawClass.equals(EntityId.class)) { + wrappedType = simpleType.getBindings().getBoundType(0).getRawClass(); + } else { + var constructor = RecordReflectionUtil.getCanonicalConstructor(rawClass); + wrappedType = constructor.getParameters()[0].getType(); + } - Class wrappedType; - if (EntityId.class.isAssignableFrom(rawClass)) { - displayName = resolveEntityIdDisplayName(rawClass); - } + var isIdentity = EntityId.class.isAssignableFrom(rawClass); - if (rawClass.equals(EntityId.class)) { - wrappedType = simpleType.getBindings().getBoundType(0).getRawClass(); - } else { - var constructor = RecordReflectionUtil.getCanonicalConstructor(rawClass); - wrappedType = constructor.getParameters()[0].getType(); - } + var description = SwaggerMetaUtil.setIsIdentity(property.getDescription(), isIdentity); - if (Short.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Integer.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int32"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Long.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int64"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (BigInteger.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int64"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Float.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format("float"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Double.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format("double"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (BigDecimal.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } + if (Short.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Integer.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int32"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Long.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Float.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("float"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Double.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("double"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } - if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (String.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format(null); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return property; + } + log.warn("Property {} of type WrappedValue: Can not resolve parameter type!", property.getName()); } - if (String.class.isAssignableFrom(wrappedType)) { - property.type("string"); - property.format(null); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - log.warn("Property {} of type WrappedValue: Can not resolve parameter type!", property.getName()); } return property; } - - @NonNull - private static String resolveEntityIdDisplayName(Class rawClass) { - var parent = rawClass.getEnclosingClass(); - if (parent != null) { - return parent.getSimpleName() + "." + rawClass.getSimpleName(); - } - return rawClass.getSimpleName(); - } }