ruby_http_client 3.2.0 → 3.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +16 -0
- data/.env_sample +1 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +34 -13
- data/CHANGELOG.md +87 -0
- data/CODE_OF_CONDUCT.md +73 -0
- data/CONTRIBUTING.md +36 -50
- data/FIRST_TIMERS.md +79 -0
- data/Gemfile +3 -0
- data/ISSUE_TEMPLATE.md +30 -0
- data/{LICENSE.txt → LICENSE.md} +8 -8
- data/Makefile +7 -0
- data/PULL_REQUEST_TEMPLATE.md +31 -0
- data/README.md +54 -18
- data/Rakefile +25 -2
- data/TROUBLESHOOTING.md +17 -0
- data/USAGE.md +108 -0
- data/examples/example.rb +29 -1
- data/lib/ruby_http_client.rb +119 -22
- data/ruby_http_client.gemspec +16 -13
- data/static/img/github-fork.png +0 -0
- data/static/img/github-sign-up.png +0 -0
- data/test/test_helper.rb +7 -0
- data/test/test_ruby_http_client.rb +237 -11
- data/twilio_sendgrid_logo.png +0 -0
- data/use_cases/README.md +3 -0
- metadata +80 -9
- data/.github/ISSUE_TEMPLATE +0 -17
data/examples/example.rb
CHANGED
@@ -6,9 +6,28 @@ headers = JSON.parse('
|
|
6
6
|
"Authorization": "Bearer ' + ENV['SENDGRID_API_KEY'] + '"
|
7
7
|
}
|
8
8
|
')
|
9
|
-
host =
|
9
|
+
host = 'https://api.sendgrid.com'
|
10
10
|
client = SendGrid::Client.new(host: host, request_headers: headers)
|
11
11
|
|
12
|
+
# You can pass in an http_options hash to set values for NET::HTTP attributes
|
13
|
+
# https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html
|
14
|
+
# client = SendGrid::Client.new(host: host,
|
15
|
+
# request_headers: headers,
|
16
|
+
# http_options: {open_timeout: 15, read_timeout: 30})
|
17
|
+
|
18
|
+
# If you want to make request via proxy, you can set your proxy server in two ways.
|
19
|
+
#
|
20
|
+
# (1) Pass proxy_options hash
|
21
|
+
#
|
22
|
+
# client = SendGrid::Client.new(host: host,
|
23
|
+
# request_headers: headers,
|
24
|
+
# proxy_options: { host: '127.0.0.1', port: 8080 })
|
25
|
+
#
|
26
|
+
# (2) Set 'http_proxy' environment variable
|
27
|
+
#
|
28
|
+
# ENV['http_proxy'] = 'user:pass@127.0.0.1:8080'
|
29
|
+
# client = SendGrid::Client.new(host: host, request_headers: headers)
|
30
|
+
|
12
31
|
# GET Collection
|
13
32
|
query_params = { 'limit' => 100, 'offset' => 0 }
|
14
33
|
response = client.version('v3').api_keys.get(query_params: query_params)
|
@@ -69,3 +88,12 @@ puts response.headers
|
|
69
88
|
response = client.api_keys._(api_key_id).delete
|
70
89
|
puts response.status_code
|
71
90
|
puts response.headers
|
91
|
+
|
92
|
+
# Rate limit information
|
93
|
+
response = client.version('v3').api_keys._(api_key_id).get
|
94
|
+
puts response.ratelimit.limit
|
95
|
+
puts response.ratelimit.remaining
|
96
|
+
puts response.ratelimit.reset
|
97
|
+
puts response.ratelimit.exceeded?
|
98
|
+
# Sleep the current thread until the reset has happened
|
99
|
+
response.ratelimit.wait!
|
data/lib/ruby_http_client.rb
CHANGED
@@ -6,10 +6,54 @@ module SendGrid
|
|
6
6
|
|
7
7
|
# Holds the response from an API call.
|
8
8
|
class Response
|
9
|
+
# Provide useful functionality around API rate limiting.
|
10
|
+
class Ratelimit
|
11
|
+
attr_reader :limit, :remaining, :reset
|
12
|
+
|
13
|
+
# * *Args* :
|
14
|
+
# - +limit+ -> The total number of requests allowed within a rate limit window
|
15
|
+
# - +remaining+ -> The number of requests that have been processed within this current rate limit window
|
16
|
+
# - +reset+ -> The time (in seconds since Unix Epoch) when the rate limit will reset
|
17
|
+
def initialize(limit, remaining, reset)
|
18
|
+
@limit = limit.to_i
|
19
|
+
@remaining = remaining.to_i
|
20
|
+
@reset = Time.at reset.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def exceeded?
|
24
|
+
remaining <= 0
|
25
|
+
end
|
26
|
+
|
27
|
+
# * *Returns* :
|
28
|
+
# - The number of requests that have been used out of this
|
29
|
+
# rate limit window
|
30
|
+
def used
|
31
|
+
limit - remaining
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sleep until the reset time arrives. If given a block, it will
|
35
|
+
# be called after sleeping is finished.
|
36
|
+
#
|
37
|
+
# * *Returns* :
|
38
|
+
# - The amount of time (in seconds) that the rate limit slept
|
39
|
+
# for.
|
40
|
+
def wait!
|
41
|
+
now = Time.now.utc.to_i
|
42
|
+
duration = (reset.to_i - now) + 1
|
43
|
+
|
44
|
+
sleep duration if duration >= 0
|
45
|
+
|
46
|
+
yield if block_given?
|
47
|
+
|
48
|
+
duration
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
9
52
|
# * *Args* :
|
10
53
|
# - +response+ -> A NET::HTTP response object
|
11
54
|
#
|
12
55
|
attr_reader :status_code, :body, :headers
|
56
|
+
|
13
57
|
def initialize(response)
|
14
58
|
@status_code = response.code
|
15
59
|
@body = response.body
|
@@ -21,6 +65,20 @@ module SendGrid
|
|
21
65
|
def parsed_body
|
22
66
|
@parsed_body ||= JSON.parse(@body, symbolize_names: true)
|
23
67
|
end
|
68
|
+
|
69
|
+
def ratelimit
|
70
|
+
return @ratelimit unless @ratelimit.nil?
|
71
|
+
|
72
|
+
limit = headers['X-RateLimit-Limit']
|
73
|
+
remaining = headers['X-RateLimit-Remaining']
|
74
|
+
reset = headers['X-RateLimit-Reset']
|
75
|
+
|
76
|
+
# Guard against possibility that one (or probably, all) of the
|
77
|
+
# needed headers were not returned.
|
78
|
+
@ratelimit = Ratelimit.new(limit, remaining, reset) if limit && remaining && reset
|
79
|
+
|
80
|
+
@ratelimit
|
81
|
+
end
|
24
82
|
end
|
25
83
|
|
26
84
|
# A simple REST client.
|
@@ -35,15 +93,19 @@ module SendGrid
|
|
35
93
|
# Or just pass the version as part of the URL
|
36
94
|
# (e.g. client._("/v3"))
|
37
95
|
# - +url_path+ -> A list of the url path segments
|
96
|
+
# - +proxy_options+ -> A hash of proxy settings.
|
97
|
+
# (e.g. { host: '127.0.0.1', port: 8080 })
|
38
98
|
#
|
39
|
-
def initialize(host: nil, request_headers: nil, version: nil, url_path: nil)
|
99
|
+
def initialize(host: nil, request_headers: nil, version: nil, url_path: nil, http_options: {}, proxy_options: {}) # rubocop:disable Metrics/ParameterLists
|
40
100
|
@host = host
|
41
101
|
@request_headers = request_headers || {}
|
42
102
|
@version = version
|
43
103
|
@url_path = url_path || []
|
44
|
-
@methods = %w
|
104
|
+
@methods = %w[delete get patch post put]
|
45
105
|
@query_params = nil
|
46
106
|
@request_body = nil
|
107
|
+
@http_options = http_options
|
108
|
+
@proxy_options = proxy_options
|
47
109
|
end
|
48
110
|
|
49
111
|
# Update the headers for the request
|
@@ -90,7 +152,7 @@ module SendGrid
|
|
90
152
|
# - The url string with the query parameters appended
|
91
153
|
#
|
92
154
|
def build_query_params(url, query_params)
|
93
|
-
params =
|
155
|
+
params = URI.encode_www_form(query_params)
|
94
156
|
url.concat("?#{params}")
|
95
157
|
end
|
96
158
|
|
@@ -137,21 +199,10 @@ module SendGrid
|
|
137
199
|
#
|
138
200
|
def build_request(name, args)
|
139
201
|
build_args(args) if args
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
if (@request_body &&
|
145
|
-
(!@request_headers.has_key?('Content-Type') ||
|
146
|
-
@request_headers['Content-Type'] == 'application/json')
|
147
|
-
)
|
148
|
-
@request.body = @request_body.to_json
|
149
|
-
@request['Content-Type'] = 'application/json'
|
150
|
-
elsif !@request_body and (name.to_s == "post")
|
151
|
-
@request['Content-Type'] = ''
|
152
|
-
else
|
153
|
-
@request.body = @request_body
|
154
|
-
end
|
202
|
+
# build the request & http object
|
203
|
+
build_http_request(name)
|
204
|
+
# set the content type & request body
|
205
|
+
update_content_type(name)
|
155
206
|
make_request(@http, @request)
|
156
207
|
end
|
157
208
|
|
@@ -169,6 +220,16 @@ module SendGrid
|
|
169
220
|
Response.new(response)
|
170
221
|
end
|
171
222
|
|
223
|
+
# Build HTTP request object
|
224
|
+
#
|
225
|
+
# * *Returns* :
|
226
|
+
# - Request object
|
227
|
+
def build_http(host, port)
|
228
|
+
params = [host, port]
|
229
|
+
params += @proxy_options.values_at(:host, :port, :user, :pass) unless @proxy_options.empty?
|
230
|
+
add_ssl(Net::HTTP.new(*params))
|
231
|
+
end
|
232
|
+
|
172
233
|
# Allow for https calls
|
173
234
|
#
|
174
235
|
# * *Args* :
|
@@ -195,20 +256,22 @@ module SendGrid
|
|
195
256
|
# - Client object
|
196
257
|
#
|
197
258
|
def _(name = nil)
|
198
|
-
url_path = name ? @url_path
|
199
|
-
@url_path = []
|
259
|
+
url_path = name ? @url_path + [name] : @url_path
|
200
260
|
Client.new(host: @host, request_headers: @request_headers,
|
201
|
-
version: @version, url_path: url_path
|
261
|
+
version: @version, url_path: url_path,
|
262
|
+
http_options: @http_options)
|
202
263
|
end
|
203
264
|
|
204
265
|
# Dynamically add segments to the url, then call a method.
|
205
266
|
# (e.g. client.name.name.get())
|
206
267
|
#
|
207
268
|
# * *Args* :
|
208
|
-
# - The args are
|
269
|
+
# - The args are automatically passed in
|
209
270
|
# * *Returns* :
|
210
271
|
# - Client object or Response object
|
211
272
|
#
|
273
|
+
# rubocop:disable Style/MethodMissingSuper
|
274
|
+
# rubocop:disable Style/MissingRespondToMissing
|
212
275
|
def method_missing(name, *args, &_block)
|
213
276
|
# Capture the version
|
214
277
|
if name.to_s == 'version'
|
@@ -217,8 +280,42 @@ module SendGrid
|
|
217
280
|
end
|
218
281
|
# We have reached the end of the method chain, make the API call
|
219
282
|
return build_request(name, args) if @methods.include?(name.to_s)
|
283
|
+
|
220
284
|
# Add a segment to the URL
|
221
285
|
_(name)
|
222
286
|
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
def build_http_request(http_method)
|
291
|
+
uri = build_url(query_params: @query_params)
|
292
|
+
net_http = Kernel.const_get('Net::HTTP::' + http_method.to_s.capitalize)
|
293
|
+
|
294
|
+
@http = build_http(uri.host, uri.port)
|
295
|
+
@request = build_request_headers(net_http.new(uri.request_uri))
|
296
|
+
end
|
297
|
+
|
298
|
+
def update_content_type(http_method)
|
299
|
+
if @request_body && content_type_json?
|
300
|
+
# If body is a hash or array, encode it; else leave it alone
|
301
|
+
@request.body = if [Hash, Array].include?(@request_body.class)
|
302
|
+
@request_body.to_json
|
303
|
+
else
|
304
|
+
@request_body
|
305
|
+
end
|
306
|
+
@request['Content-Type'] = 'application/json'
|
307
|
+
elsif !@request_body && http_method.to_s == 'post'
|
308
|
+
@request['Content-Type'] = ''
|
309
|
+
else
|
310
|
+
@request.body = @request_body
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def content_type_json?
|
315
|
+
!@request_headers.key?('Content-Type') ||
|
316
|
+
@request_headers['Content-Type'] == 'application/json'
|
317
|
+
end
|
318
|
+
# rubocop:enable Style/MethodMissingSuper
|
319
|
+
# rubocop:enable Style/MissingRespondToMissing
|
223
320
|
end
|
224
321
|
end
|
data/ruby_http_client.gemspec
CHANGED
@@ -1,20 +1,23 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
|
5
4
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.version
|
8
|
-
spec.authors
|
9
|
-
spec.email
|
10
|
-
spec.summary
|
5
|
+
spec.name = 'ruby_http_client'
|
6
|
+
spec.version = '3.5.1'
|
7
|
+
spec.authors = ['Elmer Thomas']
|
8
|
+
spec.email = 'help@twilio.com'
|
9
|
+
spec.summary = 'A simple REST client'
|
11
10
|
spec.description = 'Quickly and easily access any REST or REST-like API.'
|
12
|
-
spec.homepage
|
13
|
-
spec.license
|
14
|
-
spec.files
|
15
|
-
spec.executables
|
16
|
-
spec.test_files
|
11
|
+
spec.homepage = 'http://github.com/sendgrid/ruby-http-client'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.files = `git ls-files -z`.split("\x0")
|
14
|
+
spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)/)
|
17
16
|
spec.require_paths = ['lib']
|
18
17
|
|
19
|
-
spec.add_development_dependency '
|
18
|
+
spec.add_development_dependency 'codecov'
|
19
|
+
spec.add_development_dependency 'minitest'
|
20
|
+
spec.add_development_dependency 'rake'
|
21
|
+
spec.add_development_dependency 'rubocop', '~> 0.88.0'
|
22
|
+
spec.add_development_dependency 'simplecov', '~> 0.18.5'
|
20
23
|
end
|
Binary file
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -1,3 +1,4 @@
|
|
1
|
+
require './test/test_helper'
|
1
2
|
require 'ruby_http_client'
|
2
3
|
require 'minitest/autorun'
|
3
4
|
|
@@ -11,6 +12,26 @@ class MockResponse
|
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
class MockHttpResponse
|
16
|
+
attr_reader :code, :body, :headers
|
17
|
+
|
18
|
+
def initialize(code, body, headers)
|
19
|
+
@code = code
|
20
|
+
@body = body
|
21
|
+
@headers = headers
|
22
|
+
end
|
23
|
+
|
24
|
+
alias to_hash headers
|
25
|
+
end
|
26
|
+
|
27
|
+
class MockResponseWithRequestBody < MockResponse
|
28
|
+
attr_reader :request_body
|
29
|
+
|
30
|
+
def initialize(response)
|
31
|
+
@request_body = response['request_body']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
14
35
|
class MockRequest < SendGrid::Client
|
15
36
|
def make_request(_http, _request)
|
16
37
|
response = {}
|
@@ -21,6 +42,17 @@ class MockRequest < SendGrid::Client
|
|
21
42
|
end
|
22
43
|
end
|
23
44
|
|
45
|
+
class MockRequestWithRequestBody < SendGrid::Client
|
46
|
+
def make_request(_http, request)
|
47
|
+
response = {}
|
48
|
+
response['code'] = 200
|
49
|
+
response['body'] = { 'message' => 'success' }
|
50
|
+
response['headers'] = { 'headers' => 'test' }
|
51
|
+
response['request_body'] = request.body
|
52
|
+
MockResponseWithRequestBody.new(response)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
24
56
|
class TestClient < Minitest::Test
|
25
57
|
def setup
|
26
58
|
@headers = JSON.parse('
|
@@ -31,9 +63,14 @@ class TestClient < Minitest::Test
|
|
31
63
|
')
|
32
64
|
@host = 'http://localhost:4010'
|
33
65
|
@version = 'v3'
|
66
|
+
@http_options = { open_timeout: 60, read_timeout: 60 }
|
34
67
|
@client = MockRequest.new(host: @host,
|
35
68
|
request_headers: @headers,
|
36
69
|
version: @version)
|
70
|
+
@client_with_options = MockRequest.new(host: @host,
|
71
|
+
request_headers: @headers,
|
72
|
+
version: @version,
|
73
|
+
http_options: @http_options)
|
37
74
|
end
|
38
75
|
|
39
76
|
def test_init
|
@@ -61,9 +98,9 @@ class TestClient < Minitest::Test
|
|
61
98
|
|
62
99
|
def test_build_query_params
|
63
100
|
url = ''
|
64
|
-
query_params = { 'limit' => 100, 'offset' => 0 }
|
101
|
+
query_params = { 'limit' => 100, 'offset' => 0, 'categories' => %w[category1 category2] }
|
65
102
|
url = @client.build_query_params(url, query_params)
|
66
|
-
assert_equal('?limit=100&offset=0', url)
|
103
|
+
assert_equal('?limit=100&offset=0&categories=category1&categories=category2', url)
|
67
104
|
end
|
68
105
|
|
69
106
|
def test_build_url
|
@@ -91,8 +128,8 @@ class TestClient < Minitest::Test
|
|
91
128
|
args = nil
|
92
129
|
response = @client.build_request(name, args)
|
93
130
|
assert_equal(200, response.status_code)
|
94
|
-
assert_equal({'message' => 'success'}, response.body)
|
95
|
-
assert_equal({'headers' => 'test'}, response.headers)
|
131
|
+
assert_equal({ 'message' => 'success' }, response.body)
|
132
|
+
assert_equal({ 'headers' => 'test' }, response.headers)
|
96
133
|
end
|
97
134
|
|
98
135
|
def test_build_request_post_empty_content_type
|
@@ -103,7 +140,7 @@ class TestClient < Minitest::Test
|
|
103
140
|
request_headers: headers,
|
104
141
|
version: 'v3'
|
105
142
|
)
|
106
|
-
args = [{'request_body' => {
|
143
|
+
args = [{ 'request_body' => { 'hogekey' => 'hogevalue' } }]
|
107
144
|
client.build_request('post', args)
|
108
145
|
assert_equal('application/json', client.request['Content-Type'])
|
109
146
|
assert_equal('{"hogekey":"hogevalue"}', client.request.body)
|
@@ -120,7 +157,7 @@ class TestClient < Minitest::Test
|
|
120
157
|
)
|
121
158
|
client.build_request('get', nil)
|
122
159
|
assert_equal('application/json', client.request['Content-Type'])
|
123
|
-
|
160
|
+
assert_nil(client.request.body)
|
124
161
|
end
|
125
162
|
|
126
163
|
def test_build_request_post_empty_body
|
@@ -134,7 +171,7 @@ class TestClient < Minitest::Test
|
|
134
171
|
)
|
135
172
|
client.build_request('post', nil)
|
136
173
|
assert_equal('', client.request['Content-Type'])
|
137
|
-
|
174
|
+
assert_nil(client.request.body)
|
138
175
|
end
|
139
176
|
|
140
177
|
def test_build_request_post_multipart
|
@@ -143,15 +180,71 @@ class TestClient < Minitest::Test
|
|
143
180
|
}
|
144
181
|
client = MockRequest.new(
|
145
182
|
host: 'https://localhost',
|
146
|
-
request_headers: headers
|
183
|
+
request_headers: headers
|
147
184
|
)
|
148
185
|
name = 'post'
|
149
|
-
args = [{'request_body' => 'hogebody'}]
|
186
|
+
args = [{ 'request_body' => 'hogebody' }]
|
150
187
|
client.build_request(name, args)
|
151
188
|
assert_equal('multipart/form-data; boundary=xYzZY', client.request['Content-Type'])
|
152
189
|
assert_equal('hogebody', client.request.body)
|
153
190
|
end
|
154
191
|
|
192
|
+
def test_json_body_encode_hash
|
193
|
+
headers = {
|
194
|
+
'Content-Type' => 'application/json'
|
195
|
+
}
|
196
|
+
client = MockRequestWithRequestBody.new(
|
197
|
+
host: 'https://localhost',
|
198
|
+
request_headers: headers
|
199
|
+
)
|
200
|
+
name = 'post'
|
201
|
+
args = [{ 'request_body' => { 'this_is' => 'json' } }]
|
202
|
+
response = client.build_request(name, args)
|
203
|
+
assert_equal('{"this_is":"json"}', response.request_body)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_json_body_encode_array
|
207
|
+
headers = {
|
208
|
+
'Content-Type' => 'application/json'
|
209
|
+
}
|
210
|
+
client = MockRequestWithRequestBody.new(
|
211
|
+
host: 'https://localhost',
|
212
|
+
request_headers: headers
|
213
|
+
)
|
214
|
+
name = 'post'
|
215
|
+
args = [{ 'request_body' => [{ 'this_is' => 'json' }] }]
|
216
|
+
response = client.build_request(name, args)
|
217
|
+
assert_equal('[{"this_is":"json"}]', response.request_body)
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_json_body_do_not_reencode
|
221
|
+
headers = {
|
222
|
+
'Content-Type' => 'application/json'
|
223
|
+
}
|
224
|
+
client = MockRequestWithRequestBody.new(
|
225
|
+
host: 'https://localhost',
|
226
|
+
request_headers: headers
|
227
|
+
)
|
228
|
+
name = 'post'
|
229
|
+
args = [{ 'request_body' => '{"this_is":"json"}' }]
|
230
|
+
response = client.build_request(name, args)
|
231
|
+
assert_equal('{"this_is":"json"}', response.request_body)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_json_body_do_not_reencode_simplejson
|
235
|
+
headers = {
|
236
|
+
'Content-Type' => 'application/json'
|
237
|
+
}
|
238
|
+
client = MockRequestWithRequestBody.new(
|
239
|
+
host: 'https://localhost',
|
240
|
+
request_headers: headers
|
241
|
+
)
|
242
|
+
name = 'post'
|
243
|
+
args = [{ 'request_body' => 'true' }]
|
244
|
+
response = client.build_request(name, args)
|
245
|
+
assert_equal('true', response.request_body)
|
246
|
+
end
|
247
|
+
|
155
248
|
def add_ssl
|
156
249
|
uri = URI.parse('https://localhost:4010')
|
157
250
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -165,10 +258,143 @@ class TestClient < Minitest::Test
|
|
165
258
|
assert_equal(['test'], url1.url_path)
|
166
259
|
end
|
167
260
|
|
261
|
+
def test_ratelimit_core
|
262
|
+
expiry = Time.now.to_i + 1
|
263
|
+
rl = SendGrid::Response::Ratelimit.new(500, 100, expiry)
|
264
|
+
rl2 = SendGrid::Response::Ratelimit.new(500, 0, expiry)
|
265
|
+
|
266
|
+
refute rl.exceeded?
|
267
|
+
assert rl2.exceeded?
|
268
|
+
|
269
|
+
assert_equal(rl.used, 400)
|
270
|
+
assert_equal(rl2.used, 500)
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_response_ratelimit_parsing
|
274
|
+
headers = {
|
275
|
+
'X-RateLimit-Limit' => '500',
|
276
|
+
'X-RateLimit-Remaining' => '300',
|
277
|
+
'X-RateLimit-Reset' => Time.now.to_i.to_s
|
278
|
+
}
|
279
|
+
|
280
|
+
body = ''
|
281
|
+
code = 204
|
282
|
+
http_response = MockHttpResponse.new(code, body, headers)
|
283
|
+
response = SendGrid::Response.new(http_response)
|
284
|
+
|
285
|
+
refute_nil response.ratelimit
|
286
|
+
refute response.ratelimit.exceeded?
|
287
|
+
end
|
288
|
+
|
168
289
|
def test_method_missing
|
169
290
|
response = @client.get
|
170
291
|
assert_equal(200, response.status_code)
|
171
|
-
assert_equal({'message' => 'success'}, response.body)
|
172
|
-
assert_equal({'headers' => 'test'}, response.headers)
|
292
|
+
assert_equal({ 'message' => 'success' }, response.body)
|
293
|
+
assert_equal({ 'headers' => 'test' }, response.headers)
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_http_options
|
297
|
+
url1 = @client_with_options._('test')
|
298
|
+
assert_equal(@host, @client_with_options.host)
|
299
|
+
assert_equal(@headers, @client_with_options.request_headers)
|
300
|
+
assert_equal(['test'], url1.url_path)
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_proxy_options
|
304
|
+
proxy_options = {
|
305
|
+
host: '127.0.0.1', port: 8080, user: 'anonymous', pass: 'secret'
|
306
|
+
}
|
307
|
+
client = MockRequest.new(
|
308
|
+
host: 'https://api.sendgrid.com',
|
309
|
+
request_headers: { 'Authorization' => 'Bearer xxx' },
|
310
|
+
proxy_options: proxy_options
|
311
|
+
).version('v3').api_keys
|
312
|
+
|
313
|
+
assert(client.proxy_address, '127.0.0.1')
|
314
|
+
assert(client.proxy_pass, 'secret')
|
315
|
+
assert(client.proxy_port, 8080)
|
316
|
+
assert(client.proxy_user, 'anonymous')
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_proxy_from_http_proxy_environment_variable
|
320
|
+
ENV['http_proxy'] = 'anonymous:secret@127.0.0.1:8080'
|
321
|
+
|
322
|
+
client = MockRequest.new(
|
323
|
+
host: 'https://api.sendgrid.com',
|
324
|
+
request_headers: { 'Authorization' => 'Bearer xxx' }
|
325
|
+
).version('v3').api_keys
|
326
|
+
|
327
|
+
assert(client.proxy_address, '127.0.0.1')
|
328
|
+
assert(client.proxy_pass, 'secret')
|
329
|
+
assert(client.proxy_port, 8080)
|
330
|
+
assert(client.proxy_user, 'anonymous')
|
331
|
+
ensure
|
332
|
+
ENV.delete('http_proxy')
|
333
|
+
end
|
334
|
+
|
335
|
+
# def test_docker_exists
|
336
|
+
# assert(File.file?('./Dockerfile') || File.file?('./docker/Dockerfile'))
|
337
|
+
# end
|
338
|
+
|
339
|
+
# def test_docker_compose_exists
|
340
|
+
# assert(File.file?('./docker-compose.yml') || File.file?('./docker/docker-compose.yml'))
|
341
|
+
# end
|
342
|
+
|
343
|
+
def test_env_sample_exists
|
344
|
+
assert(File.file?('./.env_sample'))
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_gitignore_exists
|
348
|
+
assert(File.file?('./.gitignore'))
|
349
|
+
end
|
350
|
+
|
351
|
+
def test_travis_exists
|
352
|
+
assert(File.file?('./.travis.yml'))
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_codeclimate_exists
|
356
|
+
assert(File.file?('./.codeclimate.yml'))
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_changelog_exists
|
360
|
+
assert(File.file?('./CHANGELOG.md'))
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_code_of_conduct_exists
|
364
|
+
assert(File.file?('./CODE_OF_CONDUCT.md'))
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_contributing_exists
|
368
|
+
assert(File.file?('./CONTRIBUTING.md'))
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_issue_template_exists
|
372
|
+
assert(File.file?('./ISSUE_TEMPLATE.md'))
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_license_exists
|
376
|
+
assert(File.file?('./LICENSE.md') || File.file?('./LICENSE.txt'))
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_pull_request_template_exists
|
380
|
+
assert(File.file?('./PULL_REQUEST_TEMPLATE.md'))
|
381
|
+
end
|
382
|
+
|
383
|
+
def test_readme_exists
|
384
|
+
assert(File.file?('./README.md'))
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_troubleshooting_exists
|
388
|
+
assert(File.file?('./TROUBLESHOOTING.md'))
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_use_cases_exists
|
392
|
+
assert(File.file?('use_cases/README.md'))
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_license_date_is_updated
|
396
|
+
license_end_year = IO.read('LICENSE.md').match(/Copyright \(C\) (\d{4}), Twilio SendGrid/)[1].to_i
|
397
|
+
current_year = Time.new.year
|
398
|
+
assert_equal(current_year, license_end_year)
|
173
399
|
end
|
174
400
|
end
|