Skip to content

Commit 7413c27

Browse files
slessardsl255051
andauthored
openapi3filter: Include schema ref or title in response body validation errors (#699)
Co-authored-by: Steve Lessard <[email protected]>
1 parent b003421 commit 7413c27

File tree

6 files changed

+39
-11
lines changed

6 files changed

+39
-11
lines changed

openapi3filter/options_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,5 @@ paths:
7878

7979
fmt.Println(err.Error())
8080

81-
// Output: request body has an error: doesn't match the schema: field "Some field" must be an integer
81+
// Output: request body has an error: doesn't match schema: field "Some field" must be an integer
8282
}

openapi3filter/validate_request.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,12 @@ func ValidateRequestBody(ctx context.Context, input *RequestValidationInput, req
286286

287287
// Validate JSON with the schema
288288
if err := contentType.Schema.Value.VisitJSON(value, opts...); err != nil {
289+
schemaId := getSchemaIdentifier(contentType.Schema)
290+
schemaId = prependSpaceIfNeeded(schemaId)
289291
return &RequestError{
290292
Input: input,
291293
RequestBody: requestBody,
292-
Reason: "doesn't match the schema",
294+
Reason: fmt.Sprintf("doesn't match schema%s", schemaId),
293295
Err: err,
294296
}
295297
}

openapi3filter/validate_response.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io/ioutil"
99
"net/http"
1010
"sort"
11+
"strings"
1112

1213
"github.com/getkin/kin-openapi/openapi3"
1314
)
@@ -159,11 +160,36 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error
159160

160161
// Validate data with the schema.
161162
if err := contentType.Schema.Value.VisitJSON(value, append(opts, openapi3.VisitAsResponse())...); err != nil {
163+
schemaId := getSchemaIdentifier(contentType.Schema)
164+
schemaId = prependSpaceIfNeeded(schemaId)
162165
return &ResponseError{
163166
Input: input,
164-
Reason: "response body doesn't match the schema",
167+
Reason: fmt.Sprintf("response body doesn't match schema%s", schemaId),
165168
Err: err,
166169
}
167170
}
168171
return nil
169172
}
173+
174+
// getSchemaIdentifier gets something by which a schema could be identified.
175+
// A schema by itself doesn't have a true identity field. This function makes
176+
// a best effort to get a value that can fill that void.
177+
func getSchemaIdentifier(schema *openapi3.SchemaRef) string {
178+
var id string
179+
180+
if schema != nil {
181+
id = strings.TrimSpace(schema.Ref)
182+
}
183+
if id == "" && schema.Value != nil {
184+
id = strings.TrimSpace(schema.Value.Title)
185+
}
186+
187+
return id
188+
}
189+
190+
func prependSpaceIfNeeded(value string) string {
191+
if len(value) > 0 {
192+
value = " " + value
193+
}
194+
return value
195+
}

openapi3filter/validation_error_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ func getValidationTests(t *testing.T) []*validationTest {
322322
args: validationArgs{
323323
r: newPetstoreRequest(t, http.MethodPost, "/pet", bytes.NewBufferString(`{"status":"watdis"}`)),
324324
},
325-
wantErrReason: "doesn't match the schema",
325+
wantErrReason: "doesn't match schema #/components/schemas/PetWithRequired",
326326
wantErrSchemaReason: "value \"watdis\" is not one of the allowed values",
327327
wantErrSchemaValue: "watdis",
328328
wantErrSchemaPath: "/status",
@@ -336,7 +336,7 @@ func getValidationTests(t *testing.T) []*validationTest {
336336
args: validationArgs{
337337
r: newPetstoreRequest(t, http.MethodPost, "/pet", bytes.NewBufferString(`{"name":"Bahama"}`)),
338338
},
339-
wantErrReason: "doesn't match the schema",
339+
wantErrReason: "doesn't match schema #/components/schemas/PetWithRequired",
340340
wantErrSchemaReason: `property "photoUrls" is missing`,
341341
wantErrSchemaValue: map[string]string{"name": "Bahama"},
342342
wantErrSchemaPath: "/photoUrls",
@@ -350,7 +350,7 @@ func getValidationTests(t *testing.T) []*validationTest {
350350
r: newPetstoreRequest(t, http.MethodPost, "/pet",
351351
bytes.NewBufferString(`{"name":"Bahama","photoUrls":[],"category":{}}`)),
352352
},
353-
wantErrReason: "doesn't match the schema",
353+
wantErrReason: "doesn't match schema #/components/schemas/PetWithRequired",
354354
wantErrSchemaReason: `property "name" is missing`,
355355
wantErrSchemaValue: map[string]string{},
356356
wantErrSchemaPath: "/category/name",
@@ -364,7 +364,7 @@ func getValidationTests(t *testing.T) []*validationTest {
364364
r: newPetstoreRequest(t, http.MethodPost, "/pet",
365365
bytes.NewBufferString(`{"name":"Bahama","photoUrls":[],"category":{"tags": [{}]}}`)),
366366
},
367-
wantErrReason: "doesn't match the schema",
367+
wantErrReason: "doesn't match schema #/components/schemas/PetWithRequired",
368368
wantErrSchemaReason: `property "name" is missing`,
369369
wantErrSchemaValue: map[string]string{},
370370
wantErrSchemaPath: "/category/tags/0/name",
@@ -378,7 +378,7 @@ func getValidationTests(t *testing.T) []*validationTest {
378378
r: newPetstoreRequest(t, http.MethodPost, "/pet",
379379
bytes.NewBufferString(`{"name":"Bahama","photoUrls":"http://cat"}`)),
380380
},
381-
wantErrReason: "doesn't match the schema",
381+
wantErrReason: "doesn't match schema #/components/schemas/PetWithRequired",
382382
wantErrSchemaReason: "field must be set to array or not be present",
383383
wantErrSchemaPath: "/photoUrls",
384384
wantErrSchemaValue: "string",
@@ -393,7 +393,7 @@ func getValidationTests(t *testing.T) []*validationTest {
393393
args: validationArgs{
394394
r: newPetstoreRequest(t, http.MethodPost, "/pet2", bytes.NewBufferString(`{"name":"Bahama"}`)),
395395
},
396-
wantErrReason: "doesn't match the schema",
396+
wantErrReason: "doesn't match schema",
397397
wantErrSchemaPath: "/",
398398
wantErrSchemaValue: map[string]string{"name": "Bahama"},
399399
wantErrSchemaOriginReason: `property "photoUrls" is missing`,

routers/gorillamux/example_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func Example() {
5353
err = openapi3filter.ValidateResponse(ctx, responseValidationInput)
5454
fmt.Println(err)
5555
// Output:
56-
// response body doesn't match the schema: field must be set to string or not be present
56+
// response body doesn't match schema pathref.openapi.yml#/components/schemas/TestSchema: field must be set to string or not be present
5757
// Schema:
5858
// {
5959
// "type": "string"

routers/legacy/validate_request_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,6 @@ func Example() {
107107
fmt.Println(err)
108108
}
109109
// Output:
110-
// request body has an error: doesn't match the schema: input matches more than one oneOf schemas
110+
// request body has an error: doesn't match schema: input matches more than one oneOf schemas
111111

112112
}

0 commit comments

Comments
 (0)