Skip to content

Commit 1c498b7

Browse files
committed
Pick serializer based on the request content type.
1 parent 9db690a commit 1c498b7

File tree

3 files changed

+124
-20
lines changed

3 files changed

+124
-20
lines changed

lib/reynard/context.rb

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,7 @@ def params(params)
3131
end
3232

3333
def body(data)
34-
return unless @request_context.operation
35-
36-
serialized_body = @specification.build_body(@request_context.operation.node, data)
37-
return unless serialized_body
38-
39-
copy(
40-
request: {
41-
headers: @request_context.headers.merge(serialized_body.headers),
42-
body: serialized_body.to_s
43-
}
44-
)
34+
copy(request: { body: self.class.merge_body(@request_context.body, data) })
4535
end
4636

4737
def headers(headers)
@@ -55,14 +45,41 @@ def logger(logger)
5545
)
5646
end
5747

48+
def serializer(content_type, serializer)
49+
copy(
50+
request: {
51+
serializers: @request_context.serializers.merge({ content_type => serializer }).compact
52+
}
53+
)
54+
end
55+
5856
def execute
5957
build_response(build_request.perform)
6058
end
6159

60+
def self.merge_body(current, data)
61+
case current
62+
when NilClass
63+
data
64+
when Hash
65+
current.merge(data)
66+
else
67+
raise(
68+
ArgumentError,
69+
"Please assign the request body once, we can't merge #{data.inspect} into " \
70+
"#{current.inspect}."
71+
)
72+
end
73+
end
74+
6275
private
6376

6477
def build_request_context
65-
RequestContext.new(base_url: @specification.default_base_url, headers: {})
78+
RequestContext.new(
79+
base_url: @specification.default_base_url,
80+
headers: {},
81+
serializers: Reynard.serializers.dup
82+
)
6683
end
6784

6885
def build_response_context
@@ -79,7 +96,16 @@ def copy(request: {}, response: {})
7996
end
8097

8198
def build_request
82-
Reynard::Http::Request.new(request_context: @request_context)
99+
Reynard::Http::Request.new(
100+
request_context: @request_context,
101+
serializer_selection: serializer_selection
102+
)
103+
end
104+
105+
def serializer_selection
106+
@specification
107+
.content(@request_context.operation.node)
108+
.pick_serializer(@request_context.serializers)
83109
end
84110

85111
def build_response(http_response)

lib/reynard/http/request.rb

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ class Http
99
class Request
1010
attr_reader :uri
1111

12-
def initialize(request_context:)
12+
def initialize(request_context:, serializer_selection:)
1313
@request_context = request_context
14+
@serializer_selection = serializer_selection
1415
@uri = URI(@request_context.url)
1516
end
1617

@@ -26,17 +27,44 @@ def request_class
2627
end
2728

2829
def request_headers
29-
{ 'User-Agent' => Reynard.user_agent }.merge(@request_context.headers || {})
30+
{
31+
'User-Agent' => Reynard.user_agent
32+
}
33+
.merge(serializer&.headers || {})
34+
.merge(@request_context.headers || {})
3035
end
3136

3237
def build_request
3338
request = request_class.new(uri, request_headers)
34-
if @request_context.body
35-
@request_context.logger&.debug { @request_context.body }
36-
request.body = @request_context.body
39+
if serializer
40+
write_serializer_body(request)
41+
elsif @request_context.body
42+
write_serializer_params(request)
3743
end
3844
request
3945
end
46+
47+
def write_serializer_body(request)
48+
@request_context.logger&.debug { @request_context.body }
49+
request.body = serializer.body
50+
end
51+
52+
def write_serializer_params(request)
53+
@request_context.logger&.debug { @request_context.body }
54+
request.body = @request_context.body
55+
end
56+
57+
def serializer
58+
return @serializer if defined?(@serializer)
59+
60+
@serializer = build_serializer
61+
end
62+
63+
def build_serializer
64+
return nil unless @serializer_selection&.serializer_class
65+
66+
@serializer_selection.serializer_class.new(data: @request_context.body)
67+
end
4068
end
4169
end
4270
end

test/reynard/context_test.rb

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ class Reynard
66
class ContextTest < Reynard::Test
77
def setup
88
@specification = Specification.new(filename: fixture_file('openapi/simple.yml'))
9-
@request_context = RequestContext.new(base_url: @specification.default_base_url, headers: {})
9+
@request_context = RequestContext.new(
10+
base_url: @specification.default_base_url,
11+
headers: {},
12+
serializers: Reynard.serializers.dup
13+
)
1014
@inflector = Inflector.new
1115
@context = Context.new(
1216
specification: @specification, inflector: @inflector, request_context: @request_context
@@ -199,12 +203,58 @@ def setup
199203
assert_equal '200', response.code
200204
assert_equal [1, 2, 3], response.object.map(&:id)
201205
end
206+
207+
test 'uses the first available serializer' do
208+
@request_body = nil
209+
stub_request(:post, 'http://example.com/v1/books').with do |request|
210+
@request_body = request.body
211+
end
212+
data = { 'name' => 'Parcival' }
213+
@context
214+
.operation('createBook')
215+
.body(data)
216+
.execute
217+
assert_equal('{"name":"Parcival"}', @request_body)
218+
end
219+
220+
test 'allows users to post as multipart form data' do
221+
@request_body = nil
222+
stub_request(:post, 'http://example.com/v1/books').with do |request|
223+
@request_body = request.body
224+
end
225+
data = { 'name' => 'Parcival' }
226+
@context
227+
.serializer('application/json', nil)
228+
.operation('createBook')
229+
.body(data)
230+
.execute
231+
assert_includes(@request_body, 'Content-Disposition: form-data')
232+
end
233+
234+
test 'allows users to post as plain text' do
235+
@request_body = nil
236+
stub_request(:post, 'http://example.com/v1/books').with do |request|
237+
@request_body = request.body
238+
end
239+
data = 'Name: Parcival'
240+
@context
241+
.serializer('application/json', nil)
242+
.serializer('multipart/form-data', nil)
243+
.operation('createBook')
244+
.body(data)
245+
.execute
246+
assert_equal(data, @request_body)
247+
end
202248
end
203249

204250
class BareContextTest < Reynard::Test
205251
def setup
206252
@specification = Specification.new(filename: fixture_file('openapi/bare.yml'))
207-
@request_context = RequestContext.new(base_url: @specification.default_base_url, headers: {})
253+
@request_context = RequestContext.new(
254+
base_url: @specification.default_base_url,
255+
headers: {},
256+
serializers: {}
257+
)
208258
@inflector = Inflector.new
209259
@context = Context.new(
210260
specification: @specification, inflector: @inflector, request_context: @request_context

0 commit comments

Comments
 (0)