From aa8fceae7eccbd7d2506edade618fdf3b705006c Mon Sep 17 00:00:00 2001 From: Omer E <33223663+tcdsv@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:49:27 +0300 Subject: [PATCH] flatten-allof fix circular additionalProperties overflow (#529) --- flatten/allof/merge_allof.go | 4 +- flatten/allof/merge_allof_spec_test.go | 45 +++++++++++++++++++ .../testdata/circular_additional_props1.yaml | 14 ++++++ .../testdata/circular_additional_props2.yaml | 19 ++++++++ .../testdata/circular_additional_props3.yaml | 17 +++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 flatten/allof/testdata/circular_additional_props1.yaml create mode 100644 flatten/allof/testdata/circular_additional_props2.yaml create mode 100644 flatten/allof/testdata/circular_additional_props3.yaml diff --git a/flatten/allof/merge_allof.go b/flatten/allof/merge_allof.go index 0bdac0fe..1ff2c311 100644 --- a/flatten/allof/merge_allof.go +++ b/flatten/allof/merge_allof.go @@ -580,7 +580,9 @@ func resolveNonFalseAdditionalProps(state *state, schema *openapi3.Schema, colle } var schemaRef *openapi3.SchemaRef - if len(additionalSchemas) > 0 { + if len(additionalSchemas) == 1 { + schemaRef = additionalSchemas[0] + } else if len(additionalSchemas) > 1 { result := openapi3.NewSchemaRef("", openapi3.NewSchema()) err := flattenSchemas(state, result, additionalSchemas) if err != nil { diff --git a/flatten/allof/merge_allof_spec_test.go b/flatten/allof/merge_allof_spec_test.go index 48fff4f7..507e96ff 100644 --- a/flatten/allof/merge_allof_spec_test.go +++ b/flatten/allof/merge_allof_spec_test.go @@ -26,3 +26,48 @@ func Test_MergeSpecInvalid(t *testing.T) { _, err := load.NewSpecInfo(openapi3.NewLoader(), load.NewSource("../../data/allof/invalid.yaml"), load.WithFlattenAllOf()) require.EqualError(t, err, "failed to flatten allOf in \"../../data/allof/invalid.yaml\": unable to resolve Type conflict: all Type values must be identical") } + +func TestMergeSpec_CircularAdditionalPropsWithoutAllOf(t *testing.T) { + spec, err := load.NewSpecInfo(openapi3.NewLoader(), load.NewSource("testdata/circular_additional_props1.yaml"), load.WithFlattenAllOf()) + require.NoError(t, err) + + merged := spec.Spec + require.Equal(t, "object", merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.Type) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value) + + baseSchema := merged.Components.Schemas["BaseSchema"].Value + referencedAdditionalPropSchema := merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value + require.Equal(t, baseSchema, referencedAdditionalPropSchema) +} + +func TestMergeSpec_MergeCircularAdditionalPropsWithAllOf(t *testing.T) { + spec, err := load.NewSpecInfo(openapi3.NewLoader(), load.NewSource("testdata/circular_additional_props2.yaml"), load.WithFlattenAllOf()) + require.NoError(t, err) + + merged := spec.Spec + require.Nil(t, merged.Components.Schemas["BaseSchema"].Value.AllOf) + require.Equal(t, "string", merged.Components.Schemas["BaseSchema"].Value.Properties["fixedProperty"].Value.Type) + require.Equal(t, "object", merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.Type) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value) + + baseSchema := merged.Components.Schemas["BaseSchema"].Value + referencedAdditionalPropSchema := merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value + require.Equal(t, baseSchema, referencedAdditionalPropSchema) +} + +func TestMergeSpec_MergeCircularAdditionalPropsNestedWithinAllOf(t *testing.T) { + spec, err := load.NewSpecInfo(openapi3.NewLoader(), load.NewSource("testdata/circular_additional_props3.yaml"), load.WithFlattenAllOf()) + require.NoError(t, err) + + merged := spec.Spec + require.Nil(t, merged.Components.Schemas["BaseSchema"].Value.AllOf) + require.Equal(t, "object", merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.Type) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema) + require.NotNil(t, merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value) + + baseSchemaReferencedAdditionalPropSchema := merged.Components.Schemas["BaseSchema"].Value.Properties["prop1"].Value.AdditionalProperties.Schema.Value + NestedSelfReferentialSchema := merged.Components.Schemas["NestedSelfReferentialSchema"].Value + require.Equal(t, baseSchemaReferencedAdditionalPropSchema, NestedSelfReferentialSchema) +} diff --git a/flatten/allof/testdata/circular_additional_props1.yaml b/flatten/allof/testdata/circular_additional_props1.yaml new file mode 100644 index 00000000..e51a38f4 --- /dev/null +++ b/flatten/allof/testdata/circular_additional_props1.yaml @@ -0,0 +1,14 @@ +openapi: 3.0.0 +info: + title: Circular AdditionalProperties + version: 1.0.0 +paths: {} +components: + schemas: + BaseSchema: + type: object + properties: + prop1: + type: object + additionalProperties: + $ref: '#/components/schemas/BaseSchema' \ No newline at end of file diff --git a/flatten/allof/testdata/circular_additional_props2.yaml b/flatten/allof/testdata/circular_additional_props2.yaml new file mode 100644 index 00000000..9fe3da1c --- /dev/null +++ b/flatten/allof/testdata/circular_additional_props2.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.0 +info: + title: Circular AdditionalProperties + version: 1.0.0 +paths: {} +components: + schemas: + BaseSchema: + type: object + allOf: + - type: object + properties: + fixedProperty: + type: string + properties: + prop1: + type: object + additionalProperties: + $ref: '#/components/schemas/BaseSchema' \ No newline at end of file diff --git a/flatten/allof/testdata/circular_additional_props3.yaml b/flatten/allof/testdata/circular_additional_props3.yaml new file mode 100644 index 00000000..4bc49fd8 --- /dev/null +++ b/flatten/allof/testdata/circular_additional_props3.yaml @@ -0,0 +1,17 @@ +openapi: 3.0.0 +info: + title: Self-Referential allOf Schema API + version: 1.0.0 +paths: {} +components: + schemas: + BaseSchema: + allOf: + - $ref: '#/components/schemas/NestedSelfReferentialSchema' + NestedSelfReferentialSchema: + type: object + properties: + prop1: + type: object + additionalProperties: + $ref: '#/components/schemas/NestedSelfReferentialSchema'