Skip to content

Commit deac891

Browse files
committed
Add pruning post-validate algorithm
1 parent 94e900b commit deac891

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

fixtures/pruning/schema.json

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

post/prune.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2018 go-swagger maintainers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package post
16+
17+
import (
18+
"github.com/go-openapi/validate"
19+
)
20+
21+
// Prune recursively removes all non-specified fields from the data.
22+
func Prune(r *validate.Result) {
23+
prune(r.Data(), r)
24+
}
25+
26+
func prune(data interface{}, result *validate.Result) {
27+
switch obj := data.(type) {
28+
case map[string]interface{}:
29+
pruneObject(obj, result)
30+
for _, val := range obj {
31+
prune(val, result)
32+
}
33+
case []interface{}:
34+
for _, item := range obj {
35+
prune(item, result)
36+
}
37+
}
38+
}
39+
40+
func pruneObject(obj map[string]interface{}, result *validate.Result) {
41+
fieldSchemata := result.FieldSchemata()
42+
for field := range obj {
43+
if schemata, ok := fieldSchemata[validate.NewFieldKey(obj, field)]; !ok || len(schemata) == 0 {
44+
delete(obj, field)
45+
}
46+
}
47+
}

post/prune_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright 2015 go-swagger maintainers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package post
16+
17+
import (
18+
"encoding/json"
19+
"fmt"
20+
"io/ioutil"
21+
"path/filepath"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
26+
"github.com/go-openapi/spec"
27+
"github.com/go-openapi/strfmt"
28+
validate "github.com/go-openapi/validate"
29+
)
30+
31+
var pruneFixturesPath = filepath.Join("..", "fixtures", "pruning")
32+
33+
func TestPrune(t *testing.T) {
34+
schema, err := pruningFixture()
35+
assert.NoError(t, err)
36+
37+
x := map[string]interface{}{
38+
"foo": 42,
39+
"bar": 42,
40+
"x": 42,
41+
"nested": map[string]interface{}{
42+
"x": 42,
43+
"inner": map[string]interface{}{
44+
"foo": 42,
45+
"bar": 42,
46+
"x": 42,
47+
},
48+
},
49+
"all": map[string]interface{}{
50+
"foo": 42,
51+
"bar": 42,
52+
"x": 42,
53+
},
54+
"any": map[string]interface{}{
55+
"foo": 42,
56+
"bar": 42,
57+
"x": 42,
58+
},
59+
"one": map[string]interface{}{
60+
"bar": 42,
61+
"x": 42,
62+
},
63+
"array": []interface{}{
64+
map[string]interface{}{
65+
"foo": 42,
66+
"bar": 123,
67+
},
68+
map[string]interface{}{
69+
"x": 42,
70+
"y": 123,
71+
},
72+
},
73+
}
74+
t.Logf("Before: %v", x)
75+
76+
validator := validate.NewSchemaValidator(schema, nil, "", strfmt.Default)
77+
r := validator.Validate(x)
78+
assert.False(t, r.HasErrors(), fmt.Sprintf("unexpected validation error: %v", r.AsError()))
79+
80+
Prune(r)
81+
t.Logf("After: %v", x)
82+
expected := map[string]interface{}{
83+
"foo": 42,
84+
"bar": 42,
85+
"nested": map[string]interface{}{
86+
"inner": map[string]interface{}{
87+
"foo": 42,
88+
"bar": 42,
89+
},
90+
},
91+
"all": map[string]interface{}{
92+
"foo": 42,
93+
"bar": 42,
94+
},
95+
"any": map[string]interface{}{
96+
// intentionally only list one: the first matching
97+
"foo": 42,
98+
},
99+
"one": map[string]interface{}{
100+
"bar": 42,
101+
},
102+
"array": []interface{}{
103+
map[string]interface{}{
104+
"foo": 42,
105+
},
106+
map[string]interface{}{},
107+
},
108+
}
109+
assert.Equal(t, expected, x)
110+
}
111+
112+
func pruningFixture() (*spec.Schema, error) {
113+
fname := filepath.Join(pruneFixturesPath, "schema.json")
114+
b, err := ioutil.ReadFile(fname)
115+
if err != nil {
116+
return nil, err
117+
}
118+
var schema spec.Schema
119+
if err := json.Unmarshal(b, &schema); err != nil {
120+
return nil, err
121+
}
122+
123+
return &schema, spec.ExpandSchema(&schema, nil, nil /*new(noopResCache)*/)
124+
}

0 commit comments

Comments
 (0)