koala 0.7.1 → 0.8.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.
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ v0.8.0
2
+ -- Breaking interface changes
3
+ -- Removed string overloading for the methods, per 0.7.3, which caused Marshaling issues
4
+ -- Removed ability to provide a string as the second argument to url_for_access_token, per 0.5.0
5
+
6
+ v0.7.4
7
+ -- Fixed bug with get_user_from_cookies
8
+
9
+ v0.7.3
10
+ -- Added support for picture sizes -- thanks thhermansen for the patch!
11
+ -- Adjusted the return values for several methods (get_access_token, get_app_access_token, get_token_from_session_key, get_tokens_from_session_keys, get_user_from_cookies)
12
+ -- These methods now return strings, rather than hashes, which makes more sense
13
+ -- The strings are overloaded with an [] method for backwards compatibility (Ruby is truly amazing)
14
+ -- Using those methods triggers a deprecation warning
15
+ -- This will be removed by 1.0
16
+ -- There are new info methods (get_access_token_info, get_app_access_token_info, get_token_info_from_session_keys, and get_user_info_from_cookies) that natively return hashes, for when you want the expiration date
17
+ -- Responses with HTTP status 500+ now properly throw errors under Net::HTTP
18
+ -- Updated changelog
19
+ -- Added license
20
+
21
+ v0.7.2
22
+ -- Added support for exchanging session keys for OAuth access tokens (get_token_from_session_key for single keys, get_tokens_from_session_keys for multiple)
23
+ -- Moved Koala files into a koala/ subdirectory to minimize risk of name collisions
24
+ -- Added OAuth Playground git submodule as an example
25
+ -- Updated tests, readme, and changelog
26
+
1
27
  v0.7.1
2
28
  -- Updated RealtimeUpdates#list_subscriptions and GraphAPI#get_connections to now return an
3
29
  array of results directly (rather than a hash with one key)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Alex Koppel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest CHANGED
@@ -1,12 +1,14 @@
1
1
  CHANGELOG
2
+ LICENSE
2
3
  Manifest
3
4
  Rakefile
4
5
  init.rb
5
- lib/graph_api.rb
6
- lib/http_services.rb
6
+ koala.gemspec
7
7
  lib/koala.rb
8
- lib/realtime_updates.rb
9
- lib/rest_api.rb
8
+ lib/koala/graph_api.rb
9
+ lib/koala/http_services.rb
10
+ lib/koala/realtime_updates.rb
11
+ lib/koala/rest_api.rb
10
12
  readme.md
11
13
  spec/facebook_data.yml
12
14
  spec/koala/api_base_tests.rb
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake'
4
4
  require 'echoe'
5
5
 
6
6
  # gem management
7
- Echoe.new('koala', '0.7.1') do |p|
7
+ Echoe.new('koala', '0.8.0') do |p|
8
8
  p.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation."
9
9
  p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
10
10
  p.url = "http://github.com/arsduo/koala"
@@ -12,4 +12,5 @@ Echoe.new('koala', '0.7.1') do |p|
12
12
  p.email = "alex@alexkoppel.com"
13
13
  p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
14
14
  p.development_dependencies = []
15
+ p.retain_gemspec = true
15
16
  end
data/koala.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{koala}
5
- s.version = "0.7.1"
5
+ s.version = "0.8.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional"]
9
- s.date = %q{2010-05-27}
9
+ s.date = %q{2010-06-27}
10
10
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
11
11
  s.email = %q{alex@alexkoppel.com}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/realtime_updates.rb", "lib/rest_api.rb"]
13
- s.files = ["CHANGELOG", "Manifest", "Rakefile", "init.rb", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/realtime_updates.rb", "lib/rest_api.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb", "koala.gemspec"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
14
14
  s.homepage = %q{http://github.com/arsduo/koala}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala", "--main", "readme.md"]
16
16
  s.require_paths = ["lib"]
@@ -47,11 +47,11 @@ module Koala
47
47
  graph_call("#{id}/#{connection_name}", args)["data"]
48
48
  end
49
49
 
50
- def get_picture(object)
51
- result = graph_call("#{object}/picture", {}, "get", :http_component => :headers)
50
+ def get_picture(object, args = {})
51
+ result = graph_call("#{object}/picture", args, "get", :http_component => :headers)
52
52
  result["Location"]
53
- end
54
-
53
+ end
54
+
55
55
  def put_object(parent_object, connection_name, args = {})
56
56
  # Writes the given object to the graph, connected to the given parent.
57
57
  #
@@ -44,7 +44,7 @@ module Koala
44
44
  # fetch the access token if we're provided a secret
45
45
  if @secret && !@app_access_token
46
46
  oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
47
- @app_access_token = oauth.get_app_access_token["access_token"]
47
+ @app_access_token = oauth.get_app_access_token
48
48
  end
49
49
  end
50
50
 
data/lib/koala.rb CHANGED
@@ -6,15 +6,15 @@ require 'rubygems'
6
6
  require 'json'
7
7
 
8
8
  # include default http services
9
- require 'http_services'
9
+ require 'koala/http_services'
10
10
 
11
11
  # add Graph API methods
12
- require 'graph_api'
12
+ require 'koala/graph_api'
13
13
 
14
14
  # add REST API methods
15
- require 'rest_api'
15
+ require 'koala/rest_api'
16
16
 
17
- require 'realtime_updates'
17
+ require 'koala/realtime_updates'
18
18
 
19
19
  module Koala
20
20
 
@@ -52,6 +52,11 @@ module Koala
52
52
  # make the request via the provided service
53
53
  result = Koala.make_request(path, args, verb, options)
54
54
 
55
+ # Check for any 500 errors before parsing the body
56
+ # since we're not guaranteed that the body is valid JSON
57
+ # in the case of a server error
58
+ raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
59
+
55
60
  # Parse the body as JSON and check for errors if provided a mechanism to do so
56
61
  # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
57
62
  # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
@@ -102,12 +107,11 @@ module Koala
102
107
  @app_secret = app_secret
103
108
  @oauth_callback_url = oauth_callback_url
104
109
  end
105
-
106
- def get_user_from_cookie(cookie_hash)
110
+
111
+ def get_user_info_from_cookie(cookie_hash)
107
112
  # Parses the cookie set by the official Facebook JavaScript SDK.
108
113
  #
109
- # cookies should be a dictionary-like object mapping cookie names to
110
- # cookie values.
114
+ # cookies should be a Hash, like the one Rails provides
111
115
  #
112
116
  # If the user is logged in via Facebook, we return a dictionary with the
113
117
  # keys "uid" and "access_token". The former is the user's Facebook ID,
@@ -132,8 +136,17 @@ module Koala
132
136
  sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
133
137
  end
134
138
  end
139
+ alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
140
+
141
+ def get_user_from_cookie(cookies)
142
+ if info = get_user_info_from_cookies(cookies)
143
+ string = info["uid"]
144
+ end
145
+ end
135
146
  alias_method :get_user_from_cookies, :get_user_from_cookie
136
147
 
148
+ # URLs
149
+
137
150
  def url_for_oauth_code(options = {})
138
151
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
139
152
  permissions = options[:permissions]
@@ -148,26 +161,65 @@ module Koala
148
161
 
149
162
  def url_for_access_token(code, options = {})
150
163
  # Creates the URL for the token corresponding to a given code generated by Facebook
151
- if options.is_a?(String) # changing the arguments
152
- puts "Deprecation warning: url_for_access_token now takes an options hash as the second argument; pass the callback as :callback."
153
- options = {:callback => options}
154
- end
155
164
  callback = options[:callback] || @oauth_callback_url
156
165
  raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
157
166
  "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
158
167
  end
159
-
160
- def get_access_token(code)
168
+
169
+ def get_access_token_info(code)
161
170
  # convenience method to get a parsed token from Facebook for a given code
162
171
  # should this require an OAuth callback URL?
163
172
  get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
164
173
  end
165
174
 
166
- def get_app_access_token
175
+ def get_access_token(code)
176
+ # upstream methods will throw errors if needed
177
+ if info = get_access_token_info(code)
178
+ string = info["access_token"]
179
+ end
180
+ end
181
+
182
+ def get_app_access_token_info
167
183
  # convenience method to get a the application's sessionless access token
168
184
  get_token_from_server({:type => 'client_cred'}, true)
169
185
  end
170
186
 
187
+ def get_app_access_token
188
+ if info = get_app_access_token_info
189
+ string = info["access_token"]
190
+ end
191
+ end
192
+
193
+ # from session keys
194
+ def get_token_info_from_session_keys(sessions)
195
+ # fetch the OAuth tokens from Facebook
196
+ response = fetch_token_string({
197
+ :type => 'client_cred',
198
+ :sessions => sessions.join(",")
199
+ }, true, "exchange_sessions")
200
+
201
+ # get_token_from_session_key should return an empty body if an empty string or nil is provided
202
+ # if invalid tokens are provided, it returns an array of nulls, which is a valid result
203
+ if response == ""
204
+ raise APIError.new("ArgumentError", "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!")
205
+ end
206
+
207
+ JSON.parse(response)
208
+ end
209
+
210
+ def get_tokens_from_session_keys(sessions)
211
+ # get the original hash results
212
+ results = get_token_info_from_session_keys(sessions)
213
+ # now recollect them as just the access tokens
214
+ results.collect { |r| string = r["access_token"] }
215
+ end
216
+
217
+ def get_token_from_session_key(session)
218
+ # convenience method for a single key
219
+ # gets the overlaoded strings automatically
220
+ get_tokens_from_session_keys([session])[0]
221
+ end
222
+
171
223
  protected
172
224
 
173
225
  def get_token_from_server(args, post = false)
@@ -189,8 +241,8 @@ module Koala
189
241
  components
190
242
  end
191
243
 
192
- def fetch_token_string(args, post = false)
193
- Koala.make_request("oauth/access_token", {
244
+ def fetch_token_string(args, post = false, endpoint = "access_token")
245
+ Koala.make_request("oauth/#{endpoint}", {
194
246
  :client_id => @app_id,
195
247
  :client_secret => @app_secret
196
248
  }.merge!(args), post ? "post" : "get").body
data/readme.md CHANGED
@@ -34,7 +34,7 @@ We reserve the right to expand the built-in REST API coverage to additional conv
34
34
  OAuth
35
35
  -----
36
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)
37
+ @oauth = Koala::Facebook::OAuth.new(app_id, code, callback_url)
38
38
 
39
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
40
  @oauth.get_user_from_cookie(cookies)
@@ -50,6 +50,9 @@ You can also get your application's own access token, which can be used without
50
50
 
51
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
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)
53
56
 
54
57
  Real-time Updates
55
58
  -----
@@ -5,14 +5,19 @@
5
5
 
6
6
  # You must supply this value yourself to test the GraphAPI class.
7
7
  # Your OAuth token should have publish_stream and read_stream permissions.
8
- oauth_token: 119908831367602|2.6WkxQTbQPGFCyLblPYdsMg__.3600.1274979600-2905623|8YPVs-jBmpWC6y10pMCWzMRFrdk.
8
+ oauth_token:
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: 2.6WkxQTbQPGFCyLblPYdsMg__.3600.1274979600-2905623|zHzbqwoONeqNxOXX7z9LZlpEGdc.
15
-
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
+
16
21
  # These values will work out of the box
17
22
  app_id: 119908831367602
18
23
  secret: e45e55a333eec232d4206d2703de1307
@@ -29,9 +29,45 @@ class ApiBaseTests < Test::Unit::TestCase
29
29
  service.api('anything')
30
30
  end
31
31
 
32
- it "should properly handle the http_component parameter"
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
33
52
 
34
- it "should execute a block to test for errors if passed one"
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
35
71
 
36
72
  it "should handle rogue true/false as responses" do
37
73
  Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
@@ -34,6 +34,10 @@ shared_examples_for "Koala GraphAPI without an access token" do
34
34
  @api.get_picture("chris.baclig").should =~ /http\:\/\//
35
35
  end
36
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
+
37
41
  it "should be able to access connections from public Pages" do
38
42
  result = @api.get_connections("contextoptional", "likes")
39
43
  result.should be_a(Array)
@@ -31,6 +31,10 @@ it "should get public data about a user" do
31
31
  @api.get_picture("chris.baclig").should =~ /http\:\/\//
32
32
  end
33
33
 
34
+ it "should be able to access a user's picture, given a picture type" do
35
+ @api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
36
+ end
37
+
34
38
  it "should be able to access connections from users" do
35
39
  result = @api.get_connections("lukeshepard", "likes")
36
40
  result.length.should > 0
@@ -1,4 +1,4 @@
1
- require 'http_services'
1
+ require 'koala/http_services'
2
2
 
3
3
  class NetHTTPServiceTests < Test::Unit::TestCase
4
4
  module Bear
@@ -8,10 +8,174 @@ class NetHTTPServiceTests < Test::Unit::TestCase
8
8
  it "should define a make_request static module method" do
9
9
  Bear.respond_to?(:make_request).should be_true
10
10
  end
11
-
12
- it "should return a string for location header"
13
11
 
14
- it "should use POST if verb is not GET"
12
+ describe "when making a request" do
13
+ before(:each) do
14
+ # Setup stubs for make_request to execute without exceptions
15
+ @mock_http_response = stub('Net::HTTPResponse', :code => 1)
16
+ @mock_body = stub('Net::HTTPResponse body')
17
+ @http_request_result = [@mock_http_response, @mock_body]
18
+
19
+ @http_yield_mock = mock('Net::HTTP start yielded object')
20
+
21
+ @http_yield_mock.stub(:post).and_return(@http_request_result)
22
+ @http_yield_mock.stub(:get).and_return(@http_request_result)
23
+
24
+ @http_mock = stub('Net::HTTP object', 'use_ssl=' => true, 'verify_mode=' => true)
25
+ @http_mock.stub(:start).and_yield(@http_yield_mock)
26
+
27
+ Net::HTTP.stub(:new).and_return(@http_mock)
28
+ end
29
+
30
+ it "should use POST if verb is not GET" do
31
+ @http_yield_mock.should_receive(:post).and_return(@mock_http_response)
32
+ @http_mock.should_receive(:start).and_yield(@http_yield_mock)
33
+
34
+ Bear.make_request('anything', {}, 'anything')
35
+ end
36
+
37
+ it "should use port 443" do
38
+ Net::HTTP.should_receive(:new).with(anything, 443).and_return(@http_mock)
39
+
40
+ Bear.make_request('anything', {}, 'anything')
41
+ end
42
+
43
+ it "should use SSL" do
44
+ @http_mock.should_receive('use_ssl=').with(true)
45
+
46
+ Bear.make_request('anything', {}, 'anything')
47
+ end
48
+
49
+ it "should use the graph server by default" do
50
+ Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
51
+ Bear.make_request('anything', {}, 'anything')
52
+ end
53
+
54
+ it "should use the REST server if the :rest_api option is true" do
55
+ Net::HTTP.should_receive(:new).with(Koala::Facebook::REST_SERVER, anything).and_return(@http_mock)
56
+ Bear.make_request('anything', {}, 'anything', :rest_api => true)
57
+ end
58
+
59
+ it "should turn off vertificate validaiton warnings" do
60
+ @http_mock.should_receive('verify_mode=').with(OpenSSL::SSL::VERIFY_NONE)
61
+
62
+ Bear.make_request('anything', {}, 'anything')
63
+ end
64
+
65
+ it "should start an HTTP connection" do
66
+ @http_mock.should_receive(:start).and_yield(@http_yield_mock)
67
+ Bear.make_request('anything', {}, 'anything')
68
+ end
69
+
70
+ describe "via POST" do
71
+ it "should use Net::HTTP to make a POST request" do
72
+ @http_yield_mock.should_receive(:post).and_return(@http_request_result)
73
+
74
+ Bear.make_request('anything', {}, 'post')
75
+ end
76
+
77
+ it "should go to the specified path" do
78
+ path = mock('Path')
79
+ @http_yield_mock.should_receive(:post).with(path, anything).and_return(@http_request_result)
80
+
81
+ Bear.make_request(path, {}, 'post')
82
+ end
83
+
84
+ it "should use encoded parameters" do
85
+ args = {}
86
+ params = mock('Encoded parameters')
87
+ Bear.should_receive(:encode_params).with(args).and_return(params)
88
+
89
+ @http_yield_mock.should_receive(:post).with(anything, params).and_return(@http_request_result)
90
+
91
+ Bear.make_request('anything', args, 'post')
92
+ end
93
+ end
94
+
95
+ describe "via GET" do
96
+ it "should use Net::HTTP to make a GET request" do
97
+ @http_yield_mock.should_receive(:get).and_return(@http_request_result)
98
+
99
+ Bear.make_request('anything', {}, 'get')
100
+ end
101
+
102
+ it "should use the correct path, including arguments" do
103
+ path = mock('Path')
104
+ params = mock('Encoded parameters')
105
+ args = {}
106
+
107
+ Bear.should_receive(:encode_params).with(args).and_return(params)
108
+ @http_yield_mock.should_receive(:get).with("#{path}?#{params}").and_return(@http_request_result)
109
+
110
+ Bear.make_request(path, args, 'get')
111
+ end
112
+ end
113
+
114
+ describe "the returned value" do
115
+ before(:each) do
116
+ @response = Bear.make_request('anything', {}, 'anything')
117
+ end
118
+
119
+ it "should return a Koala::Response object" do
120
+ @response.class.should == Koala::Response
121
+ end
122
+
123
+ it "should return a Koala::Response with the right status" do
124
+ @response.status.should == @mock_http_response.code
125
+ end
126
+
127
+ it "should reutrn a Koala::Response with the right body" do
128
+ @response.body.should == @mock_body
129
+ end
130
+
131
+ it "should return a Koala::Response with the Net::HTTPResponse object as headers" do
132
+ @response.headers.should == @mock_http_response
133
+ end
134
+ end # describe return value
135
+ end # describe when making a request
15
136
 
16
- it "should return a Koala::Response object"
137
+ describe "when encoding parameters" do
138
+ it "should return an empty string if param_hash evaluates to false" do
139
+ Bear.encode_params(nil).should == ''
140
+ end
141
+
142
+ it "should convert values to JSON if the value is not a String" do
143
+ val = 'json_value'
144
+ not_a_string = 'not_a_string'
145
+ not_a_string.stub(:class).and_return('NotAString')
146
+ not_a_string.should_receive(:to_json).and_return(val)
147
+
148
+ string = "hi"
149
+
150
+ args = {
151
+ not_a_string => not_a_string,
152
+ string => string
153
+ }
154
+
155
+ result = Bear.encode_params(args)
156
+ result.split('&').find do |key_and_val|
157
+ key_and_val.match("#{not_a_string}=#{val}")
158
+ end.should be_true
159
+ end
160
+
161
+ it "should escape all values" do
162
+ args = Hash[*(1..4).map {|i| [i.to_s, "Value #{i}($"]}.flatten]
163
+
164
+ result = Bear.encode_params(args)
165
+ result.split('&').each do |key_val|
166
+ key, val = key_val.split('=')
167
+ val.should == CGI.escape(args[key])
168
+ end
169
+ end
170
+
171
+ it "should convert all keys to Strings" do
172
+ args = Hash[*(1..4).map {|i| [i, "val#{i}"]}.flatten]
173
+
174
+ result = Bear.encode_params(args)
175
+ result.split('&').each do |key_val|
176
+ key, val = key_val.split('=')
177
+ key.should == args.find{|key_val_arr| key_val_arr.last == val}.first.to_s
178
+ end
179
+ end
180
+ end
17
181
  end
@@ -51,41 +51,64 @@ class FacebookOAuthTests < Test::Unit::TestCase
51
51
  @oauth.oauth_callback_url == nil).should be_true
52
52
  end
53
53
 
54
- # cookie parsing
55
- it "should properly parse valid cookies" do
56
- result = @oauth.get_user_from_cookie(@oauth_data["valid_cookies"])
57
- result["uid"].should
58
- end
54
+ describe "cookie parsing" do
55
+ describe "get_user_info_from_cookies" do
56
+ it "should properly parse valid cookies" do
57
+ result = @oauth.get_user_info_from_cookies(@oauth_data["valid_cookies"])
58
+ result.should be_a(Hash)
59
+ end
59
60
 
60
- it "should return all the cookie components from valid cookie string" do
61
- cookie_data = @oauth_data["valid_cookies"]
62
- parsing_results = @oauth.get_user_from_cookie(cookie_data)
63
- number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
64
- parsing_results.length.should == number_of_components
65
- end
61
+ it "should return all the cookie components from valid cookie string" do
62
+ cookie_data = @oauth_data["valid_cookies"]
63
+ parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
64
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
65
+ parsing_results.length.should == number_of_components
66
+ end
66
67
 
67
- it "should properly parse valid offline access cookies (e.g. no expiration)" do
68
- result = @oauth.get_user_from_cookie(@oauth_data["offline_access_cookies"])
69
- result["uid"].should
70
- end
68
+ it "should properly parse valid offline access cookies (e.g. no expiration)" do
69
+ result = @oauth.get_user_info_from_cookies(@oauth_data["offline_access_cookies"])
70
+ result["uid"].should
71
+ end
71
72
 
72
- it "should return all the cookie components from offline access cookies" do
73
- cookie_data = @oauth_data["offline_access_cookies"]
74
- parsing_results = @oauth.get_user_from_cookie(cookie_data)
75
- number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
76
- parsing_results.length.should == number_of_components
77
- end
73
+ it "should return all the cookie components from offline access cookies" do
74
+ cookie_data = @oauth_data["offline_access_cookies"]
75
+ parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
76
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
77
+ parsing_results.length.should == number_of_components
78
+ end
78
79
 
79
- it "shouldn't parse expired cookies" do
80
- result = @oauth.get_user_from_cookie(@oauth_data["expired_cookies"])
81
- result.should be_nil
82
- end
80
+ it "shouldn't parse expired cookies" do
81
+ result = @oauth.get_user_info_from_cookies(@oauth_data["expired_cookies"])
82
+ result.should be_nil
83
+ end
83
84
 
84
- it "shouldn't parse invalid cookies" do
85
- # make an invalid string by replacing some values
86
- bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
87
- result = @oauth.get_user_from_cookie(bad_cookie_hash)
88
- result.should be_nil
85
+ it "shouldn't parse invalid cookies" do
86
+ # make an invalid string by replacing some values
87
+ bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
88
+ result = @oauth.get_user_info_from_cookies(bad_cookie_hash)
89
+ result.should be_nil
90
+ end
91
+ end
92
+
93
+ describe "get_user_from_cookies" do
94
+ it "should use get_user_info_from_cookies to parse the cookies" do
95
+ data = @oauth_data["valid_cookies"]
96
+ @oauth.should_receive(:get_user_info_from_cookies).with(data).and_return({})
97
+ @oauth.get_user_from_cookies(data)
98
+ end
99
+
100
+ it "should use return a string if the cookies are valid" do
101
+ result = @oauth.get_user_from_cookies(@oauth_data["valid_cookies"])
102
+ result.should be_a(String)
103
+ end
104
+
105
+ it "should return nil if the cookies are invalid" do
106
+ # make an invalid string by replacing some values
107
+ bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
108
+ result = @oauth.get_user_from_cookies(bad_cookie_hash)
109
+ result.should be_nil
110
+ end
111
+ end
89
112
  end
90
113
 
91
114
  # OAuth URLs
@@ -138,40 +161,116 @@ class FacebookOAuthTests < Test::Unit::TestCase
138
161
  url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
139
162
  end
140
163
 
141
- it "should output a deprecation warning but generate a properly formatted OAuth token URL when provided a callback in the deprecated fashion" do
142
- callback = "foo.com"
143
- url = out = nil
144
-
145
- begin
146
- # we want to capture the deprecation warning as well as the output
147
- # credit to http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/ for the technique
148
- out = StringIO.new
149
- $stdout = out
150
- url = @oauth.url_for_access_token(@code, callback)
151
- ensure
152
- $stdout = STDOUT
164
+ describe "get_access_token_info" do
165
+ it "should properly get and parse an access token token results into a hash" do
166
+ result = @oauth.get_access_token_info(@code)
167
+ result.should be_a(Hash)
168
+ end
169
+
170
+ it "should properly include the access token results" do
171
+ result = @oauth.get_access_token_info(@code)
172
+ result["access_token"].should
173
+ end
174
+
175
+ it "should raise an error when get_access_token is called with a bad code" do
176
+ lambda { @oauth.get_access_token_info("foo") }.should raise_error(Koala::Facebook::APIError)
153
177
  end
154
-
155
- # two assertions may be bad test writing, but this is for a deprecated method
156
- url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
157
- out.should_not be_nil
158
178
  end
159
179
 
160
- # START CODE THAT NEEDS MOCKING
180
+ describe "get_access_token" do
181
+ it "should use get_access_token_info to get and parse an access token token results" do
182
+ result = @oauth.get_access_token(@code)
183
+ result.should be_a(String)
184
+ end
161
185
 
162
- # get_access_token
163
- it "should properly get and parse an access token token results" do
164
- result = @oauth.get_access_token(@code)
165
- result["access_token"].should
186
+ it "should return the access token as a string" do
187
+ result = @oauth.get_access_token(@code)
188
+ original = @oauth.get_access_token_info(@code)
189
+ result.should == original["access_token"]
190
+ end
191
+
192
+ it "should raise an error when get_access_token is called with a bad code" do
193
+ lambda { @oauth.get_access_token("foo") }.should raise_error(Koala::Facebook::APIError)
194
+ end
166
195
  end
167
196
 
168
- it "should raise an error when get_access_token is called with a bad code" do
169
- lambda { @oauth.get_access_token("foo") }.should raise_error(Koala::Facebook::APIError)
197
+ describe "get_app_access_token_info" do
198
+ it "should properly get and parse an app's access token as a hash" do
199
+ result = @oauth.get_app_access_token_info
200
+ result.should be_a(Hash)
201
+ end
202
+
203
+ it "should include the access token" do
204
+ result = @oauth.get_app_access_token_info
205
+ result["access_token"].should
206
+ end
170
207
  end
171
208
 
172
- it "should properly get and parse an app's access token token results" do
173
- result = @oauth.get_app_access_token
174
- result["access_token"].should
209
+ describe "get_app_acess_token" do
210
+ it "should use get_access_token_info to get and parse an access token token results" do
211
+ result = @oauth.get_app_access_token
212
+ result.should be_a(String)
213
+ end
214
+
215
+ it "should return the access token as a string" do
216
+ result = @oauth.get_app_access_token
217
+ original = @oauth.get_app_access_token_info
218
+ result.should == original["access_token"]
219
+ end
220
+ end
221
+
222
+ describe "exchanging session keys" do
223
+ describe "with get_token_info_from_session_keys" do
224
+ it "should get an array of session keys from Facebook when passed a single key" do
225
+ result = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
226
+ result.should be_an(Array)
227
+ result.length.should == 1
228
+ end
229
+
230
+ it "should get an array of session keys from Facebook when passed multiple keys" do
231
+ result = @oauth.get_tokens_from_session_keys(@oauth_data["multiple_session_keys"])
232
+ result.should be_an(Array)
233
+ result.length.should == 2
234
+ end
235
+
236
+ it "should return the original hashes" do
237
+ result = @oauth.get_token_info_from_session_keys(@oauth_data["multiple_session_keys"])
238
+ result[0].should be_a(Hash)
239
+ end
240
+ end
241
+
242
+ describe "with get_tokens_from_session_keys" do
243
+ it "should call get_token_info_from_session_keys" do
244
+ args = @oauth_data["multiple_session_keys"]
245
+ @oauth.should_receive(:get_token_info_from_session_keys).with(args).and_return([])
246
+ @oauth.get_tokens_from_session_keys(args)
247
+ end
248
+
249
+ it "should return an array of strings" do
250
+ args = @oauth_data["multiple_session_keys"]
251
+ result = @oauth.get_tokens_from_session_keys(args)
252
+ result.each {|r| r.should be_a(String) }
253
+ end
254
+ end
255
+
256
+ describe "get_token_from_session_key" do
257
+ it "should call get_tokens_from_session_keys when the get_token_from_session_key is called" do
258
+ key = @oauth_data["session_key"]
259
+ @oauth.should_receive(:get_tokens_from_session_keys).with([key]).and_return([])
260
+ @oauth.get_token_from_session_key(key)
261
+ end
262
+
263
+ it "should get back the access token string from get_token_from_session_key" do
264
+ result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
265
+ result.should be_a(String)
266
+ end
267
+
268
+ it "should be the first value in the array" do
269
+ result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
270
+ array = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
271
+ result.should == array[0]
272
+ end
273
+ end
175
274
  end
176
275
 
177
276
  # protected methods
@@ -114,6 +114,18 @@ graph_api:
114
114
  code: 302
115
115
  headers:
116
116
  Location: http://facebook.com/
117
+ type=large:
118
+ get:
119
+ no_token:
120
+ code: 302
121
+ headers:
122
+ Location: http://facebook.com/large
123
+ with_token:
124
+ code: 302
125
+ headers:
126
+ Location: http://facebook.com/large
127
+
128
+
117
129
  search:
118
130
  q=facebook:
119
131
  get:
@@ -136,7 +148,15 @@ graph_api:
136
148
  client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&type=client_cred:
137
149
  post:
138
150
  no_token: access_token=<%= ACCESS_TOKEN %>
139
-
151
+ oauth/exchange_sessions:
152
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= OAUTH_DATA["session_key"] %>&type=client_cred:
153
+ post:
154
+ no_token: '[{"access_token":"<%= ACCESS_TOKEN %>","expires":4315}]'
155
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= OAUTH_DATA["multiple_session_keys"].join(",") %>&type=client_cred:
156
+ post:
157
+ no_token: '[{"access_token":"<%= ACCESS_TOKEN %>","expires":4315}, {"access_token":"<%= ACCESS_TOKEN %>","expires":4315}]'
158
+
159
+
140
160
 
141
161
  # -- Subscription Responses --
142
162
  <%= APP_ID %>/subscriptions:
@@ -1,4 +1,5 @@
1
1
  require 'erb'
2
+ require 'yaml'
2
3
 
3
4
  module Koala
4
5
  module MockHTTPService
@@ -15,7 +16,11 @@ module Koala
15
16
 
16
17
  # Useful in mock_facebook_responses.yml
17
18
  OAUTH_DATA = TEST_DATA['oauth_test_data']
18
- OAUTH_DATA.merge!('app_access_token' => Koala::MockHTTPService::ACCESS_TOKEN)
19
+ OAUTH_DATA.merge!({
20
+ 'app_access_token' => Koala::MockHTTPService::ACCESS_TOKEN,
21
+ 'session_key' => "session_key",
22
+ 'multiple_session_keys' => ["session_key", "session_key_2"]
23
+ })
19
24
  APP_ID = OAUTH_DATA['app_id']
20
25
  SECRET = OAUTH_DATA['secret']
21
26
  SUBSCRIPTION_DATA = TEST_DATA["subscription_test_data"]
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 7
8
- - 1
9
- version: 0.7.1
7
+ - 8
8
+ - 0
9
+ version: 0.8.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-27 00:00:00 -07:00
17
+ date: 2010-06-27 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -26,21 +26,24 @@ extensions: []
26
26
 
27
27
  extra_rdoc_files:
28
28
  - CHANGELOG
29
- - lib/graph_api.rb
30
- - lib/http_services.rb
29
+ - LICENSE
31
30
  - lib/koala.rb
32
- - lib/realtime_updates.rb
33
- - lib/rest_api.rb
31
+ - lib/koala/graph_api.rb
32
+ - lib/koala/http_services.rb
33
+ - lib/koala/realtime_updates.rb
34
+ - lib/koala/rest_api.rb
34
35
  files:
35
36
  - CHANGELOG
37
+ - LICENSE
36
38
  - Manifest
37
39
  - Rakefile
38
40
  - init.rb
39
- - lib/graph_api.rb
40
- - lib/http_services.rb
41
+ - koala.gemspec
41
42
  - lib/koala.rb
42
- - lib/realtime_updates.rb
43
- - lib/rest_api.rb
43
+ - lib/koala/graph_api.rb
44
+ - lib/koala/http_services.rb
45
+ - lib/koala/realtime_updates.rb
46
+ - lib/koala/rest_api.rb
44
47
  - readme.md
45
48
  - spec/facebook_data.yml
46
49
  - spec/koala/api_base_tests.rb
@@ -59,7 +62,6 @@ files:
59
62
  - spec/koala_spec_without_mocks.rb
60
63
  - spec/mock_facebook_responses.yml
61
64
  - spec/mock_http_service.rb
62
- - koala.gemspec
63
65
  has_rdoc: true
64
66
  homepage: http://github.com/arsduo/koala
65
67
  licenses: []
File without changes
File without changes