koala 3.1.0 → 3.2.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 +4 -4
- data/Gemfile +1 -1
- data/changelog.md +13 -1
- data/lib/koala/api/graph_error_checker.rb +1 -1
- data/lib/koala/api.rb +16 -2
- data/lib/koala/configuration.rb +3 -0
- data/lib/koala/errors.rb +21 -1
- data/lib/koala/version.rb +1 -1
- data/readme.md +31 -0
- data/spec/cases/api_spec.rb +77 -0
- data/spec/cases/error_spec.rb +16 -3
- data/spec/cases/graph_api_batch_spec.rb +1 -1
- data/spec/cases/graph_error_checker_spec.rb +19 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f879bff1a0797804e8a89a2d8d34366ba21fafedd5317b4649326169dc9b7ed
|
4
|
+
data.tar.gz: 8f77e5f1d2f3eebcf5e08983ba4f2af69b64566d7d2d4dac6185a8b5c3222ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 704dee302c62d906fd9f065abc86ea0e9268a4b160460ccfb455f8a7dbd3d41631bacf75e155953e5fd9d54e01fa81abc39f7730db4ea1023893b80dd378aa22
|
7
|
+
data.tar.gz: 38c9e731f1a76e3d441c3e783032a8f306168723e1552e00167dcd0bd5ad696833afffd2797ff4f4e0ed97e986578f08533b58f5064fa0610819fb409d10ddd3
|
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ group :development, :test do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
group :test do
|
16
|
-
gem "rspec",
|
16
|
+
gem "rspec", "~> 3.0", "< 3.10" # resrict rspec version until https://github.com/rspec/rspec-support/pull/537 gets merged
|
17
17
|
gem "vcr", github: 'vcr/vcr', ref: '8ced6c96e01737a418cd270e0382a8c2c6d85f7f' # needs https://github.com/vcr/vcr/pull/907 for ruby 3.1
|
18
18
|
gem "webmock"
|
19
19
|
gem "simplecov"
|
data/changelog.md
CHANGED
@@ -15,7 +15,19 @@ Testing improvements:
|
|
15
15
|
|
16
16
|
Others:
|
17
17
|
|
18
|
-
v3.
|
18
|
+
v3.2.0 (2022-05-27)
|
19
|
+
======
|
20
|
+
|
21
|
+
New features:
|
22
|
+
|
23
|
+
* Exposes limiting headers(`x-business-use-case-usage, x-ad-account-usage, x-app-usage`) to APIError ([#668](https://github.com/arsduo/koala/pull/668))
|
24
|
+
* Add `rate_limit_hook` configuration to get rate limiting headers (`x-business-use-case-usage, x-ad-account-usage, x-app-usage`) ([#670](https://github.com/arsduo/koala/pull/670))
|
25
|
+
|
26
|
+
Testing improvements:
|
27
|
+
|
28
|
+
* Fix builds for ruby 3.x
|
29
|
+
|
30
|
+
v3.1.0 (2022-01-18)
|
19
31
|
======
|
20
32
|
|
21
33
|
New features:
|
@@ -17,7 +17,7 @@ module Koala
|
|
17
17
|
|
18
18
|
# Facebook can return debug information in the response headers -- see
|
19
19
|
# https://developers.facebook.com/docs/graph-api/using-graph-api#bugdebug
|
20
|
-
DEBUG_HEADERS = [
|
20
|
+
DEBUG_HEADERS = %w[x-fb-debug x-fb-rev x-fb-trace-id x-business-use-case-usage x-ad-account-usage x-app-usage]
|
21
21
|
|
22
22
|
def error_if_appropriate
|
23
23
|
if http_status >= 400
|
data/lib/koala/api.rb
CHANGED
@@ -13,14 +13,16 @@ module Koala
|
|
13
13
|
# signed by default, unless you pass appsecret_proof:
|
14
14
|
# false as an option to the API call. (See
|
15
15
|
# https://developers.facebook.com/docs/graph-api/securing-requests/)
|
16
|
+
# @param [Block] rate_limit_hook block called with limits received in facebook response headers
|
16
17
|
# @note If no access token is provided, you can only access some public information.
|
17
18
|
# @return [Koala::Facebook::API] the API client
|
18
|
-
def initialize(access_token = Koala.config.access_token, app_secret = Koala.config.app_secret)
|
19
|
+
def initialize(access_token = Koala.config.access_token, app_secret = Koala.config.app_secret, rate_limit_hook = Koala.config.rate_limit_hook)
|
19
20
|
@access_token = access_token
|
20
21
|
@app_secret = app_secret
|
22
|
+
@rate_limit_hook = rate_limit_hook
|
21
23
|
end
|
22
24
|
|
23
|
-
attr_reader :access_token, :app_secret
|
25
|
+
attr_reader :access_token, :app_secret, :rate_limit_hook
|
24
26
|
|
25
27
|
include GraphAPIMethods
|
26
28
|
|
@@ -58,6 +60,18 @@ module Koala
|
|
58
60
|
API::GraphCollection.evaluate(response, self)
|
59
61
|
end
|
60
62
|
|
63
|
+
if rate_limit_hook
|
64
|
+
limits = %w(x-business-use-case-usage x-ad-account-usage x-app-usage).each_with_object({}) do |key, hash|
|
65
|
+
value = response.headers.fetch(key, nil)
|
66
|
+
next unless value
|
67
|
+
hash[key] = JSON.parse(response.headers[key])
|
68
|
+
rescue JSON::ParserError => e
|
69
|
+
Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{key} = #{value}")
|
70
|
+
end
|
71
|
+
|
72
|
+
rate_limit_hook.call(limits) if limits.keys.any?
|
73
|
+
end
|
74
|
+
|
61
75
|
# now process as appropriate for the given call (get picture header, etc.)
|
62
76
|
post_processing ? post_processing.call(desired_data) : desired_data
|
63
77
|
end
|
data/lib/koala/configuration.rb
CHANGED
@@ -31,6 +31,9 @@ class Koala::Configuration
|
|
31
31
|
# Whether or not to mask tokens
|
32
32
|
attr_accessor :mask_tokens
|
33
33
|
|
34
|
+
# Called with the info for the rate limits in the response header
|
35
|
+
attr_accessor :rate_limit_hook
|
36
|
+
|
34
37
|
# Certain Facebook services (beta, video) require you to access different
|
35
38
|
# servers. If you're using your own servers, for instance, for a proxy,
|
36
39
|
# you can change both the matcher (what value to change when updating the URL) and the
|
data/lib/koala/errors.rb
CHANGED
@@ -23,7 +23,10 @@ module Koala
|
|
23
23
|
:fb_error_user_title,
|
24
24
|
:fb_error_trace_id,
|
25
25
|
:fb_error_debug,
|
26
|
-
:fb_error_rev
|
26
|
+
:fb_error_rev,
|
27
|
+
:fb_buc_usage,
|
28
|
+
:fb_ada_usage,
|
29
|
+
:fb_app_usage
|
27
30
|
|
28
31
|
# Create a new API Error
|
29
32
|
#
|
@@ -66,6 +69,9 @@ module Koala
|
|
66
69
|
self.fb_error_trace_id = error_info["x-fb-trace-id"]
|
67
70
|
self.fb_error_debug = error_info["x-fb-debug"]
|
68
71
|
self.fb_error_rev = error_info["x-fb-rev"]
|
72
|
+
self.fb_buc_usage = json_parse_for(error_info, "x-business-use-case-usage")
|
73
|
+
self.fb_ada_usage = json_parse_for(error_info, "x-ad-account-usage")
|
74
|
+
self.fb_app_usage = json_parse_for(error_info, "x-app-usage")
|
69
75
|
|
70
76
|
error_array = []
|
71
77
|
%w(type code error_subcode message error_user_title error_user_msg x-fb-trace-id).each do |key|
|
@@ -82,6 +88,20 @@ module Koala
|
|
82
88
|
|
83
89
|
super(message)
|
84
90
|
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# refs: https://developers.facebook.com/docs/graph-api/overview/rate-limiting/#headers
|
95
|
+
# NOTE: The header will contain a JSON-formatted string that describes current application rate limit usage.
|
96
|
+
def json_parse_for(error_info, key)
|
97
|
+
string = error_info[key]
|
98
|
+
return if string.nil?
|
99
|
+
|
100
|
+
JSON.parse(string)
|
101
|
+
rescue JSON::ParserError => e
|
102
|
+
Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{key} = #{string}")
|
103
|
+
nil
|
104
|
+
end
|
85
105
|
end
|
86
106
|
|
87
107
|
# Facebook returned an invalid response body
|
data/lib/koala/version.rb
CHANGED
data/readme.md
CHANGED
@@ -177,6 +177,37 @@ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
|
|
177
177
|
```
|
178
178
|
For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
|
179
179
|
|
180
|
+
Rate limits
|
181
|
+
-----------
|
182
|
+
|
183
|
+
We support Facebook rate limit informations as defined here: [https://developers.facebook.com/docs/graph-api/overview/rate-limiting/](https://developers.facebook.com/docs/graph-api/overview/rate-limiting/)
|
184
|
+
|
185
|
+
The information is available either via the `Facebook::APIError`:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
error.fb_buc_usage
|
189
|
+
error.fb_ada_usage
|
190
|
+
error.fb_app_usage
|
191
|
+
```
|
192
|
+
|
193
|
+
Or with the rate_limit_hook:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
# App level configuration
|
197
|
+
|
198
|
+
Koala.configure do |config|
|
199
|
+
config.rate_limit_hook = ->(limits) {
|
200
|
+
limits["x-app-usage"] # {"call_count"=>0, "total_cputime"=>0, "total_time"=>0}
|
201
|
+
limits["x-ad-account-usage"] # {"acc_id_util_pct"=>9.67}
|
202
|
+
limits["x-business-use-case-usage"] # {"123456789012345"=>[{"type"=>"messenger", "call_count"=>1, "total_cputime"=>1, "total_time"=>1, "estimated_time_to_regain_access"=>0}]}
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
# Per API configuration
|
207
|
+
|
208
|
+
Koala::Facebook::API.new('', '', ->(limits) {})
|
209
|
+
```
|
210
|
+
|
180
211
|
Test Users
|
181
212
|
----------
|
182
213
|
|
data/spec/cases/api_spec.rb
CHANGED
@@ -282,4 +282,81 @@ describe "Koala::Facebook::API" do
|
|
282
282
|
expect(@service.graph_call('anything')).to be_falsey
|
283
283
|
end
|
284
284
|
end
|
285
|
+
|
286
|
+
describe "Rate limit hook" do
|
287
|
+
it "is called when x-business-use-case-usage header is present" do
|
288
|
+
api = Koala::Facebook::API.new('', '', ->(limits) {
|
289
|
+
expect(limits["x-business-use-case-usage"]).to eq({"123456789012345"=>[{"type"=>"messenger", "call_count"=>1, "total_cputime"=>1, "total_time"=>1, "estimated_time_to_regain_access"=>0}]})
|
290
|
+
})
|
291
|
+
|
292
|
+
result = {"a" => 2}
|
293
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, { "x-business-use-case-usage" => "{\"123456789012345\":[{\"type\":\"messenger\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}" })
|
294
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
295
|
+
|
296
|
+
api.graph_call('anything')
|
297
|
+
end
|
298
|
+
|
299
|
+
it "is called when x-ad-account-usage header is present" do
|
300
|
+
api = Koala::Facebook::API.new('', '', ->(limits) {
|
301
|
+
expect(limits["x-ad-account-usage"]).to eq({"acc_id_util_pct"=>9.67})
|
302
|
+
})
|
303
|
+
|
304
|
+
result = {"a" => 2}
|
305
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, { "x-ad-account-usage" => "{\"acc_id_util_pct\":9.67}" })
|
306
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
307
|
+
|
308
|
+
api.graph_call('anything')
|
309
|
+
end
|
310
|
+
|
311
|
+
it "is called when x-app-usage header is present" do
|
312
|
+
api = Koala::Facebook::API.new('', '', ->(limits) {
|
313
|
+
expect(limits["x-app-usage"]).to eq({"call_count"=>0, "total_cputime"=>0, "total_time"=>0})
|
314
|
+
})
|
315
|
+
|
316
|
+
result = {"a" => 2}
|
317
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, { "x-app-usage" => "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}" })
|
318
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
319
|
+
|
320
|
+
api.graph_call('anything')
|
321
|
+
end
|
322
|
+
|
323
|
+
it "isn't called if none of the rate limit header is present" do
|
324
|
+
rate_limit_hook_called = false
|
325
|
+
|
326
|
+
api = Koala::Facebook::API.new('', '', ->(limits) {
|
327
|
+
rate_limit_hook_called = true
|
328
|
+
})
|
329
|
+
|
330
|
+
result = {"a" => 2}
|
331
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, {})
|
332
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
333
|
+
|
334
|
+
api.graph_call('anything')
|
335
|
+
|
336
|
+
expect(rate_limit_hook_called).to be(false)
|
337
|
+
end
|
338
|
+
|
339
|
+
it "isn't called if no rate limit hook is defined" do
|
340
|
+
api = Koala::Facebook::API.new('', '', ->(limits) {
|
341
|
+
#noop
|
342
|
+
})
|
343
|
+
|
344
|
+
result = {"a" => 2}
|
345
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, { "x-ad-account-usage" => "{\"acc_id_util_pct\"9.67}"})
|
346
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
347
|
+
|
348
|
+
expect(Koala::Utils.logger).to receive(:error).with("JSON::ParserError: 859: unexpected token at '{\"acc_id_util_pct\"9.67}' while parsing x-ad-account-usage = {\"acc_id_util_pct\"9.67}")
|
349
|
+
api.graph_call('anything')
|
350
|
+
end
|
351
|
+
|
352
|
+
it "logs an error if the rate limit header can't be properly parsed" do
|
353
|
+
api = Koala::Facebook::API.new('', '', nil)
|
354
|
+
|
355
|
+
result = {"a" => 2}
|
356
|
+
response = Koala::HTTPService::Response.new(200, result.to_json, {})
|
357
|
+
allow(Koala).to receive(:make_request).and_return(response)
|
358
|
+
|
359
|
+
api.graph_call('anything')
|
360
|
+
end
|
361
|
+
end
|
285
362
|
end
|
data/spec/cases/error_spec.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
BUC_USAGE_JSON = "{\"123456789012345\":[{\"type\":\"messenger\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}"
|
4
|
+
ADA_USAGE_JSON = "{\"acc_id_util_pct\":9.67}"
|
5
|
+
APP_USAGE_JSON = "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}"
|
6
|
+
|
3
7
|
describe Koala::Facebook::APIError do
|
4
8
|
it "is a Koala::KoalaError" do
|
5
9
|
expect(Koala::Facebook::APIError.new(nil, nil)).to be_a(Koala::KoalaError)
|
@@ -32,7 +36,10 @@ describe Koala::Facebook::APIError do
|
|
32
36
|
'error_user_title' => 'error user title',
|
33
37
|
'x-fb-trace-id' => 'fb trace id',
|
34
38
|
'x-fb-debug' => 'fb debug token',
|
35
|
-
'x-fb-rev' => 'fb revision'
|
39
|
+
'x-fb-rev' => 'fb revision',
|
40
|
+
'x-business-use-case-usage' => BUC_USAGE_JSON,
|
41
|
+
'x-ad-account-usage' => ADA_USAGE_JSON,
|
42
|
+
'x-app-usage' => APP_USAGE_JSON
|
36
43
|
}
|
37
44
|
Koala::Facebook::APIError.new(400, '', error_info)
|
38
45
|
}
|
@@ -46,7 +53,10 @@ describe Koala::Facebook::APIError do
|
|
46
53
|
:fb_error_user_title => 'error user title',
|
47
54
|
:fb_error_trace_id => 'fb trace id',
|
48
55
|
:fb_error_debug => 'fb debug token',
|
49
|
-
:fb_error_rev => 'fb revision'
|
56
|
+
:fb_error_rev => 'fb revision',
|
57
|
+
:fb_buc_usage => JSON.parse(BUC_USAGE_JSON),
|
58
|
+
:fb_ada_usage => JSON.parse(ADA_USAGE_JSON),
|
59
|
+
:fb_app_usage => JSON.parse(APP_USAGE_JSON)
|
50
60
|
}.each_pair do |accessor, value|
|
51
61
|
it "sets #{accessor} to #{value}" do
|
52
62
|
expect(error.send(accessor)).to eq(value)
|
@@ -76,7 +86,10 @@ describe Koala::Facebook::APIError do
|
|
76
86
|
:fb_error_code => 1,
|
77
87
|
:fb_error_subcode => 'subcode',
|
78
88
|
:fb_error_user_msg => 'error user message',
|
79
|
-
:fb_error_user_title => 'error user title'
|
89
|
+
:fb_error_user_title => 'error user title',
|
90
|
+
:fb_buc_usage => nil,
|
91
|
+
:fb_ada_usage => nil,
|
92
|
+
:fb_app_usage => nil
|
80
93
|
}.each_pair do |accessor, value|
|
81
94
|
expect(error.send(accessor)).to eq(value)
|
82
95
|
end
|
@@ -593,7 +593,7 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
|
|
593
593
|
hash_including(@other_access_token_args.dup),
|
594
594
|
anything,
|
595
595
|
anything
|
596
|
-
).and_return(Koala::HTTPService::Response.new(200, "",
|
596
|
+
).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
597
597
|
|
598
598
|
# Page the collection
|
599
599
|
app_event_types.next_page
|
@@ -11,7 +11,10 @@ module Koala
|
|
11
11
|
expect(GraphErrorChecker::DEBUG_HEADERS).to match_array([
|
12
12
|
"x-fb-rev",
|
13
13
|
"x-fb-debug",
|
14
|
-
"x-fb-trace-id"
|
14
|
+
"x-fb-trace-id",
|
15
|
+
"x-business-use-case-usage",
|
16
|
+
"x-ad-account-usage",
|
17
|
+
"x-app-usage"
|
15
18
|
])
|
16
19
|
end
|
17
20
|
|
@@ -84,10 +87,25 @@ module Koala
|
|
84
87
|
"x-fb-debug" => double("fb debug"),
|
85
88
|
"x-fb-rev" => double("fb rev"),
|
86
89
|
"x-fb-trace-id" => double("fb trace id"),
|
90
|
+
"x-business-use-case-usage" => { 'a' => 1, 'b' => 2 }.to_json,
|
91
|
+
"x-ad-account-usage" => { 'c' => 3, 'd' => 4 }.to_json,
|
92
|
+
"x-app-usage" => { 'e' => 5, 'f' => 6 }.to_json
|
87
93
|
)
|
88
94
|
expect(error.fb_error_trace_id).to eq(headers["x-fb-trace-id"])
|
89
95
|
expect(error.fb_error_debug).to eq(headers["x-fb-debug"])
|
90
96
|
expect(error.fb_error_rev).to eq(headers["x-fb-rev"])
|
97
|
+
expect(error.fb_buc_usage).to eq({ 'a' => 1, 'b' => 2 })
|
98
|
+
expect(error.fb_ada_usage).to eq({ 'c' => 3, 'd' => 4 })
|
99
|
+
expect(error.fb_app_usage).to eq({ 'e' => 5, 'f' => 6 })
|
100
|
+
end
|
101
|
+
|
102
|
+
it "logs if one of the FB debug headers can't be parsed" do
|
103
|
+
headers.merge!(
|
104
|
+
"x-app-usage" => '{invalid:json}'
|
105
|
+
)
|
106
|
+
|
107
|
+
expect(Koala::Utils.logger).to receive(:error).with("JSON::ParserError: 859: unexpected token at '{invalid:json}' while parsing x-app-usage = {invalid:json}")
|
108
|
+
expect(error.fb_app_usage).to eq(nil)
|
91
109
|
end
|
92
110
|
|
93
111
|
context "it returns an AuthenticationError" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: koala
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Koppel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|