koala 2.2.0 → 2.3.0rc1

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: 56b21bc1a1b3fc57084dc12a4627457682c04556
4
- data.tar.gz: 0c7c10177f312102ad6f3c0a99e56d83d4dbf3de
3
+ metadata.gz: 76e59769142ebe10d76554dfdf8c33e1f61cadc6
4
+ data.tar.gz: 57b9b65c9fd36ff249bc75dac3bab8f795a352db
5
5
  SHA512:
6
- metadata.gz: 5327a17fda38dbbc4699069c57f7993397f6bceb9a968d4080bf13ade1c81c1a6f58a2e1dc37c01f36f94dba6c80533cebfc5585d8f77b9089d37c14071ecb35
7
- data.tar.gz: 64df380a4025e40bc4539c845f9876750a86d744c312a39b3001d6d38dfc1b0a902182c2dbfca52c6fc55cd390b4dff811fef6f686fbc6741e70b3214dfe235f
6
+ metadata.gz: d6ff1b2181cdbab58b6b2a010349492102b6e598d7021d6bb7bce7949cc327259d37e4a16b51c609323fe410b87fbbfa835bfed377353d2e71a1a3c9f6125f79
7
+ data.tar.gz: 85c0c8dcf1a629043970be2a6c3eb9f6504ad5c1ca041189b3fe13e63aa70a3b90e68ca98e7cbbb042f8f616c59eac26600627ee20f0fed39419ba49b42a03da
@@ -7,8 +7,8 @@ rvm:
7
7
  - 2.0
8
8
  - 2.1
9
9
  - 2.2
10
- # rbx
11
- - rbx-2.4.1
10
+ # Rubinius is failing due to segfaults on Travis (and takes significantly longer to run)
11
+ # those builds will be restored later
12
12
  # jruby
13
13
  - jruby-19mode
14
14
  bundler_args: --without development
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ group :development, :test do
12
12
  end
13
13
 
14
14
  group :test do
15
- gem "rspec", '~> 3.0.0.beta1'
15
+ gem "rspec", '~> 3.4'
16
16
  gem "vcr"
17
17
  gem "webmock"
18
18
  gem "codeclimate-test-reporter", require: nil
@@ -1,3 +1,24 @@
1
+ v2.3.0
2
+ ======
3
+
4
+ Updated features:
5
+
6
+ * API#get_user_picture_data is now API#get_picture_data. The old method and API#get_picture both
7
+ remain with deprecation warnings. (Thanks noahsilas for earlier work on this!)
8
+ * Koala::Facebook::APIError now includes [debug and trace
9
+ info](https://github.com/arsduo/koala/blob/master/lib/koala/errors.rb) provided by Facebook in the headers
10
+ (thanks, @elhu!)
11
+
12
+ Internal Improvements:
13
+
14
+ * Graph API error handling is now done via the GraphErrorChecker class
15
+
16
+ Testing improvements:
17
+
18
+ * Upgraded RSpec to 3.3.0
19
+ * Removed pended specs that were no longer relevant
20
+ * Improved https regex in test suite (thanks, lucaskds!)
21
+
1
22
  v2.2.0
2
23
  ======
3
24
 
@@ -0,0 +1,22 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
6
+
7
+ Examples of unacceptable behavior by participants include:
8
+
9
+ * The use of sexualized language or imagery
10
+ * Personal attacks
11
+ * Trolling or insulting/derogatory comments
12
+ * Public or private harassment
13
+ * Publishing other's private information, such as physical or electronic addresses, without explicit permission
14
+ * Other unethical or unprofessional conduct.
15
+
16
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
17
+
18
+ This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
19
+
20
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
21
+
22
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
@@ -2,6 +2,7 @@ require 'addressable/uri'
2
2
 
3
3
  require 'koala/api/graph_collection'
4
4
  require 'koala/http_service/uploadable_io'
5
+ require 'koala/api/graph_error_checker'
5
6
 
6
7
  module Koala
7
8
  module Facebook
@@ -164,9 +165,10 @@ module Koala
164
165
  graph_call("#{id}/#{connection_name}", args, "delete", options, &block)
165
166
  end
166
167
 
167
- # Fetches a photo.
168
- # (Facebook returns the src of the photo as a response header; this method parses that properly,
169
- # unlike using get_connections("photo").)
168
+ # Fetches a photo url.
169
+ # Note that this method returns the picture url, not the full API
170
+ # response. For the hash containing the full metadata for the photo, use
171
+ # #get_user_picture_data instead.
170
172
  #
171
173
  # @param options options for Facebook (see #get_object).
172
174
  # To get a different size photo, pass :type => size (small, normal, large, square).
@@ -176,22 +178,34 @@ module Koala
176
178
  #
177
179
  # @return the URL to the image
178
180
  def get_picture(object, args = {}, options = {}, &block)
179
- # Gets a picture object, returning the URL (which Facebook sends as a header)
180
- graph_call("#{object}/picture", args, "get", options.merge(:http_component => :headers)) do |result|
181
- resolved_result = result ? result["Location"] : nil
182
- block ? block.call(resolved_result) : resolved_result
181
+ Koala::Utils.deprecate("API#get_picture will be removed in a future version. Please use API#get_picture_data, which returns a hash including the url.")
182
+
183
+ get_user_picture_data(object, args, options) do |result|
184
+ # Try to extract the URL
185
+ result = result.fetch('data', {})['url'] if result.respond_to?(:fetch)
186
+ block ? block.call(result) : result
183
187
  end
184
188
  end
185
189
 
186
- # Fetches a photo data.
190
+ # Fetches a photo data hash.
187
191
  #
188
192
  # @param args (see #get_object)
189
193
  # @param options (see Koala::Facebook::API#api)
190
194
  # @param block (see Koala::Facebook::API#api)
191
195
  #
192
196
  # @return a hash of object data
193
- def get_user_picture_data(object, args = {}, options = {}, &block)
194
- graph_call("#{object}/picture", args.merge(:redirect => false), "get", options, &block)
197
+ def get_picture_data(object, args = {}, options = {}, &block)
198
+ # The default response for a Graph API query like GET /me/picture is to
199
+ # return a 302 redirect. This is a surprising difference from the
200
+ # common return type, so we add the `redirect: false` parameter to get
201
+ # a RESTful API response instead.
202
+ args = args.merge(:redirect => false)
203
+ graph_call("#{object}/picture", args, "get", options, &block)
204
+ end
205
+
206
+ def get_user_picture_data(*args, &block)
207
+ Koala::Utils.deprecate("API#get_user_picture_data is deprecated and will be removed in a future version. Please use API#get_picture_data, which has the same signature.")
208
+ get_picture_data(*args, &block)
195
209
  end
196
210
 
197
211
  # Upload a photo.
@@ -511,7 +525,7 @@ module Koala
511
525
  # enable appsecret_proof by default
512
526
  options = {:appsecret_proof => true}.merge(options) if @app_secret
513
527
  result = api(path, args, verb, options) do |response|
514
- error = check_response(response.status, response.body)
528
+ error = check_response(response.status, response.body, response.headers)
515
529
  raise error if error
516
530
  end
517
531
 
@@ -524,39 +538,8 @@ module Koala
524
538
 
525
539
  private
526
540
 
527
- def check_response(http_status, response_body)
528
- # Check for Graph API-specific errors. This returns an error of the appropriate type
529
- # which is immediately raised (non-batch) or added to the list of batch results (batch)
530
- http_status = http_status.to_i
531
-
532
- if http_status >= 400
533
- begin
534
- response_hash = MultiJson.load(response_body)
535
- rescue MultiJson::DecodeError
536
- response_hash = {}
537
- end
538
-
539
- if response_hash['error_code']
540
- # Old batch api error format. This can be removed on July 5, 2012.
541
- # See https://developers.facebook.com/roadmap/#graph-batch-api-exception-format
542
- error_info = {
543
- 'code' => response_hash['error_code'],
544
- 'message' => response_hash['error_description']
545
- }
546
- else
547
- error_info = response_hash['error'] || {}
548
- end
549
-
550
- if error_info['type'] == 'OAuthException' &&
551
- ( !error_info['code'] || [102, 190, 450, 452, 2500].include?(error_info['code'].to_i))
552
-
553
- # See: https://developers.facebook.com/docs/authentication/access-token-expiration/
554
- # https://developers.facebook.com/bugs/319643234746794?browse=search_4fa075c0bd9117b20604672
555
- AuthenticationError.new(http_status, response_body, error_info)
556
- else
557
- ClientError.new(http_status, response_body, error_info)
558
- end
559
- end
541
+ def check_response(http_status, body, headers)
542
+ GraphErrorChecker.new(http_status, body, headers).error_if_appropriate
560
543
  end
561
544
 
562
545
  def parse_media_args(media_args, method)
@@ -22,7 +22,7 @@ module Koala
22
22
  # normalize options for consistency
23
23
  options = Koala::Utils.symbolize_hash(options)
24
24
 
25
- # for batch APIs, we queue up the call details (incl. post-processing)
25
+ # for batch APIs, we queue up the call details (incl. post-processing)
26
26
  batch_calls << BatchOperation.new(
27
27
  :url => path,
28
28
  :args => args,
@@ -65,7 +65,13 @@ module Koala
65
65
 
66
66
  raw_result = nil
67
67
  if call_result
68
- if ( error = check_response(call_result['code'], call_result['body'].to_s) )
68
+ parsed_headers = if call_result.has_key?('headers')
69
+ call_result['headers'].inject({}) { |headers, h| headers[h['name']] = h['value']; headers}
70
+ else
71
+ {}
72
+ end
73
+
74
+ if (error = check_response(call_result['code'], call_result['body'].to_s, parsed_headers))
69
75
  raw_result = error
70
76
  else
71
77
  # (see note in regular api method about JSON parsing)
@@ -77,7 +83,7 @@ module Koala
77
83
  call_result["code"].to_i
78
84
  when :headers
79
85
  # facebook returns the headers as an array of k/v pairs, but we want a regular hash
80
- call_result['headers'].inject({}) { |headers, h| headers[h['name']] = h['value']; headers}
86
+ parsed_headers
81
87
  else
82
88
  body
83
89
  end
@@ -0,0 +1,71 @@
1
+ module Koala
2
+ module Facebook
3
+ # This class, given a Koala::HTTPService::Response object, will check for Graph API-specific
4
+ # errors. This returns an error of the appropriate type which can be immediately raised
5
+ # (non-batch) or added to the list of batch results (batch)
6
+ class GraphErrorChecker
7
+ attr_reader :http_status, :body, :headers
8
+ def initialize(http_status, body, headers)
9
+ @http_status = http_status.to_i
10
+ @body = body
11
+ @headers = headers
12
+ end
13
+
14
+ # Facebook has a set of standardized error codes, some of which represent problems with the
15
+ # token.
16
+ AUTHENTICATION_ERROR_CODES = [102, 190, 450, 452, 2500]
17
+
18
+ # Facebook can return debug information in the response headers -- see
19
+ # https://developers.facebook.com/docs/graph-api/using-graph-api#bugdebug
20
+ DEBUG_HEADERS = ["x-fb-debug", "x-fb-rev", "x-fb-trace-id"]
21
+
22
+ def error_if_appropriate
23
+ if http_status >= 400
24
+ error_class.new(http_status, body, error_info)
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def error_class
31
+ if auth_error?
32
+ # See: https://developers.facebook.com/docs/authentication/access-token-expiration/
33
+ # https://developers.facebook.com/bugs/319643234746794?browse=search_4fa075c0bd9117b20604672
34
+ AuthenticationError
35
+ else
36
+ ClientError
37
+ end
38
+ end
39
+
40
+ def auth_error?
41
+ # tbh, I'm not sure why we restrict Facebook-reported OAuthExceptions to only those without
42
+ # codes or whose codes match the list above -- let's investigate changing this later.
43
+ error_info['type'] == 'OAuthException' &&
44
+ (!error_info['code'] || AUTHENTICATION_ERROR_CODES.include?(error_info['code'].to_i))
45
+ end
46
+
47
+ def error_info
48
+ # Build up the complete error info from whatever Facebook gives us plus the header
49
+ # information
50
+ @error_info ||= DEBUG_HEADERS.inject(base_error_info) do |hash, error_key|
51
+ hash[error_key] = headers[error_key] if headers[error_key]
52
+ hash
53
+ end
54
+ end
55
+
56
+ def base_error_info
57
+ response_hash['error'] || {}
58
+ end
59
+
60
+ def response_hash
61
+ # Normally, we start with the response body. If it isn't valid JSON, we start with an empty
62
+ # hash and fill it with error data.
63
+ @response_hash ||= begin
64
+ MultiJson.load(body)
65
+ rescue MultiJson::DecodeError
66
+ {}
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -13,8 +13,17 @@ module Koala
13
13
  # Facebook responded with an error to an API request. If the exception contains a nil
14
14
  # http_status, then the error was detected before making a call to Facebook. (e.g. missing access token)
15
15
  class APIError < ::Koala::KoalaError
16
- attr_accessor :fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message,
17
- :fb_error_user_msg, :fb_error_user_title, :http_status, :response_body
16
+ attr_accessor :http_status,
17
+ :response_body,
18
+ :fb_error_type,
19
+ :fb_error_code,
20
+ :fb_error_subcode,
21
+ :fb_error_message,
22
+ :fb_error_user_msg,
23
+ :fb_error_user_title,
24
+ :fb_error_trace_id,
25
+ :fb_error_debug,
26
+ :fb_error_rev
18
27
 
19
28
  # Create a new API Error
20
29
  #
@@ -54,8 +63,12 @@ module Koala
54
63
  self.fb_error_user_msg = error_info["error_user_msg"]
55
64
  self.fb_error_user_title = error_info["error_user_title"]
56
65
 
66
+ self.fb_error_trace_id = error_info["x-fb-trace-id"]
67
+ self.fb_error_debug = error_info["x-fb-debug"]
68
+ self.fb_error_rev = error_info["x-fb-rev"]
69
+
57
70
  error_array = []
58
- %w(type code error_subcode message error_user_title error_user_msg).each do |key|
71
+ %w(type code error_subcode message error_user_title error_user_msg x-fb-trace-id).each do |key|
59
72
  error_array << "#{key}: #{error_info[key]}" if error_info[key]
60
73
  end
61
74
 
@@ -197,7 +197,7 @@ module Koala
197
197
  # @return the application access token
198
198
  def get_app_access_token(options = {})
199
199
  if info = get_app_access_token_info(options)
200
- string = info["access_token"]
200
+ info["access_token"]
201
201
  end
202
202
  end
203
203
 
@@ -1,3 +1,3 @@
1
1
  module Koala
2
- VERSION = "2.2.0"
2
+ VERSION = "2.3.0rc1"
3
3
  end
data/readme.md CHANGED
@@ -7,12 +7,14 @@ Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/
7
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.
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
+ **Found a bug? Interested in contributing?** Check out the Maintenance section below!
11
+
10
12
  Installation
11
- ---
13
+ ------------
12
14
 
13
15
  In Bundler:
14
16
  ```ruby
15
- gem "koala", "~> 2.0"
17
+ gem "koala", "~> 2.2"
16
18
  ```
17
19
 
18
20
  Otherwise:
@@ -20,8 +22,8 @@ Otherwise:
20
22
  [sudo|rvm] gem install koala
21
23
  ```
22
24
 
23
- Upgrading to 2.0
24
- ---------
25
+ Upgrading to 2.0+
26
+ -----------------
25
27
 
26
28
  Koala 2.0 is not a major refactor, but rather a set of small, mostly internal
27
29
  refactors, which should not require significant changes by users. See changelog.md for more
@@ -112,7 +114,8 @@ the results apart from a long list of array entries:
112
114
  Check out the wiki for more details and examples.
113
115
 
114
116
  The REST API
115
- -----
117
+ ------------
118
+
116
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.
117
120
 
118
121
  Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
@@ -132,7 +135,8 @@ fql = @api.fql_query(my_fql_query)
132
135
  ```
133
136
 
134
137
  Configuration
135
- ----
138
+ -------------
139
+
136
140
  You can change the host that koala makes requests to (point to a mock server, apigee, runscope etc..)
137
141
  ```ruby
138
142
  # config/initializers/koala.rb
@@ -150,6 +154,7 @@ tier and video upload matching and replacement strings.
150
154
 
151
155
  OAuth
152
156
  -----
157
+
153
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:
154
159
  ```ruby
155
160
  @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
@@ -189,7 +194,8 @@ Or, if for some horrible reason, you're still using session keys, despair not!
189
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).
190
195
 
191
196
  Real-time Updates
192
- -----
197
+ -----------------
198
+
193
199
  Sometimes, reaching out to Facebook is a pain -- let it reach out to you instead. The Graph API allows your application to subscribe to real-time updates for certain objects in the graph; check the [official Facebook documentation](http://developers.facebook.com/docs/api/realtime) for more details on what objects you can subscribe to and what limitations may apply.
194
200
 
195
201
  Koala makes it easy to interact with your applications using the RealtimeUpdates class:
@@ -215,7 +221,7 @@ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
215
221
  For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
216
222
 
217
223
  Test Users
218
- -----
224
+ ----------
219
225
 
220
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:
221
227
  ```ruby
@@ -226,7 +232,7 @@ user_graph_api = Koala::Facebook::API.new(user["access_token"])
226
232
  @test_users.create_network(network_size, is_app_installed, common_permissions)
227
233
  ```
228
234
  Talking to Facebook
229
- -----
235
+ -------------------
230
236
 
231
237
  Koala uses Faraday to make HTTP requests, which means you have complete control over how your app makes HTTP requests to Facebook. You can set Faraday options globally or pass them in on a per-request (or both):
232
238
  ```ruby
@@ -240,7 +246,7 @@ Koala.http_service.http_options = {
240
246
  The <a href="https://github.com/arsduo/koala/wiki/HTTP-Services">HTTP Services wiki page</a> has more information on what options are available, as well as on how to configure your own Faraday middleware stack (for instance, to implement request logging).
241
247
 
242
248
  See examples, ask questions
243
- -----
249
+ ---------------------------
244
250
 
245
251
  Some resources to help you as you play with Koala and the Graph API:
246
252
 
@@ -257,7 +263,7 @@ issues filed about how to use the Facebook API may be closed with a reference
257
263
  to the Facebook Stack Overflow page.
258
264
 
259
265
  Testing
260
- -----
266
+ -------
261
267
 
262
268
  Unit tests are provided for all of Koala's methods. By default, these tests run against mock responses and hence are ready out of the box:
263
269
  ```bash
@@ -273,3 +279,25 @@ LIVE=true bundle exec rake spec
273
279
  LIVE=true BETA=true bundle exec rake spec
274
280
  ```
275
281
  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
+
283
+ Maintenance
284
+ -----------
285
+
286
+ _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.
289
+
290
+ Please note that this project is released with a Contributor Code of Conduct. By participating in
291
+ this project you agree to abide by its terms. See
292
+ [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.
@@ -5,7 +5,7 @@ describe Koala::Facebook::APIError do
5
5
  expect(Koala::Facebook::APIError.new(nil, nil)).to be_a(Koala::KoalaError)
6
6
  end
7
7
 
8
- [:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :fb_error_user_msg, :fb_error_user_title, :http_status, :response_body].each do |accessor|
8
+ [:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :fb_error_user_msg, :fb_error_user_title, :fb_error_trace_id, :fb_error_rev, :fb_error_debug, :http_status, :response_body].each do |accessor|
9
9
  it "has an accessor for #{accessor}" do
10
10
  expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(accessor)
11
11
  expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(:"#{accessor}=")
@@ -29,7 +29,10 @@ describe Koala::Facebook::APIError do
29
29
  'code' => 1,
30
30
  'error_subcode' => 'subcode',
31
31
  'error_user_msg' => 'error user message',
32
- 'error_user_title' => 'error user title'
32
+ 'error_user_title' => 'error user title',
33
+ 'x-fb-trace-id' => 'fb trace id',
34
+ 'x-fb-debug' => 'fb debug token',
35
+ 'x-fb-rev' => 'fb revision'
33
36
  }
34
37
  Koala::Facebook::APIError.new(400, '', error_info)
35
38
  }
@@ -40,7 +43,10 @@ describe Koala::Facebook::APIError do
40
43
  :fb_error_code => 1,
41
44
  :fb_error_subcode => 'subcode',
42
45
  :fb_error_user_msg => 'error user message',
43
- :fb_error_user_title => 'error user title'
46
+ :fb_error_user_title => 'error user title',
47
+ :fb_error_trace_id => 'fb trace id',
48
+ :fb_error_debug => 'fb debug token',
49
+ :fb_error_rev => 'fb revision'
44
50
  }.each_pair do |accessor, value|
45
51
  it "sets #{accessor} to #{value}" do
46
52
  expect(error.send(accessor)).to eq(value)
@@ -48,7 +54,7 @@ describe Koala::Facebook::APIError do
48
54
  end
49
55
 
50
56
  it "sets the error message appropriately" do
51
- expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_msg: error user message [HTTP 400]")
57
+ expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_msg: error user message, x-fb-trace-id: fb trace id [HTTP 400]")
52
58
  end
53
59
  end
54
60
 
@@ -401,30 +401,7 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
401
401
  }.to raise_exception(Koala::Facebook::BadFacebookResponse)
402
402
  end
403
403
 
404
- context "with the old style" do
405
- before :each do
406
- allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error_code":190,"error_description":"Error validating access token."}', {}))
407
- end
408
-
409
- it "throws an error" do
410
- expect {
411
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
412
- }.to raise_exception(Koala::Facebook::APIError)
413
- end
414
-
415
- it "passes all the error details" do
416
- begin
417
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
418
- rescue Koala::Facebook::APIError => err
419
- expect(err.fb_error_code).to eq(190)
420
- expect(err.fb_error_message).to eq("Error validating access token.")
421
- err.http_status == 400
422
- err.response_body == '{"error_code":190,"error_description":"Error validating access token."}'
423
- end
424
- end
425
- end
426
-
427
- context "with the new style" do
404
+ context "with error info" do
428
405
  before :each do
429
406
  allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error":{"message":"Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests","type":"GraphBatchException"}}', {}))
430
407
  end
@@ -30,7 +30,7 @@ describe 'Koala::Facebook::GraphAPIMethods' do
30
30
 
31
31
  context '#get_picture' do
32
32
  it 'returns result of block' do
33
- allow(@api).to receive(:api).and_return("Location" => double("other result"))
33
+ allow(@api).to receive(:api).and_return({"data" => {"is_silhouette" => false, "url" => result}})
34
34
  expect(@api.get_picture('lukeshepard', &post_processing)["result"]).to eq(result)
35
35
  end
36
36
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ module Koala
4
+ module Facebook
5
+ RSpec.describe GraphErrorChecker do
6
+ it "defines a set of AUTHENTICATION_ERROR_CODES" do
7
+ expect(GraphErrorChecker::AUTHENTICATION_ERROR_CODES).to match_array([102, 190, 450, 452, 2500])
8
+ end
9
+
10
+ it "defines a set of DEBUG_HEADERS" do
11
+ expect(GraphErrorChecker::DEBUG_HEADERS).to match_array([
12
+ "x-fb-rev",
13
+ "x-fb-debug",
14
+ "x-fb-trace-id"
15
+ ])
16
+ end
17
+
18
+ describe "#error_if_appropriate" do
19
+ shared_examples_for :returning_no_error do |status|
20
+ it "returns no error" do
21
+ expect(GraphErrorChecker.new(status, "{}", {}).error_if_appropriate).to be_nil
22
+ end
23
+
24
+ it "ignores error data even if present" do
25
+ checker = GraphErrorChecker.new(
26
+ status,
27
+ {"error" => {"some" => "error"}},
28
+ {"x-fb-rev" => "data"}
29
+ )
30
+ expect(checker.error_if_appropriate).to be_nil
31
+ end
32
+ end
33
+
34
+ context "if the status is 2xx" do
35
+ it_should_behave_like :returning_no_error, 202
36
+ end
37
+
38
+ context "if the status is 3xx" do
39
+ it_should_behave_like :returning_no_error, 302
40
+ end
41
+
42
+ shared_examples_for :returns_an_error do |status|
43
+ let(:body) { "{}" }
44
+ let(:headers) { {} }
45
+ let(:error) { GraphErrorChecker.new(status, body, headers).error_if_appropriate }
46
+ it "returns a ClientError for a generic error" do
47
+ expect(error).to be_a(ClientError)
48
+ expect(error.response_body).to eq(body)
49
+ end
50
+
51
+ it "returns a ClientError even if the body can't be parsed as JSON" do
52
+ body.replace("hello from Chicago")
53
+ expect(error).to be_a(ClientError)
54
+ expect(error.response_body).to eq(body)
55
+ end
56
+
57
+ it "adds error data from the body" do
58
+ error_data = {
59
+ "type" => "FB error type",
60
+ "code" => "FB error code",
61
+ "error_subcode" => "FB error subcode",
62
+ "message" => "An error occurred!",
63
+ "error_user_msg" => "A user msg",
64
+ "error_user_title" => "usr title"
65
+ }
66
+ body.replace({"error" => error_data}.to_json)
67
+
68
+ expect(error.fb_error_type).to eq(error_data["type"])
69
+ expect(error.fb_error_code).to eq(error_data["code"])
70
+ expect(error.fb_error_subcode).to eq(error_data["error_subcode"])
71
+ expect(error.fb_error_message).to eq(error_data["message"])
72
+ expect(error.fb_error_user_msg).to eq(error_data["error_user_msg"])
73
+ expect(error.fb_error_user_title).to eq(error_data["error_user_title"])
74
+ end
75
+
76
+ it "adds the FB debug headers to the errors" do
77
+ headers.merge!(
78
+ "x-fb-debug" => double("fb debug"),
79
+ "x-fb-rev" => double("fb rev"),
80
+ "x-fb-trace-id" => double("fb trace id"),
81
+ )
82
+ expect(error.fb_error_trace_id).to eq(headers["x-fb-trace-id"])
83
+ expect(error.fb_error_debug).to eq(headers["x-fb-debug"])
84
+ expect(error.fb_error_rev).to eq(headers["x-fb-rev"])
85
+ end
86
+
87
+ context "it returns an AuthenticationError" do
88
+ it "if FB says it's an OAuthException and it has no code" do
89
+ body.replace({"error" => {"type" => "OAuthException"}}.to_json)
90
+ expect(error).to be_an(AuthenticationError)
91
+ end
92
+
93
+ GraphErrorChecker::AUTHENTICATION_ERROR_CODES.each do |error_code|
94
+ it "if FB says it's an OAuthException and it has code #{error_code}" do
95
+ body.replace({"error" => {"type" => "OAuthException", "code" => error_code}}.to_json)
96
+ expect(error).to be_an(AuthenticationError)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Note: I'm not sure why this behavior was implemented, it may be incorrect. To
102
+ # investigate.
103
+ it "doesn't return an AuthenticationError if FB says it's an OAuthException but the code doesn't match" do
104
+ body.replace({"error" => {"type" => "OAuthException", "code" => 2499}}.to_json)
105
+ expect(error).to be_an(ClientError)
106
+ end
107
+ end
108
+
109
+ context "if the status is 4xx" do
110
+ it_should_behave_like :returns_an_error, 400
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -585,18 +585,18 @@ describe "Koala::Facebook::OAuth" do
585
585
  # so we only need to test at a high level that it works
586
586
  it "throws an error if the algorithm is unsupported" do
587
587
  allow(MultiJson).to receive(:load).and_return("algorithm" => "my fun algorithm")
588
- expect { @oauth.parse_signed_request(@signed_request) }.to raise_error
588
+ expect { @oauth.parse_signed_request(@signed_params) }.to raise_error(Koala::Facebook::OAuthSignatureError)
589
589
  end
590
590
 
591
591
  it "throws an error if the signature is invalid" do
592
592
  allow(OpenSSL::HMAC).to receive(:hexdigest).and_return("i'm an invalid signature")
593
- expect { @oauth.parse_signed_request(@signed_request) }.to raise_error
593
+ expect { @oauth.parse_signed_request(@signed_params) }.to raise_error(Koala::Facebook::OAuthSignatureError)
594
594
  end
595
595
 
596
596
  it "throws an error if the signature string is empty" do
597
597
  # this occasionally happens due to Facebook error
598
- expect { @oauth.parse_signed_request("") }.to raise_error
599
- expect { @oauth.parse_signed_request("abc-def") }.to raise_error
598
+ expect { @oauth.parse_signed_request("") }.to raise_error(Koala::Facebook::OAuthSignatureError)
599
+ expect { @oauth.parse_signed_request("abc-def") }.to raise_error(Koala::Facebook::OAuthSignatureError)
600
600
  end
601
601
 
602
602
  it "properly parses requests" do
@@ -106,18 +106,6 @@ describe "Koala::Facebook::RealtimeUpdates" do
106
106
  @updates.subscribe("user", "name", @subscription_path, @verify_token)
107
107
  end
108
108
 
109
- pending "doesn't require a verify_token" do
110
- # see https://github.com/arsduo/koala/issues/150
111
- obj = "user"
112
- fields = "name"
113
- expect(@updates.api).not_to receive(:graph_call).with(anything, hash_including(:verify_token => anything), anything, anything)
114
- @updates.subscribe("user", "name", @subscription_path)
115
- end
116
-
117
- it "requires verify_token" do
118
- expect { @updates.subscribe("user", "name", @subscription_path) }.to raise_exception
119
- end
120
-
121
109
  it "accepts an options hash" do
122
110
  options = {:a => 2, :b => "c"}
123
111
  expect(@updates.api).to receive(:graph_call).with(anything, anything, anything, hash_including(options))
@@ -129,10 +117,6 @@ describe "Koala::Facebook::RealtimeUpdates" do
129
117
  expect { @updates.subscribe("user", "name", @subscription_path, @verify_token) }.to_not raise_error
130
118
  end
131
119
 
132
- pending "sends a subscription request without a verify token" do
133
- expect { @updates.subscribe("user", "name", @subscription_path) }.to_not raise_error
134
- end
135
-
136
120
  it "fails if you try to hit an invalid path on your valid server" do
137
121
  expect { result = @updates.subscribe("user", "name", @subscription_path + "foo", @verify_token) }.to raise_exception(Koala::Facebook::APIError)
138
122
  end
@@ -261,10 +261,6 @@ describe "Koala::Facebook::TestUsers" do
261
261
  expect(result).to be_truthy
262
262
  end
263
263
 
264
- it "does not accept user IDs anymore" do
265
- expect { @test_users.befriend(@user1["id"], @user2["id"]) }.to raise_exception
266
- end
267
-
268
264
  it "accepts http options passed to both calls" do
269
265
  options = {:some_http_option => true}
270
266
  # should come twice, once for each user
@@ -79,7 +79,10 @@ graph_api:
79
79
  with_token: '[{"code": 200, "body":"{\"id\":\"123\"}"}, {"code": 200, "body":"{\"id\":\"456\"}"}]'
80
80
  batch=<%= MultiJson.dump([{"method" => "get", "relative_url" => "me/picture"}]) %>:
81
81
  post:
82
- with_token: '[{"code": 200, "headers":[{"name":"Location","value":"http://google.com"}]}]'
82
+ with_token: '[{"code": 302, "headers":[{"name":"Location","value":"http://google.com"}]}]'
83
+ batch=<%= MultiJson.dump([{"method" => "get", "relative_url" => "me/picture?redirect=false"}]) %>:
84
+ post:
85
+ with_token: '[{"code": 200, "body":"{\"data\":{\"is_silhouette\":false,\"url\":\"http:\/\/google.com\"}}"}]'
83
86
  batch=<%= MultiJson.dump([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}]) %>:
84
87
  post:
85
88
  with_token: '[{"code": 200, "body":"{\"id\":\"koppel\"}"}, {"code": 200, "body":"{\"data\":[{\"id\":\"lukeshepard\"}],\"paging\":{}}"}]'
@@ -167,6 +170,11 @@ graph_api:
167
170
  post:
168
171
  with_token: '{"id": "FEED_ITEM_DICTIONARY"}'
169
172
 
173
+ /me/picture:
174
+ redirect=false:
175
+ no_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com" } }'
176
+ with_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com" } }'
177
+
170
178
  /me/photos:
171
179
  source=[FILE]:
172
180
  post:
@@ -248,11 +256,15 @@ graph_api:
248
256
  code: 302
249
257
  headers:
250
258
  Location: https://facebook.com/large
251
-
252
259
  redirect=false:
253
260
  get:
254
- no_token: '{"is_silhouette": true, "url": "https://facebook.com/large"}'
255
- with_token: '{"is_silhouette": true, "url": "https://facebook.com/large"}'
261
+ no_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com" } }'
262
+ with_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com" } }'
263
+ redirect=false&type=large:
264
+ get:
265
+ no_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com/large" } }'
266
+ with_token: '{ "data": { "is_silhouette": true, "url": "https://facebook.com/large" } }'
267
+
256
268
  /comments:
257
269
  ids=http://developers.facebook.com/blog/post/472:
258
270
  get:
@@ -13,3 +13,13 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
13
13
  KoalaTest.setup_test_environment!
14
14
 
15
15
  BEACH_BALL_PATH = File.join(File.dirname(__FILE__), "fixtures", "beach.jpg")
16
+
17
+ RSpec.configure do |config|
18
+ config.mock_with :rspec do |mocks|
19
+ # This option should be set when all dependencies are being loaded
20
+ # before a spec run, as is the case in a typical spec helper. It will
21
+ # cause any verifying double instantiation for a class that does not
22
+ # exist to raise, protecting against incorrectly spelt names.
23
+ mocks.verify_doubled_constant_names = true
24
+ end
25
+ end
@@ -90,11 +90,11 @@ shared_examples_for "Koala GraphAPI" do
90
90
 
91
91
  describe "#get_picture" do
92
92
  it "can access a user's picture" do
93
- expect(@api.get_picture(KoalaTest.user2)).to match(/http[s]*\:\/\//)
93
+ expect(@api.get_picture(KoalaTest.user2)).to match(/https?\:\/\//)
94
94
  end
95
95
 
96
96
  it "can access a user's picture, given a picture type" do
97
- expect(@api.get_picture(KoalaTest.user2, {:type => 'large'})).to match(/^http[s]*\:\/\//)
97
+ expect(@api.get_picture(KoalaTest.user2, {:type => 'large'})).to match(/^https?\:\/\//)
98
98
  end
99
99
 
100
100
  it "works even if Facebook returns nil" do
@@ -103,9 +103,24 @@ shared_examples_for "Koala GraphAPI" do
103
103
  end
104
104
  end
105
105
 
106
- it "can access a user's picture data" do
107
- result = @api.get_user_picture_data(KoalaTest.user2)
108
- expect(result.key?("is_silhouette")).to be_truthy
106
+ describe "#get_picture_data" do
107
+ it "can access a user's picture data" do
108
+ result = @api.get_picture_data(KoalaTest.user2)
109
+ expect(result).to be_kind_of(Hash)
110
+ expect(result["data"]).to be_kind_of(Hash)
111
+ expect(result['data']).to be_truthy
112
+ expect(result['data'].keys).to include('is_silhouette', 'url')
113
+ end
114
+ end
115
+
116
+ describe "#get_user_picture_data" do
117
+ it "can access a user's picture data" do
118
+ result = @api.get_picture_data(KoalaTest.user2)
119
+ expect(result).to be_kind_of(Hash)
120
+ expect(result["data"]).to be_kind_of(Hash)
121
+ expect(result['data']).to be_truthy
122
+ expect(result['data'].keys).to include('is_silhouette', 'url')
123
+ end
109
124
  end
110
125
 
111
126
  it "can access connections from public Pages" 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: 2.2.0
4
+ version: 2.3.0rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Koppel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-11 00:00:00.000000000 Z
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -76,6 +76,7 @@ files:
76
76
  - Rakefile
77
77
  - autotest/discover.rb
78
78
  - changelog.md
79
+ - code_of_conduct.md
79
80
  - koala.gemspec
80
81
  - lib/koala.rb
81
82
  - lib/koala/api.rb
@@ -83,6 +84,7 @@ files:
83
84
  - lib/koala/api/graph_api.rb
84
85
  - lib/koala/api/graph_batch_api.rb
85
86
  - lib/koala/api/graph_collection.rb
87
+ - lib/koala/api/graph_error_checker.rb
86
88
  - lib/koala/api/rest_api.rb
87
89
  - lib/koala/errors.rb
88
90
  - lib/koala/http_service.rb
@@ -100,6 +102,7 @@ files:
100
102
  - spec/cases/graph_api_batch_spec.rb
101
103
  - spec/cases/graph_api_spec.rb
102
104
  - spec/cases/graph_collection_spec.rb
105
+ - spec/cases/graph_error_checker_spec.rb
103
106
  - spec/cases/http_service_spec.rb
104
107
  - spec/cases/koala_spec.rb
105
108
  - spec/cases/koala_test_spec.rb
@@ -141,9 +144,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
144
  version: '0'
142
145
  required_rubygems_version: !ruby/object:Gem::Requirement
143
146
  requirements:
144
- - - ">="
147
+ - - ">"
145
148
  - !ruby/object:Gem::Version
146
- version: '0'
149
+ version: 1.3.1
147
150
  requirements: []
148
151
  rubyforge_project:
149
152
  rubygems_version: 2.4.6
@@ -157,6 +160,7 @@ test_files:
157
160
  - spec/cases/graph_api_batch_spec.rb
158
161
  - spec/cases/graph_api_spec.rb
159
162
  - spec/cases/graph_collection_spec.rb
163
+ - spec/cases/graph_error_checker_spec.rb
160
164
  - spec/cases/http_service_spec.rb
161
165
  - spec/cases/koala_spec.rb
162
166
  - spec/cases/koala_test_spec.rb