From 82da6e2aa7869db1c22f1bc3b04b3d54e254991c Mon Sep 17 00:00:00 2001 From: Omer E <33223663+tcdsv@users.noreply.github.com> Date: Wed, 15 Nov 2023 22:02:35 +0200 Subject: [PATCH] flatten Default field (#432) fixes https://github.com/Tufin/oasdiff/issues/431 --- flatten/merge_allof.go | 30 ++++++++- flatten/merge_allof_test.go | 121 ++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/flatten/merge_allof.go b/flatten/merge_allof.go index 928e9814..00790efa 100644 --- a/flatten/merge_allof.go +++ b/flatten/merge_allof.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "reflect" "regexp" "strings" @@ -13,7 +14,7 @@ import ( const ( FormatErrorMessage = "unable to resolve Format conflict using default resolver: all Format values must be identical" TypeErrorMessage = "unable to resolve Type conflict: all Type values must be identical" - FormatResolverError = "" + DefaultErrorMessage = "unable to resolve Default conflict: all Default values must be identical" FormatInt32 = "int32" FormatInt64 = "int64" @@ -50,6 +51,7 @@ type SchemaCollection struct { Nullable []bool ReadOnly []bool WriteOnly []bool + Default []interface{} } type state struct { @@ -137,6 +139,7 @@ func mergeInternal(state *state, base *openapi3.SchemaRef) (*openapi3.SchemaRef, result.Value.Max = base.Value.Max result.Value.MultipleOf = base.Value.MultipleOf result.Value.MinLength = base.Value.MinLength + result.Value.Default = base.Value.Default if base.Value.MaxLength != nil { result.Value.MaxLength = openapi3.Uint64Ptr(*base.Value.MaxLength) } @@ -301,7 +304,10 @@ func flattenSchemas(state *state, result *openapi3.SchemaRef, schemas []*openapi result.Value.Required = resolveRequired(collection.Required) result.Value = resolveMultipleOf(result.Value, &collection) result.Value.UniqueItems = resolveUniqueItems(collection.UniqueItems) - + result.Value.Default, err = resolveDefault(&collection) + if err != nil { + return err + } result.Value.Enum, err = resolveEnum(collection.Enum) if err != nil { return err @@ -873,6 +879,7 @@ func collect(schemas []*openapi3.SchemaRef) SchemaCollection { collection.Nullable = append(collection.Nullable, s.Value.Nullable) collection.ReadOnly = append(collection.ReadOnly, s.Value.ReadOnly) collection.WriteOnly = append(collection.WriteOnly, s.Value.WriteOnly) + collection.Default = append(collection.Default, s.Value.Default) } return collection } @@ -1008,3 +1015,22 @@ func findIntersection(arrays ...[]string) []string { return intersection } + +func resolveDefault(collection *SchemaCollection) (interface{}, error) { + values := make([]interface{}, 0) + for _, v := range collection.Default { + if v != nil { + values = append(values, v) + } + } + if len(values) == 0 { + return nil, nil + } + first := values[0] + for _, v := range values { + if !reflect.DeepEqual(first, v) { + return nil, errors.New(DefaultErrorMessage) + } + } + return first, nil +} diff --git a/flatten/merge_allof_test.go b/flatten/merge_allof_test.go index 7ad5fb3b..99106da5 100644 --- a/flatten/merge_allof_test.go +++ b/flatten/merge_allof_test.go @@ -10,6 +10,127 @@ import ( "github.com/stretchr/testify/require" ) +// identical Default fields are merged successfully +func TestMerge_Default(t *testing.T) { + merged, err := flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Default: 10, + }, + }) + + require.NoError(t, err) + require.Equal(t, 10, merged.Default) + + merged, err = flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.Nil(t, merged.AllOf) + require.Equal(t, nil, merged.Default) + + merged, err = flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: 10, + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.Nil(t, merged.AllOf) + require.Equal(t, 10, merged.Default) + + merged, err = flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: 10, + }, + }, + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: 10, + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.Nil(t, merged.AllOf) + require.Equal(t, 10, merged.Default) + + merged, err = flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: "abc", + }, + }, + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: "abc", + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.Nil(t, merged.AllOf) + require.Equal(t, "abc", merged.Default) +} + +// Conflicting Default values cannot be resolved +func TestMerge_DefaultFailure(t *testing.T) { + _, err := flatten.Merge( + openapi3.SchemaRef{ + Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: 10, + }, + }, + &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: "object", + Default: "abc", + }, + }, + }, + }, + }) + + require.EqualError(t, err, flatten.DefaultErrorMessage) +} + // verify that if all ReadOnly fields are set to false, then the ReadOnly field in the merged schema is false. func TestMerge_ReadOnlyIsSetToFalse(t *testing.T) { merged, err := flatten.Merge(