tyler_koala 1.2.0beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +185 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +22 -0
  7. data/Manifest +39 -0
  8. data/Rakefile +16 -0
  9. data/autotest/discover.rb +1 -0
  10. data/koala.gemspec +50 -0
  11. data/lib/koala.rb +119 -0
  12. data/lib/koala/batch_operation.rb +74 -0
  13. data/lib/koala/graph_api.rb +281 -0
  14. data/lib/koala/graph_batch_api.rb +87 -0
  15. data/lib/koala/graph_collection.rb +54 -0
  16. data/lib/koala/http_service.rb +161 -0
  17. data/lib/koala/oauth.rb +181 -0
  18. data/lib/koala/realtime_updates.rb +89 -0
  19. data/lib/koala/rest_api.rb +95 -0
  20. data/lib/koala/test_users.rb +102 -0
  21. data/lib/koala/uploadable_io.rb +180 -0
  22. data/lib/koala/utils.rb +7 -0
  23. data/readme.md +160 -0
  24. data/spec/cases/api_base_spec.rb +101 -0
  25. data/spec/cases/error_spec.rb +30 -0
  26. data/spec/cases/graph_and_rest_api_spec.rb +48 -0
  27. data/spec/cases/graph_api_batch_spec.rb +600 -0
  28. data/spec/cases/graph_api_spec.rb +42 -0
  29. data/spec/cases/http_service_spec.rb +420 -0
  30. data/spec/cases/koala_spec.rb +21 -0
  31. data/spec/cases/oauth_spec.rb +428 -0
  32. data/spec/cases/realtime_updates_spec.rb +198 -0
  33. data/spec/cases/rest_api_spec.rb +41 -0
  34. data/spec/cases/test_users_spec.rb +281 -0
  35. data/spec/cases/uploadable_io_spec.rb +206 -0
  36. data/spec/cases/utils_spec.rb +8 -0
  37. data/spec/fixtures/beach.jpg +0 -0
  38. data/spec/fixtures/cat.m4v +0 -0
  39. data/spec/fixtures/facebook_data.yml +61 -0
  40. data/spec/fixtures/mock_facebook_responses.yml +439 -0
  41. data/spec/spec_helper.rb +43 -0
  42. data/spec/support/graph_api_shared_examples.rb +502 -0
  43. data/spec/support/json_testing_fix.rb +42 -0
  44. data/spec/support/koala_test.rb +163 -0
  45. data/spec/support/mock_http_service.rb +98 -0
  46. data/spec/support/ordered_hash.rb +205 -0
  47. data/spec/support/rest_api_shared_examples.rb +285 -0
  48. data/spec/support/uploadable_io_shared_examples.rb +70 -0
  49. metadata +221 -0
@@ -0,0 +1,161 @@
1
+ require 'faraday'
2
+ require 'faraday_stack'
3
+
4
+ module Koala
5
+ class Response
6
+ attr_reader :status, :body, :headers
7
+ def initialize(status, body, headers)
8
+ @status = status
9
+ @body = body
10
+ @headers = headers
11
+ end
12
+ end
13
+
14
+ module HTTPService
15
+ # common functionality for all HTTP services
16
+
17
+ class << self
18
+ attr_accessor :faraday_middleware, :faraday_options
19
+ end
20
+
21
+ @faraday_options ||= {}
22
+
23
+ DEFAULT_MIDDLEWARE = Proc.new do |builder|
24
+ builder.request :multipart
25
+ builder.request :url_encoded
26
+ builder.adapter Faraday.default_adapter
27
+ end
28
+
29
+ def self.server(options = {})
30
+ server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
31
+ server.gsub!(/\.facebook/, "-video.facebook") if options[:video]
32
+ "#{options[:use_ssl] ? "https" : "http"}://#{options[:beta] ? "beta." : ""}#{server}"
33
+ end
34
+
35
+ def self.make_request(path, args, verb, options = {})
36
+ # if the verb isn't get or post, send it as a post argument
37
+ args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
38
+
39
+ # turn all the keys to strings (Faraday has issues with symbols under 1.8.7) and resolve UploadableIOs
40
+ params = args.inject({}) {|hash, kv| hash[kv.first.to_s] = kv.last.is_a?(UploadableIO) ? kv.last.to_upload_io : kv.last; hash}
41
+
42
+ # figure out our options for this request
43
+ http_options = {:params => (verb == "get" ? params : {})}.merge(faraday_options || {}).merge(process_options(options))
44
+ http_options[:use_ssl] = true if args["access_token"] # require http if there's a token
45
+
46
+ # set up our Faraday connection
47
+ # we have to manually assign params to the URL or the
48
+ conn = Faraday.new(server(http_options), http_options, &(faraday_middleware || DEFAULT_MIDDLEWARE))
49
+
50
+ response = conn.send(verb, path, (verb == "post" ? params : {}))
51
+ Koala::Response.new(response.status.to_i, response.body, response.headers)
52
+ end
53
+
54
+ def self.encode_params(param_hash)
55
+ # unfortunately, we can't use to_query because that's Rails, not Ruby
56
+ # if no hash (e.g. no auth token) return empty string
57
+ # this is used mainly by the Batch API nowadays
58
+ ((param_hash || {}).collect do |key_and_value|
59
+ key_and_value[1] = MultiJson.encode(key_and_value[1]) unless key_and_value[1].is_a? String
60
+ "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
61
+ end).join("&")
62
+ end
63
+
64
+ # deprecations
65
+ # not elegant or compact code, but temporary
66
+
67
+ def self.always_use_ssl
68
+ Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.faraday_options[:use_ssl]; always_use_ssl will be removed in a future version.")
69
+ faraday_options[:use_ssl]
70
+ end
71
+
72
+ def self.always_use_ssl=(value)
73
+ Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.faraday_options[:use_ssl]; always_use_ssl will be removed in a future version.")
74
+ faraday_options[:use_ssl] = value
75
+ end
76
+
77
+ def self.timeout
78
+ Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.faraday_options[:timeout]; .timeout will be removed in a future version.")
79
+ faraday_options[:timeout]
80
+ end
81
+
82
+ def self.timeout=(value)
83
+ Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.faraday_options[:timeout]; .timeout will be removed in a future version.")
84
+ faraday_options[:timeout] = value
85
+ end
86
+
87
+ def self.timeout
88
+ Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.faraday_options[:timeout]; .timeout will be removed in a future version.")
89
+ faraday_options[:timeout]
90
+ end
91
+
92
+ def self.timeout=(value)
93
+ Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.faraday_options[:timeout]; .timeout will be removed in a future version.")
94
+ faraday_options[:timeout] = value
95
+ end
96
+
97
+ def self.proxy
98
+ Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.faraday_options[:proxy]; .proxy will be removed in a future version.")
99
+ faraday_options[:proxy]
100
+ end
101
+
102
+ def self.proxy=(value)
103
+ Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.faraday_options[:proxy]; .proxy will be removed in a future version.")
104
+ faraday_options[:proxy] = value
105
+ end
106
+
107
+ def self.ca_path
108
+ Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.faraday_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
109
+ (faraday_options[:ssl] || {})[:ca_path]
110
+ end
111
+
112
+ def self.ca_path=(value)
113
+ Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.faraday_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
114
+ (faraday_options[:ssl] ||= {})[:ca_path] = value
115
+ end
116
+
117
+ def self.ca_file
118
+ Koala::Utils.deprecate("HTTPService.ca_file is now (HTTPService.faraday_options[:ssl] ||= {})[:ca_file]; .ca_file will be removed in a future version.")
119
+ (faraday_options[:ssl] || {})[:ca_file]
120
+ end
121
+
122
+ def self.ca_file=(value)
123
+ Koala::Utils.deprecate("HTTPService.ca_file is now (HTTPService.faraday_options[:ssl] ||= {})[:ca_file]; .ca_file will be removed in a future version.")
124
+ (faraday_options[:ssl] ||= {})[:ca_file] = value
125
+ end
126
+
127
+ def self.verify_mode
128
+ Koala::Utils.deprecate("HTTPService.verify_mode is now (HTTPService.faraday_options[:ssl] ||= {})[:verify_mode]; .verify_mode will be removed in a future version.")
129
+ (faraday_options[:ssl] || {})[:verify_mode]
130
+ end
131
+
132
+ def self.verify_mode=(value)
133
+ Koala::Utils.deprecate("HTTPService.verify_mode is now (HTTPService.faraday_options[:ssl] ||= {})[:verify_mode]; .verify_mode will be removed in a future version.")
134
+ (faraday_options[:ssl] ||= {})[:verify_mode] = value
135
+ end
136
+
137
+ def self.process_options(options)
138
+ if typhoeus_options = options.delete(:typhoeus_options)
139
+ Koala::Utils.deprecate("typhoeus_options should now be included directly in the http_options hash. Support for this key will be removed in a future version.")
140
+ options = options.merge(typhoeus_options)
141
+ end
142
+
143
+ if ca_file = options.delete(:ca_file)
144
+ Koala::Utils.deprecate("http_options[:ca_file] should now be passed inside (http_options[:ssl] = {}) -- that is, http_options[:ssl][:ca_file]. Support for this key will be removed in a future version.")
145
+ (options[:ssl] ||= {})[:ca_file] = ca_file
146
+ end
147
+
148
+ if ca_path = options.delete(:ca_path)
149
+ Koala::Utils.deprecate("http_options[:ca_path] should now be passed inside (http_options[:ssl] = {}) -- that is, http_options[:ssl][:ca_path]. Support for this key will be removed in a future version.")
150
+ (options[:ssl] ||= {})[:ca_path] = ca_path
151
+ end
152
+
153
+ if verify_mode = options.delete(:verify_mode)
154
+ Koala::Utils.deprecate("http_options[:verify_mode] should now be passed inside (http_options[:ssl] = {}) -- that is, http_options[:ssl][:verify_mode]. Support for this key will be removed in a future version.")
155
+ (options[:ssl] ||= {})[:verify_mode] = verify_mode
156
+ end
157
+
158
+ options
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,181 @@
1
+ module Koala
2
+ module Facebook
3
+ class OAuth
4
+ attr_reader :app_id, :app_secret, :oauth_callback_url
5
+ def initialize(app_id, app_secret, oauth_callback_url = nil)
6
+ @app_id = app_id
7
+ @app_secret = app_secret
8
+ @oauth_callback_url = oauth_callback_url
9
+ end
10
+
11
+ def get_user_info_from_cookie(cookie_hash)
12
+ # Parses the cookie set by the official Facebook JavaScript SDK.
13
+ #
14
+ # cookies should be a Hash, like the one Rails provides
15
+ #
16
+ # If the user is logged in via Facebook, we return a dictionary with the
17
+ # keys "uid" and "access_token". The former is the user's Facebook ID,
18
+ # and the latter can be used to make authenticated requests to the Graph API.
19
+ # If the user is not logged in, we return None.
20
+ #
21
+ # Download the official Facebook JavaScript SDK at
22
+ # http://github.com/facebook/connect-js/. Read more about Facebook
23
+ # authentication at http://developers.facebook.com/docs/authentication/.
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
37
+ end
38
+ end
39
+ alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
40
+
41
+ def get_user_from_cookie(cookies)
42
+ if info = get_user_info_from_cookies(cookies)
43
+ string = info["uid"]
44
+ end
45
+ end
46
+ alias_method :get_user_from_cookies, :get_user_from_cookie
47
+
48
+ # URLs
49
+
50
+ def url_for_oauth_code(options = {})
51
+ # for permissions, see http://developers.facebook.com/docs/authentication/permissions
52
+ permissions = options[:permissions]
53
+ scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
54
+ display = options.has_key?(:display) ? "&display=#{options[:display]}" : ""
55
+
56
+ callback = options[:callback] || @oauth_callback_url
57
+ raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
58
+
59
+ # Creates the URL for oauth authorization for a given callback and optional set of permissions
60
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}#{display}"
61
+ end
62
+
63
+ def url_for_access_token(code, options = {})
64
+ # Creates the URL for the token corresponding to a given code generated by Facebook
65
+ callback = options[:callback] || @oauth_callback_url
66
+ raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
67
+ "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
68
+ end
69
+
70
+ def get_access_token_info(code, options = {})
71
+ # convenience method to get a parsed token from Facebook for a given code
72
+ # should this require an OAuth callback URL?
73
+ get_token_from_server({:code => code, :redirect_uri => @oauth_callback_url}, false, options)
74
+ end
75
+
76
+ def get_access_token(code, options = {})
77
+ # upstream methods will throw errors if needed
78
+ if info = get_access_token_info(code, options)
79
+ string = info["access_token"]
80
+ end
81
+ end
82
+
83
+ def get_app_access_token_info(options = {})
84
+ # convenience method to get a the application's sessionless access token
85
+ get_token_from_server({:type => 'client_cred'}, true, options)
86
+ end
87
+
88
+ def get_app_access_token(options = {})
89
+ if info = get_app_access_token_info(options)
90
+ string = info["access_token"]
91
+ end
92
+ end
93
+
94
+ # Originally provided directly by Facebook, however this has changed
95
+ # as their concept of crypto changed. For historic purposes, this is their proposal:
96
+ # https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
97
+ # Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
98
+ # for a more accurate reference implementation strategy.
99
+ def parse_signed_request(input)
100
+ encoded_sig, encoded_envelope = input.split('.', 2)
101
+ signature = base64_url_decode(encoded_sig).unpack("H*").first
102
+ envelope = MultiJson.decode(base64_url_decode(encoded_envelope))
103
+
104
+ raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
105
+
106
+ # now see if the signature is valid (digest, key, data)
107
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope.tr("-_", "+/"))
108
+ raise 'SignedRequest: Invalid signature' if (signature != hmac)
109
+
110
+ return envelope
111
+ end
112
+
113
+ # from session keys
114
+ def get_token_info_from_session_keys(sessions, options = {})
115
+ # fetch the OAuth tokens from Facebook
116
+ response = fetch_token_string({
117
+ :type => 'client_cred',
118
+ :sessions => sessions.join(",")
119
+ }, true, "exchange_sessions", options)
120
+
121
+ # Facebook returns an empty body in certain error conditions
122
+ if response == ""
123
+ raise APIError.new({
124
+ "type" => "ArgumentError",
125
+ "message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
126
+ })
127
+ end
128
+
129
+ MultiJson.decode(response)
130
+ end
131
+
132
+ def get_tokens_from_session_keys(sessions, options = {})
133
+ # get the original hash results
134
+ results = get_token_info_from_session_keys(sessions, options)
135
+ # now recollect them as just the access tokens
136
+ results.collect { |r| r ? r["access_token"] : nil }
137
+ end
138
+
139
+ def get_token_from_session_key(session, options = {})
140
+ # convenience method for a single key
141
+ # gets the overlaoded strings automatically
142
+ get_tokens_from_session_keys([session], options)[0]
143
+ end
144
+
145
+ protected
146
+
147
+ def get_token_from_server(args, post = false, options = {})
148
+ # fetch the result from Facebook's servers
149
+ result = fetch_token_string(args, post, "access_token", options)
150
+
151
+ # if we have an error, parse the error JSON and raise an error
152
+ raise APIError.new((MultiJson.decode(result)["error"] rescue nil) || {}) if result =~ /error/
153
+
154
+ # otherwise, parse the access token
155
+ parse_access_token(result)
156
+ end
157
+
158
+ def parse_access_token(response_text)
159
+ components = response_text.split("&").inject({}) do |hash, bit|
160
+ key, value = bit.split("=")
161
+ hash.merge!(key => value)
162
+ end
163
+ components
164
+ end
165
+
166
+ def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
167
+ Koala.make_request("/oauth/#{endpoint}", {
168
+ :client_id => @app_id,
169
+ :client_secret => @app_secret
170
+ }.merge!(args), post ? "post" : "get", {:use_ssl => true}.merge!(options)).body
171
+ end
172
+
173
+ # base 64
174
+ # directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
175
+ def base64_url_decode(str)
176
+ str += '=' * (4 - str.length.modulo(4))
177
+ Base64.decode64(str.tr('-_', '+/'))
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,89 @@
1
+ module Koala
2
+ module Facebook
3
+ module RealtimeUpdateMethods
4
+ # note: to subscribe to real-time updates, you must have an application access token
5
+
6
+ def self.included(base)
7
+ # make the attributes readable
8
+ base.class_eval do
9
+ attr_reader :api, :app_id, :app_access_token, :secret
10
+
11
+ # parses the challenge params and makes sure the call is legitimate
12
+ # returns the challenge string to be sent back to facebook if true
13
+ # returns false otherwise
14
+ # this is a class method, since you don't need to know anything about the app
15
+ # saves a potential trip fetching the app access token
16
+ def self.meet_challenge(params, verify_token = nil, &verification_block)
17
+ if params["hub.mode"] == "subscribe" &&
18
+ # you can make sure this is legitimate through two ways
19
+ # if your store the token across the calls, you can pass in the token value
20
+ # and we'll make sure it matches
21
+ (verify_token && params["hub.verify_token"] == verify_token) ||
22
+ # alternately, if you sent a specially-constructed value (such as a hash of various secret values)
23
+ # you can pass in a block, which we'll call with the verify_token sent by Facebook
24
+ # if it's legit, return anything that evaluates to true; otherwise, return nil or false
25
+ (verification_block && yield(params["hub.verify_token"]))
26
+ params["hub.challenge"]
27
+ else
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def initialize(options = {})
35
+ @app_id = options[:app_id]
36
+ @app_access_token = options[:app_access_token]
37
+ @secret = options[:secret]
38
+ unless @app_id && (@app_access_token || @secret) # make sure we have what we need
39
+ raise ArgumentError, "Initialize must receive a hash with :app_id and either :app_access_token or :secret! (received #{options.inspect})"
40
+ end
41
+
42
+ # fetch the access token if we're provided a secret
43
+ if @secret && !@app_access_token
44
+ oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
45
+ @app_access_token = oauth.get_app_access_token
46
+ end
47
+
48
+ @graph_api = API.new(@app_access_token)
49
+ end
50
+
51
+ # subscribes for realtime updates
52
+ # your callback_url must be set up to handle the verification request or the subscription will not be set up
53
+ # http://developers.facebook.com/docs/api/realtime
54
+ def subscribe(object, fields, callback_url, verify_token)
55
+ args = {
56
+ :object => object,
57
+ :fields => fields,
58
+ :callback_url => callback_url,
59
+ :verify_token => verify_token
60
+ }
61
+ # a subscription is a success if Facebook returns a 200 (after hitting your server for verification)
62
+ @graph_api.graph_call(subscription_path, args, 'post', :http_component => :status) == 200
63
+ end
64
+
65
+ # removes subscription for object
66
+ # if object is nil, it will remove all subscriptions
67
+ def unsubscribe(object = nil)
68
+ args = {}
69
+ args[:object] = object if object
70
+ @graph_api.graph_call(subscription_path, args, 'delete', :http_component => :status) == 200
71
+ end
72
+
73
+ def list_subscriptions
74
+ @graph_api.graph_call(subscription_path)["data"]
75
+ end
76
+
77
+ def graph_api
78
+ Koala::Utils.deprecate("the TestUsers.graph_api accessor is deprecated and will be removed in a future version; please use .api instead.")
79
+ @api
80
+ end
81
+
82
+ protected
83
+
84
+ def subscription_path
85
+ @subscription_path ||= "#{@app_id}/subscriptions"
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,95 @@
1
+ module Koala
2
+ module Facebook
3
+ REST_SERVER = "api.facebook.com"
4
+
5
+ module RestAPIMethods
6
+ def fql_query(fql, args = {}, options = {})
7
+ rest_call('fql.query', args.merge(:query => fql), options)
8
+ end
9
+
10
+ def fql_multiquery(queries = {}, args = {}, options = {})
11
+ if results = rest_call('fql.multiquery', args.merge(:queries => MultiJson.encode(queries)), options)
12
+ # simplify the multiquery result format
13
+ results.inject({}) {|outcome, data| outcome[data["name"]] = data["fql_result_set"]; outcome}
14
+ end
15
+ end
16
+
17
+ def rest_call(fb_method, args = {}, options = {}, method = "get")
18
+ options = options.merge!(:rest_api => true, :read_only => READ_ONLY_METHODS.include?(fb_method.to_s))
19
+
20
+ api("method/#{fb_method}", args.merge('format' => 'json'), method, options) do |response|
21
+ # check for REST API-specific errors
22
+ if response.is_a?(Hash) && response["error_code"]
23
+ raise APIError.new("type" => response["error_code"], "message" => response["error_msg"])
24
+ end
25
+ end
26
+ end
27
+
28
+ # read-only methods for which we can use API-read
29
+ # taken directly from the FB PHP library (https://github.com/facebook/php-sdk/blob/master/src/facebook.php)
30
+ READ_ONLY_METHODS = [
31
+ 'admin.getallocation',
32
+ 'admin.getappproperties',
33
+ 'admin.getbannedusers',
34
+ 'admin.getlivestreamvialink',
35
+ 'admin.getmetrics',
36
+ 'admin.getrestrictioninfo',
37
+ 'application.getpublicinfo',
38
+ 'auth.getapppublickey',
39
+ 'auth.getsession',
40
+ 'auth.getsignedpublicsessiondata',
41
+ 'comments.get',
42
+ 'connect.getunconnectedfriendscount',
43
+ 'dashboard.getactivity',
44
+ 'dashboard.getcount',
45
+ 'dashboard.getglobalnews',
46
+ 'dashboard.getnews',
47
+ 'dashboard.multigetcount',
48
+ 'dashboard.multigetnews',
49
+ 'data.getcookies',
50
+ 'events.get',
51
+ 'events.getmembers',
52
+ 'fbml.getcustomtags',
53
+ 'feed.getappfriendstories',
54
+ 'feed.getregisteredtemplatebundlebyid',
55
+ 'feed.getregisteredtemplatebundles',
56
+ 'fql.multiquery',
57
+ 'fql.query',
58
+ 'friends.arefriends',
59
+ 'friends.get',
60
+ 'friends.getappusers',
61
+ 'friends.getlists',
62
+ 'friends.getmutualfriends',
63
+ 'gifts.get',
64
+ 'groups.get',
65
+ 'groups.getmembers',
66
+ 'intl.gettranslations',
67
+ 'links.get',
68
+ 'notes.get',
69
+ 'notifications.get',
70
+ 'pages.getinfo',
71
+ 'pages.isadmin',
72
+ 'pages.isappadded',
73
+ 'pages.isfan',
74
+ 'permissions.checkavailableapiaccess',
75
+ 'permissions.checkgrantedapiaccess',
76
+ 'photos.get',
77
+ 'photos.getalbums',
78
+ 'photos.gettags',
79
+ 'profile.getinfo',
80
+ 'profile.getinfooptions',
81
+ 'stream.get',
82
+ 'stream.getcomments',
83
+ 'stream.getfilters',
84
+ 'users.getinfo',
85
+ 'users.getloggedinuser',
86
+ 'users.getstandardinfo',
87
+ 'users.hasapppermission',
88
+ 'users.isappuser',
89
+ 'users.isverified',
90
+ 'video.getuploadlimits'
91
+ ]
92
+ end
93
+
94
+ end # module Facebook
95
+ end # module Koala