koala 1.2.1 → 1.3.0

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.
Files changed (55) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +4 -0
  4. data/.yardopts +3 -0
  5. data/CHANGELOG +28 -0
  6. data/Gemfile +14 -0
  7. data/Guardfile +6 -0
  8. data/koala.gemspec +3 -3
  9. data/lib/koala/api/batch_operation.rb +83 -0
  10. data/lib/koala/api/graph_api.rb +476 -0
  11. data/lib/koala/{graph_batch_api.rb → api/graph_batch_api.rb} +22 -17
  12. data/lib/koala/api/graph_collection.rb +107 -0
  13. data/lib/koala/api/legacy.rb +26 -0
  14. data/lib/koala/{rest_api.rb → api/rest_api.rb} +34 -13
  15. data/lib/koala/api.rb +93 -0
  16. data/lib/koala/http_service/multipart_request.rb +41 -0
  17. data/lib/koala/http_service/response.rb +18 -0
  18. data/lib/koala/http_service/uploadable_io.rb +187 -0
  19. data/lib/koala/http_service.rb +69 -20
  20. data/lib/koala/oauth.rb +170 -36
  21. data/lib/koala/realtime_updates.rb +89 -51
  22. data/lib/koala/test_users.rb +122 -32
  23. data/lib/koala/utils.rb +11 -4
  24. data/lib/koala/version.rb +1 -1
  25. data/lib/koala.rb +16 -96
  26. data/readme.md +9 -9
  27. data/spec/cases/api_spec.rb +19 -12
  28. data/spec/cases/error_spec.rb +10 -0
  29. data/spec/cases/graph_api_batch_spec.rb +100 -58
  30. data/spec/cases/graph_collection_spec.rb +23 -7
  31. data/spec/cases/http_service_spec.rb +5 -26
  32. data/spec/cases/koala_spec.rb +22 -4
  33. data/spec/cases/legacy_spec.rb +115 -0
  34. data/spec/cases/multipart_request_spec.rb +7 -7
  35. data/spec/cases/oauth_spec.rb +134 -48
  36. data/spec/cases/realtime_updates_spec.rb +154 -47
  37. data/spec/cases/test_users_spec.rb +276 -219
  38. data/spec/cases/uploadable_io_spec.rb +1 -1
  39. data/spec/cases/utils_spec.rb +29 -5
  40. data/spec/fixtures/mock_facebook_responses.yml +41 -30
  41. data/spec/spec_helper.rb +3 -0
  42. data/spec/support/custom_matchers.rb +28 -0
  43. data/spec/support/graph_api_shared_examples.rb +192 -14
  44. data/spec/support/koala_test.rb +10 -1
  45. data/spec/support/mock_http_service.rb +2 -2
  46. data/spec/support/rest_api_shared_examples.rb +5 -165
  47. metadata +75 -99
  48. data/lib/koala/batch_operation.rb +0 -74
  49. data/lib/koala/graph_api.rb +0 -270
  50. data/lib/koala/graph_collection.rb +0 -59
  51. data/lib/koala/multipart_request.rb +0 -35
  52. data/lib/koala/uploadable_io.rb +0 -181
  53. data/spec/cases/graph_and_rest_api_spec.rb +0 -22
  54. data/spec/cases/graph_api_spec.rb +0 -22
  55. data/spec/cases/rest_api_spec.rb +0 -22
@@ -1,30 +1,37 @@
1
1
  require 'faraday'
2
+ require 'koala/http_service/multipart_request'
3
+ require 'koala/http_service/uploadable_io'
4
+ require 'koala/http_service/response'
2
5
 
3
6
  module Koala
4
- class Response
5
- attr_reader :status, :body, :headers
6
- def initialize(status, body, headers)
7
- @status = status
8
- @body = body
9
- @headers = headers
10
- end
11
- end
12
-
13
- module HTTPService
14
- # common functionality for all HTTP services
15
-
7
+ module HTTPService
16
8
  class << self
17
- attr_accessor :faraday_middleware, :http_options
9
+ # A customized stack of Faraday middleware that will be used to make each request.
10
+ attr_accessor :faraday_middleware
11
+ # A default set of HTTP options (see https://github.com/arsduo/koala/wiki/HTTP-Services)
12
+ attr_accessor :http_options
18
13
  end
19
14
 
20
15
  @http_options ||= {}
21
16
 
17
+ # Koala's default middleware stack.
18
+ # We encode requests in a Facebook-compatible multipart request,
19
+ # and use whichever adapter has been configured for this application.
22
20
  DEFAULT_MIDDLEWARE = Proc.new do |builder|
23
- builder.use Koala::MultipartRequest
21
+ builder.use Koala::HTTPService::MultipartRequest
24
22
  builder.request :url_encoded
25
23
  builder.adapter Faraday.default_adapter
26
24
  end
27
25
 
26
+ # The address of the appropriate Facebook server.
27
+ #
28
+ # @param options various flags to indicate which server to use.
29
+ # @option options :rest_api use the old REST API instead of the Graph API
30
+ # @option options :video use the server designated for video uploads
31
+ # @option options :beta use the beta tier
32
+ # @option options :use_ssl force https, even if not needed
33
+ #
34
+ # @return a complete server address with protocol
28
35
  def self.server(options = {})
29
36
  server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
30
37
  server.gsub!(/\.facebook/, "-video.facebook") if options[:video]
@@ -32,6 +39,23 @@ module Koala
32
39
  "#{options[:use_ssl] ? "https" : "http"}://#{server}"
33
40
  end
34
41
 
42
+ # Makes a request directly to Facebook.
43
+ # @note You'll rarely need to call this method directly.
44
+ #
45
+ # @see Koala::Facebook::API#api
46
+ # @see Koala::Facebook::GraphAPIMethods#graph_call
47
+ # @see Koala::Facebook::RestAPIMethods#rest_call
48
+ #
49
+ # @param path the server path for this request
50
+ # @param args (see Koala::Facebook::API#api)
51
+ # @param verb the HTTP method to use.
52
+ # If not get or post, this will be turned into a POST request with the appropriate :method
53
+ # specified in the arguments.
54
+ # @param options (see Koala::Facebook::API#api)
55
+ #
56
+ # @raise an appropriate connection error if unable to make the request to Facebook
57
+ #
58
+ # @return [Koala::HTTPService::Response] a response object representing the results from Facebook
35
59
  def self.make_request(path, args, verb, options = {})
36
60
  # if the verb isn't get or post, send it as a post argument
37
61
  args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
@@ -48,13 +72,20 @@ module Koala
48
72
  conn = Faraday.new(server(request_options), request_options, &(faraday_middleware || DEFAULT_MIDDLEWARE))
49
73
 
50
74
  response = conn.send(verb, path, (verb == "post" ? params : {}))
51
- Koala::Response.new(response.status.to_i, response.body, response.headers)
52
- end
53
-
75
+ Koala::HTTPService::Response.new(response.status.to_i, response.body, response.headers)
76
+ end
77
+
78
+ # Encodes a given hash into a query string.
79
+ # This is used mainly by the Batch API nowadays, since Faraday handles this for regular cases.
80
+ #
81
+ # @param params_hash a hash of values to CGI-encode and appropriately join
82
+ #
83
+ # @example
84
+ # Koala.http_service.encode_params({:a => 2, :b => "My String"})
85
+ # => "a=2&b=My+String"
86
+ #
87
+ # @return the appropriately-encoded string
54
88
  def self.encode_params(param_hash)
55
- # unfortunately, we can't use to_query because that's Rails, not Ruby
56
- # if no hash (e.g. no auth token) return empty string
57
- # this is used mainly by the Batch API nowadays
58
89
  ((param_hash || {}).collect do |key_and_value|
59
90
  key_and_value[1] = MultiJson.encode(key_and_value[1]) unless key_and_value[1].is_a? String
60
91
  "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
@@ -64,76 +95,92 @@ module Koala
64
95
  # deprecations
65
96
  # not elegant or compact code, but temporary
66
97
 
98
+ # @private
67
99
  def self.always_use_ssl
68
100
  Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.http_options[:use_ssl]; always_use_ssl will be removed in a future version.")
69
101
  http_options[:use_ssl]
70
102
  end
71
103
 
104
+ # @private
72
105
  def self.always_use_ssl=(value)
73
106
  Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.http_options[:use_ssl]; always_use_ssl will be removed in a future version.")
74
107
  http_options[:use_ssl] = value
75
108
  end
76
109
 
110
+ # @private
77
111
  def self.timeout
78
112
  Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
79
113
  http_options[:timeout]
80
114
  end
81
115
 
116
+ # @private
82
117
  def self.timeout=(value)
83
118
  Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
84
119
  http_options[:timeout] = value
85
120
  end
86
121
 
122
+ # @private
87
123
  def self.timeout
88
124
  Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
89
125
  http_options[:timeout]
90
126
  end
91
127
 
128
+ # @private
92
129
  def self.timeout=(value)
93
130
  Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
94
131
  http_options[:timeout] = value
95
132
  end
96
133
 
134
+ # @private
97
135
  def self.proxy
98
136
  Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.http_options[:proxy]; .proxy will be removed in a future version.")
99
137
  http_options[:proxy]
100
138
  end
101
139
 
140
+ # @private
102
141
  def self.proxy=(value)
103
142
  Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.http_options[:proxy]; .proxy will be removed in a future version.")
104
143
  http_options[:proxy] = value
105
144
  end
106
145
 
146
+ # @private
107
147
  def self.ca_path
108
148
  Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.http_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
109
149
  (http_options[:ssl] || {})[:ca_path]
110
150
  end
111
151
 
152
+ # @private
112
153
  def self.ca_path=(value)
113
154
  Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.http_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
114
155
  (http_options[:ssl] ||= {})[:ca_path] = value
115
156
  end
116
157
 
158
+ # @private
117
159
  def self.ca_file
118
160
  Koala::Utils.deprecate("HTTPService.ca_file is now (HTTPService.http_options[:ssl] ||= {})[:ca_file]; .ca_file will be removed in a future version.")
119
161
  (http_options[:ssl] || {})[:ca_file]
120
162
  end
121
163
 
164
+ # @private
122
165
  def self.ca_file=(value)
123
166
  Koala::Utils.deprecate("HTTPService.ca_file is now (HTTPService.http_options[:ssl] ||= {})[:ca_file]; .ca_file will be removed in a future version.")
124
167
  (http_options[:ssl] ||= {})[:ca_file] = value
125
168
  end
126
169
 
170
+ # @private
127
171
  def self.verify_mode
128
172
  Koala::Utils.deprecate("HTTPService.verify_mode is now (HTTPService.http_options[:ssl] ||= {})[:verify_mode]; .verify_mode will be removed in a future version.")
129
173
  (http_options[:ssl] || {})[:verify_mode]
130
174
  end
131
175
 
176
+ # @private
132
177
  def self.verify_mode=(value)
133
178
  Koala::Utils.deprecate("HTTPService.verify_mode is now (HTTPService.http_options[:ssl] ||= {})[:verify_mode]; .verify_mode will be removed in a future version.")
134
179
  (http_options[:ssl] ||= {})[:verify_mode] = value
135
180
  end
136
181
 
182
+ private
183
+
137
184
  def self.process_options(options)
138
185
  if typhoeus_options = options.delete(:typhoeus_options)
139
186
  Koala::Utils.deprecate("typhoeus_options should now be included directly in the http_options hash. Support for this key will be removed in a future version.")
@@ -159,6 +206,7 @@ module Koala
159
206
  end
160
207
  end
161
208
 
209
+ # @private
162
210
  module TyphoeusService
163
211
  def self.deprecated_interface
164
212
  # support old-style interface with a warning
@@ -167,6 +215,7 @@ module Koala
167
215
  end
168
216
  end
169
217
 
218
+ # @private
170
219
  module NetHTTPService
171
220
  def self.deprecated_interface
172
221
  # support old-style interface with a warning
data/lib/koala/oauth.rb CHANGED
@@ -1,66 +1,165 @@
1
+ # OpenSSL and Base64 are required to support signed_request
2
+ require 'openssl'
3
+ require 'base64'
4
+
1
5
  module Koala
2
6
  module Facebook
7
+
8
+ DIALOG_HOST = "www.facebook.com"
9
+
3
10
  class OAuth
4
11
  attr_reader :app_id, :app_secret, :oauth_callback_url
12
+
13
+ # Creates a new OAuth client.
14
+ #
15
+ # @param app_id [String, Integer] a Facebook application ID
16
+ # @param app_secret a Facebook application secret
17
+ # @param oauth_callback_url the URL in your app to which users authenticating with OAuth will be sent
5
18
  def initialize(app_id, app_secret, oauth_callback_url = nil)
6
19
  @app_id = app_id
7
20
  @app_secret = app_secret
8
21
  @oauth_callback_url = oauth_callback_url
9
22
  end
10
-
11
- def get_user_info_from_cookie(cookie_hash)
12
- # Parses the cookie set Facebook's JavaScript SDK.
13
- # You can pass Rack/Rails/Sinatra's cookie hash directly to this method.
14
- #
15
- # If the user is logged in via Facebook, we return a dictionary with the
16
- # keys "uid" and "access_token". The former is the user's Facebook ID,
17
- # and the latter can be used to make authenticated requests to the Graph API.
18
- # If the user is not logged in, we return None.
19
-
23
+
24
+ # Parses the cookie set Facebook's JavaScript SDK.
25
+ #
26
+ # @note in parsing Facebook's new signed cookie format this method has to make a request to Facebook.
27
+ # We recommend storing authenticated user info in your Rails session (or equivalent) and only
28
+ # calling this when needed.
29
+ #
30
+ # @param cookie_hash a set of cookies that includes the Facebook cookie.
31
+ # You can pass Rack/Rails/Sinatra's cookie hash directly to this method.
32
+ #
33
+ # @return the authenticated user's information as a hash, or nil.
34
+ def get_user_info_from_cookies(cookie_hash)
20
35
  if signed_cookie = cookie_hash["fbsr_#{@app_id}"]
21
36
  parse_signed_cookie(signed_cookie)
22
37
  elsif unsigned_cookie = cookie_hash["fbs_#{@app_id}"]
23
38
  parse_unsigned_cookie(unsigned_cookie)
24
39
  end
25
40
  end
26
- alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
41
+ alias_method :get_user_info_from_cookie, :get_user_info_from_cookies
27
42
 
28
- def get_user_from_cookie(cookies)
29
- if info = get_user_info_from_cookies(cookies)
30
- # signed cookie has user_id, unsigned cookie has uid
31
- string = info["user_id"] || info["uid"]
43
+ # Parses the cookie set Facebook's JavaScript SDK and returns only the user ID.
44
+ #
45
+ # @note (see #get_user_info_from_cookie)
46
+ #
47
+ # @param (see #get_user_info_from_cookie)
48
+ #
49
+ # @return the authenticated user's Facebook ID, or nil.
50
+ def get_user_from_cookies(cookies)
51
+ if signed_cookie = cookies["fbsr_#{@app_id}"]
52
+ if components = parse_signed_request(signed_cookie)
53
+ components["user_id"]
54
+ end
55
+ elsif info = get_user_info_from_cookies(cookies)
56
+ # Parsing unsigned cookie
57
+ info["uid"]
32
58
  end
33
59
  end
34
- alias_method :get_user_from_cookies, :get_user_from_cookie
60
+ alias_method :get_user_from_cookie, :get_user_from_cookies
35
61
 
36
62
  # URLs
37
-
63
+
64
+ # Builds an OAuth URL, where users will be prompted to log in and for any desired permissions.
65
+ # When the users log in, you receive a callback with their
66
+ # See http://developers.facebook.com/docs/authentication/.
67
+ #
68
+ # @see #url_for_access_token
69
+ #
70
+ # @note The server-side authentication and dialog methods should only be used
71
+ # if your application can't use the Facebook Javascript SDK,
72
+ # which provides a much better user experience.
73
+ # See http://developers.facebook.com/docs/reference/javascript/.
74
+ #
75
+ # @param options any query values to add to the URL, as well as any special/required values listed below.
76
+ # @option options permissions an array or comma-separated string of desired permissions
77
+ #
78
+ # @raise ArgumentError if no OAuth callback was specified in OAuth#new or in options as :redirect_uri
79
+ #
80
+ # @return an OAuth URL you can send your users to
38
81
  def url_for_oauth_code(options = {})
39
82
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
40
- permissions = options[:permissions]
41
- scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
42
- display = options.has_key?(:display) ? "&display=#{options[:display]}" : ""
43
-
44
- callback = options[:callback] || @oauth_callback_url
45
- raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
46
-
83
+ if permissions = options.delete(:permissions)
84
+ options[:scope] = permissions.is_a?(Array) ? permissions.join(",") : permissions
85
+ end
86
+ url_options = {:client_id => @app_id}.merge(options)
87
+
47
88
  # Creates the URL for oauth authorization for a given callback and optional set of permissions
48
- "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}#{display}"
89
+ build_url("https://#{GRAPH_SERVER}/oauth/authorize", true, url_options)
49
90
  end
50
91
 
92
+ # Once you receive an OAuth code, you need to redeem it from Facebook using an appropriate URL.
93
+ # (This is done by your server behind the scenes.)
94
+ # See http://developers.facebook.com/docs/authentication/.
95
+ #
96
+ # @see #url_for_oauth_code
97
+ #
98
+ # @note (see #url_for_oauth_code)
99
+ #
100
+ # @param code an OAuth code received from Facebook
101
+ # @param options any additional query parameters to add to the URL
102
+ #
103
+ # @raise (see #url_for_oauth_code)
104
+ #
105
+ # @return an URL your server can query for the user's access token
51
106
  def url_for_access_token(code, options = {})
52
107
  # Creates the URL for the token corresponding to a given code generated by Facebook
53
- callback = options[:callback] || @oauth_callback_url
54
- raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
55
- "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
108
+ url_options = {
109
+ :client_id => @app_id,
110
+ :code => code,
111
+ :client_secret => @app_secret
112
+ }.merge(options)
113
+ build_url("https://#{GRAPH_SERVER}/oauth/access_token", true, url_options)
56
114
  end
57
115
 
116
+ # Builds a URL for a given dialog (feed, friends, OAuth, pay, send, etc.)
117
+ # See http://developers.facebook.com/docs/reference/dialogs/.
118
+ #
119
+ # @note (see #url_for_oauth_code)
120
+ #
121
+ # @param dialog_type the kind of Facebook dialog you want to show
122
+ # @param options any additional query parameters to add to the URL
123
+ #
124
+ # @return an URL your server can query for the user's access token
125
+ def url_for_dialog(dialog_type, options = {})
126
+ # some endpoints require app_id, some client_id, supply both doesn't seem to hurt
127
+ url_options = {:app_id => @app_id, :client_id => @app_id}.merge(options)
128
+ build_url("http://#{DIALOG_HOST}/dialog/#{dialog_type}", true, url_options)
129
+ end
130
+
131
+ # access tokens
132
+
133
+ # Fetches an access token, token expiration, and other info from Facebook.
134
+ # Useful when you've received an OAuth code using the server-side authentication process.
135
+ # @see url_for_oauth_code
136
+ #
137
+ # @note (see #url_for_oauth_code)
138
+ #
139
+ # @param code (see #url_for_access_token)
140
+ # @param options any additional parameters to send to Facebook when redeeming the token
141
+ #
142
+ # @raise Koala::Facebook::APIError if Facebook returns an error response
143
+ #
144
+ # @return a hash of the access token info returned by Facebook (token, expiration, etc.)
58
145
  def get_access_token_info(code, options = {})
59
146
  # convenience method to get a parsed token from Facebook for a given code
60
147
  # should this require an OAuth callback URL?
61
148
  get_token_from_server({:code => code, :redirect_uri => options[:redirect_uri] || @oauth_callback_url}, false, options)
62
149
  end
63
150
 
151
+
152
+ # Fetches the access token (ignoring expiration and other info) from Facebook.
153
+ # Useful when you've received an OAuth code using the server-side authentication process.
154
+ # @see get_access_token_info
155
+ #
156
+ # @note (see #url_for_oauth_code)
157
+ #
158
+ # @param (see #get_access_token_info)
159
+ #
160
+ # @raise (see #get_access_token_info)
161
+ #
162
+ # @return the access token
64
163
  def get_access_token(code, options = {})
65
164
  # upstream methods will throw errors if needed
66
165
  if info = get_access_token_info(code, options)
@@ -68,22 +167,36 @@ module Koala
68
167
  end
69
168
  end
70
169
 
170
+ # Fetches the application's access token, along with any other information provided by Facebook.
171
+ # See http://developers.facebook.com/docs/authentication/ (search for App Login).
172
+ #
173
+ # @param options any additional parameters to send to Facebook when redeeming the token
174
+ #
175
+ # @return the application access token and other information (expiration, etc.)
71
176
  def get_app_access_token_info(options = {})
72
177
  # convenience method to get a the application's sessionless access token
73
178
  get_token_from_server({:type => 'client_cred'}, true, options)
74
179
  end
75
180
 
181
+ # Fetches the application's access token (ignoring expiration and other info).
182
+ # @see get_app_access_token_info
183
+ #
184
+ # @param (see #get_app_access_token_info)
185
+ #
186
+ # @return the application access token
76
187
  def get_app_access_token(options = {})
77
188
  if info = get_app_access_token_info(options)
78
189
  string = info["access_token"]
79
190
  end
80
191
  end
81
192
 
82
- # Originally provided directly by Facebook, however this has changed
83
- # as their concept of crypto changed. For historic purposes, this is their proposal:
84
- # https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
85
- # Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
86
- # for a more accurate reference implementation strategy.
193
+ # Parses a signed request string provided by Facebook to canvas apps or in a secure cookie.
194
+ #
195
+ # @param input the signed request from Facebook
196
+ #
197
+ # @raise RuntimeError if the signature is incomplete, invalid, or using an unsupported algorithm
198
+ #
199
+ # @return a hash of the validated request information
87
200
  def parse_signed_request(input)
88
201
  encoded_sig, encoded_envelope = input.split('.', 2)
89
202
  raise 'SignedRequest: Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
@@ -100,8 +213,12 @@ module Koala
100
213
  envelope
101
214
  end
102
215
 
103
- # from session keys
216
+ # Old session key code
217
+
218
+ # @deprecated Facebook no longer provides session keys.
104
219
  def get_token_info_from_session_keys(sessions, options = {})
220
+ Koala::Utils.deprecate("Facebook no longer provides session keys. The relevant OAuth methods will be removed in the next release.")
221
+
105
222
  # fetch the OAuth tokens from Facebook
106
223
  response = fetch_token_string({
107
224
  :type => 'client_cred',
@@ -119,6 +236,7 @@ module Koala
119
236
  MultiJson.decode(response)
120
237
  end
121
238
 
239
+ # @deprecated (see #get_token_info_from_session_keys)
122
240
  def get_tokens_from_session_keys(sessions, options = {})
123
241
  # get the original hash results
124
242
  results = get_token_info_from_session_keys(sessions, options)
@@ -126,6 +244,7 @@ module Koala
126
244
  results.collect { |r| r ? r["access_token"] : nil }
127
245
  end
128
246
 
247
+ # @deprecated (see #get_token_info_from_session_keys)
129
248
  def get_token_from_session_key(session, options = {})
130
249
  # convenience method for a single key
131
250
  # gets the overlaoded strings automatically
@@ -169,8 +288,15 @@ module Koala
169
288
 
170
289
  def parse_signed_cookie(fb_cookie)
171
290
  components = parse_signed_request(fb_cookie)
172
- if (code = components["code"]) && token_info = get_access_token_info(code, :redirect_uri => '')
173
- components.merge(token_info)
291
+ if code = components["code"]
292
+ begin
293
+ token_info = get_access_token_info(code, :redirect_uri => '')
294
+ rescue Koala::Facebook::APIError => err
295
+ return nil if err.message =~ /Code was invalid or expired/
296
+ raise
297
+ end
298
+
299
+ components.merge(token_info) if token_info
174
300
  else
175
301
  nil
176
302
  end
@@ -189,6 +315,14 @@ module Koala
189
315
  str += '=' * (4 - str.length.modulo(4))
190
316
  Base64.decode64(str.tr('-_', '+/'))
191
317
  end
318
+
319
+ def build_url(base, require_redirect_uri = false, url_options = {})
320
+ if require_redirect_uri && !(url_options[:redirect_uri] ||= url_options.delete(:callback) || @oauth_callback_url)
321
+ raise ArgumentError, "url_for_dialog must get a callback either from the OAuth object or in the parameters!"
322
+ end
323
+
324
+ "#{base}?#{Koala::HTTPService.encode_params(url_options)}"
325
+ end
192
326
  end
193
327
  end
194
328
  end