koala 2.5.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/Gemfile +1 -1
  4. data/ISSUE_TEMPLATE +25 -0
  5. data/PULL_REQUEST_TEMPLATE +11 -0
  6. data/changelog.md +66 -4
  7. data/code_of_conduct.md +64 -12
  8. data/koala.gemspec +3 -0
  9. data/lib/koala/api/batch_operation.rb +3 -6
  10. data/lib/koala/api/{graph_api.rb → graph_api_methods.rb} +13 -102
  11. data/lib/koala/api/graph_batch_api.rb +112 -65
  12. data/lib/koala/api/graph_collection.rb +19 -12
  13. data/lib/koala/api/graph_error_checker.rb +1 -1
  14. data/lib/koala/api.rb +49 -25
  15. data/lib/koala/configuration.rb +49 -0
  16. data/lib/koala/errors.rb +1 -1
  17. data/lib/koala/http_service/multipart_request.rb +6 -10
  18. data/lib/koala/http_service/request.rb +135 -0
  19. data/lib/koala/http_service/response.rb +6 -4
  20. data/lib/koala/http_service/uploadable_io.rb +0 -4
  21. data/lib/koala/http_service.rb +18 -76
  22. data/lib/koala/oauth.rb +7 -7
  23. data/lib/koala/realtime_updates.rb +26 -21
  24. data/lib/koala/test_users.rb +9 -8
  25. data/lib/koala/version.rb +1 -1
  26. data/lib/koala.rb +6 -8
  27. data/readme.md +50 -109
  28. data/spec/cases/api_spec.rb +99 -69
  29. data/spec/cases/configuration_spec.rb +11 -0
  30. data/spec/cases/graph_api_batch_spec.rb +73 -42
  31. data/spec/cases/graph_api_spec.rb +15 -29
  32. data/spec/cases/graph_collection_spec.rb +47 -34
  33. data/spec/cases/graph_error_checker_spec.rb +6 -1
  34. data/spec/cases/http_service/request_spec.rb +242 -0
  35. data/spec/cases/http_service/response_spec.rb +24 -0
  36. data/spec/cases/http_service_spec.rb +102 -296
  37. data/spec/cases/koala_spec.rb +7 -5
  38. data/spec/cases/oauth_spec.rb +40 -1
  39. data/spec/cases/realtime_updates_spec.rb +51 -13
  40. data/spec/cases/test_users_spec.rb +56 -2
  41. data/spec/cases/uploadable_io_spec.rb +31 -31
  42. data/spec/fixtures/cat.m4v +0 -0
  43. data/spec/fixtures/facebook_data.yml +4 -6
  44. data/spec/fixtures/mock_facebook_responses.yml +29 -69
  45. data/spec/fixtures/vcr_cassettes/app_test_accounts.yml +97 -0
  46. data/spec/integration/graph_collection_spec.rb +8 -5
  47. data/spec/spec_helper.rb +2 -2
  48. data/spec/support/graph_api_shared_examples.rb +143 -336
  49. data/spec/support/koala_test.rb +8 -10
  50. data/spec/support/mock_http_service.rb +9 -9
  51. data/spec/support/uploadable_io_shared_examples.rb +4 -4
  52. metadata +31 -11
  53. data/.autotest +0 -12
  54. data/Guardfile +0 -6
  55. data/autotest/discover.rb +0 -1
  56. data/lib/koala/api/rest_api.rb +0 -135
  57. data/spec/support/rest_api_shared_examples.rb +0 -168
@@ -1,28 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Koala::Facebook::GraphCollection do
4
- let(:paging){ {:paging => true} }
3
+ describe Koala::Facebook::API::GraphCollection do
4
+ let(:paging){ {"paging" => true} }
5
5
 
6
6
  before(:each) do
7
- @result = {
8
- "data" => [1, 2, :three],
7
+ @headers = {'Content-Type' => 'application/json'}
8
+ @data = {
9
+ "data" => [1, 2, 'three'],
9
10
  "paging" => paging,
10
11
  "summary" => [3]
11
12
  }
13
+ @result = Koala::HTTPService::Response.new(200, @data.to_json, @headers)
12
14
  @api = Koala::Facebook::API.new("123")
13
- @collection = Koala::Facebook::GraphCollection.new(@result, @api)
15
+ @collection = Koala::Facebook::API::GraphCollection.new(@result, @api)
14
16
  end
15
17
 
16
18
  it "subclasses Array" do
17
- expect(Koala::Facebook::GraphCollection.ancestors).to include(Array)
19
+ expect(Koala::Facebook::API::GraphCollection.ancestors).to include(Array)
18
20
  end
19
21
 
20
22
  it "creates an array-like object" do
21
- expect(Koala::Facebook::GraphCollection.new(@result, @api)).to be_an(Array)
23
+ expect(Koala::Facebook::API::GraphCollection.new(@result, @api)).to be_an(Array)
22
24
  end
23
25
 
24
26
  it "contains the result data" do
25
- @result["data"].each_with_index {|r, i| expect(@collection[i]).to eq(r)}
27
+ @data["data"].each_with_index {|r, i| expect(@collection[i]).to eq(r)}
26
28
  end
27
29
 
28
30
  it "has a read-only paging attribute" do
@@ -31,43 +33,49 @@ describe Koala::Facebook::GraphCollection do
31
33
  end
32
34
 
33
35
  it "sets paging to results['paging']" do
34
- expect(@collection.paging).to eq(@result["paging"])
36
+ expect(@collection.paging).to eq(@data["paging"])
35
37
  end
36
38
 
37
39
  it "sets summary to results['summary']" do
38
- expect(@collection.summary).to eq(@result["summary"])
40
+ expect(@collection.summary).to eq(@data["summary"])
39
41
  end
40
42
 
41
43
  it "sets raw_response to the original results" do
42
- expect(@collection.raw_response).to eq(@result)
44
+ expect(@collection.raw_response).to eq(@result.data)
43
45
  end
44
46
 
45
47
  it "sets the API to the provided API" do
46
48
  expect(@collection.api).to eq(@api)
47
49
  end
48
50
 
51
+ it "sets the headers correctly" do
52
+ expect(@collection.headers).to eq(@headers)
53
+ end
54
+
49
55
  describe "when getting a whole page" do
50
56
  before(:each) do
51
57
  @second_page = {
52
- "data" => [:second, :page, :data],
58
+ "data" => ["second", "page", "data"],
53
59
  "paging" => {}
54
60
  }
55
61
  @base = double("base")
56
62
  @args = {"a" => 1}
57
63
  @page_of_results = double("page of results")
64
+ @result = Koala::HTTPService::Response.new(200, @second_page.to_json, {})
65
+ @result.data
58
66
  end
59
67
 
60
68
  it "should return the previous page of results" do
61
69
  expect(@collection).to receive(:previous_page_params).and_return([@base, @args])
62
- expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@second_page)
63
- expect(Koala::Facebook::GraphCollection).to receive(:new).with(@second_page, @api).and_return(@page_of_results)
70
+ expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@result)
71
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(@result, @api).and_return(@page_of_results)
64
72
  expect(@collection.previous_page).to eq(@page_of_results)
65
73
  end
66
74
 
67
75
  it "should return the next page of results" do
68
76
  expect(@collection).to receive(:next_page_params).and_return([@base, @args])
69
- expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@second_page)
70
- expect(Koala::Facebook::GraphCollection).to receive(:new).with(@second_page, @api).and_return(@page_of_results)
77
+ expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@result)
78
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(@result, @api).and_return(@page_of_results)
71
79
 
72
80
  expect(@collection.next_page).to eq(@page_of_results)
73
81
  end
@@ -84,13 +92,13 @@ describe Koala::Facebook::GraphCollection do
84
92
  describe "#parse_page_url" do
85
93
  it "should pass the url to the class method" do
86
94
  url = double("url")
87
- expect(Koala::Facebook::GraphCollection).to receive(:parse_page_url).with(url)
95
+ expect(Koala::Facebook::API::GraphCollection).to receive(:parse_page_url).with(url)
88
96
  @collection.parse_page_url(url)
89
97
  end
90
98
 
91
99
  it "should return the result of the class method" do
92
100
  parsed_content = double("parsed_content")
93
- allow(Koala::Facebook::GraphCollection).to receive(:parse_page_url).and_return(parsed_content)
101
+ allow(Koala::Facebook::API::GraphCollection).to receive(:parse_page_url).and_return(parsed_content)
94
102
  expect(@collection.parse_page_url(double("url"))).to eq(parsed_content)
95
103
  end
96
104
  end
@@ -98,54 +106,59 @@ describe Koala::Facebook::GraphCollection do
98
106
  describe ".parse_page_url" do
99
107
  it "should return the base as the first array entry" do
100
108
  base = "url_path"
101
- expect(Koala::Facebook::GraphCollection.parse_page_url("http://facebook.com/#{base}?anything").first).to eq(base)
109
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/#{base}?anything").first).to eq(base)
102
110
  end
103
111
 
104
112
  it "should return the arguments as a hash as the last array entry" do
105
113
  args_hash = {"one" => "val_one", "two" => "val_two"}
106
- expect(Koala::Facebook::GraphCollection.parse_page_url("http://facebook.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last).to eq(args_hash)
114
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last).to eq(args_hash)
107
115
  end
108
116
 
109
117
  it "works with non-.com addresses" do
110
118
  base = "url_path"
111
119
  args_hash = {"one" => "val_one", "two" => "val_two"}
112
- expect(Koala::Facebook::GraphCollection.parse_page_url("http://facebook.com/#{base}?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}")).to eq([base, args_hash])
120
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/#{base}?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}")).to eq([base, args_hash])
113
121
  end
114
122
 
115
123
  it "works with addresses with irregular characters" do
116
124
  access_token = "appid123a|fdcba"
117
- base, args_hash = Koala::Facebook::GraphCollection.parse_page_url("http://facebook.com/foo?token=#{access_token}")
125
+ base, args_hash = Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/foo?token=#{access_token}")
118
126
  expect(args_hash["token"]).to eq(access_token)
119
127
  end
120
128
  end
121
129
  end
122
130
 
123
131
  describe ".evaluate" do
124
- it "returns the original result if it's provided a non-hash result" do
125
- result = []
126
- expect(Koala::Facebook::GraphCollection.evaluate(result, @api)).to eq(result)
132
+ it "returns the body of the original response if it's provided a Response with a non-hash data key" do
133
+ result = double('fake response')
134
+ allow(result).to receive(:is_a?).with(Hash).and_return(false)
135
+ allow(result).to receive(:data).and_return([])
136
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq([])
127
137
  end
128
138
 
129
139
  it "returns the original result if it's provided a nil result" do
130
140
  result = nil
131
- expect(Koala::Facebook::GraphCollection.evaluate(result, @api)).to eq(result)
141
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(result)
132
142
  end
133
143
 
134
- it "returns the original result if the result doesn't have a data key" do
135
- result = {"paging" => {}}
136
- expect(Koala::Facebook::GraphCollection.evaluate(result, @api)).to eq(result)
144
+ it "returns the original result body if the result doesn't have a data key" do
145
+ paging = {"paging" => {}}
146
+ result = Koala::HTTPService::Response.new(200, paging.to_json, {})
147
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(paging)
137
148
  end
138
149
 
139
150
  it "returns the original result if the result's data key isn't an array" do
140
- result = {"data" => {}, "paging" => {}}
141
- expect(Koala::Facebook::GraphCollection.evaluate(result, @api)).to eq(result)
151
+ body = {"data" => {}, "paging" => {}}
152
+ result = Koala::HTTPService::Response.new(200, body.to_json, {})
153
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(body)
142
154
  end
143
155
 
144
156
  it "returns a new GraphCollection of the result if it has an array data key and a paging key" do
145
- result = {"data" => [], "paging" => {}}
157
+ body = {"data" => [], "paging" => {}}
158
+ result = Koala::HTTPService::Response.new(200, body.to_json, {})
146
159
  expected = :foo
147
- expect(Koala::Facebook::GraphCollection).to receive(:new).with(result, @api).and_return(expected)
148
- expect(Koala::Facebook::GraphCollection.evaluate(result, @api)).to eq(expected)
160
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(result, @api).and_return(expected)
161
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(expected)
149
162
  end
150
163
  end
151
164
 
@@ -54,6 +54,12 @@ module Koala
54
54
  expect(error.response_body).to eq(body)
55
55
  end
56
56
 
57
+ it "returns a ClientError if the body is empty" do
58
+ body.replace("")
59
+ expect(error).to be_a(ClientError)
60
+ expect(error.response_body).to eq(body)
61
+ end
62
+
57
63
  it "adds error data from the body" do
58
64
  error_data = {
59
65
  "type" => "FB error type",
@@ -113,4 +119,3 @@ module Koala
113
119
  end
114
120
  end
115
121
  end
116
-
@@ -0,0 +1,242 @@
1
+ require "spec_helper"
2
+
3
+ module Koala
4
+ module HTTPService
5
+ RSpec.describe Request do
6
+ let(:path) { "/foo" }
7
+ let(:args) { {"a" => 2, "b" => 3, "access_token" => "a_token"} }
8
+ let(:verb) { "get" }
9
+ let(:options) { {an: :option} }
10
+ let(:request) { Request.new(path: path, args: args, verb: verb, options: options) }
11
+
12
+ shared_context post: true do
13
+ let(:verb) { "post" }
14
+ end
15
+
16
+ shared_context delete: true do
17
+ let(:verb) { "delete" }
18
+ end
19
+
20
+ shared_context put: true do
21
+ let(:verb) { "put" }
22
+ end
23
+
24
+ it "raises an ArgumentError if no verb is supplied" do
25
+ expect { Request.new(path: path) }.to raise_exception(ArgumentError)
26
+ end
27
+
28
+ it "raises an ArgumentError if no path is supplied" do
29
+ expect { Request.new(verb: verb) }.to raise_exception(ArgumentError)
30
+ end
31
+
32
+ describe "#verb" do
33
+ it "returns get for get" do
34
+ expect(Request.new(path: path, verb: "get").verb).to eq("get")
35
+ end
36
+
37
+ it "returns post for post" do
38
+ expect(Request.new(path: path, verb: "post").verb).to eq("post")
39
+ end
40
+
41
+ it "returns post for put" do
42
+ expect(Request.new(path: path, verb: "put").verb).to eq("post")
43
+ end
44
+
45
+ it "returns post for delete" do
46
+ expect(Request.new(path: path, verb: "delete").verb).to eq("post")
47
+ end
48
+ end
49
+
50
+ describe "#path" do
51
+ context "if there's no API version" do
52
+ it "returns the path" do
53
+ expect(request.path).to eq(path)
54
+ end
55
+ end
56
+
57
+ context "if there's an API version" do
58
+ shared_examples_for :including_the_version do
59
+ it "prefixes the version appropriately when the path starts with /" do
60
+ expect(Request.new(path: "/foo", verb: "get", options: options).path).to eq("/#{version}/foo")
61
+ end
62
+
63
+ it "prefixes the version appropriately when the path doesn't start with /" do
64
+ expect(Request.new(path: "foo", verb: "get", options: options).path).to eq("/#{version}/foo")
65
+ end
66
+ end
67
+
68
+ context "set by Koala.config" do
69
+ let(:version) { "v2.7" }
70
+
71
+ before :each do
72
+ Koala.configure do |config|
73
+ config.api_version = version
74
+ end
75
+ end
76
+
77
+ it_should_behave_like :including_the_version
78
+ end
79
+
80
+ context "set in options" do
81
+ let(:version) { "v2.8" }
82
+
83
+ let(:options) { {api_version: version} }
84
+
85
+ it_should_behave_like :including_the_version
86
+ end
87
+
88
+ context "for versions without a ." do
89
+ let(:version) { "v21" }
90
+
91
+ let(:options) { {api_version: version} }
92
+
93
+ it_should_behave_like :including_the_version
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#post_args" do
99
+ it "returns {} for get" do
100
+ expect(request.post_args).to eq({})
101
+ end
102
+
103
+ it "returns args for post", :post do
104
+ expect(request.post_args).to eq(args)
105
+ end
106
+
107
+ it "returns args + method: delete for delete", :delete do
108
+ expect(request.post_args).to eq(args.merge(method: "delete"))
109
+ end
110
+
111
+ it "returns args + method: put for put", :put do
112
+ expect(request.post_args).to eq(args.merge(method: "put"))
113
+ end
114
+
115
+ it "turns any UploadableIOs to UploadIOs" do
116
+ # technically this is done for all requests, but you don't send GET requests with files
117
+ upload_io = double("UploadIO")
118
+ u = Koala::HTTPService::UploadableIO.new("/path/to/stuff", "img/jpg")
119
+ allow(u).to receive(:to_upload_io).and_return(upload_io)
120
+ request = Request.new(path: path, verb: "post", args: {an_upload: u})
121
+ expect(request.post_args[:an_upload]).to eq(upload_io)
122
+ end
123
+ end
124
+
125
+ describe "#json?" do
126
+ it "returns false if the option for JSON isn't set" do
127
+ expect(request).not_to be_json
128
+ end
129
+
130
+ context "if JSON is set" do
131
+ let(:options) { {format: :json} }
132
+
133
+ it "returns true if it is" do
134
+ expect(request).to be_json
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#options" do
140
+ it "returns the options provided" do
141
+ expect(request.options).to include(options)
142
+ end
143
+
144
+ it "merges in any global options" do
145
+ global_options = {some: :opts}
146
+ HTTPService.http_options = global_options
147
+ results = request.options
148
+ HTTPService.http_options = {}
149
+ expect(results).to include(global_options)
150
+ end
151
+
152
+ it "overwrites any global options with equivalent ones" do
153
+ options = {foo: :bar}
154
+ HTTPService.http_options = {foo: 2}
155
+ results = Request.new(path: path, args: args, verb: verb, options: options).options
156
+ HTTPService.http_options = {}
157
+ expect(results).to include(options)
158
+ end
159
+
160
+ describe "get parameters" do
161
+ it "includes the parameters for a get " do
162
+ expect(request.options[:params]).to eq(args)
163
+ end
164
+
165
+ it "returns {} for post", :post do
166
+ expect(request.options[:params]).to eq({})
167
+ end
168
+
169
+ it "returns {} for delete", :delete do
170
+ expect(request.options[:params]).to eq({})
171
+ end
172
+
173
+ it "returns {} for put", :put do
174
+ expect(request.options[:params]).to eq({})
175
+ end
176
+ end
177
+
178
+ describe "ssl options" do
179
+ it "does nothing if there's no access token" do
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)
182
+ end
183
+
184
+ context "if there is an access_token" do
185
+ it "includes the default SSL options" do
186
+ expect(request.options).to include(use_ssl: true, ssl: {verify: true})
187
+ end
188
+
189
+ it "incorporates any provided SSL options" do
190
+ new_ssl_options = {an: :option2}
191
+ request_options = Request.new(path: path, args: args, verb: verb, options: options.merge(ssl: new_ssl_options)).options
192
+ expect(request_options[:ssl]).to include(new_ssl_options)
193
+ end
194
+
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)
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "server" do
204
+ describe "with no options" do
205
+ it "returns the graph server by default" do
206
+ expect(request.server).to eq("https://graph.facebook.com")
207
+ end
208
+
209
+ it "uses another base server if specified" do
210
+ Koala.config.graph_server = "foo"
211
+ expect(request.server).to eq("https://foo")
212
+ end
213
+
214
+ context "if options[:use_ssl] is false (e.g. no access token)" do
215
+ let(:args) { {"a" => "b"} }
216
+
217
+ it "uses http" do
218
+ expect(request.server).to eq("http://graph.facebook.com")
219
+ end
220
+ end
221
+
222
+ context "if options[:beta]" do
223
+ let(:options) { {beta: true} }
224
+
225
+ it "returns https if options[:beta]" do
226
+ expect(request.server).to eq("https://graph.beta.facebook.com")
227
+ end
228
+ end
229
+
230
+ context "if options[:video]" do
231
+ let(:options) { {video: true} }
232
+
233
+ it "returns https if options[:video]" do
234
+ expect(request.server).to eq("https://graph-video.facebook.com")
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ module Koala::HTTPService
4
+ RSpec.describe Response do
5
+ describe "#data" do
6
+ it "returns nothing if the body is empty" do
7
+ expect(Response.new(200, "", {}).data).to be_nil
8
+ end
9
+
10
+ it "parses the JSON if there's a body" do
11
+ result = {"foo" => "bar"}
12
+ expect(Response.new(200, result.to_json, {}).data).to eq(result)
13
+ end
14
+
15
+ it "allows raw values" do
16
+ expect(Response.new(200, "true", {}).data).to be true
17
+ end
18
+
19
+ it "raises a ParserError if it's invalid JSON" do
20
+ expect { Response.new(200, "{", {}).data }.to raise_exception(JSON::ParserError)
21
+ end
22
+ end
23
+ end
24
+ end