Skip to content

Commit

Permalink
Merge pull request #1017 from trainline-eu/add-part-params-class-to-a…
Browse files Browse the repository at this point in the history
…llow-multipart-posts-with-json-content-and-file-upload

Create PartParams class to allow multipart posts with JSON content and file upload at the same time
  • Loading branch information
technoweenie authored Sep 17, 2019
2 parents f4ae96e + 7dded89 commit 1418454
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/faraday.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def self.default_connection_options=(options)

require_libs 'utils', 'options', 'connection', 'rack_builder', 'parameters',
'middleware', 'adapter', 'request', 'response', 'upload_io',
'error'
'error', 'param_part'

require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
end
25 changes: 25 additions & 0 deletions lib/faraday/param_part.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

module Faraday
# Allows multipart posts while specifying headers of a part
class ParamPart
def initialize(value, content_type, content_id = nil)
@value = value
@content_type = content_type
@content_id = content_id
end

def to_part(boundary, key)
Faraday::Parts::Part.new(boundary, key, value, headers)
end

def headers
{
'Content-Type' => content_type,
'Content-ID' => content_id
}
end

attr_reader :value, :content_type, :content_id
end
end
10 changes: 9 additions & 1 deletion lib/faraday/request/multipart.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def has_multipart?(obj) # rubocop:disable Naming/PredicateName
def create_multipart(env, params)
boundary = env.request.boundary
parts = process_params(params) do |key, value|
Faraday::Parts::Part.new(boundary, key, value)
part(boundary, key, value)
end
parts << Faraday::Parts::EpiloguePart.new(boundary)

Expand All @@ -61,6 +61,14 @@ def create_multipart(env, params)
body
end

def part(boundary, key, value)
if value.respond_to?(:to_part)
value.to_part(boundary, key)
else
Faraday::Parts::Part.new(boundary, key, value)
end
end

# @return [String]
def unique_boundary
"#{DEFAULT_BOUNDARY_PREFIX}-#{SecureRandom.hex}"
Expand Down
39 changes: 39 additions & 0 deletions spec/faraday/request/multipart_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,45 @@
end
end

context 'when providing json and IO content in the same payload' do
let(:io) { StringIO.new('io-content') }
let(:json) do
{
b: 1,
c: 2
}.to_json
end

let(:payload) do
{
json: Faraday::ParamPart.new(json, 'application/json'),
io: Faraday::UploadIO.new(io, 'application/pdf')
}
end

it_behaves_like 'a multipart request'

it 'forms a multipart request' do
response = conn.post('/echo', payload)

boundary = parse_multipart_boundary(response.headers['Content-Type'])
result = parse_multipart(boundary, response.body)
expect(result[:errors]).to be_empty

part_json, body_json = result.part('json')
expect(part_json).to_not be_nil
expect(part_json.mime).to eq('application/json')
expect(part_json.filename).to be_nil
expect(body_json).to eq(json)

part_io, body_io = result.part('io')
expect(part_io).to_not be_nil
expect(part_io.mime).to eq('application/pdf')
expect(part_io.filename).to eq('local.path')
expect(body_io).to eq(io.string)
end
end

context 'when multipart objects in array param' do
let(:payload) do
{
Expand Down

0 comments on commit 1418454

Please sign in to comment.