Skip to content

Commit 04189e5

Browse files
committed
address issue pb33f/wiretap#146
1 parent ce27721 commit 04189e5

4 files changed

Lines changed: 92 additions & 9 deletions

File tree

requests/validate_body.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,11 @@ func (v *requestBodyValidator) ValidateRequestBodyWithPathItem(request *http.Req
126126
}
127127

128128
validationSucceeded, validationErrors := ValidateRequestSchema(&ValidateRequestSchemaInput{
129-
Request: request,
130-
Schema: schema,
131-
Version: helpers.VersionToFloat(v.document.Version),
132-
Options: []config.Option{config.WithExistingOpts(v.options)},
129+
Request: request,
130+
Schema: schema,
131+
Version: helpers.VersionToFloat(v.document.Version),
132+
Options: []config.Option{config.WithExistingOpts(v.options)},
133+
BodyRequired: required,
133134
})
134135

135136
errors.PopulateValidationErrors(validationErrors, request, pathValue)

requests/validate_body_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,7 @@ paths:
11541154
/burgers/createBurger:
11551155
post:
11561156
requestBody:
1157+
required: true
11571158
content:
11581159
application/json:
11591160
schema:
@@ -1448,6 +1449,41 @@ func TestValidateBody_SchemaNoType_Issue75(t *testing.T) {
14481449
assert.Equal(t, "PUT request body is empty for '/path1'", valErrs[0].Message)
14491450
}
14501451

1452+
// https://github.com/pb33f/wiretap/issues/146
1453+
func TestValidateBody_OptionalRequestBody_EmptyBody(t *testing.T) {
1454+
spec := `openapi: 3.0.1
1455+
info:
1456+
title: test
1457+
version: "1.0"
1458+
paths:
1459+
/test:
1460+
post:
1461+
requestBody:
1462+
required: false
1463+
content:
1464+
application/json:
1465+
schema:
1466+
type: object
1467+
properties:
1468+
name:
1469+
type: string
1470+
responses:
1471+
"200":
1472+
description: OK`
1473+
1474+
doc, _ := libopenapi.NewDocument([]byte(spec))
1475+
v3Model, _ := doc.BuildV3Model()
1476+
1477+
req, _ := http.NewRequest("POST", "/test", nil)
1478+
req.Header.Set("Content-Type", "application/json")
1479+
1480+
reqBodyValidator := NewRequestBodyValidator(&v3Model.Model)
1481+
isSuccess, valErrs := reqBodyValidator.ValidateRequestBody(req)
1482+
1483+
assert.True(t, isSuccess)
1484+
assert.Empty(t, valErrs)
1485+
}
1486+
14511487
// https://github.com/pb33f/libopenapi-validator/issues/144
14521488
func TestValidateBody_InvalidSchema_EnsureOptionsPassthrough(t *testing.T) {
14531489
spec := `openapi: 3.1.0

requests/validate_request.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ var instanceLocationRegex = regexp.MustCompile(`^/(\d+)`)
3232

3333
// ValidateRequestSchemaInput contains parameters for request schema validation.
3434
type ValidateRequestSchemaInput struct {
35-
Request *http.Request // Required: The HTTP request to validate
36-
Schema *base.Schema // Required: The OpenAPI schema to validate against
37-
Version float32 // Required: OpenAPI version (3.0 or 3.1)
38-
Options []config.Option // Optional: Functional options (defaults applied if empty/nil)
35+
Request *http.Request // Required: The HTTP request to validate
36+
Schema *base.Schema // Required: The OpenAPI schema to validate against
37+
Version float32 // Required: OpenAPI version (3.0 or 3.1)
38+
Options []config.Option // Optional: Functional options (defaults applied if empty/nil)
39+
BodyRequired bool // Optional: Whether the request body is required (default false)
3940
}
4041

4142
// ValidateRequestSchema will validate a http.Request pointer against a schema.
@@ -178,6 +179,9 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
178179

179180
// no request body? but we do have a schema?
180181
if len(requestBody) == 0 && len(jsonSchema) > 0 {
182+
if !input.BodyRequired {
183+
return true, nil
184+
}
181185

182186
line := schema.ParentProxy.GetSchemaKeyNode().Line
183187
col := schema.ParentProxy.GetSchemaKeyNode().Line
@@ -186,7 +190,6 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
186190
col = schema.GoLow().Type.KeyNode.Column
187191
}
188192

189-
// cannot decode the request body, so it's not valid
190193
validationErrors = append(validationErrors, &errors.ValidationError{
191194
ValidationType: helpers.RequestBodyValidation,
192195
ValidationSubType: helpers.Schema,

requests/validate_request_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,41 @@ func TestValidateRequestSchema_NilSchemaGoLow(t *testing.T) {
192192
assert.Contains(t, errors[0].Reason, "does not have low-level information")
193193
}
194194

195+
func TestValidateRequestSchema_EmptyBodyOptional(t *testing.T) {
196+
schema := parseSchemaFromSpec(t, `type: object
197+
properties:
198+
name:
199+
type: string`, 3.1)
200+
201+
valid, errors := ValidateRequestSchema(&ValidateRequestSchemaInput{
202+
Request: emptyPostRequest(),
203+
Schema: schema,
204+
Version: 3.1,
205+
BodyRequired: false,
206+
})
207+
208+
assert.True(t, valid)
209+
assert.Empty(t, errors)
210+
}
211+
212+
func TestValidateRequestSchema_EmptyBodyRequired(t *testing.T) {
213+
schema := parseSchemaFromSpec(t, `type: object
214+
properties:
215+
name:
216+
type: string`, 3.1)
217+
218+
valid, errors := ValidateRequestSchema(&ValidateRequestSchemaInput{
219+
Request: emptyPostRequest(),
220+
Schema: schema,
221+
Version: 3.1,
222+
BodyRequired: true,
223+
})
224+
225+
assert.False(t, valid)
226+
require.Len(t, errors, 1)
227+
assert.Contains(t, errors[0].Message, "request body is empty")
228+
}
229+
195230
func postRequestWithBody(payload string) *http.Request {
196231
return &http.Request{
197232
Method: http.MethodPost,
@@ -200,6 +235,14 @@ func postRequestWithBody(payload string) *http.Request {
200235
}
201236
}
202237

238+
func emptyPostRequest() *http.Request {
239+
return &http.Request{
240+
Method: http.MethodPost,
241+
URL: &url.URL{Path: "/test"},
242+
Body: http.NoBody,
243+
}
244+
}
245+
203246
// parseSchemaFromSpec creates a base.Schema from an OpenAPI spec YAML string.
204247
// This ensures that we're using the native libopenapi logic for generating the schema.
205248
func parseSchemaFromSpec(t *testing.T, schemaYAML string, version float32) *base.Schema {

0 commit comments

Comments
 (0)