koala 1.10.1 → 1.11.0rc
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/changelog.md +16 -0
- data/lib/koala/api/graph_batch_api.rb +5 -2
- data/lib/koala/errors.rb +6 -4
- data/lib/koala/http_service.rb +14 -2
- data/lib/koala/oauth.rb +3 -2
- data/lib/koala/test_users.rb +1 -1
- data/lib/koala/utils.rb +8 -0
- data/lib/koala/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/cases/error_spec.rb +14 -8
- data/spec/cases/graph_api_batch_spec.rb +9 -0
- data/spec/cases/graph_collection_spec.rb +4 -4
- data/spec/cases/http_service_spec.rb +24 -0
- data/spec/cases/oauth_spec.rb +28 -0
- data/spec/cases/test_users_spec.rb +2 -2
- data/spec/fixtures/facebook_data.yml +4 -1
- data/spec/fixtures/vcr_cassettes/friend_list_next_page.yml +121 -0
- data/spec/integration/graph_collection_spec.rb +21 -0
- data/spec/support/koala_test.rb +72 -22
- metadata +24 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44c50b37f43f7bc407a12420de96e89f181d7b6f
|
4
|
+
data.tar.gz: 21203b9c7703b5bbacd43711ce990d3684a9d34c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29b23719eaaedf448162e7d477525d5a999d975ad6d2847bf5a4170d08bb42efcfee924d99c9ea747aa0310b046ba38ea4804a69aa858e9b52be9097bc706d41
|
7
|
+
data.tar.gz: 89dd9133b9976792fda084c954046f0828c30e48c727a3c9f37c15a5f0905e24d6161f884c025a5ae5040b7d4cd5a0f7c3918f4ef6a70352656dae8a0749cb0b
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/changelog.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
v.1.11.0
|
2
|
+
========
|
3
|
+
|
4
|
+
Updated features:
|
5
|
+
* OAuth now supports decoding the new JSON responses from the access token endpoints (thanks, ridiculous!)
|
6
|
+
* Batch API now accepts either symbols or strings for the access token (thanks, mattmueller!)
|
7
|
+
* Incorporate user message and title into error handling (thanks, richrines and isra17!)
|
8
|
+
|
9
|
+
Bug fixes:
|
10
|
+
* Fixed bug in GraphCollection URL versioning (thanks, deviousdodo and jankowy!)
|
11
|
+
* TestUsers#create_network now properly handles options (thanks, crx!)
|
12
|
+
|
13
|
+
Documentation improvements:
|
14
|
+
* Updated gem version (thanks, thomasklemm!)
|
15
|
+
|
16
|
+
|
1
17
|
v.1.10.1
|
2
18
|
========
|
3
19
|
|
@@ -19,12 +19,15 @@ module Koala
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def graph_call_in_batch(path, args = {}, verb = "get", options = {}, &post_processing)
|
22
|
-
#
|
22
|
+
# normalize options for consistency
|
23
|
+
options = Koala::Utils.symbolize_hash(options)
|
24
|
+
|
25
|
+
# for batch APIs, we queue up the call details (incl. post-processing)
|
23
26
|
batch_calls << BatchOperation.new(
|
24
27
|
:url => path,
|
25
28
|
:args => args,
|
26
29
|
:method => verb,
|
27
|
-
:access_token => options[
|
30
|
+
:access_token => options[:access_token] || access_token,
|
28
31
|
:http_options => options,
|
29
32
|
:post_processing => post_processing
|
30
33
|
)
|
data/lib/koala/errors.rb
CHANGED
@@ -14,14 +14,14 @@ module Koala
|
|
14
14
|
# http_status, then the error was detected before making a call to Facebook. (e.g. missing access token)
|
15
15
|
class APIError < ::Koala::KoalaError
|
16
16
|
attr_accessor :fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message,
|
17
|
-
:http_status, :response_body
|
17
|
+
:fb_error_user_message, :fb_error_user_title, :http_status, :response_body
|
18
18
|
|
19
19
|
# Create a new API Error
|
20
20
|
#
|
21
21
|
# @param http_status [Integer] The HTTP status code of the response
|
22
22
|
# @param response_body [String] The response body
|
23
23
|
# @param error_info One of the following:
|
24
|
-
# [Hash] The error information extracted from the request
|
24
|
+
# [Hash] The error information extracted from the request
|
25
25
|
# ("type", "code", "error_subcode", "message")
|
26
26
|
# [String] The error description
|
27
27
|
# If error_info is nil or not provided, the method will attempt to extract
|
@@ -51,9 +51,11 @@ module Koala
|
|
51
51
|
self.fb_error_code = error_info["code"]
|
52
52
|
self.fb_error_subcode = error_info["error_subcode"]
|
53
53
|
self.fb_error_message = error_info["message"]
|
54
|
+
self.fb_error_user_message = error_info["error_user_message"]
|
55
|
+
self.fb_error_user_title = error_info["error_user_title"]
|
54
56
|
|
55
57
|
error_array = []
|
56
|
-
%w(type code error_subcode message).each do |key|
|
58
|
+
%w(type code error_subcode message error_user_title error_user_message).each do |key|
|
57
59
|
error_array << "#{key}: #{error_info[key]}" if error_info[key]
|
58
60
|
end
|
59
61
|
|
@@ -78,7 +80,7 @@ module Koala
|
|
78
80
|
# Any error with a 5xx HTTP status code
|
79
81
|
class ServerError < APIError; end
|
80
82
|
|
81
|
-
# Any error with a 4xx HTTP status code
|
83
|
+
# Any error with a 4xx HTTP status code
|
82
84
|
class ClientError < APIError; end
|
83
85
|
|
84
86
|
# All graph API authentication failures.
|
data/lib/koala/http_service.rb
CHANGED
@@ -88,8 +88,10 @@ module Koala
|
|
88
88
|
ssl[:verify] = true unless ssl.has_key?(:verify)
|
89
89
|
end
|
90
90
|
|
91
|
-
# if an api_version is specified
|
92
|
-
|
91
|
+
# if an api_version is specified and the path does not already contain
|
92
|
+
# one, prepend it to the path
|
93
|
+
api_version = request_options[:api_version] || Koala.config.api_version
|
94
|
+
if api_version && !path_contains_api_version?(path)
|
93
95
|
begins_with_slash = path[0] == "/"
|
94
96
|
divider = begins_with_slash ? "" : "/"
|
95
97
|
path = "/#{api_version}#{divider}#{path}"
|
@@ -123,6 +125,16 @@ module Koala
|
|
123
125
|
end).join("&")
|
124
126
|
end
|
125
127
|
|
128
|
+
# Determines whether a given path already contains an API version.
|
129
|
+
#
|
130
|
+
# @param path the URL path.
|
131
|
+
#
|
132
|
+
# @return true or false accordingly.
|
133
|
+
def self.path_contains_api_version?(path)
|
134
|
+
match = /^\/?(v\d+(?:\.\d+)?)\//.match(path)
|
135
|
+
!!(match && match[1])
|
136
|
+
end
|
137
|
+
|
126
138
|
# deprecations
|
127
139
|
# not elegant or compact code, but temporary
|
128
140
|
|
data/lib/koala/oauth.rb
CHANGED
@@ -316,11 +316,12 @@ module Koala
|
|
316
316
|
end
|
317
317
|
|
318
318
|
def parse_access_token(response_text)
|
319
|
-
|
319
|
+
MultiJson.load(response_text)
|
320
|
+
rescue MultiJson::ParseError
|
321
|
+
response_text.split("&").inject({}) do |hash, bit|
|
320
322
|
key, value = bit.split("=")
|
321
323
|
hash.merge!(key => value)
|
322
324
|
end
|
323
|
-
components
|
324
325
|
end
|
325
326
|
|
326
327
|
def parse_unsigned_cookie(fb_cookie)
|
data/lib/koala/test_users.rb
CHANGED
@@ -166,7 +166,7 @@ module Koala
|
|
166
166
|
#
|
167
167
|
# @return the list of users created
|
168
168
|
def create_network(network_size, installed = true, permissions = '', options = {})
|
169
|
-
users = (0...network_size).collect { create(installed, permissions, options) }
|
169
|
+
users = (0...network_size).collect { create(installed, permissions, {}, options) }
|
170
170
|
friends = users.clone
|
171
171
|
users.each do |user|
|
172
172
|
# Remove this user from list of friends
|
data/lib/koala/utils.rb
CHANGED
@@ -29,5 +29,13 @@ module Koala
|
|
29
29
|
@posted_deprecations << message
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
# Ensures that a hash uses symbols as opposed to strings
|
34
|
+
# Useful for allowing either syntax for end users
|
35
|
+
def symbolize_hash(hash)
|
36
|
+
return hash unless hash.is_a?(Hash)
|
37
|
+
|
38
|
+
hash.inject({}){ |memo,(key,value)| memo[key.to_sym] = symbolize_hash(value); memo }
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
data/lib/koala/version.rb
CHANGED
data/readme.md
CHANGED
data/spec/cases/error_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe Koala::Facebook::APIError do
|
|
5
5
|
expect(Koala::Facebook::APIError.new(nil, nil)).to be_a(Koala::KoalaError)
|
6
6
|
end
|
7
7
|
|
8
|
-
[:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :http_status, :response_body].each do |accessor|
|
8
|
+
[:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :fb_error_user_message, :fb_error_user_title, :http_status, :response_body].each do |accessor|
|
9
9
|
it "has an accessor for #{accessor}" do
|
10
10
|
expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(accessor)
|
11
11
|
expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(:"#{accessor}=")
|
@@ -22,12 +22,14 @@ describe Koala::Facebook::APIError do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
context "with an error_info hash" do
|
25
|
-
let(:error) {
|
25
|
+
let(:error) {
|
26
26
|
error_info = {
|
27
27
|
'type' => 'type',
|
28
28
|
'message' => 'message',
|
29
29
|
'code' => 1,
|
30
|
-
'error_subcode' => 'subcode'
|
30
|
+
'error_subcode' => 'subcode',
|
31
|
+
'error_user_message' => 'error user message',
|
32
|
+
'error_user_title' => 'error user title'
|
31
33
|
}
|
32
34
|
Koala::Facebook::APIError.new(400, '', error_info)
|
33
35
|
}
|
@@ -36,15 +38,17 @@ describe Koala::Facebook::APIError do
|
|
36
38
|
:fb_error_type => 'type',
|
37
39
|
:fb_error_message => 'message',
|
38
40
|
:fb_error_code => 1,
|
39
|
-
:fb_error_subcode => 'subcode'
|
41
|
+
:fb_error_subcode => 'subcode',
|
42
|
+
:fb_error_user_message => 'error user message',
|
43
|
+
:fb_error_user_title => 'error user title'
|
40
44
|
}.each_pair do |accessor, value|
|
41
45
|
it "sets #{accessor} to #{value}" do
|
42
46
|
expect(error.send(accessor)).to eq(value)
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
it "sets the error message
|
47
|
-
expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message [HTTP 400]")
|
50
|
+
it "sets the error message appropriately" do
|
51
|
+
expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_message: error user message [HTTP 400]")
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
@@ -58,13 +62,15 @@ describe Koala::Facebook::APIError do
|
|
58
62
|
|
59
63
|
context "with no error_info and a response_body containing error JSON" do
|
60
64
|
it "should extract the error info from the response body" do
|
61
|
-
response_body = '{ "error": { "type": "type", "message": "message", "code": 1, "error_subcode": "subcode" } }'
|
65
|
+
response_body = '{ "error": { "type": "type", "message": "message", "code": 1, "error_subcode": "subcode", "error_user_message": "error user message", "error_user_title": "error user title" } }'
|
62
66
|
error = Koala::Facebook::APIError.new(400, response_body)
|
63
67
|
{
|
64
68
|
:fb_error_type => 'type',
|
65
69
|
:fb_error_message => 'message',
|
66
70
|
:fb_error_code => 1,
|
67
|
-
:fb_error_subcode => 'subcode'
|
71
|
+
:fb_error_subcode => 'subcode',
|
72
|
+
:fb_error_user_message => 'error user message',
|
73
|
+
:fb_error_user_title => 'error user title'
|
68
74
|
}.each_pair do |accessor, value|
|
69
75
|
expect(error.send(accessor)).to eq(value)
|
70
76
|
end
|
@@ -519,6 +519,15 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
|
|
519
519
|
expect(insights).to be_an(Koala::Facebook::GraphCollection)
|
520
520
|
end
|
521
521
|
|
522
|
+
it "handles requests passing the access token option as a symbol instead of a string" do
|
523
|
+
me, insights = @api.batch do |batch_api|
|
524
|
+
batch_api.get_object('me')
|
525
|
+
batch_api.get_connections(@app_id, 'insights', {}, {:access_token => @app_api.access_token})
|
526
|
+
end
|
527
|
+
expect(me['id']).not_to be_nil
|
528
|
+
expect(insights).to be_an(Koala::Facebook::GraphCollection)
|
529
|
+
end
|
530
|
+
|
522
531
|
it "preserves batch-op specific access tokens in GraphCollection returned from batch" do
|
523
532
|
# Provide an alternate token for a batch operation
|
524
533
|
@other_access_token_args = { 'access_token' => @app_api.access_token }
|
@@ -145,15 +145,15 @@ describe Koala::Facebook::GraphCollection do
|
|
145
145
|
end
|
146
146
|
|
147
147
|
describe "#next_page" do
|
148
|
-
let(:paging){ {"next" => "http://example.com
|
148
|
+
let(:paging){ {"next" => "http://example.com/abc?a=2&b=3"} }
|
149
149
|
|
150
150
|
it "should get next page" do
|
151
|
-
expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3"}])
|
151
|
+
expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3"}])
|
152
152
|
@collection.next_page
|
153
153
|
end
|
154
154
|
|
155
155
|
it "should get next page with extra parameters" do
|
156
|
-
expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3", "c" => "4"}])
|
156
|
+
expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3", "c" => "4"}])
|
157
157
|
@collection.next_page("c" => "4")
|
158
158
|
end
|
159
159
|
end
|
@@ -171,4 +171,4 @@ describe Koala::Facebook::GraphCollection do
|
|
171
171
|
@collection.previous_page("c" => "4")
|
172
172
|
end
|
173
173
|
end
|
174
|
-
end
|
174
|
+
end
|
@@ -296,6 +296,12 @@ describe Koala::HTTPService do
|
|
296
296
|
expect(@mock_connection).to receive(:get).with("/v12/anything", anything)
|
297
297
|
Koala::HTTPService.make_request("/anything", {}, "get")
|
298
298
|
end
|
299
|
+
|
300
|
+
it "doesn't add a version if the path already contains one" do
|
301
|
+
expect(Koala.config).to receive(:api_version).and_return("v11")
|
302
|
+
expect(@mock_connection).to receive(:get).with("/v12/anything", anything)
|
303
|
+
Koala::HTTPService.make_request("/v12/anything", {}, "get")
|
304
|
+
end
|
299
305
|
end
|
300
306
|
|
301
307
|
it "makes a POST request if the verb isn't get" do
|
@@ -378,6 +384,24 @@ describe Koala::HTTPService do
|
|
378
384
|
end
|
379
385
|
end
|
380
386
|
|
387
|
+
describe ".path_contains_api_version?" do
|
388
|
+
it "works when the path is prefixed by a slash" do
|
389
|
+
expect(Koala::HTTPService.path_contains_api_version?('/v2.1/anything')).to be true
|
390
|
+
end
|
391
|
+
|
392
|
+
it "works when the path is not prefixed by a slash" do
|
393
|
+
expect(Koala::HTTPService.path_contains_api_version?('v2.1/anything')).to be true
|
394
|
+
end
|
395
|
+
|
396
|
+
it "works with versions without a ." do
|
397
|
+
expect(Koala::HTTPService.path_contains_api_version?('v21/anything')).to be true
|
398
|
+
end
|
399
|
+
|
400
|
+
it "returns nil for paths without a version" do
|
401
|
+
expect(Koala::HTTPService.path_contains_api_version?('/anything')).to be false
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
381
405
|
describe "deprecated options" do
|
382
406
|
before :each do
|
383
407
|
allow(Koala::HTTPService).to receive(:http_options).and_return({})
|
data/spec/cases/oauth_spec.rb
CHANGED
@@ -449,6 +449,34 @@ describe "Koala::Facebook::OAuth" do
|
|
449
449
|
@oauth.get_access_token_info(@code)
|
450
450
|
end
|
451
451
|
|
452
|
+
it "properly decodes JSON results" do
|
453
|
+
result = {
|
454
|
+
"access_token" => "foo",
|
455
|
+
"expires_in" => "baz",
|
456
|
+
"machine_id" => "bar"
|
457
|
+
}
|
458
|
+
allow(Koala).to receive(:make_request).and_return(
|
459
|
+
Koala::HTTPService::Response.new(
|
460
|
+
200,
|
461
|
+
MultiJson.dump(result),
|
462
|
+
{}
|
463
|
+
)
|
464
|
+
)
|
465
|
+
expect(@oauth.get_access_token_info(@code)).to eq(result)
|
466
|
+
end
|
467
|
+
|
468
|
+
it "falls back to URL-style parsing " do
|
469
|
+
result = "access_token=foo&expires_in=baz&machine_id=bar"
|
470
|
+
allow(Koala).to receive(:make_request).and_return(
|
471
|
+
Koala::HTTPService::Response.new(200, result, {})
|
472
|
+
)
|
473
|
+
expect(@oauth.get_access_token_info(@code)).to eq({
|
474
|
+
"access_token" => "foo",
|
475
|
+
"expires_in" => "baz",
|
476
|
+
"machine_id" => "bar"
|
477
|
+
})
|
478
|
+
end
|
479
|
+
|
452
480
|
if KoalaTest.code
|
453
481
|
it "properly gets and parses an access token token results into a hash" do
|
454
482
|
result = @oauth.get_access_token_info(@code)
|
@@ -328,7 +328,7 @@ describe "Koala::Facebook::TestUsers" do
|
|
328
328
|
perms = ["read_stream", "offline_access"]
|
329
329
|
installed = false
|
330
330
|
count = 25
|
331
|
-
expect(@test_users).to receive(:create).exactly(count).times.with(installed, perms, anything)
|
331
|
+
expect(@test_users).to receive(:create).exactly(count).times.with(installed, perms, anything, anything)
|
332
332
|
allow(@test_users).to receive(:befriend)
|
333
333
|
@test_users.create_network(count, installed, perms)
|
334
334
|
end
|
@@ -336,7 +336,7 @@ describe "Koala::Facebook::TestUsers" do
|
|
336
336
|
it "accepts http options that are passed to both the create and befriend calls" do
|
337
337
|
count = 25
|
338
338
|
options = {:some_http_option => true}
|
339
|
-
expect(@test_users).to receive(:create).exactly(count).times.with(anything, anything, options).and_return({})
|
339
|
+
expect(@test_users).to receive(:create).exactly(count).times.with(anything, anything, anything, options).and_return({})
|
340
340
|
# there are more befriends than creates, but we don't need to do the extra work to calculate out the exact #
|
341
341
|
expect(@test_users).to receive(:befriend).at_least(count).times.with(anything, anything, options)
|
342
342
|
@test_users.create_network(count, true, "", options)
|
@@ -59,4 +59,7 @@ subscription_test_data:
|
|
59
59
|
challenge_data:
|
60
60
|
"hub.challenge": "1290024882"
|
61
61
|
"hub.verify_token": "myverificationtoken|1f54545d5f722733e17faae15377928f"
|
62
|
-
"hub.mode": "subscribe"
|
62
|
+
"hub.mode": "subscribe"
|
63
|
+
|
64
|
+
vcr_data:
|
65
|
+
oauth_token: CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
@@ -0,0 +1,121 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/v2.2/me/friends?access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.9.0
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Facebook-Api-Version:
|
22
|
+
- v2.2
|
23
|
+
Etag:
|
24
|
+
- "\"405645cb180f38f9c1e373a1fe301c47c2a763db\""
|
25
|
+
Content-Type:
|
26
|
+
- application/json; charset=UTF-8
|
27
|
+
Pragma:
|
28
|
+
- no-cache
|
29
|
+
Access-Control-Allow-Origin:
|
30
|
+
- "*"
|
31
|
+
X-Fb-Rev:
|
32
|
+
- '1543964'
|
33
|
+
Cache-Control:
|
34
|
+
- private, no-cache, no-store, must-revalidate
|
35
|
+
Expires:
|
36
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
37
|
+
X-Fb-Debug:
|
38
|
+
- sR8XVZI7FA7B7iZBo18REPDIaoeLkIbkg06WTYKMmJ/wzV/Gr5wYOYE47wIIgvnLBMgTe2D3RKvGo668AMWYXw==
|
39
|
+
Date:
|
40
|
+
- Fri, 26 Dec 2014 14:54:14 GMT
|
41
|
+
Connection:
|
42
|
+
- keep-alive
|
43
|
+
Content-Length:
|
44
|
+
- '834'
|
45
|
+
body:
|
46
|
+
encoding: UTF-8
|
47
|
+
string: "{\"data\":[{\"name\":\"Brian Rosenthal\",\"id\":\"6580\"},{\"name\":\"Waynn
|
48
|
+
Lue\",\"id\":\"202035\"},{\"name\":\"Chris Baclig\",\"id\":\"216743\"},{\"name\":\"Scott
|
49
|
+
Kleper\",\"id\":\"219493\"},{\"name\":\"Nick Elser\",\"id\":\"429772\"},{\"name\":\"Ian
|
50
|
+
Spivey\",\"id\":\"700103\"},{\"name\":\"Hong Dave\",\"id\":\"1215057\"},{\"name\":\"Trek
|
51
|
+
Glowacki\",\"id\":\"2203865\"},{\"name\":\"Tung Bach Ly\",\"id\":\"2803850\"},{\"name\":\"Isaac
|
52
|
+
Wolkerstorfer\",\"id\":\"2900100\"},{\"name\":\"Avishai Weiss\",\"id\":\"2900461\"},{\"name\":\"Harold
|
53
|
+
Liss\",\"id\":\"2900570\"},{\"name\":\"Ryo Chijiiwa\",\"id\":\"2901026\"},{\"name\":\"Luke
|
54
|
+
Shepard\",\"id\":\"2901279\"},{\"name\":\"Daniela Wellisz\",\"id\":\"2903998\"},{\"name\":\"Daniel
|
55
|
+
Hirschberg\",\"id\":\"2904437\"},{\"name\":\"Borja Sotomayor\",\"id\":\"2905626\"},{\"name\":\"David
|
56
|
+
Underwood\",\"id\":\"2906370\"},{\"name\":\"Avi Schwab\",\"id\":\"2908086\"},{\"name\":\"Kathleen
|
57
|
+
Burnett\",\"id\":\"6827168\"},{\"name\":\"Solomon Hursey\",\"id\":\"6857763\"},{\"name\":\"Sarah
|
58
|
+
Allen\",\"id\":\"10100146\"},{\"name\":\"Zach Wolfenbarger\",\"id\":\"10115065\"},{\"name\":\"JT
|
59
|
+
Archie\",\"id\":\"10603565\"},{\"name\":\"Colin McCloskey\",\"id\":\"11308679\"}],\"paging\":{\"next\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&limit=25&offset=25&__after_id=enc_Aez9Lwck5wLvRR_mHYNCzYNGmnYunJfnA3Z8hYXQ0xOluJfy4KVv4YYD1I4Im036hrA\"},\"summary\":{\"total_count\":821}}"
|
60
|
+
http_version:
|
61
|
+
recorded_at: Fri, 26 Dec 2014 14:54:14 GMT
|
62
|
+
- request:
|
63
|
+
method: get
|
64
|
+
uri: https://graph.facebook.com/v2.2/2905623/friends?__after_id=enc_Aez9Lwck5wLvRR_mHYNCzYNGmnYunJfnA3Z8hYXQ0xOluJfy4KVv4YYD1I4Im036hrA&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&limit=25&offset=25
|
65
|
+
body:
|
66
|
+
encoding: US-ASCII
|
67
|
+
string: ''
|
68
|
+
headers:
|
69
|
+
User-Agent:
|
70
|
+
- Faraday v0.9.0
|
71
|
+
Accept-Encoding:
|
72
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
73
|
+
Accept:
|
74
|
+
- "*/*"
|
75
|
+
response:
|
76
|
+
status:
|
77
|
+
code: 200
|
78
|
+
message: OK
|
79
|
+
headers:
|
80
|
+
Facebook-Api-Version:
|
81
|
+
- v2.2
|
82
|
+
Etag:
|
83
|
+
- "\"ad597c7482d414a109407197c49d9c102b245fec\""
|
84
|
+
Content-Type:
|
85
|
+
- application/json; charset=UTF-8
|
86
|
+
Pragma:
|
87
|
+
- no-cache
|
88
|
+
Access-Control-Allow-Origin:
|
89
|
+
- "*"
|
90
|
+
X-Fb-Rev:
|
91
|
+
- '1543964'
|
92
|
+
Cache-Control:
|
93
|
+
- private, no-cache, no-store, must-revalidate
|
94
|
+
Expires:
|
95
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
96
|
+
X-Fb-Debug:
|
97
|
+
- 4rfPGqFD0OuxktinjEn9le7gO3aRg+E1a6367E4h3cFk3O/vsYMACfGJFCAgsLakTiOMJaaNj4ep+CkgFPnFiw==
|
98
|
+
Date:
|
99
|
+
- Fri, 26 Dec 2014 14:54:15 GMT
|
100
|
+
Connection:
|
101
|
+
- keep-alive
|
102
|
+
Content-Length:
|
103
|
+
- '972'
|
104
|
+
body:
|
105
|
+
encoding: UTF-8
|
106
|
+
string: "{\"data\":[{\"name\":\"Andrew Kane\",\"id\":\"12460925\"},{\"name\":\"Robin
|
107
|
+
Greenwood\",\"id\":\"34602916\"},{\"name\":\"Tom Elliott\",\"id\":\"36902548\"},{\"name\":\"Brian
|
108
|
+
Maula\",\"id\":\"77800072\"},{\"name\":\"Brian Wis\",\"id\":\"78500422\"},{\"name\":\"Ryan
|
109
|
+
Merket\",\"id\":\"136300045\"},{\"name\":\"Alex Coles\",\"id\":\"202905335\"},{\"name\":\"Chris
|
110
|
+
Vincent\",\"id\":\"205300220\"},{\"name\":\"Craig Stoe\",\"id\":\"500388531\"},{\"name\":\"Alex
|
111
|
+
Neill\",\"id\":\"504120418\"},{\"name\":\"Riste\\u00e1rd Br\\u00e9anainn\",\"id\":\"10152350043011181\"},{\"name\":\"Rafi
|
112
|
+
Jacoby\",\"id\":\"518938454\"},{\"name\":\"Marc Blinder\",\"id\":\"519467425\"},{\"name\":\"Erik
|
113
|
+
Ogan\",\"id\":\"542404932\"},{\"name\":\"Nigel Benito Griffin\",\"id\":\"566130340\"},{\"name\":\"Karla
|
114
|
+
Geci\",\"id\":\"569882615\"},{\"name\":\"Dennis Schneider\",\"id\":\"582757122\"},{\"name\":\"Chad
|
115
|
+
Krsek\",\"id\":\"605431196\"},{\"name\":\"Simon Cross\",\"id\":\"605665581\"},{\"name\":\"Matt
|
116
|
+
Patterson\",\"id\":\"629514342\"},{\"name\":\"Adeel Ahmad\",\"id\":\"630665156\"},{\"name\":\"Nicholas
|
117
|
+
Solter\",\"id\":\"647473136\"},{\"name\":\"David Morcillo Mu\\u00f1oz\",\"id\":\"674832191\"},{\"name\":\"Ana
|
118
|
+
Luisa Santos\",\"id\":\"685010072\"},{\"name\":\"Matthias Wiemann\",\"id\":\"693274202\"}],\"paging\":{\"next\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?limit=25&offset=50&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&__after_id=enc_Aez79XsNc6mXAfVDW2zvCkfHWTRTl_YprQ2VgrtLMqPa71dSOCoC34H-6YHCCNonr-w\",\"previous\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?limit=25&offset=0&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&__before_id=enc_Aey0OlxZalVBlh623QavOqlYyEOlclWjZMQ_7Hv2x4BHFV_JzG9H2j-_nOikaW5PsyY\"},\"summary\":{\"total_count\":821}}"
|
119
|
+
http_version:
|
120
|
+
recorded_at: Fri, 26 Dec 2014 14:54:15 GMT
|
121
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Koala
|
4
|
+
RSpec.describe "requests using GraphCollections" do
|
5
|
+
let(:api) { Facebook::API.new(KoalaTest.vcr_oauth_token) }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
# use the right version of the API as of the writing of this test
|
9
|
+
Koala.config.api_version = "v2.2"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can access the next page of a friend list" do
|
13
|
+
KoalaTest.with_vcr_unless_live("friend_list_next_page") do
|
14
|
+
result = api.get_connection("me", "friends")
|
15
|
+
expect(result).not_to be_empty
|
16
|
+
expect(result.next_page).not_to be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/spec/support/koala_test.rb
CHANGED
@@ -5,6 +5,7 @@ module KoalaTest
|
|
5
5
|
attr_accessor :oauth_token, :app_id, :secret, :app_access_token, :code, :session_key
|
6
6
|
attr_accessor :oauth_test_data, :subscription_test_data, :search_time
|
7
7
|
attr_accessor :test_user_api
|
8
|
+
attr_accessor :vcr_oauth_token
|
8
9
|
end
|
9
10
|
|
10
11
|
# Test setup
|
@@ -12,31 +13,13 @@ module KoalaTest
|
|
12
13
|
def self.setup_test_environment!
|
13
14
|
setup_rspec
|
14
15
|
|
15
|
-
|
16
|
-
# By default the Koala specs are run using stubs for HTTP requests,
|
17
|
-
# so they won't fail due to Facebook-imposed rate limits or server timeouts.
|
18
|
-
#
|
19
|
-
# However as a result they are more brittle since
|
20
|
-
# we are not testing the latest responses from the Facebook servers.
|
21
|
-
# To be certain all specs pass with the current Facebook services,
|
22
|
-
# run LIVE=true bundle exec rake spec.
|
23
|
-
Koala.http_service = Koala::MockHTTPService
|
24
|
-
KoalaTest.setup_test_data(Koala::MockHTTPService::TEST_DATA)
|
25
|
-
else
|
16
|
+
if live?
|
26
17
|
# Runs Koala specs through the Facebook servers
|
27
18
|
# using data for a real app
|
28
19
|
live_data = YAML.load_file(File.join(File.dirname(__FILE__), '../fixtures/facebook_data.yml'))
|
29
20
|
KoalaTest.setup_test_data(live_data)
|
30
21
|
|
31
|
-
|
32
|
-
adapter = ENV['ADAPTER'] || "typhoeus" # use Typhoeus by default if available
|
33
|
-
begin
|
34
|
-
require adapter
|
35
|
-
require 'typhoeus/adapters/faraday' if adapter.to_s == "typhoeus"
|
36
|
-
Faraday.default_adapter = adapter.to_sym
|
37
|
-
rescue LoadError
|
38
|
-
puts "Unable to load adapter #{adapter}, using Net::HTTP."
|
39
|
-
end
|
22
|
+
activate_adapter!
|
40
23
|
|
41
24
|
Koala.http_service.http_options[:beta] = true if ENV["beta"] || ENV["BETA"]
|
42
25
|
|
@@ -46,6 +29,17 @@ module KoalaTest
|
|
46
29
|
else
|
47
30
|
KoalaTest.validate_user_info(token)
|
48
31
|
end
|
32
|
+
else
|
33
|
+
# By default the Koala specs are run using stubs for HTTP requests,
|
34
|
+
# so they won't fail due to Facebook-imposed rate limits or server timeouts.
|
35
|
+
#
|
36
|
+
# However as a result they are more brittle since
|
37
|
+
# we are not testing the latest responses from the Facebook servers.
|
38
|
+
# To be certain all specs pass with the current Facebook services,
|
39
|
+
# run LIVE=true bundle exec rake spec.
|
40
|
+
activate_vcr!
|
41
|
+
Koala.http_service = Koala::MockHTTPService
|
42
|
+
KoalaTest.setup_test_data(Koala::MockHTTPService::TEST_DATA)
|
49
43
|
end
|
50
44
|
end
|
51
45
|
|
@@ -79,6 +73,28 @@ module KoalaTest
|
|
79
73
|
end
|
80
74
|
end
|
81
75
|
|
76
|
+
# If we're running live tests against the Facebook API, don't use the VCR
|
77
|
+
# fixtures. (Another alternative would be to rerecord those fixtures, but we
|
78
|
+
# don't necessarily want to do that.
|
79
|
+
def self.with_vcr_unless_live(name)
|
80
|
+
if live?
|
81
|
+
yield
|
82
|
+
else
|
83
|
+
begin
|
84
|
+
# if we're using VCR, we don't want to use the mock service, which was
|
85
|
+
# an early implementation of the same type of tool
|
86
|
+
old_adapter = Koala.http_service
|
87
|
+
Koala.http_service = Koala::HTTPService
|
88
|
+
activate_adapter!
|
89
|
+
VCR.use_cassette(name) do
|
90
|
+
yield
|
91
|
+
end
|
92
|
+
ensure
|
93
|
+
Koala.http_service = old_adapter
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
82
98
|
def self.setup_test_data(data)
|
83
99
|
# make data accessible to all our tests
|
84
100
|
self.oauth_test_data = data["oauth_test_data"]
|
@@ -90,12 +106,14 @@ module KoalaTest
|
|
90
106
|
self.code = data["oauth_test_data"]["code"]
|
91
107
|
self.session_key = data["oauth_test_data"]["session_key"]
|
92
108
|
|
109
|
+
self.vcr_oauth_token = data["vcr_data"]["oauth_token"]
|
110
|
+
|
93
111
|
# fix the search time so it can be used in the mock responses
|
94
112
|
self.search_time = data["search_time"] || (Time.now - 3600).to_s
|
95
113
|
end
|
96
114
|
|
97
115
|
def self.testing_permissions
|
98
|
-
"read_stream,
|
116
|
+
"read_stream, publish_actions, user_photos, user_videos, read_insights"
|
99
117
|
end
|
100
118
|
|
101
119
|
def self.setup_test_users
|
@@ -143,7 +161,7 @@ module KoalaTest
|
|
143
161
|
perms.each_pair do |perm, value|
|
144
162
|
if value == (perm == "read_insights" ? 1 : 0) # live testing depends on insights calls failing
|
145
163
|
puts "failed!\n" # put a new line after the print above
|
146
|
-
raise ArgumentError, "Your access token must have
|
164
|
+
raise ArgumentError, "Your access token must have #{testing_permissions.join(", ")}, and lack read_insights. You have: #{perms.inspect}"
|
147
165
|
end
|
148
166
|
end
|
149
167
|
puts "done!"
|
@@ -199,4 +217,36 @@ module KoalaTest
|
|
199
217
|
def self.app_properties
|
200
218
|
mock_interface? ? {"desktop" => 0} : {"description" => "A test framework for Koala and its users. (#{rand(10000).to_i})"}
|
201
219
|
end
|
220
|
+
|
221
|
+
def self.live?
|
222
|
+
ENV['LIVE']
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.activate_vcr!
|
226
|
+
require 'vcr'
|
227
|
+
|
228
|
+
VCR.configure do |c|
|
229
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
230
|
+
c.hook_into :webmock # or :fakeweb
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.activate_adapter!
|
235
|
+
unless @adapter_activation_attempted
|
236
|
+
# allow live tests with different adapters
|
237
|
+
adapter = ENV['ADAPTER'] || "typhoeus" # use Typhoeus by default if available
|
238
|
+
begin
|
239
|
+
# JRuby doesn't support typhoeus on Travis
|
240
|
+
unless defined? JRUBY_VERSION
|
241
|
+
require adapter
|
242
|
+
require 'typhoeus/adapters/faraday' if adapter.to_s == "typhoeus"
|
243
|
+
Faraday.default_adapter = adapter.to_sym
|
244
|
+
end
|
245
|
+
rescue LoadError
|
246
|
+
puts "Unable to load adapter #{adapter}, using Net::HTTP."
|
247
|
+
ensure
|
248
|
+
@adapter_activation_attempted = true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
202
252
|
end
|
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: koala
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0rc
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Koppel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: faraday
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: addressable
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
description: Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write
|
@@ -64,11 +64,11 @@ extra_rdoc_files:
|
|
64
64
|
- readme.md
|
65
65
|
- changelog.md
|
66
66
|
files:
|
67
|
-
-
|
68
|
-
-
|
69
|
-
-
|
70
|
-
-
|
71
|
-
-
|
67
|
+
- .autotest
|
68
|
+
- .gitignore
|
69
|
+
- .rspec
|
70
|
+
- .travis.yml
|
71
|
+
- .yardopts
|
72
72
|
- Gemfile
|
73
73
|
- Guardfile
|
74
74
|
- LICENSE
|
@@ -115,6 +115,8 @@ files:
|
|
115
115
|
- spec/fixtures/cat.m4v
|
116
116
|
- spec/fixtures/facebook_data.yml
|
117
117
|
- spec/fixtures/mock_facebook_responses.yml
|
118
|
+
- spec/fixtures/vcr_cassettes/friend_list_next_page.yml
|
119
|
+
- spec/integration/graph_collection_spec.rb
|
118
120
|
- spec/spec_helper.rb
|
119
121
|
- spec/support/custom_matchers.rb
|
120
122
|
- spec/support/graph_api_shared_examples.rb
|
@@ -128,25 +130,25 @@ licenses:
|
|
128
130
|
metadata: {}
|
129
131
|
post_install_message:
|
130
132
|
rdoc_options:
|
131
|
-
-
|
132
|
-
-
|
133
|
-
-
|
133
|
+
- --line-numbers
|
134
|
+
- --inline-source
|
135
|
+
- --title
|
134
136
|
- Koala
|
135
137
|
require_paths:
|
136
138
|
- lib
|
137
139
|
required_ruby_version: !ruby/object:Gem::Requirement
|
138
140
|
requirements:
|
139
|
-
- -
|
141
|
+
- - '>='
|
140
142
|
- !ruby/object:Gem::Version
|
141
143
|
version: '0'
|
142
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
145
|
requirements:
|
144
|
-
- -
|
146
|
+
- - '>'
|
145
147
|
- !ruby/object:Gem::Version
|
146
|
-
version:
|
148
|
+
version: 1.3.1
|
147
149
|
requirements: []
|
148
150
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.2.
|
151
|
+
rubygems_version: 2.2.2
|
150
152
|
signing_key:
|
151
153
|
specification_version: 4
|
152
154
|
summary: A lightweight, flexible library for Facebook with support for the Graph API,
|
@@ -171,6 +173,8 @@ test_files:
|
|
171
173
|
- spec/fixtures/cat.m4v
|
172
174
|
- spec/fixtures/facebook_data.yml
|
173
175
|
- spec/fixtures/mock_facebook_responses.yml
|
176
|
+
- spec/fixtures/vcr_cassettes/friend_list_next_page.yml
|
177
|
+
- spec/integration/graph_collection_spec.rb
|
174
178
|
- spec/spec_helper.rb
|
175
179
|
- spec/support/custom_matchers.rb
|
176
180
|
- spec/support/graph_api_shared_examples.rb
|