ruby_http_client 3.3.0 → 3.5.5
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 +5 -5
- data/.env_sample +1 -0
- data/.github/ISSUE_TEMPLATE/config.yml +10 -0
- data/.github/workflows/test-and-deploy.yml +104 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +49 -0
- data/.rubocop_todo.yml +65 -0
- data/CHANGELOG.md +109 -1
- data/CODE_OF_CONDUCT.md +57 -25
- data/CONTRIBUTING.md +21 -37
- data/FIRST_TIMERS.md +79 -0
- data/Gemfile +3 -0
- data/ISSUE_TEMPLATE.md +30 -0
- data/{LICENSE.txt → LICENSE} +8 -8
- data/Makefile +7 -0
- data/PULL_REQUEST_TEMPLATE.md +31 -0
- data/README.md +32 -24
- data/Rakefile +25 -2
- data/TROUBLESHOOTING.md +1 -1
- data/USAGE.md +108 -0
- data/examples/example.rb +29 -1
- data/lib/ruby_http_client.rb +134 -21
- data/ruby_http_client.gemspec +15 -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 +238 -8
- data/twilio_sendgrid_logo.png +0 -0
- data/use_cases/README.md +3 -0
- metadata +66 -12
- data/.codeclimate.yml +0 -16
- data/.github/ISSUE_TEMPLATE +0 -17
- data/.github/PULL_REQUEST_TEMPLATE +0 -24
- data/.travis.yml +0 -17
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
|
@@ -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,18 @@ 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
|
+
http = add_ssl(Net::HTTP.new(*params))
|
231
|
+
http = add_http_options(http) unless @http_options.empty?
|
232
|
+
http
|
233
|
+
end
|
234
|
+
|
172
235
|
# Allow for https calls
|
173
236
|
#
|
174
237
|
# * *Args* :
|
@@ -184,6 +247,20 @@ module SendGrid
|
|
184
247
|
http
|
185
248
|
end
|
186
249
|
|
250
|
+
# Add others http options to http object
|
251
|
+
#
|
252
|
+
# * *Args* :
|
253
|
+
# - +http+ -> HTTP::NET object
|
254
|
+
# * *Returns* :
|
255
|
+
# - HTTP::NET object
|
256
|
+
#
|
257
|
+
def add_http_options(http)
|
258
|
+
@http_options.each do |attribute, value|
|
259
|
+
http.send("#{attribute}=", value)
|
260
|
+
end
|
261
|
+
http
|
262
|
+
end
|
263
|
+
|
187
264
|
# Add variable values to the url.
|
188
265
|
# (e.g. /your/api/{variable_value}/call)
|
189
266
|
# Another example: if you have a ruby reserved word, such as true,
|
@@ -195,20 +272,22 @@ module SendGrid
|
|
195
272
|
# - Client object
|
196
273
|
#
|
197
274
|
def _(name = nil)
|
198
|
-
url_path = name ? @url_path
|
199
|
-
@url_path = []
|
275
|
+
url_path = name ? @url_path + [name] : @url_path
|
200
276
|
Client.new(host: @host, request_headers: @request_headers,
|
201
|
-
version: @version, url_path: url_path
|
277
|
+
version: @version, url_path: url_path,
|
278
|
+
http_options: @http_options)
|
202
279
|
end
|
203
280
|
|
204
281
|
# Dynamically add segments to the url, then call a method.
|
205
282
|
# (e.g. client.name.name.get())
|
206
283
|
#
|
207
284
|
# * *Args* :
|
208
|
-
# - The args are
|
285
|
+
# - The args are automatically passed in
|
209
286
|
# * *Returns* :
|
210
287
|
# - Client object or Response object
|
211
288
|
#
|
289
|
+
# rubocop:disable Style/MethodMissingSuper
|
290
|
+
# rubocop:disable Style/MissingRespondToMissing
|
212
291
|
def method_missing(name, *args, &_block)
|
213
292
|
# Capture the version
|
214
293
|
if name.to_s == 'version'
|
@@ -217,8 +296,42 @@ module SendGrid
|
|
217
296
|
end
|
218
297
|
# We have reached the end of the method chain, make the API call
|
219
298
|
return build_request(name, args) if @methods.include?(name.to_s)
|
299
|
+
|
220
300
|
# Add a segment to the URL
|
221
301
|
_(name)
|
222
302
|
end
|
303
|
+
|
304
|
+
private
|
305
|
+
|
306
|
+
def build_http_request(http_method)
|
307
|
+
uri = build_url(query_params: @query_params)
|
308
|
+
net_http = Kernel.const_get('Net::HTTP::' + http_method.to_s.capitalize)
|
309
|
+
|
310
|
+
@http = build_http(uri.host, uri.port)
|
311
|
+
@request = build_request_headers(net_http.new(uri.request_uri))
|
312
|
+
end
|
313
|
+
|
314
|
+
def update_content_type(http_method)
|
315
|
+
if @request_body && content_type_json?
|
316
|
+
# If body is a hash or array, encode it; else leave it alone
|
317
|
+
@request.body = if [Hash, Array].include?(@request_body.class)
|
318
|
+
@request_body.to_json
|
319
|
+
else
|
320
|
+
@request_body
|
321
|
+
end
|
322
|
+
@request['Content-Type'] = 'application/json'
|
323
|
+
elsif !@request_body && http_method.to_s == 'post'
|
324
|
+
@request['Content-Type'] = ''
|
325
|
+
else
|
326
|
+
@request.body = @request_body
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def content_type_json?
|
331
|
+
!@request_headers.key?('Content-Type') ||
|
332
|
+
@request_headers['Content-Type'] == 'application/json'
|
333
|
+
end
|
334
|
+
# rubocop:enable Style/MethodMissingSuper
|
335
|
+
# rubocop:enable Style/MissingRespondToMissing
|
223
336
|
end
|
224
337
|
end
|
data/ruby_http_client.gemspec
CHANGED
@@ -1,20 +1,22 @@
|
|
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.5'
|
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 'simplecov', '~> 0.18.5'
|
20
22
|
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,7 +98,7 @@ class TestClient < Minitest::Test
|
|
61
98
|
|
62
99
|
def test_build_query_params
|
63
100
|
url = ''
|
64
|
-
query_params = { 'limit' => 100, 'offset' => 0, 'categories' => [
|
101
|
+
query_params = { 'limit' => 100, 'offset' => 0, 'categories' => %w[category1 category2] }
|
65
102
|
url = @client.build_query_params(url, query_params)
|
66
103
|
assert_equal('?limit=100&offset=0&categories=category1&categories=category2', url)
|
67
104
|
end
|
@@ -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)
|
@@ -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)
|
@@ -160,15 +253,152 @@ class TestClient < Minitest::Test
|
|
160
253
|
assert_equal(http.verify_mode, OpenSSL::SSL::VERIFY_PEER)
|
161
254
|
end
|
162
255
|
|
256
|
+
def test_add_http_options
|
257
|
+
uri = URI.parse('https://localhost:4010')
|
258
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
259
|
+
http = @client_with_options.add_http_options(http)
|
260
|
+
assert_equal(http.open_timeout, 60)
|
261
|
+
assert_equal(http.read_timeout, 60)
|
262
|
+
end
|
263
|
+
|
163
264
|
def test__
|
164
265
|
url1 = @client._('test')
|
165
266
|
assert_equal(['test'], url1.url_path)
|
166
267
|
end
|
167
268
|
|
269
|
+
def test_ratelimit_core
|
270
|
+
expiry = Time.now.to_i + 1
|
271
|
+
rl = SendGrid::Response::Ratelimit.new(500, 100, expiry)
|
272
|
+
rl2 = SendGrid::Response::Ratelimit.new(500, 0, expiry)
|
273
|
+
|
274
|
+
refute rl.exceeded?
|
275
|
+
assert rl2.exceeded?
|
276
|
+
|
277
|
+
assert_equal(rl.used, 400)
|
278
|
+
assert_equal(rl2.used, 500)
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_response_ratelimit_parsing
|
282
|
+
headers = {
|
283
|
+
'X-RateLimit-Limit' => '500',
|
284
|
+
'X-RateLimit-Remaining' => '300',
|
285
|
+
'X-RateLimit-Reset' => Time.now.to_i.to_s
|
286
|
+
}
|
287
|
+
|
288
|
+
body = ''
|
289
|
+
code = 204
|
290
|
+
http_response = MockHttpResponse.new(code, body, headers)
|
291
|
+
response = SendGrid::Response.new(http_response)
|
292
|
+
|
293
|
+
refute_nil response.ratelimit
|
294
|
+
refute response.ratelimit.exceeded?
|
295
|
+
end
|
296
|
+
|
168
297
|
def test_method_missing
|
169
298
|
response = @client.get
|
170
299
|
assert_equal(200, response.status_code)
|
171
|
-
assert_equal({'message' => 'success'}, response.body)
|
172
|
-
assert_equal({'headers' => 'test'}, response.headers)
|
300
|
+
assert_equal({ 'message' => 'success' }, response.body)
|
301
|
+
assert_equal({ 'headers' => 'test' }, response.headers)
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_http_options
|
305
|
+
url1 = @client_with_options._('test')
|
306
|
+
assert_equal(@host, @client_with_options.host)
|
307
|
+
assert_equal(@headers, @client_with_options.request_headers)
|
308
|
+
assert_equal(['test'], url1.url_path)
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_proxy_options
|
312
|
+
proxy_options = {
|
313
|
+
host: '127.0.0.1', port: 8080, user: 'anonymous', pass: 'secret'
|
314
|
+
}
|
315
|
+
client = MockRequest.new(
|
316
|
+
host: 'https://api.sendgrid.com',
|
317
|
+
request_headers: { 'Authorization' => 'Bearer xxx' },
|
318
|
+
proxy_options: proxy_options
|
319
|
+
).version('v3').api_keys
|
320
|
+
|
321
|
+
assert(client.proxy_address, '127.0.0.1')
|
322
|
+
assert(client.proxy_pass, 'secret')
|
323
|
+
assert(client.proxy_port, 8080)
|
324
|
+
assert(client.proxy_user, 'anonymous')
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_proxy_from_http_proxy_environment_variable
|
328
|
+
ENV['http_proxy'] = 'anonymous:secret@127.0.0.1:8080'
|
329
|
+
|
330
|
+
client = MockRequest.new(
|
331
|
+
host: 'https://api.sendgrid.com',
|
332
|
+
request_headers: { 'Authorization' => 'Bearer xxx' }
|
333
|
+
).version('v3').api_keys
|
334
|
+
|
335
|
+
assert(client.proxy_address, '127.0.0.1')
|
336
|
+
assert(client.proxy_pass, 'secret')
|
337
|
+
assert(client.proxy_port, 8080)
|
338
|
+
assert(client.proxy_user, 'anonymous')
|
339
|
+
ensure
|
340
|
+
ENV.delete('http_proxy')
|
341
|
+
end
|
342
|
+
|
343
|
+
# def test_docker_exists
|
344
|
+
# assert(File.file?('./Dockerfile') || File.file?('./docker/Dockerfile'))
|
345
|
+
# end
|
346
|
+
|
347
|
+
# def test_docker_compose_exists
|
348
|
+
# assert(File.file?('./docker-compose.yml') || File.file?('./docker/docker-compose.yml'))
|
349
|
+
# end
|
350
|
+
|
351
|
+
def test_env_sample_exists
|
352
|
+
assert(File.file?('./.env_sample'))
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_gitignore_exists
|
356
|
+
assert(File.file?('./.gitignore'))
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_gh_actions_exists
|
360
|
+
assert(File.file?('./.github/workflows/test-and-deploy.yml'))
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_changelog_exists
|
364
|
+
assert(File.file?('./CHANGELOG.md'))
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_code_of_conduct_exists
|
368
|
+
assert(File.file?('./CODE_OF_CONDUCT.md'))
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_contributing_exists
|
372
|
+
assert(File.file?('./CONTRIBUTING.md'))
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_issue_template_exists
|
376
|
+
assert(File.file?('./ISSUE_TEMPLATE.md'))
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_license_exists
|
380
|
+
assert(File.file?('./LICENSE'))
|
381
|
+
end
|
382
|
+
|
383
|
+
def test_pull_request_template_exists
|
384
|
+
assert(File.file?('./PULL_REQUEST_TEMPLATE.md'))
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_readme_exists
|
388
|
+
assert(File.file?('./README.md'))
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_troubleshooting_exists
|
392
|
+
assert(File.file?('./TROUBLESHOOTING.md'))
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_use_cases_exists
|
396
|
+
assert(File.file?('use_cases/README.md'))
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_license_date_is_updated
|
400
|
+
license_end_year = IO.read('LICENSE').match(/Copyright \(C\) (\d{4}), Twilio SendGrid/)[1].to_i
|
401
|
+
current_year = Time.new.year
|
402
|
+
assert_equal(current_year, license_end_year)
|
173
403
|
end
|
174
404
|
end
|
Binary file
|
data/use_cases/README.md
ADDED