api_client_builder 1.0.1 → 1.4.1
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/.gitignore +3 -1
- data/.rubocop.yml +34 -0
- data/.travis.yml +14 -12
- data/Jenkinsfile +13 -0
- data/LICENSE.txt +1 -1
- data/README.md +65 -50
- data/api_client_builder.gemspec +11 -10
- data/build.sh +15 -3
- data/lib/api_client_builder.rb +14 -9
- data/lib/api_client_builder/api_client.rb +18 -7
- data/lib/api_client_builder/delete_request.rb +19 -0
- data/lib/api_client_builder/get_collection_request.rb +2 -2
- data/lib/api_client_builder/get_item_request.rb +0 -2
- data/lib/api_client_builder/post_request.rb +0 -2
- data/lib/api_client_builder/put_request.rb +0 -2
- data/lib/api_client_builder/request.rb +6 -6
- data/lib/api_client_builder/url_generator.rb +13 -6
- data/lib/api_client_builder/version.rb +1 -1
- 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 +31 -7
- data/spec/spec_helper.rb +8 -2
- metadata +37 -36
- data/.dockerignore +0 -1
- data/Dockerfile +0 -16
- data/api_client_builder-1.0.0.gem +0 -0
- data/docker-compose.yml +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6fe7e4e6055363b600d2544fab5f2585f30b73da723f2ee928762b0913251190
|
|
4
|
+
data.tar.gz: d6a81b59ceacb77cd86d5f4df6badeb21ed15d2f90889cd048a0ffcf8dab0f3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0775c90d669b8fe323902689e9d5c8c12fc7d62039f32704a5a3b2f4158c68aa77a9aa5e7ea4d5fff458435beacd564f5f12ba3145967ee37368c66fd6cb3981
|
|
7
|
+
data.tar.gz: 804e72d9df7452aac1756568c9663d61fe6d5c8bf34e9952340a9445cd616a55d5fa041312c3793f2b56e9f1561b7fe55051c47531716ab2a30783cca62ce621
|
data/.gitignore
CHANGED
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
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
dist: trusty
|
|
2
2
|
sudo: false
|
|
3
|
+
language: ruby
|
|
3
4
|
cache: bundler
|
|
5
|
+
|
|
4
6
|
rvm:
|
|
5
|
-
# - ruby-head
|
|
6
|
-
- 2.1
|
|
7
|
-
- 2.2
|
|
8
7
|
- 2.3
|
|
8
|
+
- 2.4
|
|
9
|
+
- 2.5
|
|
10
|
+
|
|
9
11
|
matrix:
|
|
10
12
|
fast_finish: true
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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/Jenkinsfile
ADDED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -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
|
+
[](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,24 +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
11
|
gem.version = APIClientBuilder::VERSION
|
|
12
|
-
gem.required_ruby_version = '>= 2.
|
|
12
|
+
gem.required_ruby_version = '>= 2.3'
|
|
13
13
|
|
|
14
|
-
gem.license
|
|
14
|
+
gem.license = 'MIT'
|
|
15
15
|
|
|
16
16
|
gem.add_development_dependency 'pry'
|
|
17
|
-
gem.add_development_dependency 'rspec'
|
|
18
|
-
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'
|
|
19
20
|
|
|
20
21
|
gem.files = `git ls-files`.split("\n")
|
|
21
22
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
22
23
|
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
23
|
-
gem.require_paths = [
|
|
24
|
+
gem.require_paths = ['lib']
|
|
24
25
|
end
|
data/build.sh
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
#!/bin/bash
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
data/lib/api_client_builder.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
require '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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'
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
require 'api_client_builder/get_collection_request'
|
|
2
|
-
require 'api_client_builder/get_item_request'
|
|
3
|
-
require 'api_client_builder/post_request'
|
|
4
|
-
require 'api_client_builder/put_request'
|
|
5
|
-
require 'api_client_builder/url_generator'
|
|
6
|
-
|
|
7
1
|
module APIClientBuilder
|
|
8
2
|
# The base APIClient that defines the interface for defining an API Client.
|
|
9
3
|
# Should be sub-classed and then provided an HTTPClient handler and a
|
|
@@ -28,7 +22,7 @@ module APIClientBuilder
|
|
|
28
22
|
# @param route [String] defines the routes endpoint
|
|
29
23
|
#
|
|
30
24
|
# @return [Request] either a GetCollection or GetItem request
|
|
31
|
-
def self.get(type, plurality, route, **
|
|
25
|
+
def self.get(type, plurality, route, **_opts)
|
|
32
26
|
if plurality == :collection
|
|
33
27
|
define_method("get_#{type}") do |**params|
|
|
34
28
|
GetCollectionRequest.new(
|
|
@@ -81,5 +75,22 @@ module APIClientBuilder
|
|
|
81
75
|
)
|
|
82
76
|
end
|
|
83
77
|
end
|
|
78
|
+
|
|
79
|
+
# Used to define a DELETE api route on the base class. Will
|
|
80
|
+
# yield a method that takes the shape of 'delete_type' that will
|
|
81
|
+
# return a DeleteRequest.
|
|
82
|
+
#
|
|
83
|
+
# @param type [Symbol] defines the route model
|
|
84
|
+
# @param route [String] defines the routes endpoint
|
|
85
|
+
#
|
|
86
|
+
# @return [DeleteRequest] the request object that handles puts
|
|
87
|
+
def self.delete(type, route)
|
|
88
|
+
define_method("delete_#{type}") do |**params|
|
|
89
|
+
DeleteRequest.new(
|
|
90
|
+
type,
|
|
91
|
+
response_handler_build(http_client, @url_generator.build_route(route, **params), type)
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
84
95
|
end
|
|
85
96
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module APIClientBuilder
|
|
2
|
+
class DeleteRequest < Request
|
|
3
|
+
# Yields the response body if the response was successful. Will call
|
|
4
|
+
# the response handlers if there was not a successful response.
|
|
5
|
+
#
|
|
6
|
+
# @return [JSON] the http response body
|
|
7
|
+
def response
|
|
8
|
+
response = response_handler.delete_request
|
|
9
|
+
|
|
10
|
+
if response.success?
|
|
11
|
+
response
|
|
12
|
+
else
|
|
13
|
+
error_handlers.each do |handler|
|
|
14
|
+
handler.call(response, self)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'api_client_builder/request'
|
|
2
|
-
|
|
3
1
|
module APIClientBuilder
|
|
4
2
|
# The multi item response object to be used as the container for
|
|
5
3
|
# collection responses from the defined API
|
|
@@ -11,6 +9,7 @@ module APIClientBuilder
|
|
|
11
9
|
# strategy is defined concretely on the response handler.
|
|
12
10
|
#
|
|
13
11
|
# @return [JSON] the http response body
|
|
12
|
+
# rubocop:disable Metrics/AbcSize
|
|
14
13
|
def each
|
|
15
14
|
if block_given?
|
|
16
15
|
each_page do |page|
|
|
@@ -32,6 +31,7 @@ module APIClientBuilder
|
|
|
32
31
|
Enumerator.new(self, :each)
|
|
33
32
|
end
|
|
34
33
|
end
|
|
34
|
+
# rubocop:enable Metrics/AbcSize
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module APIClientBuilder
|
|
2
|
-
class DefaultPageError < StandardError;end
|
|
2
|
+
class DefaultPageError < StandardError; end
|
|
3
3
|
|
|
4
4
|
class Request
|
|
5
5
|
attr_reader :type, :response_handler, :body, :error_handlers_collection
|
|
@@ -23,11 +23,11 @@ module APIClientBuilder
|
|
|
23
23
|
# @return [Array<Block>] the error handlers collection
|
|
24
24
|
def error_handlers
|
|
25
25
|
if error_handlers_collection.empty?
|
|
26
|
-
|
|
27
|
-
raise DefaultPageError,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
on_error do |page, _handler|
|
|
27
|
+
raise DefaultPageError, <<~MESSAGE
|
|
28
|
+
Default error for bad response. If you want to handle this error use #on_error
|
|
29
|
+
on the response in your api consumer. Error Code: #{page.status_code}.
|
|
30
|
+
MESSAGE
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
error_handlers_collection
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require 'uri'
|
|
2
|
-
|
|
3
1
|
module APIClientBuilder
|
|
4
|
-
class NoURLError < StandardError;end
|
|
2
|
+
class NoURLError < StandardError; end
|
|
5
3
|
class URLGenerator
|
|
6
4
|
# Receives a domain and parses it into a URI
|
|
7
5
|
#
|
|
@@ -23,17 +21,26 @@ module APIClientBuilder
|
|
|
23
21
|
#
|
|
24
22
|
# @return [URI] the fully built route
|
|
25
23
|
def build_route(route, **params)
|
|
26
|
-
string_params = route.split(
|
|
27
|
-
symboled_params = string_params.map{|param| param.tr(':', '').to_sym}
|
|
24
|
+
string_params = route.split(%r{[\/=]}).select { |param| param.start_with?(':') }
|
|
25
|
+
symboled_params = string_params.map { |param| param.tr(':', '').to_sym }
|
|
28
26
|
|
|
29
27
|
new_route = route.clone
|
|
30
28
|
symboled_params.each do |param|
|
|
31
29
|
value = params[param]
|
|
32
30
|
raise ArgumentError, "Param :#{param} is required" unless value
|
|
33
|
-
new_route.gsub!(":#{param}", value.to_s)
|
|
31
|
+
new_route.gsub!(":#{param}", encode_ascii(value.to_s))
|
|
34
32
|
end
|
|
35
33
|
|
|
36
34
|
@base_uri.merge(new_route)
|
|
37
35
|
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def encode_ascii(str)
|
|
40
|
+
str.each_char.map do |c|
|
|
41
|
+
next c if c.force_encoding('ascii').valid_encoding?
|
|
42
|
+
CGI.escape c
|
|
43
|
+
end.join
|
|
44
|
+
end
|
|
38
45
|
end
|
|
39
46
|
end
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'api_client_builder/api_client'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe APIClient do
|
|
7
|
-
let(:domain) {'https://www.domain.com/api/endpoints/'}
|
|
8
|
-
let(:client) {TestClient::Client.new(domain: domain)}
|
|
6
|
+
let(:domain) { 'https://www.domain.com/api/endpoints/' }
|
|
7
|
+
let(:client) { TestClient::Client.new(domain: domain) }
|
|
9
8
|
|
|
10
9
|
describe '.get' do
|
|
11
10
|
context 'plurality is :collection' do
|
|
@@ -42,5 +41,15 @@ module APIClientBuilder
|
|
|
42
41
|
expect(client.put_some_object({})).to be_a(APIClientBuilder::PutRequest)
|
|
43
42
|
end
|
|
44
43
|
end
|
|
44
|
+
|
|
45
|
+
describe '.delete' do
|
|
46
|
+
it 'defines a delete method on the client' do
|
|
47
|
+
expect(client).to respond_to(:delete_some_object)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'returns a DeleteRequest object' do
|
|
51
|
+
expect(client.delete_some_object({})).to be_a(APIClientBuilder::DeleteRequest)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
45
54
|
end
|
|
46
55
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require_relative 'test_client/client'
|
|
3
|
+
|
|
4
|
+
module APIClientBuilder
|
|
5
|
+
describe DeleteRequest do
|
|
6
|
+
describe '#response' do
|
|
7
|
+
context 'request was successful' do
|
|
8
|
+
it 'returns a response object' do
|
|
9
|
+
client = TestClient::Client.new(domain: 'https://www.domain.com/api/endpoints/')
|
|
10
|
+
|
|
11
|
+
some_object = client.delete_some_object({}).response
|
|
12
|
+
expect(some_object.body).to eq('good delete request')
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context 'request was unsuccessful' do
|
|
17
|
+
it 'calls the error handlers' do
|
|
18
|
+
client = TestClient::Client.new(domain: 'https://www.domain.com/api/endpoints/')
|
|
19
|
+
|
|
20
|
+
bad_response = APIClientBuilder::Response.new('bad request', 400, [200])
|
|
21
|
+
allow_any_instance_of(TestClient::ResponseHandler).to receive(:delete_request).and_return(bad_response)
|
|
22
|
+
expect { client.delete_some_object({}).response }.to raise_error(
|
|
23
|
+
APIClientBuilder::DefaultPageError, /Error Code: 400/
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'lib/api_client_builder/test_client/client'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe GetCollectionRequest do
|
|
@@ -21,9 +20,8 @@ module APIClientBuilder
|
|
|
21
20
|
bad_response = APIClientBuilder::Response.new('bad request', 500, [200])
|
|
22
21
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
23
22
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(bad_response)
|
|
24
|
-
expect{ client.get_some_objects.each{} }.to raise_error(
|
|
25
|
-
APIClientBuilder::DefaultPageError,
|
|
26
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 500"
|
|
23
|
+
expect { client.get_some_objects.each {} }.to raise_error(
|
|
24
|
+
APIClientBuilder::DefaultPageError, /Error Code: 500/
|
|
27
25
|
)
|
|
28
26
|
end
|
|
29
27
|
|
|
@@ -32,7 +30,7 @@ module APIClientBuilder
|
|
|
32
30
|
client = TestClient::Client.new(domain: 'https://www.domain.com/api/endpoints/')
|
|
33
31
|
|
|
34
32
|
bad_response = APIClientBuilder::Response.new('bad request', 500, [200])
|
|
35
|
-
good_response = APIClientBuilder::Response.new([1,2,3], 200, [200])
|
|
33
|
+
good_response = APIClientBuilder::Response.new([1, 2, 3], 200, [200])
|
|
36
34
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
37
35
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:more_pages?).and_return(false)
|
|
38
36
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(good_response)
|
|
@@ -47,13 +45,12 @@ module APIClientBuilder
|
|
|
47
45
|
client = TestClient::Client.new(domain: 'https://www.domain.com/api/endpoints/')
|
|
48
46
|
|
|
49
47
|
bad_response = APIClientBuilder::Response.new('bad request', 400, [200])
|
|
50
|
-
good_response = APIClientBuilder::Response.new([1,2,3], 200, [200])
|
|
48
|
+
good_response = APIClientBuilder::Response.new([1, 2, 3], 200, [200])
|
|
51
49
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
52
50
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:more_pages?).and_return(false)
|
|
53
51
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(good_response)
|
|
54
|
-
expect{ client.get_some_objects.each{} }.to raise_error(
|
|
55
|
-
APIClientBuilder::DefaultPageError,
|
|
56
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 400"
|
|
52
|
+
expect { client.get_some_objects.each {} }.to raise_error(
|
|
53
|
+
APIClientBuilder::DefaultPageError, /Error Code: 400/
|
|
57
54
|
)
|
|
58
55
|
end
|
|
59
56
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'lib/api_client_builder/test_client/client'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe GetItemRequest do
|
|
@@ -21,9 +20,8 @@ module APIClientBuilder
|
|
|
21
20
|
bad_response = APIClientBuilder::Response.new('bad request', 500, [200])
|
|
22
21
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
23
22
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(bad_response)
|
|
24
|
-
expect{ client.get_some_object(some_id: '123').response }.to raise_error(
|
|
25
|
-
APIClientBuilder::DefaultPageError,
|
|
26
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 500"
|
|
23
|
+
expect { client.get_some_object(some_id: '123').response }.to raise_error(
|
|
24
|
+
APIClientBuilder::DefaultPageError, /Error Code: 500/
|
|
27
25
|
)
|
|
28
26
|
end
|
|
29
27
|
|
|
@@ -32,7 +30,7 @@ module APIClientBuilder
|
|
|
32
30
|
client = TestClient::Client.new(domain: 'https://www.domain.com/api/endpoints/')
|
|
33
31
|
|
|
34
32
|
bad_response = APIClientBuilder::Response.new('bad request', 500, [200])
|
|
35
|
-
good_response = APIClientBuilder::Response.new([1,2,3], 200, [200])
|
|
33
|
+
good_response = APIClientBuilder::Response.new([1, 2, 3], 200, [200])
|
|
36
34
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
37
35
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:more_pages?).and_return(false)
|
|
38
36
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(good_response)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'lib/api_client_builder/test_client/client'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe PostRequest do
|
|
@@ -20,9 +19,8 @@ module APIClientBuilder
|
|
|
20
19
|
|
|
21
20
|
bad_response = APIClientBuilder::Response.new('bad request', 400, [200])
|
|
22
21
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:post_request).and_return(bad_response)
|
|
23
|
-
expect{ client.post_some_object({}).response }.to raise_error(
|
|
24
|
-
APIClientBuilder::DefaultPageError,
|
|
25
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 400"
|
|
22
|
+
expect { client.post_some_object({}).response }.to raise_error(
|
|
23
|
+
APIClientBuilder::DefaultPageError, /Error Code: 400/
|
|
26
24
|
)
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'lib/api_client_builder/test_client/client'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe PutRequest do
|
|
@@ -20,9 +19,8 @@ module APIClientBuilder
|
|
|
20
19
|
|
|
21
20
|
bad_response = APIClientBuilder::Response.new('bad request', 400, [200])
|
|
22
21
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:put_request).and_return(bad_response)
|
|
23
|
-
expect{ client.put_some_object({}).response }.to raise_error(
|
|
24
|
-
APIClientBuilder::DefaultPageError,
|
|
25
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 400"
|
|
22
|
+
expect { client.put_some_object({}).response }.to raise_error(
|
|
23
|
+
APIClientBuilder::DefaultPageError, /Error Code: 400/
|
|
26
24
|
)
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
require 'api_client_builder/request'
|
|
2
|
+
require_relative 'test_client/client'
|
|
4
3
|
|
|
5
4
|
module APIClientBuilder
|
|
6
5
|
describe Request do
|
|
@@ -12,9 +11,8 @@ module APIClientBuilder
|
|
|
12
11
|
bad_response = APIClientBuilder::Response.new('bad request', 400, [200])
|
|
13
12
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:get_first_page).and_return(bad_response)
|
|
14
13
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(bad_response)
|
|
15
|
-
expect{ client.get_some_object(some_id: '123').response }.to raise_error(
|
|
16
|
-
APIClientBuilder::DefaultPageError,
|
|
17
|
-
"Default error for bad response. If you want to handle this error use #on_error on the response in your api consumer. Error Code: 400"
|
|
14
|
+
expect { client.get_some_object(some_id: '123').response }.to raise_error(
|
|
15
|
+
APIClientBuilder::DefaultPageError, /Error Code: 400/
|
|
18
16
|
)
|
|
19
17
|
end
|
|
20
18
|
end
|
|
@@ -28,13 +26,13 @@ module APIClientBuilder
|
|
|
28
26
|
allow_any_instance_of(TestClient::ResponseHandler).to receive(:retry_request).and_return(bad_response)
|
|
29
27
|
object_response = client.get_some_object(some_id: '123')
|
|
30
28
|
|
|
31
|
-
object_response.on_error do |
|
|
32
|
-
raise StandardError,
|
|
29
|
+
object_response.on_error do |_page, _response|
|
|
30
|
+
raise StandardError, 'Something Bad Happened'
|
|
33
31
|
end
|
|
34
32
|
|
|
35
|
-
expect{object_response.response}.to raise_error(
|
|
33
|
+
expect { object_response.response }.to raise_error(
|
|
36
34
|
StandardError,
|
|
37
|
-
|
|
35
|
+
'Something Bad Happened'
|
|
38
36
|
)
|
|
39
37
|
end
|
|
40
38
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'api_client_builder/response'
|
|
3
2
|
|
|
4
3
|
module APIClientBuilder
|
|
5
4
|
describe Response do
|
|
@@ -18,7 +17,7 @@ module APIClientBuilder
|
|
|
18
17
|
|
|
19
18
|
it 'returns false when the response is otherwise marked as failed' do
|
|
20
19
|
page = Response.new([], 200, [200])
|
|
21
|
-
page.mark_failed
|
|
20
|
+
page.mark_failed 'Something happened'
|
|
22
21
|
|
|
23
22
|
expect(page.success?).to eq(false)
|
|
24
23
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'lib/api_client_builder/test_client/http_client_handler'
|
|
1
|
+
require_relative 'response_handler'
|
|
2
|
+
require_relative 'http_client_handler'
|
|
4
3
|
|
|
5
4
|
module TestClient
|
|
6
5
|
class Client < APIClientBuilder::APIClient
|
|
@@ -18,5 +17,6 @@ module TestClient
|
|
|
18
17
|
|
|
19
18
|
post :some_object, 'some/post/url'
|
|
20
19
|
put :some_object, 'som/put/url'
|
|
20
|
+
delete :some_object, 'som/delete/url'
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
require 'api_client_builder/response'
|
|
2
|
-
|
|
3
1
|
module TestClient
|
|
4
2
|
class ResponseHandler
|
|
5
3
|
SUCCESS_STATUS = 200
|
|
6
|
-
SUCCESS_RANGE = [200]
|
|
4
|
+
SUCCESS_RANGE = [200].freeze
|
|
7
5
|
MAX_RETRIES = 4
|
|
8
6
|
|
|
9
|
-
def initialize(
|
|
7
|
+
def initialize(_client, _start_url, _type, **_opts)
|
|
10
8
|
@retries = 0
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
def get_first_page
|
|
14
12
|
@current_page = 0
|
|
15
|
-
APIClientBuilder::Response.new([1,2,3], SUCCESS_STATUS, SUCCESS_RANGE)
|
|
13
|
+
APIClientBuilder::Response.new([1, 2, 3], SUCCESS_STATUS, SUCCESS_RANGE)
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def get_next_page
|
|
19
17
|
@current_page += 1
|
|
20
|
-
APIClientBuilder::Response.new([4,5,6], SUCCESS_STATUS, SUCCESS_RANGE)
|
|
18
|
+
APIClientBuilder::Response.new([4, 5, 6], SUCCESS_STATUS, SUCCESS_RANGE)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete_request
|
|
22
|
+
APIClientBuilder::Response.new('good delete request', SUCCESS_STATUS, SUCCESS_RANGE)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
def put_request(
|
|
25
|
+
def put_request(_body)
|
|
24
26
|
APIClientBuilder::Response.new('good request', SUCCESS_STATUS, SUCCESS_RANGE)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
|
-
def post_request(
|
|
29
|
+
def post_request(_body)
|
|
28
30
|
APIClientBuilder::Response.new('good request', SUCCESS_STATUS, SUCCESS_RANGE)
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
def more_pages?
|
|
32
|
-
|
|
33
|
-
return true
|
|
34
|
+
@current_page < 2
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def retryable?(status_code)
|
|
@@ -38,10 +39,8 @@ module TestClient
|
|
|
38
39
|
status_code != 400 && @retries < MAX_RETRIES
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
def retry_request
|
|
42
|
-
end
|
|
42
|
+
def retry_request; end
|
|
43
43
|
|
|
44
|
-
def reset_retries
|
|
45
|
-
end
|
|
44
|
+
def reset_retries; end
|
|
46
45
|
end
|
|
47
46
|
end
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'api_client_builder/url_generator'
|
|
3
2
|
|
|
4
3
|
module APIClientBuilder
|
|
5
4
|
describe URLGenerator do
|
|
6
5
|
describe '#build_route' do
|
|
7
6
|
context 'route with colon params and matching keys' do
|
|
8
7
|
it 'replaces to route keys with their respective values' do
|
|
9
|
-
url_generator = URLGenerator.new(
|
|
8
|
+
url_generator = URLGenerator.new('https://www.domain.com/api/endpoints/')
|
|
10
9
|
|
|
11
10
|
route = url_generator.build_route(
|
|
12
11
|
'object_one/:object_one_id/:object_one_id/route_to_object/:other_object_id/object',
|
|
@@ -16,16 +15,41 @@ module APIClientBuilder
|
|
|
16
15
|
|
|
17
16
|
expect(route).to eq(URI.parse('https://www.domain.com/api/endpoints/object_one/4/4/route_to_object/10/object'))
|
|
18
17
|
end
|
|
18
|
+
|
|
19
|
+
it 'replaces to route keys with their respective values in embedded params' do
|
|
20
|
+
url_generator = URLGenerator.new('https://www.domain.com/api/endpoints/')
|
|
21
|
+
|
|
22
|
+
route = url_generator.build_route(
|
|
23
|
+
'object_one/:object_one_id/:object_one_id/object&as_user_id=:user_object_id',
|
|
24
|
+
object_one_id: '4',
|
|
25
|
+
user_object_id: '1'
|
|
26
|
+
)
|
|
27
|
+
expect(route).to eq(URI.parse('https://www.domain.com/api/endpoints/object_one/4/4/object&as_user_id=1'))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'encodes the URI correctly' do
|
|
31
|
+
url_generator = URLGenerator.new('https://www.domain.com/api/endpoints/')
|
|
32
|
+
|
|
33
|
+
route = url_generator.build_route(
|
|
34
|
+
'route_to_object/:object_id/object/:query_string',
|
|
35
|
+
object_id: "\u1F4a9",
|
|
36
|
+
query_string: "?sample[]=\u1F648\u1F649\u1F64A&success=party\u1F64C"
|
|
37
|
+
)
|
|
38
|
+
expect(route).to eq(URI.parse('https://www.domain.com/api/endpoints/route_to_object/%E1%BD%8A9/object/' \
|
|
39
|
+
'?sample[]=%E1%BD%A48%E1%BD%A49%E1%BD%A4A&success=party%E1%BD%A4C'))
|
|
40
|
+
end
|
|
19
41
|
end
|
|
20
42
|
|
|
21
43
|
context 'route with colon params and non matching keys' do
|
|
22
44
|
it 'raises an argument error' do
|
|
23
|
-
url_generator = URLGenerator.new(
|
|
45
|
+
url_generator = URLGenerator.new('https://www.domain.com/api/endpoints/')
|
|
24
46
|
|
|
25
|
-
expect
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
expect do
|
|
48
|
+
url_generator.build_route(
|
|
49
|
+
'object_one/:object_one_id/:object_one_id/route_to_object/:other_object_id/object',
|
|
50
|
+
other_object_id: '10'
|
|
51
|
+
)
|
|
52
|
+
end.to raise_error(ArgumentError, 'Param :object_one_id is required')
|
|
29
53
|
end
|
|
30
54
|
end
|
|
31
55
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
require 'simplecov'
|
|
2
|
+
SimpleCov.start do
|
|
3
|
+
add_filter 'lib/api_client_builder/version.rb'
|
|
4
|
+
add_filter 'spec'
|
|
5
|
+
track_files 'lib/**/*.rb'
|
|
6
|
+
end
|
|
7
|
+
SimpleCov.minimum_coverage(97)
|
|
8
|
+
|
|
1
9
|
require 'bundler/setup'
|
|
2
10
|
require 'api_client_builder'
|
|
3
|
-
|
|
4
|
-
SPEC_DIR = File.dirname(__FILE__)
|
metadata
CHANGED
|
@@ -1,55 +1,69 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_client_builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jayce Higgins
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pry
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '0'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rspec
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '3.7'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '3.7'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rubocop
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.57.2
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.57.2
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
56
|
+
name: simplecov
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
44
58
|
requirements:
|
|
45
|
-
- -
|
|
59
|
+
- - "~>"
|
|
46
60
|
- !ruby/object:Gem::Version
|
|
47
61
|
version: '0'
|
|
48
62
|
type: :development
|
|
49
63
|
prerelease: false
|
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
65
|
requirements:
|
|
52
|
-
- -
|
|
66
|
+
- - "~>"
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '0'
|
|
55
69
|
description: API Client Builder provides an easy to use interface for creating HTTP
|
|
@@ -61,19 +75,18 @@ executables: []
|
|
|
61
75
|
extensions: []
|
|
62
76
|
extra_rdoc_files: []
|
|
63
77
|
files:
|
|
64
|
-
- .
|
|
65
|
-
- .
|
|
66
|
-
- .travis.yml
|
|
67
|
-
- Dockerfile
|
|
78
|
+
- ".gitignore"
|
|
79
|
+
- ".rubocop.yml"
|
|
80
|
+
- ".travis.yml"
|
|
68
81
|
- Gemfile
|
|
82
|
+
- Jenkinsfile
|
|
69
83
|
- LICENSE.txt
|
|
70
84
|
- README.md
|
|
71
|
-
- api_client_builder-1.0.0.gem
|
|
72
85
|
- api_client_builder.gemspec
|
|
73
86
|
- build.sh
|
|
74
|
-
- docker-compose.yml
|
|
75
87
|
- lib/api_client_builder.rb
|
|
76
88
|
- lib/api_client_builder/api_client.rb
|
|
89
|
+
- lib/api_client_builder/delete_request.rb
|
|
77
90
|
- lib/api_client_builder/get_collection_request.rb
|
|
78
91
|
- lib/api_client_builder/get_item_request.rb
|
|
79
92
|
- lib/api_client_builder/post_request.rb
|
|
@@ -83,6 +96,7 @@ files:
|
|
|
83
96
|
- lib/api_client_builder/url_generator.rb
|
|
84
97
|
- lib/api_client_builder/version.rb
|
|
85
98
|
- spec/lib/api_client_builder/api_client_spec.rb
|
|
99
|
+
- spec/lib/api_client_builder/delete_request_spec.rb
|
|
86
100
|
- spec/lib/api_client_builder/get_collection_request_spec.rb
|
|
87
101
|
- spec/lib/api_client_builder/get_item_request_spec.rb
|
|
88
102
|
- spec/lib/api_client_builder/post_request_spec.rb
|
|
@@ -94,7 +108,7 @@ files:
|
|
|
94
108
|
- spec/lib/api_client_builder/test_client/response_handler.rb
|
|
95
109
|
- spec/lib/api_client_builder/url_generator_spec.rb
|
|
96
110
|
- spec/spec_helper.rb
|
|
97
|
-
homepage:
|
|
111
|
+
homepage: https://github.com/instructure/api-client-builder
|
|
98
112
|
licenses:
|
|
99
113
|
- MIT
|
|
100
114
|
metadata: {}
|
|
@@ -104,31 +118,18 @@ require_paths:
|
|
|
104
118
|
- lib
|
|
105
119
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
120
|
requirements:
|
|
107
|
-
- -
|
|
121
|
+
- - ">="
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '2.
|
|
123
|
+
version: '2.3'
|
|
110
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
125
|
requirements:
|
|
112
|
-
- -
|
|
126
|
+
- - ">="
|
|
113
127
|
- !ruby/object:Gem::Version
|
|
114
128
|
version: '0'
|
|
115
129
|
requirements: []
|
|
116
|
-
|
|
117
|
-
rubygems_version: 2.0.14
|
|
130
|
+
rubygems_version: 3.0.3
|
|
118
131
|
signing_key:
|
|
119
132
|
specification_version: 4
|
|
120
133
|
summary: API Client Builder provides an easy to use interface for creating HTTP api
|
|
121
134
|
clients
|
|
122
|
-
test_files:
|
|
123
|
-
- spec/lib/api_client_builder/api_client_spec.rb
|
|
124
|
-
- spec/lib/api_client_builder/get_collection_request_spec.rb
|
|
125
|
-
- spec/lib/api_client_builder/get_item_request_spec.rb
|
|
126
|
-
- spec/lib/api_client_builder/post_request_spec.rb
|
|
127
|
-
- spec/lib/api_client_builder/put_request_spec.rb
|
|
128
|
-
- spec/lib/api_client_builder/request_spec.rb
|
|
129
|
-
- spec/lib/api_client_builder/response_spec.rb
|
|
130
|
-
- spec/lib/api_client_builder/test_client/client.rb
|
|
131
|
-
- spec/lib/api_client_builder/test_client/http_client_handler.rb
|
|
132
|
-
- spec/lib/api_client_builder/test_client/response_handler.rb
|
|
133
|
-
- spec/lib/api_client_builder/url_generator_spec.rb
|
|
134
|
-
- spec/spec_helper.rb
|
|
135
|
+
test_files: []
|
data/.dockerignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Gemfile.lock
|
data/Dockerfile
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
FROM instructure/rvm
|
|
2
|
-
MAINTAINER Instructure
|
|
3
|
-
|
|
4
|
-
COPY Gemfile* *.gemspec /usr/src/app/
|
|
5
|
-
COPY lib/api_client_builder/version.rb /usr/src/app/lib/api_client_builder/
|
|
6
|
-
|
|
7
|
-
USER root
|
|
8
|
-
RUN chown -R docker:docker /usr/src/app
|
|
9
|
-
USER docker
|
|
10
|
-
RUN /bin/bash -l -c "cd /usr/src/app && bundle install"
|
|
11
|
-
|
|
12
|
-
COPY . /usr/src/app
|
|
13
|
-
USER root
|
|
14
|
-
RUN chown -R docker:docker /usr/src/app/*
|
|
15
|
-
USER docker
|
|
16
|
-
CMD /bin/bash -l -c "cd /usr/src/app && pwd && bundle exec wwtd --parallel"
|
|
Binary file
|
data/docker-compose.yml
DELETED