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