2019-06-13 17:20:00 +03:00
|
|
|
package engine
|
|
|
|
|
|
|
|
import (
|
2019-06-25 18:16:02 -07:00
|
|
|
"fmt"
|
2019-06-13 17:20:00 +03:00
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
// CreateAnchorHandler is a factory that create anchor handlers
|
|
|
|
func CreateAnchorHandler(anchor string, pattern interface{}, path string) ValidationAnchorHandler {
|
|
|
|
switch {
|
|
|
|
case isConditionAnchor(anchor):
|
|
|
|
return NewConditionAnchorValidationHandler(anchor, pattern, path)
|
|
|
|
case isExistanceAnchor(anchor):
|
|
|
|
return NewExistanceAnchorValidationHandler(anchor, pattern, path)
|
|
|
|
default:
|
|
|
|
return NewNoAnchorValidationHandler(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidationAnchorHandler is an interface that represents
|
|
|
|
// a family of anchor handlers for array of maps
|
|
|
|
// resourcePart must be an array of dictionaries
|
|
|
|
// patternPart must be a dictionary with anchors
|
|
|
|
type ValidationAnchorHandler interface {
|
2019-06-25 18:16:02 -07:00
|
|
|
Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) error
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NoAnchorValidationHandler just calls validateMap
|
|
|
|
// because no anchors were found in the pattern map
|
|
|
|
type NoAnchorValidationHandler struct {
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNoAnchorValidationHandler creates new instance of
|
|
|
|
// NoAnchorValidationHandler
|
|
|
|
func NewNoAnchorValidationHandler(path string) ValidationAnchorHandler {
|
|
|
|
return &NoAnchorValidationHandler{
|
|
|
|
path: path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle performs validation in context of NoAnchorValidationHandler
|
2019-06-25 18:16:02 -07:00
|
|
|
func (navh *NoAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) error {
|
2019-06-13 17:20:00 +03:00
|
|
|
|
|
|
|
for i, resourceElement := range resourcePart {
|
|
|
|
currentPath := navh.path + strconv.Itoa(i) + "/"
|
|
|
|
|
|
|
|
typedResourceElement, ok := resourceElement.(map[string]interface{})
|
|
|
|
if !ok {
|
2019-06-25 18:16:02 -07:00
|
|
|
return fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", currentPath, patternPart, resourceElement)
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 18:16:02 -07:00
|
|
|
err := validateMap(typedResourceElement, patternPart, originPattern, currentPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 18:16:02 -07:00
|
|
|
return nil
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ConditionAnchorValidationHandler performs
|
|
|
|
// validation only for array elements that
|
|
|
|
// pass condition in the anchor
|
|
|
|
// (key): value
|
|
|
|
type ConditionAnchorValidationHandler struct {
|
|
|
|
anchor string
|
|
|
|
pattern interface{}
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConditionAnchorValidationHandler creates new instance of
|
|
|
|
// NoAnchorValidationHandler
|
|
|
|
func NewConditionAnchorValidationHandler(anchor string, pattern interface{}, path string) ValidationAnchorHandler {
|
|
|
|
return &ConditionAnchorValidationHandler{
|
|
|
|
anchor: anchor,
|
|
|
|
pattern: pattern,
|
|
|
|
path: path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle performs validation in context of ConditionAnchorValidationHandler
|
2019-06-25 18:16:02 -07:00
|
|
|
func (cavh *ConditionAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) error {
|
2019-06-20 18:21:55 +03:00
|
|
|
_, handlingResult := handleConditionCases(resourcePart, patternPart, cavh.anchor, cavh.pattern, cavh.path, originPattern)
|
2019-06-13 17:20:00 +03:00
|
|
|
|
|
|
|
return handlingResult
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExistanceAnchorValidationHandler performs
|
|
|
|
// validation only for array elements that
|
|
|
|
// pass condition in the anchor
|
|
|
|
// AND requires an existance of at least one
|
|
|
|
// element that passes this condition
|
|
|
|
// ^(key): value
|
|
|
|
type ExistanceAnchorValidationHandler struct {
|
|
|
|
anchor string
|
|
|
|
pattern interface{}
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewExistanceAnchorValidationHandler creates new instance of
|
|
|
|
// NoAnchorValidationHandler
|
|
|
|
func NewExistanceAnchorValidationHandler(anchor string, pattern interface{}, path string) ValidationAnchorHandler {
|
|
|
|
return &ExistanceAnchorValidationHandler{
|
|
|
|
anchor: anchor,
|
|
|
|
pattern: pattern,
|
|
|
|
path: path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle performs validation in context of ExistanceAnchorValidationHandler
|
2019-06-25 18:16:02 -07:00
|
|
|
func (eavh *ExistanceAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) error {
|
|
|
|
anchoredEntries, err := handleConditionCases(resourcePart, patternPart, eavh.anchor, eavh.pattern, eavh.path, originPattern)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-18 13:07:55 +03:00
|
|
|
if 0 == anchoredEntries {
|
2019-06-25 18:16:02 -07:00
|
|
|
return fmt.Errorf("Existance anchor %s used, but no suitable entries were found", eavh.anchor)
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 18:16:02 -07:00
|
|
|
return nil
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if array element fits the anchor
|
|
|
|
func checkForAnchorCondition(anchor string, pattern interface{}, resourceMap map[string]interface{}) bool {
|
|
|
|
anchorKey := removeAnchor(anchor)
|
|
|
|
|
|
|
|
if value, ok := resourceMap[anchorKey]; ok {
|
|
|
|
return ValidateValueWithPattern(value, pattern)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// both () and ^() are checking conditions and have a lot of similar logic
|
|
|
|
// the only difference is that ^() requires existace of one element
|
2019-06-18 13:07:55 +03:00
|
|
|
// anchoredEntries var counts this occurences.
|
2019-06-25 18:16:02 -07:00
|
|
|
func handleConditionCases(resourcePart []interface{}, patternPart map[string]interface{}, anchor string, pattern interface{}, path string, originPattern interface{}) (int, error) {
|
2019-06-18 13:07:55 +03:00
|
|
|
anchoredEntries := 0
|
2019-06-13 17:20:00 +03:00
|
|
|
|
|
|
|
for i, resourceElement := range resourcePart {
|
|
|
|
currentPath := path + strconv.Itoa(i) + "/"
|
|
|
|
|
|
|
|
typedResourceElement, ok := resourceElement.(map[string]interface{})
|
|
|
|
if !ok {
|
2019-06-25 18:16:02 -07:00
|
|
|
return 0, fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", currentPath, patternPart, resourceElement)
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if !checkForAnchorCondition(anchor, pattern, typedResourceElement) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-06-18 13:07:55 +03:00
|
|
|
anchoredEntries++
|
2019-06-25 18:16:02 -07:00
|
|
|
err := validateMap(typedResourceElement, patternPart, originPattern, currentPath)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 18:16:02 -07:00
|
|
|
return anchoredEntries, nil
|
2019-06-13 17:20:00 +03:00
|
|
|
}
|