diff --git a/lib/rspec/openapi/schema_merger.rb b/lib/rspec/openapi/schema_merger.rb index 0eef16e0..9d10e4e3 100644 --- a/lib/rspec/openapi/schema_merger.rb +++ b/lib/rspec/openapi/schema_merger.rb @@ -25,22 +25,23 @@ def normalize_keys(spec) # Also this needs to be aware of OpenAPI details unlike an ordinary deep_reverse_merge # because a Hash-like structure may be an array whose Hash elements have a key name. # - # TODO: Perform more intelligent merges like rerouting edits / merging types # TODO: Should we probably force-merge `summary` regardless of manual modifications? def deep_reverse_merge!(base, spec) spec.each do |key, value| if base[key].is_a?(Hash) && value.is_a?(Hash) - deep_reverse_merge!(base[key], value) - elsif !base.key?(key) - base[key] = value + if !base[key].key?("$ref") + deep_reverse_merge!(base[key], value) + end elsif base[key].is_a?(Array) && value.is_a?(Array) # parameters need to be merged as if `name` and `in` were the Hash keys. if key == 'parameters' base[key] |= value base[key].uniq! { |param| param.slice('name', 'in') } + else + base[key] = value end else - # no-op + base[key] = value end end base diff --git a/spec/rails/doc/openapi.json b/spec/rails/doc/openapi.json index 0a0aceb6..a9847448 100644 --- a/spec/rails/doc/openapi.json +++ b/spec/rails/doc/openapi.json @@ -41,7 +41,7 @@ } }, "200": { - "description": "with flat query parameters", + "description": "with different deep query parameters", "content": { "application/json": { "schema": { @@ -406,7 +406,7 @@ }, "example": { "nested": { - "image": "#", + "image": "test.png", "caption": "Some caption" } } diff --git a/spec/rails/doc/openapi.yaml b/spec/rails/doc/openapi.yaml index 1c5bc521..642fee47 100644 --- a/spec/rails/doc/openapi.yaml +++ b/spec/rails/doc/openapi.yaml @@ -56,7 +56,7 @@ paths: example: token responses: '200': - description: returns a list of tables + description: with different deep query parameters content: application/json: schema: @@ -93,7 +93,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -165,7 +165,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -218,7 +218,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -310,7 +310,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -362,7 +362,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' diff --git a/spec/rspec/schema_merger/base.json b/spec/rspec/schema_merger/base.json new file mode 100644 index 00000000..38449306 --- /dev/null +++ b/spec/rspec/schema_merger/base.json @@ -0,0 +1,64 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "My API", + "version": "1.0.0" + }, + "servers": [], + "paths": { + "/tables": { + "get": { + "summary": "index", + "tags": [ + "Table" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 1 + } + ], + "responses": { + "200": { + "description": "foo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Table" + }, + "example": [ + { + "id": 1, + "name": "THIS SHOULD BE UPDATED" + } + ] + } + } + } + }, + "parameters": [] + } + } + }, + "components": { + "schemas": { + "Table": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/spec/rspec/schema_merger/expected.json b/spec/rspec/schema_merger/expected.json new file mode 100644 index 00000000..b4b8db07 --- /dev/null +++ b/spec/rspec/schema_merger/expected.json @@ -0,0 +1,63 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "My API", + "version": "1.0.0" + }, + "servers": [], + "paths": { + "/tables": { + "get": { + "summary": "index", + "tags": [ + "Table" + ], + "parameters": [ + { + "name": "foo_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 9999 + } + ], + "responses": { + "200": { + "description": "foo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Table" + }, + "example": [ + { + "id": 9999, + "name": "UPDATED" + } + ] + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Table": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/spec/rspec/schema_merger/input.json b/spec/rspec/schema_merger/input.json new file mode 100644 index 00000000..32c0706e --- /dev/null +++ b/spec/rspec/schema_merger/input.json @@ -0,0 +1,49 @@ +{ + "paths": { + "/tables": { + "get": { + "summary": "index", + "tags": [ + "Table" + ], + "parameters": [ + { + "name": "foo_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 9999 + } + ], + "responses": { + "200": { + "description": "foo", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "example": [ + { + "id": 9999, + "name": "UPDATED" + } + ] + } + } + } + } + } + } + } +} diff --git a/spec/rspec/schema_merger/schema_merger_spec.rb b/spec/rspec/schema_merger/schema_merger_spec.rb new file mode 100644 index 00000000..a87154c9 --- /dev/null +++ b/spec/rspec/schema_merger/schema_merger_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +require 'json' +require "rspec/openapi/schema_merger" + +RSpec.describe "SchemaMerger" do + include SpecHelper + + let(:base_path) do + File.expand_path('spec/rspec/schema_merger/base.json', repo_root) + end + + let(:input_path) do + File.expand_path('spec/rspec/schema_merger/input.json', repo_root) + end + + let(:expected_path) do + File.expand_path('spec/rspec/schema_merger/expected.json', repo_root) + end + + it "overwrite the supported key, but leaves the unsupported keys" do + base_json = JSON.load(File.read(base_path)) + input_json = JSON.load(File.read(input_path)) + res = RSpec::OpenAPI::SchemaMerger.reverse_merge!(base_json, input_json) + expected_json = JSON.load(File.read(expected_path)) + expect(res).to eq(expected_json) + end +end