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 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: