" + description + "
" + currentDescription.get(); - } - - operation.description( - description - ); - } - } catch (Exception e) { - log.error("Error when creating swagger documentation for authorities.", e); - } - return operation; - } -} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeModelConverter.java similarity index 98% rename from src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java rename to src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeModelConverter.java index c8b8539..4181a6c 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypeModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeModelConverter.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.swagger.type; +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverter; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeOpenApiCustomizerForCodeGenerator.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeOpenApiCustomizerForCodeGenerator.java new file mode 100644 index 0000000..5ac7f00 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeOpenApiCustomizerForCodeGenerator.java @@ -0,0 +1,45 @@ +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.swagger.SwaggerMetaUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.OpenApiCustomizer; + +import static it.aboutbits.springboot.toolbox.swagger.customization.custom_type.SchemaUtil.getClassFromSchemaReference; + +@Slf4j +public class CustomTypeOpenApiCustomizerForCodeGenerator implements OpenApiCustomizer { + @Override + public void customise(OpenAPI openApi) { + openApi.getComponents().getSchemas().forEach(this::updateSchema); + } + + + private void updateSchema(String fqn, Schema> schema) { + var type = getClassFromSchemaReference(fqn); + + if (type.isEmpty()) { + log.debug("Can not resolve type for schema reference: {}", fqn); + return; + } + + var clazz = type.get(); + + if (CustomType.class.isAssignableFrom(clazz)) { + + var isIdentity = EntityId.class.isAssignableFrom(clazz); + + var description = SwaggerMetaUtil.setOriginalTypeFqn( + schema.getDescription(), + fqn + ); + description = SwaggerMetaUtil.setIsIdentity(description, isIdentity); + description = SwaggerMetaUtil.setIsCustomType(description, true); + schema.setDescription(description); + } + + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizer.java new file mode 100644 index 0000000..fe4e5d4 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizer.java @@ -0,0 +1,199 @@ +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import it.aboutbits.springboot.toolbox.reflection.util.CustomTypeReflectionUtil; +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.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.ParameterCustomizer; +import org.springframework.core.MethodParameter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.UUID; + +import static it.aboutbits.springboot.toolbox.swagger.customization.custom_type.SchemaUtil.getClassFromSchemaReference; + +@Slf4j +public class CustomTypeParameterCustomizer implements ParameterCustomizer { + @SuppressWarnings("checkstyle:MethodLength") + @Override + @SneakyThrows(NoSuchMethodException.class) + public Parameter customize(Parameter parameter, MethodParameter methodParameter) { + if (parameter == null) { + return null; + } + + String schemaRef = null; + var property = parameter.getSchema(); + + if (parameter.getSchema() instanceof ArraySchema + && parameter.getSchema().getItems() != null + && parameter.getSchema().getItems().get$ref() != null + ) { + schemaRef = parameter.getSchema().getItems().get$ref(); + property = parameter.getSchema().getItems(); + } + if (parameter.getSchema() instanceof ObjectSchema + && parameter.getSchema().get$ref() != null + ) { + schemaRef = parameter.getSchema().getItems().get$ref(); + } + + if (schemaRef == null) { + return parameter; + } + + var optionalClass = getClassFromSchemaReference(schemaRef); + + if (optionalClass.isEmpty()) { + log.error("Can not resolve type for schema reference: {}", schemaRef); + return parameter; + } + + + var rawClass = optionalClass.get(); + + if (rawClass.getName().contains(".") && !rawClass.getName().startsWith("java.")) { + property.setDescription(SwaggerMetaUtil.setOriginalTypeFqn( + property.getDescription(), + rawClass.getName() + )); + } + + if (CustomType.class.isAssignableFrom(rawClass)) { + Class> wrappedType = null; + if (!rawClass.equals(EntityId.class)) { + wrappedType = CustomTypeReflectionUtil.getWrappedType( + (Class extends CustomType>>) rawClass + ); + } + + var isIdentity = EntityId.class.isAssignableFrom(rawClass); + + var description = SwaggerMetaUtil.setIsIdentity(property.getDescription(), isIdentity); + + if (wrappedType == null) { + return parameter; + } + + if (Boolean.class.isAssignableFrom(wrappedType)) { + property.type("boolean"); + property.format(null); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Byte.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Short.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Integer.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int32"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Long.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Float.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("float"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Double.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("double"); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (String.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format(null); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (Character.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format(null); + property.minLength(1); + property.maxLength(1); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + if (UUID.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format("uuid"); + property.minLength(36); + property.maxLength(36); + property.setDescription(description); + property.setProperties(null); + property.set$ref(null); + return parameter; + } + + log.warn("Property {} of type WrappedValue: Can not resolve parameter type!", property.getName()); + } + + return parameter; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizerForCodeGenerator.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizerForCodeGenerator.java new file mode 100644 index 0000000..dfe39de --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypeParameterCustomizerForCodeGenerator.java @@ -0,0 +1,61 @@ +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import it.aboutbits.springboot.toolbox.swagger.SwaggerMetaUtil; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.ParameterCustomizer; +import org.springframework.core.MethodParameter; + +import static it.aboutbits.springboot.toolbox.swagger.customization.custom_type.SchemaUtil.getClassFromSchemaReference; + +@Slf4j +public class CustomTypeParameterCustomizerForCodeGenerator implements ParameterCustomizer { + @SuppressWarnings("checkstyle:MethodLength") + @Override + public Parameter customize(Parameter parameter, MethodParameter methodParameter) { + if (parameter == null) { + return null; + } + + String schemaRef = null; + var property = parameter.getSchema(); + + if (parameter.getSchema() instanceof ArraySchema + && parameter.getSchema().getItems() != null + && parameter.getSchema().getItems().get$ref() != null + ) { + schemaRef = parameter.getSchema().getItems().get$ref(); + property = parameter.getSchema().getItems(); + } + if (parameter.getSchema() instanceof ObjectSchema + && parameter.getSchema().get$ref() != null + ) { + schemaRef = parameter.getSchema().getItems().get$ref(); + } + + if (schemaRef == null) { + return parameter; + } + + var optionalClass = getClassFromSchemaReference(schemaRef); + + if (optionalClass.isEmpty()) { + log.error("Can not resolve type for schema reference: {}", schemaRef); + return parameter; + } + + + var rawClass = optionalClass.get(); + + if (rawClass.getName().contains(".") && !rawClass.getName().startsWith("java.")) { + property.setDescription(SwaggerMetaUtil.setOriginalTypeFqn( + property.getDescription(), + rawClass.getName() + )); + } + + return parameter; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizer.java similarity index 98% rename from src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java rename to src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizer.java index 85bcadc..093a926 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/type/CustomTypePropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizer.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.swagger.type; +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; import com.fasterxml.jackson.databind.type.SimpleType; import io.swagger.v3.core.converter.AnnotatedType; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizerForCodeGenerator.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizerForCodeGenerator.java new file mode 100644 index 0000000..180f850 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/CustomTypePropertyCustomizerForCodeGenerator.java @@ -0,0 +1,29 @@ +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; + +import com.fasterxml.jackson.databind.type.SimpleType; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.swagger.SwaggerMetaUtil; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.PropertyCustomizer; + +@Slf4j +public class CustomTypePropertyCustomizerForCodeGenerator implements PropertyCustomizer { + @Override + public Schema> customize(Schema property, AnnotatedType annotatedType) { + var type = annotatedType.getType(); + + if (type instanceof SimpleType simpleType) { + var rawClass = simpleType.getRawClass(); + + if (rawClass.getName().contains(".") && !rawClass.getName().startsWith("java.")) { + property.setDescription(SwaggerMetaUtil.setOriginalTypeFqn( + property.getDescription(), + rawClass.getName() + )); + } + } + + return property; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/SchemaUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/SchemaUtil.java new file mode 100644 index 0000000..25e54ca --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/custom_type/SchemaUtil.java @@ -0,0 +1,17 @@ +package it.aboutbits.springboot.toolbox.swagger.customization.custom_type; + +import java.util.Optional; + +final class SchemaUtil { + private SchemaUtil() { + } + + static Optional
+ * This customizer adds a nullability maker. Later we process this to manipulate the required fields of a schema.
+ */
@Component
public class NullablePropertyCustomizer implements PropertyCustomizer {
static {
/*
- We need this because the ModelResolver will only process these whitelisted annotations.
- We will then be able to manipulate each property based on the set annotations.
+ We need this because the ModelResolver will only retain annotations with a matching name
+ that is contained in the list.
*/
var list = new ArrayList<>(ModelResolver.NOT_NULL_ANNOTATIONS);
@@ -25,15 +33,27 @@ public class NullablePropertyCustomizer implements PropertyCustomizer {
@Override
public Schema> customize(Schema property, AnnotatedType annotatedType) {
- /*
- Mark the nullable ones as nullable.
- */
+ if (isAnnotatedAsNullable(annotatedType)) {
+ property.setTitle(NULLABLE_MARKER);
- if (annotatedType.getCtxAnnotations() != null && Arrays.stream(annotatedType.getCtxAnnotations())
- .anyMatch(a -> "Nullable".equals(a.annotationType().getSimpleName()))) {
- property.setTitle("NULLABLE");
+ // refs do not retain other properties
+ if (property.get$ref() != null) {
+ property.set$ref(property.get$ref() + NULLABLE_MARKER);
+ }
}
return property;
}
+
+ @SuppressWarnings("java:S1872") // Disabled: "Use an 'instanceof' comparison instead." We MUST match by name.
+ private static boolean isAnnotatedAsNullable(AnnotatedType annotatedType) {
+ if (annotatedType.getCtxAnnotations() == null) {
+ return false;
+ }
+
+ return Arrays.stream(annotatedType.getCtxAnnotations())
+ .anyMatch(
+ a -> "Nullable".equals(a.annotationType().getSimpleName())
+ );
+ }
}
diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/error_response/ErrorCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/error_response/ErrorCustomizer.java
deleted file mode 100644
index d7c246a..0000000
--- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/customization/error_response/ErrorCustomizer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package it.aboutbits.springboot.toolbox.swagger.customization.error_response;
-
-import io.swagger.v3.core.converter.ModelConverters;
-import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.oas.models.media.Content;
-import io.swagger.v3.oas.models.media.MediaType;
-import io.swagger.v3.oas.models.media.Schema;
-import io.swagger.v3.oas.models.responses.ApiResponse;
-import io.swagger.v3.oas.models.responses.ApiResponses;
-import it.aboutbits.springboot.toolbox.web.response.ErrorResponse;
-import org.springdoc.core.customizers.OpenApiCustomizer;
-
-import java.util.Map;
-
-public class ErrorCustomizer implements OpenApiCustomizer {
- @Override
- public void customise(OpenAPI openApi) {
- openApi.getComponents()
- .getSchemas()
- .putAll(
- ModelConverters.getInstance().read(ErrorResponse.class)
- );
-
- var errorResponseSchema = openApi.getComponents().getSchemas().get("ErrorResponse");
- @SuppressWarnings("unchecked")
- Map