koala 0.10.0 → 1.0.0.beta2.1
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.
- data/CHANGELOG +34 -7
- data/Manifest +8 -1
- data/Rakefile +4 -4
- data/koala.gemspec +10 -8
- data/lib/koala/graph_api.rb +188 -123
- data/lib/koala/http_services.rb +92 -22
- data/lib/koala/rest_api.rb +73 -6
- data/lib/koala/test_users.rb +18 -5
- data/lib/koala/uploadable_io.rb +115 -0
- data/lib/koala.rb +81 -72
- data/readme.md +18 -12
- data/spec/facebook_data.yml +18 -14
- data/spec/koala/assets/beach.jpg +0 -0
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +5 -1
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +8 -3
- data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +13 -62
- data/spec/koala/graph_api/graph_api_tests.rb +85 -0
- data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +167 -123
- data/spec/koala/http_services/http_service_tests.rb +51 -0
- data/spec/koala/http_services/net_http_service_tests.rb +339 -0
- data/spec/koala/http_services/typhoeus_service_tests.rb +162 -0
- data/spec/koala/live_testing_data_helper.rb +1 -1
- data/spec/koala/oauth/oauth_tests.rb +19 -85
- data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +5 -74
- data/spec/koala/rest_api/rest_api_tests.rb +118 -0
- data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +5 -3
- data/spec/koala/test_users/test_users_tests.rb +49 -48
- data/spec/koala/uploadable_io/uploadable_io_tests.rb +246 -0
- data/spec/koala_spec_helper.rb +32 -6
- data/spec/koala_spec_without_mocks.rb +3 -3
- data/spec/mock_facebook_responses.yml +43 -20
- data/spec/mock_http_service.rb +16 -3
- metadata +39 -8
- data/spec/koala/net_http_service_tests.rb +0 -186
data/lib/koala.rb
CHANGED
|
@@ -5,8 +5,9 @@ require 'digest/md5'
|
|
|
5
5
|
require 'rubygems'
|
|
6
6
|
require 'json'
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# OpenSSL and Base64 are required to support signed_request
|
|
9
9
|
require 'openssl'
|
|
10
|
+
require 'base64'
|
|
10
11
|
|
|
11
12
|
# include default http services
|
|
12
13
|
require 'koala/http_services'
|
|
@@ -23,8 +24,11 @@ require 'koala/realtime_updates'
|
|
|
23
24
|
# add test user methods
|
|
24
25
|
require 'koala/test_users'
|
|
25
26
|
|
|
27
|
+
# add KoalaIO class
|
|
28
|
+
require 'koala/uploadable_io'
|
|
29
|
+
|
|
26
30
|
module Koala
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
module Facebook
|
|
29
33
|
# Ruby client library for the Facebook Platform.
|
|
30
34
|
# Copyright 2010 Facebook
|
|
@@ -40,7 +44,7 @@ module Koala
|
|
|
40
44
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
41
45
|
# License for the specific language governing permissions and limitations
|
|
42
46
|
# under the License.
|
|
43
|
-
#
|
|
47
|
+
#
|
|
44
48
|
# This client library is designed to support the Graph API and the official
|
|
45
49
|
# Facebook JavaScript SDK, which is the canonical way to implement
|
|
46
50
|
# Facebook authentication. Read more about the Graph API at
|
|
@@ -53,7 +57,7 @@ module Koala
|
|
|
53
57
|
@access_token = access_token
|
|
54
58
|
end
|
|
55
59
|
attr_reader :access_token
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
|
|
58
62
|
# Fetches the given path in the Graph API.
|
|
59
63
|
args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
|
|
@@ -63,20 +67,20 @@ module Koala
|
|
|
63
67
|
|
|
64
68
|
# make the request via the provided service
|
|
65
69
|
result = Koala.make_request(path, args, verb, options)
|
|
66
|
-
|
|
70
|
+
|
|
67
71
|
# Check for any 500 errors before parsing the body
|
|
68
72
|
# since we're not guaranteed that the body is valid JSON
|
|
69
73
|
# in the case of a server error
|
|
70
74
|
raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
|
|
71
|
-
|
|
72
|
-
# Parse the body as JSON and check for errors if provided a mechanism to do so
|
|
75
|
+
|
|
76
|
+
# Parse the body as JSON and check for errors if provided a mechanism to do so
|
|
73
77
|
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
|
74
78
|
# and cause JSON.parse to fail -- so we account for that by wrapping the result in []
|
|
75
79
|
body = response = JSON.parse("[#{result.body.to_s}]")[0]
|
|
76
80
|
if error_checking_block
|
|
77
81
|
yield(body)
|
|
78
82
|
end
|
|
79
|
-
|
|
83
|
+
|
|
80
84
|
# now return the desired information
|
|
81
85
|
if options[:http_component]
|
|
82
86
|
result.send(options[:http_component])
|
|
@@ -85,57 +89,57 @@ module Koala
|
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
91
|
end
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
class GraphAPI < API
|
|
90
94
|
include GraphAPIMethods
|
|
91
95
|
end
|
|
92
|
-
|
|
96
|
+
|
|
93
97
|
class RestAPI < API
|
|
94
98
|
include RestAPIMethods
|
|
95
99
|
end
|
|
96
|
-
|
|
100
|
+
|
|
97
101
|
class GraphAndRestAPI < API
|
|
98
102
|
include GraphAPIMethods
|
|
99
103
|
include RestAPIMethods
|
|
100
104
|
end
|
|
101
|
-
|
|
105
|
+
|
|
102
106
|
class RealtimeUpdates < API
|
|
103
107
|
include RealtimeUpdateMethods
|
|
104
108
|
end
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
class TestUsers < API
|
|
107
111
|
include TestUserMethods
|
|
108
112
|
# make the Graph API accessible in case someone wants to make other calls to interact with their users
|
|
109
113
|
attr_reader :graph_api
|
|
110
114
|
end
|
|
111
|
-
|
|
112
|
-
class APIError <
|
|
115
|
+
|
|
116
|
+
class APIError < StandardError
|
|
113
117
|
attr_accessor :fb_error_type
|
|
114
118
|
def initialize(details = {})
|
|
115
|
-
self.fb_error_type = details["type"]
|
|
119
|
+
self.fb_error_type = details["type"]
|
|
116
120
|
super("#{fb_error_type}: #{details["message"]}")
|
|
117
121
|
end
|
|
118
122
|
end
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
|
|
124
|
+
|
|
121
125
|
class OAuth
|
|
122
126
|
attr_reader :app_id, :app_secret, :oauth_callback_url
|
|
123
127
|
def initialize(app_id, app_secret, oauth_callback_url = nil)
|
|
124
128
|
@app_id = app_id
|
|
125
129
|
@app_secret = app_secret
|
|
126
|
-
@oauth_callback_url = oauth_callback_url
|
|
130
|
+
@oauth_callback_url = oauth_callback_url
|
|
127
131
|
end
|
|
128
132
|
|
|
129
133
|
def get_user_info_from_cookie(cookie_hash)
|
|
130
134
|
# Parses the cookie set by the official Facebook JavaScript SDK.
|
|
131
|
-
#
|
|
135
|
+
#
|
|
132
136
|
# cookies should be a Hash, like the one Rails provides
|
|
133
|
-
#
|
|
137
|
+
#
|
|
134
138
|
# If the user is logged in via Facebook, we return a dictionary with the
|
|
135
139
|
# keys "uid" and "access_token". The former is the user's Facebook ID,
|
|
136
140
|
# and the latter can be used to make authenticated requests to the Graph API.
|
|
137
141
|
# If the user is not logged in, we return None.
|
|
138
|
-
#
|
|
142
|
+
#
|
|
139
143
|
# Download the official Facebook JavaScript SDK at
|
|
140
144
|
# http://github.com/facebook/connect-js/. Read more about Facebook
|
|
141
145
|
# authentication at http://developers.facebook.com/docs/authentication/.
|
|
@@ -150,21 +154,21 @@ module Koala
|
|
|
150
154
|
|
|
151
155
|
# generate the signature and make sure it matches what we expect
|
|
152
156
|
auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
|
|
153
|
-
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
|
157
|
+
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
|
154
158
|
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
|
155
159
|
end
|
|
156
160
|
end
|
|
157
161
|
alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
|
|
158
|
-
|
|
162
|
+
|
|
159
163
|
def get_user_from_cookie(cookies)
|
|
160
164
|
if info = get_user_info_from_cookies(cookies)
|
|
161
165
|
string = info["uid"]
|
|
162
166
|
end
|
|
163
167
|
end
|
|
164
168
|
alias_method :get_user_from_cookies, :get_user_from_cookie
|
|
165
|
-
|
|
169
|
+
|
|
166
170
|
# URLs
|
|
167
|
-
|
|
171
|
+
|
|
168
172
|
def url_for_oauth_code(options = {})
|
|
169
173
|
# for permissions, see http://developers.facebook.com/docs/authentication/permissions
|
|
170
174
|
permissions = options[:permissions]
|
|
@@ -174,54 +178,57 @@ module Koala
|
|
|
174
178
|
raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
|
|
175
179
|
|
|
176
180
|
# Creates the URL for oauth authorization for a given callback and optional set of permissions
|
|
177
|
-
"https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
|
|
181
|
+
"https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
|
|
178
182
|
end
|
|
179
|
-
|
|
183
|
+
|
|
180
184
|
def url_for_access_token(code, options = {})
|
|
181
185
|
# Creates the URL for the token corresponding to a given code generated by Facebook
|
|
182
186
|
callback = options[:callback] || @oauth_callback_url
|
|
183
187
|
raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
|
|
184
188
|
"https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
|
|
185
189
|
end
|
|
186
|
-
|
|
190
|
+
|
|
187
191
|
def get_access_token_info(code)
|
|
188
192
|
# convenience method to get a parsed token from Facebook for a given code
|
|
189
193
|
# should this require an OAuth callback URL?
|
|
190
194
|
get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
|
|
191
195
|
end
|
|
192
|
-
|
|
196
|
+
|
|
193
197
|
def get_access_token(code)
|
|
194
198
|
# upstream methods will throw errors if needed
|
|
195
|
-
if info = get_access_token_info(code)
|
|
196
|
-
string = info["access_token"]
|
|
199
|
+
if info = get_access_token_info(code)
|
|
200
|
+
string = info["access_token"]
|
|
197
201
|
end
|
|
198
202
|
end
|
|
199
|
-
|
|
203
|
+
|
|
200
204
|
def get_app_access_token_info
|
|
201
|
-
# convenience method to get a the application's sessionless access token
|
|
205
|
+
# convenience method to get a the application's sessionless access token
|
|
202
206
|
get_token_from_server({:type => 'client_cred'}, true)
|
|
203
207
|
end
|
|
204
|
-
|
|
208
|
+
|
|
205
209
|
def get_app_access_token
|
|
206
210
|
if info = get_app_access_token_info
|
|
207
|
-
string = info["access_token"]
|
|
211
|
+
string = info["access_token"]
|
|
208
212
|
end
|
|
209
213
|
end
|
|
210
|
-
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
214
|
+
|
|
215
|
+
# Originally provided directly by Facebook, however this has changed
|
|
216
|
+
# as their concept of crypto changed. For historic purposes, this is their proposal:
|
|
217
|
+
# https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
|
|
218
|
+
# Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
|
|
219
|
+
# for a more accurate reference implementation strategy.
|
|
220
|
+
def parse_signed_request(input)
|
|
221
|
+
encoded_sig, encoded_envelope = input.split('.', 2)
|
|
222
|
+
signature = base64_url_decode(encoded_sig).unpack("H*").first
|
|
223
|
+
envelope = JSON.parse(base64_url_decode(encoded_envelope))
|
|
224
|
+
|
|
225
|
+
raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
|
|
226
|
+
|
|
227
|
+
# now see if the signature is valid (digest, key, data)
|
|
228
|
+
hmac = OpenSSL::HMAC.hexdigest('sha256', @app_secret, encoded_envelope.tr("-_", "+/"))
|
|
229
|
+
raise 'SignedRequest: Invalid signature' if (signature != hmac)
|
|
230
|
+
|
|
231
|
+
return envelope
|
|
225
232
|
end
|
|
226
233
|
|
|
227
234
|
# from session keys
|
|
@@ -231,69 +238,70 @@ module Koala
|
|
|
231
238
|
:type => 'client_cred',
|
|
232
239
|
:sessions => sessions.join(",")
|
|
233
240
|
}, true, "exchange_sessions")
|
|
234
|
-
|
|
241
|
+
|
|
235
242
|
# Facebook returns an empty body in certain error conditions
|
|
236
|
-
if response == ""
|
|
243
|
+
if response == ""
|
|
237
244
|
raise APIError.new({
|
|
238
|
-
"type" => "ArgumentError",
|
|
245
|
+
"type" => "ArgumentError",
|
|
239
246
|
"message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
|
|
240
247
|
})
|
|
241
248
|
end
|
|
242
|
-
|
|
249
|
+
|
|
243
250
|
JSON.parse(response)
|
|
244
251
|
end
|
|
245
|
-
|
|
252
|
+
|
|
246
253
|
def get_tokens_from_session_keys(sessions)
|
|
247
254
|
# get the original hash results
|
|
248
255
|
results = get_token_info_from_session_keys(sessions)
|
|
249
256
|
# now recollect them as just the access tokens
|
|
250
257
|
results.collect { |r| r ? r["access_token"] : nil }
|
|
251
258
|
end
|
|
252
|
-
|
|
259
|
+
|
|
253
260
|
def get_token_from_session_key(session)
|
|
254
261
|
# convenience method for a single key
|
|
255
262
|
# gets the overlaoded strings automatically
|
|
256
263
|
get_tokens_from_session_keys([session])[0]
|
|
257
264
|
end
|
|
258
|
-
|
|
265
|
+
|
|
259
266
|
protected
|
|
260
|
-
|
|
267
|
+
|
|
261
268
|
def get_token_from_server(args, post = false)
|
|
262
269
|
# fetch the result from Facebook's servers
|
|
263
270
|
result = fetch_token_string(args, post)
|
|
264
|
-
|
|
271
|
+
|
|
265
272
|
# if we have an error, parse the error JSON and raise an error
|
|
266
273
|
raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
|
|
267
274
|
|
|
268
275
|
# otherwise, parse the access token
|
|
269
|
-
parse_access_token(result)
|
|
276
|
+
parse_access_token(result)
|
|
270
277
|
end
|
|
271
|
-
|
|
278
|
+
|
|
272
279
|
def parse_access_token(response_text)
|
|
273
280
|
components = response_text.split("&").inject({}) do |hash, bit|
|
|
274
281
|
key, value = bit.split("=")
|
|
275
282
|
hash.merge!(key => value)
|
|
276
283
|
end
|
|
277
|
-
components
|
|
284
|
+
components
|
|
278
285
|
end
|
|
279
286
|
|
|
280
287
|
def fetch_token_string(args, post = false, endpoint = "access_token")
|
|
281
288
|
Koala.make_request("/oauth/#{endpoint}", {
|
|
282
|
-
:client_id => @app_id,
|
|
289
|
+
:client_id => @app_id,
|
|
283
290
|
:client_secret => @app_secret
|
|
284
|
-
}.merge!(args), post ? "post" : "get").body
|
|
291
|
+
}.merge!(args), post ? "post" : "get", :use_ssl => true).body
|
|
285
292
|
end
|
|
286
|
-
|
|
293
|
+
|
|
287
294
|
# base 64
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
"#{string}==".tr("-_", "+/").unpack("m")[0]
|
|
295
|
+
# directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
|
|
296
|
+
def base64_url_decode(str)
|
|
297
|
+
str += '=' * (4 - str.length.modulo(4))
|
|
298
|
+
Base64.decode64(str.tr('-_', '+/'))
|
|
293
299
|
end
|
|
294
300
|
end
|
|
295
301
|
end
|
|
296
302
|
|
|
303
|
+
class KoalaError< StandardError; end
|
|
304
|
+
|
|
297
305
|
# finally, set up the http service Koala methods used to make requests
|
|
298
306
|
# you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
|
|
299
307
|
def self.http_service=(service)
|
|
@@ -301,8 +309,9 @@ module Koala
|
|
|
301
309
|
end
|
|
302
310
|
|
|
303
311
|
# by default, try requiring Typhoeus -- if that works, use it
|
|
312
|
+
# if you have Typheous and don't want to use it (or want another service),
|
|
313
|
+
# you can run Koala.http_service = NetHTTPService (or MyHTTPService)
|
|
304
314
|
begin
|
|
305
|
-
require 'typhoeus'
|
|
306
315
|
Koala.http_service = TyphoeusService
|
|
307
316
|
rescue LoadError
|
|
308
317
|
Koala.http_service = NetHTTPService
|
data/readme.md
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
Koala
|
|
2
2
|
====
|
|
3
|
-
Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.com/arsduo/koala</a>) is a new Facebook library for Ruby, supporting the Graph API, the old REST API, realtime updates, and OAuth validation. We wrote Koala with four goals:
|
|
3
|
+
Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.com/arsduo/koala</a>) is a new Facebook library for Ruby, supporting the Graph API (including photo uploads), the old REST API, realtime updates, and OAuth validation. We wrote Koala with four goals:
|
|
4
4
|
|
|
5
|
-
* Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over
|
|
5
|
+
* Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over 750 lines of code.)
|
|
6
6
|
* Fast: Koala should, out of the box, be quick. In addition to supporting the vanilla Ruby networking libraries, it natively supports Typhoeus, our preferred gem for making fast HTTP requests. Of course, That brings us to our next topic:
|
|
7
7
|
* Flexible: Koala should be useful to everyone, regardless of their current configuration. (We have no dependencies beyond the JSON gem. Koala also has a built-in mechanism for using whichever HTTP library you prefer to make requests against the graph.)
|
|
8
8
|
* Tested: Koala should have complete test coverage, so you can rely on it. (Our complete test coverage can be run against either mocked responses or the live Facebook servers.)
|
|
9
9
|
|
|
10
|
+
1.0 beta
|
|
11
|
+
---
|
|
12
|
+
Version 1.0 is currently in beta, chock-full of great new features. To download the beta, just add --pre when installing the gem:
|
|
13
|
+
|
|
14
|
+
sudo gem install koala --pre
|
|
15
|
+
|
|
10
16
|
Graph API
|
|
11
17
|
----
|
|
12
|
-
The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
|
|
18
|
+
The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
|
|
13
19
|
|
|
14
20
|
graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
|
|
15
21
|
profile = graph.get_object("me")
|
|
16
22
|
friends = graph.get_connections("me", "friends")
|
|
17
23
|
graph.put_object("me", "feed", :message => "I am writing on my wall!")
|
|
18
24
|
|
|
19
|
-
The response of most requests is the JSON data returned from the Facebook servers as a Hash.
|
|
25
|
+
The response of most requests is the JSON data returned from the Facebook servers as a Hash.
|
|
20
26
|
|
|
21
27
|
When retrieving data that returns an array of results (for example, when calling GraphAPI#get_connections or GraphAPI#search) a GraphCollection object (a sub-class of Array) will be returned, which contains added methods for getting the next and previous page of results:
|
|
22
|
-
|
|
28
|
+
|
|
23
29
|
# Returns the feed items for the currently logged-in user as a GraphCollection
|
|
24
30
|
feed = graph.get_connections("me", "feed")
|
|
25
|
-
|
|
31
|
+
|
|
26
32
|
# GraphCollection is a sub-class of Array, so you can use it as a usual Array
|
|
27
33
|
first_entry = feed[0]
|
|
28
34
|
last_entry = feed.last
|
|
@@ -33,7 +39,7 @@ When retrieving data that returns an array of results (for example, when calling
|
|
|
33
39
|
# Returns an array describing the URL for the next page: [path, arguments]
|
|
34
40
|
# This is useful for paging across multiple requests
|
|
35
41
|
next_path, next_args = feed.next_page_params
|
|
36
|
-
|
|
42
|
+
|
|
37
43
|
# You can use those params to easily get the next (or prevous) page
|
|
38
44
|
page = graph.get_page(feed.next_page_params)
|
|
39
45
|
|
|
@@ -41,15 +47,15 @@ Check out the wiki for more examples.
|
|
|
41
47
|
|
|
42
48
|
The old-school REST API
|
|
43
49
|
-----
|
|
44
|
-
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.
|
|
50
|
+
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.
|
|
45
51
|
|
|
46
52
|
Koala now supports the old-school REST API using OAuth access tokens; to use this, instantiate your class using the RestAPI class:
|
|
47
53
|
|
|
48
54
|
@rest = Koala::Facebook::RestAPI.new(oauth_access_token)
|
|
49
55
|
@rest.fql_query(my_fql_query) # convenience method
|
|
50
56
|
@rest.rest_call("stream.publish", arguments_hash) # generic version
|
|
51
|
-
|
|
52
|
-
We reserve the right to expand the built-in REST API coverage to additional convenience methods in the future, depending on how fast Facebook moves to fill in the gaps.
|
|
57
|
+
|
|
58
|
+
We reserve the right to expand the built-in REST API coverage to additional convenience methods in the future, depending on how fast Facebook moves to fill in the gaps.
|
|
53
59
|
|
|
54
60
|
(If you want the power of both APIs in the palm of your hand, try out the GraphAndRestAPI class.)
|
|
55
61
|
|
|
@@ -119,7 +125,7 @@ Some resources to help you as you play with Koala and the Graph API:
|
|
|
119
125
|
Testing
|
|
120
126
|
-----
|
|
121
127
|
|
|
122
|
-
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:
|
|
128
|
+
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:
|
|
123
129
|
# From the spec directory
|
|
124
130
|
spec koala_spec.rb
|
|
125
131
|
|
|
@@ -127,4 +133,4 @@ You can also run live tests against Facebook's servers:
|
|
|
127
133
|
# Again from the spec directory
|
|
128
134
|
spec koala_spec_without_mocks.rb
|
|
129
135
|
|
|
130
|
-
Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream
|
|
136
|
+
Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream, read\_stream, and user\_photos permissions and an OAuth code that can be used to generate an access token. You can get these data at the OAuth Playground; if you want to use your own app, remember to swap out the app ID, secret, and other values. (The file also provides valid values for other tests, which you're welcome to swap out for data specific to your own application.)
|
data/spec/facebook_data.yml
CHANGED
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
# Just remember to update all fields!
|
|
5
5
|
|
|
6
6
|
# You must supply this value yourself to test the GraphAPI class.
|
|
7
|
-
# Your OAuth token should have publish_stream and
|
|
8
|
-
oauth_token:
|
|
7
|
+
# Your OAuth token should have publish_stream, read_stream, and user_photos permissions.
|
|
8
|
+
oauth_token: 119908831367602|2.SzVOt20se6d7VnLzsG3USg__.3600.1302102000-2905623|Qxhc8NPxIQFKuNOaL1XdYNcztAM
|
|
9
9
|
|
|
10
10
|
# for testing the OAuth class
|
|
11
11
|
# baseline app
|
|
12
12
|
oauth_test_data:
|
|
13
13
|
# You must supply this value yourself, since they will expire.
|
|
14
|
-
code:
|
|
14
|
+
code: 2.SzVOt20se6d7VnLzsG3USg__.3600.1302102000-2905623|Z5x3PcJgROjfj6DOwVQpb714nkQ
|
|
15
15
|
# easiest way to get session keys: use multiple test accounts with the Javascript login at http://oauth.twoalex.com
|
|
16
|
-
session_key:
|
|
16
|
+
session_key: 2.SzVOt20se6d7VnLzsG3USg__.3600.1302102000-2905623
|
|
17
17
|
multiple_session_keys:
|
|
18
|
-
-
|
|
19
|
-
-
|
|
18
|
+
- 2.SzVOt20se6d7VnLzsG3USg__.3600.1302102000-2905623
|
|
19
|
+
- 2.SzVOt20se6d7VnLzsG3USg__.3600.1302102000-2905623
|
|
20
20
|
|
|
21
21
|
# These values will work out of the box
|
|
22
22
|
app_id: 119908831367602
|
|
@@ -35,17 +35,21 @@ oauth_test_data:
|
|
|
35
35
|
# note: I've revoked the offline access for security reasons, so you can't make calls against this :)
|
|
36
36
|
fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
|
|
37
37
|
|
|
38
|
-
# These values will work out of the box
|
|
39
|
-
# They're from Facebook's example at http://developers.facebook.com/docs/authentication/canvas
|
|
38
|
+
# These values from the OAuth Playground (see above) will work out of the box
|
|
40
39
|
# You can update this to live data if desired
|
|
41
40
|
# request_secret is optional and will fall back to the secret above if absent
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"0": payload
|
|
41
|
+
signed_params: "zWRm0gd5oHW_jzXP_WA9CirO7c5CLHotn-SKRqH2NmU.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDE5MjIwMDAsImlzc3VlZF9hdCI6MTMwMTkxNzI5OSwib2F1dGhfdG9rZW4iOiIxMTk5MDg4MzEzNjc2MDJ8Mi56VkZfNk5yTUVMSHVKYTRnSVU5dEt3X18uMzYwMC4xMzAxOTIyMDAwLTI5MDU2MjN8emdxUHNyMkJHOUxvT0s5a2VrR2dSVVJaeDBrIiwidXNlciI6eyJjb3VudHJ5IjoiZGUiLCJsb2NhbGUiOiJkZV9ERSIsImFnZSI6eyJtaW4iOjIxfX0sInVzZXJfaWQiOiIyOTA1NjIzIn0"
|
|
42
|
+
signed_params_result:
|
|
43
|
+
expires: 1301922000
|
|
46
44
|
algorithm: HMAC-SHA256
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
user_id: "2905623"
|
|
46
|
+
oauth_token: 119908831367602|2.zVF_6NrMELHuJa4gIU9tKw__.3600.1301922000-2905623|zgqPsr2BG9LoOK9kekGgRURZx0k
|
|
47
|
+
user:
|
|
48
|
+
country: de
|
|
49
|
+
locale: de_DE
|
|
50
|
+
age:
|
|
51
|
+
min: 21
|
|
52
|
+
issued_at: 1301917299
|
|
49
53
|
|
|
50
54
|
subscription_test_data:
|
|
51
55
|
subscription_path: http://oauth.twoalex.com/subscriptions
|
|
Binary file
|
|
@@ -3,8 +3,12 @@ class GraphAndRestAPINoTokenTests < Test::Unit::TestCase
|
|
|
3
3
|
before(:each) do
|
|
4
4
|
@api = Koala::Facebook::GraphAndRestAPI.new
|
|
5
5
|
end
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
it_should_behave_like "Koala RestAPI"
|
|
7
8
|
it_should_behave_like "Koala RestAPI without an access token"
|
|
9
|
+
|
|
10
|
+
it_should_behave_like "Koala GraphAPI"
|
|
8
11
|
it_should_behave_like "Koala GraphAPI without an access token"
|
|
12
|
+
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
|
9
13
|
end
|
|
10
14
|
end
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
class GraphAndRestAPIWithTokenTests < Test::Unit::TestCase
|
|
2
2
|
describe "Koala GraphAndRestAPI without an access token" do
|
|
3
3
|
include LiveTestingDataHelper
|
|
4
|
-
|
|
5
|
-
it_should_behave_like "Koala GraphAPI with an access token"
|
|
6
|
-
|
|
4
|
+
|
|
7
5
|
before(:each) do
|
|
8
6
|
@api = Koala::Facebook::GraphAndRestAPI.new(@token)
|
|
9
7
|
end
|
|
8
|
+
|
|
9
|
+
it_should_behave_like "Koala RestAPI"
|
|
10
|
+
it_should_behave_like "Koala RestAPI with an access token"
|
|
11
|
+
|
|
12
|
+
it_should_behave_like "Koala GraphAPI"
|
|
13
|
+
it_should_behave_like "Koala GraphAPI with an access token"
|
|
14
|
+
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
|
10
15
|
end
|
|
11
16
|
end
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
shared_examples_for "Koala GraphAPI without an access token" do
|
|
2
|
-
it "should get public data about a user" do
|
|
3
|
-
result = @api.get_object("koppel")
|
|
4
|
-
# the results should have an ID and a name, among other things
|
|
5
|
-
(result["id"] && result["name"]).should
|
|
6
|
-
end
|
|
7
2
|
|
|
8
3
|
it "should not get private data about a user" do
|
|
9
4
|
result = @api.get_object("koppel")
|
|
@@ -11,46 +6,14 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
11
6
|
result["updated_time"].should be_nil
|
|
12
7
|
end
|
|
13
8
|
|
|
14
|
-
it "should get public data about a Page" do
|
|
15
|
-
result = @api.get_object("contextoptional")
|
|
16
|
-
# the results should have an ID and a name, among other things
|
|
17
|
-
(result["id"] && result["name"]).should
|
|
18
|
-
end
|
|
19
|
-
|
|
20
9
|
it "should not be able to get data about 'me'" do
|
|
21
10
|
lambda { @api.get_object("me") }.should raise_error(Koala::Facebook::APIError)
|
|
22
11
|
end
|
|
23
12
|
|
|
24
|
-
it "should be able to get multiple objects" do
|
|
25
|
-
results = @api.get_objects(["contextoptional", "naitik"])
|
|
26
|
-
results.length.should == 2
|
|
27
|
-
end
|
|
28
|
-
|
|
29
13
|
it "shouldn't be able to access connections from users" do
|
|
30
14
|
lambda { @api.get_connections("lukeshepard", "likes") }.should raise_error(Koala::Facebook::APIError)
|
|
31
15
|
end
|
|
32
16
|
|
|
33
|
-
it "should be able to access a user's picture" do
|
|
34
|
-
@api.get_picture("chris.baclig").should =~ /http\:\/\//
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "should be able to access a user's picture, given a picture type" do
|
|
38
|
-
@api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "should be able to access connections from public Pages" do
|
|
42
|
-
result = @api.get_connections("contextoptional", "photos")
|
|
43
|
-
result.should be_a(Array)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# paging
|
|
47
|
-
# see also graph_collection_tests
|
|
48
|
-
it "should make a request for a page when provided a specific set of page params" do
|
|
49
|
-
query = [1, 2]
|
|
50
|
-
@api.should_receive(:graph_call).with(*query)
|
|
51
|
-
@api.get_page(query)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
17
|
it "should not be able to put an object" do
|
|
55
18
|
lambda { @result = @api.put_object("lukeshepard", "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::APIError)
|
|
56
19
|
puts "Error! Object #{@result.inspect} somehow put onto Luke Shepard's wall!" if @result
|
|
@@ -59,8 +22,8 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
59
22
|
# these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
|
|
60
23
|
it "should not be able to post to a feed" do
|
|
61
24
|
(lambda do
|
|
62
|
-
attachment = {:name => "
|
|
63
|
-
@result = @api.put_wall_post("Hello, world", attachment, "contextoptional")
|
|
25
|
+
attachment = {:name => "OAuth Playground", :link => "http://www.oauth.twoalex.com/"}
|
|
26
|
+
@result = @api.put_wall_post("Hello, world", attachment, "contextoptional")
|
|
64
27
|
end).should raise_error(Koala::Facebook::APIError)
|
|
65
28
|
puts "Error! Object #{@result.inspect} somehow put onto Context Optional's wall!" if @result
|
|
66
29
|
end
|
|
@@ -68,7 +31,7 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
68
31
|
it "should not be able to comment on an object" do
|
|
69
32
|
# random public post on the ContextOptional wall
|
|
70
33
|
lambda { @result = @api.put_comment("7204941866_119776748033392", "The hackathon was great!") }.should raise_error(Koala::Facebook::APIError)
|
|
71
|
-
puts "Error! Object #{@result.inspect} somehow commented on post 7204941866_119776748033392!" if @result
|
|
34
|
+
puts "Error! Object #{@result.inspect} somehow commented on post 7204941866_119776748033392!" if @result
|
|
72
35
|
end
|
|
73
36
|
|
|
74
37
|
it "should not be able to like an object" do
|
|
@@ -76,39 +39,27 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
76
39
|
end
|
|
77
40
|
|
|
78
41
|
# DELETE
|
|
79
|
-
it "should not be able to delete posts" do
|
|
42
|
+
it "should not be able to delete posts" do
|
|
80
43
|
# test post on the Ruby SDK Test application
|
|
81
44
|
lambda { @result = @api.delete_object("115349521819193_113815981982767") }.should raise_error(Koala::Facebook::APIError)
|
|
82
45
|
end
|
|
83
|
-
|
|
84
|
-
# SEARCH
|
|
85
|
-
it "should be able to search" do
|
|
86
|
-
result = @api.search("facebook")
|
|
87
|
-
result.length.should be_an(Integer)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
|
91
46
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Koala.should_receive(:make_request).with(
|
|
95
|
-
anything,
|
|
96
|
-
anything,
|
|
97
|
-
anything,
|
|
98
|
-
hash_not_including(:rest_api => true)
|
|
99
|
-
).and_return(Koala::Response.new(200, "", {}))
|
|
100
|
-
|
|
101
|
-
@api.api("anything")
|
|
47
|
+
it "should not be able to delete a like" do
|
|
48
|
+
lambda { @api.delete_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::APIError)
|
|
102
49
|
end
|
|
103
50
|
end
|
|
104
51
|
|
|
105
52
|
class FacebookNoAccessTokenTests < Test::Unit::TestCase
|
|
106
53
|
describe "Koala GraphAPI without an access token" do
|
|
54
|
+
include LiveTestingDataHelper
|
|
55
|
+
|
|
107
56
|
before :each do
|
|
108
57
|
@api = Koala::Facebook::GraphAPI.new
|
|
109
|
-
end
|
|
110
|
-
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it_should_behave_like "Koala GraphAPI"
|
|
111
61
|
it_should_behave_like "Koala GraphAPI without an access token"
|
|
62
|
+
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
|
63
|
+
|
|
112
64
|
end
|
|
113
65
|
end
|
|
114
|
-
|