api_client_builder 1.0.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.rubocop.yml +34 -0
- data/.travis.yml +20 -0
- data/Gemfile +6 -0
- data/Jenkinsfile +13 -0
- data/LICENSE.txt +22 -0
- data/{readme.md → README.md} +65 -50
- data/api_client_builder.gemspec +15 -13
- data/build.sh +16 -0
- data/lib/api_client_builder.rb +14 -0
- data/lib/api_client_builder/api_client.rb +96 -0
- data/lib/api_client_builder/delete_request.rb +19 -0
- data/lib/api_client_builder/get_collection_request.rb +43 -0
- data/lib/api_client_builder/get_item_request.rb +27 -0
- data/lib/api_client_builder/post_request.rb +19 -0
- data/lib/api_client_builder/put_request.rb +19 -0
- data/lib/api_client_builder/request.rb +66 -0
- data/lib/api_client_builder/response.rb +32 -0
- data/lib/api_client_builder/url_generator.rb +37 -0
- data/lib/api_client_builder/version.rb +3 -0
- data/spec/lib/api_client_builder/api_client_spec.rb +13 -4
- data/spec/lib/api_client_builder/delete_request_spec.rb +29 -0
- data/spec/lib/api_client_builder/get_collection_request_spec.rb +7 -10
- data/spec/lib/api_client_builder/get_item_request_spec.rb +4 -6
- data/spec/lib/api_client_builder/post_request_spec.rb +3 -5
- data/spec/lib/api_client_builder/put_request_spec.rb +3 -5
- data/spec/lib/api_client_builder/request_spec.rb +7 -9
- data/spec/lib/api_client_builder/response_spec.rb +1 -2
- data/spec/lib/api_client_builder/test_client/client.rb +3 -3
- data/spec/lib/api_client_builder/test_client/http_client_handler.rb +1 -2
- data/spec/lib/api_client_builder/test_client/response_handler.rb +13 -14
- data/spec/lib/api_client_builder/url_generator_spec.rb +29 -7
- data/spec/spec_helper.rb +8 -2
- metadata +51 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 61488aa64b23c4e971039af08754ac43a70277fe13cdb14ec5cc95cdc2cb4c1a
|
4
|
+
data.tar.gz: 1a14eaed9a3b913023c76e7b9788a3f8f39dea98749d299283eb5fe11d2a9d96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b0431c58dff83f80caffcd891a7b984982143cc829403b1b49b97889570a452ea6df1d3b216fc1d2d76e68599d2ba1f3add4101eaca2402727e3d9ddbb274ec
|
7
|
+
data.tar.gz: b717e8415fc64ffbe9de0eb2c8086c282d87b943577e1e4d5bf6e84b620594ad6de9b3ac0e89a6ac3e79c570ff4cd72d08be854ffa8dc4d819ca843d4b17be42
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.3
|
3
|
+
|
4
|
+
Metrics/BlockLength:
|
5
|
+
Exclude:
|
6
|
+
- spec/**/*.rb
|
7
|
+
|
8
|
+
Metrics/LineLength:
|
9
|
+
Max: 120 # Default: 80
|
10
|
+
|
11
|
+
Metrics/MethodLength:
|
12
|
+
Max: 20 # Default: 10
|
13
|
+
|
14
|
+
Naming/AccessorMethodName: # TODO: Enable and fix API at some point.
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Style/Documentation:
|
18
|
+
# This cop checks for missing top-level documentation of classes and modules.
|
19
|
+
# Classes with no body and namespace modules are exempt from the check.
|
20
|
+
# Namespace modules are modules that have nothing in their bodies except
|
21
|
+
# classes or other modules.
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/FrozenStringLiteralComment:
|
25
|
+
# `when_needed` will add the frozen string literal comment to files
|
26
|
+
# only when the `TargetRubyVersion` is set to 2.3+.
|
27
|
+
# `always` will always add the frozen string literal comment to a file
|
28
|
+
# regardless of the Ruby version or if `freeze` or `<<` are called on a
|
29
|
+
# string literal. If you run code against multiple versions of Ruby, it is
|
30
|
+
# possible that this will create errors in Ruby 2.3.0+.
|
31
|
+
#
|
32
|
+
# See: https://wyeworks.com/blog/2015/12/1/immutable-strings-in-ruby-2-dot-3
|
33
|
+
EnforcedStyle: when_needed
|
34
|
+
Enabled: false
|
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
dist: trusty
|
2
|
+
sudo: false
|
3
|
+
language: ruby
|
4
|
+
cache: bundler
|
5
|
+
|
6
|
+
rvm:
|
7
|
+
- 2.3
|
8
|
+
- 2.4
|
9
|
+
- 2.5
|
10
|
+
|
11
|
+
matrix:
|
12
|
+
fast_finish: true
|
13
|
+
|
14
|
+
before_install: gem update bundler
|
15
|
+
bundler_args: --jobs 3
|
16
|
+
install: bundle install --jobs 3
|
17
|
+
|
18
|
+
script:
|
19
|
+
- bundle exec rubocop --fail-level autocorrect
|
20
|
+
- bundle exec rspec
|
data/Gemfile
ADDED
data/Jenkinsfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016-2018 Instructure Inc.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/{readme.md → README.md}
RENAMED
@@ -3,7 +3,9 @@
|
|
3
3
|
API Client Builder was created to reduce the overhead of creating API clients.
|
4
4
|
|
5
5
|
It provides a DSL for defining endpoints and only requires you to define handlers
|
6
|
-
for
|
6
|
+
for HTTP requests and responses.
|
7
|
+
|
8
|
+
[![Build Status](https://travis-ci.org/instructure/api-client-builder.svg?branch=master)](https://travis-ci.org/instructure/api-client-builder)
|
7
9
|
|
8
10
|
---
|
9
11
|
|
@@ -28,10 +30,6 @@ Or install it yourself as:
|
|
28
30
|
The basic client structure looks like this.
|
29
31
|
|
30
32
|
```ruby
|
31
|
-
require 'api_client_builder/api_client'
|
32
|
-
require 'path/to/your/http_client_handler'
|
33
|
-
require 'path/to/your/response_handler'
|
34
|
-
|
35
33
|
class Client < APIClientBulder::APIClient
|
36
34
|
def initialize(**opts)
|
37
35
|
super(domain: opts[:domain],
|
@@ -55,8 +53,8 @@ def response_handler_build(http_client, start_url, type)
|
|
55
53
|
end
|
56
54
|
```
|
57
55
|
|
58
|
-
|
59
56
|
### Defining routes on the client
|
57
|
+
|
60
58
|
To define routes on the api client, use the DSL provided by the
|
61
59
|
builder's APIClient class. Four parts have been defined to help:
|
62
60
|
|
@@ -81,7 +79,6 @@ builder's APIClient class. Four parts have been defined to help:
|
|
81
79
|
- Note that any symbols in the route will be interpolated as required
|
82
80
|
params when calling the method on the client.
|
83
81
|
|
84
|
-
|
85
82
|
---
|
86
83
|
|
87
84
|
## Route Examples
|
@@ -91,7 +88,7 @@ builder's APIClient class. Four parts have been defined to help:
|
|
91
88
|
Define the route on the client
|
92
89
|
|
93
90
|
```ruby
|
94
|
-
|
91
|
+
get :some_object, :singular, 'some_objects/:id'
|
95
92
|
```
|
96
93
|
|
97
94
|
Use the defined route
|
@@ -107,7 +104,7 @@ response_body = single_request.response
|
|
107
104
|
Define the route on the client
|
108
105
|
|
109
106
|
```ruby
|
110
|
-
|
107
|
+
get :some_objects, :collection, 'some_objects'
|
111
108
|
```
|
112
109
|
|
113
110
|
Use the defined route
|
@@ -116,7 +113,7 @@ Use the defined route
|
|
116
113
|
collection_request = client.get_some_objects
|
117
114
|
|
118
115
|
collection_request.each do |item|
|
119
|
-
#Item will be a Hash if you use the default response in the response handler
|
116
|
+
# Item will be a Hash if you use the default response in the response handler
|
120
117
|
end
|
121
118
|
```
|
122
119
|
|
@@ -166,16 +163,16 @@ get :some_objects_for_course, :collection, 'course/:course_id/some_objects'
|
|
166
163
|
|
167
164
|
## Defining an HTTP Client Handler
|
168
165
|
|
169
|
-
The HTTP Client Handler is designed to manage the
|
166
|
+
The HTTP Client Handler is designed to manage the HTTP requests themselves. Since
|
170
167
|
actually making an HTTP request typically requires some amount of authentication,
|
171
168
|
it is suggested that authentication and headers are managed here as well.
|
172
169
|
|
173
|
-
The
|
170
|
+
The HTTP client handler requires '#get', '#post', and '#put' to be defined here
|
174
171
|
with the shown method signature.
|
175
172
|
|
176
173
|
```ruby
|
177
174
|
class HTTPClientHandler
|
178
|
-
#Do initialization here, generally authentication creds and a domain is sent in
|
175
|
+
# Do initialization here, generally authentication creds and a domain is sent in
|
179
176
|
|
180
177
|
def get(route, params = nil, headers = {})
|
181
178
|
client.get(route, params, headers)
|
@@ -189,9 +186,13 @@ class HTTPClientHandler
|
|
189
186
|
client.post(route, params, headers)
|
190
187
|
end
|
191
188
|
|
192
|
-
|
189
|
+
def delete(route, params = nil, headers = {})
|
190
|
+
client.delete(route, params, headers)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Define a client to use here. The HTTPClient gem is a good option
|
193
194
|
|
194
|
-
#Build up headers and authentication handling here as well
|
195
|
+
# Build up headers and authentication handling here as well
|
195
196
|
end
|
196
197
|
```
|
197
198
|
|
@@ -219,11 +220,11 @@ these actions simpler.
|
|
219
220
|
|
220
221
|
```ruby
|
221
222
|
class ResponseHandler
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
223
|
+
def initialize(http_client_handler, start_url, type)
|
224
|
+
@http_client = http_client_handler
|
225
|
+
@start_url = start_url
|
226
|
+
@type = type
|
227
|
+
end
|
227
228
|
end
|
228
229
|
```
|
229
230
|
|
@@ -239,13 +240,13 @@ of pages and also start the page counter.
|
|
239
240
|
|
240
241
|
```ruby
|
241
242
|
def get_first_page
|
242
|
-
#Build the URL -- this could be to add pagination params to the route, or
|
243
|
-
#
|
243
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
244
|
+
# add whatever else is necessary to the route.
|
244
245
|
http_response = @http_client.get("a URL")
|
245
246
|
|
246
|
-
#Generally the first page will contain information about how many pages a
|
247
|
-
#
|
248
|
-
#Be sure to set the current page count as well
|
247
|
+
# Generally the first page will contain information about how many pages a
|
248
|
+
# paginated response will have. Set that here: `@max_pages`
|
249
|
+
# Be sure to set the current page count as well: `@current_page`
|
249
250
|
build_response(http_response)
|
250
251
|
end
|
251
252
|
```
|
@@ -258,17 +259,16 @@ return a boolean denoting the presence of more pages.
|
|
258
259
|
|
259
260
|
```ruby
|
260
261
|
def get_next_page
|
261
|
-
#Build the URL -- this could be to add pagination params to the route, or
|
262
|
-
#
|
262
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
263
|
+
# add whatever else is necessary to the route:
|
263
264
|
http_response = @http_client.get("a URL")
|
264
265
|
|
265
|
-
#If the http_response is valid then increment the page counter here
|
266
|
+
# If the http_response is valid then increment the page counter here.
|
266
267
|
build_response(http_response)
|
267
268
|
end
|
268
269
|
|
269
270
|
def more_pages?
|
270
|
-
|
271
|
-
return true
|
271
|
+
@current_page < @max_pages
|
272
272
|
end
|
273
273
|
```
|
274
274
|
|
@@ -278,9 +278,9 @@ The builder will call `#put_request` when handling put routes.
|
|
278
278
|
|
279
279
|
```ruby
|
280
280
|
def put_request
|
281
|
-
#Build the URL -- this could be to add pagination params to the route, or
|
282
|
-
#
|
283
|
-
#Also send the body if thats how the client handler is configured
|
281
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
282
|
+
# add whatever else is necessary to the route.
|
283
|
+
# Also send the body if thats how the client handler is configured.
|
284
284
|
http_response = @http_client.put("a URL", {})
|
285
285
|
build_response(http_response)
|
286
286
|
end
|
@@ -292,14 +292,28 @@ The builder will call `#post_request` when handling post routes.
|
|
292
292
|
|
293
293
|
```ruby
|
294
294
|
def post_request
|
295
|
-
#Build the URL -- this could be to add pagination params to the route, or
|
296
|
-
#
|
297
|
-
#Also send the body if
|
295
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
296
|
+
# add whatever else is necessary to the route.
|
297
|
+
# Also send the body if that's how the client handler is configured.
|
298
298
|
http_response = @http_client.post("a URL", {})
|
299
299
|
build_response(http_response)
|
300
300
|
end
|
301
301
|
```
|
302
302
|
|
303
|
+
#### For deletes
|
304
|
+
|
305
|
+
The builder will call `#delete_request` when handling delete routes.
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
def delete_request
|
309
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
310
|
+
# add whatever else is necessary to the route.
|
311
|
+
# Also send the body if that's how the client handler is configured.
|
312
|
+
http_response = @http_client.delete("a URL")
|
313
|
+
build_response(http_response)
|
314
|
+
end
|
315
|
+
```
|
316
|
+
|
303
317
|
#### Handling retry-able requests
|
304
318
|
|
305
319
|
If requests defined need to be retry-able, extend the response handler by providing
|
@@ -308,34 +322,35 @@ the following methods.
|
|
308
322
|
```ruby
|
309
323
|
def retryable?(status_code)
|
310
324
|
if @opts[:exponential_backoff]
|
311
|
-
#Define the conditions of whether or not the provided status code is retry-able
|
325
|
+
# Define the conditions of whether or not the provided status code is retry-able
|
326
|
+
true
|
312
327
|
else
|
313
|
-
|
328
|
+
false
|
314
329
|
end
|
315
330
|
end
|
316
331
|
|
317
332
|
def reset_retries
|
318
|
-
#Track the number of retries so the request is not retried indefinitely.
|
319
|
-
#
|
320
|
-
#
|
333
|
+
# Track the number of retries so the request is not retried indefinitely.
|
334
|
+
# The builder will reset them when it no longer is retrying by calling this
|
335
|
+
# method.
|
321
336
|
@retries = 0
|
322
337
|
end
|
323
338
|
|
324
339
|
def retry_request
|
325
|
-
#Increment the retries here so the request is not retried indefinitely.
|
340
|
+
# Increment the retries here so the request is not retried indefinitely.
|
326
341
|
@retries += 1
|
327
342
|
|
328
|
-
#Build the URL -- this could be to add pagination params to the route, or
|
329
|
-
#
|
343
|
+
# Build the URL -- this could be to add pagination params to the route, or
|
344
|
+
# add whatever else is necessary to the route.
|
330
345
|
response = @http_client.the_action_to_retry("a URL")
|
331
346
|
build_response(response)
|
332
347
|
end
|
333
348
|
```
|
334
349
|
|
335
|
-
#### Managing the
|
350
|
+
#### Managing the HTTP response
|
336
351
|
|
337
352
|
The builder defines a default `Response` object that will provide the minimally
|
338
|
-
required interface for managing an
|
353
|
+
required interface for managing an HTTP response.
|
339
354
|
|
340
355
|
```ruby
|
341
356
|
def build_response(http_response)
|
@@ -373,10 +388,10 @@ is not a "success."
|
|
373
388
|
single_request = client.get_some_object(id: 123)
|
374
389
|
|
375
390
|
single_request.on_error do |page, handler|
|
376
|
-
#The page will have all of the status information
|
377
|
-
#The handler is the defined response_handler
|
378
|
-
#Use either to glean more information about why the request was an error and
|
379
|
-
# handle the error here
|
391
|
+
# The page will have all of the status information.
|
392
|
+
# The handler is the defined response_handler.
|
393
|
+
# Use either to glean more information about why the request was an error and
|
394
|
+
# handle the error here.
|
380
395
|
end
|
381
396
|
|
382
397
|
response_body = single_request.response
|
data/api_client_builder.gemspec
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
-
|
2
|
-
require File.expand_path('../lib/api_client_builder/version', __FILE__)
|
1
|
+
require File.expand_path('lib/api_client_builder/version', __dir__)
|
3
2
|
|
4
3
|
Gem::Specification.new do |gem|
|
5
|
-
gem.name =
|
6
|
-
gem.summary =
|
7
|
-
gem.description =
|
4
|
+
gem.name = 'api_client_builder'
|
5
|
+
gem.summary = 'API Client Builder provides an easy to use interface for creating HTTP api clients'
|
6
|
+
gem.description = 'API Client Builder provides an easy to use interface for creating HTTP api clients'
|
8
7
|
gem.authors = ['Jayce Higgins']
|
9
8
|
gem.email = ['jhiggins@instructure.com', 'eng@instructure.com']
|
9
|
+
gem.homepage = 'https://github.com/instructure/api-client-builder'
|
10
10
|
|
11
|
-
gem.files = %w[api_client_builder.gemspec readme.md]
|
12
|
-
|
13
|
-
gem.test_files = Dir.glob("spec/**/*")
|
14
|
-
gem.require_paths = ["lib"]
|
15
11
|
gem.version = APIClientBuilder::VERSION
|
16
|
-
gem.required_ruby_version = '>= 2.
|
12
|
+
gem.required_ruby_version = '>= 2.3'
|
17
13
|
|
18
|
-
gem.license
|
14
|
+
gem.license = 'MIT'
|
19
15
|
|
20
16
|
gem.add_development_dependency 'pry'
|
21
|
-
gem.add_development_dependency 'rspec'
|
22
|
-
gem.add_development_dependency '
|
17
|
+
gem.add_development_dependency 'rspec', '~> 3.7'
|
18
|
+
gem.add_development_dependency 'rubocop', '~> 0.57.2'
|
19
|
+
gem.add_development_dependency 'simplecov', '~> 0'
|
20
|
+
|
21
|
+
gem.files = `git ls-files`.split("\n")
|
22
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
24
|
+
gem.require_paths = ['lib']
|
23
25
|
end
|
data/build.sh
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
docker pull ruby:2.5
|
6
|
+
docker pull ruby:2.6
|
7
|
+
|
8
|
+
docker run --rm -v "`pwd`:/app" -w /app --user `id -u`:`id -g` -e HOME="/tmp" "ruby:2.3" \
|
9
|
+
/bin/sh -c "echo \"gem: --no-document\" >> ~/.gemrc && bundle install --jobs 5 --quiet && bundle exec rubocop --cache false --fail-level autocorrect"
|
10
|
+
|
11
|
+
for version in '2.5' '2.6'; do
|
12
|
+
echo "Testing Ruby $version..."
|
13
|
+
docker run --rm -v "`pwd`:/app" -w /app --user `id -u`:`id -g` \
|
14
|
+
-e HOME="/tmp" "ruby:$version" /bin/sh -c \
|
15
|
+
"echo \"gem: --no-document\" >> ~/.gemrc && bundle install --jobs 5 --quiet && bundle exec rspec"
|
16
|
+
done
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require_relative 'api_client_builder/version'
|
4
|
+
require_relative 'api_client_builder/request'
|
5
|
+
require_relative 'api_client_builder/response'
|
6
|
+
|
7
|
+
require_relative 'api_client_builder/get_collection_request'
|
8
|
+
require_relative 'api_client_builder/get_item_request'
|
9
|
+
require_relative 'api_client_builder/post_request'
|
10
|
+
require_relative 'api_client_builder/put_request'
|
11
|
+
require_relative 'api_client_builder/delete_request'
|
12
|
+
require_relative 'api_client_builder/url_generator'
|
13
|
+
|
14
|
+
require_relative 'api_client_builder/api_client'
|