-
Notifications
You must be signed in to change notification settings - Fork 0
/
validator.go
95 lines (80 loc) · 2.53 KB
/
validator.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
package util
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/CactusDev/Xerophi/types"
jschema "github.com/xeipuuv/gojsonschema"
)
// APIError is an alias for map[string]string to make cleaner code
type APIError struct {
Data map[string]interface{}
}
// Error allows APIError to be returned as an error object
func (e APIError) Error() string {
var errorString string
// We know that APIError is map[string]interface{}, so no error catching on type-assertion
for field, msg := range e.Data {
errorString += fmt.Sprintf("[%s] \"%s\" ", field, msg)
}
return errorString
}
// ValidateAndMap takes an io reader (most like io.ReadCloser from the request),
// reads in the data, and then validates it against the JSONSchema provided.
// Then it converts it into the provided schema via the interface (it's always a
// struct as we use it) to apply the JSON stuff we want and then returns a map
func ValidateAndMap(in io.Reader, schemaPath string, schema types.Schema) (map[string]interface{}, error) {
var conv map[string]interface{}
// Retrieve the data from the reader, most likely the request body
bodyData, err := ioutil.ReadAll(in)
if err != nil {
return nil, err
}
// Validate the data
err = ValidateInput(bodyData, schemaPath)
// It's not an APIError and an actual error exists
if validateErr, ok := err.(APIError); !ok && err != nil {
return nil, err
} else if ok {
// It's a validation error
return nil, validateErr
}
// Dump the body data into the schema and get the bytes back
schemaBytes, err := schema.DumpBody(bodyData)
if err != nil {
return nil, err
}
// Unmarshal our byte array into the output
if err := json.Unmarshal(schemaBytes, &conv); err != nil {
return nil, err
}
return conv, nil
}
// ValidateInput valids the data provided against the provided JSON schema
// Will only return an error if there's a problem with the data
func ValidateInput(source []byte, schema string) error {
var errors APIError
path, err := os.Getwd()
if err != nil {
return err
}
// TODO: Make schemaLoader static so we're not constantly re-creating a schema
// each time
schemaLoader := jschema.NewReferenceLoader("file://" + path + schema)
dataLoader := jschema.NewBytesLoader(source)
res, err := jschema.Validate(schemaLoader, dataLoader)
if err != nil {
return err
}
if res.Errors() != nil {
for _, jschemaErr := range res.Errors() {
errors.Data[jschemaErr.Field()] = jschemaErr.Description()
}
// There were errors, return those
return errors
}
// Passed validation
return nil
}