pco_api 1.2.0 → 2.0.0

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
- SHA1:
3
- metadata.gz: d1dc3d66fb23d3d8a8c5c76b90518ffc7782d797
4
- data.tar.gz: 45679ff9c0d32e87f7f29004295070d85db456df
2
+ SHA256:
3
+ metadata.gz: 6e9b56ec41159e601c1afb0b4fa004797f91d0ae845ba396e09617738f98cdea
4
+ data.tar.gz: 931ed18a093f13ba313d6070b2307d888db9d38fe434308f76e6b0b5b0cf9715
5
5
  SHA512:
6
- metadata.gz: 336847bc5528ccc12b316646a889b72475aad57e8ad27f7eae91e13c53444733517c4bf8564f32719255f1abe30182babe9ac97adf30742f9ddafd2284258f25
7
- data.tar.gz: 9ebecfee7f1c1006047bfc967d28693e0cd6eef8a1fbed23e781b14e8290cd270bd9beb8338c05e8a9e05312f38d8a3bf7077f072d235fc78c91d6ffe14c4001
6
+ metadata.gz: 21f690d99a0ea0896ab49a416d64ae802c7f993a6f63afa725b7e6ab098a2049a6d7aad3e1dc7b0f230eb2db4bb2faceba7760d054b0c8595138708438348a66
7
+ data.tar.gz: cc5b2478ae1de0b2911594483573266d9f3447078b81529c97e9765c209d49af7b9c99f80a14a089d6c92d0c7d5be3e33cde7a17a4d25cfc4ad64e0f0d030e37
data/README.md CHANGED
@@ -42,6 +42,13 @@ gem install pco_api
42
42
  # GET /people/v2/households?order=name
43
43
  ```
44
44
 
45
+ 5. To query dataset according to `can_query_by` variables:
46
+
47
+ ```ruby
48
+ api.people.v2.people.get('where[membership]': 'Member')
49
+ # GET /people/v2/people?where[membership]=Member
50
+ ```
51
+
45
52
  ## Example
46
53
 
47
54
  ```ruby
@@ -203,6 +210,7 @@ The following errors may be raised by the library, depending on the API response
203
210
  | 404 | `PCO::API::Errors::NotFound` < `PCO::API::Errors::ClientError` |
204
211
  | 405 | `PCO::API::Errors::MethodNotAllowed` < `PCO::API::Errors::ClientError` |
205
212
  | 422 | `PCO::API::Errors::UnprocessableEntity` < `PCO::API::Errors::ClientError` |
213
+ | 429 | `PCO::API::Errors::TooManyRequests` < `PCO::API::Errors::ClientError` |
206
214
  | other 4xx errors | `PCO::API::Errors::ClientError` |
207
215
  | 500 | `PCO::API::Errors::InternalServerError` < `PCO::API::Errors::ServerError` |
208
216
  | other 5xx errors | `PCO::API::Errors::ServerError` |
@@ -214,6 +222,7 @@ The exception object has the following methods:
214
222
  | status | HTTP status code returned by the server |
215
223
  | message | the message returned by the API |
216
224
  | detail | the full error response returned by the API |
225
+ | headers | hash of HTTP headers returned by the API |
217
226
 
218
227
  The `message` should be a simple string given by the API, e.g. "Resource Not Found".
219
228
 
@@ -222,6 +231,17 @@ In the case of validation errors, the `message` is a summary string built from t
222
231
  Alternatively, you may rescue `PCO::API::Errors::BaseError` and branch your code based on
223
232
  the status code returned by calling `error.status`.
224
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
+
225
245
  ## Copyright & License
226
246
 
227
- Copyright 2015, Ministry Centered Technologies. Licensed MIT.
247
+ Copyright Ministry Centered Technologies. Licensed MIT.
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
@@ -5,9 +5,15 @@ module PCO
5
5
  module API
6
6
  URL = 'https://api.planningcenteronline.com'
7
7
 
8
+ class Response < Hash
9
+ attr_accessor :headers
10
+ end
11
+
8
12
  class Endpoint
9
13
  attr_reader :url, :last_result
10
14
 
15
+ attr_accessor :retry_when_rate_limited
16
+
11
17
  def initialize(url: URL, oauth_access_token: nil, basic_auth_token: nil, basic_auth_secret: nil, connection: nil)
12
18
  @url = url
13
19
  @oauth_access_token = oauth_access_token
@@ -15,6 +21,7 @@ module PCO
15
21
  @basic_auth_secret = basic_auth_secret
16
22
  @connection = connection || _build_connection
17
23
  @cache = {}
24
+ @retry_when_rate_limited = true
18
25
  end
19
26
 
20
27
  def method_missing(method_name, *_args)
@@ -25,7 +32,7 @@ module PCO
25
32
  _build_endpoint(id.to_s)
26
33
  end
27
34
 
28
- def respond_to?(method_name)
35
+ def respond_to?(method_name, _include_all = false)
29
36
  endpoint = _build_endpoint(method_name.to_s)
30
37
  begin
31
38
  endpoint.get
@@ -39,6 +46,8 @@ module PCO
39
46
  def get(params = {})
40
47
  @last_result = @connection.get(@url, params)
41
48
  _build_response(@last_result)
49
+ rescue Errors::TooManyRequests => e
50
+ _retry_after_timeout?(e) ? retry : raise
42
51
  end
43
52
 
44
53
  def post(body = {})
@@ -46,6 +55,8 @@ module PCO
46
55
  req.body = _build_body(body)
47
56
  end
48
57
  _build_response(@last_result)
58
+ rescue Errors::TooManyRequests => e
59
+ _retry_after_timeout?(e) ? retry : raise
49
60
  end
50
61
 
51
62
  def patch(body = {})
@@ -53,6 +64,8 @@ module PCO
53
64
  req.body = _build_body(body)
54
65
  end
55
66
  _build_response(@last_result)
67
+ rescue Errors::TooManyRequests => e
68
+ _retry_after_timeout?(e) ? retry : raise
56
69
  end
57
70
 
58
71
  def delete
@@ -62,6 +75,8 @@ module PCO
62
75
  else
63
76
  _build_response(@last_result)
64
77
  end
78
+ rescue Errors::TooManyRequests => e
79
+ _retry_after_timeout?(e) ? retry : raise
65
80
  end
66
81
 
67
82
  private
@@ -69,7 +84,9 @@ module PCO
69
84
  def _build_response(result)
70
85
  case result.status
71
86
  when 200..299
72
- result.body
87
+ res = Response[result.body]
88
+ res.headers = result.headers
89
+ res
73
90
  when 400
74
91
  fail Errors::BadRequest, result
75
92
  when 401
@@ -82,6 +99,8 @@ module PCO
82
99
  fail Errors::MethodNotAllowed, result
83
100
  when 422
84
101
  fail Errors::UnprocessableEntity, result
102
+ when 429
103
+ fail Errors::TooManyRequests, result
85
104
  when 400..499
86
105
  fail Errors::ClientError, result
87
106
  when 500
@@ -116,7 +135,6 @@ module PCO
116
135
 
117
136
  def _build_connection
118
137
  Faraday.new(url: url) do |faraday|
119
- faraday.adapter :excon
120
138
  faraday.response :json, content_type: /\bjson$/
121
139
  if @basic_auth_token && @basic_auth_secret
122
140
  faraday.basic_auth @basic_auth_token, @basic_auth_secret
@@ -125,6 +143,17 @@ module PCO
125
143
  else
126
144
  fail Errors::AuthRequiredError, "You must specify either HTTP basic auth credentials or an OAuth2 access token."
127
145
  end
146
+ faraday.adapter :excon
147
+ end
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
128
157
  end
129
158
  end
130
159
  end
@@ -4,11 +4,12 @@ module PCO
4
4
  class AuthRequiredError < StandardError; end
5
5
 
6
6
  class BaseError < StandardError
7
- attr_reader :status, :detail
7
+ attr_reader :status, :detail, :headers
8
8
 
9
9
  def initialize(response)
10
10
  @status = response.status
11
11
  @detail = response.body
12
+ @headers = response.headers
12
13
  end
13
14
 
14
15
  def to_s
@@ -52,6 +53,7 @@ module PCO
52
53
  class NotFound < ClientError; end # 404
53
54
  class MethodNotAllowed < ClientError; end # 405
54
55
  class UnprocessableEntity < ClientError; end # 422
56
+ class TooManyRequests < ClientError; end # 429
55
57
 
56
58
  class ServerError < BaseError; end # 500..599
57
59
  class InternalServerError < ServerError; end # 500
@@ -1,5 +1,5 @@
1
1
  module PCO
2
2
  module API
3
- VERSION = '1.2.0'
3
+ VERSION = '2.0.0'
4
4
  end
5
5
  end
@@ -50,6 +50,7 @@ describe PCO::API::Endpoint do
50
50
  it 'returns the result of making a GET request to the endpoint' do
51
51
  expect(@result).to be_a(Hash)
52
52
  expect(@result['data']).to eq(result)
53
+ expect(@result.headers).to eq('Content-Type' => 'application/vnd.api+json')
53
54
  end
54
55
  end
55
56
 
@@ -76,6 +77,7 @@ describe PCO::API::Endpoint do
76
77
  end
77
78
  expect(error.status).to eq(404)
78
79
  expect(error.message).to eq('Resource Not Found')
80
+ expect(error.headers).to eq('Content-Type' => 'application/vnd.api+json')
79
81
  end
80
82
  end
81
83
 
@@ -122,6 +124,48 @@ describe PCO::API::Endpoint do
122
124
  }.to raise_error(PCO::API::Errors::ServerError)
123
125
  end
124
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
125
169
  end
126
170
 
127
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.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Planning Center Online
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-23 00:00:00.000000000 Z
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.9.1
19
+ version: '0.10'
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.9.1
26
+ version: '0.10'
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.9.1
33
+ version: '0.10'
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.9.1
40
+ version: '0.10'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: excon
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.45.3
47
+ version: 0.71.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.45.3
54
+ version: 0.71.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +109,7 @@ 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:
@@ -129,12 +130,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
130
  - !ruby/object:Gem::Version
130
131
  version: '0'
131
132
  requirements: []
132
- rubyforge_project:
133
- rubygems_version: 2.4.5
133
+ rubygems_version: 3.0.3
134
134
  signing_key:
135
135
  specification_version: 4
136
136
  summary: API wrapper for api.planningcenteronline.com
137
137
  test_files:
138
+ - spec/pco/api_spec.rb
138
139
  - spec/pco/api/endpoint_spec.rb
139
140
  - spec/spec_helper.rb
140
- has_rdoc: