Evgeny Mamaev
Evgeny Mamaev

Reputation: 1357

Custom ConstraintValidator for list validating

Spring Boot 2.0.3.RELEASE, javax.validation 2.0.1.Final are used.

I need to validate a request coming in into a controller:

@RestController
@CrossOrigin
@RequestMapping("/quotas/")
@AllArgsConstructor
@Slf4j
public class QuotasController {

    private QuotasService quotasService;

    @ApiOperation("Post quotas")
    @PostMapping(value = "/quotas", produces = MediaType.APPLICATION_JSON_VALUE)
    public void quotas(@Valid @RequestBody List<Quota> quotaList){
        quotasService.insertQuotas(quotaList);
    }
}

Which calls a service:

@AllArgsConstructor
@Service
@Slf4j
public class QuotasService {
    private QuotasRepository quotasRepository;

    public void insertQuotas(List<Quota> quotaList) {
        quotasRepository.saveAll(quotaList);
    }
}

Which saves the objects into MongoRepository method <S extends T> List<S> saveAll(Iterable<S> var1).

Also I have a custom annotation:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = 
SumOfValuesEqualsToOneHundredValidator.class)
public @interface SumOfValuesEqualsToOneHundred {
    String message() default "Sum of fields doesn't equal to 100";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Which is validated by:

public class SumOfValuesEqualsToOneHundredValidator implements 
ConstraintValidator<SumOfValuesEqualsToOneHundred, List<QuotaPayment>> 
{
    @Override
    public void initialize(SumOfValuesEqualsToOneHundred sumOfValuesEqualsToOneHundred) {
        //nothing to do
    }

    @Override
    public boolean isValid(List<QuotaPayment> quotaPaymentList, 
    ConstraintValidatorContext context) {
         return quotaPaymentList
                .stream()
                .mapToDouble(QuotaPayment::getValue)
                .sum() == 100;
    }
}

My aim is to validate DTO:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Quota {
    @Id
    private Integer storeId;
    @JsonProperty("quotas")
    private List<QuotaDelivery> quotaDeliveryList;
}

Specifically QuotaDelivery objects inside:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class QuotaDelivery {
    QuotaDeliveryTypeEnum type;
    @JsonProperty("quotasForPaymentTypes")
    @SumOfValuesEqualsToOneHundred
    List<QuotaPayment> quotaPaymentList;
}

Against equality of sum of QuotaPayments' values to 100.

Another words, only those Quota objects are valid, whose values from QuotaDelivery from QuotaPayment give 100 in total.

The problem is forementioned SumOfValuesEqualsToOneHundredValidator is been ignored. I mean debug mode shows that control never executes public boolean isValid(List<QuotaPayment> quotaPaymentList, ConstraintValidatorContext context) method.

What did I miss?

Any help is appreciated.

Upvotes: 2

Views: 1977

Answers (1)

GolamMazid Sajib
GolamMazid Sajib

Reputation: 9437

Use @Valid inside list and @Validated in controller class:

@RestController
@CrossOrigin
@RequestMapping("/quotas/")
@AllArgsConstructor
@Slf4j
@Validated
public class QuotasController {

private QuotasService quotasService;

@ApiOperation("Post quotas")
@PostMapping(value = "/quotas", produces = MediaType.APPLICATION_JSON_VALUE)
public void quotas(@Valid @RequestBody List<Quota> quotaList){
    quotasService.insertQuotas(quotaList);
}

}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Quota {
    @Id
    private Integer storeId;
    @JsonProperty("quotas")
    private List< @Valid QuotaDelivery> quotaDeliveryList;
}

List< @Valid QuotaPayment> quotaPaymentList;

Upvotes: 2

Related Questions