Skip to content

Commit 0862ed8

Browse files
emilien-pugetdaveshanley
authored andcommitted
handle media range
1 parent 7337a8a commit 0862ed8

File tree

2 files changed

+153
-2
lines changed

2 files changed

+153
-2
lines changed

requests/validate_body.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ func (v *requestBodyValidator) ValidateRequestBodyWithPathItem(request *http.Req
6464
}
6565

6666
// extract the media type from the content type header.
67-
ct, _, _ := helpers.ExtractContentType(contentType)
68-
mediaType, ok := operation.RequestBody.Content.Get(ct)
67+
mediaType, ok := v.extractContentType(contentType, operation)
6968
if !ok {
7069
return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, pathValue)}
7170
}
@@ -116,3 +115,20 @@ func (v *requestBodyValidator) ValidateRequestBodyWithPathItem(request *http.Req
116115

117116
return validationSucceeded, validationErrors
118117
}
118+
119+
func (v *requestBodyValidator) extractContentType(contentType string, operation *v3.Operation) (*v3.MediaType, bool) {
120+
ct, _, _ := helpers.ExtractContentType(contentType)
121+
mediaType, ok := operation.RequestBody.Content.Get(ct)
122+
if ok {
123+
return mediaType, true
124+
}
125+
ctMediaRange := strings.SplitN(ct, "/", 2)
126+
for s, mediaTypeValue := range operation.RequestBody.Content.FromOldest() {
127+
opMediaRange := strings.SplitN(s, "/", 2)
128+
if (opMediaRange[0] == "*" || opMediaRange[0] == ctMediaRange[0]) &&
129+
(opMediaRange[1] == "*" || opMediaRange[1] == ctMediaRange[1]) {
130+
return mediaTypeValue, true
131+
}
132+
}
133+
return nil, false
134+
}

requests/validate_body_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/pb33f/libopenapi"
1414
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
1516

1617
"github.com/pb33f/libopenapi-validator/paths"
1718
)
@@ -48,6 +49,140 @@ paths:
4849
assert.Len(t, errors, 0)
4950
}
5051

52+
func TestValidateBody_MediaRangeContentType_Wildcard_end(t *testing.T) {
53+
spec := `openapi: 3.1.0
54+
paths:
55+
/burgers/createBurger:
56+
post:
57+
requestBody:
58+
required: true
59+
content:
60+
thomas/*:
61+
schema:
62+
type: object
63+
properties:
64+
name:
65+
type: string
66+
patties:
67+
type: integer
68+
vegetarian:
69+
type: boolean`
70+
71+
doc, _ := libopenapi.NewDocument([]byte(spec))
72+
73+
m, _ := doc.BuildV3Model()
74+
v := NewRequestBodyValidator(&m.Model)
75+
76+
// mix up the primitives to fire two schema violations.
77+
body := map[string]interface{}{
78+
"name": "Big Mac",
79+
"patties": false,
80+
"vegetarian": 2,
81+
}
82+
83+
bodyBytes, _ := json.Marshal(body)
84+
85+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger",
86+
bytes.NewBuffer(bodyBytes))
87+
request.Header.Set("Content-Type", "thomas/tank-engine") // wtf kinda content type is this?
88+
89+
valid, errors := v.ValidateRequestBody(request)
90+
91+
assert.True(t, valid)
92+
assert.Len(t, errors, 0)
93+
}
94+
95+
func TestValidateBody_MediaRangeContentType_Wildcards(t *testing.T) {
96+
spec := `openapi: 3.1.0
97+
paths:
98+
/burgers/createBurger:
99+
post:
100+
requestBody:
101+
required: true
102+
content:
103+
"*/*":
104+
schema:
105+
type: object
106+
properties:
107+
name:
108+
type: string
109+
patties:
110+
type: integer
111+
vegetarian:
112+
type: boolean`
113+
114+
doc, err := libopenapi.NewDocument([]byte(spec))
115+
require.NoError(t, err)
116+
m, _ := doc.BuildV3Model()
117+
v := NewRequestBodyValidator(&m.Model)
118+
119+
// mix up the primitives to fire two schema violations.
120+
body := map[string]interface{}{
121+
"name": "Big Mac",
122+
"patties": false,
123+
"vegetarian": 2,
124+
}
125+
126+
bodyBytes, _ := json.Marshal(body)
127+
128+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger",
129+
bytes.NewBuffer(bodyBytes))
130+
request.Header.Set("Content-Type", "thomas/tank-engine") // wtf kinda content type is this?
131+
132+
valid, errors := v.ValidateRequestBody(request)
133+
134+
assert.True(t, valid)
135+
assert.Len(t, errors, 0)
136+
}
137+
138+
func TestValidateBody_InvalidBasicSchema_MediaRangeContentType_Wildcard_Required(t *testing.T) {
139+
spec := `openapi: 3.1.0
140+
paths:
141+
/burgers/createBurger:
142+
post:
143+
requestBody:
144+
required: false
145+
content:
146+
"*/json":
147+
schema:
148+
type: object
149+
properties:
150+
name:
151+
type: string
152+
patties:
153+
type: integer
154+
vegetarian:
155+
type: boolean`
156+
157+
doc, _ := libopenapi.NewDocument([]byte(spec))
158+
159+
m, _ := doc.BuildV3Model()
160+
v := NewRequestBodyValidator(&m.Model)
161+
162+
// mix up the primitives to fire two schema violations.
163+
body := map[string]interface{}{
164+
"name": "Big Mac",
165+
"patties": false,
166+
"vegetarian": 2,
167+
}
168+
169+
bodyBytes, _ := json.Marshal(body)
170+
171+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger",
172+
bytes.NewBuffer(bodyBytes))
173+
request.Header.Set("Content-Type", "foo/json")
174+
175+
valid, errors := v.ValidateRequestBody(request)
176+
177+
// double-tap to hit the cache
178+
_, _ = v.ValidateRequestBody(request)
179+
180+
assert.False(t, valid)
181+
assert.Len(t, errors, 1)
182+
assert.Len(t, errors[0].SchemaValidationErrors, 2)
183+
assert.Equal(t, "POST request body for '/burgers/createBurger' failed to validate schema", errors[0].Message)
184+
}
185+
51186
func TestValidateBody_UnknownContentType(t *testing.T) {
52187
spec := `openapi: 3.1.0
53188
paths:

0 commit comments

Comments
 (0)