Skip to content

Commit b304f8c

Browse files
authored
fix: only inject default value for matched oneOf or anyOf (#604)
1 parent e03b5a8 commit b304f8c

File tree

4 files changed

+166
-13
lines changed

4 files changed

+166
-13
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/go-openapi/jsonpointer v0.19.5
77
github.com/gorilla/mux v1.8.0
88
github.com/invopop/yaml v0.1.0
9+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
910
github.com/stretchr/testify v1.5.1
1011
gopkg.in/yaml.v2 v2.4.0 // indirect
1112
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1717
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
1818
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
1919
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
20+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
21+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
2022
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2123
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2224
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

openapi3/schema.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"unicode/utf16"
1414

1515
"github.com/go-openapi/jsonpointer"
16+
"github.com/mohae/deepcopy"
1617

1718
"github.com/getkin/kin-openapi/jsoninfo"
1819
)
@@ -915,9 +916,17 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
915916
}
916917
}
917918

918-
ok := 0
919-
validationErrors := []error{}
920-
for _, item := range v {
919+
var (
920+
ok = 0
921+
validationErrors = []error{}
922+
matchedOneOfIdx = 0
923+
tempValue = value
924+
)
925+
// make a deep copy to protect origin value from being injected default value that defined in mismatched oneOf schema
926+
if settings.asreq || settings.asrep {
927+
tempValue = deepcopy.Copy(value)
928+
}
929+
for idx, item := range v {
921930
v := item.Value
922931
if v == nil {
923932
return foundUnresolvedRef(item.Ref)
@@ -927,11 +936,12 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
927936
continue
928937
}
929938

930-
if err := v.visitJSON(settings, value); err != nil {
939+
if err := v.visitJSON(settings, tempValue); err != nil {
931940
validationErrors = append(validationErrors, err)
932941
continue
933942
}
934943

944+
matchedOneOfIdx = idx
935945
ok++
936946
}
937947

@@ -962,17 +972,30 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
962972

963973
return e
964974
}
975+
976+
if settings.asreq || settings.asrep {
977+
_ = v[matchedOneOfIdx].Value.visitJSON(settings, value)
978+
}
965979
}
966980

967981
if v := schema.AnyOf; len(v) > 0 {
968-
ok := false
969-
for _, item := range v {
982+
var (
983+
ok = false
984+
matchedAnyOfIdx = 0
985+
tempValue = value
986+
)
987+
// make a deep copy to protect origin value from being injected default value that defined in mismatched anyOf schema
988+
if settings.asreq || settings.asrep {
989+
tempValue = deepcopy.Copy(value)
990+
}
991+
for idx, item := range v {
970992
v := item.Value
971993
if v == nil {
972994
return foundUnresolvedRef(item.Ref)
973995
}
974-
if err := v.visitJSON(settings, value); err == nil {
996+
if err := v.visitJSON(settings, tempValue); err == nil {
975997
ok = true
998+
matchedAnyOfIdx = idx
976999
break
9771000
}
9781001
}
@@ -986,6 +1009,8 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
9861009
SchemaField: "anyOf",
9871010
}
9881011
}
1012+
1013+
_ = v[matchedAnyOfIdx].Value.visitJSON(settings, value)
9891014
}
9901015

9911016
for _, item := range schema.AllOf {

openapi3filter/validate_set_default_test.go

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,78 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
245245
}
246246
}
247247
}
248+
},
249+
"social_network": {
250+
"oneOf": [
251+
{
252+
"type": "object",
253+
"required": ["platform"],
254+
"properties": {
255+
"platform": {
256+
"type": "string",
257+
"enum": [
258+
"twitter"
259+
]
260+
},
261+
"tw_link": {
262+
"type": "string",
263+
"default": "www.twitter.com"
264+
}
265+
}
266+
},
267+
{
268+
"type": "object",
269+
"required": ["platform"],
270+
"properties": {
271+
"platform": {
272+
"type": "string",
273+
"enum": [
274+
"facebook"
275+
]
276+
},
277+
"fb_link": {
278+
"type": "string",
279+
"default": "www.facebook.com"
280+
}
281+
}
282+
}
283+
]
284+
},
285+
"social_network_2": {
286+
"anyOf": [
287+
{
288+
"type": "object",
289+
"required": ["platform"],
290+
"properties": {
291+
"platform": {
292+
"type": "string",
293+
"enum": [
294+
"twitter"
295+
]
296+
},
297+
"tw_link": {
298+
"type": "string",
299+
"default": "www.twitter.com"
300+
}
301+
}
302+
},
303+
{
304+
"type": "object",
305+
"required": ["platform"],
306+
"properties": {
307+
"platform": {
308+
"type": "string",
309+
"enum": [
310+
"facebook"
311+
]
312+
},
313+
"fb_link": {
314+
"type": "string",
315+
"default": "www.facebook.com"
316+
}
317+
}
318+
}
319+
]
248320
}
249321
}
250322
}
@@ -281,13 +353,20 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
281353
OP string `json:"op,omitempty"`
282354
Value int `json:"value,omitempty"`
283355
}
356+
type socialNetwork struct {
357+
Platform string `json:"platform,omitempty"`
358+
FBLink string `json:"fb_link,omitempty"`
359+
TWLink string `json:"tw_link,omitempty"`
360+
}
284361
type body struct {
285-
ID string `json:"id,omitempty"`
286-
Name string `json:"name,omitempty"`
287-
Code int `json:"code,omitempty"`
288-
All bool `json:"all,omitempty"`
289-
Page *page `json:"page,omitempty"`
290-
Filters []filter `json:"filters,omitempty"`
362+
ID string `json:"id,omitempty"`
363+
Name string `json:"name,omitempty"`
364+
Code int `json:"code,omitempty"`
365+
All bool `json:"all,omitempty"`
366+
Page *page `json:"page,omitempty"`
367+
Filters []filter `json:"filters,omitempty"`
368+
SocialNetwork *socialNetwork `json:"social_network,omitempty"`
369+
SocialNetwork2 *socialNetwork `json:"social_network_2,omitempty"`
291370
}
292371

293372
testCases := []struct {
@@ -531,6 +610,52 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
531610
"value": 456
532611
}
533612
]
613+
}
614+
`, body)
615+
},
616+
},
617+
{
618+
name: "social_network(oneOf)",
619+
body: body{
620+
ID: "bt6kdc3d0cvp6u8u3ft0",
621+
SocialNetwork: &socialNetwork{
622+
Platform: "facebook",
623+
},
624+
},
625+
bodyAssertion: func(t *testing.T, body string) {
626+
require.JSONEq(t, `
627+
{
628+
"id": "bt6kdc3d0cvp6u8u3ft0",
629+
"name": "default",
630+
"code": 123,
631+
"all": false,
632+
"social_network": {
633+
"platform": "facebook",
634+
"fb_link": "www.facebook.com"
635+
}
636+
}
637+
`, body)
638+
},
639+
},
640+
{
641+
name: "social_network_2(anyOf)",
642+
body: body{
643+
ID: "bt6kdc3d0cvp6u8u3ft0",
644+
SocialNetwork2: &socialNetwork{
645+
Platform: "facebook",
646+
},
647+
},
648+
bodyAssertion: func(t *testing.T, body string) {
649+
require.JSONEq(t, `
650+
{
651+
"id": "bt6kdc3d0cvp6u8u3ft0",
652+
"name": "default",
653+
"code": 123,
654+
"all": false,
655+
"social_network_2": {
656+
"platform": "facebook",
657+
"fb_link": "www.facebook.com"
658+
}
534659
}
535660
`, body)
536661
},

0 commit comments

Comments
 (0)