Giter Site home page Giter Site logo

mvallim / java-fluent-validator Goto Github PK

View Code? Open in Web Editor NEW
155.0 12.0 17.0 961 KB

Java Fluent Validator is inspired by Fluent Interface and .Net FluentValidation which defined an inner-DSL within Java language for programmers to use. A fluent interface implies that its primary goal is to make it easy to SPEAK and UNDERSTAND.

Home Page: https://mvallim.github.io/java-fluent-validator/

License: Apache License 2.0

Java 100.00%
java fluent validator validation validation-library validation-rules validation-engine fluent-api spring kotlin

java-fluent-validator's Introduction

Java Fluent Validator

Java CI with Maven CodeQL Quality Gate Status Coverage Maven Central Hex.pm

Validating data is a common task that occurs throughout any application, especially the business logic layer. As for some quite complex scenarios, often the same or similar validations are scattered everywhere, thus it is hard to reuse code and break the DRY rule.

Compatible JDK 8, 11, 15, 16 and 17

This library supports Kotlin aswell

Sample

Java

import static br.com.fluentvalidator.predicate.ComparablePredicate.equalTo;
import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
import static br.com.fluentvalidator.predicate.ObjectPredicate.nullValue;
import static br.com.fluentvalidator.predicate.StringPredicate.stringContains;
import static br.com.fluentvalidator.predicate.StringPredicate.stringEmptyOrNull;

import br.com.fluentvalidator.model.Boy
import br.com.fluentvalidator.model.Gender;

import br.com.fluentvalidator.AbstractValidator;

public class JavaValidatorBoy extends AbstractValidator<Boy> {

  @Override
  protected void rules() {

    ruleFor(Boy::getGender)
      .must(equalTo(Gender.MALE))
      .when(not(nullValue()))
        .withMessage("gender of boy must be MALE")
        .withFieldName("gender")
        .critical();

    ruleFor(Boy::getName)
      .must(stringContains("John"))
      .when(not(stringEmptyOrNull()))
        .withMessage("child name must contains key John")
        .withFieldName("name");
  }

}

Kotlin

import br.com.fluentvalidator.predicate.ComparablePredicate.equalTo;
import br.com.fluentvalidator.predicate.LogicalPredicate.not;
import br.com.fluentvalidator.predicate.ObjectPredicate.nullValue;
import br.com.fluentvalidator.predicate.StringPredicate.stringContains;
import br.com.fluentvalidator.predicate.StringPredicate.stringEmptyOrNull;

import br.com.fluentvalidator.model.Boy
import br.com.fluentvalidator.model.Gender;

import br.com.fluentvalidator.AbstractValidator;

class KotlinValidatorBoy : AbstractValidator<Boy> {
	
  constructor() : super();

  override fun rules() {

    ruleFor(Boy::getGender)
      .must(equalTo(Gender.MALE))
      .`when`(not(nullValue()))
        .withMessage("gender of boy must be MALE")
        .withFieldName("gender")
        .critical();

    ruleFor(Boy::getName)
      .must(stringContains("John"))
      .`when`(not(stringEmptyOrNull()))
        .withMessage("child name must contains key John")
        .withFieldName("name");
  }

}

Index

  1. Quick start
  2. Validator
  3. Builder
  4. Predicate
  5. ValidationResult
  6. PredicateBuilder
  7. FunctionBuilder
  8. Examples

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use GitHub for versioning. For the versions available, see the tags on this repository.

Authors

  • Marcos Vallim - Founder, Author, Development, Test, Documentation - mvallim
  • Paulo Sergio - Manteiner, Development, Test, Documentation - paulosergio-jnr

See also the list of contributors who participated in this project.

License

This project is licensed under the Apache License - see the LICENSE file for details

java-fluent-validator's People

Contributors

dependabot[bot] avatar mvallim avatar paulosergio-jnr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

java-fluent-validator's Issues

Suggestion: between() should be inclusive of min, max

I believe the most user-friendly implementation would be (below is Kotlin code, may not convert entirely cleanly to java):

fun <E, T : Comparable<E>?> betweenInclusive(min: E, max: E): Predicate<T>? =
    PredicateBuilder.from<T>(LogicalPredicate.not(ObjectPredicate.nullValue()))
            .and(ComparablePredicate.lessThanOrEqual(max).and(ComparablePredicate.greaterThanOrEqual(min)))

Which relies on *OrEqual functions.

Otherwise this can be implemented as a separate method from between, or as a parameter (Boolean inclusive)

ruleFor com fieldName

Seria interessante ter um método ruleFor(String fieldName, Function function), pois assim as mensagens de validação poderiam aproveitar essa informação em comum, e a declaração do fieldName não ficaria replicado por vários pontos.

Execute Predicate for each element in collection

Is your feature request related to a problem? Please describe.
I am using this package for validating complex data structures and often need to validate lists or other collection types. I want to be able to validate list items without having to write a new validator specifically for each case in which the item occurs in a list.

For example, suppose there is a class with a list of persons, and I want to ensure that every person in the list is younger than 21 years. I don't want to write a separate validator for each list that contains a person as this would be overkill for such a simple validation.

Describe the solution you'd like
I would like to be able to validate list items without having to create an additional validator, similar to the functionality provided by FluentValidation.

Describe alternatives you've considered
One alternative is to write validators for each collection in my data structure, but this would create unnecessary overhead.

Additional context
I realize that the example above may not be the best, but it should help illustrate my issue.

.xor() in Predicate

That would be a nice feature, instead of testing ((A && not(B)) || (not(A) && B))

BR

Predicate to search for a String in a list

it would be nice to have a predicate that validate whether a String is present on a Collection of Strings.

Suggestion:

ruleFor(Domain::getStringField)
  .must(stringInCollection(Arrays.asList("foo", "bar")))

It might receive any class that implements java.util.Collection

Sharing thread local data when exception occur

I detected a problem when the validation process throws an exception. The validation process is not finished and the threadlocal is not clearing the objects in memory.

The partial solution was extends AbstractValidator, catch exception and clean threadlocal.

public abstract class MyAbstractValidator<T> extends AbstractValidator<T> {

    @Override
    public ValidationResult validate(T instance) {
        try {
            return super.validate(instance);
        } catch (Exception exc) {
            ValidationContext.remove();
            throw new RuntimeException(exc);
        }
    }
}

[Query]: How to have dependent validations

Hello, first of all I have to appreciate the efforts that have gone in this library and it's pretty amazing.

I had a question though, how to do validation on one field dependent on the other?

Following is my experiment :

public class Student {

    private final String enrolmentId;
    private final String id;

    public Student(Builder builder) {
        this.enrolmentId = builder.enrolmentId;
        this.id = builder.id;
    }

    public String getId() {
        return id;
    }

    public String getEnrolmentId() {
        return enrolmentId;
    }

    public static class Builder {

        private String enrolmentId;
        private String id;

        public static Builder newInstance()
        {
            return new Student.Builder();
        }

        public Builder setEnrolmentId(String enrolmentId) {
            this.enrolmentId = enrolmentId;
            return this;
        }

        public Builder setId(String id) {
            this.id = id;
            return this;
        }

        public Student build(){
            return new Student(this);
        }

    }

}

and its validator class

import br.com.fluentvalidator.AbstractValidator;
import com.api.request.models.Student;

import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
import static br.com.fluentvalidator.predicate.StringPredicate.stringEmptyOrNull;

public class StudentValidator extends AbstractValidator<Student> {

    @Override
    public void  rules() {
        ruleFor(n -> n)
                .whenever(stringEmptyOrNull(n -> n.getEnrolmentId()))
                .must(not(stringEmptyOrNull(n -> n.getId())))
                .withMessage("Enrolment Id is null, Id must not be null");

        ruleFor(n -> n)
                .whenever(stringEmptyOrNull(n -> n.getId()))
                .must(not(stringEmptyOrNull(n -> n.getEnrolmentId())))
                .withMessage("Id is null, Enrolment Id must not be null");
    }
}

and then its test

import br.com.fluentvalidator.Validator;
import br.com.fluentvalidator.context.ValidationResult;
import com.api.request.models.Student;
import org.junit.Before;
import org.junit.Test;

import static com.google.common.truth.Truth.assertThat;

public class StudentValidatorTest {

    private Student.Builder studentBuilder;
    private Student student;
    private Validator<Student> studentValidator;

    @Before
    public void setUp() throws Exception {
        studentBuilder = Student.Builder.newInstance();
        studentValidator = new StudentValidator();
    }

    @Test
    public void shouldValidateStudentAndReturnFalse() {
        student = studentBuilder.build();
        final ValidationResult result = studentValidator.validate(student);
        assertThat(result.isValid()).isFalse();
    }
}

Sadly, the test results in a NullPointerException

java.lang.NullPointerException
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.process(RuleProcessorStrategy.java:11)
	at br.com.fluentvalidator.rule.RuleBuilderPropertyImpl$ValidatorRuleInternal.apply(RuleBuilderPropertyImpl.java:184)
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.process(RuleProcessorStrategy.java:11)
	at br.com.fluentvalidator.rule.RuleProcessorFailFast.lambda$process$0(RuleProcessorFailFast.java:9)
	at java.util.stream.MatchOps$1MatchSink.accept(MatchOps.java:90)
	at java.util.LinkedList$LLSpliterator.tryAdvance(LinkedList.java:1249)
	at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
	at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
	at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.allMatch(ReferencePipeline.java:521)
	at br.com.fluentvalidator.rule.RuleProcessorFailFast.process(RuleProcessorFailFast.java:9)
	at br.com.fluentvalidator.rule.RuleBuilderPropertyImpl.apply(RuleBuilderPropertyImpl.java:42)
	at br.com.fluentvalidator.rule.Rule.apply(Rule.java:16)
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.process(RuleProcessorStrategy.java:11)
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.lambda$process$4(RuleProcessorStrategy.java:42)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1235)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.process(RuleProcessorStrategy.java:43)
	at br.com.fluentvalidator.AbstractValidator.apply(AbstractValidator.java:120)
	at br.com.fluentvalidator.rule.RuleProcessorStrategy.process(RuleProcessorStrategy.java:15)
	at br.com.fluentvalidator.AbstractValidator.validate(AbstractValidator.java:84)
	at com.api.request.validators.StudentValidatorTest.validationTest(StudentValidatorTest.java:26)

Any help would greatly appreciated.

Question: Working with Protobuf types

First, this is a pretty cool library, I'm glad I found it, and hoping to make it standard on my projects.

I did bump into a small issue, working with protobuf types.

Let's say I have a protobuf that looks like this:

message User {

Address address = 1;
Vehicle vehicle = 2;

}

And if I write:

 ruleFor(User::getAddress)
                .must(not(nullValue()))

That would never match, because the proto java class will generate a default Address object even if the caller has not passed one.

I attempted to use

ruleFor(user -> user)
.must(isTrue(user.hasAddress())
.withMessage(...)
.whenever(isTrue(user.hasAddress())
.withValidator(new AddressValidator())

But the problem is that down the chain if I want to use whenever, its expecting a Boolean from the first must predicate I guess.

Is there a way around this? I ended up using critical at a validation and the next one I'd do the custom validator, but bumped into another problem, now, if address is empty/null Vehicle never gets a chance to be checked.

Any suggestions?

Once again, awesome library dude!

.critical() should not be needed when fail-fast strategy is used

It seems strange to require .critical() on each rule when failfast is already enabled - just wanted to file a suggestion to consider changing this design to not require the extra .critical() call

Side note: nice work on this library. This is the best alternative to FluentValidation (.NET library) that I've found for java. Thanks!

Question: is it possible to validate a hash map

Great library!

I was wondering if it was possible to validate a string map.
Let's say that I'd like to represent my data entities as string maps.

Would it be possible to define rules and run validation on string maps?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.