koala 1.2.0beta1 → 1.2.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 +27 -2
- data/koala.gemspec +7 -8
- data/lib/koala/graph_api.rb +11 -22
- data/lib/koala/graph_batch_api.rb +11 -1
- data/lib/koala/graph_collection.rb +7 -2
- data/lib/koala/http_service.rb +3 -3
- data/lib/koala/multipart_request.rb +35 -0
- data/lib/koala/oauth.rb +37 -24
- data/lib/koala/realtime_updates.rb +1 -1
- data/lib/koala/rest_api.rb +5 -0
- data/lib/koala/test_users.rb +2 -3
- data/lib/koala/uploadable_io.rb +2 -1
- data/lib/koala/version.rb +3 -0
- data/lib/koala.rb +8 -2
- data/readme.md +44 -7
- data/spec/cases/{api_base_spec.rb → api_spec.rb} +35 -10
- data/spec/cases/graph_and_rest_api_spec.rb +0 -26
- data/spec/cases/graph_api_batch_spec.rb +12 -30
- data/spec/cases/graph_api_spec.rb +0 -20
- data/spec/cases/graph_collection_spec.rb +116 -0
- data/spec/cases/http_service_spec.rb +13 -12
- data/spec/cases/koala_spec.rb +7 -3
- data/spec/cases/multipart_request_spec.rb +66 -0
- data/spec/cases/oauth_spec.rb +227 -102
- data/spec/cases/rest_api_spec.rb +0 -19
- data/spec/cases/test_users_spec.rb +25 -35
- data/spec/cases/uploadable_io_spec.rb +28 -0
- data/spec/fixtures/facebook_data.yml +3 -2
- data/spec/fixtures/mock_facebook_responses.yml +18 -5
- data/spec/support/graph_api_shared_examples.rb +105 -126
- data/spec/support/koala_test.rb +41 -15
- data/spec/support/mock_http_service.rb +2 -1
- data/spec/support/rest_api_shared_examples.rb +69 -25
- metadata +94 -72
data/CHANGELOG
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
|
+
v1.2.1
|
|
2
|
+
New methods:
|
|
3
|
+
-- RestAPI.set_app_properties handles JSON-encoding application properties
|
|
4
|
+
Updated methods:
|
|
5
|
+
-- OAuth.get_user_from_cookie works with the new signed cookie format (thanks, gmccreight!)
|
|
6
|
+
-- Beta server URLs are now correct
|
|
7
|
+
-- OAuth.parse_signed_request now raises an informative error if the signed_request is malformed
|
|
8
|
+
Internal improvements:
|
|
9
|
+
-- Koala::Multipart middleware properly encoding nested parameters (hashes) in POSTs
|
|
10
|
+
-- Updated readme, changelog, etc.
|
|
11
|
+
Testing improvements:
|
|
12
|
+
-- Live tests with test users now clean up all fake users they create
|
|
13
|
+
-- Removed duplicate test cases
|
|
14
|
+
-- Live tests with test users no longer delete each object they create, speeding things up
|
|
15
|
+
|
|
1
16
|
v1.2
|
|
2
17
|
New methods:
|
|
3
18
|
-- API is now the main API class, contains both Graph and REST methods
|
|
4
19
|
-- Old classes are aliased with deprecation warnings (non-breaking change)
|
|
5
20
|
-- TestUsers#update lets you update the name or password of an existing test user
|
|
6
21
|
-- API.get_page_access_token lets you easily fetch the access token for a page you manage (thanks, marcgg!)
|
|
22
|
+
-- Added version.rb (Koala::VERSION)
|
|
7
23
|
Updated methods:
|
|
24
|
+
-- OAuth now parses Facebook's new signed cookie format
|
|
8
25
|
-- API.put_picture now accepts URLs to images (thanks, marcgg!)
|
|
26
|
+
-- Bug fixes to put_picture, parse_signed_request, and the test suite (thanks, johnbhall and Will S.!)
|
|
27
|
+
-- Smarter GraphCollection use
|
|
28
|
+
-- Any pageable result will now become a GraphCollection
|
|
29
|
+
-- Non-pageable results from get_connections no longer error
|
|
30
|
+
-- GraphCollection.raw_results allows access to original result data
|
|
31
|
+
-- Koala no longer enforces any limits on the number of test users you create at once
|
|
9
32
|
Internal improvements:
|
|
10
33
|
-- Koala now uses Faraday to make requests, replacing the HTTPServices (see wiki)
|
|
11
34
|
-- Koala::HTTPService.http_options allows specification of default Faraday connection options
|
|
@@ -15,13 +38,15 @@ Internal improvements:
|
|
|
15
38
|
-- Koala no longer automatically switches to Net::HTTP when uploading IO objects to Facebook
|
|
16
39
|
-- RealTimeUpdates and TestUsers are no longer subclasses of API, but have their own .api objects
|
|
17
40
|
-- The old .graph_api accessor is aliases to .api with a deprecation warning
|
|
41
|
+
-- Removed deprecation warnings for pre-1.1 batch interface
|
|
18
42
|
Testing improvements:
|
|
19
43
|
-- Live test suites now run against test users by default
|
|
20
44
|
-- Test suite can be repeatedly run live without having to update facebook_data.yml
|
|
21
45
|
-- OAuth code and session key tests cannot be run against test users
|
|
22
|
-
-- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the
|
|
46
|
+
-- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the rspec command
|
|
47
|
+
-- Live tests can be run against the beta server by specifying BETA=true in the rspec command
|
|
23
48
|
-- Tests now pass against all rubies on Travis CI
|
|
24
|
-
-- Expanded test coverage
|
|
49
|
+
-- Expanded and refactored test coverage
|
|
25
50
|
-- Fixed bug with YAML parsing in Ruby 1.9
|
|
26
51
|
|
|
27
52
|
v1.1
|
data/koala.gemspec
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
|
3
|
+
require 'koala/version'
|
|
2
4
|
|
|
3
5
|
Gem::Specification.new do |s|
|
|
4
6
|
s.name = %q{koala}
|
|
5
|
-
s.version =
|
|
6
|
-
s.date = %q{2011-
|
|
7
|
+
s.version = Koala::VERSION
|
|
8
|
+
s.date = %q{2011-10-04}
|
|
7
9
|
|
|
8
10
|
s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
|
|
9
11
|
s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph and REST APIs, 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.}
|
|
@@ -29,22 +31,19 @@ Gem::Specification.new do |s|
|
|
|
29
31
|
|
|
30
32
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
31
33
|
s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
|
|
32
|
-
s.add_runtime_dependency(%q<faraday>, ["~> 0.7.
|
|
33
|
-
s.add_runtime_dependency(%q<faraday-stack>, ["~> 0.1.3"])
|
|
34
|
+
s.add_runtime_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
34
35
|
s.add_development_dependency(%q<rspec>, ["~> 2.5"])
|
|
35
36
|
s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
|
|
36
37
|
else
|
|
37
38
|
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
38
39
|
s.add_dependency(%q<rspec>, ["~> 2.5"])
|
|
39
40
|
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
40
|
-
s.add_dependency(%q<faraday>, ["~> 0.7.
|
|
41
|
-
s.add_dependency(%q<faraday-stack>, ["~> 0.1.3"])
|
|
41
|
+
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
42
42
|
end
|
|
43
43
|
else
|
|
44
44
|
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
45
45
|
s.add_dependency(%q<rspec>, ["~> 2.5"])
|
|
46
46
|
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
47
|
-
s.add_dependency(%q<faraday>, ["~> 0.7.
|
|
48
|
-
s.add_dependency(%q<faraday-stack>, ["~> 0.1.3"])
|
|
47
|
+
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
49
48
|
end
|
|
50
49
|
end
|
data/lib/koala/graph_api.rb
CHANGED
|
@@ -71,9 +71,7 @@ module Koala
|
|
|
71
71
|
|
|
72
72
|
def get_connections(id, connection_name, args = {}, options = {})
|
|
73
73
|
# Fetchs the connections for given object.
|
|
74
|
-
graph_call("#{id}/#{connection_name}", args, "get", options)
|
|
75
|
-
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
|
76
|
-
end
|
|
74
|
+
graph_call("#{id}/#{connection_name}", args, "get", options)
|
|
77
75
|
end
|
|
78
76
|
|
|
79
77
|
def put_connections(id, connection_name, args = {}, options = {})
|
|
@@ -174,9 +172,7 @@ module Koala
|
|
|
174
172
|
|
|
175
173
|
def search(search_terms, args = {}, options = {})
|
|
176
174
|
args.merge!({:q => search_terms}) unless search_terms.nil?
|
|
177
|
-
graph_call("search", args, "get", options)
|
|
178
|
-
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
|
179
|
-
end
|
|
175
|
+
graph_call("search", args, "get", options)
|
|
180
176
|
end
|
|
181
177
|
|
|
182
178
|
# Convenience Methods
|
|
@@ -199,14 +195,12 @@ module Koala
|
|
|
199
195
|
def get_page(params)
|
|
200
196
|
# Pages through a set of results stored in a GraphCollection
|
|
201
197
|
# Used for connections and search results
|
|
202
|
-
graph_call(*params)
|
|
203
|
-
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
|
204
|
-
end
|
|
198
|
+
graph_call(*params)
|
|
205
199
|
end
|
|
206
200
|
|
|
207
201
|
# Batch API
|
|
208
202
|
def batch(http_options = {}, &block)
|
|
209
|
-
batch_client = GraphBatchAPI.new(access_token)
|
|
203
|
+
batch_client = GraphBatchAPI.new(access_token, self)
|
|
210
204
|
if block
|
|
211
205
|
yield batch_client
|
|
212
206
|
batch_client.execute(http_options)
|
|
@@ -214,15 +208,7 @@ module Koala
|
|
|
214
208
|
batch_client
|
|
215
209
|
end
|
|
216
210
|
end
|
|
217
|
-
|
|
218
|
-
def self.included(base)
|
|
219
|
-
base.class_eval do
|
|
220
|
-
def self.batch
|
|
221
|
-
raise NoMethodError, "The BatchAPI signature has changed (the original implementation was not thread-safe). Please see https://github.com/arsduo/koala/wiki/Batch-requests. (This message will be removed in the final 1.1 release.)"
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
211
|
+
|
|
226
212
|
# Direct access to the Facebook API
|
|
227
213
|
# see any of the above methods for example invocations
|
|
228
214
|
def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
|
|
@@ -231,10 +217,15 @@ module Koala
|
|
|
231
217
|
raise error if error
|
|
232
218
|
end
|
|
233
219
|
|
|
234
|
-
#
|
|
220
|
+
# turn this into a GraphCollection if it's pageable
|
|
221
|
+
result = GraphCollection.evaluate(result, self)
|
|
222
|
+
|
|
223
|
+
# now process as appropriate for the given call (get picture header, etc.)
|
|
235
224
|
post_processing ? post_processing.call(result) : result
|
|
236
225
|
end
|
|
237
226
|
|
|
227
|
+
private
|
|
228
|
+
|
|
238
229
|
def check_response(response)
|
|
239
230
|
# check for Graph API-specific errors
|
|
240
231
|
# this returns an error, which is immediately raised (non-batch)
|
|
@@ -244,8 +235,6 @@ module Koala
|
|
|
244
235
|
end
|
|
245
236
|
end
|
|
246
237
|
|
|
247
|
-
private
|
|
248
|
-
|
|
249
238
|
def parse_media_args(media_args, method)
|
|
250
239
|
# photo and video uploads can accept different types of arguments (see above)
|
|
251
240
|
# so here, we parse the arguments into a form directly usable in put_object
|
|
@@ -4,6 +4,13 @@ module Koala
|
|
|
4
4
|
|
|
5
5
|
def self.included(base)
|
|
6
6
|
base.class_eval do
|
|
7
|
+
attr_reader :original_api
|
|
8
|
+
|
|
9
|
+
def initialize(access_token, api)
|
|
10
|
+
super(access_token)
|
|
11
|
+
@original_api = api
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
alias_method :graph_call_outside_batch, :graph_call
|
|
8
15
|
alias_method :graph_call, :graph_call_in_batch
|
|
9
16
|
|
|
@@ -46,7 +53,7 @@ module Koala
|
|
|
46
53
|
batch_op.to_batch_params(access_token)
|
|
47
54
|
})
|
|
48
55
|
|
|
49
|
-
graph_call_outside_batch('/', args, 'post', http_options) do |response|
|
|
56
|
+
batch_result = graph_call_outside_batch('/', args, 'post', http_options) do |response|
|
|
50
57
|
# map the results with post-processing included
|
|
51
58
|
index = 0 # keep compat with ruby 1.8 - no with_index for map
|
|
52
59
|
response.map do |call_result|
|
|
@@ -80,6 +87,9 @@ module Koala
|
|
|
80
87
|
end
|
|
81
88
|
end
|
|
82
89
|
end
|
|
90
|
+
|
|
91
|
+
# turn any results that are pageable into GraphCollections
|
|
92
|
+
batch_result.inject([]) {|processed_results, raw| processed_results << GraphCollection.evaluate(raw, @original_api)}
|
|
83
93
|
end
|
|
84
94
|
|
|
85
95
|
end
|
|
@@ -10,12 +10,17 @@ module Koala
|
|
|
10
10
|
# It also allows access to paging information and the
|
|
11
11
|
# ability to get the next/previous page in the collection
|
|
12
12
|
# by calling next_page or previous_page.
|
|
13
|
-
attr_reader :paging
|
|
14
|
-
attr_reader :api
|
|
13
|
+
attr_reader :paging, :api, :raw_response
|
|
15
14
|
|
|
15
|
+
def self.evaluate(response, api)
|
|
16
|
+
# turn the response into a GraphCollection if it's pageable; if not, return the original response
|
|
17
|
+
response.is_a?(Hash) && response["data"].is_a?(Array) ? self.new(response, api) : response
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
def initialize(response, api)
|
|
17
21
|
super response["data"]
|
|
18
22
|
@paging = response["paging"]
|
|
23
|
+
@raw_response = response
|
|
19
24
|
@api = api
|
|
20
25
|
end
|
|
21
26
|
|
data/lib/koala/http_service.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'faraday'
|
|
2
|
-
require 'faraday_stack'
|
|
3
2
|
|
|
4
3
|
module Koala
|
|
5
4
|
class Response
|
|
@@ -21,7 +20,7 @@ module Koala
|
|
|
21
20
|
@http_options ||= {}
|
|
22
21
|
|
|
23
22
|
DEFAULT_MIDDLEWARE = Proc.new do |builder|
|
|
24
|
-
builder.
|
|
23
|
+
builder.use Koala::MultipartRequest
|
|
25
24
|
builder.request :url_encoded
|
|
26
25
|
builder.adapter Faraday.default_adapter
|
|
27
26
|
end
|
|
@@ -29,7 +28,8 @@ module Koala
|
|
|
29
28
|
def self.server(options = {})
|
|
30
29
|
server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
|
|
31
30
|
server.gsub!(/\.facebook/, "-video.facebook") if options[:video]
|
|
32
|
-
|
|
31
|
+
server.gsub!(/\.facebook/, ".beta.facebook") if options[:beta]
|
|
32
|
+
"#{options[:use_ssl] ? "https" : "http"}://#{server}"
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def self.make_request(path, args, verb, options = {})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Koala
|
|
4
|
+
class MultipartRequest < Faraday::Request::Multipart
|
|
5
|
+
# Facebook expects nested parameters to be passed in a certain way
|
|
6
|
+
# Based on our testing (https://github.com/arsduo/koala/issues/125),
|
|
7
|
+
# Faraday needs two changes to make that work:
|
|
8
|
+
# 1) [] need to be escaped (e.g. params[foo]=bar ==> params%5Bfoo%5D=bar)
|
|
9
|
+
# 2) such messages need to be multipart-encoded
|
|
10
|
+
|
|
11
|
+
self.mime_type = 'multipart/form-data'.freeze
|
|
12
|
+
|
|
13
|
+
def process_request?(env)
|
|
14
|
+
# if the request values contain any hashes or arrays, multipart it
|
|
15
|
+
super || !!(env[:body].respond_to?(:values) && env[:body].values.find {|v| v.is_a?(Hash) || v.is_a?(Array)})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def process_params(params, prefix = nil, pieces = nil, &block)
|
|
20
|
+
params.inject(pieces || []) do |all, (key, value)|
|
|
21
|
+
key = "#{prefix}%5B#{key}%5D" if prefix
|
|
22
|
+
|
|
23
|
+
case value
|
|
24
|
+
when Array
|
|
25
|
+
values = value.inject([]) { |a,v| a << [nil, v] }
|
|
26
|
+
process_params(values, key, all, &block)
|
|
27
|
+
when Hash
|
|
28
|
+
process_params(value, key, all, &block)
|
|
29
|
+
else
|
|
30
|
+
all << block.call(key, value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/koala/oauth.rb
CHANGED
|
@@ -9,38 +9,26 @@ module Koala
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def get_user_info_from_cookie(cookie_hash)
|
|
12
|
-
# Parses the cookie set
|
|
13
|
-
#
|
|
14
|
-
# cookies should be a Hash, like the one Rails provides
|
|
12
|
+
# Parses the cookie set Facebook's JavaScript SDK.
|
|
13
|
+
# You can pass Rack/Rails/Sinatra's cookie hash directly to this method.
|
|
15
14
|
#
|
|
16
15
|
# If the user is logged in via Facebook, we return a dictionary with the
|
|
17
16
|
# keys "uid" and "access_token". The former is the user's Facebook ID,
|
|
18
17
|
# and the latter can be used to make authenticated requests to the Graph API.
|
|
19
18
|
# If the user is not logged in, we return None.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
|
|
26
|
-
# remove the opening/closing quote
|
|
27
|
-
fb_cookie = fb_cookie.gsub(/\"/, "")
|
|
28
|
-
|
|
29
|
-
# since we no longer get individual cookies, we have to separate out the components ourselves
|
|
30
|
-
components = {}
|
|
31
|
-
fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
|
|
32
|
-
|
|
33
|
-
# generate the signature and make sure it matches what we expect
|
|
34
|
-
auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
|
|
35
|
-
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
|
36
|
-
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
|
19
|
+
|
|
20
|
+
if signed_cookie = cookie_hash["fbsr_#{@app_id}"]
|
|
21
|
+
parse_signed_cookie(signed_cookie)
|
|
22
|
+
elsif unsigned_cookie = cookie_hash["fbs_#{@app_id}"]
|
|
23
|
+
parse_unsigned_cookie(unsigned_cookie)
|
|
37
24
|
end
|
|
38
25
|
end
|
|
39
26
|
alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
|
|
40
27
|
|
|
41
28
|
def get_user_from_cookie(cookies)
|
|
42
29
|
if info = get_user_info_from_cookies(cookies)
|
|
43
|
-
|
|
30
|
+
# signed cookie has user_id, unsigned cookie has uid
|
|
31
|
+
string = info["user_id"] || info["uid"]
|
|
44
32
|
end
|
|
45
33
|
end
|
|
46
34
|
alias_method :get_user_from_cookies, :get_user_from_cookie
|
|
@@ -70,7 +58,7 @@ module Koala
|
|
|
70
58
|
def get_access_token_info(code, options = {})
|
|
71
59
|
# convenience method to get a parsed token from Facebook for a given code
|
|
72
60
|
# should this require an OAuth callback URL?
|
|
73
|
-
get_token_from_server({:code => code, :redirect_uri => @oauth_callback_url}, false, options)
|
|
61
|
+
get_token_from_server({:code => code, :redirect_uri => options[:redirect_uri] || @oauth_callback_url}, false, options)
|
|
74
62
|
end
|
|
75
63
|
|
|
76
64
|
def get_access_token(code, options = {})
|
|
@@ -98,16 +86,18 @@ module Koala
|
|
|
98
86
|
# for a more accurate reference implementation strategy.
|
|
99
87
|
def parse_signed_request(input)
|
|
100
88
|
encoded_sig, encoded_envelope = input.split('.', 2)
|
|
89
|
+
raise 'SignedRequest: Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
|
|
90
|
+
|
|
101
91
|
signature = base64_url_decode(encoded_sig).unpack("H*").first
|
|
102
92
|
envelope = MultiJson.decode(base64_url_decode(encoded_envelope))
|
|
103
93
|
|
|
104
94
|
raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
|
|
105
95
|
|
|
106
96
|
# now see if the signature is valid (digest, key, data)
|
|
107
|
-
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope
|
|
97
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope)
|
|
108
98
|
raise 'SignedRequest: Invalid signature' if (signature != hmac)
|
|
109
99
|
|
|
110
|
-
|
|
100
|
+
envelope
|
|
111
101
|
end
|
|
112
102
|
|
|
113
103
|
# from session keys
|
|
@@ -162,6 +152,29 @@ module Koala
|
|
|
162
152
|
end
|
|
163
153
|
components
|
|
164
154
|
end
|
|
155
|
+
|
|
156
|
+
def parse_unsigned_cookie(fb_cookie)
|
|
157
|
+
# remove the opening/closing quote
|
|
158
|
+
fb_cookie = fb_cookie.gsub(/\"/, "")
|
|
159
|
+
|
|
160
|
+
# since we no longer get individual cookies, we have to separate out the components ourselves
|
|
161
|
+
components = {}
|
|
162
|
+
fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
|
|
163
|
+
|
|
164
|
+
# generate the signature and make sure it matches what we expect
|
|
165
|
+
auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
|
|
166
|
+
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
|
167
|
+
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def parse_signed_cookie(fb_cookie)
|
|
171
|
+
components = parse_signed_request(fb_cookie)
|
|
172
|
+
if (code = components["code"]) && token_info = get_access_token_info(code, :redirect_uri => '')
|
|
173
|
+
components.merge(token_info)
|
|
174
|
+
else
|
|
175
|
+
nil
|
|
176
|
+
end
|
|
177
|
+
end
|
|
165
178
|
|
|
166
179
|
def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
|
|
167
180
|
Koala.make_request("/oauth/#{endpoint}", {
|
data/lib/koala/rest_api.rb
CHANGED
|
@@ -13,6 +13,11 @@ module Koala
|
|
|
13
13
|
results.inject({}) {|outcome, data| outcome[data["name"]] = data["fql_result_set"]; outcome}
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
def set_app_properties(properties, args = {}, options = {})
|
|
18
|
+
raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "setAppProperties requires an access token"}) unless @access_token
|
|
19
|
+
rest_call("admin.setAppProperties", args.merge(:properties => MultiJson.encode(properties)), options, "post")
|
|
20
|
+
end
|
|
16
21
|
|
|
17
22
|
def rest_call(fb_method, args = {}, options = {}, method = "get")
|
|
18
23
|
options = options.merge!(:rest_api => true, :read_only => READ_ONLY_METHODS.include?(fb_method.to_s))
|
data/lib/koala/test_users.rb
CHANGED
|
@@ -31,11 +31,11 @@ module Koala
|
|
|
31
31
|
# Creates and returns a test user
|
|
32
32
|
args['installed'] = installed
|
|
33
33
|
args['permissions'] = (permissions.is_a?(Array) ? permissions.join(",") : permissions) if installed
|
|
34
|
-
|
|
34
|
+
@api.graph_call(accounts_path, args, "post", options)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def list
|
|
38
|
-
@api.graph_call(accounts_path)
|
|
38
|
+
@api.graph_call(accounts_path)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def delete(test_user)
|
|
@@ -72,7 +72,6 @@ module Koala
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def create_network(network_size, installed = true, permissions = '')
|
|
75
|
-
network_size = 50 if network_size > 50 # FB's max is 50
|
|
76
75
|
users = (0...network_size).collect { create(installed, permissions) }
|
|
77
76
|
friends = users.clone
|
|
78
77
|
users.each do |user|
|
data/lib/koala/uploadable_io.rb
CHANGED
data/lib/koala.rb
CHANGED
|
@@ -8,7 +8,6 @@ require 'openssl'
|
|
|
8
8
|
require 'base64'
|
|
9
9
|
|
|
10
10
|
# include koala modules
|
|
11
|
-
require 'koala/http_service'
|
|
12
11
|
require 'koala/oauth'
|
|
13
12
|
require 'koala/graph_api'
|
|
14
13
|
require 'koala/graph_batch_api'
|
|
@@ -17,11 +16,18 @@ require 'koala/graph_collection'
|
|
|
17
16
|
require 'koala/rest_api'
|
|
18
17
|
require 'koala/realtime_updates'
|
|
19
18
|
require 'koala/test_users'
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
# HTTP module so we can communicate with Facebook
|
|
21
|
+
require 'koala/http_service'
|
|
22
|
+
require 'koala/multipart_request'
|
|
21
23
|
|
|
22
24
|
# add KoalaIO class
|
|
23
25
|
require 'koala/uploadable_io'
|
|
24
26
|
|
|
27
|
+
# miscellaneous
|
|
28
|
+
require 'koala/utils'
|
|
29
|
+
require 'koala/version'
|
|
30
|
+
|
|
25
31
|
module Koala
|
|
26
32
|
|
|
27
33
|
module Facebook
|
data/readme.md
CHANGED
|
@@ -9,28 +9,37 @@ Koala
|
|
|
9
9
|
* Flexible: Koala should be useful to everyone, regardless of their current configuration. (We support JRuby, Rubinius, and REE as well as vanilla Ruby, and use the Faraday library to provide complete flexibility over how HTTP requests are made.)
|
|
10
10
|
* Tested: Koala should have complete test coverage, so you can rely on it. (Our test coverage is complete and can be run against either mocked responses or the live Facebook servers.)
|
|
11
11
|
|
|
12
|
+
Facebook Changes on October 1, 2011
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Koala 1.2 supports all of Facebook's new authentication schemes, which were introduced on October 1, 2011. If you have the appropriate calls to get_user_info_from_cookies (apps using the Javascript SDK) and/or parse_signed_params (for Canvas and tab apps), your application should work without a hitch. For more information, see Facebook's [OAuth 2.0 and HTTPS Migration](https://developers.facebook.com/docs/oauth2-https-migration/) guide.
|
|
16
|
+
|
|
12
17
|
Installation
|
|
13
18
|
---
|
|
14
19
|
|
|
15
20
|
Easy:
|
|
16
21
|
|
|
17
|
-
[sudo|rvm] gem install koala
|
|
18
|
-
[sudo|rvm] gem install koala # for 1.1
|
|
22
|
+
[sudo|rvm] gem install koala
|
|
19
23
|
|
|
20
24
|
Or in Bundler:
|
|
21
25
|
|
|
22
|
-
gem "koala"
|
|
23
|
-
gem "koala" # for 1.1
|
|
26
|
+
gem "koala"
|
|
24
27
|
|
|
25
28
|
Graph API
|
|
26
29
|
----
|
|
27
30
|
The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
@graph = Koala::Facebook::API.new(oauth_access_token)
|
|
33
|
+
# in 1.1 or earlier, use GraphAPI instead of API
|
|
34
|
+
|
|
30
35
|
profile = @graph.get_object("me")
|
|
31
36
|
friends = @graph.get_connections("me", "friends")
|
|
32
37
|
@graph.put_object("me", "feed", :message => "I am writing on my wall!")
|
|
33
38
|
|
|
39
|
+
# you can even use the new Timeline API
|
|
40
|
+
# see https://developers.facebook.com/docs/beta/opengraph/tutorial/
|
|
41
|
+
@graph.put_connections("me", "namespace:action", :object => object_url)
|
|
42
|
+
|
|
34
43
|
The response of most requests is the JSON data returned from the Facebook servers as a Hash.
|
|
35
44
|
|
|
36
45
|
When retrieving data that returns an array of results (for example, when calling API#get_connections or API#search) a GraphCollection object will be returned, which makes it easy to page through the results:
|
|
@@ -61,13 +70,18 @@ Where the Graph API and the old REST API overlap, you should choose the Graph AP
|
|
|
61
70
|
|
|
62
71
|
Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
@rest = Koala::Facebook::API.new(oauth_access_token)
|
|
74
|
+
# in 1.1 or earlier, use RestAPI instead of API
|
|
75
|
+
|
|
65
76
|
@rest.fql_query(my_fql_query) # convenience method
|
|
66
77
|
@rest.fql_multiquery(fql_query_hash) # convenience method
|
|
67
78
|
@rest.rest_call("stream.publish", arguments_hash) # generic version
|
|
68
79
|
|
|
69
80
|
Of course, you can use the Graph API methods on the same object -- the power of two APIs right in the palm of your hand.
|
|
70
81
|
|
|
82
|
+
@api = Koala::Facebook::API.new(oauth_access_token)
|
|
83
|
+
# in 1.1 or earlier, use GraphAndRestAPI instead of API
|
|
84
|
+
|
|
71
85
|
@api = Koala::Facebook::API.new(oauth_access_token)
|
|
72
86
|
fql = @api.fql_query(my_fql_query)
|
|
73
87
|
@api.put_wall_post(process_result(fql))
|
|
@@ -76,25 +90,31 @@ Of course, you can use the Graph API methods on the same object -- the power of
|
|
|
76
90
|
OAuth
|
|
77
91
|
-----
|
|
78
92
|
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:
|
|
93
|
+
|
|
79
94
|
@oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
|
|
80
95
|
|
|
81
96
|
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:
|
|
97
|
+
|
|
82
98
|
@oauth.get_user_from_cookies(cookies) # gets the user's ID
|
|
83
99
|
@oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
|
|
84
100
|
|
|
85
101
|
And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
|
|
102
|
+
|
|
86
103
|
# generate authenticating URL
|
|
87
104
|
@oauth.url_for_oauth_code
|
|
88
105
|
# fetch the access token once you have the code
|
|
89
106
|
@oauth.get_access_token(code)
|
|
90
107
|
|
|
91
108
|
You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
|
|
109
|
+
|
|
92
110
|
@oauth.get_app_access_token
|
|
93
111
|
|
|
94
112
|
For those building apps on Facebook, parsing signed requests is simple:
|
|
95
|
-
|
|
113
|
+
|
|
114
|
+
@oauth.parse_signed_request(signed_request_string)
|
|
96
115
|
|
|
97
116
|
Or, if for some horrible reason, you're still using session keys, despair not! It's easy to turn them into shiny, modern OAuth tokens:
|
|
117
|
+
|
|
98
118
|
@oauth.get_token_from_session_key(session_key)
|
|
99
119
|
@oauth.get_tokens_from_session_keys(array_of_session_keys)
|
|
100
120
|
|
|
@@ -137,12 +157,27 @@ We also support the test users API, allowing you to conjure up fake users and co
|
|
|
137
157
|
# or, if you want to make a whole community:
|
|
138
158
|
@test_users.create_network(network_size, is_app_installed, common_permissions)
|
|
139
159
|
|
|
160
|
+
Talking to Facebook
|
|
161
|
+
-----
|
|
162
|
+
|
|
163
|
+
Koala uses Faraday to make HTTP requests, which means you have complete control over how your app makes HTTP requests to Facebook. You can set Faraday options globally or pass them in on a per-request (or both):
|
|
164
|
+
|
|
165
|
+
# Set an SSL certificate to avoid Net::HTTP errors
|
|
166
|
+
Koala.http_service.http_options = {
|
|
167
|
+
:ssl => { :ca_path => "/etc/ssl/certs" }
|
|
168
|
+
}
|
|
169
|
+
# or on a per-request basis
|
|
170
|
+
@api.get_object(id, args_hash, { :timeout => 10 })
|
|
171
|
+
|
|
172
|
+
The <a href="https://github.com/arsduo/koala/wiki/HTTP-Services">HTTP Services wiki page</a> has more information on what options are available, as well as on how to configure your own Faraday middleware stack (for instance, to implement request logging).
|
|
173
|
+
|
|
140
174
|
See examples, ask questions
|
|
141
175
|
-----
|
|
142
176
|
Some resources to help you as you play with Koala and the Graph API:
|
|
143
177
|
|
|
144
178
|
* Complete Koala documentation <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>
|
|
145
179
|
* 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
|
|
180
|
+
* Facebook's <a href="http://developers.facebook.com/tools/explorer/">Graph API Explorer</a>, where you can play with the Graph API in your browser
|
|
146
181
|
* 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
|
|
147
182
|
|
|
148
183
|
Testing
|
|
@@ -158,5 +193,7 @@ You can also run live tests against Facebook's servers:
|
|
|
158
193
|
|
|
159
194
|
# Again from anywhere in the project directory:
|
|
160
195
|
LIVE=true bundle exec rake spec
|
|
196
|
+
# you can also test against Facebook's beta tier
|
|
197
|
+
LIVE=true BETA=true bundle exec rake spec
|
|
161
198
|
|
|
162
199
|
By default, the live tests are run against test users, so you can run them as frequently as you want. If you want to run them against a real user, however, you can fill in the OAuth token, code, and access\_token values in spec/fixtures/facebook_data.yml. See the wiki for more details.
|