koala 2.4.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +32 -0
  3. data/Gemfile +5 -3
  4. data/ISSUE_TEMPLATE +25 -0
  5. data/PULL_REQUEST_TEMPLATE +11 -0
  6. data/changelog.md +161 -4
  7. data/code_of_conduct.md +64 -12
  8. data/koala.gemspec +5 -1
  9. data/lib/koala/api/batch_operation.rb +3 -6
  10. data/lib/koala/api/{graph_api.rb → graph_api_methods.rb} +29 -104
  11. data/lib/koala/api/graph_batch_api.rb +112 -65
  12. data/lib/koala/api/graph_collection.rb +19 -12
  13. data/lib/koala/api/graph_error_checker.rb +4 -3
  14. data/lib/koala/api.rb +65 -26
  15. data/lib/koala/configuration.rb +56 -0
  16. data/lib/koala/errors.rb +22 -2
  17. data/lib/koala/http_service/request.rb +133 -0
  18. data/lib/koala/http_service/response.rb +6 -4
  19. data/lib/koala/http_service/uploadable_io.rb +0 -5
  20. data/lib/koala/http_service.rb +29 -76
  21. data/lib/koala/oauth.rb +8 -8
  22. data/lib/koala/realtime_updates.rb +26 -21
  23. data/lib/koala/test_users.rb +9 -8
  24. data/lib/koala/version.rb +1 -1
  25. data/lib/koala.rb +7 -9
  26. data/readme.md +83 -109
  27. data/spec/cases/api_spec.rb +176 -69
  28. data/spec/cases/configuration_spec.rb +11 -0
  29. data/spec/cases/error_spec.rb +16 -3
  30. data/spec/cases/graph_api_batch_spec.rb +75 -44
  31. data/spec/cases/graph_api_spec.rb +15 -29
  32. data/spec/cases/graph_collection_spec.rb +47 -34
  33. data/spec/cases/graph_error_checker_spec.rb +31 -2
  34. data/spec/cases/http_service/request_spec.rb +250 -0
  35. data/spec/cases/http_service/response_spec.rb +24 -0
  36. data/spec/cases/http_service_spec.rb +126 -286
  37. data/spec/cases/koala_spec.rb +7 -5
  38. data/spec/cases/oauth_spec.rb +41 -2
  39. data/spec/cases/realtime_updates_spec.rb +51 -13
  40. data/spec/cases/test_users_spec.rb +56 -2
  41. data/spec/cases/uploadable_io_spec.rb +31 -31
  42. data/spec/fixtures/cat.m4v +0 -0
  43. data/spec/fixtures/facebook_data.yml +4 -6
  44. data/spec/fixtures/mock_facebook_responses.yml +41 -78
  45. data/spec/fixtures/vcr_cassettes/app_test_accounts.yml +97 -0
  46. data/spec/integration/graph_collection_spec.rb +8 -5
  47. data/spec/spec_helper.rb +2 -2
  48. data/spec/support/graph_api_shared_examples.rb +152 -337
  49. data/spec/support/koala_test.rb +11 -13
  50. data/spec/support/mock_http_service.rb +11 -14
  51. data/spec/support/uploadable_io_shared_examples.rb +4 -4
  52. metadata +47 -48
  53. data/.autotest +0 -12
  54. data/.travis.yml +0 -17
  55. data/Guardfile +0 -6
  56. data/autotest/discover.rb +0 -1
  57. data/lib/koala/api/rest_api.rb +0 -135
  58. data/lib/koala/http_service/multipart_request.rb +0 -41
  59. data/spec/cases/multipart_request_spec.rb +0 -65
  60. data/spec/support/rest_api_shared_examples.rb +0 -168
data/readme.md CHANGED
@@ -1,10 +1,10 @@
1
- Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/gems/koala) [![Dependencies](https://img.shields.io/gemnasium/arsduo/koala.svg)](https://gemnasium.com/arsduo/koala) [![Build Status](https://img.shields.io/travis/arsduo/koala.svg)](http://travis-ci.org/arsduo/koala) [![Code Climate](https://img.shields.io/codeclimate/github/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala) [![Code Coverage](https://img.shields.io/codeclimate/coverage/github/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala)
1
+ Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/gems/koala) [![Build Status](https://img.shields.io/travis/arsduo/koala.svg)](http://travis-ci.org/arsduo/koala) [![Code Climate](https://img.shields.io/codeclimate/coverage-letter/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala) [![Code Coverage](https://img.shields.io/codeclimate/coverage/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala)
2
2
  ====
3
- [Koala](http://github.com/arsduo/koala) is a Facebook library for Ruby, supporting the Graph API (including the batch requests and photo uploads), the REST API, realtime updates, test users, and OAuth validation. We wrote Koala with four goals:
3
+ [Koala](http://github.com/arsduo/koala) is a Facebook library for Ruby, supporting the Graph API (including the batch requests and photo uploads), the Marketing API, the Atlas API, realtime updates, test users, and OAuth validation. We wrote Koala with four goals:
4
4
 
5
5
  * Lightweight: Koala should be as light and simple as Facebook’s own libraries, providing API accessors and returning simple JSON.
6
6
  * Fast: Koala should, out of the box, be quick. Out of the box, we use Facebook's faster read-only servers when possible and if available, the Typhoeus gem to make snappy Facebook requests. Of course, that brings us to our next topic:
7
- * Flexible: Koala should be useful to everyone, regardless of their current configuration. We support JRuby, Rubinius, and REE as well as vanilla Ruby (1.8.7, 1.9.2, 1.9.3, and 2.0.0), and use the Faraday library to provide complete flexibility over how HTTP requests are made.
7
+ * Flexible: Koala should be useful to everyone, regardless of their current configuration. We support all currently-supported Ruby versions (MRI 2.1-2.4) and Koala should work on JRuby and Rubinius.
8
8
  * Tested: Koala should have complete test coverage, so you can rely on it. Our test coverage is complete and can be run against either mocked responses or the live Facebook servers; we're also on [Travis CI](http://travis-ci.org/arsduo/koala/).
9
9
 
10
10
  **Found a bug? Interested in contributing?** Check out the Maintenance section below!
@@ -12,9 +12,12 @@ Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/
12
12
  Installation
13
13
  ------------
14
14
 
15
+ **Koala 3.0 is out! There should be no significant changes** for most users. If you encounter any
16
+ problems, please file an issue and I'll take a look.
17
+
15
18
  In Bundler:
16
19
  ```ruby
17
- gem "koala", "~> 2.2"
20
+ gem "koala"
18
21
  ```
19
22
 
20
23
  Otherwise:
@@ -22,23 +25,41 @@ Otherwise:
22
25
  [sudo|rvm] gem install koala
23
26
  ```
24
27
 
25
- Upgrading to 2.0+
26
- -----------------
28
+ Configuration
29
+ -------------
30
+
31
+ Most applications will only use one application configuration. Rather than having to provide that
32
+ value every time, you can configure Koala to use global settings:
33
+
34
+ ```ruby
35
+ # In Rails, you could put this in config/initializers/koala.rb
36
+ Koala.configure do |config|
37
+ config.access_token = MY_TOKEN
38
+ config.app_access_token = MY_APP_ACCESS_TOKEN
39
+ config.app_id = MY_APP_ID
40
+ config.app_secret = MY_APP_SECRET
41
+ # See Koala::Configuration for more options, including details on how to send requests through
42
+ # your own proxy servers.
43
+ end
44
+ ```
27
45
 
28
- Koala 2.0 is not a major refactor, but rather a set of small, mostly internal
29
- refactors, which should not require significant changes by users. See changelog.md for more
30
- details.
46
+ **Note**: this is not currently threadsafe. (PRs welcome as long as they support both threaded and
47
+ non-threaded configuration.)
31
48
 
32
49
  Graph API
33
50
  ---------
34
51
 
35
- The Graph API is the simple, slick new interface to Facebook's data.
36
- Using it with Koala is quite straightforward. First, you'll need an access token, which you can get through
37
- Facebook's [Graph API Explorer](https://developers.facebook.com/tools/explorer) (click on 'Get Access Token').
52
+ The Graph API is the interface to Facebook's data. Using it with Koala is quite straightforward.
53
+ First, you'll need an access token, which you can get through Facebook's [Graph API
54
+ Explorer](https://developers.facebook.com/tools/explorer) (click on 'Get Access Token').
55
+
38
56
  Then, go exploring:
39
57
 
40
58
  ```ruby
41
- @graph = Koala::Facebook::API.new(oauth_access_token)
59
+ require 'koala'
60
+
61
+ # access_token and other values aren't required if you set the defaults as described above
62
+ @graph = Koala::Facebook::API.new(access_token)
42
63
 
43
64
  profile = @graph.get_object("me")
44
65
  friends = @graph.get_connections("me", "friends")
@@ -54,14 +75,14 @@ friends = @graph.get_connections("me", "friends")
54
75
  # For extra security (recommended), you can provide an appsecret parameter,
55
76
  # tying your access tokens to your app secret.
56
77
  # (See https://developers.facebook.com/docs/reference/api/securing-graph-api/
57
- # You'll need to turn on 'Require proof on all calls' in the advanced section
78
+
79
+ # You may need to turn on 'Require proof on all calls' in the advanced section
58
80
  # of your app's settings when doing this.
59
- @graph = Koala::Facebook::API.new(oauth_access_token, app_secret)
81
+ @graph = Koala::Facebook::API.new(access_token, app_secret)
60
82
 
61
83
  # Facebook is now versioning their API. # If you don't specify a version, Facebook
62
- # will default to the oldest version your app is allowed to use. Note that apps
63
- # created after f8 2014 *cannot* use the v1.0 API. See
64
- # https://developers.facebook.com/docs/apps/versions for more information.
84
+ # will default to the oldest version your app is allowed to use.
85
+ # See https://developers.facebook.com/docs/apps/versions for more information.
65
86
  #
66
87
  # You can specify version either globally:
67
88
  Koala.config.api_version = "v2.0"
@@ -113,85 +134,20 @@ the results apart from a long list of array entries:
113
134
 
114
135
  Check out the wiki for more details and examples.
115
136
 
116
- The REST API
117
- ------------
118
-
119
- Where the Graph API and the old REST API overlap, you should choose the Graph API. Unfortunately, that overlap is far from complete, and there are many important API calls that can't yet be done via the Graph.
120
-
121
- Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
122
- ```ruby
123
- @rest = Koala::Facebook::API.new(oauth_access_token)
124
-
125
- @rest.fql_query(my_fql_query) # convenience method
126
- @rest.fql_multiquery(fql_query_hash) # convenience method
127
- @rest.rest_call("stream.publish", arguments_hash) # generic version
128
- ```
129
-
130
- Of course, you can use the Graph API methods on the same object -- the power of two APIs right in the palm of your hand.
131
- ```ruby
132
- @api = Koala::Facebook::API.new(oauth_access_token)
133
- fql = @api.fql_query(my_fql_query)
134
- @api.put_wall_post(process_result(fql))
135
- ```
136
-
137
- Configuration
138
- -------------
139
-
140
- You can change the host that koala makes requests to (point to a mock server, apigee, runscope etc..)
141
- ```ruby
142
- # config/initializers/koala.rb
143
- require 'koala'
144
-
145
- Koala.configure do |config|
146
- config.graph_server = 'my-graph-mock.mysite.com'
147
- # other common options are `rest_server` and `dialog_host`
148
- # see lib/koala/http_service.rb
149
- end
150
- ```
151
-
152
- Of course the defaults are the facebook endpoints and you can additionally configure the beta
153
- tier and video upload matching and replacement strings.
154
-
155
- OAuth
137
+ App Access Tokens
156
138
  -----
157
139
 
158
- You can use the Graph and REST APIs without an OAuth access token, but the real magic happens when you provide Facebook an OAuth token to prove you're authenticated. Koala provides an OAuth class to make that process easy:
140
+ You get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
159
141
  ```ruby
160
142
  @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
161
- ```
162
-
163
- If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/facebook-js-sdk) (formerly Facebook Connect), you can use the OAuth class to parse the cookies:
164
- ```ruby
165
- # parses and returns a hash including the token and the user id
166
- # NOTE: this method can only be called once per session, as the OAuth code
167
- # Facebook supplies can only be redeemed once. Your application must handle
168
- # cross-request storage of this information; you can no longer call this method
169
- # multiple times.
170
- @oauth.get_user_info_from_cookies(cookies)
171
- ```
172
- And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
173
-
174
- ```ruby
175
- # generate authenticating URL
176
- @oauth.url_for_oauth_code
177
- # fetch the access token once you have the code
178
- @oauth.get_access_token(code)
179
- ```
180
-
181
- You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
182
- ```ruby
183
143
  @oauth.get_app_access_token
184
144
  ```
185
145
  For those building apps on Facebook, parsing signed requests is simple:
186
146
  ```ruby
187
147
  @oauth.parse_signed_request(signed_request_string)
188
148
  ```
189
- Or, if for some horrible reason, you're still using session keys, despair not! It's easy to turn them into shiny, modern OAuth tokens:
190
- ```ruby
191
- @oauth.get_token_from_session_key(session_key)
192
- @oauth.get_tokens_from_session_keys(array_of_session_keys)
193
- ```
194
- That's it! It's pretty simple once you get the hang of it. If you're new to OAuth, though, check out the wiki and the OAuth Playground example site (see below).
149
+
150
+ The OAuth class has additional methods that may occasionally be useful.
195
151
 
196
152
  Real-time Updates
197
153
  -----------------
@@ -200,6 +156,7 @@ Sometimes, reaching out to Facebook is a pain -- let it reach out to you instead
200
156
 
201
157
  Koala makes it easy to interact with your applications using the RealtimeUpdates class:
202
158
  ```ruby
159
+ # This class also supports the defaults as described above
203
160
  @updates = Koala::Facebook::RealtimeUpdates.new(app_id: app_id, secret: secret)
204
161
  ```
205
162
  You can do just about anything with your real-time update subscriptions using the RealtimeUpdates class:
@@ -220,17 +177,50 @@ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
220
177
  ```
221
178
  For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
222
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
+
223
211
  Test Users
224
212
  ----------
225
213
 
226
- We also support the test users API, allowing you to conjure up fake users and command them to do your bidding using the Graph or REST API:
214
+ We also support the test users API, allowing you to conjure up fake users and command them to do your bidding using the Graph API:
227
215
  ```ruby
216
+ # This class also supports the defaults as described above
228
217
  @test_users = Koala::Facebook::TestUsers.new(app_id: id, secret: secret)
229
218
  user = @test_users.create(is_app_installed, desired_permissions)
230
219
  user_graph_api = Koala::Facebook::API.new(user["access_token"])
231
220
  # or, if you want to make a whole community:
232
221
  @test_users.create_network(network_size, is_app_installed, common_permissions)
233
222
  ```
223
+
234
224
  Talking to Facebook
235
225
  -------------------
236
226
 
@@ -253,14 +243,6 @@ Some resources to help you as you play with Koala and the Graph API:
253
243
  * Complete Koala documentation <a href="https://github.com/arsduo/koala/wiki">on the wiki</a>
254
244
  * Facebook's <a href="http://facebook.stackoverflow.com/">Stack Overflow site</a> is a stupendous place to ask questions, filled with people who will help you figure out what's up with the Facebook API.
255
245
  * Facebook's <a href="http://developers.facebook.com/tools/explorer/">Graph API Explorer</a>, where you can play with the Graph API in your browser
256
- * The Koala-powered <a href="http://oauth.twoalex.com" target="_blank">OAuth Playground</a>, where you can easily generate OAuth access tokens and any other data needed to test out the APIs or OAuth
257
- * Follow Koala on <a href="http://www.facebook.com/pages/Koala/315368291823667">Facebook</a> and <a href="https://twitter.com/#!/koala_fb">Twitter</a> for SDK updates and occasional news about Facebook API changes.
258
-
259
- *Note*: I use the Koala issues tracker on Github to triage and address issues
260
- with the gem itself; if you need help using the Facebook API, the above
261
- resources will be far more effective. Depending on how much time I have, Github
262
- issues filed about how to use the Facebook API may be closed with a reference
263
- to the Facebook Stack Overflow page.
264
246
 
265
247
  Testing
266
248
  -------
@@ -278,26 +260,18 @@ LIVE=true bundle exec rake spec
278
260
  # you can also test against Facebook's beta tier
279
261
  LIVE=true BETA=true bundle exec rake spec
280
262
  ```
263
+
281
264
  By default, the live tests are run against test users, so you can run them as frequently as you want. If you want to run them against a real user, however, you can fill in the OAuth token, code, and access\_token values in spec/fixtures/facebook_data.yml. See the wiki for more details.
282
265
 
283
266
  Maintenance
284
267
  -----------
285
268
 
286
269
  _Pull requests_: Koala exists as it does thanks to the amazing support and work of community members of all
287
- backgrounds and levels of experience. Pull requests are very welcome! If you have any questions,
288
- just open an issue.
270
+ backgrounds and levels of experience. Pull requests are very welcome!
271
+
272
+ _Issues_: If you have any questions about the gem, found an issue in the Ruby code or
273
+ documentation, or have another question that isn't right for StackOverflow, just open an issue and fill out the template.
289
274
 
290
275
  Please note that this project is released with a Contributor Code of Conduct. By participating in
291
276
  this project you agree to abide by its terms. See
292
277
  [code_of_conduct.md](https://github.com/arsduo/koala/blob/master/code_of_conduct.md) for more information.
293
-
294
- _Schedule_: In order to keep Koala moving forward on a regular and predictable schedule, I will
295
- address issues and pull requests at least three times a year: late July/early August, late
296
- December/early January, and late March/early April. I may respond to issues in between maintenance
297
- periods, but it'll depend on other life/work goings-on.
298
-
299
- Breaking/new Facebook changes and other urgent issues obviously will get addressed much more
300
- quickly. (We've never had a security issue, but obviously that would be priority 0.)
301
-
302
- Have questions? Found a breaking bug or urgent issue? [Tweet at me](http://twitter.com/arsduo) --
303
- I'm always happy to respond.
@@ -1,9 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Koala::Facebook::API" do
4
- before(:each) do
4
+ before :each do
5
5
  @service = Koala::Facebook::API.new
6
6
  end
7
+ let(:dummy_response) { double("fake response", data: {}, status: 200, body: "", headers: {}) }
8
+
9
+ it "defaults to the globally configured token if one's provided" do
10
+ token = "Foo"
11
+
12
+ Koala.configure do |config|
13
+ config.access_token = token
14
+ end
15
+
16
+ expect(Koala::Facebook::API.new.access_token).to eq(token)
17
+ end
18
+
19
+ it "defaults to the globally configured app_secret if one's provided" do
20
+ app_secret = "Foo"
21
+
22
+ Koala.configure do |config|
23
+ config.app_secret = app_secret
24
+ end
25
+
26
+ expect(Koala::Facebook::API.new.app_secret).to eq(app_secret)
27
+ end
7
28
 
8
29
  it "doesn't include an access token if none was given" do
9
30
  expect(Koala).to receive(:make_request).with(
@@ -45,36 +66,6 @@ describe "Koala::Facebook::API" do
45
66
  service.api('anything', args)
46
67
  end
47
68
 
48
- it "has an attr_reader for access token" do
49
- token = 'adfadf'
50
- service = Koala::Facebook::API.new token
51
- expect(service.access_token).to eq(token)
52
- end
53
-
54
- it "has an attr_reader for app_secret" do
55
- secret = double
56
- service = Koala::Facebook::API.new(@token, secret)
57
- expect(service.app_secret).to eq(secret)
58
- end
59
-
60
- it "gets the attribute of a Koala::HTTPService::Response given by the http_component parameter" do
61
- http_component = :method_name
62
-
63
- response = double('Mock KoalaResponse', :body => '', :status => 200)
64
- result = double("result")
65
- allow(response).to receive(http_component).and_return(result)
66
- allow(Koala).to receive(:make_request).and_return(response)
67
-
68
- expect(@service.api('anything', {}, 'get', :http_component => http_component)).to eq(result)
69
- end
70
-
71
- it "returns the entire response if http_component => :response" do
72
- http_component = :response
73
- response = double('Mock KoalaResponse', :body => '', :status => 200)
74
- allow(Koala).to receive(:make_request).and_return(response)
75
- expect(@service.api('anything', {}, 'get', :http_component => http_component)).to eq(response)
76
- end
77
-
78
69
  it "turns arrays of non-enumerables into comma-separated arguments by default" do
79
70
  args = [12345, {:foo => [1, 2, "3", :four]}]
80
71
  expected = ["/12345", {:foo => "1,2,3,four"}, "get", {}]
@@ -123,43 +114,12 @@ describe "Koala::Facebook::API" do
123
114
  @service.api(*args)
124
115
  end
125
116
 
126
- it "returns the body of the request as JSON if no http_component is given" do
127
- response = double('response', :body => 'body', :status => 200)
128
- allow(Koala).to receive(:make_request).and_return(response)
129
-
130
- json_body = double('JSON body')
131
- allow(MultiJson).to receive(:load).and_return([json_body])
132
-
133
- expect(@service.api('anything')).to eq(json_body)
134
- end
135
-
136
- it "executes an error checking block if provided" do
137
- response = Koala::HTTPService::Response.new(200, '{}', {})
138
- allow(Koala).to receive(:make_request).and_return(response)
139
-
140
- yield_test = double('Yield Tester')
141
- expect(yield_test).to receive(:pass)
142
-
143
- @service.api('anything', {}, "get") do |arg|
144
- yield_test.pass
145
- expect(arg).to eq(response)
146
- end
147
- end
148
-
149
117
  it "raises an API error if the HTTP response code is greater than or equal to 500" do
150
118
  allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(500, 'response body', {}))
151
119
 
152
120
  expect { @service.api('anything') }.to raise_exception(Koala::Facebook::APIError)
153
121
  end
154
122
 
155
- it "handles rogue true/false as responses" do
156
- expect(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'true', {}))
157
- expect(@service.api('anything')).to be_truthy
158
-
159
- expect(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'false', {}))
160
- expect(@service.api('anything')).to be_falsey
161
- end
162
-
163
123
  describe "path manipulation" do
164
124
  context "leading /" do
165
125
  it "adds a leading / to the path if not present" do
@@ -179,11 +139,9 @@ describe "Koala::Facebook::API" do
179
139
  describe "with an access token" do
180
140
  before(:each) do
181
141
  @api = Koala::Facebook::API.new(@token)
142
+ @app_access_token = KoalaTest.app_access_token
182
143
  end
183
144
 
184
- it_should_behave_like "Koala RestAPI"
185
- it_should_behave_like "Koala RestAPI with an access token"
186
-
187
145
  it_should_behave_like "Koala GraphAPI"
188
146
  it_should_behave_like "Koala GraphAPI with an access token"
189
147
  it_should_behave_like "Koala GraphAPI with GraphCollection"
@@ -194,12 +152,10 @@ describe "Koala::Facebook::API" do
194
152
  @api = Koala::Facebook::API.new
195
153
  end
196
154
 
197
- it_should_behave_like "Koala RestAPI"
198
- it_should_behave_like "Koala RestAPI without an access token"
199
-
155
+ # In theory this should behave the same with a GraphCollection, but those tests currently hit
156
+ # an endpoint that now requires a token.
200
157
  it_should_behave_like "Koala GraphAPI"
201
158
  it_should_behave_like "Koala GraphAPI without an access token"
202
- it_should_behave_like "Koala GraphAPI with GraphCollection"
203
159
  end
204
160
 
205
161
  context '#api' do
@@ -252,4 +208,155 @@ describe "Koala::Facebook::API" do
252
208
  end
253
209
  end
254
210
  end
211
+
212
+ describe "#graph_call" do
213
+ it "passes all arguments to the api method" do
214
+ user = KoalaTest.user1
215
+ args = {}
216
+ verb = 'get'
217
+ opts = {:a => :b}
218
+ expect(@service).to receive(:api).with(user, args, verb, opts).and_return(dummy_response)
219
+ @service.graph_call(user, args, verb, opts)
220
+ end
221
+
222
+ it "throws an APIError if the result hash has an error key" do
223
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(500, '{"error": "An error occurred!"}', {}))
224
+ expect { @service.graph_call(KoalaTest.user1, {}) }.to raise_exception(Koala::Facebook::APIError)
225
+ end
226
+
227
+ it "passes the results through GraphCollection.evaluate" do
228
+ allow(@service).to receive(:api).and_return(dummy_response)
229
+ expect(Koala::Facebook::API::GraphCollection).to receive(:evaluate).with(dummy_response, @service)
230
+ @service.graph_call("/me")
231
+ end
232
+
233
+ it "returns the results of GraphCollection.evaluate" do
234
+ expected = {}
235
+ allow(@service).to receive(:api).and_return(dummy_response)
236
+ expect(Koala::Facebook::API::GraphCollection).to receive(:evaluate).and_return(expected)
237
+ expect(@service.graph_call("/me")).to eq(expected)
238
+ end
239
+
240
+ it "returns the post_processing block's results if one is supplied" do
241
+ other_result = [:a, 2, :three]
242
+ block = Proc.new {|r| other_result}
243
+ allow(@service).to receive(:api).and_return(dummy_response)
244
+ expect(@service.graph_call("/me", {}, "get", {}, &block)).to eq(other_result)
245
+ end
246
+
247
+ it "gets the status of a Koala::HTTPService::Response if requested" do
248
+ response = Koala::HTTPService::Response.new(200, '', {})
249
+ allow(Koala).to receive(:make_request).and_return(response)
250
+
251
+ expect(@service.graph_call('anything', {}, 'get', http_component: :status)).to eq(200)
252
+ end
253
+
254
+ it "gets the headers of a Koala::HTTPService::Response if requested" do
255
+ headers = {"a" => 2}
256
+ response = Koala::HTTPService::Response.new(200, '', headers)
257
+ allow(Koala).to receive(:make_request).and_return(response)
258
+
259
+ expect(@service.graph_call('anything', {}, 'get', :http_component => :headers)).to eq(headers)
260
+ end
261
+
262
+ it "returns the entire response if http_component => :response" do
263
+ http_component = :response
264
+ response = Koala::HTTPService::Response.new(200, '', {})
265
+ allow(Koala).to receive(:make_request).and_return(response)
266
+ expect(@service.graph_call('anything', {}, 'get', :http_component => http_component)).to eq(response)
267
+ end
268
+
269
+ it "returns the body of the request as JSON if no http_component is given" do
270
+ result = {"a" => 2}
271
+ response = Koala::HTTPService::Response.new(200, result.to_json, {})
272
+ allow(Koala).to receive(:make_request).and_return(response)
273
+
274
+ expect(@service.graph_call('anything')).to eq(result)
275
+ end
276
+
277
+ it "handles rogue true/false as responses" do
278
+ expect(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'true', {}))
279
+ expect(@service.graph_call('anything')).to be_truthy
280
+
281
+ expect(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'false', {}))
282
+ expect(@service.graph_call('anything')).to be_falsey
283
+ end
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:.*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
255
362
  end
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Koala::Configuration do
4
+ let(:config) { Koala::Configuration.new }
5
+
6
+ it "defaults the HTTPService's DEFAULT_SERVERS" do
7
+ Koala::HTTPService::DEFAULT_SERVERS.each_pair do |key, value|
8
+ expect(config.public_send(key)).to eq(value)
9
+ end
10
+ end
11
+ end
@@ -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