koala 3.0.0.beta3 → 3.0.0.rc
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/changelog.md +1 -0
- data/lib/koala/api/graph_batch_api.rb +24 -13
- data/lib/koala/realtime_updates.rb +23 -18
- data/lib/koala/version.rb +1 -1
- data/readme.md +3 -1
- data/spec/cases/graph_api_batch_spec.rb +41 -0
- data/spec/cases/realtime_updates_spec.rb +12 -13
- data/spec/fixtures/facebook_data.yml +4 -6
- data/spec/fixtures/mock_facebook_responses.yml +6 -6
- data/spec/support/graph_api_shared_examples.rb +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4f00f2411c9a02ce7505dd449f8d2ae4782ebd5
|
4
|
+
data.tar.gz: 3d54abde36f96607babc134fd48ceb86935d4d19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89f355e9ad1162a1a41ded724e27e878a542139941893985528e784f0f8db8b5448bed368670bb2a8a7cfc47a8a0315050c22840cd54a0927e035b284d71c46c
|
7
|
+
data.tar.gz: 716ce03878f4bf59f67ddd959d1fc3182deffe68c5d25b6aac74b434fccc58423fd0c8e55304406c74dea3b81d99e42f1a9e44f73c8a009fa1887484f580a767
|
data/changelog.md
CHANGED
@@ -29,6 +29,7 @@ Updated features:
|
|
29
29
|
* Koala.config now uses a dedicated Koala::Configuration object
|
30
30
|
* TestUser#befriend will provide the appsecret_proof if a secret is set (thanks, kwasimensah!)
|
31
31
|
* API#search now requires an object type parameter to be included, matching Facebook's API (#575)
|
32
|
+
* RealtimeUpdates will now only fetch the app access token if necessary, avoiding unnecessary calls
|
32
33
|
|
33
34
|
Removed features:
|
34
35
|
|
@@ -8,6 +8,9 @@ module Koala
|
|
8
8
|
# inside a batch call we can do anything a regular Graph API can do
|
9
9
|
include GraphAPIMethods
|
10
10
|
|
11
|
+
# Limits from @see https://developers.facebook.com/docs/marketing-api/batch-requests/v2.8
|
12
|
+
MAX_CALLS = 50
|
13
|
+
|
11
14
|
attr_reader :original_api
|
12
15
|
def initialize(api)
|
13
16
|
@original_api = api
|
@@ -35,26 +38,34 @@ module Koala
|
|
35
38
|
nil # batch operations return nothing immediately
|
36
39
|
end
|
37
40
|
|
38
|
-
# execute the queued batch calls
|
41
|
+
# execute the queued batch calls. limits it to 50 requests per call.
|
42
|
+
# NOTE: if you use `name` and JsonPath references, you should ensure to call `execute` for each
|
43
|
+
# co-reference group and that the group size is not greater than the above limits.
|
39
44
|
def execute(http_options = {})
|
40
45
|
return [] if batch_calls.empty?
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
args
|
46
|
-
|
47
|
+
batch_results = []
|
48
|
+
batch_calls.each_slice(MAX_CALLS) do |batch|
|
49
|
+
# Turn the call args collected into what facebook expects
|
50
|
+
args = {"batch" => batch_args(batch)}
|
51
|
+
batch.each do |call|
|
52
|
+
args.merge!(call.files || {})
|
53
|
+
end
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
55
|
+
original_api.graph_call("/", args, "post", http_options) do |response|
|
56
|
+
raise bad_response if response.nil?
|
57
|
+
|
58
|
+
batch_results += generate_results(response, batch)
|
59
|
+
end
|
51
60
|
end
|
61
|
+
|
62
|
+
batch_results
|
52
63
|
end
|
53
64
|
|
54
|
-
def generate_results(response)
|
65
|
+
def generate_results(response, batch)
|
55
66
|
index = 0
|
56
67
|
response.map do |call_result|
|
57
|
-
batch_op =
|
68
|
+
batch_op = batch[index]
|
58
69
|
index += 1
|
59
70
|
post_process = batch_op.post_processing
|
60
71
|
|
@@ -104,8 +115,8 @@ module Koala
|
|
104
115
|
GraphErrorChecker.new(code, body, headers).error_if_appropriate
|
105
116
|
end
|
106
117
|
|
107
|
-
def batch_args
|
108
|
-
calls =
|
118
|
+
def batch_args(calls_for_batch)
|
119
|
+
calls = calls_for_batch.map do |batch_op|
|
109
120
|
batch_op.to_batch_params(access_token, app_secret)
|
110
121
|
end
|
111
122
|
|
@@ -7,9 +7,6 @@ module Koala
|
|
7
7
|
# @note: to subscribe to real-time updates, you must have an application access token
|
8
8
|
# or provide the app secret when initializing your RealtimeUpdates object.
|
9
9
|
|
10
|
-
# The application API interface used to communicate with Facebook.
|
11
|
-
# @return [Koala::Facebook::API]
|
12
|
-
attr_reader :api
|
13
10
|
attr_reader :app_id, :app_access_token, :secret
|
14
11
|
|
15
12
|
# Create a new RealtimeUpdates instance.
|
@@ -29,14 +26,21 @@ module Koala
|
|
29
26
|
unless @app_id && (@app_access_token || @secret) # make sure we have what we need
|
30
27
|
raise ArgumentError, "Initialize must receive a hash with :app_id and either :app_access_token or :secret! (received #{options.inspect})"
|
31
28
|
end
|
29
|
+
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
# The app access token, either provided on initialization or fetched from Facebook using the
|
32
|
+
# app_id and secret.
|
33
|
+
def app_access_token
|
34
|
+
# If a token isn't provided but we need it, fetch it
|
35
|
+
@app_access_token ||= Koala::Facebook::OAuth.new(@app_id, @secret).get_app_access_token
|
36
|
+
end
|
38
37
|
|
39
|
-
|
38
|
+
# The application API interface used to communicate with Facebook.
|
39
|
+
# @return [Koala::Facebook::API]
|
40
|
+
def api
|
41
|
+
# Only instantiate the API if needed. validate_update doesn't require it, so we shouldn't
|
42
|
+
# make an unnecessary request to get the app_access_token.
|
43
|
+
@api ||= API.new(app_access_token)
|
40
44
|
end
|
41
45
|
|
42
46
|
# Subscribe to realtime updates for certain fields on a given object (user, page, etc.).
|
@@ -60,7 +64,7 @@ module Koala
|
|
60
64
|
:callback_url => callback_url,
|
61
65
|
}.merge(verify_token ? {:verify_token => verify_token} : {})
|
62
66
|
# a subscription is a success if Facebook returns a 200 (after hitting your server for verification)
|
63
|
-
|
67
|
+
api.graph_call(subscription_path, args, 'post', options)
|
64
68
|
end
|
65
69
|
|
66
70
|
# Unsubscribe from updates for a particular object or from updates.
|
@@ -71,7 +75,7 @@ module Koala
|
|
71
75
|
#
|
72
76
|
# @raise A subclass of Koala::Facebook::APIError if the subscription request failed.
|
73
77
|
def unsubscribe(object = nil, options = {})
|
74
|
-
|
78
|
+
api.graph_call(subscription_path, object ? {:object => object} : {}, "delete", options)
|
75
79
|
end
|
76
80
|
|
77
81
|
# List all active subscriptions for this application.
|
@@ -80,7 +84,7 @@ module Koala
|
|
80
84
|
#
|
81
85
|
# @return [Array] a list of active subscriptions
|
82
86
|
def list_subscriptions(options = {})
|
83
|
-
|
87
|
+
api.graph_call(subscription_path, {}, "get", options)
|
84
88
|
end
|
85
89
|
|
86
90
|
# As a security measure (to prevent DDoS attacks), Facebook sends a verification request to your server
|
@@ -129,12 +133,13 @@ module Koala
|
|
129
133
|
raise AppSecretNotDefinedError, "You must init RealtimeUpdates with your app secret in order to validate updates"
|
130
134
|
end
|
131
135
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
136
|
+
request_signature = headers['X-Hub-Signature'] || headers['HTTP_X_HUB_SIGNATURE']
|
137
|
+
return unless request_signature
|
138
|
+
|
139
|
+
signature_parts = request_signature.split("sha1=")
|
140
|
+
request_signature = signature_parts[1]
|
141
|
+
calculated_signature = OpenSSL::HMAC.hexdigest('sha1', @secret, body)
|
142
|
+
calculated_signature == request_signature
|
138
143
|
end
|
139
144
|
|
140
145
|
# The Facebook subscription management URL for your application.
|
data/lib/koala/version.rb
CHANGED
data/readme.md
CHANGED
@@ -32,7 +32,9 @@ Otherwise:
|
|
32
32
|
Configuration
|
33
33
|
-------------
|
34
34
|
|
35
|
-
|
35
|
+
**Available with 3.0.0.beta3**
|
36
|
+
|
37
|
+
Most applications will only use one application configuration. Rather than having to provide that
|
36
38
|
value every time, you can configure Koala to use global settings:
|
37
39
|
|
38
40
|
```ruby
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'json' unless Hash.respond_to?(:to_json)
|
2
3
|
|
3
4
|
describe "Koala::Facebook::GraphAPI in batch mode" do
|
4
5
|
|
@@ -468,6 +469,46 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
|
|
468
469
|
expect(thread_one_count).to eq(first_count)
|
469
470
|
expect(thread_two_count).to eq(second_count)
|
470
471
|
end
|
472
|
+
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
describe '#big_batches' do
|
477
|
+
before :each do
|
478
|
+
payload = [{code: 200, headers: [{name: "Content-Type", value: "text/javascript; charset=UTF-8"}], body: "{\"id\":\"1234\"}"}]
|
479
|
+
allow(Koala).to receive(:make_request) do |_request, args, _verb, _options|
|
480
|
+
request_count = JSON.parse(args['batch']).length
|
481
|
+
expect(request_count).to be <= 50 # check FB's limit
|
482
|
+
Koala::HTTPService::Response.new(200, (payload * request_count).to_json, {})
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'stays within fb limits' do
|
487
|
+
count_calls = 0
|
488
|
+
expected_calls = 100
|
489
|
+
@api.batch do |batch_api|
|
490
|
+
expected_calls.times { |_i| batch_api.get_object('me') { |_ret| count_calls += 1 } }
|
491
|
+
end
|
492
|
+
|
493
|
+
expect(count_calls).to eq(expected_calls)
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'is recursive safe' do
|
497
|
+
# ensure batch operations whose callbacks call batch operations don't resubmit
|
498
|
+
call_count = 0
|
499
|
+
iterations = 60
|
500
|
+
@api.batch do |batch_api|
|
501
|
+
# must do enough calls to provoke a batch submission
|
502
|
+
iterations.times { |_i|
|
503
|
+
batch_api.get_object('me') { |_ret|
|
504
|
+
call_count += 1
|
505
|
+
batch_api.get_object('me') { |_ret|
|
506
|
+
call_count += 1
|
507
|
+
}
|
508
|
+
}
|
509
|
+
}
|
510
|
+
end
|
511
|
+
expect(call_count).to eq(2 * iterations)
|
471
512
|
end
|
472
513
|
end
|
473
514
|
|
@@ -106,25 +106,24 @@ describe "Koala::Facebook::RealtimeUpdates" do
|
|
106
106
|
expect(updates).to be_a(Koala::Facebook::RealtimeUpdates)
|
107
107
|
end
|
108
108
|
|
109
|
-
it "
|
110
|
-
updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :
|
111
|
-
expect(updates.
|
109
|
+
it "sets up the API with the app access token" do
|
110
|
+
updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :app_access_token => @app_access_token)
|
111
|
+
expect(updates.api).to be_a(Koala::Facebook::API)
|
112
|
+
expect(updates.api.access_token).to eq(@app_access_token)
|
112
113
|
end
|
114
|
+
end
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
expect(oauth).to receive(:get_app_access_token).and_return(token)
|
118
|
-
expect(Koala::Facebook::OAuth).to receive(:new).with(@app_id, @secret).and_return(oauth)
|
116
|
+
describe "#app_access_token" do
|
117
|
+
it "fetches an app_token from Facebook when provided an app_id and a secret" do
|
118
|
+
# integration test
|
119
119
|
updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :secret => @secret)
|
120
|
+
expect(updates.app_access_token).not_to be_nil
|
120
121
|
end
|
121
122
|
|
122
|
-
it "
|
123
|
-
updates = Koala::Facebook::RealtimeUpdates.new(:
|
124
|
-
expect(updates.
|
125
|
-
expect(updates.api.access_token).to eq(@app_access_token)
|
123
|
+
it "returns the provided token if provided" do
|
124
|
+
updates = Koala::Facebook::RealtimeUpdates.new(app_id: @app_id, app_access_token: @app_access_token)
|
125
|
+
expect(updates.app_access_token).to eq(@app_access_token)
|
126
126
|
end
|
127
|
-
|
128
127
|
end
|
129
128
|
|
130
129
|
describe "#subscribe" do
|
@@ -5,9 +5,7 @@
|
|
5
5
|
# by enter an OAuth token, code, and session_key (for real users) or changing the app_id and secret (for test users)
|
6
6
|
# (note for real users: this will leave some photos and videos posted to your wall, since they can't be deleted through the API)
|
7
7
|
|
8
|
-
# These values are configured to work with
|
9
|
-
# Of course, you can change this to work with your own app.
|
10
|
-
# Check out http://oauth.twoalex.com/ to easily generate tokens, cookies, etc.
|
8
|
+
# These values are configured to work with a test app. If you want, you can change this to work with your own app.
|
11
9
|
|
12
10
|
# Your OAuth token should have the read_stream, publish_stream, user_photos, user_videos, and read_insights permissions.
|
13
11
|
oauth_token:
|
@@ -22,7 +20,7 @@ oauth_test_data:
|
|
22
20
|
# These values will work out of the box
|
23
21
|
app_id: 119908831367602
|
24
22
|
secret: e45e55a333eec232d4206d2703de1307
|
25
|
-
callback_url: http://
|
23
|
+
callback_url: http://testdomain.koalatest.test/
|
26
24
|
app_access_token: 119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.
|
27
25
|
raw_token_string: "access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.&expires=6621"
|
28
26
|
raw_offline_access_token_string: access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.
|
@@ -54,7 +52,7 @@ oauth_test_data:
|
|
54
52
|
issued_at: 1301917299
|
55
53
|
|
56
54
|
subscription_test_data:
|
57
|
-
subscription_path: https://
|
55
|
+
subscription_path: https://testdomain.koalatest.test/subscriptions
|
58
56
|
verify_token: "myverificationtoken|1f54545d5f722733e17faae15377928f"
|
59
57
|
challenge_data:
|
60
58
|
"hub.challenge": "1290024882"
|
@@ -62,4 +60,4 @@ subscription_test_data:
|
|
62
60
|
"hub.mode": "subscribe"
|
63
61
|
|
64
62
|
vcr_data:
|
65
|
-
oauth_token: CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
63
|
+
oauth_token: CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
@@ -144,10 +144,10 @@ graph_api:
|
|
144
144
|
message=Hello, world, from the test suite batch API!:
|
145
145
|
post:
|
146
146
|
with_token: '{"id": "FEED_ITEM_BATCH"}'
|
147
|
-
link=http://
|
147
|
+
link=http://testdomain.koalatest.test/&message=Hello, world, from the test suite again!&name=OAuth Playground:
|
148
148
|
post:
|
149
149
|
with_token: '{"id": "FEED_ITEM_CONTEXT"}'
|
150
|
-
link=http://
|
150
|
+
link=http://testdomain.koalatest.test/&message=body&name=It's a big question&picture=http://testdomain.koalatest.test//images/logo.png&properties=<%= JSON.dump({"Link1"=>{"text"=>"Left", "href"=>"http://testdomain.koalatest.test/"}, "other" => {"text"=>"Straight ahead", "href"=>"http://testdomain.koalatest.test/?"}}) %>&type=link:
|
151
151
|
post:
|
152
152
|
with_token: '{"id": "FEED_ITEM_DICTIONARY"}'
|
153
153
|
|
@@ -367,12 +367,12 @@ graph_api:
|
|
367
367
|
with_token:
|
368
368
|
code: 200
|
369
369
|
get:
|
370
|
-
with_token: '{"data":[{"callback_url":"https://
|
370
|
+
with_token: '{"data":[{"callback_url":"https://testdomain.koalatest.test/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
|
371
371
|
|
372
372
|
|
373
373
|
callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>:
|
374
374
|
get:
|
375
|
-
with_token: '{"data":[{"callback_url":"https://
|
375
|
+
with_token: '{"data":[{"callback_url":"https://testdomain.koalatest.test/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
|
376
376
|
|
377
377
|
# -- Mock Item Responses --
|
378
378
|
|
@@ -394,13 +394,13 @@ graph_api:
|
|
394
394
|
no_args:
|
395
395
|
<<: *item_deleted
|
396
396
|
get:
|
397
|
-
with_token: '{"link":"http://
|
397
|
+
with_token: '{"link":"http://testdomain.koalatest.test/", "name": "OAuth Playground"}'
|
398
398
|
|
399
399
|
/FEED_ITEM_DICTIONARY:
|
400
400
|
no_args:
|
401
401
|
<<: *item_deleted
|
402
402
|
get:
|
403
|
-
with_token: '{"link":"http://
|
403
|
+
with_token: '{"link":"http://testdomain.koalatest.test/", "name": "OAuth Playground", "properties": {}}'
|
404
404
|
|
405
405
|
/FEED_ITEM_CATS:
|
406
406
|
no_args:
|
@@ -175,7 +175,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
175
175
|
end
|
176
176
|
|
177
177
|
it "can post a message with an attachment to a feed" do
|
178
|
-
result = @api.put_wall_post("Hello, world, from the test suite again!", {:name => "OAuth Playground", :link => "http://
|
178
|
+
result = @api.put_wall_post("Hello, world, from the test suite again!", {:name => "OAuth Playground", :link => "http://testdomain.koalatest.test/"})
|
179
179
|
@temporary_object_id = result["id"]
|
180
180
|
expect(@temporary_object_id).not_to be_nil
|
181
181
|
end
|
@@ -311,7 +311,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
311
311
|
end
|
312
312
|
|
313
313
|
it "can verify a message with an attachment posted to a feed" do
|
314
|
-
attachment = {"name" => "OAuth Playground", "link" => "http://
|
314
|
+
attachment = {"name" => "OAuth Playground", "link" => "http://testdomain.koalatest.test/"}
|
315
315
|
result = @api.put_wall_post("Hello, world, from the test suite again!", attachment)
|
316
316
|
@temporary_object_id = result["id"]
|
317
317
|
get_result = @api.get_object(@temporary_object_id)
|
@@ -508,7 +508,7 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
508
508
|
# but are here for completeness
|
509
509
|
it "can't post to a feed" do
|
510
510
|
expect(lambda do
|
511
|
-
attachment = {:name => "OAuth Playground", :link => "http://
|
511
|
+
attachment = {:name => "OAuth Playground", :link => "http://testdomain.koalatest.test/"}
|
512
512
|
@result = @api.put_wall_post("Hello, world", attachment, "facebook")
|
513
513
|
end).to raise_error(Koala::Facebook::AuthenticationError)
|
514
514
|
end
|
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.0.0.
|
4
|
+
version: 3.0.0.rc
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Koppel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|