diff --git a/rswag-specs/lib/rswag/specs/request_factory.rb b/rswag-specs/lib/rswag/specs/request_factory.rb index 523b50f..3e6237a 100644 --- a/rswag-specs/lib/rswag/specs/request_factory.rb +++ b/rswag-specs/lib/rswag/specs/request_factory.rb @@ -186,8 +186,22 @@ module Rswag # Rather that serializing with the appropriate encoding (e.g. multipart/form-data), # Rails test infrastructure allows us to send the values directly as a hash # PROS: simple to implement, CONS: serialization/deserialization is bypassed in test + smart_payload = build_smart_form_payload(parameters, example) + raw_payload = build_raw_form_payload(parameters, example) + + smart_payload.merge(raw_payload) + end + + def build_smart_form_payload(parameters, example) + smart_tuples = parameters + .select { |p| p[:in] == :formData && p[:schema] } + .map { |p| example.send(p[:name]) } + .reduce({}, :merge) + end + + def build_raw_form_payload(parameters, example) tuples = parameters - .select { |p| p[:in] == :formData } + .select { |p| p[:in] == :formData && !p[:schema] } .map { |p| [p[:name], example.send(p[:name])] } Hash[tuples] end diff --git a/rswag-specs/spec/rswag/specs/request_factory_spec.rb b/rswag-specs/spec/rswag/specs/request_factory_spec.rb index aff5fb4..0269bd8 100644 --- a/rswag-specs/spec/rswag/specs/request_factory_spec.rb +++ b/rswag-specs/spec/rswag/specs/request_factory_spec.rb @@ -178,6 +178,20 @@ module Rswag ) end end + + context 'smart form payload' do + before do + metadata[:operation][:consumes] = ['multipart/form-data'] + metadata[:operation][:parameters] = [{ name: 'comment', in: :formData, schema: { type: 'object' } }] + allow(example).to receive(:comment).and_return(text: 'Some comment') + end + + it 'sets payload to hash of names and example values' do + expect(request[:payload]).to eq( + :text => 'Some comment' + ) + end + end end context 'produces content' do diff --git a/test-app/app/controllers/blogs_controller.rb b/test-app/app/controllers/blogs_controller.rb index 3bf6e5e..80216ab 100644 --- a/test-app/app/controllers/blogs_controller.rb +++ b/test-app/app/controllers/blogs_controller.rb @@ -8,6 +8,12 @@ class BlogsController < ApplicationController respond_with @blog end + # POST /blogs/multipart + def multipart_create + @blog = Blog.create(params.require(:blog).permit(:title, :content)) + respond_with @blog + end + # POST /blogs/flexible def flexible_create diff --git a/test-app/config/routes.rb b/test-app/config/routes.rb index 3107e95..d72365a 100644 --- a/test-app/config/routes.rb +++ b/test-app/config/routes.rb @@ -1,5 +1,6 @@ TestApp::Application.routes.draw do + post '/blogs/multipart', to: 'blogs#multipart_create' post '/blogs/flexible', to: 'blogs#flexible_create' post '/blogs/alternate', to: 'blogs#alternate_create' resources :blogs diff --git a/test-app/spec/features/swagger_ui_spec.rb b/test-app/spec/features/swagger_ui_spec.rb index 7c86110..e76f088 100644 --- a/test-app/spec/features/swagger_ui_spec.rb +++ b/test-app/spec/features/swagger_ui_spec.rb @@ -8,6 +8,7 @@ RSpec.feature 'swagger-ui', js: true do expect(page).to have_content('GET /blogs Searches blogs', normalize_ws: true) expect(page).to have_content('POST /blogs Creates a blog', normalize_ws: true) + expect(page).to have_content('POST /blogs/multipart Creates a blog using multipart', normalize_ws: true) expect(page).to have_content('GET /blogs/{id} Retrieves a blog', normalize_ws: true) end end diff --git a/test-app/spec/integration/blogs_spec.rb b/test-app/spec/integration/blogs_spec.rb index bfe67b6..f0adb6e 100644 --- a/test-app/spec/integration/blogs_spec.rb +++ b/test-app/spec/integration/blogs_spec.rb @@ -53,6 +53,33 @@ RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do end end + path '/blogs/multipart' do + post 'Creates a blog using multipart' do + tags 'Blogs' + description 'Creates a new blog from provided data' + operationId 'createBlogWithMultipart' + consumes 'multipart/form-data' + produces 'application/json' + parameter name: :blog, in: :formData, schema: { type: :object, properties: { name: :blog, type: :object, properties: { '$ref' => '#/definitions/blog' } } } + + let(:blog) { { blog: { title: 'foo', content: 'bar' } } } + + response '201', 'blog created' do + # schema '$ref' => '#/definitions/blog' + run_test! + end + + response '422', 'invalid request' do + schema '$ref' => '#/definitions/errors_object' + + let(:blog) { { blog: { title: 'foo' } } } + run_test! do |response| + expect(response.body).to include("can't be blank") + end + end + end + end + path '/blogs/flexible' do post 'Creates a blog flexible body' do tags 'Blogs' diff --git a/test-app/swagger/v1/swagger.json b/test-app/swagger/v1/swagger.json index 866f240..9759fc2 100644 --- a/test-app/swagger/v1/swagger.json +++ b/test-app/swagger/v1/swagger.json @@ -139,6 +139,52 @@ } } }, + "/blogs/multipart": { + "post": { + "summary": "Creates a blog using multipart", + "tags": [ + "Blogs" + ], + "description": "Creates a new blog from provided data", + "operationId": "createBlogWithMultipart", + "parameters": [ + + ], + "responses": { + "201": { + "description": "blog created", + "content": { + } + }, + "422": { + "description": "invalid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/errors_object" + } + } + } + } + }, + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "name": "blog", + "type": "object", + "properties": { + "$ref": "#/definitions/blog" + } + } + } + } + } + } + } + }, "/blogs/flexible": { "post": { "summary": "Creates a blog flexible body",