Skip to content

Commit

Permalink
GH-204 Support OpenAPI type validators (Resolve #204)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Feb 17, 2024
1 parent 49689e3 commit 2ea601b
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.javalin.openapi.JsonSchemaResource;
import io.javalin.openapi.OneOf;
import io.javalin.openapi.OpenApi;
import io.javalin.openapi.OpenApiArrayValidation;
import io.javalin.openapi.OpenApiByFields;
import io.javalin.openapi.OpenApiCallback;
import io.javalin.openapi.OpenApiContent;
Expand All @@ -21,11 +22,14 @@
import io.javalin.openapi.OpenApiExampleProperty;
import io.javalin.openapi.OpenApiIgnore;
import io.javalin.openapi.OpenApiName;
import io.javalin.openapi.OpenApiNumberValidation;
import io.javalin.openapi.OpenApiObjectValidation;
import io.javalin.openapi.OpenApiParam;
import io.javalin.openapi.OpenApiPropertyType;
import io.javalin.openapi.OpenApiRequestBody;
import io.javalin.openapi.OpenApiResponse;
import io.javalin.openapi.OpenApiSecurity;
import io.javalin.openapi.OpenApiStringValidation;
import io.javalin.openapi.Visibility;
import io.javalin.openapi.plugin.OpenApiPlugin;
import io.javalin.openapi.plugin.redoc.ReDocPlugin;
Expand Down Expand Up @@ -369,6 +373,28 @@ public String getFormattedMessage() {

// should contain example for primitive types, SwaggerUI will automatically display this as an Integer
@OpenApiExample("5050")
@OpenApiNumberValidation(
minimum = "5000",
exclusiveMinimum = true,
maximum = "6000",
exclusiveMaximum = true,
multipleOf = "50"
)
@OpenApiStringValidation(
minLength = "4",
maxLength = "4",
pattern = "^[0-9]{4}$",
format = "int32"
)
@OpenApiArrayValidation(
minItems = "1",
maxItems = "1",
uniqueItems = true
)
@OpenApiObjectValidation(
minProperties = "1",
maxProperties = "1"
)
public int getVeryImportantNumber() {
return status + 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,40 @@ enum class Nullability {
AUTO
}

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(RUNTIME)
annotation class OpenApiNumberValidation(
val minimum: String = NULL_STRING,
val exclusiveMinimum: Boolean = false,
val maximum: String = NULL_STRING,
val exclusiveMaximum: Boolean = false,
val multipleOf: String = NULL_STRING
)

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(RUNTIME)
annotation class OpenApiStringValidation(
val minLength: String = NULL_STRING,
val maxLength: String = NULL_STRING,
val format: String = NULL_STRING,
val pattern: String = NULL_STRING
)

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(RUNTIME)
annotation class OpenApiArrayValidation(
val minItems: String = NULL_STRING,
val maxItems: String = NULL_STRING,
val uniqueItems: Boolean = false
)

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(RUNTIME)
annotation class OpenApiObjectValidation(
val minProperties: String = NULL_STRING,
val maxProperties: String = NULL_STRING,
)

@Target(CLASS, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(RUNTIME)
annotation class OpenApiPropertyType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import io.javalin.openapi.CustomAnnotation
import io.javalin.openapi.JsonSchema
import io.javalin.openapi.NULL_STRING
import io.javalin.openapi.Nullability
import io.javalin.openapi.OpenApiArrayValidation
import io.javalin.openapi.OpenApiByFields
import io.javalin.openapi.OpenApiDescription
import io.javalin.openapi.OpenApiExample
import io.javalin.openapi.OpenApiIgnore
import io.javalin.openapi.OpenApiName
import io.javalin.openapi.OpenApiNumberValidation
import io.javalin.openapi.OpenApiObjectValidation
import io.javalin.openapi.OpenApiPropertyType
import io.javalin.openapi.OpenApiRequired
import io.javalin.openapi.OpenApiStringValidation
import io.javalin.openapi.Visibility
import io.javalin.openapi.experimental.AnnotationProcessorContext
import io.javalin.openapi.experimental.ClassDefinition
import io.javalin.openapi.experimental.CustomProperty
import io.javalin.openapi.experimental.EmbeddedTypeProcessorContext
import io.javalin.openapi.experimental.processor.generators.ExampleGenerator.ExampleProperty
import io.javalin.openapi.experimental.processor.generators.ExampleGenerator.toExampleProperty
import io.javalin.openapi.experimental.processor.shared.MessagerWriter
import io.javalin.openapi.experimental.processor.shared.getTypeMirror
Expand Down Expand Up @@ -319,6 +322,31 @@ private fun Element.findExtra(context: AnnotationProcessorContext): Map<String,
}
}

getAnnotationsByType(OpenApiNumberValidation::class.java).forEach { validation ->
extra["minimum"] = validation.minimum.takeIf { it != NULL_STRING }
extra["maximum"] = validation.maximum.takeIf { it != NULL_STRING }
extra["exclusiveMinimum"] = validation.exclusiveMinimum.takeIf { it }
extra["exclusiveMaximum"] = validation.exclusiveMaximum.takeIf { it }
extra["multipleOf"] = validation.multipleOf.takeIf { it != NULL_STRING }
}

getAnnotationsByType(OpenApiStringValidation::class.java).forEach { validation ->
extra["minLength"] = validation.minLength.takeIf { it != NULL_STRING }
extra["maxLength"] = validation.maxLength.takeIf { it != NULL_STRING }
extra["pattern"] = validation.pattern.takeIf { it != NULL_STRING }
}

getAnnotationsByType(OpenApiArrayValidation::class.java).forEach { validation ->
extra["minItems"] = validation.minItems.takeIf { it != NULL_STRING }
extra["maxItems"] = validation.maxItems.takeIf { it != NULL_STRING }
extra["uniqueItems"] = validation.uniqueItems.takeIf { it }
}

getAnnotationsByType(OpenApiObjectValidation::class.java).forEach { validation ->
extra["minProperties"] = validation.minProperties.takeIf { it != NULL_STRING }
extra["maxProperties"] = validation.maxProperties.takeIf { it != NULL_STRING }
}

getAnnotationsByType(Custom::class.java).forEach { custom ->
extra[custom.name] = custom.value
}
Expand All @@ -327,7 +355,7 @@ private fun Element.findExtra(context: AnnotationProcessorContext): Map<String,
.filterNot { it.annotationType.getFullName() == Metadata::class.qualifiedName }
.onEach { annotation -> inDebug { it.info("TypeSchemaGenerator#findExtra | Annotation: ${annotation.annotationType}") } }
.filter { annotation ->
val isCustom = annotation.annotationType.asElement().getAnnotation(CustomAnnotation::class.java) != null
val isCustom = annotation.annotationType?.asElement()?.getAnnotation(CustomAnnotation::class.java) != null

if (!isCustom) {
inDebug {
Expand Down
2 changes: 1 addition & 1 deletion wiki
Submodule wiki updated from b94cc1 to c865b6

0 comments on commit 2ea601b

Please sign in to comment.