Search code examples
javamicronaut

Validation Constraint not working on a List of String


I have created a custom validator to validate Strings. It works on a single String but not on a List of Strings. This is what I have tried so far:

@Get("/test1")
public String test1(
       @QueryValue(value = "ids") List<@DurationPattern String> ids) { //NOT WORKING
    return "not working";
}

@Get("/test2")
public String test2(
        @QueryValue(value = "id") @DurationPattern String id){ //WORKS
    //it does not get here which is what I want.
    return "done";
}

My @DurationPattern code:

package my.package;

import javax.validation.Constraint;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import my.package.DurationPattern.List;

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE, LOCAL_VARIABLE, PACKAGE, TYPE_PARAMETER, MODULE })
@Repeatable(List.class)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { })
public @interface DurationPattern {

    String message() default "invalid duration ({validatedValue})";

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE, LOCAL_VARIABLE, PACKAGE, TYPE_PARAMETER, MODULE})
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        DurationPattern[] value();
    }
} 

Actual Validator:

package my.package;

import io.micronaut.context.annotation.Factory;
import io.micronaut.validation.validator.constraints.ConstraintValidator;

import javax.inject.Singleton;

@Factory
public class MyValidatorFactory {

    @Singleton
    ConstraintValidator<DurationPattern, CharSequence> durationPatternValidator() {
        return (value, annotationMetadata, context) -> {
            System.out.println("Please Print!!! It doesn't for Strings within List");
            return value == null || value.toString().matches("^PT?[\\d]+[SMHD]{1}$");
        };
    }
}

Solution

  • Basically, create an implementation of io.micronaut.validation.validator.constraints.ConstraintValidator and provide them inside @Constraint(validatedBy = { })

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Repeatable(Regexp.List.class)
    @Retention(RUNTIME)
    @Documented
    @Constraint(validatedBy = {DurationPatternValidator.class, DurationPatternValidatorList.class}) //to validate a single string or a list of strings
    public @interface DurationPattern {
        ...
    }
     
    
    @Singleton
    public class RegexValidatorList implements ConstraintValidator<DurationPattern, Collection<CharSequence>> {
    
    @Override
    public boolean isValid(...) {
       ....
    }
    

    And then in the controller's handler methods you would use

    @Get("/test1")
    public String test1(
           @QueryValue(value = "ids") @DurationPattern List<String> ids) { //@DurationPattern would appear outside of diamond brackets for it to work.
        return "working now";
    }