Giter Club home page Giter Club logo

go-validate's Introduction

go-validate

Yet another Go struct/object validation package, with a focus on simplicity, flexibility, and full control over validation logic.

Go Reference GitHub tag (latest SemVer) Actions Status Coverage GitHub issues GitHub pull requests License Status

Add validation to any type, by simply implementing the Validatable interface:

type Validatable interface {
	Validate() error
}

Import

import "github.com/romdo/go-validate"

Example

type Order struct {
	Books []*Book `json:"books"`
}

type Book struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

func (s *Book) Validate() error {
	var errs error
	if s.Title == "" {
		errs = validate.Append(errs, &validate.Error{
			Field: "Title", Msg: "is required",
		})
	}

	// Helper to perform the same kind of check as above for Title.
	errs = validate.Append(errs, validate.RequireField("Author", s.Author))

	return errs
}

func main() {
	errs := validate.Validate(&Order{Books: []*Book{{Title: ""}}})

	for _, err := range validate.Errors(errs) {
		fmt.Println(err.Error())
	}
}

Above example produces the following output:

books.0.title: is required
books.0.author: is required

Documentation

Please see the Go Reference for documentation and examples.

LICENSE

MIT

go-validate's People

Contributors

jimeh avatar

Watchers

 avatar

go-validate's Issues

Error Message Templating

We want to support some very basic templating features in error messages
returned by Validate() functions.

Specifically, within a Validate() method on a struct type, we want to support
field name normalization via the FieldNameFunc feature, to convert the field
name to it's JSON/YAML/Form tag.

This would allow a error message like:

only allowed ObjectType='book'

The ObjectType field name is the Go struct field name, if this object uses
JSON, the external name of the field in JSON might be object_type or
objectType. So we want to be able to indicate in the error message that
ObjectType is a field on the struct.

I think we could do this a few different ways. Assuming we the following struct:

type Item struct {
	ObjectType string `json:"object_type"`
	BookTitle  string `json:"book_title"`
}

We want the end result to be this:

only allowed object_type='book'

We could use a custom syntax, something like one of these:

only allowed #{ObjectType}='book'
only allowed ${ObjectType}='book'
only allowed {{ObjectType}}='book'

Or we could leverage the text/template package using a custom field
function:

only allowed {{ field "ObjectType" }}='book'

Some of the field validation helpers mentioned in #4 could benefit from this
feature too.

Validation Helpers

With helper functions we can make common types of validation much easier. I
propose two categories of helper functions:

  • Value Assertion: Helpers which assert a specific state/property of a given
    value, returning a boolean to indicate if the given value meets the condition
    or not. These functions should typically have a name which starts with Is,
    Has, or similar words. For example: IsNil(), HasLen()
  • Validation: Helpers which will perform a specific type of check against a
    value, and return nil on success, or a *Error on failure. Under the hood
    they'll use Value Assertion functions, along with custom logic as needed.
    These functions should typically have a name which starts with Require or
    Allow. For example: RequireField(), RequireLen(), AllowFieldWhen()

Value Assertion helpers

Value assertion helpers simply return a boolean value indicating if the given
value meets the condition of the helper or not. These can be used directly in
custom Validate() functions, and they are also used within validation helper
functions too.

Functions

Below is rough list of value assertion helpers I think are useful, please
suggest changes to this list as you see fit.

  • IsNil(v interface{}) bool — Checks if given value is nil via reflection.
  • IsEmpty(v interface{}) bool — Checks if given value is equal to the
    type's zero value, or for slices that it is a non-nil empty slice.
  • IsBlank(v interface{}) bool — Checks if given value returns true from
    either of IsNil() or IsEmpty().
  • HasLen(v interface{}, n int) bool — Check if given value has a length of
    exactly n.
  • HasMinLen(v interface{}, min int) bool — Check if given value has a
    length equal to or greater than min.
  • HasMaxLen(v interface{}, max int) bool — Check if given value has a
    length equal to or less than max.
  • HasLenBetween(v interface{}, min, max int) bool — Check if given value
    has a length equal to or greater than min and equal to or less than
    max.

Validation helpers

Validation helpers verify if given values meets specific conditions, returning
nil on success, and a *Error if it fails that describes why the validation
failed.

There are two variants of all validation helpers, the "direct validation" and
"field validation". The direct variants validates the given value directly, and
does not set a Field value on the returned *Error object. While a field
validation helper accepts an instance of a struct, and one or more fields as
strings, and performs the validation against the given field(s) on the given
struct, and sets the Field value as needed on the returned *Error.

Translations / i18n

While I believe providing multi-language translations for built-in validation
helpers is probably outside the scope of this project right now, we should
strive to make translations possible and hopefully easy to implement if/when
needed.

As such, I propose we create *Validation structs for each type of validation,
which contains all the input fields needed to produce a human readable error
message, along with a Error() string function that produces the English
language string for the error.

For example, using the RequireLenBetween(v interface{}, min, max int) error
helper described below, we could have a struct like this:

type RequireLenBetweenValidation struct {
    Min int
    Max int
}

func (s *RequireLenBetweenValidation) Error() string {
	return fmt.Sprintf(
		"must have a length equal to or greater than %d " +
			"and equal to or less than %d",
		s.Min, s.Max,
	)
}

This enables Validate() functions to return *RequireLenBetweenValidation
instances directly which will be wrapped up in a *Error with the field set
correctly.

RequireFieldLenBetween(s interface{}, field string, min, max int) error helper
described below, would simply wrap the validation error struct in a *Error
directly with the field set accordingly.

This should allow easy translation by performing type assertions on the Err
value for each type of validation error you want to support translating.

Though effectively it means validation translations happen on he returned errors
after executing validation, rather than it being built-in to the validation
package itself. It simply exposes enough information about each validation error
to implement your own humanly readable error in any language you see fit.

Functions

Below is rough list of validation helpers I think are useful, please suggest
changes to this list as you see fit.

Require

  • Require(v interface{}) error
  • RequireField(s interface{}, field string) error

Validates value is not nil or empty as determined by IsBlank().

Error message on failure:

"is required"

RequireNotNil

  • RequireNotNil(v interface{}) error
  • RequireFieldNotNil(s interface{}, field string) error

Validates value is not nil as determined by IsNil().

Error message on failure:

"must not be nil"

RequireNotEmpty

  • RequireNotEmpty(v interface{}) error
  • RequireFieldNotEmpty(s interface{}, field string) error

Validates value is not empty as determined by IsEmpty().

Error message on failure:

"must not be empty"

RequireLen

  • RequireLen(v interface{}, n int) error
  • RequireFieldLen(s interface{}, field string, n int) error

Validates value has a length of exactly n.

Error message on failure:

"must have a length of exactly <n>"

RequireMinLen

  • RequireMinLen(v interface{}, min int) error
  • RequireFieldMinLen(s interface{}, field string, min int) error

Validates value has a length equal to or greater than min.

Error message on failure:

"must have a length equal to or greater than <min>"

RequireMaxLen

  • RequireMaxLen(v interface{}, max int) error
  • RequireFieldMaxLen(s interface{}, field string, max int) error

Validates value has a length equal to or less than max.

Error message on failure:

"must have a length equal to or greater than <max>"

RequireLenBetween

  • RequireLenBetween(v interface{}, min, max int) error
  • RequireFieldLenBetween(s interface{}, field string, min, max int) error

Validates that the given value has a length equal to or greater than min and
equal to or less than max.

"must have a length equal to or greater than <min> and equal to or less than <max>"

RequireOneOf

  • RequireOneOf(s interface{}, fields ...string) error

Validates that only one of specified fields on struct s has a non-nil or
non-empty value as determined by IsBlank().

Error message on failure:

"exactly one of 'Field1', 'Field2', or 'Field3' must be specified"

RequireExactlyNOf

  • RequireExactlyNOf(s interface{}, n int, fields ...string) error

Validates that exactly n of specified fields on struct s has a non-nil or
non-empty value as determined by IsBlank().

Error message on failure:

"exactly <n> of 'Field1', 'Field2', or 'Field3' must be specified"

RequireFieldWhen

  • RequireFieldWhen(s interface{}, field, other string, v interface{})

When field other on struct s is equal to value in v, validates that field
field on struct s is not nil or empty as determined by IsBlank().

Error message when other is "Type" and v is "Book":

"is required when Type='Book'"

AllowFieldWhen

  • AllowFieldWhen(s interface{}, field, other string, v interface{})

When field other on struct s is not equal to value in v, validates that
field field on struct s is nil or empty as determined by IsBlank().

Effectively prohibiting setting field field if field other is not equal to
v.

Error message when other is "Type" and v is "Book":

"is only allowed when Type='Book'"

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.