-
Notifications
You must be signed in to change notification settings - Fork 11
/
validation.go
138 lines (128 loc) · 4.38 KB
/
validation.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package stub
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
type StubsValidator interface {
IsValid(stub *Stub) (isValid bool, errorMessages []string)
}
func NewCompositeStubsValidator(validators []StubsValidator) StubsValidator {
return compositeStubsValidator{
stubsValidators: validators,
}
}
type compositeStubsValidator struct {
stubsValidators []StubsValidator
}
func (c compositeStubsValidator) IsValid(stub *Stub) (isValid bool, errorMessages []string) {
for _, validator := range c.stubsValidators {
isValid, errorMessages = validator.IsValid(stub)
if !isValid {
return isValid, errorMessages
}
}
return true, nil
}
func IsStubValid(stub *Stub, request, response reflect.Type) (isValid bool, errorMessages []string) {
valid, errorMessages := stub.IsValid()
if !valid {
return valid, errorMessages
}
reqValid, reqErrorMessages := stub.Request.Content.isJsonValid(request, "request.content")
respValid := true
respErrorMessages := make([]string, 0)
if stub.Response.Type == "success" {
respValid, respErrorMessages = stub.Response.Content.isJsonValid(response, "response.content")
}
errorMessages = append(errorMessages, reqErrorMessages...)
errorMessages = append(errorMessages, respErrorMessages...)
return reqValid && respValid, errorMessages
}
func (j JsonString) isJsonValid(t reflect.Type, baseName string) (isValid bool, errorMessages []string) {
jsonResult := new(map[string]interface{})
err := json.Unmarshal([]byte(string(j)), jsonResult)
if err != nil {
return false, []string{fmt.Sprintf("%s: invalid JSON", baseName)}
}
return isJsonValid(t, *jsonResult, baseName)
}
func isJsonValid(t reflect.Type, json map[string]interface{}, baseName string) (isValid bool, errorMessages []string) {
errorMessages = make([]string, 0)
reverseFields := make(map[string]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag == "-" || jsonTag == "" {
continue
}
jsonTag = strings.ReplaceAll(jsonTag, ",omitempty", "")
reverseFields[jsonTag] = field
}
for jsonName, fieldValue := range json {
field, ok := reverseFields[jsonName]
if !ok {
errorMessages = append(errorMessages, fmt.Sprintf("Field '%s.%s' does not exist", baseName, jsonName))
continue
}
if fieldValue == nil {
continue
}
switch {
case field.Type.Kind() == reflect.String:
switch fieldValue.(type) {
case string:
default:
errorMessages = append(errorMessages, fmt.Sprintf("Field '%s.%s' is expected to be a string.", baseName, jsonName))
}
case field.Type.Kind() == reflect.Ptr:
{
_, subTypeErrorMessages := isJsonValid(field.Type.Elem(), fieldValue.(map[string]interface{}), baseName+"."+jsonName)
errorMessages = append(errorMessages, subTypeErrorMessages...)
}
case isEnum(field.Type):
enum := reflect.New(field.Type).Interface()
values := getEnumValues(enum.(EnumType))
found := false
for _, value := range values {
if value == fieldValue {
found = true
}
}
if !found {
errorMessages = append(errorMessages, fmt.Sprintf("Value '%s' is not valid for field '%s.%s'. Possible values are '%s'.", fieldValue, baseName, jsonName, strings.Join(values, ", ")))
}
}
}
return len(errorMessages) == 0, errorMessages
}
func (stub *Stub) IsValid() (isValid bool, errMsgs []string) {
if stub.FullMethod == "" {
errMsgs = append(errMsgs, "Method can't be empty.")
}
// Validate request
if stub.Request == nil {
errMsgs = append(errMsgs, "Request can't be empty.")
}
if stub.Request.Content == "" {
errMsgs = append(errMsgs, "Request content can't be empty.")
}
if stub.Request.Match != "exact" && stub.Request.Match != "partial" {
errMsgs = append(errMsgs, "Request matching type can only be either 'exact' or 'partial'.")
}
// Validate response
if stub.Response == nil {
errMsgs = append(errMsgs, "Response can't be empty.")
}
if stub.Response.Type != "error" && stub.Response.Type != "success" {
errMsgs = append(errMsgs, "Response type can only be either 'error' or 'success'.")
}
if stub.Response.Type == "success" && stub.Response.Content == "" {
errMsgs = append(errMsgs, "Response content is mandatory when the response type is 'success'.")
}
if stub.Response.Type == "error" && stub.Response.Error == nil {
errMsgs = append(errMsgs, "Response error is mandatory when the response type ir 'error'.")
}
return len(errMsgs) == 0, errMsgs
}