flexirest 1.2.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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +37 -0
  7. data/CONTRIBUTING.md +62 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +9 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +846 -0
  12. data/Rakefile +13 -0
  13. data/doc/ActiveRestClient Internals.graffle +1236 -0
  14. data/doc/ActiveRestClient Internals.png +0 -0
  15. data/flexirest.gemspec +39 -0
  16. data/lib/flexirest.rb +25 -0
  17. data/lib/flexirest/base.rb +189 -0
  18. data/lib/flexirest/caching.rb +92 -0
  19. data/lib/flexirest/configuration.rb +209 -0
  20. data/lib/flexirest/connection.rb +103 -0
  21. data/lib/flexirest/connection_manager.rb +36 -0
  22. data/lib/flexirest/headers_list.rb +47 -0
  23. data/lib/flexirest/instrumentation.rb +62 -0
  24. data/lib/flexirest/lazy_association_loader.rb +97 -0
  25. data/lib/flexirest/lazy_loader.rb +23 -0
  26. data/lib/flexirest/logger.rb +67 -0
  27. data/lib/flexirest/mapping.rb +69 -0
  28. data/lib/flexirest/monkey_patching.rb +7 -0
  29. data/lib/flexirest/proxy_base.rb +193 -0
  30. data/lib/flexirest/recording.rb +24 -0
  31. data/lib/flexirest/request.rb +573 -0
  32. data/lib/flexirest/request_delegator.rb +44 -0
  33. data/lib/flexirest/request_filtering.rb +62 -0
  34. data/lib/flexirest/result_iterator.rb +85 -0
  35. data/lib/flexirest/validation.rb +60 -0
  36. data/lib/flexirest/version.rb +3 -0
  37. data/spec/lib/base_spec.rb +389 -0
  38. data/spec/lib/caching_spec.rb +217 -0
  39. data/spec/lib/configuration_spec.rb +234 -0
  40. data/spec/lib/connection_manager_spec.rb +43 -0
  41. data/spec/lib/connection_spec.rb +159 -0
  42. data/spec/lib/headers_list_spec.rb +61 -0
  43. data/spec/lib/instrumentation_spec.rb +58 -0
  44. data/spec/lib/lazy_association_loader_spec.rb +135 -0
  45. data/spec/lib/lazy_loader_spec.rb +25 -0
  46. data/spec/lib/logger_spec.rb +63 -0
  47. data/spec/lib/mapping_spec.rb +52 -0
  48. data/spec/lib/proxy_spec.rb +189 -0
  49. data/spec/lib/recording_spec.rb +34 -0
  50. data/spec/lib/request_filtering_spec.rb +84 -0
  51. data/spec/lib/request_spec.rb +711 -0
  52. data/spec/lib/result_iterator_spec.rb +140 -0
  53. data/spec/lib/validation_spec.rb +113 -0
  54. data/spec/lib/xml_spec.rb +74 -0
  55. data/spec/spec_helper.rb +88 -0
  56. metadata +347 -0
@@ -0,0 +1,711 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Request do
4
+ before :each do
5
+ class ExampleOtherClient < Flexirest::Base ; end
6
+ class ExampleSingleClient < Flexirest::Base ; end
7
+ class ExampleClient < Flexirest::Base
8
+ base_url "http://www.example.com"
9
+ request_body_type :form_encoded
10
+ api_auth_credentials('id123', 'secret123')
11
+
12
+ before_request do |name, request|
13
+ if request.method[:name] == :headers
14
+ request.headers["X-My-Header"] = "myvalue"
15
+ end
16
+ end
17
+
18
+ after_request do |name, response|
19
+ if name == :change
20
+ response.body = "{\"test\": 1}"
21
+ end
22
+ end
23
+
24
+ get :all, "/", :has_many => {:expenses => ExampleOtherClient}
25
+ get :babies, "/babies", :has_many => {:children => ExampleOtherClient}
26
+ get :single_association, "/single", :has_one => {:single => ExampleSingleClient}, :has_many => {:children => ExampleOtherClient}
27
+ get :headers, "/headers"
28
+ put :headers_default, "/headers_default"
29
+ put :headers_json, "/headers_json", request_body_type: :json
30
+ get :find, "/:id"
31
+ get :change, "/change"
32
+ post :create, "/create"
33
+ post :test_encoding, "/encoding", request_body_type: :json
34
+ put :update, "/put/:id"
35
+ delete :remove, "/remove/:id"
36
+ get :hal, "/hal", fake:"{\"_links\":{\"child\": {\"href\": \"/child/1\"}, \"other\": {\"href\": \"/other/1\"}, \"cars\":[{\"href\": \"/car/1\", \"name\":\"car1\"}, {\"href\": \"/car/2\", \"name\":\"car2\"}, {\"href\": \"/car/not-embed\", \"name\":\"car_not_embed\"} ], \"lazy\": {\"href\": \"/lazy/load\"}, \"invalid\": [{\"href\": \"/invalid/1\"}]}, \"_embedded\":{\"other\":{\"name\":\"Jane\"},\"child\":{\"name\":\"Billy\"}, \"cars\":[{\"_links\": {\"self\": {\"href\": \"/car/1\"} }, \"make\": \"Bugatti\", \"model\": \"Veyron\"}, {\"_links\": {\"self\": {\"href\": \"/car/2\"} }, \"make\": \"Ferrari\", \"model\": \"F458 Italia\"} ], \"invalid\": [{\"present\":true, \"_links\": {} } ] } }", has_many:{other:ExampleOtherClient}
37
+ get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
38
+ get :fake_proc, "/fake", fake:->(request) { "{\"result\":#{request.get_params[:id]}}" }
39
+ get :defaults, "/defaults", defaults:{overwrite:"no", persist:"yes"}
40
+ end
41
+
42
+ class AuthenticatedExampleClient < Flexirest::Base
43
+ base_url "http://www.example.com"
44
+ username "john"
45
+ password "smith"
46
+ get :all, "/"
47
+ end
48
+
49
+ class LazyLoadedExampleClient < ExampleClient
50
+ base_url "http://www.example.com"
51
+ lazy_load!
52
+ get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
53
+ get :lazy_test, "/does-not-matter", fake:"{\"people\":[\"http://www.example.com/some/url\"]}", :lazy => [:people]
54
+ end
55
+
56
+ class VerboseExampleClient < ExampleClient
57
+ base_url "http://www.example.com"
58
+ verbose!
59
+ get :all, "/all"
60
+ post :create, "/create"
61
+ end
62
+
63
+ class FilteredBodyExampleClient < ExampleClient
64
+ base_url "http://www.example.com"
65
+ before_request do |name, request|
66
+ request.body = MultiJson.dump(request.post_params)
67
+ end
68
+
69
+ post :save, "/save"
70
+ end
71
+
72
+ allow_any_instance_of(Flexirest::Request).to receive(:read_cached_response)
73
+ end
74
+
75
+ it "should get an HTTP connection when called" do
76
+ connection = double(Flexirest::Connection).as_null_object
77
+ expect(Flexirest::ConnectionManager).to receive(:get_connection).with("http://www.example.com").and_return(connection)
78
+ expect(connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
79
+ ExampleClient.all
80
+ end
81
+
82
+ it "should get an HTTP connection with authentication when called" do
83
+ connection = double(Flexirest::Connection).as_null_object
84
+ expect(Flexirest::ConnectionManager).to receive(:get_connection).with("http://john:smith@www.example.com").and_return(connection)
85
+ expect(connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
86
+ AuthenticatedExampleClient.all
87
+ end
88
+
89
+ it "should get an HTTP connection when called and call get on it" do
90
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
91
+ ExampleClient.all
92
+ end
93
+
94
+ it "should get an HTTP connection when called and call delete on it" do
95
+ expect_any_instance_of(Flexirest::Connection).to receive(:delete).with("/remove/1", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
96
+ ExampleClient.remove(id:1)
97
+ end
98
+
99
+ it "should work with faraday response objects" do
100
+ response = Faraday::Response.new
101
+ allow(response).to receive(:body).and_return({}.to_json)
102
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).and_return(response)
103
+ expect { ExampleClient.all }.to_not raise_error
104
+ end
105
+
106
+ it "should pass through get parameters" do
107
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/?debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
108
+ ExampleClient.all debug:true
109
+ end
110
+
111
+ it "should pass through get parameters, using defaults specified" do
112
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/defaults?overwrite=yes&persist=yes", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
113
+ ExampleClient.defaults overwrite:"yes"
114
+ end
115
+
116
+ it "should pass through url parameters" do
117
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/1234", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
118
+ ExampleClient.find id:1234
119
+ end
120
+
121
+ it "should accept an integer as the only parameter and use it as id" do
122
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/1234", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
123
+ ExampleClient.find(1234)
124
+ end
125
+
126
+ it "should accept a string as the only parameter and use it as id" do
127
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/1234", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
128
+ ExampleClient.find("1234")
129
+ end
130
+
131
+ it "should pass through url parameters and get parameters" do
132
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/1234?debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
133
+ ExampleClient.find id:1234, debug:true
134
+ end
135
+
136
+ it "should pass through url parameters and put parameters" do
137
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
138
+ ExampleClient.update id:1234, debug:true
139
+ end
140
+
141
+ it "should pass through 'array type' get parameters" do
142
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/?include%5B%5D=your&include%5B%5D=friends", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
143
+ ExampleClient.all :include => [:your,:friends]
144
+ end
145
+
146
+ it "should encode the body in a form-encoded format by default" do
147
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true&test=foo", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
148
+ ExampleClient.update id:1234, debug:true, test:'foo'
149
+ end
150
+
151
+ it "should encode the body in a JSON format if specified" do
152
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", %q({"debug":true,"test":"foo"}), an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
153
+ ExampleClient.request_body_type :json
154
+ ExampleClient.update id:1234, debug:true, test:'foo'
155
+ end
156
+
157
+ it "allows forcing a request_body_type per request" do
158
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with("/encoding", %q({"id":1234,"test":"something"}), an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true}", response_headers:{})))
159
+ ExampleClient.request_body_type :form_encoded # Should be ignored and the per_method :json used
160
+ ExampleClient.test_encoding id:1234, test: "something"
161
+ end
162
+
163
+ it "should pass through custom headers" do
164
+ expect_any_instance_of(Flexirest::Connection).to receive(:get){ |connection, path, options|
165
+ expect(path).to eq('/headers')
166
+ expect(options[:headers]).to include("X-My-Header" => "myvalue")
167
+ }.and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
168
+ ExampleClient.headers
169
+ end
170
+
171
+ it "should set request header with content-type for default" do
172
+ expect_any_instance_of(Flexirest::Connection).to receive(:put){ |connection, path, data, options|
173
+ expect(path).to eq('/headers_default')
174
+ expect(data).to eq('')
175
+ expect(options[:headers]).to include("Content-Type" => "application/x-www-form-urlencoded")
176
+ }.and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
177
+ ExampleClient.headers_default
178
+ end
179
+
180
+ it "should set request header with content-type for JSON" do
181
+ expect_any_instance_of(Flexirest::Connection).to receive(:put){ |connection, path, data, options|
182
+ expect(path).to eq('/headers_json')
183
+ expect(data).to eq('{}')
184
+ expect(options[:headers]).to include("Content-Type" => "application/json; charset=utf-8")
185
+ }.and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
186
+ ExampleClient.headers_json
187
+ end
188
+
189
+ it "should parse JSON to give a nice object" do
190
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"created_at\":\"2012-03-04T01:02:03Z\", \"child\":{\"grandchild\":{\"test\":true}}}", response_headers:{})))
191
+ object = ExampleClient.update id:1234, debug:true
192
+ expect(object.result).to eq(true)
193
+ expect(object.list.first).to eq(1)
194
+ expect(object.list.last.test).to eq(true)
195
+ expect(object.created_at).to be_an_instance_of(DateTime)
196
+ expect(object.child.grandchild.test).to eq(true)
197
+ end
198
+
199
+ it "should parse JSON and return a nice object for faked responses" do
200
+ object = ExampleClient.fake id:1234, debug:true
201
+ expect(object.result).to eq(true)
202
+ expect(object.list.first).to eq(1)
203
+ expect(object.list.last.test).to eq(true)
204
+ expect(object.child.grandchild.test).to eq(true)
205
+ end
206
+
207
+ it "should parse JSON from a fake response generated by a proc" do
208
+ object = ExampleClient.fake_proc id:1234
209
+ expect(object.result).to eq(1234)
210
+ end
211
+
212
+ it "should return a lazy loader object if lazy loading is enabled" do
213
+ object = LazyLoadedExampleClient.fake id:1234, debug:true
214
+ expect(object).to be_an_instance_of(Flexirest::LazyLoader)
215
+ end
216
+
217
+ it "should proxy through nice object for lazy loaded responses" do
218
+ object = LazyLoadedExampleClient.fake id:1234, debug:true
219
+ expect(object.result).to eq(true)
220
+ expect(object.list.first).to eq(1)
221
+ expect(object.list.last.test).to eq(true)
222
+ expect(object.child.grandchild.test).to eq(true)
223
+ end
224
+
225
+ it "should return a LazyAssociationLoader for lazy loaded properties" do
226
+ object = LazyLoadedExampleClient.lazy_test
227
+ expect(object.people.size).to eq(1)
228
+ expect(object.people).to be_an_instance_of(Flexirest::LazyAssociationLoader)
229
+ end
230
+
231
+ it "should log faked responses" do
232
+ allow(Flexirest::Logger).to receive(:debug)
233
+ expect(Flexirest::Logger).to receive(:debug).with(/Faked response found/)
234
+ ExampleClient.fake id:1234, debug:true
235
+ end
236
+
237
+ it "should parse an array within JSON to be a result iterator" do
238
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[{\"first_name\":\"Johnny\"}, {\"first_name\":\"Billy\"}]", status:200, response_headers:{})))
239
+ object = ExampleClient.update id:1234, debug:true
240
+ expect(object).to be_instance_of(Flexirest::ResultIterator)
241
+ expect(object.first.first_name).to eq("Johnny")
242
+ expect(object[1].first_name).to eq("Billy")
243
+ expect(object._status).to eq(200)
244
+ end
245
+
246
+ it "should instantiate other classes using has_many when required to do so" do
247
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, response_headers:{})))
248
+ object = ExampleClient.all
249
+ expect(object.expenses.first).to be_instance_of(ExampleOtherClient)
250
+ end
251
+
252
+ it "should instantiate other classes using has_many even if nested off the root" do
253
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/babies", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"children\":{\"eldest\":[{\"name\":\"Billy\"}]}}", status:200, response_headers:{})))
254
+ object = ExampleClient.babies
255
+ expect(object.children.eldest.first).to be_instance_of(ExampleOtherClient)
256
+ end
257
+
258
+ it "should instantiate other classes using has_one when required to do so" do
259
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/single", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"single\":{\"name\":\"Billy\"}}", status:200, response_headers:{})))
260
+ object = ExampleClient.single_association
261
+ expect(object.single).to be_instance_of(ExampleSingleClient)
262
+ end
263
+
264
+ it "should instantiate other classes using has_one even if nested off the root" do
265
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/single", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"children\":[{\"single\":{\"name\":\"Billy\"}}, {\"single\":{\"name\":\"Sharon\"}}]}", status:200, response_headers:{})))
266
+ object = ExampleClient.single_association
267
+ expect(object.children.first.single).to be_instance_of(ExampleSingleClient)
268
+ end
269
+
270
+ it "should assign new attributes to the existing object if possible" do
271
+ expect_any_instance_of(Flexirest::Connection).
272
+ to receive(:post).
273
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
274
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{})))
275
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
276
+ object.create
277
+ expect(object.first_name).to eq("John")
278
+ expect(object.should_disappear).to eq(nil)
279
+ expect(object.id).to eq(1234)
280
+ end
281
+
282
+ it "should expose etag if available" do
283
+ response = ::FaradayResponseMock.new(OpenStruct.new(body: "{}", response_headers: {"ETag" => "123456"}, status: 200))
284
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/123", an_instance_of(Hash)).and_return(response)
285
+ object = ExampleClient.find(123)
286
+ expect(object._etag).to eq("123456")
287
+ end
288
+
289
+ it "should expose all headers" do
290
+ response = ::FaradayResponseMock.new(OpenStruct.new(body: "{}", response_headers: {"X-Test-Header" => "true"}, status: 200))
291
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/123", an_instance_of(Hash)).and_return(response)
292
+ object = ExampleClient.find(123)
293
+ expect(object._headers["X-Test-Header"]).to eq("true")
294
+ end
295
+
296
+ it "should expose all headers on collection" do
297
+ response = ::FaradayResponseMock.new(OpenStruct.new(body: "[{}]", response_headers: {"X-Test-Header" => "true"}, status: 200))
298
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/123", an_instance_of(Hash)).and_return(response)
299
+ object = ExampleClient.find(123)
300
+ expect(object._headers["X-Test-Header"]).to eq("true")
301
+ end
302
+
303
+ it "should clearly pass through 200 status responses" do
304
+ expect_any_instance_of(Flexirest::Connection).
305
+ to receive(:post).
306
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
307
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
308
+ expect(Flexirest::Logger).to receive(:info).with(%r'Requesting http://www.example.com/create')
309
+ expect(Flexirest::Logger).to receive(:debug).at_least(1).times.with(%r'(Response received \d+ bytes|Trying to read from cache)')
310
+
311
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
312
+ object.create
313
+ expect(object._status).to eq(200)
314
+ end
315
+
316
+ it "should debug log 200 responses" do
317
+ expect_any_instance_of(Flexirest::Connection).
318
+ to receive(:post).
319
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
320
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
321
+ expect(Flexirest::Logger).to receive(:info).with(%r'Requesting http://www.example.com/create')
322
+ expect(Flexirest::Logger).to receive(:debug).at_least(1).times.with(%r'(Response received \d+ bytes|Trying to read from cache)')
323
+
324
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
325
+ object.create
326
+ end
327
+
328
+ it "should verbose debug the with the right http verb" do
329
+ expect_any_instance_of(Flexirest::Connection).
330
+ to receive(:post).
331
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
332
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
333
+ expect(Flexirest::Logger).to receive(:debug).with(/ POST /)
334
+ allow(Flexirest::Logger).to receive(:debug).with(any_args)
335
+
336
+ object = VerboseExampleClient.new(first_name:"John", should_disappear:true)
337
+ object.create
338
+ end
339
+
340
+ it "should verbose log if enabled" do
341
+ connection = double(Flexirest::Connection).as_null_object
342
+ expect(Flexirest::ConnectionManager).to receive(:get_connection).and_return(connection)
343
+ expect(connection).to receive(:get).with("/all", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{"Content-Type" => "application/json", "Connection" => "close"})))
344
+ expect(Flexirest::Logger).to receive(:debug).with("Flexirest Verbose Log:")
345
+ expect(Flexirest::Logger).to receive(:debug).with(/ >> /).at_least(:twice)
346
+ expect(Flexirest::Logger).to receive(:debug).with(/ << /).at_least(:twice)
347
+ allow(Flexirest::Logger).to receive(:debug).with(any_args)
348
+ VerboseExampleClient.all
349
+ end
350
+
351
+ it "should raise an unauthorised exception for 401 errors" do
352
+ expect_any_instance_of(Flexirest::Connection).
353
+ to receive(:post).
354
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
355
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:401)))
356
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
357
+ begin
358
+ object.create
359
+ rescue Flexirest::HTTPUnauthorisedClientException => e
360
+ e
361
+ end
362
+ expect(e).to be_instance_of(Flexirest::HTTPUnauthorisedClientException)
363
+ expect(e.status).to eq(401)
364
+ expect(e.result.first_name).to eq("John")
365
+ end
366
+
367
+ it "should raise a forbidden client exception for 403 errors" do
368
+ expect_any_instance_of(Flexirest::Connection).
369
+ to receive(:post).
370
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
371
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:403)))
372
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
373
+ begin
374
+ object.create
375
+ rescue Flexirest::HTTPForbiddenClientException => e
376
+ e
377
+ end
378
+ expect(e).to be_instance_of(Flexirest::HTTPForbiddenClientException)
379
+ expect(e.status).to eq(403)
380
+ expect(e.result.first_name).to eq("John")
381
+ end
382
+
383
+ it "should raise a not found client exception for 404 errors" do
384
+ expect_any_instance_of(Flexirest::Connection).
385
+ to receive(:post).
386
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
387
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:404)))
388
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
389
+ begin
390
+ object.create
391
+ rescue Flexirest::HTTPNotFoundClientException => e
392
+ e
393
+ end
394
+ expect(e).to be_instance_of(Flexirest::HTTPNotFoundClientException)
395
+ expect(e.status).to eq(404)
396
+ expect(e.result.first_name).to eq("John")
397
+ end
398
+
399
+ it "should raise a client exceptions for 4xx errors" do
400
+ expect_any_instance_of(Flexirest::Connection).
401
+ to receive(:post).
402
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
403
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:409)))
404
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
405
+ begin
406
+ object.create
407
+ rescue Flexirest::HTTPClientException => e
408
+ e
409
+ end
410
+ expect(e).to be_instance_of(Flexirest::HTTPClientException)
411
+ expect(e.status).to eq(409)
412
+ expect(e.result.first_name).to eq("John")
413
+ end
414
+
415
+ it "should raise a server exception for 5xx errors" do
416
+ expect_any_instance_of(Flexirest::Connection).
417
+ to receive(:post).
418
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
419
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:500)))
420
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
421
+ begin
422
+ object.create
423
+ rescue Flexirest::HTTPServerException => e
424
+ e
425
+ end
426
+ expect(e).to be_instance_of(Flexirest::HTTPServerException)
427
+ expect(e.status).to eq(500)
428
+ expect(e.result.first_name).to eq("John")
429
+ end
430
+
431
+ it "should raise a parse exception for invalid JSON returns" do
432
+ error_content = "<h1>500 Server Error</h1>"
433
+ expect_any_instance_of(Flexirest::Connection).
434
+ to receive(:post).
435
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
436
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:error_content, response_headers:{'Content-Type' => 'text/html'}, status:500)))
437
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
438
+ begin
439
+ object.create
440
+ rescue => e
441
+ e
442
+ end
443
+ expect(e).to be_instance_of(Flexirest::HTTPServerException)
444
+ expect(e.status).to eq(500)
445
+ expect(e.result).to eq(error_content)
446
+ end
447
+
448
+ it "should raise a bad request exception for 400 response status" do
449
+ error_content = "<h1>400 Bad Request</h1>"
450
+ expect_any_instance_of(Flexirest::Connection).
451
+ to receive(:post).
452
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
453
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:error_content, response_headers:{'Content-Type' => 'text/html'}, status:400)))
454
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
455
+ begin
456
+ object.create
457
+ rescue => e
458
+ e
459
+ end
460
+ expect(e).to be_instance_of(Flexirest::HTTPBadRequestClientException)
461
+ expect(e.status).to eq(400)
462
+ expect(e.result).to eq(error_content)
463
+ end
464
+
465
+ it "should raise response parse exception for 200 response status and non json content type" do
466
+ error_content = "<h1>malformed json</h1>"
467
+ expect_any_instance_of(Flexirest::Connection).
468
+ to receive(:post).
469
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
470
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:error_content, response_headers:{'Content-Type' => 'text/html'}, status:200)))
471
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
472
+ begin
473
+ object.create
474
+ rescue => e
475
+ e
476
+ end
477
+ expect(e).to be_instance_of(Flexirest::ResponseParseException)
478
+ expect(e.status).to eq(200)
479
+ expect(e.body).to eq(error_content)
480
+ end
481
+
482
+ it "should not override the attributes of the existing object on error response status" do
483
+ expect_any_instance_of(Flexirest::Connection).
484
+ to receive(:post).
485
+ with("/create", "first_name=John&should_disappear=true", an_instance_of(Hash)).
486
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"errors": ["validation": "error in validation"]}', response_headers:{'Content-Type' => 'text/html'}, status:400)))
487
+ object = ExampleClient.new(first_name:"John", should_disappear:true)
488
+ begin
489
+ object.create
490
+ rescue => e
491
+ e
492
+ end
493
+ expect(e).to be_instance_of(Flexirest::HTTPBadRequestClientException)
494
+ expect(e.status).to eq(400)
495
+ expect(object.first_name).to eq 'John'
496
+ expect(object.errors).to eq(nil)
497
+ end
498
+
499
+ it "should raise an exception if you try to pass in an unsupport method" do
500
+ method = {:method => :wiggle, url:"/"}
501
+ class RequestFakeObject < Flexirest::Base
502
+ base_url "http://www.example.com/"
503
+
504
+ def request_body_type
505
+ :form_encoded
506
+ end
507
+
508
+ def username ; end
509
+ def password ; end
510
+ def name ; end
511
+ def _filter_request(*args) ; end
512
+ def verbose ; false ; end
513
+ end
514
+ fake_object = RequestFakeObject.new
515
+ request = Flexirest::Request.new(method, fake_object, {})
516
+ allow(fake_object).to receive(:read_cached_response).and_return(nil)
517
+ expect{request.call}.to raise_error(Flexirest::InvalidRequestException)
518
+ end
519
+
520
+ it "should send all class mapped methods through _filter_request" do
521
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, response_headers:{})))
522
+ expect(ExampleClient).to receive(:_filter_request).with(any_args).exactly(2).times
523
+ ExampleClient.all
524
+ end
525
+
526
+ it "should send all instance mapped methods through _filter_request" do
527
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, response_headers:{})))
528
+ expect(ExampleClient).to receive(:_filter_request).with(any_args).exactly(2).times
529
+ e = ExampleClient.new
530
+ e.all
531
+ end
532
+
533
+ it "should change the generated object if an after_filter changes it" do
534
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/change", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"expenses\":[{\"amount\":1}, {\"amount\":2}]}", status:200, response_headers:{})))
535
+ obj = ExampleClient.change
536
+ expect(obj.test).to eq(1)
537
+ end
538
+
539
+ context "Direct URL requests" do
540
+ class SameServerExampleClient < Flexirest::Base
541
+ URL = "http://www.example.com/some/url"
542
+ base_url "http://www.example.com/v1"
543
+ get :same_server, "/does-not-matter", url:URL
544
+ end
545
+
546
+ class OtherServerExampleClient < Flexirest::Base
547
+ URL = "http://other.example.com/some/url"
548
+ base_url "http://www.example.com/v1"
549
+ get :other_server, "/does-not-matter", url:URL
550
+ end
551
+
552
+ it "should allow requests directly to URLs" do
553
+ Flexirest::ConnectionManager.reset!
554
+ expect_any_instance_of(Flexirest::Connection).
555
+ to receive(:get).
556
+ with("/some/url", an_instance_of(Hash)).
557
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
558
+ SameServerExampleClient.same_server
559
+ end
560
+
561
+ it "should allow requests directly to URLs even if to different URLs" do
562
+ Flexirest::ConnectionManager.reset!
563
+ connection = double("Connection")
564
+ expect(connection).
565
+ to receive(:get).
566
+ with("/some/url", an_instance_of(Hash)).
567
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{}", response_headers:{}, status:304)))
568
+ allow(connection).
569
+ to receive(:base_url).
570
+ and_return("http://other.example.com")
571
+ expect(Flexirest::ConnectionManager).to receive(:find_connection_for_url).with(OtherServerExampleClient::URL).and_return(connection)
572
+ OtherServerExampleClient.other_server
573
+ end
574
+
575
+ it "should allow requests to partial URLs using the current base_url" do
576
+ Flexirest::ConnectionManager.reset!
577
+ connection = double("Connection")
578
+ allow(connection).to receive(:base_url).and_return("http://www.example.com")
579
+ expect(Flexirest::ConnectionManager).to receive(:get_connection).with("http://www.example.com").and_return(connection)
580
+ expect(connection).
581
+ to receive(:get).
582
+ with("/v1/people", an_instance_of(Hash)).
583
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
584
+ @obj = SameServerExampleClient._request('/people')
585
+ end
586
+ end
587
+
588
+ # HAL is Hypermedia Application Language
589
+ context "HAL" do
590
+ let(:hal) { ExampleClient.hal }
591
+
592
+ it "should request a HAL response or plain JSON" do
593
+ expect_any_instance_of(Flexirest::Connection).to receive(:get){ |connection, path, options|
594
+ expect(path).to eq('/headers')
595
+ expect(options[:headers]).to include("Accept" => "application/hal+json, application/json;q=0.5")
596
+ }.and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
597
+ ExampleClient.headers
598
+ end
599
+
600
+ it "should recognise a HAL response" do
601
+ method = {:method => :get, url:"/"}
602
+ class RequestFakeObject
603
+ def base_url
604
+ "http://www.example.com/"
605
+ end
606
+
607
+ def name ; end
608
+ def _filter_request(*args) ; end
609
+ end
610
+ fake_object = RequestFakeObject.new
611
+ request = Flexirest::Request.new(method, fake_object, {})
612
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => "application/hal+json"}))
613
+ expect(request.hal_response?).to be_truthy
614
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => "application/json"}))
615
+ expect(request.hal_response?).to be_truthy
616
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => "text/plain"}))
617
+ expect(request.hal_response?).to be_falsey
618
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => ["text/plain", "application/hal+json"]}))
619
+ expect(request.hal_response?).to be_truthy
620
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => ["text/plain", "application/json"]}))
621
+ expect(request.hal_response?).to be_truthy
622
+ request.instance_variable_set(:@response, OpenStruct.new(response_headers:{"Content-Type" => ["text/plain"]}))
623
+ expect(request.hal_response?).to be_falsey
624
+ end
625
+
626
+ it "should map _links in to the normal attributes" do
627
+ expect(hal.child).to be_an_instance_of(ExampleClient)
628
+ expect(hal.cars.size).to eq(3)
629
+ end
630
+
631
+ it "should be able to use other attributes of _links using _hal_attributes method with a key" do
632
+ expect(hal.child).to be_an_instance_of(ExampleClient)
633
+ expect(hal.cars[2]._hal_attributes("name")).to eq('car_not_embed')
634
+ end
635
+
636
+ it "should use _embedded responses instead of lazy loading if possible" do
637
+ expect(hal.child.name).to eq("Billy")
638
+ expect(hal.cars.first.make).to eq("Bugatti")
639
+ end
640
+
641
+ it "should instantiate other classes defined using has_many when using _embedded responses" do
642
+ expect(hal.other).to be_an(ExampleOtherClient)
643
+ end
644
+
645
+ it "should convert invalid _embedded responses in to lazy loading on error" do
646
+ expect(hal.invalid.first).to be_an_instance_of(Flexirest::LazyAssociationLoader)
647
+ end
648
+
649
+ it "should lazy load _links attributes if not embedded" do
650
+ expect(hal.lazy).to be_an_instance_of(Flexirest::LazyAssociationLoader)
651
+ expect(hal.lazy.instance_variable_get(:@url)).to eq("/lazy/load")
652
+ end
653
+ end
654
+
655
+ it "replaces the body completely in a filter" do
656
+ expect_any_instance_of(Flexirest::Connection).to receive(:post).with("/save", "{\"id\":1234,\"name\":\"john\"}", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{}", response_headers:{})))
657
+ FilteredBodyExampleClient.save id:1234, name:'john'
658
+ end
659
+
660
+ context 'Simulating Faraday connection in_parallel' do
661
+ it 'should parse JSON and return a single object' do
662
+ response = ::FaradayResponseMock.new(
663
+ OpenStruct.new(body:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"created_at\":\"2012-03-04T01:02:03Z\", \"child\":{\"grandchild\":{\"test\":true}}}", response_headers:{}),
664
+ false)
665
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(response)
666
+ object = ExampleClient.update id:1234, debug:true
667
+
668
+ expect(object).to eq(nil)
669
+
670
+ response.finish
671
+ expect(object.result).to eq(true)
672
+ expect(object.list.first).to eq(1)
673
+ expect(object.list.last.test).to eq(true)
674
+ expect(object.created_at).to be_an_instance_of(DateTime)
675
+ expect(object.child.grandchild.test).to eq(true)
676
+ end
677
+
678
+ it 'should parse an array within JSON and return a result iterator' do
679
+ response = ::FaradayResponseMock.new(
680
+ OpenStruct.new(body:"[{\"first_name\":\"Johnny\"}, {\"first_name\":\"Billy\"}]", status:200, response_headers:{}),
681
+ false)
682
+ expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(response)
683
+ object = ExampleClient.all
684
+
685
+ expect(object).to eq(nil)
686
+
687
+ response.finish
688
+ expect(object).to be_instance_of(Flexirest::ResultIterator)
689
+ expect(object.first.first_name).to eq("Johnny")
690
+ expect(object[1].first_name).to eq("Billy")
691
+ expect(object._status).to eq(200)
692
+ object.each do |item|
693
+ expect(item).to_not be_nil
694
+ end
695
+ end
696
+
697
+ it 'should return a RequestDelegator object to wrap the result' do
698
+ response = ::FaradayResponseMock.new(
699
+ OpenStruct.new(body:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"created_at\":\"2012-03-04T01:02:03Z\", \"child\":{\"grandchild\":{\"test\":true}}}", response_headers:{}),
700
+ false)
701
+ expect_any_instance_of(Flexirest::Connection).to receive(:put).with("/put/1234", "debug=true", an_instance_of(Hash)).and_return(response)
702
+ object = ExampleClient.update id:1234, debug:true
703
+ response.finish
704
+
705
+ expect(object.class).to eq(ExampleClient)
706
+ expect(object.kind_of?(ExampleClient)).to be_truthy
707
+ expect(object.is_a?(ExampleClient)).to be_truthy
708
+ expect(object._delegate?).to be_truthy
709
+ end
710
+ end
711
+ end