pco_api 1.3.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb681a310a78eeb3ac455edc7ab2b4696a246e74a54077b1f0c5ce26e9a31c10
4
- data.tar.gz: fe3398a9fd3c50f784e139fd65879a31cdf56d210a97e3e46e5f66309058b0f6
3
+ metadata.gz: 80591277ecc37d298f7a7c68c35d32cd9f5d117f401ee6c811a417aab5b9e494
4
+ data.tar.gz: 756f395bb87741e0786452d1391a47c08bc3a36df064f8fa6d368706e49128de
5
5
  SHA512:
6
- metadata.gz: 41d1955114ffe087506d63c3907ebad24f9c0a34e9e3784d620bc2be8080bf309b3eb0fbed52a60b5805e1e2cf0ccfe07d6adcc0072a48f78336461d9e7ca14b
7
- data.tar.gz: 1ead2832a8c2f62c13845b5c7c28254494efdbdedf404b6e70ccbd9791fd12ee9932acfad3ed9255dc9d3451eaf4dd4841cb2a1ed99721304a15fc5260c1301d
6
+ metadata.gz: a64b77f5502cb0e7a83b3841596002402ed6484738185f0397d4bd9fccd451c3b3494e95a7e9c13a22f9123e20509c484b77528db19a2b972e8bfc61db43ff3d
7
+ data.tar.gz: a7c05f672220939fc491e20e334ba5511173ac4ed50e3bf3969530d7f1556cd7ed8147b0172dc1d727ed9696e134c32ab4a7cb3564585cfd96abea2743a8ff26
data/README.md CHANGED
@@ -210,6 +210,7 @@ The following errors may be raised by the library, depending on the API response
210
210
  | 404 | `PCO::API::Errors::NotFound` < `PCO::API::Errors::ClientError` |
211
211
  | 405 | `PCO::API::Errors::MethodNotAllowed` < `PCO::API::Errors::ClientError` |
212
212
  | 422 | `PCO::API::Errors::UnprocessableEntity` < `PCO::API::Errors::ClientError` |
213
+ | 429 | `PCO::API::Errors::TooManyRequests` < `PCO::API::Errors::ClientError` |
213
214
  | other 4xx errors | `PCO::API::Errors::ClientError` |
214
215
  | 500 | `PCO::API::Errors::InternalServerError` < `PCO::API::Errors::ServerError` |
215
216
  | other 5xx errors | `PCO::API::Errors::ServerError` |
@@ -221,6 +222,7 @@ The exception object has the following methods:
221
222
  | status | HTTP status code returned by the server |
222
223
  | message | the message returned by the API |
223
224
  | detail | the full error response returned by the API |
225
+ | headers | hash of HTTP headers returned by the API |
224
226
 
225
227
  The `message` should be a simple string given by the API, e.g. "Resource Not Found".
226
228
 
@@ -229,6 +231,17 @@ In the case of validation errors, the `message` is a summary string built from t
229
231
  Alternatively, you may rescue `PCO::API::Errors::BaseError` and branch your code based on
230
232
  the status code returned by calling `error.status`.
231
233
 
234
+ ### TooManyRequests Error
235
+
236
+ By default, PCO::API::Endpoint will sleep and retry a request that fails with TooManyRequests due
237
+ to rate limiting. If you would rather catch and handle such errors yourself, you can disable this
238
+ behavior like this:
239
+
240
+ ```ruby
241
+ api = PCO::API.new(...)
242
+ api.retry_when_rate_limited = false
243
+ ```
244
+
232
245
  ## Copyright & License
233
246
 
234
247
  Copyright Ministry Centered Technologies. Licensed MIT.
@@ -12,6 +12,8 @@ module PCO
12
12
  class Endpoint
13
13
  attr_reader :url, :last_result
14
14
 
15
+ attr_accessor :retry_when_rate_limited
16
+
15
17
  def initialize(url: URL, oauth_access_token: nil, basic_auth_token: nil, basic_auth_secret: nil, connection: nil)
16
18
  @url = url
17
19
  @oauth_access_token = oauth_access_token
@@ -19,6 +21,7 @@ module PCO
19
21
  @basic_auth_secret = basic_auth_secret
20
22
  @connection = connection || _build_connection
21
23
  @cache = {}
24
+ @retry_when_rate_limited = true
22
25
  end
23
26
 
24
27
  def method_missing(method_name, *_args)
@@ -29,7 +32,7 @@ module PCO
29
32
  _build_endpoint(id.to_s)
30
33
  end
31
34
 
32
- def respond_to?(method_name)
35
+ def respond_to?(method_name, _include_all = false)
33
36
  endpoint = _build_endpoint(method_name.to_s)
34
37
  begin
35
38
  endpoint.get
@@ -43,6 +46,8 @@ module PCO
43
46
  def get(params = {})
44
47
  @last_result = @connection.get(@url, params)
45
48
  _build_response(@last_result)
49
+ rescue Errors::TooManyRequests => e
50
+ _retry_after_timeout?(e) ? retry : raise
46
51
  end
47
52
 
48
53
  def post(body = {})
@@ -50,6 +55,8 @@ module PCO
50
55
  req.body = _build_body(body)
51
56
  end
52
57
  _build_response(@last_result)
58
+ rescue Errors::TooManyRequests => e
59
+ _retry_after_timeout?(e) ? retry : raise
53
60
  end
54
61
 
55
62
  def patch(body = {})
@@ -57,6 +64,8 @@ module PCO
57
64
  req.body = _build_body(body)
58
65
  end
59
66
  _build_response(@last_result)
67
+ rescue Errors::TooManyRequests => e
68
+ _retry_after_timeout?(e) ? retry : raise
60
69
  end
61
70
 
62
71
  def delete
@@ -66,6 +75,8 @@ module PCO
66
75
  else
67
76
  _build_response(@last_result)
68
77
  end
78
+ rescue Errors::TooManyRequests => e
79
+ _retry_after_timeout?(e) ? retry : raise
69
80
  end
70
81
 
71
82
  private
@@ -126,15 +137,25 @@ module PCO
126
137
  Faraday.new(url: url) do |faraday|
127
138
  faraday.response :json, content_type: /\bjson$/
128
139
  if @basic_auth_token && @basic_auth_secret
129
- faraday.basic_auth @basic_auth_token, @basic_auth_secret
140
+ faraday.request :basic_auth, @basic_auth_token, @basic_auth_secret
130
141
  elsif @oauth_access_token
131
- faraday.headers['Authorization'] = "Bearer #{@oauth_access_token}"
142
+ faraday.request :authorization, 'Bearer', @oauth_access_token
132
143
  else
133
144
  fail Errors::AuthRequiredError, "You must specify either HTTP basic auth credentials or an OAuth2 access token."
134
145
  end
135
146
  faraday.adapter :excon
136
147
  end
137
148
  end
149
+
150
+ def _retry_after_timeout?(e)
151
+ if @retry_when_rate_limited
152
+ secs = e.headers['Retry-After']
153
+ Kernel.sleep(secs ? secs.to_i : 1)
154
+ true
155
+ else
156
+ false
157
+ end
158
+ end
138
159
  end
139
160
  end
140
- end
161
+ end
@@ -1,5 +1,5 @@
1
1
  module PCO
2
2
  module API
3
- VERSION = '1.3.0'
3
+ VERSION = '2.0.1'
4
4
  end
5
5
  end
data/lib/pco/api.rb CHANGED
@@ -3,9 +3,10 @@ require_relative 'api/errors'
3
3
 
4
4
  module PCO
5
5
  module API
6
- module_function
7
- def new(*args)
8
- Endpoint.new(*args)
6
+ class << self
7
+ def new(**args)
8
+ Endpoint.new(**args)
9
+ end
9
10
  end
10
11
  end
11
12
  end
@@ -124,6 +124,48 @@ describe PCO::API::Endpoint do
124
124
  }.to raise_error(PCO::API::Errors::ServerError)
125
125
  end
126
126
  end
127
+
128
+ context 'given a 429 error due to rate limiting' do
129
+ subject { base.people.v2 }
130
+
131
+ let(:result) do
132
+ {
133
+ 'type' => 'Organization',
134
+ 'id' => '1',
135
+ 'name' => 'Ministry Centered Technologies',
136
+ 'links' => {}
137
+ }
138
+ end
139
+
140
+ before do
141
+ stub_request(:get, 'https://api.planningcenteronline.com/people/v2')
142
+ .to_return([
143
+ { status: 429, headers: { 'retry-after' => '2' } },
144
+ { status: 200, body: { data: result }.to_json, headers: { 'Content-Type' => 'application/vnd.api+json' } }
145
+ ])
146
+ end
147
+
148
+ context 'given retry_when_rate_limited is true' do
149
+ before do
150
+ subject.retry_when_rate_limited = true
151
+ end
152
+
153
+ it 'sleeps, then makes the call again' do
154
+ expect(Kernel).to receive(:sleep).with(2)
155
+ expect(subject.get).to be_a(Hash)
156
+ end
157
+ end
158
+
159
+ context 'given retry_when_rate_limited is false' do
160
+ before do
161
+ subject.retry_when_rate_limited = false
162
+ end
163
+
164
+ it 'raises the TooManyRequests error' do
165
+ expect { subject.get }.to raise_error(PCO::API::Errors::TooManyRequests)
166
+ end
167
+ end
168
+ end
127
169
  end
128
170
 
129
171
  describe '#post' do
@@ -0,0 +1,9 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe PCO::API do
4
+ describe '.new' do
5
+ it 'creates a new Endpoint instance' do
6
+ expect(described_class.new(basic_auth_token: 'abc123', basic_auth_secret: 'xyz789').class).to eq(PCO::API::Endpoint)
7
+ end
8
+ end
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pco_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Planning Center Online
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-21 00:00:00.000000000 Z
11
+ date: 2022-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.10'
19
+ version: '1.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.10'
26
+ version: '1.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faraday_middleware
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.10'
33
+ version: '1.2'
34
34
  type: :runtime
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: '0.10'
40
+ version: '1.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: excon
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -109,12 +109,13 @@ files:
109
109
  - lib/pco/api/version.rb
110
110
  - lib/pco_api.rb
111
111
  - spec/pco/api/endpoint_spec.rb
112
+ - spec/pco/api_spec.rb
112
113
  - spec/spec_helper.rb
113
114
  homepage: https://github.com/planningcenter/pco_api_ruby
114
115
  licenses:
115
116
  - MIT
116
117
  metadata: {}
117
- post_install_message:
118
+ post_install_message:
118
119
  rdoc_options: []
119
120
  require_paths:
120
121
  - lib
@@ -129,10 +130,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
130
  - !ruby/object:Gem::Version
130
131
  version: '0'
131
132
  requirements: []
132
- rubygems_version: 3.0.3
133
- signing_key:
133
+ rubygems_version: 3.0.3.1
134
+ signing_key:
134
135
  specification_version: 4
135
136
  summary: API wrapper for api.planningcenteronline.com
136
137
  test_files:
138
+ - spec/pco/api_spec.rb
137
139
  - spec/pco/api/endpoint_spec.rb
138
140
  - spec/spec_helper.rb