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.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +34 -0
  4. data/.travis.yml +20 -0
  5. data/Gemfile +6 -0
  6. data/Jenkinsfile +13 -0
  7. data/LICENSE.txt +22 -0
  8. data/{readme.md → README.md} +65 -50
  9. data/api_client_builder.gemspec +15 -13
  10. data/build.sh +16 -0
  11. data/lib/api_client_builder.rb +14 -0
  12. data/lib/api_client_builder/api_client.rb +96 -0
  13. data/lib/api_client_builder/delete_request.rb +19 -0
  14. data/lib/api_client_builder/get_collection_request.rb +43 -0
  15. data/lib/api_client_builder/get_item_request.rb +27 -0
  16. data/lib/api_client_builder/post_request.rb +19 -0
  17. data/lib/api_client_builder/put_request.rb +19 -0
  18. data/lib/api_client_builder/request.rb +66 -0
  19. data/lib/api_client_builder/response.rb +32 -0
  20. data/lib/api_client_builder/url_generator.rb +37 -0
  21. data/lib/api_client_builder/version.rb +3 -0
  22. data/spec/lib/api_client_builder/api_client_spec.rb +13 -4
  23. data/spec/lib/api_client_builder/delete_request_spec.rb +29 -0
  24. data/spec/lib/api_client_builder/get_collection_request_spec.rb +7 -10
  25. data/spec/lib/api_client_builder/get_item_request_spec.rb +4 -6
  26. data/spec/lib/api_client_builder/post_request_spec.rb +3 -5
  27. data/spec/lib/api_client_builder/put_request_spec.rb +3 -5
  28. data/spec/lib/api_client_builder/request_spec.rb +7 -9
  29. data/spec/lib/api_client_builder/response_spec.rb +1 -2
  30. data/spec/lib/api_client_builder/test_client/client.rb +3 -3
  31. data/spec/lib/api_client_builder/test_client/http_client_handler.rb +1 -2
  32. data/spec/lib/api_client_builder/test_client/response_handler.rb +13 -14
  33. data/spec/lib/api_client_builder/url_generator_spec.rb +29 -7
  34. data/spec/spec_helper.rb +8 -2
  35. metadata +51 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6a42589201e1aa43a64afa11738378eca1182f7a
4
- data.tar.gz: 6c5fea2364dc9e85db7241ea02f9a8d66dced538
2
+ SHA256:
3
+ metadata.gz: 61488aa64b23c4e971039af08754ac43a70277fe13cdb14ec5cc95cdc2cb4c1a
4
+ data.tar.gz: 1a14eaed9a3b913023c76e7b9788a3f8f39dea98749d299283eb5fe11d2a9d96
5
5
  SHA512:
6
- metadata.gz: e6362629e413db828fdfefc719be94de0c10a23a099edf33e136a4a9e2cb7394d7087eb999844a324d89e1b710ddeeb4570914a990a683490bc7659b51f16b17
7
- data.tar.gz: f201de0c2a4c149e2958cc772260f9fa0c17c1bdd29c102d3f370043427b647e229342d30a24053eccb88794c58c1656d079f50ff2903190bf85c1a99bd8e66a
6
+ metadata.gz: 6b0431c58dff83f80caffcd891a7b984982143cc829403b1b49b97889570a452ea6df1d3b216fc1d2d76e68599d2ba1f3add4101eaca2402727e3d9ddbb274ec
7
+ data.tar.gz: b717e8415fc64ffbe9de0eb2c8086c282d87b943577e1e4d5bf6e84b620594ad6de9b3ac0e89a6ac3e79c570ff4cd72d08be854ffa8dc4d819ca843d4b17be42
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /coverage
2
+ /Gemfile.lock
3
+ /*.gem
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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Declare your gem's dependencies in logging.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
data/Jenkinsfile ADDED
@@ -0,0 +1,13 @@
1
+ pipeline {
2
+ agent {
3
+ label 'docker'
4
+ }
5
+
6
+ stages {
7
+ stage('Lint & Test') {
8
+ steps {
9
+ sh './build.sh'
10
+ }
11
+ }
12
+ }
13
+ }
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.
@@ -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 http requests and responses.
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
- get :some_object, :singular, 'some_objects/:id'
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
- get :some_objects, :collection, 'some_objects'
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 http requests themselves. Since
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 http client handler requires '#get', '#post', and '#put' to be defined here
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
- #Define a client to use here. The HTTPClient gem is a good option
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
- def initialize(http_client_handler, start_url, type)
223
- @http_client = http_client_handler
224
- @start_url = start_url
225
- @type = type
226
- end
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 add
243
- # whatever else is necessary to the route
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 paginated
247
- # response will have. Set that here. `@max_pages`
248
- #Be sure to set the current page count as well -- @current_page
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 add
262
- # whatever else is necessary to the route
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
- return false if @current_page > @max_pages
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 add
282
- # whatever else is necessary to the route
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 add
296
- # whatever else is necessary to the route
297
- #Also send the body if thats how the client handler is configured
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
- return false
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
- # The builder will reset them when it no longer is retrying by calling this
320
- # method
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 add
329
- # whatever else is necessary to the route
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 http response
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 http response.
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
@@ -1,23 +1,25 @@
1
- # -*- encoding: utf-8 -*-
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 = "api_client_builder"
6
- gem.summary = "API Client Builder provides an easy to use interface for creating HTTP api clients"
7
- gem.description = "API Client Builder provides an easy to use interface for creating HTTP api clients"
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.0'
12
+ gem.required_ruby_version = '>= 2.3'
17
13
 
18
- gem.license = 'MIT'
14
+ gem.license = 'MIT'
19
15
 
20
16
  gem.add_development_dependency 'pry'
21
- gem.add_development_dependency 'rspec'
22
- gem.add_development_dependency 'wwtd'
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'