Skip to content

Commit 8a82927

Browse files
stttscasualjim
authored andcommitted
Add defaulter support
1 parent 035dcd7 commit 8a82927

File tree

6 files changed

+204
-15
lines changed

6 files changed

+204
-15
lines changed

defaulter_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package validate
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"path/filepath"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
12+
"github.com/go-openapi/spec"
13+
"github.com/go-openapi/strfmt"
14+
)
15+
16+
var defaulterFixturesPath = filepath.Join("fixtures", "defaulting")
17+
18+
func TestDefaulter(t *testing.T) {
19+
fname := filepath.Join(defaulterFixturesPath, "schema.json")
20+
b, err := ioutil.ReadFile(fname)
21+
assert.NoError(t, err)
22+
var schema spec.Schema
23+
assert.NoError(t, json.Unmarshal(b, &schema))
24+
25+
err = spec.ExpandSchema(&schema, nil, nil /*new(noopResCache)*/)
26+
assert.NoError(t, err, fname+" should expand cleanly")
27+
28+
validator := NewSchemaValidator(&schema, nil, "", strfmt.Default)
29+
x := map[string]interface{}{
30+
"nested": map[string]interface{}{},
31+
"all": map[string]interface{}{},
32+
"any": map[string]interface{}{},
33+
"one": map[string]interface{}{},
34+
}
35+
t.Logf("Before: %v", x)
36+
r := validator.Validate(x)
37+
assert.False(t, r.HasErrors(), fmt.Sprintf("unexpected validation error: %v", r.AsError()))
38+
39+
r.ApplyDefaults()
40+
t.Logf("After: %v", x)
41+
var expected interface{}
42+
err = json.Unmarshal([]byte(`{
43+
"int": 42,
44+
"str": "Hello",
45+
"obj": {"foo": "bar"},
46+
"nested": {"inner": 7},
47+
"all": {"foo": 42, "bar": 42},
48+
"any": {"foo": 42},
49+
"one": {"bar": 42}
50+
}`), &expected)
51+
assert.NoError(t, err)
52+
assert.Equal(t, expected, x)
53+
}

fixtures/defaulting/schema.json

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"properties": {
3+
"int": {
4+
"type": "integer",
5+
"default": 42
6+
},
7+
"str": {
8+
"type": "string",
9+
"minLength": 4,
10+
"default": "Hello"
11+
},
12+
"obj": {
13+
"type": "object",
14+
"default": {"foo": "bar"}
15+
},
16+
"nested": {
17+
"type": "object",
18+
"properties": {
19+
"inner": {
20+
"type": "integer",
21+
"default": 7
22+
}
23+
}
24+
},
25+
"all": {
26+
"allOf": [
27+
{
28+
"type": "object",
29+
"properties": {
30+
"foo": {
31+
"type": "integer",
32+
"default": 42
33+
}
34+
}
35+
},
36+
{
37+
"type": "object",
38+
"properties": {
39+
"bar": {
40+
"type": "integer",
41+
"default": 42
42+
}
43+
}
44+
}
45+
]
46+
},
47+
"any": {
48+
"anyOf": [
49+
{
50+
"type": "object",
51+
"properties": {
52+
"foo": {
53+
"type": "integer",
54+
"default": 42
55+
}
56+
}
57+
},
58+
{
59+
"type": "object",
60+
"properties": {
61+
"bar": {
62+
"type": "integer",
63+
"default": 42
64+
}
65+
}
66+
}
67+
]
68+
},
69+
"one": {
70+
"oneOf": [
71+
{
72+
"type": "object",
73+
"properties": {
74+
"foo": {
75+
"type": "integer"
76+
}
77+
},
78+
"required": ["foo"]
79+
},
80+
{
81+
"type": "object",
82+
"properties": {
83+
"bar": {
84+
"type": "integer",
85+
"default": 42
86+
}
87+
}
88+
}
89+
]
90+
},
91+
"not": {
92+
"not": {
93+
"type": "object",
94+
"default": {
95+
"foo": 1
96+
}
97+
}
98+
}
99+
},
100+
"required": ["int", "str", "nested", "all", "any", "one"]
101+
}

object_validator.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,6 @@ func (o *objectValidator) Validate(data interface{}) *Result {
6464
}
6565

6666
res := new(Result)
67-
if len(o.Required) > 0 {
68-
for _, k := range o.Required {
69-
if _, ok := val[k]; !ok {
70-
res.AddErrors(errors.Required(o.Path+"."+k, o.In))
71-
continue
72-
}
73-
}
74-
}
7567

7668
if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
7769
for k := range val {
@@ -102,14 +94,33 @@ func (o *objectValidator) Validate(data interface{}) *Result {
10294
}
10395
}
10496

97+
createdFromDefaults := map[string]bool{}
98+
10599
for pName, pSchema := range o.Properties {
106100
rName := pName
107101
if o.Path != "" {
108102
rName = o.Path + "." + pName
109103
}
110104

111105
if v, ok := val[pName]; ok {
112-
res.Merge(NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats).Validate(v))
106+
r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats).Validate(v)
107+
res.Merge(r)
108+
} else if pSchema.Default != nil {
109+
createdFromDefaults[pName] = true
110+
pName := pName // shaddow
111+
def := pSchema.Default
112+
res.Defaulters = append(res.Defaulters, DefaulterFunc(func() {
113+
val[pName] = def
114+
}))
115+
}
116+
}
117+
118+
if len(o.Required) > 0 {
119+
for _, k := range o.Required {
120+
if _, ok := val[k]; !ok && !createdFromDefaults[k] {
121+
res.AddErrors(errors.Required(o.Path+"."+k, o.In))
122+
continue
123+
}
113124
}
114125
}
115126

@@ -140,9 +151,6 @@ func (o *objectValidator) validatePatternProperty(key string, value interface{},
140151

141152
res := validator.Validate(value)
142153
result.Merge(res)
143-
if res.IsValid() {
144-
succeededOnce = true
145-
}
146154
}
147155
}
148156

result.go

+18
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,21 @@ var (
2525
Debug = os.Getenv("SWAGGER_DEBUG") != ""
2626
)
2727

28+
type Defaulter interface {
29+
Apply()
30+
}
31+
32+
type DefaulterFunc func()
33+
34+
func (f DefaulterFunc) Apply() {
35+
f()
36+
}
37+
2838
// Result represents a validation result
2939
type Result struct {
3040
Errors []error
3141
MatchCount int
42+
Defaulters []Defaulter
3243
}
3344

3445
// Merge merges this result with the other one, preserving match counts etc
@@ -38,6 +49,7 @@ func (r *Result) Merge(other *Result) *Result {
3849
}
3950
r.AddErrors(other.Errors...)
4051
r.MatchCount += other.MatchCount
52+
r.Defaulters = append(r.Defaulters, other.Defaulters...)
4153
return r
4254
}
4355

@@ -69,3 +81,9 @@ func (r *Result) AsError() error {
6981
}
7082
return errors.CompositeValidationError(r.Errors...)
7183
}
84+
85+
func (r *Result) ApplyDefaults() {
86+
for _, d := range r.Defaulters {
87+
d.Apply()
88+
}
89+
}

schema.go

-3
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ func (s *SchemaValidator) commonValidator() valueValidator {
148148
return &basicCommonValidator{
149149
Path: s.Path,
150150
In: s.in,
151-
Default: s.Schema.Default,
152151
Enum: s.Schema.Enum,
153152
}
154153
}
@@ -184,7 +183,6 @@ func (s *SchemaValidator) stringValidator() valueValidator {
184183
return &stringValidator{
185184
Path: s.Path,
186185
In: s.in,
187-
Default: s.Schema.Default,
188186
MaxLength: s.Schema.MaxLength,
189187
MinLength: s.Schema.MinLength,
190188
Pattern: s.Schema.Pattern,
@@ -195,7 +193,6 @@ func (s *SchemaValidator) formatValidator() valueValidator {
195193
return &formatValidator{
196194
Path: s.Path,
197195
In: s.in,
198-
//Default: s.Schema.Default,
199196
Format: s.Schema.Format,
200197
KnownFormats: s.KnownFormats,
201198
}

schema_props.go

+12
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bo
8989

9090
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
9191
mainResult := new(Result)
92+
var firstSuccess *Result
9293
if len(s.anyOfValidators) > 0 {
9394
var bestFailures *Result
9495
succeededOnce := false
@@ -97,6 +98,9 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result {
9798
if result.IsValid() {
9899
bestFailures = nil
99100
succeededOnce = true
101+
if firstSuccess == nil {
102+
firstSuccess = result
103+
}
100104
break
101105
}
102106
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
@@ -109,18 +113,24 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result {
109113
}
110114
if bestFailures != nil {
111115
mainResult.Merge(bestFailures)
116+
} else if firstSuccess != nil {
117+
mainResult.Merge(firstSuccess)
112118
}
113119
}
114120

115121
if len(s.oneOfValidators) > 0 {
116122
var bestFailures *Result
123+
var firstSuccess *Result
117124
validated := 0
118125

119126
for _, oneOfSchema := range s.oneOfValidators {
120127
result := oneOfSchema.Validate(data)
121128
if result.IsValid() {
122129
validated++
123130
bestFailures = nil
131+
if firstSuccess == nil {
132+
firstSuccess = result
133+
}
124134
continue
125135
}
126136
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
@@ -133,6 +143,8 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result {
133143
if bestFailures != nil {
134144
mainResult.Merge(bestFailures)
135145
}
146+
} else if firstSuccess != nil {
147+
mainResult.Merge(firstSuccess)
136148
}
137149
}
138150

0 commit comments

Comments
 (0)