GithubHelp home page GithubHelp logo

guard's Introduction

guard

Guard is a simple and easy-to-use, general purpose, validation library for Java.

The idea behind Guard is to guard objects, or more specifically, the data properties of an object.

Guard provides a variety of constraints that can be applied to the data properties of an object. Once all constraints have been applied, you can retrieve the list of violation which contain the metadata of the data property eg. it's name, value and error message.

Once all the violations have been gathered you can throw an exception to return all the messages to the user.

In the context of a REST api, you could return a 400 (BadRequest) containing the list of violations. Even better is to extend the Problem standard (RFC 9457) and add your violations as part of the payload.

By default Guard is set to lenient mode, meaning it simply stores the violations to a list. You can also set Guard to fail-fast. In this mode Guard will throw an exception immediately if a constraint is violated.

Why not use Bean Validation or a different Validation library?

Bean Validation is great for static validation of classes, it doesn't cater for runtime validation of objects.

Bean Validation shines when applying constraints on data structures on the boundary of the application eg. Data Transfer Objects (DTO) that is often used as input to REST requests.

But Bean Validation isn't intended for dynamic validations at runtime.

For example, given a REST endpoint to add users we must first check for an existing user in the database with the same username before adding.

In the above scenario Bean Validation isn't the tool for the job.

This is where an Object / Runtime validator can be used.

There are other Java validation libraries available, but I found them too sophisticated for my needs.

Guard is complimentary to Bean Validation and is often used on the boundary of the application, eg. REST endpoints, where data is added or updated. Guard uses the familiar concepts of Constraints and Violations and ships with basic validations such is NotNull, Empty, Size, Max etc.

Highlights

  • Easy to learn.
  • Extendable. Implement the Constraint interface to create a custom
  • validator
  • Provides basic validators out of the box. ( Most the validators
  • found in Bean Validation)
  • are also provided by Guard.
  • Constraints can be composed to create new constraints.

Basic Usage

The idea behind Guard is to guard objects, in most cases, the individual fields of an object.

A Guard must have a name.

The name should be unique and all violations that occur while applying constraints, will be added under this name.

For example:

Guard guard = new Guard("some_name")
.value("") // empty value will violate the Required constraint
.constraint( new Required());

// a Violation will be added for "some_name".
 
// Now if we change the name
guard.of("another_name")
.value("") // empty value will violate the Required constraint
.constraint( new Required());
// a Violation will be added for "another_name".

// One violation will be registered for each name
// Hence the following Assertions will be true
Assertions.assertTrue( violations.getList( FIRSTNAME ).size() == 1 );
Assertions.assertTrue( violations.getList( LASTNAME ).size() == 1 );

will be used as the name of the Violations that are created when constraints are applied to the values of the object that is guarded.

Below is a quick example:

Guard guard = new Guard("firstname");
guard.value("steve");
giard.constraint( new Required() );
Violations violations = guard.validate();

System.out.println(violations.isValid())); // true
System.out.println(violations.isValid())); // true

List<Violation> violationList = violations.getList("firstname");
Violation violation = violationList.get(0);
System.out.println("message: " +  violation.getMessage());

This can be read as "Create a Guard for the firstname Field"

Fluid API

The above example can be written a bit more fluently as most methods return the guard instance:

Violations violations = new Guard("firstname")
.value("steve")
.constraint( new Required() )
 .validate()

Multiple Constraints

We can apply more than one Constraint on a value:

Violations violations = new Guard("firstname")
.value("steve")
.constraint( new NotNull() )
.constraint( new NotEmpty() )
.constraint( new Size(2, 20) )
 .validate()

Multiple Values

The same Guard instance can be applied to different values for the same Object or Field. Just change the value and apply another Constraint.

Violations violations = new Guard("firstname")
.value("Steve")
.constraint( new Size().max(10) )
.value("Mary")
.constraint( new Size().max(5) )
 .validate()

Multiple Names

The same Guard instance can be applied to different Names , generally when we want to validate a different Field or Object. Just change the name through the of() method. This can be read as "This is a Guard of the firstname Field". The word, for, would have been better, but it's a keyword in Java, so we settle for the next best thing.

Violations violations = new Guard("firstname")
.value("Steve")
.constraint( new NotNull() )
.constraint( new Size().max(5) )

.of("lastname")
.value("Sanders")
.constraint( new Size().max(10) )
 .validate()

Context

When a Guard instance is created a GuardContext is created for the name of that Guard.

A GuardContext has a name, a value and a list of violations that was created as constraints were applied against the current context.

Context Switching

New GuardContexts are created when the name is changed through the of() method.

Violations violations = new Guard("firstname")
.value("")
.constraint( new Required() )
.of("lastname")
.value(null)
.constraint( new Required() )
of("age")
.value( 200 )
.constraint( new Max(  120 ) )
 .validate()
 
 Assertions.assertTrue( violations.getList("firstname").size() == 1 );
 Assertions.assertTrue( violations.getList("lastname").size() == 1 );
 Assertions.assertTrue( violations.getList("age").size() == 1 );

All new constraints will be applied against the current GuardContext.

NOTE: A Guard instance keeps a list of all Violations of all GuardContexts that was created.

So even when the context is switched from "firstname" to "lastname", all "firstname" Violations are still present in the list of Violations returned by the "validate()" method.

Violations violations = new Guard("firstname")
    .value(null)
    .constraint( new NotNull() )
    .of("lastname")
    .value("Sanders")
    .constraint( new Size().max( 5 ) )
    .validate();

    Assertions.assertTrue( violations.getList().size() == 2 ); 

We can apply multiple constraints on a Guard with the same name.

Violations violations = new Guard( "client phone number" )
                .value( "abc123" )
                .constraint( new Size().max(3) ) // value length cannot be greater than 3
                .constraint( new NumberOnly( ) ) // value must consist of numbers only
                .validate();

        Assertions.assertTrue( violations.isInvalid() );
        Assertions.assertTrue( violations.getList().size() == 2 );

This will result in a Violation for each Constraint.

Composition

As Java is object-oriented, we can assemble more complex constraints from others.

The AtLeast constraint is a good example of this approach.

isValid() / isInvalid() utility methods

The constraint interface exposes the isValid( Object value ) and isInvalid( Object value ) methods that can be used as utility methods to validate values.

List list = new ArrayList();
Size size = new Size( 1, 5 );
if (size.isInvalid(list)) {
    System.out.println("List must contain contain at least 1 
    but less than 5 items");

guard's People

Contributors

sabob avatar

Watchers

 avatar

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.