Skip to content

Commit

Permalink
Merge pull request #3519 from SentryMan/avaje-validator
Browse files Browse the repository at this point in the history
Add Avaje Validator Module
  • Loading branch information
jknack authored Sep 7, 2024
2 parents 22a0f5a + 77f5825 commit 7469c72
Show file tree
Hide file tree
Showing 22 changed files with 1,154 additions and 55 deletions.
2 changes: 1 addition & 1 deletion docs/asciidoc/modules/avaje-inject.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<path>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject-generator</artifactId>
<version>10.0</version>
<version>10.3</version>
</path>
</annotationProcessorPaths>
</configuration>
Expand Down
311 changes: 311 additions & 0 deletions docs/asciidoc/modules/avaje-validator.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
== Avaje Validator

Bean validation via https://avaje.io/validator/[Avaje Validator].

=== Usage

1) Add the dependency:

[dependency, artifactId="jooby-avaje-validator"]
.

2) Configure annotation processor

.Maven
[source, xml, role = "primary"]
----
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>...</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.avaje</groupId>
<artifactId>avaje-validator-generator</artifactId>
<version>2.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
----

.Gradle
[source, kotlin, role = "secondary"]
----
plugins {
id "org.jetbrains.kotlin.kapt" version "1.9.10"
}
dependencies {
kapt 'io.avaje:avaje-validator-generator:2.1'
}
----

3) Install

.Java
[source, java, role="primary"]
----
import io.jooby.avaje.validator.AvajeValidatorModule;
{
install(new AvajeValidatorModule());
}
----

.Kotlin
[source, kt, role="secondary"]
----
import io.jooby.avaje.validator.AvajeValidatorModule
{
install(new AvajeValidatorModule())
}
----

4) Usage in MVC routes

.Java
[source,java,role="primary"]
----
import io.jooby.annotation.*;
import jakarta.validation.Valid;
@Path("/mvc")
public class Controller {
@POST("/validate-body")
public void validateBody(@Valid Bean bean) { // <1>
...
}
@POST("/validate-query")
public void validateQuery(@Valid @QueryParam Bean bean) { // <2>
...
}
@POST("/validate-list")
public void validateList(@Valid List<Bean> beans) { // <3>
...
}
@POST("/validate-map")
public void validateMap(@Valid Map<String, Bean> beans) { // <4>
...
}
}
----

.Kotlin
[source, kt, role="secondary"]
----
import io.jooby.annotation.*;
import jakarta.validation.Valid
@Path("/mvc")
class Controller {
@POST("/validate-body")
fun validateBody(@Valid bean: Bean) : Unit { // <1>
...
}
@POST("/validate-query")
fun validateQuery(@Valid @QueryParam bean: Bean) : Unit { // <2>
...
}
@POST("/validate-list")
fun validateList(@Valid beans: List<Bean>) : Unit { // <3>
...
}
@POST("/validate-map")
fun validateMap(@Valid beans: Map<String, Bean>) : Unit { // <4>
...
}
}
----

<1> Validate a bean decoded from the request body
<2> Validate a bean parsed from query parameters. This works the same for `@FormParam` or `@BindParam`
<3> Validate a list of beans. This also applies to arrays `@Valid Bean[] beans`
<4> Validate a map of beans

4) Usage in in script/lambda routes

Jooby doesn't provide fully native bean validation in script/lambda at the moment,
but you can use a helper that we utilize under the hood in MVC routes:

.Java
[source, java, role="primary"]
----
import io.jooby.validation.BeanValidator;
{
post("/validate", ctx -> {
Bean bean = BeanValidator.validate(ctx, ctx.body(Bean.class));
...
});
}
----

.Kotlin
[source, kt, role="secondary"]
----
import io.jooby.validation.BeanValidator
{
post("/validate") {
val bean = BeanValidator.validate(ctx, ctx.body(Bean.class))
...
}
}
----

`BeanValidator.validate()` behaves identically to validation in MVC routes.
It also supports validating list, array, and map of beans

=== Constraint Violations Rendering

`AvajeValidatorModule` provides default built-in error handler that
catches `ConstraintViolationException` and transforms it into the following response:

.JSON:
----
{
"title": "Validation failed",
"status": 422,
"errors": [
{
"field": "firstName",
"messages": [
"must not be empty",
"must not be null"
],
"type": "FIELD"
},
{
"field": null,
"messages": [
"passwords are not the same"
],
"type": "GLOBAL"
}
]
}
----

It is possible to override the `title` and `status` code of the response above:

[source, java]
----
{
install(new AvajeJsonbModule());
install(new AvajeValidatorModule()
.statusCode(StatusCode.BAD_REQUEST)
.validationTitle("Incorrect input data")
);
}
----

If the default error handler doesn't fully meet your needs, you can always disable it and provide your own:

[source, java]
----
{
install(new AvajeJsonbModule());
install(new AvajeValidatorModule().disableViolationHandler());
error(ConstraintViolationException.class, new MyConstraintViolationHandler());
}
----

=== Manual Validation

The module exposes `Validator` as a service, allowing you to run validation manually at any time.

==== Script/lambda:

[source, java]
----
import io.avaje.validation.Validator;
{
post("/validate", ctx -> {
Validator validator = require(Validator.class);
validator.validate(ctx.body(Bean.class));
...
});
}
----

==== MVC routes with dependency injection:

1) Install DI framework at first.

[source, java]
----
import io.jooby.avaje.validator.AvajeValidatorModule;
{
install(AvajeInjectModule.of()); // <1>
install(new AvajeValidatorModule());
}
----

<1> `Avaje` is just an example, you can achieve the same with `Dagger` or `Guice`

2) Inject `Validator` in controller, service etc.

[source, java]
----
import io.avaje.validation.Validator;
import jakarta.inject.Inject;
@Path("/mvc")
public class Controller {
private final Validator validator;
@Inject
public Controller(Validator validator) {
this.validator = validator;
}
@POST("/validate")
public void validate(Bean bean) {
Set<ConstraintViolation<Bean>> violations = validator.validate(bean);
...
}
}
----

=== Configuration
Any property defined at `validation` will be added automatically:

.application.conf
[source, properties]
----
validation.fail_fast = true
----

Or programmatically:

[source, java]
----
import io.jooby.avaje.validator.AvajeValidatorModule;
{
install(new AvajeValidatorModule().doWith(cfg -> {
cfg.failFast(true);
}));
}
----
1 change: 1 addition & 0 deletions docs/asciidoc/modules/modules.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Available modules are listed next.
* link:/modules/redis[Redis]: Redis module.

=== Validation
* link:/modules/avaje-validator[Avaje Validator]: Avaje Validator module.
* link:/modules/hibernate-validator[Hibernate Validator]: Hibernate Validator module.

=== Development Tools
Expand Down
35 changes: 6 additions & 29 deletions modules/jooby-avaje-inject/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

<modelVersion>4.0.0</modelVersion>
<artifactId>jooby-avaje-inject</artifactId>


<properties>
<maven.compiler.proc>full</maven.compiler.proc>
</properties>

<dependencies>
<dependency>
<groupId>com.github.spotbugs</groupId>
Expand All @@ -21,7 +25,6 @@
<dependency>
<groupId>io.jooby</groupId>
<artifactId>jooby</artifactId>
<version>${jooby.version}</version>
</dependency>

<!-- Avaje Inject -->
Expand All @@ -33,7 +36,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject-generator</artifactId>
<scope>provided</scope>
<scope>test</scope>
</dependency>

<!-- Test dependencies -->
Expand Down Expand Up @@ -74,30 +77,4 @@
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>test-compile</phase>
</execution>
</executions>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject-generator</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit 7469c72

Please sign in to comment.