beats: github.com/elastic/beats/libbeat/common/mapval Index | Examples | Files

package mapval

import "github.com/elastic/beats/libbeat/common/mapval"

Package mapval is used to validate JSON-like nested map data-structure against a set of expectations. Its key features are allowing custom, function defined validators for values, and allowing the composition of multiple validation specs.

See the example below for more details. Most key functions include detailed examples of their use within this documentation.

Code:

// Let's say we want to validate this map
data := common.MapStr{"foo": "bar", "baz": "bot", "count": 1}

// We can validate the data by creating a mapval.Validator
// Validators are functions created by compiling the special mapval.Map
// type. This is a map[string]interface{} that can be compiled
// into a series of checks.
//
// Literal values in a mapval.Map are checked for equality.
// More complex checks can be done using values of the mapval.IsDef
// type. In this case, we're using an IsDef to see if the "foo" key
// contains the string "a", and we're using a literal to check that the
// "baz" key contains the exact value "bot".
validator := MustCompile(Map{
    "foo": IsStringContaining("a"),
    "baz": "bot",
})

// When being used in test-suites, you should use mapvaltest.Test to execute the validator
// This produces easy to read test output, and outputs one failed assertion per failed matcher
// See the docs for mapvaltest for more info
// mapvaltest.Test(t, validator, data)

// If you need more control than mapvaltest.Test provides, you can use the results directly
results := validator(data)

// The Results.Valid property indicates if the validator passed
fmt.Printf("Results.Valid: %t\n", results.Valid)

// Results.Errors() returns one error per failed match
fmt.Printf("There were %d errors\n", len(results.Errors()))

// Results.Fields is a map of paths defined in the input mapval.Map to the result of their validation
// This is useful if you need more control
fmt.Printf("Over %d fields\n", len(results.Fields))

// You may be thinking that the validation above should have failed since there was an
// extra key, 'count', defined that was encountered. By default mapval does not
// consider extra data to be an error. To change that behavior, wrap the validator
// in mapval.Strict()
strictResults := Strict(validator)(data)

fmt.Printf("Strict Results.Valid: %t\n", strictResults.Valid)

// You can check an exact field for an error
fmt.Printf("For the count field specifically .Valid is: %t\n", strictResults.Fields["count"][0].Valid)

// And get error objects for each error
for _, err := range strictResults.Errors() {
    fmt.Println(err)
}

// And even get a new Results object with only invalid fields included
strictResults.DetailedErrors()

Index

Examples

Package Files

compiled_schema.go core.go doc.go is_defs.go path.go reflect_tools.go results.go testing.go value.go walk.go

Variables

var IsDuration = Is("is a duration", func(path path, v interface{}) *Results {
    if _, ok := v.(time.Duration); ok {
        return ValidResult(path)
    }
    return SimpleResult(
        path,
        false,
        fmt.Sprintf("Expected a time.duration, got '%v' which is a %T", v, v),
    )
})

IsDuration tests that the given value is a duration.

var IsNil = Is("is nil", func(path path, v interface{}) *Results {
    if v == nil {
        return ValidResult(path)
    }
    return SimpleResult(
        path,
        false,
        fmt.Sprintf("Value %v is not nil", v),
    )
})

IsNil tests that a value is nil.

var IsNonEmptyString = Is("is a non-empty string", func(path path, v interface{}) *Results {
    strV, errorResults := isStrCheck(path, v)
    if errorResults != nil {
        return errorResults
    }

    if len(strV) == 0 {
        return SimpleResult(path, false, "String '%s' should not be empty", strV)
    }

    return ValidResult(path)
})

IsNonEmptyString checks that the given value is a string and has a length > 1.

var IsString = Is("is a string", func(path path, v interface{}) *Results {
    _, errorResults := isStrCheck(path, v)
    if errorResults != nil {
        return errorResults
    }

    return ValidResult(path)
})

IsString checks that the given value is a string.

var KeyMissing = IsDef{/* contains filtered or unexported fields */}

KeyMissing checks that the given key is not present defined.

var KeyMissingVR = ValueResult{
    false,
    "expected this key to be present",
}

KeyMissingVR is emitted when a key was expected, but was not present.

var KeyPresent = IsDef{/* contains filtered or unexported fields */}

KeyPresent checks that the given key is in the map, even if it has a nil value.

var StrictFailureVR = ValueResult{
    false,
    "unexpected field encountered during strict validation",
}

StrictFailureVR is emitted when Strict() is used, and an unexpected field is found.

var ValidVR = ValueResult{true, "is valid"}

ValidVR is a convenience value for Valid results.

func MustRegisterEqual Uses

func MustRegisterEqual(fn interface{})

MustRegisterEqual is the panic-ing equivalent of RegisterEqual.

func RegisterEqual Uses

func RegisterEqual(fn interface{}) error

RegisterEqual takes a function of the form fn(v someType) IsDef and registers it to check equality for that type.

type CompiledSchema Uses

type CompiledSchema []flatValidator

CompiledSchema represents a compiled definition for driving a Validator.

func (CompiledSchema) Check Uses

func (cs CompiledSchema) Check(actual common.MapStr) *Results

Check executes the the checks within the CompiledSchema

type InvalidEqualFnError Uses

type InvalidEqualFnError struct {
    // contains filtered or unexported fields
}

InvalidEqualFnError is the error type returned by RegisterEqual when there is an issue with the given function.

func (InvalidEqualFnError) Error Uses

func (e InvalidEqualFnError) Error() string

type InvalidPathString Uses

type InvalidPathString string

InvalidPathString is the error type returned from unparseable paths.

func (InvalidPathString) Error Uses

func (ps InvalidPathString) Error() string

type IsDef Uses

type IsDef struct {
    // contains filtered or unexported fields
}

An IsDef defines the type of check to do. Generally only name and checker are set. optional and checkKeyMissing are needed for weird checks like key presence.

func Is Uses

func Is(name string, checker ValueValidator) IsDef

Is creates a named IsDef with the given checker.

Code:

// More advanced validations can be used with built-in and custom functions.
// These are represented with the IfDef type

data := common.MapStr{"foo": "bar", "count": 1}

// Values can also be tested programatically if a mapval.IsDef is used as a value
// Here we'll define a custom IsDef using the mapval DSL, then validate it.
// The Is() function is the preferred way to costruct IsDef objects.
startsWithB := Is("starts with b", func(path path, v interface{}) *Results {
    vStr, ok := v.(string)
    if !ok {
        return SimpleResult(path, false, "Expected a string, got a %t", v)
    }

    if strings.HasPrefix(vStr, "b") {
        return ValidResult(path)
    }

    return SimpleResult(path, false, "Expected string to start with b, got %v", vStr)
})

funcValidator := MustCompile(Map{"foo": startsWithB})

funcValidatorResult := funcValidator(data)

fmt.Printf("Valid: %t", funcValidatorResult.Valid)

Output:

Valid: true

func IsAny Uses

func IsAny(of ...IsDef) IsDef

IsAny takes a variable number of IsDef's and combines them with a logical OR. If any single definition matches the key will be marked as valid.

func IsArrayOf Uses

func IsArrayOf(validator Validator) IsDef

IsArrayOf validates that the array at the given key is an array of objects all validatable via the given Validator.

func IsDeepEqual Uses

func IsDeepEqual(to interface{}) IsDef

IsDeepEqual checks equality using reflect.DeepEqual.

func IsEqual Uses

func IsEqual(to interface{}) IsDef

IsEqual tests that the given object is equal to the actual object.

func IsEqualToTime Uses

func IsEqualToTime(to time.Time) IsDef

IsEqualToTime ensures that the actual value is the given time, regardless of zone.

func IsIntGt Uses

func IsIntGt(than int) IsDef

IsIntGt tests that a value is an int greater than.

func IsStringContaining Uses

func IsStringContaining(needle string) IsDef

IsStringContaining validates that the the actual value contains the specified substring.

func IsStringMatching Uses

func IsStringMatching(regexp *regexp.Regexp) IsDef

IsStringMatching checks whether a value matches the given regexp.

func IsUnique Uses

func IsUnique() IsDef

IsUnique instances are used in multiple spots, flagging a value as being in error if it's seen across invocations. To use it, assign IsUnique to a variable, then use that variable multiple times in a Map.

func Optional Uses

func Optional(id IsDef) IsDef

Optional wraps an IsDef to mark the field's presence as optional.

Code:

dataNoError := common.MapStr{"foo": "bar"}
dataError := common.MapStr{"foo": "bar", "error": true}

validator := MustCompile(Map{"foo": "bar", "error": Optional(IsEqual(true))})

// Both inputs pass
fmt.Printf("Validator classifies both maps as true: %t", validator(dataNoError).Valid && validator(dataError).Valid)

Output:

Validator classifies both maps as true: true

type Map Uses

type Map map[string]interface{}

Map is the type used to define schema definitions for Compile and to represent an arbitrary map of values of any type.

Code:

v := MustCompile(Map{
    "foo": IsStringContaining("a"),
    "baz": "bot",
})

data := common.MapStr{
    "foo": "bar",
    "baz": "bot",
}

fmt.Printf("Result is %t", v(data).Valid)

Output:

Result is true

type Results Uses

type Results struct {
    Fields map[string][]ValueResult
    Valid  bool
}

Results the results of executing a schema. They are a flattened map (using dotted paths) of all the values []ValueResult representing the results of the IsDefs.

func KeyMissingResult Uses

func KeyMissingResult(path path) *Results

KeyMissingResult is emitted when a key was expected, but was not present.

func NewResults Uses

func NewResults() *Results

NewResults creates a new Results object.

func SimpleResult Uses

func SimpleResult(path path, valid bool, msg string, args ...interface{}) *Results

SimpleResult provides a convenient and simple method for creating a *Results object for a single validation. It's a very common way for validators to return a *Results object, and is generally simpler than using SingleResult.

func SingleResult Uses

func SingleResult(path path, result ValueResult) *Results

SingleResult returns a *Results object with a single validated value at the given path using the provided ValueResult as its sole validation.

func StrictFailureResult Uses

func StrictFailureResult(path path) *Results

StrictFailureResult is emitted when Strict() is used, and an unexpected field is found.

func Test Uses

func Test(t *testing.T, v Validator, m common.MapStr) *Results

Test takes the output from a Validator invocation and runs test assertions on the result. If you are using this library for testing you will probably want to run Test(t, Compile(Map{...}), actual) as a pattern.

func ValidResult Uses

func ValidResult(path path) *Results

ValidResult is a convenience value for Valid results.

func (*Results) DetailedErrors Uses

func (r *Results) DetailedErrors() *Results

DetailedErrors returns a new Results object consisting only of error data.

func (Results) EachResult Uses

func (r Results) EachResult(f func(path, ValueResult) bool)

EachResult executes the given callback once per Value result. The provided callback can return true to keep iterating, or false to stop.

func (Results) Errors Uses

func (r Results) Errors() []error

Errors returns a list of error objects, one per failed value validation.

type Slice Uses

type Slice []interface{}

Slice is a convenience []interface{} used to declare schema defs. You would typically nest this inside a Map as a value, and it would be able to match against any type of non-empty slice.

Code:

v := MustCompile(Map{
    "foo": Slice{"foo", IsNonEmptyString},
})

data := common.MapStr{"foo": []string{"foo", "something"}}

fmt.Printf("Result is %t", v(data).Valid)

Output:

Result is true

type UniqScopeTracker Uses

type UniqScopeTracker map[interface{}]string

UniqScopeTracker is represents the tracking data for invoking IsUniqueTo.

func ScopedIsUnique Uses

func ScopedIsUnique() UniqScopeTracker

func (UniqScopeTracker) IsUniqueTo Uses

func (ust UniqScopeTracker) IsUniqueTo(namespace string) IsDef

IsUniqueTo validates that the given value is only ever seen within a single namespace.

type Validator Uses

type Validator func(common.MapStr) *Results

Validator is the result of Compile and is run against the map you'd like to test.

func Compile Uses

func Compile(in Map) (validator Validator, err error)

Compile takes the given map, validates the paths within it, and returns a Validator that can check real data.

func Compose Uses

func Compose(validators ...Validator) Validator

Compose combines multiple SchemaValidators into a single one.

Code:

// Composition is useful when you need to share common validation logic between validators.
// Let's imagine that we want to validate maps describing pets.

pets := []common.MapStr{
    {"name": "rover", "barks": "often", "fur_length": "long"},
    {"name": "lucky", "barks": "rarely", "fur_length": "short"},
    {"name": "pounce", "meows": "often", "fur_length": "short"},
    {"name": "peanut", "meows": "rarely", "fur_length": "long"},
}

// We can see that all pets have the "fur_length" property, but that only cats meow, and dogs bark.
// We can concisely encode this in mapval using mapval.Compose.
// We can also see that both "meows" and "barks" contain the same enums of values.
// We'll start by creating a composed IsDef using the IsAny composition, which creates a new IsDef that is
// a logical 'or' of its IsDef arguments

isFrequency := IsAny(IsEqual("often"), IsEqual("rarely"))

petValidator := MustCompile(Map{
    "name":       IsNonEmptyString,
    "fur_length": IsAny(IsEqual("long"), IsEqual("short")),
})
dogValidator := Compose(
    petValidator,
    MustCompile(Map{"barks": isFrequency}),
)
catValidator := Compose(
    petValidator,
    MustCompile(Map{"meows": isFrequency}),
)

for _, pet := range pets {
    var petType string
    if dogValidator(pet).Valid {
        petType = "dog"
    } else if catValidator(pet).Valid {
        petType = "cat"
    }
    fmt.Printf("%s is a %s\n", pet["name"], petType)
}

Output:

rover is a dog
lucky is a dog
pounce is a cat
peanut is a cat

func MustCompile Uses

func MustCompile(in Map) Validator

MustCompile compiles the given map, panic-ing if that map is invalid.

func Strict Uses

func Strict(laxValidator Validator) Validator

Strict is used when you want any unspecified keys that are encountered to be considered errors.

type ValueResult Uses

type ValueResult struct {
    Valid   bool
    Message string // Reason this is invalid
}

ValueResult represents the result of checking a leaf value.

type ValueResultError Uses

type ValueResultError struct {
    // contains filtered or unexported fields
}

ValueResultError is used to represent an error validating an individual value.

func (ValueResultError) Error Uses

func (vre ValueResultError) Error() string

Error returns the error that occurred during validation with its context included.

type ValueValidator Uses

type ValueValidator func(path path, v interface{}) *Results

A ValueValidator is used to validate a value in a Map.

Package mapval imports 11 packages (graph) and is imported by 11 packages. Updated 2019-02-14. Refresh now. Tools for package owners.