-
Notifications
You must be signed in to change notification settings - Fork 3
/
validate.go
124 lines (112 loc) · 3 KB
/
validate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package manifest
import (
"fmt"
"log"
"strings"
"github.com/xeipuuv/gojsonschema"
"gopkg.in/yaml.v2"
"github.com/agilestacks/hub/cmd/hub/bindata"
"github.com/agilestacks/hub/cmd/hub/config"
"github.com/agilestacks/hub/cmd/hub/util"
)
var schemaLoader gojsonschema.JSONLoader
func validateManifest(name string, yamlDocument []byte) {
err := validate(name, yamlDocument)
if err != nil {
util.Warn("Unable to validate `%s`: %v", name, err)
}
}
func validate(name string, yamlDocument []byte) error {
schema, err := manifestSchema()
if err != nil {
return err
}
var manifest interface{}
err = yaml.Unmarshal(yamlDocument, &manifest)
if err != nil {
return err
}
converted, err := convertToStringKeysRecursive(manifest, "")
if err != nil {
return err
}
data := gojsonschema.NewGoLoader(converted)
result, err := gojsonschema.Validate(schema, data)
if err != nil {
return err
}
if result.Valid() {
if config.Trace {
log.Printf("`%s` schema is valid", name)
}
} else {
sep := "\n\t- "
var errs []string
for _, jserr := range result.Errors() {
errs = append(errs, jserr.String())
}
util.Warn("`%s` schema is not valid:%s%s", name, sep, strings.Join(errs, sep))
if config.Trace {
log.Printf("Document validated:\n%+v", converted)
}
}
return nil
}
func manifestSchema() (gojsonschema.JSONLoader, error) {
if schemaLoader == nil {
jsonBytes, err := bindata.Asset("meta/manifest.schema.json")
if err != nil {
return nil, fmt.Errorf("No manifest schema embedded: %v", err)
}
schemaLoader = gojsonschema.NewBytesLoader(jsonBytes)
}
return schemaLoader, nil
}
func convertForValidator(value interface{}) (interface{}, error) {
return convertToStringKeysRecursive(value, "")
}
func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
if mapping, ok := value.(map[interface{}]interface{}); ok {
dict := make(map[string]interface{})
for key, entry := range mapping {
str, ok := key.(string)
if !ok {
return nil, formatInvalidKeyError(keyPrefix, key)
}
var newKeyPrefix string
if keyPrefix == "" {
newKeyPrefix = str
} else {
newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str)
}
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
if err != nil {
return nil, err
}
dict[str] = convertedEntry
}
return dict, nil
}
if list, ok := value.([]interface{}); ok {
var convertedList []interface{}
for index, entry := range list {
newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index)
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
if err != nil {
return nil, err
}
convertedList = append(convertedList, convertedEntry)
}
return convertedList, nil
}
return value, nil
}
func formatInvalidKeyError(keyPrefix string, key interface{}) error {
var location string
if keyPrefix == "" {
location = "at top level"
} else {
location = fmt.Sprintf("in %s", keyPrefix)
}
return fmt.Errorf("Non-string key %s: %#v", location, key)
}