I am writing a code to validate category using vavr
private static Validation<ConstraintViolation, List<Category>> isCategoryValid(
List<Category> categories) {
java.util.List<Category> categoryList = new ArrayList<>();
for (Category category : categories) {
List<Product> detailsRequest = validateList(category.getDetails());
if (detailsRequest .isEmpty()) {
return invalid(new ConstraintViolation("Details", "Details cannot be empty"));
}
...more validations
categoryList.add(WCategory.builder().details(List.ofAll(detailsList))
.type(category.getType()).build());
}
return valid(List.ofAll(categoryList));
}
I have to use java.util.List
as I cannot achieve it using vavr itself. If I use
categories.map(x -> x..);
I cannot return from the loop if validation fails and will get the output as List<Validations<ConstraintViolation, List<Category>>>
which is not what I wanted.
EDIT:
private static Validation<RuntimeException, List<String>> isCategoryValid(
List<String> categories) {
java.util.List<String> categoryList = new ArrayList<>();
for (String category : categories) {
String detailsRequest = validateList(category);
if (detailsRequest != "") {
return invalid(new RuntimeException("Details cannot be empty"));
}
...more validations
categoryList.add(detailsRequest);
}
return valid(List.ofAll(categoryList));
}
It depends a bit on the behaviour you'd like to achieve. If you only want to get the failed validation for the first category in the list, which seems to be the case, you could use Either.traverseRight
and then convert that to a validation. The traverseRight
will only keep the first failed entry or the list of succesful things.
So the code could look like this:
private static Validation<RuntimeException, List<String>> isCategoryValid(List<String> categories) {
return Validation.fromEither(
Either.traverseRight(categories, Example::validateCategory).map(Seq::toList)
);
}
private static Either<RuntimeException, String> validateCategory(String category) {
// ...more validations
return category.equals("") ? Either.left(new RuntimeException("Details cannot be empty")) : Either.right(category);
}
It might also be a good idea, depending on the usecase, to keep more validation errors. If you opt for this approach, you might look into the Validation.traverse
to keep as much validation errors as possible. That's where the Validation
really shines in my opinion.
So then the code would look like this:
private static Validation<Seq<RuntimeException>, List<String>> isCategoryValid(
List<String> categories) {
return Validation.traverse(categories, Example::validateCategory).map(Seq::toList);
}
private static Validation<List<RuntimeException>, String> validateCategory(String category) {
// ... combine multiple validations for the same category
return category.equals("") ? invalid(List.of(new RuntimeException("Details cannot be empty"))) : valid(category);
}