braintreehttp 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/braintreehttp.gemspec +3 -1
- data/lib/braintreehttp/encoder.rb +55 -9
- data/lib/braintreehttp/http_client.rb +6 -56
- data/lib/braintreehttp/version.rb +1 -0
- data/spec/braintreehttp/encoder_spec.rb +26 -0
- data/spec/braintreehttp/http_client_spec.rb +7 -74
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 599bde79b1bc33cd0f46c65d37136855ed422343
|
4
|
+
data.tar.gz: 06d2cf5cc5eeda5fad37ba71b07c97f91068331a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81fe0a125d8b991d02e9f3d77171e4b4c18745df78e3f0a6e6c30d01c8edc873952736a047063d45925af236672f2cdc74f2887e175b98c9ebab5d62a7cf87bd
|
7
|
+
data.tar.gz: 9375dd0800ea34885828d90b3c3987cd8571ff312d28af415be69840671210fcd4edd63a06de5cc9758783472cfca22630b29c47b778a60f1d478b49ed93adec
|
data/braintreehttp.gemspec
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
require_relative './lib/braintreehttp/version'
|
2
|
+
|
1
3
|
$:.push File.expand_path("../lib", __FILE__)
|
2
4
|
|
3
5
|
Gem::Specification.new do |s|
|
4
6
|
s.name = "braintreehttp"
|
5
7
|
s.summary = "BraintreeHttp Client Library"
|
6
8
|
s.description = "Used for generated API clients"
|
7
|
-
s.version =
|
9
|
+
s.version = VERSION
|
8
10
|
s.license = "MIT"
|
9
11
|
s.author = "Braintree"
|
10
12
|
s.email = "code@getbraintree.com"
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
3
|
module BraintreeHttp
|
4
|
-
|
5
4
|
class Encoder
|
6
5
|
def initialize
|
7
|
-
@encoders = [Json.new, Text.new]
|
6
|
+
@encoders = [Json.new, Text.new, Multipart.new]
|
8
7
|
end
|
9
8
|
|
10
9
|
def serialize_request(req)
|
@@ -16,7 +15,7 @@ module BraintreeHttp
|
|
16
15
|
enc = _encoder(content_type)
|
17
16
|
raise UnsupportedEncodingError.new("Unable to serialize request with Content-Type #{content_type}. Supported encodings are #{supported_encodings}") unless enc
|
18
17
|
|
19
|
-
enc.encode(req
|
18
|
+
enc.encode(req)
|
20
19
|
end
|
21
20
|
|
22
21
|
def deserialize_response(resp, headers)
|
@@ -43,9 +42,8 @@ module BraintreeHttp
|
|
43
42
|
end
|
44
43
|
|
45
44
|
class Json
|
46
|
-
|
47
|
-
|
48
|
-
JSON.generate(body)
|
45
|
+
def encode(request)
|
46
|
+
JSON.generate(request.body)
|
49
47
|
end
|
50
48
|
|
51
49
|
def decode(body)
|
@@ -58,9 +56,8 @@ module BraintreeHttp
|
|
58
56
|
end
|
59
57
|
|
60
58
|
class Text
|
61
|
-
|
62
|
-
|
63
|
-
body.to_s
|
59
|
+
def encode(request)
|
60
|
+
request.body.to_s
|
64
61
|
end
|
65
62
|
|
66
63
|
def decode(body)
|
@@ -71,4 +68,53 @@ module BraintreeHttp
|
|
71
68
|
/^text\/.*/
|
72
69
|
end
|
73
70
|
end
|
71
|
+
|
72
|
+
class Multipart
|
73
|
+
def encode(request)
|
74
|
+
boundary = DateTime.now.strftime("%Q")
|
75
|
+
request.headers["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
|
76
|
+
|
77
|
+
form_params = []
|
78
|
+
request.body.each do |k, v|
|
79
|
+
if v.is_a? File
|
80
|
+
form_params.push(_add_file_part(k, v))
|
81
|
+
else
|
82
|
+
form_params.push(_add_form_field(k, v))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
form_params.collect {|p| "--" + boundary + "#{LINE_FEED}" + p}.join("") + "--" + boundary + "--"
|
87
|
+
end
|
88
|
+
|
89
|
+
def decode(body)
|
90
|
+
raise UnsupportedEncodingError.new("Multipart does not support deserialization")
|
91
|
+
end
|
92
|
+
|
93
|
+
def content_type
|
94
|
+
/^multipart\/.*/
|
95
|
+
end
|
96
|
+
|
97
|
+
def _add_form_field(key, value)
|
98
|
+
return "Content-Disposition: form-data; name=\"#{key}\"#{LINE_FEED}#{LINE_FEED}#{value}#{LINE_FEED}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def _add_file_part(key, file)
|
102
|
+
mime_type = _mime_type_for_file_name(file.path)
|
103
|
+
return "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{File.basename(file.path)}\"#{LINE_FEED}" +
|
104
|
+
"Content-Type: #{mime_type}#{LINE_FEED}#{LINE_FEED}#{file.read}#{LINE_FEED}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def _mime_type_for_file_name(filename)
|
108
|
+
file_extension = File.extname(filename).strip.downcase[1..-1]
|
109
|
+
if file_extension == "jpeg" || file_extension == "jpg"
|
110
|
+
return "image/jpeg"
|
111
|
+
elsif file_extension == "png"
|
112
|
+
return "image/png"
|
113
|
+
elsif file_extension == "pdf"
|
114
|
+
return "application/pdf"
|
115
|
+
else
|
116
|
+
return "application/octet-stream"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
74
120
|
end
|
@@ -7,7 +7,7 @@ module BraintreeHttp
|
|
7
7
|
LINE_FEED = "\r\n"
|
8
8
|
|
9
9
|
class HttpClient
|
10
|
-
attr_accessor :environment
|
10
|
+
attr_accessor :environment, :encoder
|
11
11
|
|
12
12
|
def initialize(environment)
|
13
13
|
@environment = environment
|
@@ -40,65 +40,15 @@ module BraintreeHttp
|
|
40
40
|
request.headers["User-Agent"] = user_agent
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
http_request = Net::HTTPGenericRequest.new(request.verb, true, true, request.path, request.headers)
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
boundary = DateTime.now.strftime("%Q")
|
48
|
-
httpRequest.set_content_type("multipart/form-data; boundary=#{boundary}")
|
49
|
-
|
50
|
-
form_params = []
|
51
|
-
request.body.each do |k, v|
|
52
|
-
if v.is_a? File
|
53
|
-
form_params.push(_add_file_part(k, v))
|
54
|
-
else
|
55
|
-
form_params.push(_add_form_field(k, v))
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
httpRequest.body = form_params.collect {|p| "--" + boundary + "#{LINE_FEED}" + p}.join("") + "--" + boundary + "--"
|
60
|
-
elsif has_body(request)
|
61
|
-
if request.body.is_a? String
|
62
|
-
httpRequest.body = request.body
|
63
|
-
else
|
64
|
-
httpRequest.body = serialize_request(request)
|
65
|
-
end
|
45
|
+
if has_body(request)
|
46
|
+
http_request.body = @encoder.serialize_request(request)
|
66
47
|
end
|
67
48
|
|
68
49
|
uri = URI(@environment.base_url)
|
69
50
|
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
70
|
-
_parse_response(http.request(
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def serialize_request(request)
|
75
|
-
@encoder.serialize_request(request)
|
76
|
-
end
|
77
|
-
|
78
|
-
def deserialize_response(response_body, headers)
|
79
|
-
@encoder.deserialize_response(response_body, headers)
|
80
|
-
end
|
81
|
-
|
82
|
-
def _add_form_field(key, value)
|
83
|
-
return "Content-Disposition: form-data; name=\"#{key}\"#{LINE_FEED}#{LINE_FEED}#{value}#{LINE_FEED}"
|
84
|
-
end
|
85
|
-
|
86
|
-
def _add_file_part(key, file)
|
87
|
-
mime_type = _mime_type_for_file_name(file.path)
|
88
|
-
return "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{File.basename(file.path)}\"#{LINE_FEED}" +
|
89
|
-
"Content-Type: #{mime_type}#{LINE_FEED}#{LINE_FEED}#{file.read}#{LINE_FEED}"
|
90
|
-
end
|
91
|
-
|
92
|
-
def _mime_type_for_file_name(filename)
|
93
|
-
file_extension = File.extname(filename).strip.downcase[1..-1]
|
94
|
-
if file_extension == "jpeg" || file_extension == "jpg"
|
95
|
-
return "image/jpeg"
|
96
|
-
elsif file_extension == "png"
|
97
|
-
return "image/png"
|
98
|
-
elsif file_extension == "pdf"
|
99
|
-
return "application/pdf"
|
100
|
-
else
|
101
|
-
return "application/octet-stream"
|
51
|
+
_parse_response(http.request(http_request))
|
102
52
|
end
|
103
53
|
end
|
104
54
|
|
@@ -108,7 +58,7 @@ module BraintreeHttp
|
|
108
58
|
result = response.body
|
109
59
|
headers = response.to_hash
|
110
60
|
if result && !result.empty?
|
111
|
-
deserialized = deserialize_response(response.body, headers)
|
61
|
+
deserialized = @encoder.deserialize_response(response.body, headers)
|
112
62
|
if deserialized.is_a?(String) || deserialized.is_a?(Array)
|
113
63
|
result = deserialized
|
114
64
|
else
|
@@ -0,0 +1 @@
|
|
1
|
+
VERSION = "0.2.0"
|
@@ -36,6 +36,32 @@ describe Encoder do
|
|
36
36
|
expect(Encoder.new.serialize_request(req)).to eq("some text")
|
37
37
|
end
|
38
38
|
|
39
|
+
it 'serializes the request when content-type == multipart/form-data' do
|
40
|
+
http_client = HttpClient.new(@environment)
|
41
|
+
file = File.new("README.md", "r")
|
42
|
+
req = OpenStruct.new({
|
43
|
+
:verb => "POST",
|
44
|
+
:path => "/v1/api",
|
45
|
+
:headers => {
|
46
|
+
"Content-Type" => "multipart/form-data"
|
47
|
+
},
|
48
|
+
:body => {
|
49
|
+
:key => "value",
|
50
|
+
:another_key => 1013,
|
51
|
+
:readme => file,
|
52
|
+
}
|
53
|
+
})
|
54
|
+
|
55
|
+
serialized = Encoder.new.serialize_request(req)
|
56
|
+
|
57
|
+
expect(serialized).to include("Content-Disposition: form-data; name=\"readme\"; filename=\"README.md\"")
|
58
|
+
expect(serialized).to include("Content-Disposition: form-data; name=\"readme\"; filename=\"README.md\"")
|
59
|
+
expect(serialized).to include("Content-Disposition: form-data; name=\"key\"")
|
60
|
+
expect(serialized).to include("value")
|
61
|
+
expect(serialized).to include("Content-Disposition: form-data; name=\"another_key\"")
|
62
|
+
expect(serialized).to include("1013")
|
63
|
+
end
|
64
|
+
|
39
65
|
it 'throws when content-type is not application/json' do
|
40
66
|
req = OpenStruct.new({
|
41
67
|
:headers => {
|
@@ -58,7 +58,13 @@ describe HttpClient do
|
|
58
58
|
|
59
59
|
stub_request(:delete, @environment.base_url + "/path")
|
60
60
|
|
61
|
-
req = OpenStruct.new({
|
61
|
+
req = OpenStruct.new({
|
62
|
+
:verb => "DELETE",
|
63
|
+
:path => "/path",
|
64
|
+
:headers => {
|
65
|
+
"Content-Type" => "text/plain"
|
66
|
+
}
|
67
|
+
})
|
62
68
|
|
63
69
|
req.body = "I want to delete the thing"
|
64
70
|
|
@@ -157,36 +163,6 @@ describe HttpClient do
|
|
157
163
|
expect(resp.result.key).to eq('value')
|
158
164
|
end
|
159
165
|
|
160
|
-
it "allows subclasses to modify response body" do
|
161
|
-
WebMock.enable!
|
162
|
-
|
163
|
-
return_data = {
|
164
|
-
:key => "value"
|
165
|
-
}
|
166
|
-
|
167
|
-
class JSONHttpClient < HttpClient
|
168
|
-
def deserialize_response(body, headers)
|
169
|
-
if headers["content-type"].include? "application/json"
|
170
|
-
return 'something else'
|
171
|
-
end
|
172
|
-
|
173
|
-
body
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
http_client = JSONHttpClient.new(@environment)
|
178
|
-
|
179
|
-
stub_request(:get, @environment.base_url + "/v1/api")
|
180
|
-
.to_return(body: JSON.generate(return_data), status: 200, headers: {"Content-Type" => "application/json"})
|
181
|
-
|
182
|
-
req = OpenStruct.new({:verb => "GET", :path => "/v1/api"})
|
183
|
-
|
184
|
-
resp = http_client.execute(req)
|
185
|
-
|
186
|
-
expect(resp.status_code).to eq(200)
|
187
|
-
expect(resp.result).to eq('something else')
|
188
|
-
end
|
189
|
-
|
190
166
|
it 'handles json array result' do
|
191
167
|
WebMock.enable!
|
192
168
|
|
@@ -204,49 +180,6 @@ describe HttpClient do
|
|
204
180
|
expect(resp.result).to eq(return_data)
|
205
181
|
end
|
206
182
|
|
207
|
-
it "encodes multipart/form-data when a file is present without body" do
|
208
|
-
WebMock.enable!
|
209
|
-
|
210
|
-
stub_request(:any, @environment.base_url + "/v1/api")
|
211
|
-
|
212
|
-
http_client = HttpClient.new(@environment)
|
213
|
-
file = File.new("README.md", "r")
|
214
|
-
req = OpenStruct.new({:verb => "POST", :path => "/v1/api", :headers => {"Content-Type" => "multipart/form-data"}})
|
215
|
-
req.body = {:readme => file}
|
216
|
-
|
217
|
-
resp = http_client.execute(req)
|
218
|
-
|
219
|
-
assert_requested(:post, @environment.base_url + "/v1/api") { |requested|
|
220
|
-
requested.body.include? "Content-Disposition: form-data; name=\"readme\"; filename=\"README.md\""
|
221
|
-
}
|
222
|
-
end
|
223
|
-
|
224
|
-
it "encodes multipart/form-data when a file is present with body" do
|
225
|
-
WebMock.enable!
|
226
|
-
|
227
|
-
stub_request(:any, @environment.base_url + "/v1/api")
|
228
|
-
|
229
|
-
http_client = HttpClient.new(@environment)
|
230
|
-
file = File.new("README.md", "r")
|
231
|
-
|
232
|
-
req = OpenStruct.new({:verb => "POST", :path => "/v1/api", :headers => {"Content-Type" => "multipart/form-data"}})
|
233
|
-
req.body = {
|
234
|
-
:key => "value",
|
235
|
-
:another_key => 1013,
|
236
|
-
:readme => file,
|
237
|
-
}
|
238
|
-
|
239
|
-
resp = http_client.execute(req)
|
240
|
-
|
241
|
-
assert_requested(:post, @environment.base_url + "/v1/api") { |requested|
|
242
|
-
requested.body.include? "Content-Disposition: form-data; name=\"readme\"; filename=\"README.md\""
|
243
|
-
requested.body.include? "Content-Disposition: form-data; name=\"key\""
|
244
|
-
requested.body.include? "value"
|
245
|
-
requested.body.include? "Content-Disposition: form-data; name=\"another_key\""
|
246
|
-
requested.body.include? "1013"
|
247
|
-
}
|
248
|
-
end
|
249
|
-
|
250
183
|
it "does not error if no file or body present on a request class" do
|
251
184
|
class Request
|
252
185
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: braintreehttp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Braintree
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Used for generated API clients
|
14
14
|
email: code@getbraintree.com
|
@@ -22,6 +22,7 @@ files:
|
|
22
22
|
- lib/braintreehttp/environment.rb
|
23
23
|
- lib/braintreehttp/errors.rb
|
24
24
|
- lib/braintreehttp/http_client.rb
|
25
|
+
- lib/braintreehttp/version.rb
|
25
26
|
- spec/braintreehttp/encoder_spec.rb
|
26
27
|
- spec/braintreehttp/http_client_spec.rb
|
27
28
|
- spec/spec_helper.rb
|
@@ -45,7 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
46
|
version: '0'
|
46
47
|
requirements: []
|
47
48
|
rubyforge_project: braintreehttp
|
48
|
-
rubygems_version: 2.
|
49
|
+
rubygems_version: 2.2.2
|
49
50
|
signing_key:
|
50
51
|
specification_version: 4
|
51
52
|
summary: BraintreeHttp Client Library
|