tyler_koala 1.2.0beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +12 -0
- data/.gitignore +5 -0
- data/.travis.yml +9 -0
- data/CHANGELOG +185 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/Manifest +39 -0
- data/Rakefile +16 -0
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +50 -0
- data/lib/koala.rb +119 -0
- data/lib/koala/batch_operation.rb +74 -0
- data/lib/koala/graph_api.rb +281 -0
- data/lib/koala/graph_batch_api.rb +87 -0
- data/lib/koala/graph_collection.rb +54 -0
- data/lib/koala/http_service.rb +161 -0
- data/lib/koala/oauth.rb +181 -0
- data/lib/koala/realtime_updates.rb +89 -0
- data/lib/koala/rest_api.rb +95 -0
- data/lib/koala/test_users.rb +102 -0
- data/lib/koala/uploadable_io.rb +180 -0
- data/lib/koala/utils.rb +7 -0
- data/readme.md +160 -0
- data/spec/cases/api_base_spec.rb +101 -0
- data/spec/cases/error_spec.rb +30 -0
- data/spec/cases/graph_and_rest_api_spec.rb +48 -0
- data/spec/cases/graph_api_batch_spec.rb +600 -0
- data/spec/cases/graph_api_spec.rb +42 -0
- data/spec/cases/http_service_spec.rb +420 -0
- data/spec/cases/koala_spec.rb +21 -0
- data/spec/cases/oauth_spec.rb +428 -0
- data/spec/cases/realtime_updates_spec.rb +198 -0
- data/spec/cases/rest_api_spec.rb +41 -0
- data/spec/cases/test_users_spec.rb +281 -0
- data/spec/cases/uploadable_io_spec.rb +206 -0
- data/spec/cases/utils_spec.rb +8 -0
- data/spec/fixtures/beach.jpg +0 -0
- data/spec/fixtures/cat.m4v +0 -0
- data/spec/fixtures/facebook_data.yml +61 -0
- data/spec/fixtures/mock_facebook_responses.yml +439 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/graph_api_shared_examples.rb +502 -0
- data/spec/support/json_testing_fix.rb +42 -0
- data/spec/support/koala_test.rb +163 -0
- data/spec/support/mock_http_service.rb +98 -0
- data/spec/support/ordered_hash.rb +205 -0
- data/spec/support/rest_api_shared_examples.rb +285 -0
- data/spec/support/uploadable_io_shared_examples.rb +70 -0
- metadata +221 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Koala::Facebook::API" do
|
4
|
+
before(:each) do
|
5
|
+
@service = Koala::Facebook::API.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not include an access token if none was given" do
|
9
|
+
Koala.should_receive(:make_request).with(
|
10
|
+
anything,
|
11
|
+
hash_not_including('access_token' => 1),
|
12
|
+
anything,
|
13
|
+
anything
|
14
|
+
).and_return(Koala::Response.new(200, "", ""))
|
15
|
+
|
16
|
+
@service.api('anything')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should include an access token if given" do
|
20
|
+
token = 'adfadf'
|
21
|
+
service = Koala::Facebook::API.new token
|
22
|
+
|
23
|
+
Koala.should_receive(:make_request).with(
|
24
|
+
anything,
|
25
|
+
hash_including('access_token' => token),
|
26
|
+
anything,
|
27
|
+
anything
|
28
|
+
).and_return(Koala::Response.new(200, "", ""))
|
29
|
+
|
30
|
+
service.api('anything')
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have an attr_reader for access token" do
|
34
|
+
token = 'adfadf'
|
35
|
+
service = Koala::Facebook::API.new token
|
36
|
+
service.access_token.should == token
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should get the attribute of a Koala::Response given by the http_component parameter" do
|
40
|
+
http_component = :method_name
|
41
|
+
|
42
|
+
response = mock('Mock KoalaResponse', :body => '', :status => 200)
|
43
|
+
response.should_receive(http_component).and_return('')
|
44
|
+
|
45
|
+
Koala.stub(:make_request).and_return(response)
|
46
|
+
|
47
|
+
@service.api('anything', {}, 'get', :http_component => http_component)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return the body of the request as JSON if no http_component is given" do
|
51
|
+
response = stub('response', :body => 'body', :status => 200)
|
52
|
+
Koala.stub(:make_request).and_return(response)
|
53
|
+
|
54
|
+
json_body = mock('JSON body')
|
55
|
+
MultiJson.stub(:decode).and_return([json_body])
|
56
|
+
|
57
|
+
@service.api('anything').should == json_body
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should execute an error checking block if provided" do
|
61
|
+
body = '{}'
|
62
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
|
63
|
+
|
64
|
+
yield_test = mock('Yield Tester')
|
65
|
+
yield_test.should_receive(:pass)
|
66
|
+
|
67
|
+
@service.api('anything', {}, "get") do |arg|
|
68
|
+
yield_test.pass
|
69
|
+
arg.should == MultiJson.decode(body)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should raise an API error if the HTTP response code is greater than or equal to 500" do
|
74
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
|
75
|
+
|
76
|
+
lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should handle rogue true/false as responses" do
|
80
|
+
Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
|
81
|
+
@service.api('anything').should be_true
|
82
|
+
|
83
|
+
Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
|
84
|
+
@service.api('anything').should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "with regard to leading slashes" do
|
88
|
+
it "should add a leading / to the path if not present" do
|
89
|
+
path = "anything"
|
90
|
+
Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
|
91
|
+
@service.api(path)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "shouldn't change the path if a leading / is present" do
|
95
|
+
path = "/anything"
|
96
|
+
Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
|
97
|
+
@service.api(path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
describe Koala::Facebook::APIError do
|
2
|
+
it "is a StandardError" do
|
3
|
+
Koala::Facebook::APIError.new.should be_a(StandardError)
|
4
|
+
end
|
5
|
+
|
6
|
+
it "has an accessor for fb_error_type" do
|
7
|
+
Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:fb_error_type)
|
8
|
+
Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:fb_error_type=)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "sets fb_error_type to details['type']" do
|
12
|
+
type = "foo"
|
13
|
+
Koala::Facebook::APIError.new("type" => type).fb_error_type.should == type
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets the error message details['type']: details['message']" do
|
17
|
+
type = "foo"
|
18
|
+
message = "bar"
|
19
|
+
error = Koala::Facebook::APIError.new("type" => type, "message" => message)
|
20
|
+
error.message.should =~ /#{type}/
|
21
|
+
error.message.should =~ /#{message}/
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe Koala::KoalaError do
|
26
|
+
it "is a StandardError" do
|
27
|
+
Koala::KoalaError.new.should be_a(StandardError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Koala::Facebook::GraphAndRestAPI" do
|
4
|
+
describe "class consolidation" do
|
5
|
+
before :each do
|
6
|
+
Koala::Utils.stub(:deprecate) # avoid actual messages to stderr
|
7
|
+
end
|
8
|
+
|
9
|
+
it "still allows you to instantiate a GraphAndRestAPI object" do
|
10
|
+
api = Koala::Facebook::GraphAndRestAPI.new("token").should be_a(Koala::Facebook::GraphAndRestAPI)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "ultimately creates an API object" do
|
14
|
+
api = Koala::Facebook::GraphAndRestAPI.new("token").should be_a(Koala::Facebook::API)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "fires a depreciation warning" do
|
18
|
+
Koala::Utils.should_receive(:deprecate)
|
19
|
+
api = Koala::Facebook::GraphAndRestAPI.new("token")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "with an access token" do
|
24
|
+
before(:each) do
|
25
|
+
@api = Koala::Facebook::API.new(@token)
|
26
|
+
end
|
27
|
+
|
28
|
+
it_should_behave_like "Koala RestAPI"
|
29
|
+
it_should_behave_like "Koala RestAPI with an access token"
|
30
|
+
|
31
|
+
it_should_behave_like "Koala GraphAPI"
|
32
|
+
it_should_behave_like "Koala GraphAPI with an access token"
|
33
|
+
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "without an access token" do
|
37
|
+
before(:each) do
|
38
|
+
@api = Koala::Facebook::API.new
|
39
|
+
end
|
40
|
+
|
41
|
+
it_should_behave_like "Koala RestAPI"
|
42
|
+
it_should_behave_like "Koala RestAPI without an access token"
|
43
|
+
|
44
|
+
it_should_behave_like "Koala GraphAPI"
|
45
|
+
it_should_behave_like "Koala GraphAPI without an access token"
|
46
|
+
it_should_behave_like "Koala GraphAPI with GraphCollection"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,600 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Koala::Facebook::GraphAPI in batch mode" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@api = Koala::Facebook::API.new(@token)
|
7
|
+
# app API
|
8
|
+
@app_id = KoalaTest.app_id
|
9
|
+
@app_access_token = KoalaTest.app_access_token
|
10
|
+
@app_api = Koala::Facebook::API.new(@app_access_token)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "BatchOperations" do
|
14
|
+
before :each do
|
15
|
+
@args = {
|
16
|
+
:url => "my url",
|
17
|
+
:args => {:a => 2, :b => 3},
|
18
|
+
:method => "get",
|
19
|
+
:access_token => "12345",
|
20
|
+
:http_options => {},
|
21
|
+
:post_processing => lambda { }
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#new" do
|
26
|
+
it "makes http_options accessible" do
|
27
|
+
Koala::Facebook::BatchOperation.new(@args).http_options.should == @args[:http_options]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "makes post_processing accessible" do
|
31
|
+
Koala::Facebook::BatchOperation.new(@args).post_processing.should == @args[:post_processing]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "makes access_token accessible" do
|
35
|
+
Koala::Facebook::BatchOperation.new(@args).access_token.should == @args[:access_token]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "doesn't change the original http_options" do
|
39
|
+
@args[:http_options][:name] = "baz2"
|
40
|
+
expected = @args[:http_options].dup
|
41
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
|
42
|
+
@args[:http_options].should == expected
|
43
|
+
end
|
44
|
+
|
45
|
+
it "leaves the file array nil by default" do
|
46
|
+
Koala::Facebook::BatchOperation.new(@args).files.should be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "raises a KoalaError if no access token supplied" do
|
50
|
+
expect { Koala::Facebook::BatchOperation.new(@args.merge(:access_token => nil)) }.to raise_exception(Koala::KoalaError)
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "when supplied binary files" do
|
54
|
+
before :each do
|
55
|
+
@binary = stub("Binary file")
|
56
|
+
@uploadable_io = stub("UploadableIO 1")
|
57
|
+
|
58
|
+
@batch_queue = []
|
59
|
+
Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
|
60
|
+
|
61
|
+
Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
|
62
|
+
Koala::UploadableIO.stub(:binary_content?).and_return(false)
|
63
|
+
Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
|
64
|
+
Koala::UploadableIO.stub(:binary_content?).with(@uploadable_io).and_return(true)
|
65
|
+
@uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
|
66
|
+
|
67
|
+
@args[:method] = "post" # files are always post
|
68
|
+
end
|
69
|
+
|
70
|
+
it "adds binary files to the files attribute as UploadableIOs" do
|
71
|
+
@args[:args].merge!("source" => @binary)
|
72
|
+
batch_op = Koala::Facebook::BatchOperation.new(@args)
|
73
|
+
batch_op.files.should_not be_nil
|
74
|
+
batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it "works if supplied an UploadableIO as an argument" do
|
78
|
+
# as happens with put_picture at the moment
|
79
|
+
@args[:args].merge!("source" => @uploadable_io)
|
80
|
+
batch_op = Koala::Facebook::BatchOperation.new(@args)
|
81
|
+
batch_op.files.should_not be_nil
|
82
|
+
batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it "assigns each binary parameter unique name" do
|
86
|
+
@args[:args].merge!("source" => @binary, "source2" => @binary)
|
87
|
+
batch_op = Koala::Facebook::BatchOperation.new(@args)
|
88
|
+
# if the name wasn't unique, there'd just be one item
|
89
|
+
batch_op.files.should have(2).items
|
90
|
+
end
|
91
|
+
|
92
|
+
it "assigns each binary parameter unique name across batch requests" do
|
93
|
+
@args[:args].merge!("source" => @binary, "source2" => @binary)
|
94
|
+
batch_op = Koala::Facebook::BatchOperation.new(@args)
|
95
|
+
# simulate the batch operation, since it's used in determination
|
96
|
+
@batch_queue << batch_op
|
97
|
+
batch_op2 = Koala::Facebook::BatchOperation.new(@args)
|
98
|
+
@batch_queue << batch_op2
|
99
|
+
# if the name wasn't unique, we should have < 4 items since keys would be the same
|
100
|
+
batch_op.files.merge(batch_op2.files).should have(4).items
|
101
|
+
end
|
102
|
+
|
103
|
+
it "removes the value from the arguments" do
|
104
|
+
@args[:args].merge!("source" => @binary)
|
105
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should_not =~ /source=/
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ".to_batch_params" do
|
112
|
+
describe "handling arguments and URLs" do
|
113
|
+
shared_examples_for "request with no body" do
|
114
|
+
it "adds the args to the URL string, with ? if no args previously present" do
|
115
|
+
test_args = "foo"
|
116
|
+
@args[:url] = url = "/"
|
117
|
+
Koala.http_service.stub(:encode_params).and_return(test_args)
|
118
|
+
|
119
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}?#{test_args}"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "adds the args to the URL string, with & if args previously present" do
|
123
|
+
test_args = "foo"
|
124
|
+
@args[:url] = url = "/?a=2"
|
125
|
+
Koala.http_service.stub(:encode_params).and_return(test_args)
|
126
|
+
|
127
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}&#{test_args}"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "adds nothing to the URL string if there are no args to be added" do
|
131
|
+
@args[:args] = {}
|
132
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])[:relative_url].should == @args[:url]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "adds nothing to the body" do
|
136
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
shared_examples_for "requests with a body param" do
|
141
|
+
it "sets the body to the encoded args string, if there are args" do
|
142
|
+
test_args = "foo"
|
143
|
+
Koala.http_service.stub(:encode_params).and_return(test_args)
|
144
|
+
|
145
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should == test_args
|
146
|
+
end
|
147
|
+
|
148
|
+
it "does not set the body if there are no args" do
|
149
|
+
test_args = ""
|
150
|
+
Koala.http_service.stub(:encode_params).and_return(test_args)
|
151
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
it "doesn't change the url" do
|
156
|
+
test_args = "foo"
|
157
|
+
Koala.http_service.stub(:encode_params).and_return(test_args)
|
158
|
+
|
159
|
+
Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == @args[:url]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "for get operations" do
|
164
|
+
before :each do
|
165
|
+
@args[:method] = :get
|
166
|
+
end
|
167
|
+
|
168
|
+
it_should_behave_like "request with no body"
|
169
|
+
end
|
170
|
+
|
171
|
+
context "for delete operations" do
|
172
|
+
before :each do
|
173
|
+
@args[:method] = :delete
|
174
|
+
end
|
175
|
+
|
176
|
+
it_should_behave_like "request with no body"
|
177
|
+
end
|
178
|
+
|
179
|
+
context "for get operations" do
|
180
|
+
before :each do
|
181
|
+
@args[:method] = :put
|
182
|
+
end
|
183
|
+
|
184
|
+
it_should_behave_like "requests with a body param"
|
185
|
+
end
|
186
|
+
|
187
|
+
context "for delete operations" do
|
188
|
+
before :each do
|
189
|
+
@args[:method] = :post
|
190
|
+
end
|
191
|
+
|
192
|
+
it_should_behave_like "requests with a body param"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it "includes the access token if the token is not the main one for the request" do
|
197
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
|
198
|
+
params[:relative_url].should =~ /access_token=#{@args[:access_token]}/
|
199
|
+
end
|
200
|
+
|
201
|
+
it "includes the other arguments if the token is not the main one for the request" do
|
202
|
+
@args[:args] = {:a => 2}
|
203
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
|
204
|
+
params[:relative_url].should =~ /a=2/
|
205
|
+
end
|
206
|
+
|
207
|
+
it "does not include the access token if the token is the main one for the request" do
|
208
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
|
209
|
+
params[:relative_url].should_not =~ /access_token=#{@args[:access_token]}/
|
210
|
+
end
|
211
|
+
|
212
|
+
it "includes the other arguments if the token is the main one for the request" do
|
213
|
+
@args[:args] = {:a => 2}
|
214
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
|
215
|
+
params[:relative_url].should =~ /a=2/
|
216
|
+
end
|
217
|
+
|
218
|
+
it "includes any arguments passed as http_options[:batch_args]" do
|
219
|
+
batch_args = {:name => "baz", :headers => {:some_param => true}}
|
220
|
+
@args[:http_options][:batch_args] = batch_args
|
221
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
|
222
|
+
params.should include(batch_args)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "includes the method" do
|
226
|
+
params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
|
227
|
+
params[:method].should == @args[:method].to_s
|
228
|
+
end
|
229
|
+
|
230
|
+
it "works with nil http_options" do
|
231
|
+
expect { Koala::Facebook::BatchOperation.new(@args.merge(:http_options => nil)).to_batch_params(nil) }.not_to raise_exception
|
232
|
+
end
|
233
|
+
|
234
|
+
it "works with nil args" do
|
235
|
+
expect { Koala::Facebook::BatchOperation.new(@args.merge(:args => nil)).to_batch_params(nil) }.not_to raise_exception
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "with binary files" do
|
239
|
+
before :each do
|
240
|
+
@binary = stub("Binary file")
|
241
|
+
Koala::UploadableIO.stub(:binary_content?).and_return(false)
|
242
|
+
Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
|
243
|
+
@uploadable_io = stub("UploadableIO")
|
244
|
+
Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
|
245
|
+
@uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
|
246
|
+
|
247
|
+
@batch_queue = []
|
248
|
+
Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
|
249
|
+
|
250
|
+
@args[:method] = "post" # files are always post
|
251
|
+
end
|
252
|
+
|
253
|
+
it "adds file identifiers as attached_files in a comma-separated list" do
|
254
|
+
@args[:args].merge!("source" => @binary, "source2" => @binary)
|
255
|
+
batch_op = Koala::Facebook::BatchOperation.new(@args)
|
256
|
+
file_ids = batch_op.files.find_all {|k, v| v == @uploadable_io}.map {|k, v| k}
|
257
|
+
params = batch_op.to_batch_params(nil)
|
258
|
+
params[:attached_files].should == file_ids.join(",")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "GraphAPI batch interface" do
|
266
|
+
|
267
|
+
it "returns nothing for a batch operation" do
|
268
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, "[]", {}))
|
269
|
+
@api.batch do |batch_api|
|
270
|
+
batch_api.get_object('me').should be_nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "#batch" do
|
275
|
+
before :each do
|
276
|
+
@fake_response = Koala::Response.new(200, "[]", {})
|
277
|
+
Koala.stub(:make_request).and_return(@fake_response)
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "making the request" do
|
281
|
+
context "with no calls" do
|
282
|
+
it "does not make any requests if batch_calls is empty" do
|
283
|
+
Koala.should_not_receive(:make_request)
|
284
|
+
@api.batch {|batch_api|}
|
285
|
+
end
|
286
|
+
|
287
|
+
it "returns []" do
|
288
|
+
@api.batch {|batch_api|}.should == []
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
it "includes the first operation's access token as the main one in the args" do
|
293
|
+
access_token = "foo"
|
294
|
+
Koala.should_receive(:make_request).with(anything, hash_including("access_token" => access_token), anything, anything).and_return(@fake_response)
|
295
|
+
Koala::Facebook::API.new(access_token).batch do |batch_api|
|
296
|
+
batch_api.get_object('me')
|
297
|
+
batch_api.get_object('me', {}, {'access_token' => 'bar'})
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
it "sets args['batch'] to a json'd map of all the batch params" do
|
302
|
+
access_token = "bar"
|
303
|
+
op = Koala::Facebook::BatchOperation.new(:access_token => access_token, :method => :get, :url => "/")
|
304
|
+
op.stub(:to_batch_params).and_return({:a => 2})
|
305
|
+
Koala::Facebook::BatchOperation.stub(:new).and_return(op)
|
306
|
+
|
307
|
+
# two requests should generate two batch operations
|
308
|
+
expected = MultiJson.encode([op.to_batch_params(access_token), op.to_batch_params(access_token)])
|
309
|
+
Koala.should_receive(:make_request).with(anything, hash_including("batch" => expected), anything, anything).and_return(@fake_response)
|
310
|
+
Koala::Facebook::API.new(access_token).batch do |batch_api|
|
311
|
+
batch_api.get_object('me')
|
312
|
+
batch_api.get_object('me')
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
it "adds any files from the batch operations to the arguments" do
|
317
|
+
# stub the batch operation
|
318
|
+
# we test above to ensure that files are properly assimilated into the BatchOperation instance
|
319
|
+
# right now, we want to make sure that batch_api handles them properly
|
320
|
+
@key = "file0_0"
|
321
|
+
@uploadable_io = stub("UploadableIO")
|
322
|
+
batch_op = stub("Koala Batch Operation", :files => {@key => @uploadable_io}, :to_batch_params => {}, :access_token => "foo")
|
323
|
+
Koala::Facebook::BatchOperation.stub(:new).and_return(batch_op)
|
324
|
+
|
325
|
+
Koala.should_receive(:make_request).with(anything, hash_including(@key => @uploadable_io), anything, anything).and_return(@fake_response)
|
326
|
+
Koala::Facebook::API.new("bar").batch do |batch_api|
|
327
|
+
batch_api.put_picture("path/to/file", "image/jpeg")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
it "preserves operation order" do
|
332
|
+
access_token = "bar"
|
333
|
+
# two requests should generate two batch operations
|
334
|
+
Koala.should_receive(:make_request) do |url, args, method, options|
|
335
|
+
# test the batch operations to make sure they appear in the right order
|
336
|
+
(args ||= {})["batch"].should =~ /.*me\/farglebarg.*otheruser\/bababa/
|
337
|
+
@fake_response
|
338
|
+
end
|
339
|
+
Koala::Facebook::API.new(access_token).batch do |batch_api|
|
340
|
+
batch_api.get_connections('me', "farglebarg")
|
341
|
+
batch_api.get_connections('otheruser', "bababa")
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
it "makes a POST request" do
|
346
|
+
Koala.should_receive(:make_request).with(anything, anything, "post", anything).and_return(@fake_response)
|
347
|
+
Koala::Facebook::API.new("foo").batch do |batch_api|
|
348
|
+
batch_api.get_object('me')
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
it "makes a request to /" do
|
353
|
+
Koala.should_receive(:make_request).with("/", anything, anything, anything).and_return(@fake_response)
|
354
|
+
Koala::Facebook::API.new("foo").batch do |batch_api|
|
355
|
+
batch_api.get_object('me')
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
it "includes any http options specified at the top level" do
|
360
|
+
http_options = {"a" => "baz"}
|
361
|
+
Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(http_options)).and_return(@fake_response)
|
362
|
+
Koala::Facebook::API.new("foo").batch(http_options) do |batch_api|
|
363
|
+
batch_api.get_object('me')
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
describe "processing the request" do
|
369
|
+
it "throws an error if the response is not 200" do
|
370
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(500, "[]", {}))
|
371
|
+
expect { Koala::Facebook::API.new("foo").batch do |batch_api|
|
372
|
+
batch_api.get_object('me')
|
373
|
+
end }.to raise_exception(Koala::Facebook::APIError)
|
374
|
+
end
|
375
|
+
|
376
|
+
it "throws an error if the response is a Batch API-style error" do
|
377
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, '{"error":190,"error_description":"Error validating access token."}', {}))
|
378
|
+
expect { Koala::Facebook::API.new("foo").batch do |batch_api|
|
379
|
+
batch_api.get_object('me')
|
380
|
+
end }.to raise_exception(Koala::Facebook::APIError)
|
381
|
+
end
|
382
|
+
|
383
|
+
it "returns the result status if http_component is status" do
|
384
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
|
385
|
+
result = @api.batch do |batch_api|
|
386
|
+
batch_api.get_object(KoalaTest.user1, {}, :http_component => :status)
|
387
|
+
end
|
388
|
+
result[0].should == 203
|
389
|
+
end
|
390
|
+
|
391
|
+
it "returns the result headers as a hash if http_component is headers" do
|
392
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
|
393
|
+
result = @api.batch do |batch_api|
|
394
|
+
batch_api.get_object(KoalaTest.user1, {}, :http_component => :headers)
|
395
|
+
end
|
396
|
+
result[0].should == {"Content-Type" => "text/javascript; charset=UTF-8"}
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
it "is thread safe" do
|
401
|
+
# ensure batch operations on one thread don't affect those on another
|
402
|
+
thread_one_count = 0
|
403
|
+
thread_two_count = 0
|
404
|
+
first_count = 20
|
405
|
+
second_count = 10
|
406
|
+
|
407
|
+
Koala.stub(:make_request).and_return(@fake_response)
|
408
|
+
|
409
|
+
thread1 = Thread.new do
|
410
|
+
@api.batch do |batch_api|
|
411
|
+
first_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
|
412
|
+
thread_one_count = batch_api.batch_calls.count
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
thread2 = Thread.new do
|
417
|
+
@api.batch do |batch_api|
|
418
|
+
second_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
|
419
|
+
thread_two_count = batch_api.batch_calls.count
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
thread1.join
|
424
|
+
thread2.join
|
425
|
+
|
426
|
+
thread_one_count.should == first_count
|
427
|
+
thread_two_count.should == second_count
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
describe "usage tests" do
|
433
|
+
it "can get two results at once" do
|
434
|
+
me, koppel = @api.batch do |batch_api|
|
435
|
+
batch_api.get_object('me')
|
436
|
+
batch_api.get_object(KoalaTest.user1)
|
437
|
+
end
|
438
|
+
me['id'].should_not be_nil
|
439
|
+
koppel['id'].should_not be_nil
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'should be able to make mixed calls inside of a batch' do
|
443
|
+
me, friends = @api.batch do |batch_api|
|
444
|
+
batch_api.get_object('me')
|
445
|
+
batch_api.get_connections('me', 'friends')
|
446
|
+
end
|
447
|
+
me['id'].should_not be_nil
|
448
|
+
friends.should be_an(Array)
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'should be able to make a get_picture call inside of a batch' do
|
452
|
+
pictures = @api.batch do |batch_api|
|
453
|
+
batch_api.get_picture('me')
|
454
|
+
end
|
455
|
+
pictures.first.should_not be_empty
|
456
|
+
end
|
457
|
+
|
458
|
+
it "should handle requests for two different tokens" do
|
459
|
+
me, insights = @api.batch do |batch_api|
|
460
|
+
batch_api.get_object('me')
|
461
|
+
batch_api.get_connections(@app_id, 'insights', {}, {"access_token" => @app_api.access_token})
|
462
|
+
end
|
463
|
+
me['id'].should_not be_nil
|
464
|
+
insights.should be_an(Array)
|
465
|
+
end
|
466
|
+
|
467
|
+
it "inserts errors in the appropriate place, without breaking other results" do
|
468
|
+
failed_insights, koppel = @api.batch do |batch_api|
|
469
|
+
batch_api.get_connections(@app_id, 'insights')
|
470
|
+
batch_api.get_object(KoalaTest.user1, {}, {"access_token" => @app_api.access_token})
|
471
|
+
end
|
472
|
+
failed_insights.should be_a(Koala::Facebook::APIError)
|
473
|
+
koppel["id"].should_not be_nil
|
474
|
+
end
|
475
|
+
|
476
|
+
it "handles different request methods" do
|
477
|
+
result = @api.put_wall_post("Hello, world, from the test suite batch API!")
|
478
|
+
wall_post = result["id"]
|
479
|
+
|
480
|
+
wall_post, koppel = @api.batch do |batch_api|
|
481
|
+
batch_api.put_like(wall_post)
|
482
|
+
batch_api.delete_object(wall_post)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
it "allows FQL" do
|
487
|
+
result = @api.batch do |batch_api|
|
488
|
+
batch_api.graph_call("method/fql.query", {:query=>"select first_name from user where uid=#{KoalaTest.user1_id}"}, "post")
|
489
|
+
end
|
490
|
+
|
491
|
+
fql_result = result[0]
|
492
|
+
fql_result[0].should be_a(Hash)
|
493
|
+
fql_result[0]["first_name"].should == "Alex"
|
494
|
+
end
|
495
|
+
|
496
|
+
describe "binary files" do
|
497
|
+
it "posts binary files" do
|
498
|
+
file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
|
499
|
+
|
500
|
+
Koala::Facebook::BatchOperation.instance_variable_set(:@identifier, 0)
|
501
|
+
result = @api.batch do |batch_api|
|
502
|
+
batch_api.put_picture(file)
|
503
|
+
end
|
504
|
+
|
505
|
+
@temporary_object_id = result[0]["id"]
|
506
|
+
@temporary_object_id.should_not be_nil
|
507
|
+
end
|
508
|
+
|
509
|
+
it "posts binary files with multiple requests" do
|
510
|
+
file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
|
511
|
+
file2 = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
|
512
|
+
|
513
|
+
Koala::Facebook::BatchOperation.instance_variable_set(:@identifier, 0)
|
514
|
+
results = @api.batch do |batch_api|
|
515
|
+
batch_api.put_picture(file)
|
516
|
+
batch_api.put_picture(file2, {}, KoalaTest.user1)
|
517
|
+
end
|
518
|
+
results[0]["id"].should_not be_nil
|
519
|
+
results[1]["id"].should_not be_nil
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
describe "relating requests" do
|
524
|
+
it "allows you create relationships between requests without omit_response_on_success" do
|
525
|
+
results = @api.batch do |batch_api|
|
526
|
+
batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends"})
|
527
|
+
batch_api.get_objects("{result=get-friends:$.data.*.id}")
|
528
|
+
end
|
529
|
+
|
530
|
+
results[0].should be_nil
|
531
|
+
results[1].should be_an(Hash)
|
532
|
+
end
|
533
|
+
|
534
|
+
it "allows you create relationships between requests with omit_response_on_success" do
|
535
|
+
results = @api.batch do |batch_api|
|
536
|
+
batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends", :omit_response_on_success => false})
|
537
|
+
batch_api.get_objects("{result=get-friends:$.data.*.id}")
|
538
|
+
end
|
539
|
+
|
540
|
+
results[0].should be_an(Array)
|
541
|
+
results[1].should be_an(Hash)
|
542
|
+
end
|
543
|
+
|
544
|
+
it "allows you to create dependencies" do
|
545
|
+
me, koppel = @api.batch do |batch_api|
|
546
|
+
batch_api.get_object("me", {}, :batch_args => {:name => "getme"})
|
547
|
+
batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getme"})
|
548
|
+
end
|
549
|
+
|
550
|
+
me.should be_nil # gotcha! it's omitted because it's a successfully-executed dependency
|
551
|
+
koppel["id"].should_not be_nil
|
552
|
+
end
|
553
|
+
|
554
|
+
it "properly handles dependencies that fail" do
|
555
|
+
data, koppel = @api.batch do |batch_api|
|
556
|
+
batch_api.get_connections(@app_id, 'insights', {}, :batch_args => {:name => "getdata"})
|
557
|
+
batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getdata"})
|
558
|
+
end
|
559
|
+
|
560
|
+
data.should be_a(Koala::Facebook::APIError)
|
561
|
+
koppel.should be_nil
|
562
|
+
end
|
563
|
+
|
564
|
+
it "throws an error for badly-constructed request relationships" do
|
565
|
+
expect {
|
566
|
+
@api.batch do |batch_api|
|
567
|
+
batch_api.get_connections("me", "friends", {:limit => 5})
|
568
|
+
batch_api.get_objects("{result=i-dont-exist:$.data.*.id}")
|
569
|
+
end
|
570
|
+
}.to raise_exception(Koala::Facebook::APIError)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
describe "new interface" do
|
576
|
+
it "includes a deprecation warning on GraphAPI" do
|
577
|
+
begin
|
578
|
+
Koala::Facebook::GraphAPI.batch do
|
579
|
+
end
|
580
|
+
rescue NoMethodError => @err
|
581
|
+
end
|
582
|
+
|
583
|
+
# verify the message points people to the wiki page
|
584
|
+
@err.should
|
585
|
+
@err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
|
586
|
+
end
|
587
|
+
|
588
|
+
it "includes a deprecation warning on GraphAndRESTAPI" do
|
589
|
+
begin
|
590
|
+
Koala::Facebook::GraphAndRestAPI.batch do
|
591
|
+
end
|
592
|
+
rescue NoMethodError => @err
|
593
|
+
end
|
594
|
+
|
595
|
+
# verify the message points people to the wiki page
|
596
|
+
@err.should
|
597
|
+
@err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|