== Hibernate Validator

Bean validation via https://hibernate.org/validator/[Hibernate Validator].

=== Usage

1) Add the dependency:

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

2) Install

.Java
[source, java, role="primary"]
----
import io.jooby.hibernate.validator.HibernateValidatorModule;

{
  install(new HibernateValidatorModule());
}
----

.Kotlin
[source, kt, role="secondary"]
----
import io.jooby.hibernate.validator.HibernateValidatorModule

{
  install(HibernateValidatorModule())
}
----

3) 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

.Java
[source, java, role="primary"]
----
import io.jooby.validation.BeanValidator;

{
  use(BeanValidator.validate());
  post("/validate", ctx -> {
    Bean bean = ctx.body(Bean.class);
    ...
  });
}
----

.Kotlin
[source, kt, role="secondary"]
----
import io.jooby.validation.BeanValidator

{
  use(BeanValidator.validate())
  post("/validate") {
    val bean = ctx, ctx.body(Bean.class)
    ...
  }
}
----

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

There is a handler version of it, so you can apply per route:

.validate
[source, java]
----
import io.jooby.validation.BeanValidator.validate;

{
  post("/validate", validate(ctx -> {
    Bean bean = ctx.body(Bean.class);
    ...
  }));
}
----

=== Constraint Violations Rendering

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

.JSON:
[source, 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"
    }
  ]
}
----

[NOTE]
====
`HibernateValidatorModule` is compliant with `ProblemDetails`. Therefore, if you enable the Problem Details feature, 
the response above will be transformed into an `RFC 7807` compliant format
====

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

[source, java]
----

{
  install(new Jackson2Module());
  install(new HibernateValidatorModule()
    .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 Jackson2Module());
  install(new HibernateValidatorModule().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 jakarta.validation.Validator;

{
  post("/validate", ctx -> {
    var validator = require(Validator.class);
    var violations = validator.validate(ctx.body(Bean.class));
    if (!violations.isEmpty()) {
      ...
    }
    ...
  });
}
----

==== MVC routes with dependency injection:

1) Install DI framework at first.

[source, java]
----
import io.jooby.hibernate.validator.HibernateValidatorModule;

{
  install(new GuiceModule());                 // <1>
  install(new HibernateValidatorModule());
}
----

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

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

[source, java]
----
import jakarta.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);
    ...
  }
}
----

=== Business rules validation

As you know, `Hibernate Validator` allows you to build fully custom `ConstraintValidator`.
In some scenarios, you may need access not only to the bean but also to services, repositories, or other resources
to perform more complex validations required by business rules.

In this case you need to implement a custom `ConstraintValidator` that will rely on your DI framework.

.Custom Annotation and Validator
[source, java]
----

@Constraint(validatedBy = MyCustomValidator.class)
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface MyCustomAnnotation {
  String message() default "My custom message";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

public class MyCustomValidator implements ConstraintValidator<MyCustomAnnotation, Bean> {

  // This is the service you want to inject
  private final MyService myService;

  @Inject
  public MyCustomValidator(MyService myService) {
    this.myService = myService;
  }

  @Override
  public boolean isValid(Bean bean, ConstraintValidatorContext context) {
    // Use the injected service for validation logic
    return myService.isValid(bean);
  }
}
----

=== Configuration
Any property defined at `hibernate.validator` will be added automatically:

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

Or programmatically:

[source, java]
----
import io.jooby.hibernate.validator.HibernateValidatorModule;

{
  var cfg = byProvider(HibernateValidator.class).configure();
  cfg.failFast(true);
  install(new HibernateValidatorModule(cfg));
}
----

=== Hibernate integration

Just install `HibernateValidatorModule` before `HibernateModule`, like:

[source, java]
----
import io.jooby.hibernate.validator.HibernateValidatorModule;

{
  install(new HibernateValidatorModule());

  install(new HibernateModule());
}
----

The `HibernateModule` will detect the constraint validator factory and setup. This avoid creating
a new instance of constraint validator factory.
