koala 3.0.0.beta3 → 3.0.0.rc

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
2
  SHA1:
3
- metadata.gz: 2ed893e5254cc1e5b3007301dc847bd025b592c1
4
- data.tar.gz: 3ad942cb33ac7e7a36304250dd3cc7c6b36712f8
3
+ metadata.gz: f4f00f2411c9a02ce7505dd449f8d2ae4782ebd5
4
+ data.tar.gz: 3d54abde36f96607babc134fd48ceb86935d4d19
5
5
  SHA512:
6
- metadata.gz: 6d4cac2752f9b72f7c1c1cf4632dfaf10b7ba9b72753699540587e3b636b35fde33ca2035b47e468d51d59c490ce9dcbc624618d5eb10e1da3b5fc4c2a11f034
7
- data.tar.gz: 4728dfc6777f3e1b29d2dcab6608c9a40f24e8ee8a2136940ed2b463846ddcffa9c0f0afd96a1ab8d21b2d2c0203bdde5ddfe0c4a8ee1d250650921361343c90
6
+ metadata.gz: 89f355e9ad1162a1a41ded724e27e878a542139941893985528e784f0f8db8b5448bed368670bb2a8a7cfc47a8a0315050c22840cd54a0927e035b284d71c46c
7
+ data.tar.gz: 716ce03878f4bf59f67ddd959d1fc3182deffe68c5d25b6aac74b434fccc58423fd0c8e55304406c74dea3b81d99e42f1a9e44f73c8a009fa1887484f580a767
@@ -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
- # Turn the call args collected into what facebook expects
43
- args = {"batch" => batch_args}
44
- batch_calls.each do |call|
45
- args.merge! call.files || {}
46
- end
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
- original_api.graph_call("/", args, "post", http_options) do |response|
49
- raise bad_response if response.nil?
50
- generate_results(response)
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 = batch_calls[index]
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 = batch_calls.map do |batch_op|
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
- # fetch the access token if we're provided a secret
34
- if @secret && !@app_access_token
35
- oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
36
- @app_access_token = oauth.get_app_access_token
37
- end
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
- @api = API.new(@app_access_token)
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
- @api.graph_call(subscription_path, args, 'post', options)
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
- @api.graph_call(subscription_path, object ? {:object => object} : {}, "delete", options)
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
- @api.graph_call(subscription_path, {}, "get", options)
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
- if request_signature = headers['X-Hub-Signature'] || headers['HTTP_X_HUB_SIGNATURE'] and
133
- signature_parts = request_signature.split("sha1=")
134
- request_signature = signature_parts[1]
135
- calculated_signature = OpenSSL::HMAC.hexdigest('sha1', @secret, body)
136
- calculated_signature == request_signature
137
- end
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.
@@ -1,3 +1,3 @@
1
1
  module Koala
2
- VERSION = "3.0.0.beta3"
2
+ VERSION = "3.0.0.rc"
3
3
  end
data/readme.md CHANGED
@@ -32,7 +32,9 @@ Otherwise:
32
32
  Configuration
33
33
  -------------
34
34
 
35
- Most applications will only use one application configuration. Rather than having toprovide that
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 "fetches an app_token from Facebook when provided an app_id and a secret" do
110
- updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :secret => @secret)
111
- expect(updates.app_access_token).not_to be_nil
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
- it "uses the OAuth class to fetch a token when provided an app_id and a secret" do
115
- oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
116
- token = oauth.get_app_access_token
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 "sets up the with the app acces token" do
123
- updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :app_access_token => @app_access_token)
124
- expect(updates.api).to be_a(Koala::Facebook::API)
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 the OAuth Playground app by default
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://oauth.twoalex.com/
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://oauth.twoalex.com/subscriptions
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://oauth.twoalex.com/&message=Hello, world, from the test suite again!&name=OAuth Playground:
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://oauth.twoalex.com/&message=body&name=It's a big question&picture=http://oauth.twoalex.com//images/logo.png&properties=<%= JSON.dump({"Link1"=>{"text"=>"Left", "href"=>"http://oauth.twoalex.com/"}, "other" => {"text"=>"Straight ahead", "href"=>"http://oauth.twoalex.com/?"}}) %>&type=link:
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://oauth.twoalex.com/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
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://oauth.twoalex.com/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
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://oauth.twoalex.com/", "name": "OAuth Playground"}'
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://oauth.twoalex.com/", "name": "OAuth Playground", "properties": {}}'
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://oauth.twoalex.com/"})
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://oauth.twoalex.com/"}
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://oauth.twoalex.com/"}
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.beta3
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-01 00:00:00.000000000 Z
11
+ date: 2017-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday