diff --git a/lib/rspec/openapi/schema_builder.rb b/lib/rspec/openapi/schema_builder.rb index 6e6cc3ea..641c27b4 100644 --- a/lib/rspec/openapi/schema_builder.rb +++ b/lib/rspec/openapi/schema_builder.rb @@ -5,7 +5,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new # @return [Hash] def build(record) response = { - description: record.description + description: record.description, } response_headers = build_response_headers(record) @@ -16,8 +16,8 @@ def build(record) response[:content] = { normalize_content_type(record.response_content_type) => { schema: build_property(record.response_body, disposition: disposition), - example: response_example(record, disposition: disposition) - }.compact + example: response_example(record, disposition: disposition), + }.compact, } end @@ -31,11 +31,11 @@ def build(record) parameters: build_parameters(record), requestBody: build_request_body(record), responses: { - record.status.to_s => response - } - }.compact - } - } + record.status.to_s => response, + }, + }.compact, + }, + }, } end @@ -65,7 +65,7 @@ def build_parameters(record) in: 'path', required: true, schema: build_property(try_cast(value)), - example: (try_cast(value) if example_enabled?) + example: (try_cast(value) if example_enabled?), }.compact end @@ -75,7 +75,7 @@ def build_parameters(record) in: 'query', required: record.required_request_params.include?(key), schema: build_property(try_cast(value)), - example: (try_cast(value) if example_enabled?) + example: (try_cast(value) if example_enabled?), }.compact end @@ -85,7 +85,7 @@ def build_parameters(record) in: 'header', required: true, schema: build_property(try_cast(value)), - example: (try_cast(value) if example_enabled?) + example: (try_cast(value) if example_enabled?), }.compact end @@ -99,7 +99,7 @@ def build_response_headers(record) record.response_headers.each do |key, value| headers[key] = { - schema: build_property(try_cast(value)) + schema: build_property(try_cast(value)), }.compact end @@ -124,9 +124,9 @@ def build_request_body(record) content: { normalize_content_type(record.request_content_type) => { schema: build_property(record.request_params), - example: (build_example(record.request_params) if example_enabled?) - }.compact - } + example: (build_example(record.request_params) if example_enabled?), + }.compact, + }, } end @@ -199,7 +199,14 @@ def adjust_params(value) adjust_params(v) when Array result = v.map do |item| - adjust_params(item) + case item + when ActionDispatch::Http::UploadedFile + item.original_filename + when Hash + adjust_params(item) + else + item + end end value[key] = result end diff --git a/spec/integration_tests/rails_test.rb b/spec/integration_tests/rails_test.rb index 4fb48930..b252a87e 100644 --- a/spec/integration_tests/rails_test.rb +++ b/spec/integration_tests/rails_test.rb @@ -162,6 +162,15 @@ class ImageTest < ActionDispatch::IntegrationTest post '/images/upload_multiple', params: { images: [image, image] } assert_response 200 end + + test 'returns a image payload with upload multiple nested' do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + image = Rack::Test::UploadedFile.new('test.png', 'image/png') + post '/images/upload_multiple_nested', params: { images: [{ image: image }, { image: image }] } + assert_response 200 + end end class ExtraRoutesTest < ActionDispatch::IntegrationTest diff --git a/spec/rails/app/controllers/images_controller.rb b/spec/rails/app/controllers/images_controller.rb index bcb49eb7..b2aab36e 100644 --- a/spec/rails/app/controllers/images_controller.rb +++ b/spec/rails/app/controllers/images_controller.rb @@ -25,6 +25,10 @@ def upload_multiple send_image end + def upload_multiple_nested + send_image + end + private def send_image diff --git a/spec/rails/config/routes.rb b/spec/rails/config/routes.rb index a0737865..4acd108b 100644 --- a/spec/rails/config/routes.rb +++ b/spec/rails/config/routes.rb @@ -8,6 +8,7 @@ post 'upload' post 'upload_nested' post 'upload_multiple' + post 'upload_multiple_nested' end end resources :users, only: [:show, :create] do diff --git a/spec/rails/doc/openapi.json b/spec/rails/doc/openapi.json index 0353c573..e6518ace 100644 --- a/spec/rails/doc/openapi.json +++ b/spec/rails/doc/openapi.json @@ -786,9 +786,9 @@ } } }, - "/images/upload_multiple": { + "/images/upload_nested": { "post": { - "summary": "upload_multiple", + "summary": "upload_nested", "tags": [ "Image" ], @@ -798,30 +798,81 @@ "schema": { "type": "object", "properties": { - "images": { - "type": "array", - "items": { - "type": "string", - "format": "binary" - } + "nested_image": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + }, + "caption": { + "type": "string" + } + }, + "required": [ + "image", + "caption" + ] } }, "required": [ - "images" + "nested_image" ] }, "example": { - "images": [ - "test.png", - "test.png" + "nested_image": { + "image": "test.png", + "caption": "Some caption" + } + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload nested", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload": { + "post": { + "summary": "upload", + "tags": [ + "Image" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "image" ] + }, + "example": { + "image": "test.png" } } } }, "responses": { "200": { - "description": "returns a image payload with upload multiple", + "description": "returns a image payload with upload", "content": { "image/png": { "schema": { @@ -834,9 +885,9 @@ } } }, - "/images/upload_nested": { + "/images/upload_multiple_nested": { "post": { - "summary": "upload_nested", + "summary": "upload_multiple_nested", "tags": [ "Image" ], @@ -846,39 +897,42 @@ "schema": { "type": "object", "properties": { - "nested_image": { - "type": "object", - "properties": { - "image": { - "type": "string", - "format": "binary" + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } }, - "caption": { - "type": "string" - } - }, - "required": [ - "image", - "caption" - ] + "required": [ + "image" + ] + } } }, "required": [ - "nested_image" + "images" ] }, "example": { - "nested_image": { - "image": "test.png", - "caption": "Some caption" - } + "images": [ + { + "image": "test.png" + }, + { + "image": "test.png" + } + ] } } } }, "responses": { "200": { - "description": "returns a image payload with upload nested", + "description": "returns a image payload with upload multiple nested", "content": { "image/png": { "schema": { @@ -891,9 +945,9 @@ } } }, - "/images/upload": { + "/images/upload_multiple": { "post": { - "summary": "upload", + "summary": "upload_multiple", "tags": [ "Image" ], @@ -903,24 +957,30 @@ "schema": { "type": "object", "properties": { - "image": { - "type": "string", - "format": "binary" + "images": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } } }, "required": [ - "image" + "images" ] }, "example": { - "image": "test.png" + "images": [ + "test.png", + "test.png" + ] } } } }, "responses": { "200": { - "description": "returns a image payload with upload", + "description": "returns a image payload with upload multiple", "content": { "image/png": { "schema": { diff --git a/spec/rails/doc/openapi.yaml b/spec/rails/doc/openapi.yaml index c9def75f..7e7eb55d 100644 --- a/spec/rails/doc/openapi.yaml +++ b/spec/rails/doc/openapi.yaml @@ -526,36 +526,6 @@ paths: example: - name: file.png tags: [] - "/images/upload_multiple": - post: - summary: upload_multiple - tags: - - Image - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - images: - type: array - items: - type: string - format: binary - required: - - images - example: - images: - - test.png - - test.png - responses: - '200': - description: returns a image payload with upload multiple - content: - image/png: - schema: - type: string - format: binary "/images/upload": post: summary: upload @@ -618,3 +588,68 @@ paths: schema: type: string format: binary + "/images/upload_multiple_nested": + post: + summary: upload_multiple_nested + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: object + properties: + image: + type: string + format: binary + required: + - image + required: + - images + example: + images: + - image: test.png + - image: test.png + responses: + '200': + description: returns a image payload with upload multiple nested + content: + image/png: + schema: + type: string + format: binary + "/images/upload_multiple": + post: + summary: upload_multiple + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: string + format: binary + required: + - images + example: + images: + - test.png + - test.png + responses: + '200': + description: returns a image payload with upload multiple + content: + image/png: + schema: + type: string + format: binary diff --git a/spec/requests/rails_spec.rb b/spec/requests/rails_spec.rb index 186174a1..d5febe6c 100644 --- a/spec/requests/rails_spec.rb +++ b/spec/requests/rails_spec.rb @@ -164,6 +164,20 @@ expect(response.status).to eq(200) end end + + describe '#upload_multiple_nested' do + before do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + end + let(:image) { Rack::Test::UploadedFile.new('test.png', 'image/png') } + + it 'returns a image payload with upload multiple nested' do + post '/images/upload_multiple_nested', params: { images: [{ image: image }, { image: image }] } + expect(response.status).to eq(200) + end + end end RSpec.describe 'Extra routes', type: :request do