Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix lost error types in oneOf #658

Merged
merged 1 commit into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 17 additions & 3 deletions openapi3/errors.go
Expand Up @@ -10,11 +10,15 @@ import (
type MultiError []error

func (me MultiError) Error() string {
return spliceErr(" | ", me)
}

func spliceErr(sep string, errs []error) string {
buff := &bytes.Buffer{}
for i, e := range me {
for i, e := range errs {
buff.WriteString(e.Error())
if i != len(me)-1 {
buff.WriteString(" | ")
if i != len(errs)-1 {
buff.WriteString(sep)
}
}
return buff.String()
Expand Down Expand Up @@ -43,3 +47,13 @@ func (me MultiError) As(target interface{}) bool {
}
return false
}

type multiErrorForOneOf MultiError

func (meo multiErrorForOneOf) Error() string {
return spliceErr(" Or ", meo)
}

func (meo multiErrorForOneOf) Unwrap() error {
return MultiError(meo)
}
79 changes: 79 additions & 0 deletions openapi3/issue657_test.go
@@ -0,0 +1,79 @@
package openapi3_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/getkin/kin-openapi/openapi3"
)

func TestOneOf_Warning_Errors(t *testing.T) {
t.Parallel()

loader := openapi3.NewLoader()
spec := `
components:
schemas:
Something:
type: object
properties:
field:
title: Some field
oneOf:
- title: First rule
type: string
minLength: 10
maxLength: 10
- title: Second rule
type: string
minLength: 15
maxLength: 15
`[1:]

doc, err := loader.LoadFromData([]byte(spec))
require.NoError(t, err)

tests := [...]struct {
name string
value string
checkErr require.ErrorAssertionFunc
}{
{
name: "valid value",
value: "ABCDE01234",
checkErr: require.NoError,
},
{
name: "valid value",
value: "ABCDE0123456789",
checkErr: require.NoError,
},
{
name: "no valid value",
value: "ABCDE",
checkErr: func(t require.TestingT, err error, i ...interface{}) {
require.Equal(t, "doesn't match schema due to: minimum string length is 10\nSchema:\n {\n \"maxLength\": 10,\n \"minLength\": 10,\n \"title\": \"First rule\",\n \"type\": \"string\"\n }\n\nValue:\n \"ABCDE\"\n Or minimum string length is 15\nSchema:\n {\n \"maxLength\": 15,\n \"minLength\": 15,\n \"title\": \"Second rule\",\n \"type\": \"string\"\n }\n\nValue:\n \"ABCDE\"\n", err.Error())

wErr := &openapi3.MultiError{}
require.ErrorAs(t, err, wErr)

require.Len(t, *wErr, 2)

require.Equal(t, "minimum string length is 10", (*wErr)[0].(*openapi3.SchemaError).Reason)
require.Equal(t, "minimum string length is 15", (*wErr)[1].(*openapi3.SchemaError).Reason)
},
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()

err = doc.Components.Schemas["Something"].Value.Properties["field"].Value.VisitJSON(test.value)

test.checkErr(t, err)
})
}
}
11 changes: 2 additions & 9 deletions openapi3/schema.go
Expand Up @@ -926,7 +926,7 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val

var (
ok = 0
validationErrors = []error{}
validationErrors = multiErrorForOneOf{}
matchedOneOfIdx = 0
tempValue = value
)
Expand Down Expand Up @@ -955,14 +955,7 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val

if ok != 1 {
if len(validationErrors) > 1 {
errorMessage := ""
for _, err := range validationErrors {
if errorMessage != "" {
errorMessage += " Or "
}
errorMessage += err.Error()
}
return errors.New("doesn't match schema due to: " + errorMessage)
return fmt.Errorf("doesn't match schema due to: %w", validationErrors)
}
if settings.failfast {
return errSchema
Expand Down