joelind-koala 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/koala.rb ADDED
@@ -0,0 +1,292 @@
1
+ require 'cgi'
2
+ require 'digest/md5'
3
+
4
+ # rubygems is required to support json, how facebook returns data
5
+ require 'rubygems'
6
+ require 'json'
7
+
8
+ # openssl is required to support signed_request
9
+ require 'openssl'
10
+
11
+ # include default http services
12
+ require 'koala/http_services'
13
+
14
+ # add Graph API methods
15
+ require 'koala/graph_api'
16
+
17
+ # add REST API methods
18
+ require 'koala/rest_api'
19
+
20
+ require 'koala/realtime_updates'
21
+
22
+ module Koala
23
+
24
+ module Facebook
25
+ # Ruby client library for the Facebook Platform.
26
+ # Copyright 2010 Facebook
27
+ # Adapted from the Python library by Alex Koppel, Rafi Jacoby, and the team at Context Optional
28
+ #
29
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
30
+ # not use this file except in compliance with the License. You may obtain
31
+ # a copy of the License at
32
+ # http://www.apache.org/licenses/LICENSE-2.0
33
+ #
34
+ # Unless required by applicable law or agreed to in writing, software
35
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
36
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
37
+ # License for the specific language governing permissions and limitations
38
+ # under the License.
39
+ #
40
+ # This client library is designed to support the Graph API and the official
41
+ # Facebook JavaScript SDK, which is the canonical way to implement
42
+ # Facebook authentication. Read more about the Graph API at
43
+ # http://developers.facebook.com/docs/api. You can download the Facebook
44
+ # JavaScript SDK at http://github.com/facebook/connect-js/.
45
+
46
+ class API
47
+ # initialize with an access token
48
+ def initialize(access_token = nil)
49
+ @access_token = access_token
50
+ end
51
+
52
+ def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
53
+ # Fetches the given path in the Graph API.
54
+ args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
55
+ # make the request via the provided service
56
+ result = Koala.make_request(path, args, verb, options)
57
+
58
+ # Check for any 500 errors before parsing the body
59
+ # since we're not guaranteed that the body is valid JSON
60
+ # in the case of a server error
61
+ raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
62
+
63
+ # Parse the body as JSON and check for errors if provided a mechanism to do so
64
+ # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
65
+ # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
66
+ body = response = JSON.parse("[#{result.body.to_s}]")[0]
67
+ if error_checking_block
68
+ yield(body)
69
+ end
70
+
71
+ # now return the desired information
72
+ if options[:http_component]
73
+ result.send(options[:http_component])
74
+ else
75
+ body
76
+ end
77
+ end
78
+ end
79
+
80
+ class GraphAPI < API
81
+ include GraphAPIMethods
82
+ end
83
+
84
+ class RestAPI < API
85
+ include RestAPIMethods
86
+ end
87
+
88
+ class GraphAndRestAPI < API
89
+ include GraphAPIMethods
90
+ include RestAPIMethods
91
+ end
92
+
93
+ class RealtimeUpdates < API
94
+ include RealtimeUpdateMethods
95
+ end
96
+
97
+ class APIError < Exception
98
+ attr_accessor :fb_error_type
99
+ def initialize(details = {})
100
+ self.fb_error_type = details["type"]
101
+ super("#{fb_error_type}: #{details["message"]}")
102
+ end
103
+ end
104
+
105
+
106
+ class OAuth
107
+ attr_reader :app_id, :app_secret, :oauth_callback_url
108
+ def initialize(app_id, app_secret, oauth_callback_url = nil)
109
+ @app_id = app_id
110
+ @app_secret = app_secret
111
+ @oauth_callback_url = oauth_callback_url
112
+ end
113
+
114
+ def get_user_info_from_cookie(cookie_hash)
115
+ # Parses the cookie set by the official Facebook JavaScript SDK.
116
+ #
117
+ # cookies should be a Hash, like the one Rails provides
118
+ #
119
+ # If the user is logged in via Facebook, we return a dictionary with the
120
+ # keys "uid" and "access_token". The former is the user's Facebook ID,
121
+ # and the latter can be used to make authenticated requests to the Graph API.
122
+ # If the user is not logged in, we return None.
123
+ #
124
+ # Download the official Facebook JavaScript SDK at
125
+ # http://github.com/facebook/connect-js/. Read more about Facebook
126
+ # authentication at http://developers.facebook.com/docs/authentication/.
127
+
128
+ if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
129
+ # remove the opening/closing quote
130
+ fb_cookie = fb_cookie.gsub(/\"/, "")
131
+
132
+ # since we no longer get individual cookies, we have to separate out the components ourselves
133
+ components = {}
134
+ fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
135
+
136
+ # generate the signature and make sure it matches what we expect
137
+ auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
138
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
139
+ sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
140
+ end
141
+ end
142
+ alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
143
+
144
+ def get_user_from_cookie(cookies)
145
+ if info = get_user_info_from_cookies(cookies)
146
+ string = info["uid"]
147
+ end
148
+ end
149
+ alias_method :get_user_from_cookies, :get_user_from_cookie
150
+
151
+ # URLs
152
+
153
+ def url_for_oauth_code(options = {})
154
+ # for permissions, see http://developers.facebook.com/docs/authentication/permissions
155
+ permissions = options[:permissions]
156
+ scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
157
+
158
+ callback = options[:callback] || @oauth_callback_url
159
+ raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
160
+
161
+ # Creates the URL for oauth authorization for a given callback and optional set of permissions
162
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
163
+ end
164
+
165
+ def url_for_access_token(code, options = {})
166
+ # Creates the URL for the token corresponding to a given code generated by Facebook
167
+ callback = options[:callback] || @oauth_callback_url
168
+ raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
169
+ "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
170
+ end
171
+
172
+ def get_access_token_info(code)
173
+ # convenience method to get a parsed token from Facebook for a given code
174
+ # should this require an OAuth callback URL?
175
+ get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
176
+ end
177
+
178
+ def get_access_token(code)
179
+ # upstream methods will throw errors if needed
180
+ if info = get_access_token_info(code)
181
+ string = info["access_token"]
182
+ end
183
+ end
184
+
185
+ def get_app_access_token_info
186
+ # convenience method to get a the application's sessionless access token
187
+ get_token_from_server({:type => 'client_cred'}, true)
188
+ end
189
+
190
+ def get_app_access_token
191
+ if info = get_app_access_token_info
192
+ string = info["access_token"]
193
+ end
194
+ end
195
+
196
+ # signed_request
197
+ def parse_signed_request(request)
198
+ # Facebook's signed requests come in two parts -- the signature and the data payload
199
+ encoded_sig, payload = request.split(".")
200
+
201
+ sig = base64_url_decode(encoded_sig)
202
+
203
+ # if the signature matches, return the data, decoded and parsed as JSON
204
+ if OpenSSL::HMAC.digest("sha256", @app_secret, payload) == sig
205
+ JSON.parse(base64_url_decode(payload))
206
+ else
207
+ nil
208
+ end
209
+ end
210
+
211
+ # from session keys
212
+ def get_token_info_from_session_keys(sessions)
213
+ # fetch the OAuth tokens from Facebook
214
+ response = fetch_token_string({
215
+ :type => 'client_cred',
216
+ :sessions => sessions.join(",")
217
+ }, true, "exchange_sessions")
218
+
219
+ # get_token_from_session_key should return an empty body if an empty string or nil is provided
220
+ # if invalid tokens are provided, it returns an array of nulls, which is a valid result
221
+ if response == ""
222
+ raise APIError.new("ArgumentError", "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!")
223
+ end
224
+
225
+ JSON.parse(response)
226
+ end
227
+
228
+ def get_tokens_from_session_keys(sessions)
229
+ # get the original hash results
230
+ results = get_token_info_from_session_keys(sessions)
231
+ # now recollect them as just the access tokens
232
+ results.collect { |r| string = r["access_token"] }
233
+ end
234
+
235
+ def get_token_from_session_key(session)
236
+ # convenience method for a single key
237
+ # gets the overlaoded strings automatically
238
+ get_tokens_from_session_keys([session])[0]
239
+ end
240
+
241
+ protected
242
+
243
+ def get_token_from_server(args, post = false)
244
+ # fetch the result from Facebook's servers
245
+ result = fetch_token_string(args, post)
246
+
247
+ # if we have an error, parse the error JSON and raise an error
248
+ raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
249
+
250
+ # otherwise, parse the access token
251
+ parse_access_token(result)
252
+ end
253
+
254
+ def parse_access_token(response_text)
255
+ components = response_text.split("&").inject({}) do |hash, bit|
256
+ key, value = bit.split("=")
257
+ hash.merge!(key => value)
258
+ end
259
+ components
260
+ end
261
+
262
+ def fetch_token_string(args, post = false, endpoint = "access_token")
263
+ Koala.make_request("oauth/#{endpoint}", {
264
+ :client_id => @app_id,
265
+ :client_secret => @app_secret
266
+ }.merge!(args), post ? "post" : "get").body
267
+ end
268
+
269
+ # base 64
270
+ def base64_url_decode(string)
271
+ # to properly decode what Facebook provides, we need to add == to the end
272
+ # and translate certain characters to others before running the actual decoding
273
+ # see http://developers.facebook.com/docs/authentication/canvas
274
+ "#{string}==".tr("-_", "+/").unpack("m")[0]
275
+ end
276
+ end
277
+ end
278
+
279
+ # finally, set up the http service Koala methods used to make requests
280
+ # you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
281
+ def self.http_service=(service)
282
+ self.send(:include, service)
283
+ end
284
+
285
+ # by default, try requiring Typhoeus -- if that works, use it
286
+ begin
287
+ require 'typhoeus'
288
+ Koala.http_service = TyphoeusService
289
+ rescue LoadError
290
+ Koala.http_service = NetHTTPService
291
+ end
292
+ end
data/readme.md ADDED
@@ -0,0 +1,104 @@
1
+ Koala
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:
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 500 lines of code.)
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
+ * 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
+ * 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
+
10
+ Graph API
11
+ ----
12
+ The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
13
+ graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
14
+ profile = graph.get_object("me")
15
+ friends = graph.get_connection("me", "friends")
16
+ graph.put_object("me", "feed", :message => "I am writing on my wall!")
17
+
18
+ Check out the wiki for more examples.
19
+
20
+ The old-school REST API
21
+ -----
22
+ 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 -- including fql.query -- that can't yet be done via the Graph.
23
+
24
+ Koala now supports the old-school REST API using OAuth access tokens; to use this, instantiate your class using the RestAPI class:
25
+
26
+ @rest = Koala::Facebook::RestAPI.new(oauth_access_token)
27
+ @rest.fql_query(my_fql_query) # convenience method
28
+ @rest.rest_call("stream.publish", arguments_hash) # generic version
29
+
30
+ 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.
31
+
32
+ (If you want the power of both APIs in the palm of your hand, try out the GraphAndRestAPI class.)
33
+
34
+ OAuth
35
+ -----
36
+ 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:
37
+ @oauth = Koala::Facebook::OAuth.new(app_id, code, callback_url)
38
+
39
+ If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/connect-js) (formerly Facebook Connect), you can use the OAuth class to parse the cookies:
40
+ @oauth.get_user_from_cookie(cookies)
41
+
42
+ And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
43
+ # generate authenticating URL
44
+ @oauth.url_for_oauth_code
45
+ # fetch the access token once you have the code
46
+ @oauth.get_access_token(code)
47
+
48
+ You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
49
+ @oauth.get_app_access_token
50
+
51
+ 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).
52
+
53
+ *Exchanging session keys:* Stuck building tab applications on Facebook? Wishing you had an OAuth token so you could use the Graph API? You're in luck! Koala now allows you to exchange session keys for OAuth access tokens:
54
+ @oauth.get_token_from_session_key(session_key)
55
+ @oauth.get_tokens_from_session_keys(array_of_session_keys)
56
+
57
+ Real-time Updates
58
+ -----
59
+ The Graph API now allows your application to subscribe to real-time updates for certain objects in the graph.
60
+
61
+ Currently, Facebook only supports subscribing to users, permissions and errors. On top of that, there are limitations on what attributes and connections for each of these objects you can subscribe to updates for. Check the [official Facebook documentation](http://developers.facebook.com/docs/api/realtime) for more details.
62
+
63
+ Koala makes it easy to interact with your applications using the RealTimeUpdates class:
64
+
65
+ @updates = Koala::Facebook::RealTimeUpdates.new(:app_id => app_id, :secret => secret)
66
+
67
+ You can do just about anything with your real-time update subscriptions using the RealTimeUpdates class:
68
+
69
+ # Add/modify a subscription to updates for when the first_name or last_name fields of any of your users is changed
70
+ @updates.subscribe("user", "first_name, last_name", callback_token, verify_token)
71
+
72
+ # Get an array of your current subscriptions (one hash for each object you've subscribed to)
73
+ @updates.list_subscriptions
74
+
75
+ # Unsubscribe from updates for an object
76
+ @updates.unsubscribe("user")
77
+
78
+ And to top it all off, RealTimeUpdates provides a static method to respond to Facebook servers' verification of your callback URLs:
79
+
80
+ # Returns the hub.challenge parameter in params if the verify token in params matches verify_token
81
+ Koala::Facebook::RealTimeUpdates.meet_challenge(params, your_verify_token)
82
+
83
+ For more information about meet_challenge and the RealTimeUpdates class, check out the Real-Time Updates page on the wiki.
84
+
85
+ See examples, ask questions
86
+ -----
87
+ Some resources to help you as you play with Koala and the Graph API:
88
+
89
+ * Complete Koala documentation <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>
90
+ * The <a href="http://groups.google.com/group/koala-users">Koala users group</a> on Google Groups, the place for your Koala and API questions
91
+ * The Koala-powered <a href="http://oauth.twoalex.com" target="_blank">OAuth Playground</a>, where you can easily generate OAuth access tokens and any other data needed to test out the APIs or OAuth
92
+
93
+ Testing
94
+ -----
95
+
96
+ 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:
97
+ # From the spec directory
98
+ spec koala_spec.rb
99
+
100
+ You can also run live tests against Facebook's servers:
101
+ # Again from the spec directory
102
+ spec koala_spec_without_mocks.rb
103
+
104
+ Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream and read\_stream 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.)
@@ -0,0 +1,44 @@
1
+ # Check out http://oauth.twoalex.com/ to easily generate tokens, cookies, etc.
2
+ # Those values will work with the default settings in this yaml.
3
+ # Of course, you can change this to work with your own app.
4
+ # Just remember to update all fields!
5
+
6
+ # You must supply this value yourself to test the GraphAPI class.
7
+ # Your OAuth token should have publish_stream and read_stream permissions.
8
+ oauth_token:
9
+
10
+ # for testing the OAuth class
11
+ # baseline app
12
+ oauth_test_data:
13
+ # You must supply this value yourself, since they will expire.
14
+ code:
15
+ # easiest way to get session keys: use multiple test accounts with the Javascript login at http://oauth.twoalex.com
16
+ session_key:
17
+ multiple_session_keys:
18
+ -
19
+ -
20
+
21
+ # These values will work out of the box
22
+ app_id: 119908831367602
23
+ secret: e45e55a333eec232d4206d2703de1307
24
+ callback_url: http://oauth.twoalex.com/
25
+ app_access_token: 119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.
26
+ raw_token_string: "access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.&expires=6621"
27
+ raw_offline_access_token_string: access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.
28
+ valid_cookies:
29
+ # note: the tests stub the time class so these default cookies are always valid (if you're using the default app)
30
+ # if not you may want to remove the stubbing to test expiration
31
+ fbs_119908831367602: '"access_token=119908831367602|2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623|CMpi0AYbn03Oukzv94AUha2qbO4.&expires=1273363200&secret=lT_9zm5r5IbJ6Aa5O54nFw__&session_key=2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623&sig=9515e93113921f9476a4efbdd4a3c746&uid=2905623"'
32
+ expired_cookies:
33
+ fbs_119908831367602: '"access_token=119908831367602|2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623|yVt5WH_S6J5p3gFa5_5lBzckhws.&expires=1273287600&secret=V_E79ovQnXqxGctFuC_n5A__&session_key=2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623&sig=eeef60838c0c800258d89b7e6ddddddb&uid=2905623"'
34
+ offline_access_cookies:
35
+ # note: I've revoked the offline access for security reasons, so you can't make calls against this :)
36
+ fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
37
+
38
+ subscription_test_data:
39
+ subscription_path: http://oauth.twoalex.com/subscriptions
40
+ verify_token: "myverificationtoken|1f54545d5f722733e17faae15377928f"
41
+ challenge_data:
42
+ "hub.challenge": "1290024882"
43
+ "hub.verify_token": "myverificationtoken|1f54545d5f722733e17faae15377928f"
44
+ "hub.mode": "subscribe"
@@ -0,0 +1,80 @@
1
+ class ApiBaseTests < Test::Unit::TestCase
2
+ describe "Koala API base class" do
3
+ before(:each) do
4
+ @service = Koala::Facebook::API.new
5
+ end
6
+
7
+ it "should not include an access token if none was given" do
8
+ Koala.should_receive(:make_request).with(
9
+ anything,
10
+ hash_not_including('access_token' => 1),
11
+ anything,
12
+ anything
13
+ ).and_return(Koala::Response.new(200, "", ""))
14
+
15
+ @service.api('anything')
16
+ end
17
+
18
+ it "should include an access token if given" do
19
+ token = 'adfadf'
20
+ service = Koala::Facebook::API.new token
21
+
22
+ Koala.should_receive(:make_request).with(
23
+ anything,
24
+ hash_including('access_token' => token),
25
+ anything,
26
+ anything
27
+ ).and_return(Koala::Response.new(200, "", ""))
28
+
29
+ service.api('anything')
30
+ end
31
+
32
+ it "should get the attribute of a Koala::Response given by the http_component parameter" do
33
+ http_component = :method_name
34
+
35
+ response = mock('Mock KoalaResponse', :body => '', :status => 200)
36
+ response.should_receive(http_component).and_return('')
37
+
38
+ Koala.stub(:make_request).and_return(response)
39
+
40
+ @service.api('anything', 'get', {}, :http_component => http_component)
41
+ end
42
+
43
+ it "should return the body of the request as JSON if no http_component is given" do
44
+ response = stub('response', :body => 'body', :status => 200)
45
+ Koala.stub(:make_request).and_return(response)
46
+
47
+ json_body = mock('JSON body')
48
+ JSON.stub(:parse).and_return([json_body])
49
+
50
+ @service.api('anything').should == json_body
51
+ end
52
+
53
+ it "should execute a block with the response body if passed one" do
54
+ body = '{}'
55
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
56
+
57
+ yield_test = mock('Yield Tester')
58
+ yield_test.should_receive(:pass)
59
+
60
+ @service.api('anything') do |arg|
61
+ yield_test.pass
62
+ arg.should == JSON.parse(body)
63
+ end
64
+ end
65
+
66
+ it "should raise an API error if the HTTP response code is greater than or equal to 500" do
67
+ Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
68
+
69
+ lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
70
+ end
71
+
72
+ it "should handle rogue true/false as responses" do
73
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
74
+ @service.api('anything').should be_true
75
+
76
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
77
+ @service.api('anything').should be_false
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,10 @@
1
+ class GraphAndRestAPINoTokenTests < Test::Unit::TestCase
2
+ describe "Koala GraphAndRestAPI without an access token" do
3
+ before(:each) do
4
+ @api = Koala::Facebook::GraphAndRestAPI.new
5
+ end
6
+
7
+ it_should_behave_like "Koala RestAPI without an access token"
8
+ it_should_behave_like "Koala GraphAPI without an access token"
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ class GraphAndRestAPIWithTokenTests < Test::Unit::TestCase
2
+ describe "Koala GraphAndRestAPI without an access token" do
3
+ it_should_behave_like "live testing examples"
4
+ it_should_behave_like "Koala RestAPI with an access token"
5
+ it_should_behave_like "Koala GraphAPI with an access token"
6
+
7
+ before(:each) do
8
+ @api = Koala::Facebook::GraphAndRestAPI.new(@token)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,105 @@
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
+
8
+ it "should not get private data about a user" do
9
+ result = @api.get_object("koppel")
10
+ # updated_time should be a pretty fixed test case
11
+ result["updated_time"].should be_nil
12
+ end
13
+
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
+ it "should not be able to get data about 'me'" do
21
+ lambda { @api.get_object("me") }.should raise_error(Koala::Facebook::APIError)
22
+ end
23
+
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
+ it "shouldn't be able to access connections from users" do
30
+ lambda { @api.get_connections("lukeshepard", "likes") }.should raise_error(Koala::Facebook::APIError)
31
+ end
32
+
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", "likes")
43
+ result.should be_a(Array)
44
+ end
45
+
46
+ it "should not be able to put an object" do
47
+ lambda { @result = @api.put_object("lukeshepard", "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::APIError)
48
+ puts "Error! Object #{@result.inspect} somehow put onto Luke Shepard's wall!" if @result
49
+ end
50
+
51
+ # these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
52
+ it "should not be able to post to a feed" do
53
+ (lambda do
54
+ attachment = {:name => "Context Optional", :link => "http://www.contextoptional.com/"}
55
+ @result = @api.put_wall_post("Hello, world", attachment, "contextoptional")
56
+ end).should raise_error(Koala::Facebook::APIError)
57
+ puts "Error! Object #{@result.inspect} somehow put onto Context Optional's wall!" if @result
58
+ end
59
+
60
+ it "should not be able to comment on an object" do
61
+ # random public post on the ContextOptional wall
62
+ lambda { @result = @api.put_comment("7204941866_119776748033392", "The hackathon was great!") }.should raise_error(Koala::Facebook::APIError)
63
+ puts "Error! Object #{@result.inspect} somehow commented on post 7204941866_119776748033392!" if @result
64
+ end
65
+
66
+ it "should not be able to like an object" do
67
+ lambda { @api.put_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::APIError)
68
+ end
69
+
70
+
71
+ # DELETE
72
+ it "should not be able to delete posts" do
73
+ # test post on the Ruby SDK Test application
74
+ lambda { @result = @api.delete_object("115349521819193_113815981982767") }.should raise_error(Koala::Facebook::APIError)
75
+ end
76
+
77
+ # SEARCH
78
+ it "should be able to search" do
79
+ result = @api.search("facebook")
80
+ result["data"].should be_an(Array)
81
+ end
82
+
83
+ # API
84
+ it "should never use the rest api server" do
85
+ Koala.should_receive(:make_request).with(
86
+ anything,
87
+ anything,
88
+ anything,
89
+ hash_not_including(:rest_api => true)
90
+ ).and_return(Koala::Response.new(200, "", {}))
91
+
92
+ @api.api("anything")
93
+ end
94
+ end
95
+
96
+ class FacebookNoAccessTokenTests < Test::Unit::TestCase
97
+ describe "Koala GraphAPI without an access token" do
98
+ before :each do
99
+ @api = Koala::Facebook::GraphAPI.new
100
+ end
101
+
102
+ it_should_behave_like "Koala GraphAPI without an access token"
103
+ end
104
+ end
105
+