forked from arsduo/koala
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.rb
159 lines (137 loc) · 7.35 KB
/
api.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# graph_batch_api and legacy are required at the bottom, since they depend on API being defined
require 'koala/api/graph_api_methods'
require 'koala/api/graph_collection'
require 'openssl'
module Koala
module Facebook
class API
# Creates a new API client.
# @param [String] access_token access token
# @param [String] app_secret app secret, for tying your access tokens to your app secret
# If you provide an app secret, your requests will be
# signed by default, unless you pass appsecret_proof:
# false as an option to the API call. (See
# https://developers.facebook.com/docs/graph-api/securing-requests/)
# @param [Block] rate_limit_hook block called with limits received in facebook response headers
# @note If no access token is provided, you can only access some public information.
# @return [Koala::Facebook::API] the API client
def initialize(access_token = Koala.config.access_token, app_secret = Koala.config.app_secret, rate_limit_hook = Koala.config.rate_limit_hook)
@access_token = access_token
@app_secret = app_secret
@rate_limit_hook = rate_limit_hook
end
attr_reader :access_token, :app_secret, :rate_limit_hook
include GraphAPIMethods
# Make a call directly to the Graph API.
# (See any of the other methods for example invocations.)
#
# @param path the Graph API path to query (no leading / needed)
# @param args (see #get_object)
# @param verb the type of HTTP request to make (get, post, delete, etc.)
# @options (see #get_object)
#
# @yield response when making a batch API call, you can pass in a block
# that parses the results, allowing for cleaner code.
# The block's return value is returned in the batch results.
# See the code for {#get_picture} for examples.
# (Not needed in regular calls; you'll probably rarely use this.)
#
# @raise [Koala::Facebook::APIError] if Facebook returns an error
#
# @return the result from Facebook
def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
# enable appsecret_proof by default
options = {:appsecret_proof => true}.merge(options) if @app_secret
response = api(path, args, verb, options)
error = GraphErrorChecker.new(response.status, response.body, response.headers).error_if_appropriate
raise error if error
# if we want a component other than the body (e.g. redirect header for images), provide that
http_component = options[:http_component]
desired_data = if options[:http_component]
http_component == :response ? response : response.send(http_component)
else
# turn this into a GraphCollection if it's pageable
API::GraphCollection.evaluate(response, self)
end
if rate_limit_hook
limits = %w(x-business-use-case-usage x-ad-account-usage x-app-usage).each_with_object({}) do |key, hash|
value = response.headers.fetch(key, nil)
next unless value
hash[key] = JSON.parse(response.headers[key])
rescue JSON::ParserError => e
Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{key} = #{value}")
end
rate_limit_hook.call(limits) if limits.keys.any?
end
# now process as appropriate for the given call (get picture header, etc.)
post_processing ? post_processing.call(desired_data) : desired_data
end
# Makes a request to the appropriate Facebook API.
# @note You'll rarely need to call this method directly.
#
# @see GraphAPIMethods#graph_call
#
# @param path the server path for this request (leading / is prepended if not present)
# @param args arguments to be sent to Facebook
# @param verb the HTTP method to use
# @param options request-related options for Koala and Faraday.
# See https://github.com/arsduo/koala/wiki/HTTP-Services for additional options.
# @option options [Symbol] :http_component which part of the response (headers, body, or status) to return
# @option options [Symbol] :format which request format to use. Currently, :json is supported
# @option options [Symbol] :preserve_form_arguments preserve arrays in arguments, which are
# expected by certain FB APIs (see the ads API in particular,
# https://developers.facebook.com/docs/marketing-api/adgroup/v2.4)
# @option options [Boolean] :beta use Facebook's beta tier
# @option options [Boolean] :use_ssl force SSL for this request, even if it's tokenless.
# (All API requests with access tokens use SSL.)
# @raise [Koala::Facebook::ServerError] if Facebook returns an error (response status >= 500)
#
# @return a Koala::HTTPService::Response object representing the returned Facebook data
def api(path, args = {}, verb = "get", options = {})
# we make a copy of args so the modifications (added access_token & appsecret_proof)
# do not affect the received argument
args = args.dup
# If a access token is explicitly provided, use that
# This is explicitly needed in batch requests so GraphCollection
# results preserve any specific access tokens provided
args["access_token"] ||= @access_token || @app_access_token if @access_token || @app_access_token
if options.delete(:appsecret_proof) && args["access_token"] && @app_secret
args["appsecret_proof"] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), @app_secret, args["access_token"])
end
# Translate any arrays in the params into comma-separated strings
args = sanitize_request_parameters(args) unless preserve_form_arguments?(options)
# add a leading / if needed...
path = "/#{path}" unless path.to_s =~ /^\//
# make the request via the provided service
result = Koala.make_request(path, args, verb, options)
if result.status.to_i >= 500
raise Koala::Facebook::ServerError.new(result.status.to_i, result.body)
end
result
end
private
# Sanitizes Ruby objects into Facebook-compatible string values.
#
# @param parameters a hash of parameters.
#
# Returns a hash in which values that are arrays of non-enumerable values
# (Strings, Symbols, Numbers, etc.) are turned into comma-separated strings.
def sanitize_request_parameters(parameters)
parameters.reduce({}) do |result, (key, value)|
# if the parameter is an array that contains non-enumerable values,
# turn it into a comma-separated list
# in Ruby 1.8.7, strings are enumerable, but we don't care
if value.is_a?(Array) && value.none? {|entry| entry.is_a?(Enumerable) && !entry.is_a?(String)}
value = value.join(",")
end
result.merge(key => value)
end
end
def preserve_form_arguments?(options)
options[:format] == :json || options[:preserve_form_arguments] || Koala.config.preserve_form_arguments
end
def check_response(http_status, body, headers)
end
end
end
end