koala 3.0.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6941f9efa1f2d1a275581f20b5d10977f3d73fdd
4
- data.tar.gz: 726f62799f0c20c9266515dc6d68bc333e9995d0
2
+ SHA256:
3
+ metadata.gz: 27591b64021ee915c13d9bcea2763739c2c86ce04d961dd5b540d964515a9905
4
+ data.tar.gz: 4a8c184674f681b193a3686b4eeb0023da0c00765d60be9694af9098014dacfd
5
5
  SHA512:
6
- metadata.gz: 10d6e349ebe3e89487f9052840d12d27b52b5fce77cb649fb97e2554855bf39c12acf60da93c3c5e55bf2f0ea66b9533310c50cad23006da331b4e253dfa4e77
7
- data.tar.gz: 67c74e5c4af1c273d4c1e7edae915897753b610357763bf398b8c8b6107853dbbdaf86ac174951e2efddadd9aa5a611cb343c0655388e7a75b407ba80823a1c4
6
+ metadata.gz: 78e4a3d670c3524211d438b8b67713f3f27cc0638aee6660b544b8b3b83a99397546a96ae826c6d613a1b6799184894c816b95008dc6083689f866357ae7ae13
7
+ data.tar.gz: 7a135a41f63d84e7e4f6c68632574eb19140aaf32ea9d9220bb2aaa4c79c50097c7ee1c957f7e9740d6b2c1141b5d24ebffdb47edd510c28bd7590f3cc09bf4f
@@ -0,0 +1,32 @@
1
+ name: Test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: on ruby ${{matrix.ruby}}
8
+ runs-on: ubuntu-latest
9
+
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ ruby: [2.7, "3.0", 3.1, 3.2, 3.3, head]
14
+
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{matrix.ruby}}
23
+
24
+ - name: Install dependencies
25
+ run: bundle install --jobs 4 --retry 3
26
+
27
+ - name: Specs & Coverage
28
+ uses: paambaati/codeclimate-action@v6
29
+ env:
30
+ CC_TEST_REPORTER_ID: 7af99d9225b4c14640f9ec3cb2e24d2f7103ac49417b0bd989188fb6c25f2909
31
+ with:
32
+ coverageCommand: bundle exec rspec
data/Gemfile CHANGED
@@ -7,15 +7,17 @@ group :development do
7
7
  end
8
8
 
9
9
  group :development, :test do
10
+ gem "psych", '< 4.0.0' # safe_load signature not compatible with older rubies
10
11
  gem "rake"
11
12
  gem "typhoeus" unless defined? JRUBY_VERSION
13
+ gem 'faraday-typhoeus' unless defined? JRUBY_VERSION
12
14
  end
13
15
 
14
16
  group :test do
15
- gem "rspec", '~> 3.4'
16
- gem "vcr"
17
+ gem "rspec", "~> 3.0", "< 3.10" # resrict rspec version until https://github.com/rspec/rspec-support/pull/537 gets merged
18
+ gem "vcr", github: 'vcr/vcr', ref: '8ced6c96e01737a418cd270e0382a8c2c6d85f7f' # needs https://github.com/vcr/vcr/pull/907 for ruby 3.1
17
19
  gem "webmock"
18
- gem "codeclimate-test-reporter", "~> 1.0.0", require: nil
20
+ gem "simplecov"
19
21
  end
20
22
 
21
23
  gem "jruby-openssl" if defined? JRUBY_VERSION
data/changelog.md CHANGED
@@ -1,3 +1,101 @@
1
+ Unreleased
2
+ ==========
3
+
4
+ **Key breaking changes:**
5
+
6
+ New features:
7
+
8
+ Updated features:
9
+
10
+ Removed features:
11
+
12
+ Internal improvements:
13
+
14
+ Testing improvements:
15
+
16
+ Others:
17
+
18
+ v3.6.0 (2024-06-27)
19
+ ==========
20
+
21
+ Updated features:
22
+
23
+ * Add fbtrace_id, x-fb-rev, x-fb-debug to error messages and error class ([#668](https://github.com/arsduo/koala/pull/686))
24
+ * Handles the invalid JSON response from Facebook when the request's http_options[:http_component] is set to ':response' ([#689](https://github.com/arsduo/koala/pull/689))
25
+
26
+ Internal improvements:
27
+
28
+ * Require base64 for ruby 3.4 support ([#688](https://github.com/arsduo/koala/pull/688))
29
+
30
+ Testing improvements:
31
+
32
+ * Fix CI for ruby 3.4 ([#688](https://github.com/arsduo/koala/pull/688))
33
+ * Add latest rubies to CI ([#687](https://github.com/arsduo/koala/pull/687))
34
+ * Bump GHA action plugins to avoid deprecation warnings ([#689](https://github.com/arsduo/koala/pull/689))
35
+
36
+ v3.5.0 (2023-08-23)
37
+ ======
38
+
39
+ Internal improvements:
40
+ * Raise ClientError instead of NoMethodError when body is 'null' ([#673](https://github.com/arsduo/koala/issues/673))
41
+
42
+ v3.4.0 (2023-01-05)
43
+ ======
44
+
45
+ Updated features:
46
+
47
+ * Force use by default of HTTPS (instead of HTTP) when there is no access token.
48
+ HTTP can still be used by passing :use_ssl => false in the options hash for an api call ([#678](https://github.com/arsduo/koala/pull/678/files))
49
+
50
+ v3.3.0 (2022-09-27)
51
+ ======
52
+
53
+ Updated features:
54
+
55
+ * Removed restriction on faraday < 2 ([#666](https://github.com/arsduo/koala/pull/666))
56
+
57
+ Internal improvements:
58
+
59
+ * Remove multipart hack and use default faraday multipart middleware ([#664](https://github.com/arsduo/koala/pull/664))
60
+
61
+ Testing improvements:
62
+
63
+ * Fix tests with ruby-head ([#674](https://github.com/arsduo/koala/pull/674))
64
+ * Keep supported rubies (non EOL) for CI ([#675](https://github.com/arsduo/koala/pull/675))
65
+
66
+ v3.2.0 (2022-05-27)
67
+ ======
68
+
69
+ New features:
70
+
71
+ * Exposes limiting headers(`x-business-use-case-usage, x-ad-account-usage, x-app-usage`) to APIError ([#668](https://github.com/arsduo/koala/pull/668))
72
+ * Add `rate_limit_hook` configuration to get rate limiting headers (`x-business-use-case-usage, x-ad-account-usage, x-app-usage`) ([#670](https://github.com/arsduo/koala/pull/670))
73
+
74
+ Testing improvements:
75
+
76
+ * Fix builds for ruby 3.x
77
+
78
+ v3.1.0 (2022-01-18)
79
+ ======
80
+
81
+ New features:
82
+
83
+ * mask_tokens config (default: true) to mask tokens in logs
84
+
85
+ Updated features:
86
+
87
+ * Log before and after sending request
88
+
89
+ Internal improvements:
90
+
91
+ * Lock Faraday to < 2
92
+ * Compatibility with ruby 3.x
93
+
94
+ Testing improvements:
95
+
96
+ * Use Github actions for CI
97
+ * Run CI on latest rubies
98
+
1
99
  v3.0.0 (2017-03-17)
2
100
  ======
3
101
 
data/koala.gemspec CHANGED
@@ -24,6 +24,9 @@ Gem::Specification.new do |gem|
24
24
  gem.required_ruby_version = '>= 2.1'
25
25
 
26
26
  gem.add_runtime_dependency("faraday")
27
+ gem.add_runtime_dependency("faraday-multipart")
27
28
  gem.add_runtime_dependency("addressable")
28
29
  gem.add_runtime_dependency("json", ">= 1.8")
30
+ gem.add_runtime_dependency("rexml")
31
+ gem.add_runtime_dependency("base64")
29
32
  end
@@ -53,7 +53,16 @@ module Koala
53
53
  end
54
54
 
55
55
  original_api.graph_call("/", args, "post", http_options) do |response|
56
- raise bad_response if response.nil?
56
+ raise bad_response('Facebook returned an empty body') if response.nil?
57
+
58
+ # when http_component is set we receive Koala::Http_service response object
59
+ # from graph_call so this needs to be parsed
60
+ # as generate_results method handles only JSON response
61
+ if http_options[:http_component] && http_options[:http_component] == :response
62
+ response = json_body(response.body)
63
+
64
+ raise bad_response('Facebook returned an invalid body') unless response.is_a?(Array)
65
+ end
57
66
 
58
67
  batch_results += generate_results(response, batch)
59
68
  end
@@ -81,9 +90,9 @@ module Koala
81
90
  end
82
91
  end
83
92
 
84
- def bad_response
93
+ def bad_response(message)
85
94
  # Facebook sometimes reportedly returns an empty body at times
86
- BadFacebookResponse.new(200, "", "Facebook returned an empty body")
95
+ BadFacebookResponse.new(200, '', message)
87
96
  end
88
97
 
89
98
  def result_from_response(response, options)
@@ -123,14 +132,17 @@ module Koala
123
132
  JSON.dump calls
124
133
  end
125
134
 
126
- def json_body(response)
127
- # quirks_mode is needed because Facebook sometimes returns a raw true or false value --
128
- # in Ruby 2.4 we can drop that.
129
- JSON.parse(response.fetch("body"), quirks_mode: true)
135
+ def json_body(body)
136
+ return if body.nil?
137
+
138
+ JSON.parse(body)
139
+ rescue JSON::ParserError => e
140
+ Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{body}")
141
+ nil
130
142
  end
131
143
 
132
144
  def desired_component(component:, response:, headers:)
133
- result = Koala::HTTPService::Response.new(response['status'], response['body'], headers)
145
+ result = Koala::HTTPService::Response.new(response['code'], response['body'], headers)
134
146
 
135
147
  # Get the HTTP component they want
136
148
  case component
@@ -138,6 +150,7 @@ module Koala
138
150
  # facebook returns the headers as an array of k/v pairs, but we want a regular hash
139
151
  when :headers then headers
140
152
  # (see note in regular api method about JSON parsing)
153
+ when :response then result
141
154
  else GraphCollection.evaluate(result, original_api)
142
155
  end
143
156
  end
@@ -17,7 +17,7 @@ module Koala
17
17
 
18
18
  # Facebook can return debug information in the response headers -- see
19
19
  # https://developers.facebook.com/docs/graph-api/using-graph-api#bugdebug
20
- DEBUG_HEADERS = ["x-fb-debug", "x-fb-rev", "x-fb-trace-id"]
20
+ DEBUG_HEADERS = %w[x-fb-debug x-fb-rev x-fb-trace-id x-business-use-case-usage x-ad-account-usage x-app-usage]
21
21
 
22
22
  def error_if_appropriate
23
23
  if http_status >= 400
@@ -61,7 +61,8 @@ module Koala
61
61
  # Normally, we start with the response body. If it isn't valid JSON, we start with an empty
62
62
  # hash and fill it with error data.
63
63
  @response_hash ||= begin
64
- JSON.parse(body)
64
+ parsed_body = JSON.parse(body)
65
+ parsed_body.is_a?(Hash) ? parsed_body : {}
65
66
  rescue JSON::ParserError
66
67
  {}
67
68
  end
data/lib/koala/api.rb CHANGED
@@ -13,14 +13,16 @@ module Koala
13
13
  # signed by default, unless you pass appsecret_proof:
14
14
  # false as an option to the API call. (See
15
15
  # https://developers.facebook.com/docs/graph-api/securing-requests/)
16
+ # @param [Block] rate_limit_hook block called with limits received in facebook response headers
16
17
  # @note If no access token is provided, you can only access some public information.
17
18
  # @return [Koala::Facebook::API] the API client
18
- def initialize(access_token = Koala.config.access_token, app_secret = Koala.config.app_secret)
19
+ def initialize(access_token = Koala.config.access_token, app_secret = Koala.config.app_secret, rate_limit_hook = Koala.config.rate_limit_hook)
19
20
  @access_token = access_token
20
21
  @app_secret = app_secret
22
+ @rate_limit_hook = rate_limit_hook
21
23
  end
22
24
 
23
- attr_reader :access_token, :app_secret
25
+ attr_reader :access_token, :app_secret, :rate_limit_hook
24
26
 
25
27
  include GraphAPIMethods
26
28
 
@@ -58,6 +60,18 @@ module Koala
58
60
  API::GraphCollection.evaluate(response, self)
59
61
  end
60
62
 
63
+ if rate_limit_hook
64
+ limits = %w(x-business-use-case-usage x-ad-account-usage x-app-usage).each_with_object({}) do |key, hash|
65
+ value = response.headers.fetch(key, nil)
66
+ next unless value
67
+ hash[key] = JSON.parse(response.headers[key])
68
+ rescue JSON::ParserError => e
69
+ Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{key} = #{value}")
70
+ end
71
+
72
+ rate_limit_hook.call(limits) if limits.keys.any?
73
+ end
74
+
61
75
  # now process as appropriate for the given call (get picture header, etc.)
62
76
  post_processing ? post_processing.call(desired_data) : desired_data
63
77
  end
@@ -102,7 +116,7 @@ module Koala
102
116
  args = sanitize_request_parameters(args) unless preserve_form_arguments?(options)
103
117
 
104
118
  # add a leading / if needed...
105
- path = "/#{path}" unless path =~ /^\//
119
+ path = "/#{path}" unless path.to_s =~ /^\//
106
120
 
107
121
  # make the request via the provided service
108
122
  result = Koala.make_request(path, args, verb, options)
@@ -28,6 +28,12 @@ class Koala::Configuration
28
28
  # The server to use when constructing dialog URLs.
29
29
  attr_accessor :dialog_host
30
30
 
31
+ # Whether or not to mask tokens
32
+ attr_accessor :mask_tokens
33
+
34
+ # Called with the info for the rate limits in the response header
35
+ attr_accessor :rate_limit_hook
36
+
31
37
  # Certain Facebook services (beta, video) require you to access different
32
38
  # servers. If you're using your own servers, for instance, for a proxy,
33
39
  # you can change both the matcher (what value to change when updating the URL) and the
@@ -45,5 +51,6 @@ class Koala::Configuration
45
51
  Koala::HTTPService::DEFAULT_SERVERS.each_pair do |key, value|
46
52
  self.public_send("#{key}=", value)
47
53
  end
54
+ self.mask_tokens = true
48
55
  end
49
56
  end
data/lib/koala/errors.rb CHANGED
@@ -22,8 +22,12 @@ module Koala
22
22
  :fb_error_user_msg,
23
23
  :fb_error_user_title,
24
24
  :fb_error_trace_id,
25
+ :fb_error_debug_trace_id,
25
26
  :fb_error_debug,
26
- :fb_error_rev
27
+ :fb_error_rev,
28
+ :fb_buc_usage,
29
+ :fb_ada_usage,
30
+ :fb_app_usage
27
31
 
28
32
  # Create a new API Error
29
33
  #
@@ -62,13 +66,17 @@ module Koala
62
66
  self.fb_error_message = error_info["message"]
63
67
  self.fb_error_user_msg = error_info["error_user_msg"]
64
68
  self.fb_error_user_title = error_info["error_user_title"]
69
+ self.fb_error_trace_id = error_info["fbtrace_id"]
65
70
 
66
- self.fb_error_trace_id = error_info["x-fb-trace-id"]
71
+ self.fb_error_debug_trace_id = error_info["x-fb-trace-id"]
67
72
  self.fb_error_debug = error_info["x-fb-debug"]
68
73
  self.fb_error_rev = error_info["x-fb-rev"]
74
+ self.fb_buc_usage = json_parse_for(error_info, "x-business-use-case-usage")
75
+ self.fb_ada_usage = json_parse_for(error_info, "x-ad-account-usage")
76
+ self.fb_app_usage = json_parse_for(error_info, "x-app-usage")
69
77
 
70
78
  error_array = []
71
- %w(type code error_subcode message error_user_title error_user_msg x-fb-trace-id).each do |key|
79
+ %w(type code error_subcode message error_user_title error_user_msg fbtrace_id x-fb-trace-id x-fb-debug x-fb-rev).each do |key|
72
80
  error_array << "#{key}: #{error_info[key]}" if error_info[key]
73
81
  end
74
82
 
@@ -82,6 +90,20 @@ module Koala
82
90
 
83
91
  super(message)
84
92
  end
93
+
94
+ private
95
+
96
+ # refs: https://developers.facebook.com/docs/graph-api/overview/rate-limiting/#headers
97
+ # NOTE: The header will contain a JSON-formatted string that describes current application rate limit usage.
98
+ def json_parse_for(error_info, key)
99
+ string = error_info[key]
100
+ return if string.nil?
101
+
102
+ JSON.parse(string)
103
+ rescue JSON::ParserError => e
104
+ Koala::Utils.logger.error("#{e.class}: #{e.message} while parsing #{key} = #{string}")
105
+ nil
106
+ end
85
107
  end
86
108
 
87
109
  # Facebook returned an invalid response body
@@ -106,9 +106,7 @@ module Koala
106
106
  end
107
107
 
108
108
  def add_ssl_options(opts)
109
- # require https if there's a token
110
- return opts unless raw_args["access_token"]
111
-
109
+ # require https by default (can be overriden by explicitly setting other SSL options)
112
110
  {
113
111
  use_ssl: true,
114
112
  ssl: {verify: true}.merge(opts[:ssl] || {})
@@ -1,4 +1,3 @@
1
- require "net/http/post/multipart"
2
1
  require "tempfile"
3
2
 
4
3
  module Koala
@@ -1,5 +1,5 @@
1
1
  require 'faraday'
2
- require 'koala/http_service/multipart_request'
2
+ require 'faraday/multipart' unless defined? Faraday::FilePart # hack for faraday < 1.9 to avoid warnings
3
3
  require 'koala/http_service/uploadable_io'
4
4
  require 'koala/http_service/response'
5
5
  require 'koala/http_service/request'
@@ -19,7 +19,7 @@ module Koala
19
19
  # We encode requests in a Facebook-compatible multipart request,
20
20
  # and use whichever adapter has been configured for this application.
21
21
  DEFAULT_MIDDLEWARE = Proc.new do |builder|
22
- builder.use Koala::HTTPService::MultipartRequest
22
+ builder.request :multipart
23
23
  builder.request :url_encoded
24
24
  builder.adapter Faraday.default_adapter
25
25
  end
@@ -49,6 +49,18 @@ module Koala
49
49
  # set up our Faraday connection
50
50
  conn = Faraday.new(request.server, faraday_options(request.options), &(faraday_middleware || DEFAULT_MIDDLEWARE))
51
51
 
52
+ filtered_args = request.raw_args.dup.transform_keys(&:to_s)
53
+
54
+ if Koala.config.mask_tokens
55
+ %w(access_token input_token).each do |arg_token|
56
+ if (token = filtered_args[arg_token])
57
+ filtered_args[arg_token] = token[0, 10] + '*****' + token[-5, 5]
58
+ end
59
+ end
60
+ end
61
+
62
+ Koala::Utils.debug "STARTED => #{request.verb.upcase}: #{request.path} params: #{filtered_args.inspect}"
63
+
52
64
  if request.verb == "post" && request.json?
53
65
  # JSON requires a bit more handling
54
66
  # remember, all non-GET requests are turned into POSTs, so this covers everything but GETs
@@ -62,8 +74,7 @@ module Koala
62
74
  response = conn.send(request.verb, request.path, request.post_args)
63
75
  end
64
76
 
65
- # Log URL information
66
- Koala::Utils.debug "#{request.verb.upcase}: #{request.path} params: #{request.raw_args.inspect}"
77
+ Koala::Utils.debug "FINISHED => #{request.verb.upcase}: #{request.path} params: #{filtered_args.inspect}"
67
78
  Koala::HTTPService::Response.new(response.status.to_i, response.body, response.headers)
68
79
  end
69
80
 
data/lib/koala/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Koala
2
- VERSION = "3.0.0"
2
+ VERSION = "3.6.0"
3
3
  end
data/readme.md CHANGED
@@ -1,6 +1,6 @@
1
- Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/gems/koala) [![Dependencies](https://img.shields.io/gemnasium/arsduo/koala.svg)](https://gemnasium.com/arsduo/koala) [![Build Status](https://img.shields.io/travis/arsduo/koala.svg)](http://travis-ci.org/arsduo/koala) [![Code Climate](https://img.shields.io/codeclimate/github/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala) [![Code Coverage](https://img.shields.io/codeclimate/coverage/github/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala)
1
+ Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/gems/koala) [![Build Status](https://img.shields.io/travis/arsduo/koala.svg)](http://travis-ci.org/arsduo/koala) [![Code Climate](https://img.shields.io/codeclimate/coverage-letter/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala) [![Code Coverage](https://img.shields.io/codeclimate/coverage/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala)
2
2
  ====
3
- [Koala](http://github.com/arsduo/koala) is a Facebook library for Ruby, supporting the Graph API (including the batch requests and photo uploads), realtime updates, test users, and OAuth validation. We wrote Koala with four goals:
3
+ [Koala](http://github.com/arsduo/koala) is a Facebook library for Ruby, supporting the Graph API (including the batch requests and photo uploads), the Marketing API, the Atlas API, realtime updates, test users, and OAuth validation. We wrote Koala with four goals:
4
4
 
5
5
  * Lightweight: Koala should be as light and simple as Facebook’s own libraries, providing API accessors and returning simple JSON.
6
6
  * Fast: Koala should, out of the box, be quick. Out of the box, we use Facebook's faster read-only servers when possible and if available, the Typhoeus gem to make snappy Facebook requests. Of course, that brings us to our next topic:
@@ -177,6 +177,37 @@ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
177
177
  ```
178
178
  For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
179
179
 
180
+ Rate limits
181
+ -----------
182
+
183
+ We support Facebook rate limit informations as defined here: [https://developers.facebook.com/docs/graph-api/overview/rate-limiting/](https://developers.facebook.com/docs/graph-api/overview/rate-limiting/)
184
+
185
+ The information is available either via the `Facebook::APIError`:
186
+
187
+ ```ruby
188
+ error.fb_buc_usage
189
+ error.fb_ada_usage
190
+ error.fb_app_usage
191
+ ```
192
+
193
+ Or with the rate_limit_hook:
194
+
195
+ ```ruby
196
+ # App level configuration
197
+
198
+ Koala.configure do |config|
199
+ config.rate_limit_hook = ->(limits) {
200
+ limits["x-app-usage"] # {"call_count"=>0, "total_cputime"=>0, "total_time"=>0}
201
+ limits["x-ad-account-usage"] # {"acc_id_util_pct"=>9.67}
202
+ limits["x-business-use-case-usage"] # {"123456789012345"=>[{"type"=>"messenger", "call_count"=>1, "total_cputime"=>1, "total_time"=>1, "estimated_time_to_regain_access"=>0}]}
203
+ }
204
+ end
205
+
206
+ # Per API configuration
207
+
208
+ Koala::Facebook::API.new('', '', ->(limits) {})
209
+ ```
210
+
180
211
  Test Users
181
212
  ----------
182
213
 
@@ -282,4 +282,81 @@ describe "Koala::Facebook::API" do
282
282
  expect(@service.graph_call('anything')).to be_falsey
283
283
  end
284
284
  end
285
+
286
+ describe "Rate limit hook" do
287
+ it "is called when x-business-use-case-usage header is present" do
288
+ api = Koala::Facebook::API.new('', '', ->(limits) {
289
+ expect(limits["x-business-use-case-usage"]).to eq({"123456789012345"=>[{"type"=>"messenger", "call_count"=>1, "total_cputime"=>1, "total_time"=>1, "estimated_time_to_regain_access"=>0}]})
290
+ })
291
+
292
+ result = {"a" => 2}
293
+ response = Koala::HTTPService::Response.new(200, result.to_json, { "x-business-use-case-usage" => "{\"123456789012345\":[{\"type\":\"messenger\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}" })
294
+ allow(Koala).to receive(:make_request).and_return(response)
295
+
296
+ api.graph_call('anything')
297
+ end
298
+
299
+ it "is called when x-ad-account-usage header is present" do
300
+ api = Koala::Facebook::API.new('', '', ->(limits) {
301
+ expect(limits["x-ad-account-usage"]).to eq({"acc_id_util_pct"=>9.67})
302
+ })
303
+
304
+ result = {"a" => 2}
305
+ response = Koala::HTTPService::Response.new(200, result.to_json, { "x-ad-account-usage" => "{\"acc_id_util_pct\":9.67}" })
306
+ allow(Koala).to receive(:make_request).and_return(response)
307
+
308
+ api.graph_call('anything')
309
+ end
310
+
311
+ it "is called when x-app-usage header is present" do
312
+ api = Koala::Facebook::API.new('', '', ->(limits) {
313
+ expect(limits["x-app-usage"]).to eq({"call_count"=>0, "total_cputime"=>0, "total_time"=>0})
314
+ })
315
+
316
+ result = {"a" => 2}
317
+ response = Koala::HTTPService::Response.new(200, result.to_json, { "x-app-usage" => "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}" })
318
+ allow(Koala).to receive(:make_request).and_return(response)
319
+
320
+ api.graph_call('anything')
321
+ end
322
+
323
+ it "isn't called if none of the rate limit header is present" do
324
+ rate_limit_hook_called = false
325
+
326
+ api = Koala::Facebook::API.new('', '', ->(limits) {
327
+ rate_limit_hook_called = true
328
+ })
329
+
330
+ result = {"a" => 2}
331
+ response = Koala::HTTPService::Response.new(200, result.to_json, {})
332
+ allow(Koala).to receive(:make_request).and_return(response)
333
+
334
+ api.graph_call('anything')
335
+
336
+ expect(rate_limit_hook_called).to be(false)
337
+ end
338
+
339
+ it "isn't called if no rate limit hook is defined" do
340
+ api = Koala::Facebook::API.new('', '', ->(limits) {
341
+ #noop
342
+ })
343
+
344
+ result = {"a" => 2}
345
+ response = Koala::HTTPService::Response.new(200, result.to_json, { "x-ad-account-usage" => "{\"acc_id_util_pct\"9.67}"})
346
+ allow(Koala).to receive(:make_request).and_return(response)
347
+
348
+ expect(Koala::Utils.logger).to receive(:error).with(/JSON::ParserError:.*unexpected token at '{"acc_id_util_pct"9.67}' while parsing x-ad-account-usage = {"acc_id_util_pct"9.67}/)
349
+ api.graph_call('anything')
350
+ end
351
+
352
+ it "logs an error if the rate limit header can't be properly parsed" do
353
+ api = Koala::Facebook::API.new('', '', nil)
354
+
355
+ result = {"a" => 2}
356
+ response = Koala::HTTPService::Response.new(200, result.to_json, {})
357
+ allow(Koala).to receive(:make_request).and_return(response)
358
+
359
+ api.graph_call('anything')
360
+ end
361
+ end
285
362
  end
@@ -1,5 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
+ BUC_USAGE_JSON = "{\"123456789012345\":[{\"type\":\"messenger\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}"
4
+ ADA_USAGE_JSON = "{\"acc_id_util_pct\":9.67}"
5
+ APP_USAGE_JSON = "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}"
6
+
3
7
  describe Koala::Facebook::APIError do
4
8
  it "is a Koala::KoalaError" do
5
9
  expect(Koala::Facebook::APIError.new(nil, nil)).to be_a(Koala::KoalaError)
@@ -30,9 +34,13 @@ describe Koala::Facebook::APIError do
30
34
  'error_subcode' => 'subcode',
31
35
  'error_user_msg' => 'error user message',
32
36
  'error_user_title' => 'error user title',
33
- 'x-fb-trace-id' => 'fb trace id',
37
+ 'fbtrace_id' => 'fb trace id',
38
+ 'x-fb-trace-id' => 'x-fb trace id',
34
39
  'x-fb-debug' => 'fb debug token',
35
- 'x-fb-rev' => 'fb revision'
40
+ 'x-fb-rev' => 'fb revision',
41
+ 'x-business-use-case-usage' => BUC_USAGE_JSON,
42
+ 'x-ad-account-usage' => ADA_USAGE_JSON,
43
+ 'x-app-usage' => APP_USAGE_JSON
36
44
  }
37
45
  Koala::Facebook::APIError.new(400, '', error_info)
38
46
  }
@@ -45,8 +53,12 @@ describe Koala::Facebook::APIError do
45
53
  :fb_error_user_msg => 'error user message',
46
54
  :fb_error_user_title => 'error user title',
47
55
  :fb_error_trace_id => 'fb trace id',
56
+ :fb_error_debug_trace_id => 'x-fb trace id',
48
57
  :fb_error_debug => 'fb debug token',
49
- :fb_error_rev => 'fb revision'
58
+ :fb_error_rev => 'fb revision',
59
+ :fb_buc_usage => JSON.parse(BUC_USAGE_JSON),
60
+ :fb_ada_usage => JSON.parse(ADA_USAGE_JSON),
61
+ :fb_app_usage => JSON.parse(APP_USAGE_JSON)
50
62
  }.each_pair do |accessor, value|
51
63
  it "sets #{accessor} to #{value}" do
52
64
  expect(error.send(accessor)).to eq(value)
@@ -54,7 +66,7 @@ describe Koala::Facebook::APIError do
54
66
  end
55
67
 
56
68
  it "sets the error message appropriately" do
57
- expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_msg: error user message, x-fb-trace-id: fb trace id [HTTP 400]")
69
+ expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_msg: error user message, fbtrace_id: fb trace id, x-fb-trace-id: x-fb trace id, x-fb-debug: fb debug token, x-fb-rev: fb revision [HTTP 400]")
58
70
  end
59
71
  end
60
72
 
@@ -76,7 +88,10 @@ describe Koala::Facebook::APIError do
76
88
  :fb_error_code => 1,
77
89
  :fb_error_subcode => 'subcode',
78
90
  :fb_error_user_msg => 'error user message',
79
- :fb_error_user_title => 'error user title'
91
+ :fb_error_user_title => 'error user title',
92
+ :fb_buc_usage => nil,
93
+ :fb_ada_usage => nil,
94
+ :fb_app_usage => nil
80
95
  }.each_pair do |accessor, value|
81
96
  expect(error.send(accessor)).to eq(value)
82
97
  end
@@ -383,28 +383,85 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
383
383
  end
384
384
  end
385
385
 
386
- describe "processing the request" do
387
- it "returns the result headers as a hash if http_component is headers" do
386
+ describe 'processing the request' do
387
+ let(:response_status) { 203 }
388
+ let(:response_body) { '{\"id\":\"1234\"}'.gsub('\\', '') }
389
+ let(:response_headers) { { 'Content-Type' => 'text/javascript; charset=UTF-8' } }
390
+
391
+ it 'returns the result headers as a hash if http_component is headers' do
388
392
  allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
389
393
  result = @api.batch do |batch_api|
390
394
  batch_api.get_object(KoalaTest.user1, {}, :http_component => :headers)
391
395
  end
392
- expect(result[0]).to eq({"Content-Type" => "text/javascript; charset=UTF-8"})
396
+ expect(response_headers).to eq(result[0])
397
+ end
398
+
399
+ it 'returns the complete response if http_component is response' do
400
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
401
+ result = @api.batch do |batch_api|
402
+ batch_api.get_object(KoalaTest.user1, {}, :http_component => :response)
403
+ end
404
+
405
+ expect(response_status).to eq(result[0].status)
406
+ expect(response_body).to eq(result[0].body)
407
+ expect(response_headers).to eq(result[0].headers)
393
408
  end
394
409
 
395
- describe "if it errors" do
396
- it "raises an APIError if the response is not 200" do
397
- allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(500, "[]", {}))
410
+ describe 'if it errors' do
411
+ it 'raises an APIError if the response is not 200' do
412
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(500, '[]', {}))
398
413
  expect {
399
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
414
+ Koala::Facebook::API.new('foo').batch { |batch_api| batch_api.get_object('me') }
400
415
  }.to raise_exception(Koala::Facebook::APIError)
401
416
  end
402
417
 
403
- it "raises a BadFacebookResponse if the body is empty" do
404
- allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, "", {}))
418
+ it 'raises a BadFacebookResponse if the body is empty' do
419
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '', {}))
405
420
  expect {
406
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
407
- }.to raise_exception(Koala::Facebook::BadFacebookResponse)
421
+ Koala::Facebook::API.new('foo').batch { |batch_api| batch_api.get_object('me') }
422
+ }.to raise_exception(Koala::Facebook::BadFacebookResponse, /Facebook returned an empty body \[HTTP 200\]/)
423
+ end
424
+
425
+ describe 'handle invalid body errors' do
426
+ describe 'with http_component set to :response' do
427
+ it 'raises a BadFacebookResponse if the body is non-empty, non-array' do
428
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '200', {}))
429
+ expect {
430
+ Koala::Facebook::API.new('foo').batch(http_component: :response) do |batch_api|
431
+ batch_api.get_object('me')
432
+ end
433
+ }.to raise_exception(Koala::Facebook::BadFacebookResponse, /Facebook returned an invalid body \[HTTP 200\]/)
434
+ end
435
+
436
+ it 'raises a BadFacebookResponse if the body is invalid JSON' do
437
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '{"\"id\":\1234\"}"}', {}))
438
+ expect {
439
+ Koala::Facebook::API.new('foo').batch(http_component: :response) do |batch_api|
440
+ batch_api.get_object('me')
441
+ end
442
+ }.to raise_exception(Koala::Facebook::BadFacebookResponse, /Facebook returned an invalid body \[HTTP 200\]/)
443
+ end
444
+ end
445
+
446
+ %i[headers status].each do |component|
447
+ describe "with http_component set to #{component}" do
448
+ it 'should not raise a BadFacebookResponse if the body is non-empty, non-array' do
449
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '200', {}))
450
+ expect {
451
+ Koala::Facebook::API.new('foo').batch(http_component: component) { |batch_api| batch_api.get_object('me') }
452
+ }.not_to raise_exception(Koala::Facebook::BadFacebookResponse, /Facebook returned an invalid body \[HTTP 200\]/)
453
+ end
454
+
455
+ it 'should not raise a BadFacebookResponse if the body is invalid JSON' do
456
+ allow(Koala).to receive(:make_request).and_return(Koala::HTTPService::Response.new(200, '{"\"id\":\1234\"}"}', {}))
457
+ expect {
458
+ Koala::Facebook::API.new('foo').batch(http_component: component) do |batch_api|
459
+ batch_api.get_object('me')
460
+ end
461
+ }.not_to raise_exception(Koala::Facebook::BadFacebookResponse, /Facebook returned an invalid body \[HTTP 200\]/)
462
+ end
463
+ end
464
+ end
408
465
  end
409
466
 
410
467
  context "with error info" do
@@ -593,7 +650,7 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
593
650
  hash_including(@other_access_token_args.dup),
594
651
  anything,
595
652
  anything
596
- ).and_return(Koala::HTTPService::Response.new(200, "", ""))
653
+ ).and_return(Koala::HTTPService::Response.new(200, "", {}))
597
654
 
598
655
  # Page the collection
599
656
  app_event_types.next_page
@@ -11,7 +11,10 @@ module Koala
11
11
  expect(GraphErrorChecker::DEBUG_HEADERS).to match_array([
12
12
  "x-fb-rev",
13
13
  "x-fb-debug",
14
- "x-fb-trace-id"
14
+ "x-fb-trace-id",
15
+ "x-business-use-case-usage",
16
+ "x-ad-account-usage",
17
+ "x-app-usage"
15
18
  ])
16
19
  end
17
20
 
@@ -60,6 +63,12 @@ module Koala
60
63
  expect(error.response_body).to eq(body)
61
64
  end
62
65
 
66
+ it "returns a ClientError if the body is null" do
67
+ body.replace("null")
68
+ expect(error).to be_a(ClientError)
69
+ expect(error.response_body).to eq(body)
70
+ end
71
+
63
72
  it "adds error data from the body" do
64
73
  error_data = {
65
74
  "type" => "FB error type",
@@ -67,10 +76,12 @@ module Koala
67
76
  "error_subcode" => "FB error subcode",
68
77
  "message" => "An error occurred!",
69
78
  "error_user_msg" => "A user msg",
70
- "error_user_title" => "usr title"
79
+ "error_user_title" => "usr title",
80
+ "fbtrace_id" => "fbtrace_id"
71
81
  }
72
82
  body.replace({"error" => error_data}.to_json)
73
83
 
84
+ expect(error.fb_error_trace_id).to eq(error_data["fbtrace_id"])
74
85
  expect(error.fb_error_type).to eq(error_data["type"])
75
86
  expect(error.fb_error_code).to eq(error_data["code"])
76
87
  expect(error.fb_error_subcode).to eq(error_data["error_subcode"])
@@ -84,10 +95,25 @@ module Koala
84
95
  "x-fb-debug" => double("fb debug"),
85
96
  "x-fb-rev" => double("fb rev"),
86
97
  "x-fb-trace-id" => double("fb trace id"),
98
+ "x-business-use-case-usage" => { 'a' => 1, 'b' => 2 }.to_json,
99
+ "x-ad-account-usage" => { 'c' => 3, 'd' => 4 }.to_json,
100
+ "x-app-usage" => { 'e' => 5, 'f' => 6 }.to_json
87
101
  )
88
- expect(error.fb_error_trace_id).to eq(headers["x-fb-trace-id"])
102
+ expect(error.fb_error_debug_trace_id).to eq(headers["x-fb-trace-id"])
89
103
  expect(error.fb_error_debug).to eq(headers["x-fb-debug"])
90
104
  expect(error.fb_error_rev).to eq(headers["x-fb-rev"])
105
+ expect(error.fb_buc_usage).to eq({ 'a' => 1, 'b' => 2 })
106
+ expect(error.fb_ada_usage).to eq({ 'c' => 3, 'd' => 4 })
107
+ expect(error.fb_app_usage).to eq({ 'e' => 5, 'f' => 6 })
108
+ end
109
+
110
+ it "logs if one of the FB debug headers can't be parsed" do
111
+ headers.merge!(
112
+ "x-app-usage" => '{invalid:json}'
113
+ )
114
+
115
+ expect(Koala::Utils.logger).to receive(:error).with(/JSON::ParserError:.*unexpected token at '{invalid:json}' while parsing x-app-usage = {invalid:json}/)
116
+ expect(error.fb_app_usage).to eq(nil)
91
117
  end
92
118
 
93
119
  context "it returns an AuthenticationError" do
@@ -176,9 +176,9 @@ module Koala
176
176
  end
177
177
 
178
178
  describe "ssl options" do
179
- it "does nothing if there's no access token" do
179
+ it "includes the default SSL options even if there's no access token" do
180
180
  request_options = Request.new(path: path, args: args.delete_if {|k, _v| k == "access_token"}, verb: verb, options: options).options
181
- expect(request_options).not_to include(:ssl, :use_ssl)
181
+ expect(request_options).to include(use_ssl: true, ssl: {verify: true})
182
182
  end
183
183
 
184
184
  context "if there is an access_token" do
@@ -193,9 +193,9 @@ module Koala
193
193
  end
194
194
 
195
195
  it "overrides default SSL options with what's provided" do
196
- new_ssl_options = {verify: :dunno}
197
- request_options = Request.new(path: path, args: args, verb: verb, options: options.merge(ssl: new_ssl_options)).options
198
- expect(request_options[:ssl]).to include(new_ssl_options)
196
+ new_ssl_options = {use_ssl: false, ssl:{verify: :dunno}}
197
+ request_options = Request.new(path: path, args: args, verb: verb, options: options.merge(new_ssl_options)).options
198
+ expect(request_options).to include(new_ssl_options)
199
199
  end
200
200
  end
201
201
  end
@@ -211,9 +211,17 @@ module Koala
211
211
  expect(request.server).to eq("https://foo")
212
212
  end
213
213
 
214
- context "if options[:use_ssl] is false (e.g. no access token)" do
214
+ context "if there is no access token" do
215
215
  let(:args) { {"a" => "b"} }
216
216
 
217
+ it "uses https" do
218
+ expect(request.server).to eq("https://graph.facebook.com")
219
+ end
220
+ end
221
+
222
+ context "if options[:use_ssl] is false" do
223
+ let(:options) { {use_ssl: false} }
224
+
217
225
  it "uses http" do
218
226
  expect(request.server).to eq("http://graph.facebook.com")
219
227
  end
@@ -39,8 +39,7 @@ describe Koala::HTTPService do
39
39
 
40
40
  it "adds the right default middleware" do
41
41
  Koala::HTTPService::DEFAULT_MIDDLEWARE.call(builder)
42
- expect(builder.requests).to eq([:url_encoded])
43
- expect(builder.uses).to eq([Koala::HTTPService::MultipartRequest])
42
+ expect(builder.requests).to eq([:multipart, :url_encoded])
44
43
  expect(builder.adapters).to eq([Faraday.default_adapter])
45
44
  end
46
45
  end
@@ -122,7 +121,8 @@ describe Koala::HTTPService do
122
121
 
123
122
  let(:verb) { "get" }
124
123
  let(:options) { {} }
125
- let(:request) { Koala::HTTPService::Request.new(path: "/foo", verb: verb, args: {"an" => :arg}, options: options) }
124
+ let(:args) { {"an" => :arg } }
125
+ let(:request) { Koala::HTTPService::Request.new(path: "/foo", verb: verb, args: args, options: options) }
126
126
 
127
127
  shared_examples_for :making_a_request do
128
128
  before :each do
@@ -153,7 +153,8 @@ describe Koala::HTTPService do
153
153
 
154
154
  it "logs verb, url and params to debug" do
155
155
  log_message = "#{verb.upcase}: #{request.path} params: #{request.raw_args.inspect}"
156
- expect(Koala::Utils.logger).to receive(:debug).with(log_message)
156
+ expect(Koala::Utils.logger).to receive(:debug).with("STARTED => #{log_message}")
157
+ expect(Koala::Utils.logger).to receive(:debug).with("FINISHED => #{log_message}")
157
158
 
158
159
  Koala::HTTPService.make_request(request)
159
160
  end
@@ -192,18 +193,16 @@ describe Koala::HTTPService do
192
193
 
193
194
  it "uses the default builder block if HTTPService.faraday_middleware block is not defined" do
194
195
  block = Proc.new { |builder|
195
- builder.use Koala::HTTPService::MultipartRequest
196
+ builder.request :multipart
196
197
  builder.request :url_encoded
197
- builder.use Koala::HTTPService::MultipartRequest
198
198
  }
199
199
  stub_const("Koala::HTTPService::DEFAULT_MIDDLEWARE", block)
200
200
  allow(Koala::HTTPService).to receive(:faraday_middleware).and_return(nil)
201
201
 
202
202
  expect_any_instance_of(Faraday::Connection).to receive(:get) do |instance|
203
203
  expect(instance.builder.handlers).to eq([
204
- Koala::HTTPService::MultipartRequest,
204
+ Faraday::Multipart::Middleware,
205
205
  Faraday::Request::UrlEncoded,
206
- Koala::HTTPService::MultipartRequest
207
206
  ])
208
207
  mock_http_response
209
208
  end
@@ -213,22 +212,57 @@ describe Koala::HTTPService do
213
212
 
214
213
  it "uses the defined HTTPService.faraday_middleware block if defined" do
215
214
  block = Proc.new { |builder|
216
- builder.use Koala::HTTPService::MultipartRequest
215
+ builder.request :multipart
217
216
  builder.request :url_encoded
218
- builder.use Koala::HTTPService::MultipartRequest
219
217
  }
220
218
  expect(Koala::HTTPService).to receive(:faraday_middleware).and_return(block)
221
219
 
222
220
  expect_any_instance_of(Faraday::Connection).to receive(:get) do |instance|
223
221
  expect(instance.builder.handlers).to eq([
224
- Koala::HTTPService::MultipartRequest,
222
+ Faraday::Multipart::Middleware,
225
223
  Faraday::Request::UrlEncoded,
226
- Koala::HTTPService::MultipartRequest
227
224
  ])
228
225
  mock_http_response
229
226
  end
230
227
 
231
228
  Koala::HTTPService.make_request(request)
232
229
  end
230
+
231
+ context 'log_tokens configuration' do
232
+ let(:args) { { "an" => :arg, "access_token" => "myvisbleaccesstoken" } }
233
+
234
+ before(:each) do
235
+ allow_any_instance_of(Faraday::Connection).to receive(:get) { double(status: '200', body: 'ok', headers: {}) }
236
+ end
237
+
238
+ it 'logs tokens' do
239
+ allow(Koala.config).to receive(:mask_tokens) { false }
240
+
241
+ expect(Koala::Utils).to receive(:debug).with('STARTED => GET: /foo params: {"an"=>:arg, "access_token"=>"myvisbleaccesstoken"}')
242
+ expect(Koala::Utils).to receive(:debug).with('FINISHED => GET: /foo params: {"an"=>:arg, "access_token"=>"myvisbleaccesstoken"}')
243
+
244
+ Koala::HTTPService.make_request(request)
245
+ end
246
+
247
+ it 'doesnt log tokens' do
248
+ allow(Koala.config).to receive(:mask_tokens) { true }
249
+
250
+ expect(Koala::Utils).to receive(:debug).with('STARTED => GET: /foo params: {"an"=>:arg, "access_token"=>"myvisbleac*****token"}')
251
+ expect(Koala::Utils).to receive(:debug).with('FINISHED => GET: /foo params: {"an"=>:arg, "access_token"=>"myvisbleac*****token"}')
252
+
253
+ Koala::HTTPService.make_request(request)
254
+ end
255
+
256
+ it 'hides the token for the debug_token api endpoint' do
257
+ request = Koala::HTTPService::Request.new(path: "/debug_token", verb: verb, args: { input_token: 'myvisibleaccesstoken', 'access_token' => 'myvisibleaccesstoken' }, options: options)
258
+
259
+ allow(Koala.config).to receive(:mask_tokens) { true }
260
+
261
+ expect(Koala::Utils).to receive(:debug).with('STARTED => GET: /debug_token params: {"input_token"=>"myvisiblea*****token", "access_token"=>"myvisiblea*****token"}')
262
+ expect(Koala::Utils).to receive(:debug).with('FINISHED => GET: /debug_token params: {"input_token"=>"myvisiblea*****token", "access_token"=>"myvisiblea*****token"}')
263
+
264
+ Koala::HTTPService.make_request(request)
265
+ end
266
+ end
233
267
  end
234
268
  end
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: get
5
- uri: https://graph.facebook.com/v2.8/119908831367602/accounts?access_token=**
5
+ uri: https://graph.facebook.com/v2.8/119908831367602/accounts?access_token=********************
6
6
  body:
7
7
  encoding: US-ASCII
8
8
  string: ''
@@ -238,11 +238,11 @@ module KoalaTest
238
238
  # JRuby doesn't support typhoeus on Travis
239
239
  unless defined? JRUBY_VERSION
240
240
  require adapter
241
- require 'typhoeus/adapters/faraday' if adapter.to_s == "typhoeus"
241
+ require "faraday/#{adapter}"
242
242
  Faraday.default_adapter = adapter.to_sym
243
243
  end
244
- rescue ParserError
245
- puts "Unable to load adapter #{adapter}, using Net::HTTP."
244
+ rescue => e
245
+ puts "Unable to load adapter #{adapter}, using Net::HTTP. #{e.class} #{e.message}"
246
246
  ensure
247
247
  @adapter_activation_attempted = true
248
248
  end
@@ -8,7 +8,7 @@ module Koala
8
8
  # Mocks all HTTP requests for with koala_spec_with_mocks.rb
9
9
  # Mocked values to be included in TEST_DATA used in specs
10
10
  ACCESS_TOKEN = '*'
11
- APP_ACCESS_TOKEN = "**"
11
+ APP_ACCESS_TOKEN = "********************"
12
12
  OAUTH_CODE = 'OAUTHCODE'
13
13
 
14
14
  # Loads testing data
@@ -30,7 +30,7 @@ module Koala
30
30
 
31
31
  # Loads the mock response data via ERB to substitue values for TEST_DATA (see oauth/access_token)
32
32
  mock_response_file_path = File.join(File.dirname(__FILE__), '..', 'fixtures', 'mock_facebook_responses.yml')
33
- RESPONSES = YAML.load(ERB.new(IO.read(mock_response_file_path)).result(binding))
33
+ RESPONSES = YAML.safe_load(ERB.new(IO.read(mock_response_file_path)).result(binding), [], [], true)
34
34
 
35
35
  def self.make_request(request)
36
36
  if response = match_response(request.raw_path, request.raw_args, request.raw_verb, request.raw_options)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: koala
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Koppel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-17 00:00:00.000000000 Z
11
+ date: 2024-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-multipart
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: addressable
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,34 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rexml
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: base64
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  description: Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write
56
98
  access to the social graph via the Graph and REST APIs, as well as support for realtime
57
99
  updates and OAuth and Facebook Connect authentication. Koala is fully tested and
@@ -64,9 +106,9 @@ extra_rdoc_files:
64
106
  - readme.md
65
107
  - changelog.md
66
108
  files:
109
+ - ".github/workflows/test.yml"
67
110
  - ".gitignore"
68
111
  - ".rspec"
69
- - ".travis.yml"
70
112
  - ".yardopts"
71
113
  - Gemfile
72
114
  - ISSUE_TEMPLATE
@@ -87,7 +129,6 @@ files:
87
129
  - lib/koala/configuration.rb
88
130
  - lib/koala/errors.rb
89
131
  - lib/koala/http_service.rb
90
- - lib/koala/http_service/multipart_request.rb
91
132
  - lib/koala/http_service/request.rb
92
133
  - lib/koala/http_service/response.rb
93
134
  - lib/koala/http_service/uploadable_io.rb
@@ -109,7 +150,6 @@ files:
109
150
  - spec/cases/http_service_spec.rb
110
151
  - spec/cases/koala_spec.rb
111
152
  - spec/cases/koala_test_spec.rb
112
- - spec/cases/multipart_request_spec.rb
113
153
  - spec/cases/oauth_spec.rb
114
154
  - spec/cases/realtime_updates_spec.rb
115
155
  - spec/cases/test_users_spec.rb
@@ -151,42 +191,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
191
  - !ruby/object:Gem::Version
152
192
  version: '0'
153
193
  requirements: []
154
- rubyforge_project:
155
- rubygems_version: 2.6.10
194
+ rubygems_version: 3.3.26
156
195
  signing_key:
157
196
  specification_version: 4
158
197
  summary: A lightweight, flexible library for Facebook with support for the Graph API,
159
198
  the REST API, realtime updates, and OAuth authentication.
160
- test_files:
161
- - spec/cases/api_spec.rb
162
- - spec/cases/configuration_spec.rb
163
- - spec/cases/error_spec.rb
164
- - spec/cases/graph_api_batch_spec.rb
165
- - spec/cases/graph_api_spec.rb
166
- - spec/cases/graph_collection_spec.rb
167
- - spec/cases/graph_error_checker_spec.rb
168
- - spec/cases/http_service/request_spec.rb
169
- - spec/cases/http_service/response_spec.rb
170
- - spec/cases/http_service_spec.rb
171
- - spec/cases/koala_spec.rb
172
- - spec/cases/koala_test_spec.rb
173
- - spec/cases/multipart_request_spec.rb
174
- - spec/cases/oauth_spec.rb
175
- - spec/cases/realtime_updates_spec.rb
176
- - spec/cases/test_users_spec.rb
177
- - spec/cases/uploadable_io_spec.rb
178
- - spec/cases/utils_spec.rb
179
- - spec/fixtures/beach.jpg
180
- - spec/fixtures/cat.m4v
181
- - spec/fixtures/facebook_data.yml
182
- - spec/fixtures/mock_facebook_responses.yml
183
- - spec/fixtures/vcr_cassettes/app_test_accounts.yml
184
- - spec/fixtures/vcr_cassettes/friend_list_next_page.yml
185
- - spec/integration/graph_collection_spec.rb
186
- - spec/spec_helper.rb
187
- - spec/support/custom_matchers.rb
188
- - spec/support/graph_api_shared_examples.rb
189
- - spec/support/koala_test.rb
190
- - spec/support/mock_http_service.rb
191
- - spec/support/uploadable_io_shared_examples.rb
192
- has_rdoc:
199
+ test_files: []
data/.travis.yml DELETED
@@ -1,19 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- cache: bundler
4
- rvm:
5
- # MRI
6
- - 2.1
7
- - 2.2
8
- - 2.3.1
9
- - 2.4.0
10
- # Rubinius is failing due to segfaults on Travis (and takes significantly longer to run)
11
- # those builds will be restored later
12
- # jruby
13
- # - jruby-19mode
14
- bundler_args: --without development
15
- addons:
16
- code_climate:
17
- repo_token: 7af99d9225b4c14640f9ec3cb2e24d2f7103ac49417b0bd989188fb6c25f2909
18
- after_success:
19
- - bundle exec codeclimate-test-reporter
@@ -1,37 +0,0 @@
1
- require 'faraday'
2
-
3
- module Koala
4
- module HTTPService
5
- class MultipartRequest < Faraday::Request::Multipart
6
- # Facebook expects nested parameters to be passed in a certain way
7
- # Based on our testing (https://github.com/arsduo/koala/issues/125),
8
- # Faraday needs two changes to make that work:
9
- # 1) [] need to be escaped (e.g. params[foo]=bar ==> params%5Bfoo%5D=bar)
10
- # 2) such messages need to be multipart-encoded
11
-
12
- self.mime_type = 'multipart/form-data'.freeze
13
-
14
- def process_request?(env)
15
- # if the request values contain any hashes or arrays, multipart it
16
- super || !!(env[:body].respond_to?(:values) && env[:body].values.find {|v| v.is_a?(Hash) || v.is_a?(Array)})
17
- end
18
-
19
-
20
- def process_params(params, prefix = nil, pieces = nil, &block)
21
- params.inject(pieces || []) do |all, (key, value)|
22
- key = "#{prefix}%5B#{key}%5D" if prefix
23
-
24
- case value
25
- when Array
26
- values = value.inject([]) { |a,v| a << [nil, v] }
27
- process_params(values, key, all, &block)
28
- when Hash
29
- process_params(value, key, all, &block)
30
- else
31
- all << block.call(key, value)
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,65 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Koala::HTTPService::MultipartRequest do
4
- it "is a subclass of Faraday::Request::Multipart" do
5
- expect(Koala::HTTPService::MultipartRequest.superclass).to eq(Faraday::Request::Multipart)
6
- end
7
-
8
- it "defines mime_type as multipart/form-data" do
9
- expect(Koala::HTTPService::MultipartRequest.mime_type).to eq('multipart/form-data')
10
- end
11
-
12
- describe "#process_request?" do
13
- before :each do
14
- @env = Faraday::Env.new
15
- @multipart = Koala::HTTPService::MultipartRequest.new
16
- allow(@multipart).to receive(:request_type).and_return("")
17
- end
18
-
19
- # no way to test the call to super, unfortunately
20
- it "returns true if env[:body] is a hash with at least one hash in its values" do
21
- @env[:body] = {:a => {:c => 2}}
22
- expect(@multipart.process_request?(@env)).to be_truthy
23
- end
24
-
25
- it "returns true if env[:body] is a hash with at least one array in its values" do
26
- @env[:body] = {:a => [:c, 2]}
27
- expect(@multipart.process_request?(@env)).to be_truthy
28
- end
29
-
30
- it "returns true if env[:body] is a hash with mixed objects in its values" do
31
- @env[:body] = {:a => [:c, 2], :b => {:e => :f}}
32
- expect(@multipart.process_request?(@env)).to be_truthy
33
- end
34
-
35
- it "returns false if env[:body] is a string" do
36
- @env[:body] = "my body"
37
- expect(@multipart.process_request?(@env)).to be_falsey
38
- end
39
-
40
- it "returns false if env[:body] is a hash without an array or hash value" do
41
- @env[:body] = {:a => 3}
42
- expect(@multipart.process_request?(@env)).to be_falsey
43
- end
44
- end
45
-
46
- describe "#process_params" do
47
- before :each do
48
- @parent = Faraday::Request::Multipart.new
49
- @multipart = Koala::HTTPService::MultipartRequest.new
50
- @block = lambda {|k, v| "#{k}=#{v}"}
51
- end
52
-
53
- it "is identical to the parent for requests without a prefix" do
54
- hash = {:a => 2, :c => "3"}
55
- expect(@multipart.process_params(hash, &@block)).to eq(@parent.process_params(hash, &@block))
56
- end
57
-
58
- it "replaces encodes [ and ] if the request has a prefix" do
59
- hash = {:a => 2, :c => "3"}
60
- prefix = "foo"
61
- # process_params returns an array
62
- expect(@multipart.process_params(hash, prefix, &@block).join("&")).to eq(@parent.process_params(hash, prefix, &@block).join("&").gsub(/\[/, "%5B").gsub(/\]/, "%5D"))
63
- end
64
- end
65
- end