add formData support

This commit is contained in:
ali.q
2017-04-15 01:34:05 +04:30
committed by ali
parent 25d8adaf8b
commit 182ee093f4
12 changed files with 154 additions and 18 deletions

View File

@@ -27,6 +27,7 @@ module Rswag
end end
def build_body(example) def build_body(example)
return build_form_data(example) if parameters_in(:formData).present?
body_parameter = parameters_in(:body).first body_parameter = parameters_in(:body).first
body_parameter.nil? ? '' : example.send(body_parameter[:name]).to_json body_parameter.nil? ? '' : example.send(body_parameter[:name]).to_json
end end
@@ -93,6 +94,10 @@ module Rswag
"#{name}=#{value.join(',')}" # csv is default "#{name}=#{value.join(',')}" # csv is default
end end
end end
def build_form_data(example)
Hash[parameters_in(:formData).map { |p| [p[:name][/^\w+[^\[]/, 0], example.send(p[:name][/^\w+[^\[]/, 0])] }]
end
end end
end end
end end

View File

@@ -158,6 +158,17 @@ module Rswag
end end
end end
context "'formData' parameter" do
before do
api_metadata[:operation][:parameters] << { name: 'comment', in: :formData, type: 'string' }
allow(example).to receive(:comment).and_return('Some comment')
end
it 'returns the example value as a hash' do
expect(body).to eq({"comment" => "Some comment"})
end
end
context "referenced 'body' parameter" do context "referenced 'body' parameter" do
before do before do
api_metadata[:operation][:parameters] << { '$ref' => '#/parameters/comment' } api_metadata[:operation][:parameters] << { '$ref' => '#/parameters/comment' }
@@ -171,6 +182,20 @@ module Rswag
expect(body).to eq("{\"text\":\"Some comment\"}") expect(body).to eq("{\"text\":\"Some comment\"}")
end end
end end
context "referenced 'formData' parameter" do
before do
api_metadata[:operation][:parameters] << { '$ref' => '#/parameters/comment' }
global_metadata[:parameters] = {
'comment' => { name: 'comment', in: :formData, type: 'string' }
}
allow(example).to receive(:comment).and_return('Some comment')
end
it 'returns the example value as a json string' do
expect(body).to eq({"comment" => "Some comment"})
end
end
end end
describe '#build_headers' do describe '#build_headers' do

View File

@@ -4,10 +4,19 @@ class BlogsController < ApplicationController
# POST /blogs # POST /blogs
def create def create
@blog = Blog.create(params.require(:blog).permit(:title, :content)) thumbnail = save_uploaded_file(params[:blog][:thumbnail])
@blog = Blog.create(params.require(:blog).permit(:title, :content).merge(:thumbnail => thumbnail))
respond_with @blog respond_with @blog
end end
# Put /blogs/1
def upload
@blog = Blog.find_by_id(params[:id])
return head :not_found if @blog.nil?
@blog.thumbnail = save_uploaded_file params[:file]
head @blog.save ? :ok : :unprocsessible_entity
end
# GET /blogs # GET /blogs
def index def index
@blogs = Blog.all @blogs = Blog.all
@@ -24,4 +33,14 @@ class BlogsController < ApplicationController
respond_with @blog, status: :not_found and return unless @blog respond_with @blog, status: :not_found and return unless @blog
respond_with @blog respond_with @blog
end end
private
def save_uploaded_file(field)
return if field.nil?
require 'fileutils'
file = File.join('tmp', field.original_filename)
FileUtils.cp field.tempfile.path, file
field.original_filename
end
end end

View File

@@ -5,7 +5,8 @@ class Blog < ActiveRecord::Base
{ {
id: id, id: id,
title: title, title: title,
content: content content: content,
thumbnail: thumbnail
} }
end end
end end

View File

@@ -1,5 +1,6 @@
TestApp::Application.routes.draw do TestApp::Application.routes.draw do
resources :blogs, defaults: { :format => :json } resources :blogs, defaults: { :format => :json }
put '/blogs/:id/upload', to: 'blogs#upload'
mount Rswag::Api::Engine => 'api-docs' mount Rswag::Api::Engine => 'api-docs'
mount Rswag::Ui::Engine => 'api-docs' mount Rswag::Ui::Engine => 'api-docs'

View File

@@ -3,6 +3,7 @@ class CreateBlogs < ActiveRecord::Migration
create_table :blogs do |t| create_table :blogs do |t|
t.string :title t.string :title
t.text :content t.text :content
t.string :thumbnail
t.timestamps t.timestamps
end end

View File

@@ -16,6 +16,7 @@ ActiveRecord::Schema.define(:version => 20160218212104) do
create_table "blogs", :force => true do |t| create_table "blogs", :force => true do |t|
t.string "title" t.string "title"
t.text "content" t.text "content"
t.string "thumbnail"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end

BIN
test-app/spec/fixtures/thumbnail.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -8,11 +8,13 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
tags 'Blogs' tags 'Blogs'
description 'Creates a new blog from provided data' description 'Creates a new blog from provided data'
operationId 'createBlog' operationId 'createBlog'
consumes 'application/json' consumes 'application/x-www-form-urlencoded'
parameter name: :blog, :in => :body, schema: { '$ref' => '#/definitions/blog' } parameter name: 'blog[title]', :in => :formData, type: 'string'
parameter name: 'blog[content]', :in => :formData, type: 'string'
parameter name: 'blog[thumbnail]', :in => :formData, type: 'file'
response '201', 'blog created' do response '201', 'blog created' do
let(:blog) { { title: 'foo', content: 'bar' } } let(:blog) { { title: 'foo', content: 'bar', thumbnail: Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) } }
run_test! run_test!
end end
@@ -34,7 +36,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
response '200', 'success' do response '200', 'success' do
schema type: 'array', items: { '$ref' => '#/definitions/blog' } schema type: 'array', items: { '$ref' => '#/definitions/blog' }
let(:keywords) { 'foo bar' } let(:keywords) { 'foo+bar' }
run_test! run_test!
end end
end end
@@ -59,10 +61,11 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
examples 'application/json' => { examples 'application/json' => {
id: 1, id: 1,
title: 'Hello world!', title: 'Hello world!',
content: 'Hello world and hello universe. Thank you all very much!!!' content: 'Hello world and hello universe. Thank you all very much!!!',
thumbnail: "thumbnail.png"
} }
let(:blog) { Blog.create(title: 'foo', content: 'bar') } let(:blog) { Blog.create(title: 'foo', content: 'bar', thumbnail: 'thumbnail.png') }
let(:id) { blog.id } let(:id) { blog.id }
run_test! run_test!
end end
@@ -73,4 +76,29 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
end end
end end
end end
path '/blogs/{id}/upload' do
parameter name: :id, :in => :path, :type => :string
put 'upload a blog thumbnail' do
tags 'Blogs'
description 'Upload a thumbnail for specific blog by id'
operationId 'uploadThumbnailBlog'
consumes 'application/x-www-form-urlencoded'
parameter name: :file, :in => :formData, :type => 'file', required: true
response '200', 'blog updated' do
let(:blog) { Blog.create(title: 'foo', content: 'bar') }
let(:id) { blog.id }
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) }
run_test!
end
response '404', 'blog not found' do
let(:id) { 'invalid' }
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) }
run_test!
end
end
end
end end

View File

@@ -47,6 +47,10 @@ RSpec.configure do |config|
# triggering implicit auto-inclusion in groups with matching metadata. # triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups config.shared_context_metadata_behavior = :apply_to_host_groups
config.after(:suite) do
File.delete("#{Rails.root}/tmp/thumbnail.png") if File.file?("#{Rails.root}/tmp/thumbnail.png")
end
# The settings below are suggested to provide a good initial experience # The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content. # with RSpec, but feel free to customize to your heart's content.
=begin =begin

View File

@@ -39,9 +39,10 @@ RSpec.configure do |config|
properties: { properties: {
id: { type: 'integer' }, id: { type: 'integer' },
title: { type: 'string' }, title: { type: 'string' },
content: { type: 'string' } content: { type: 'string' },
thumbnail: { type: 'string'}
}, },
required: [ 'id', 'title', 'content' ] required: [ 'id', 'title', 'content', 'thumbnail' ]
} }
}, },
securityDefinitions: { securityDefinitions: {

View File

@@ -14,15 +14,23 @@
"description": "Creates a new blog from provided data", "description": "Creates a new blog from provided data",
"operationId": "createBlog", "operationId": "createBlog",
"consumes": [ "consumes": [
"application/json" "application/x-www-form-urlencoded"
], ],
"parameters": [ "parameters": [
{ {
"name": "blog", "name": "blog[title]",
"in": "body", "in": "formData",
"schema": { "type": "string"
"$ref": "#/definitions/blog" },
} {
"name": "blog[content]",
"in": "formData",
"type": "string"
},
{
"name": "blog[thumbnail]",
"in": "formData",
"type": "file"
} }
], ],
"responses": { "responses": {
@@ -107,7 +115,8 @@
"application/json": { "application/json": {
"id": 1, "id": 1,
"title": "Hello world!", "title": "Hello world!",
"content": "Hello world and hello universe. Thank you all very much!!!" "content": "Hello world and hello universe. Thank you all very much!!!",
"thumbnail": "thumbnail.png"
} }
} }
}, },
@@ -116,6 +125,43 @@
} }
} }
} }
},
"/blogs/{id}/upload": {
"parameters": [
{
"name": "id",
"in": "path",
"type": "string",
"required": true
}
],
"put": {
"summary": "upload a blog thumbnail",
"tags": [
"Blogs"
],
"description": "Upload a thumbnail for specific blog by id",
"operationId": "uploadThumbnailBlog",
"consumes": [
"application/x-www-form-urlencoded"
],
"parameters": [
{
"name": "file",
"in": "formData",
"type": "file",
"required": true
}
],
"responses": {
"200": {
"description": "blog updated"
},
"404": {
"description": "blog not found"
}
}
}
} }
}, },
"definitions": { "definitions": {
@@ -147,12 +193,16 @@
}, },
"content": { "content": {
"type": "string" "type": "string"
},
"thumbnail": {
"type": "string"
} }
}, },
"required": [ "required": [
"id", "id",
"title", "title",
"content" "content",
"thumbnail"
] ]
} }
}, },