pco_api 1.2.0 → 2.0.0
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/README.md +21 -1
- data/lib/pco/api.rb +4 -3
- data/lib/pco/api/endpoint.rb +32 -3
- data/lib/pco/api/errors.rb +3 -1
- data/lib/pco/api/version.rb +1 -1
- data/spec/pco/api/endpoint_spec.rb +44 -0
- data/spec/pco/api_spec.rb +9 -0
- metadata +13 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6e9b56ec41159e601c1afb0b4fa004797f91d0ae845ba396e09617738f98cdea
|
|
4
|
+
data.tar.gz: 931ed18a093f13ba313d6070b2307d888db9d38fe434308f76e6b0b5b0cf9715
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
247
|
+
Copyright Ministry Centered Technologies. Licensed MIT.
|
data/lib/pco/api.rb
CHANGED
data/lib/pco/api/endpoint.rb
CHANGED
|
@@ -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
|
data/lib/pco/api/errors.rb
CHANGED
|
@@ -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
|
data/lib/pco/api/version.rb
CHANGED
|
@@ -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
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|