flexirest 1.9.11 → 1.9.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +31 -0
- data/docs/httpparse-error-handling.md +16 -3
- data/flexirest.gemspec +1 -1
- data/lib/flexirest/attribute_parsing.rb +2 -1
- data/lib/flexirest/base_without_validation.rb +2 -1
- data/lib/flexirest/caching.rb +4 -1
- data/lib/flexirest/json_api_proxy.rb +4 -2
- data/lib/flexirest/request.rb +50 -38
- data/lib/flexirest/result_iterator.rb +4 -0
- data/lib/flexirest/version.rb +1 -1
- data/spec/lib/base_without_validation_spec.rb +14 -0
- data/spec/lib/caching_spec.rb +12 -0
- data/spec/lib/json_api_spec.rb +35 -17
- data/spec/lib/request_spec.rb +138 -17
- data/spec/spec_helper.rb +2 -2
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70788d1c6cfd0e3490c50e6eec78136eb5f6e1031dd6135e943ab7a1a0367db0
|
4
|
+
data.tar.gz: ed5282f589dd2893fc16db20197d6109382887a6b3a5a936e2f04113c6ba70cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21b69a5820a346cbdfded7e4f0c30e79f5ff10d4f2e272d59f0163070d1e4a729ebaf95d9fe7ccbb70fc15ba952bdee741bcbb994740b40e1882d7c6a0516ba3
|
7
|
+
data.tar.gz: 57fc2b560629d9cf2521d4f06b3f0dcdca059a686a88f3fe7cc8ca2b4ac912bd1896074243c8a589d5989b4019acc8d0eac709dd95d1f2cd7885ac0523eac702
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.9.16
|
4
|
+
|
5
|
+
Bugfix:
|
6
|
+
|
7
|
+
- Cached responses were always returning as `dirty?`/`changed?` (thanks to AKRathore for the bug report).
|
8
|
+
|
9
|
+
## 1.9.15
|
10
|
+
|
11
|
+
Bugfix:
|
12
|
+
|
13
|
+
- Fix not marking unchanged attributes as dirty (thanks to Khairi Adnan for the bug report).
|
14
|
+
|
15
|
+
## 1.9.14
|
16
|
+
|
17
|
+
Bugfix:
|
18
|
+
|
19
|
+
- Remove deprecation warning for `URI.escape` on Ruby 2.7.x.
|
20
|
+
|
21
|
+
## 1.9.13
|
22
|
+
|
23
|
+
Change:
|
24
|
+
|
25
|
+
- Unified the response body to be `body` for all exceptions instead of sometimes being `body` and sometimes being `raw_response`, although both are available as aliases where they were defined before (thanks to Romain Gisiger for the PR).
|
26
|
+
- Adjust parsing of attributes to be done in a more unified way and lighten the CPU load (thanks to Romain Gisiger for the PR)
|
27
|
+
|
28
|
+
## 1.9.12
|
29
|
+
|
30
|
+
Bugfix:
|
31
|
+
|
32
|
+
- Prevent crash on JSONAPI invalid error response (thanks to François Ferrandis for the PR).
|
33
|
+
|
3
34
|
## 1.9.11
|
4
35
|
|
5
36
|
Bugfix:
|
@@ -1,16 +1,29 @@
|
|
1
1
|
# *Flexirest:* HTTP/parse error handling
|
2
2
|
|
3
|
-
|
3
|
+
## HTTP Errors
|
4
|
+
|
5
|
+
Sometimes the backend server may respond with a non-2xx/3xx header, in which case the code will raise an `Flexirest::HTTPClientException` for 4xx errors or an `Flexirest::HTTPServerException` for 5xx errors.
|
6
|
+
|
7
|
+
These both have a `status` accessor, a `result` accessor (for getting access to the parsed body) and a `body` accessor (for getting access to the original unparsed body). However, you can make use of the `message` method (or the `to_s` method) that will build a nice error message for you (containing the URL and the HTTP method used for the request as well as the HTTP status code and the original body of the response).
|
4
8
|
|
5
9
|
```ruby
|
6
10
|
begin
|
7
11
|
Person.all
|
8
12
|
rescue Flexirest::HTTPClientException, Flexirest::HTTPServerException => e
|
9
|
-
|
13
|
+
# Display the HTTP status and parsed body
|
14
|
+
puts "Backend returned HTTP #{e.status} with following parsed body: #{e.result}"
|
15
|
+
|
16
|
+
# Display the unparsed body
|
17
|
+
puts "Original body is: #{e.body}"
|
18
|
+
|
19
|
+
# Using the message method, same than calling the to_s method
|
20
|
+
puts e.message
|
10
21
|
end
|
11
22
|
```
|
12
23
|
|
13
|
-
|
24
|
+
## Parse Error
|
25
|
+
|
26
|
+
If the original response is unparsable (e.g. not in the desired content type), then it will raise an `Flexirest::ResponseParseException` which has a `status` accessor for the HTTP status code and a `body` accessor for the unparsed response body.
|
14
27
|
|
15
28
|
-----
|
16
29
|
|
data/flexirest.gemspec
CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_development_dependency "simplecov"
|
37
37
|
spec.add_development_dependency "simplecov-rcov"
|
38
38
|
spec.add_development_dependency 'coveralls'
|
39
|
-
spec.add_development_dependency "api-auth", ">= 1.3.1"
|
39
|
+
spec.add_development_dependency "api-auth", ">= 1.3.1", "< 2.4"
|
40
40
|
spec.add_development_dependency 'typhoeus'
|
41
41
|
spec.add_development_dependency 'activemodel'
|
42
42
|
|
@@ -3,7 +3,8 @@ module Flexirest
|
|
3
3
|
private
|
4
4
|
|
5
5
|
def parse_attribute_value(v)
|
6
|
-
return v if v.is_a?(Date) || v.is_a?(DateTime)
|
6
|
+
return v if v.is_a?(Date) || v.is_a?(DateTime) ||
|
7
|
+
v.kind_of?(NilClass) || v.kind_of?(TrueClass) || v.kind_of?(FalseClass) || v.kind_of?(Numeric)
|
7
8
|
|
8
9
|
if v.to_s[(/\A(((19|20)\d\d[- \/.](0[1-9]|1[012]|[1-9])[- \/.](0[1-9]|[12][0-9]|3[01]|[1-9]))|((0[1-9]|1[012]|[1-9])[- \/.](0[1-9]|[12][0-9]|3[01]|[1-9])[- \/.](19|20)\d\d))\Z/)]
|
9
10
|
Date.parse(v) rescue v
|
@@ -41,6 +41,7 @@ module Flexirest
|
|
41
41
|
def _copy_from(result)
|
42
42
|
@attributes = result._attributes
|
43
43
|
@_status = result._status
|
44
|
+
_clean!
|
44
45
|
end
|
45
46
|
|
46
47
|
def dirty?
|
@@ -189,7 +190,7 @@ module Flexirest
|
|
189
190
|
old_value = @dirty_attributes[key.to_sym]
|
190
191
|
old_value = @attributes[key.to_sym] unless old_value
|
191
192
|
old_value = old_value[0] if old_value and old_value.is_a? Array
|
192
|
-
@dirty_attributes[key.to_sym] = [old_value, value]
|
193
|
+
@dirty_attributes[key.to_sym] = [old_value, value] if old_value != value
|
193
194
|
@attributes[key.to_sym] = value
|
194
195
|
end
|
195
196
|
|
data/lib/flexirest/caching.rb
CHANGED
@@ -110,9 +110,12 @@ module Flexirest
|
|
110
110
|
if @result.is_a?(Array)
|
111
111
|
ri = ResultIterator.new(self)
|
112
112
|
ri.items = @result.map{|i| @class_name.constantize.new(i)}
|
113
|
+
ri._clean!
|
113
114
|
ri
|
114
115
|
else
|
115
|
-
@class_name.constantize.new(@result)
|
116
|
+
obj = @class_name.constantize.new(@result)
|
117
|
+
obj._clean!
|
118
|
+
obj
|
116
119
|
end
|
117
120
|
end
|
118
121
|
end
|
@@ -196,8 +196,10 @@ module Flexirest
|
|
196
196
|
# Save resource class for building lazy association loaders
|
197
197
|
save_resource_class(object)
|
198
198
|
|
199
|
-
#
|
200
|
-
|
199
|
+
# According to the spec:
|
200
|
+
# "The members data and errors MUST NOT coexist in the same document."
|
201
|
+
# Thus, if the "errors" key is present, we can return it and ignore the "data" key.
|
202
|
+
return body['errors'] if body.include?('errors')
|
201
203
|
|
202
204
|
# return early if data is an empty array
|
203
205
|
return [] if body['data'] == []
|
data/lib/flexirest/request.rb
CHANGED
@@ -380,7 +380,7 @@ module Flexirest
|
|
380
380
|
if target.to_s.blank?
|
381
381
|
missing << token
|
382
382
|
end
|
383
|
-
@url.gsub!(":#{token}", URI.
|
383
|
+
@url.gsub!(":#{token}", URI.encode_www_form_component(target.to_s))
|
384
384
|
end
|
385
385
|
end
|
386
386
|
|
@@ -656,40 +656,52 @@ module Flexirest
|
|
656
656
|
k = k.to_sym
|
657
657
|
end
|
658
658
|
overridden_name = select_name(k, overridden_name)
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
659
|
+
set_corresponding_value(v, k, object, overridden_name)
|
660
|
+
end
|
661
|
+
object.clean! unless object_is_class?
|
662
|
+
|
663
|
+
object
|
664
|
+
end
|
665
|
+
|
666
|
+
def set_corresponding_value(value, key = nil, object = nil, overridden_name = nil)
|
667
|
+
optional_args = [key, object, overridden_name]
|
668
|
+
value_from_object = optional_args.all? # trying to parse a JSON Hash value
|
669
|
+
value_from_other_type = optional_args.none? # trying to parse anything else
|
670
|
+
raise Flexirest::InvalidArgumentsException.new("Optional args need all to be filled or none") unless value_from_object || value_from_other_type
|
671
|
+
k = key || :key
|
672
|
+
v = value
|
673
|
+
assignable_hash = value_from_object ? object._attributes : {}
|
674
|
+
if value_from_object && @method[:options][:lazy].include?(k)
|
675
|
+
assignable_hash[k] = Flexirest::LazyAssociationLoader.new(overridden_name, v, self, overridden_name:(overridden_name))
|
676
|
+
elsif v.is_a? Hash
|
677
|
+
assignable_hash[k] = new_object(v, overridden_name )
|
678
|
+
elsif v.is_a? Array
|
679
|
+
if @method[:options][:array].include?(k)
|
680
|
+
assignable_hash[k] = Array.new
|
676
681
|
else
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
object._attributes[k] = v
|
683
|
-
elsif Flexirest::Base.disable_automatic_date_parsing
|
684
|
-
object._attributes[k] = v
|
682
|
+
assignable_hash[k] = Flexirest::ResultIterator.new
|
683
|
+
end
|
684
|
+
v.each do |item|
|
685
|
+
if item.is_a? Hash
|
686
|
+
assignable_hash[k] << new_object(item, overridden_name)
|
685
687
|
else
|
686
|
-
|
688
|
+
assignable_hash[k] << set_corresponding_value(item)
|
687
689
|
end
|
688
690
|
end
|
691
|
+
else
|
692
|
+
parse_fields = [ @method[:options][:parse_fields], @object._date_fields ].compact.reduce([], :|)
|
693
|
+
parse_fields = nil if parse_fields.empty?
|
694
|
+
if (parse_fields && parse_fields.include?(k))
|
695
|
+
assignable_hash[k] = parse_attribute_value(v)
|
696
|
+
elsif parse_fields
|
697
|
+
assignable_hash[k] = v
|
698
|
+
elsif Flexirest::Base.disable_automatic_date_parsing
|
699
|
+
assignable_hash[k] = v
|
700
|
+
else
|
701
|
+
assignable_hash[k] = parse_attribute_value(v)
|
702
|
+
end
|
689
703
|
end
|
690
|
-
object
|
691
|
-
|
692
|
-
object
|
704
|
+
value_from_object ? object : assignable_hash[k]
|
693
705
|
end
|
694
706
|
|
695
707
|
def hal_response?
|
@@ -822,12 +834,10 @@ module Flexirest
|
|
822
834
|
|
823
835
|
def add_nested_body_to_iterator(result, items)
|
824
836
|
items.each do |json_object|
|
825
|
-
if json_object.is_a?
|
826
|
-
iterator = ResultIterator.new
|
827
|
-
add_nested_body_to_iterator(iterator, json_object)
|
828
|
-
result << iterator
|
829
|
-
else
|
837
|
+
if json_object.is_a? Hash
|
830
838
|
result << new_object(json_object, @overridden_name)
|
839
|
+
else
|
840
|
+
result << set_corresponding_value(json_object)
|
831
841
|
end
|
832
842
|
end
|
833
843
|
end
|
@@ -838,6 +848,7 @@ module Flexirest
|
|
838
848
|
end
|
839
849
|
|
840
850
|
class RequestException < StandardError ; end
|
851
|
+
class InvalidArgumentsException < StandardError ; end
|
841
852
|
|
842
853
|
class InvalidRequestException < RequestException ; end
|
843
854
|
class MissingParametersException < RequestException ; end
|
@@ -851,18 +862,19 @@ module Flexirest
|
|
851
862
|
end
|
852
863
|
|
853
864
|
class HTTPException < RequestException
|
854
|
-
attr_accessor :status, :result, :request_url
|
865
|
+
attr_accessor :status, :result, :request_url, :body, :raw_response
|
855
866
|
def initialize(options)
|
856
867
|
@status = options[:status]
|
857
868
|
@result = options[:result]
|
858
869
|
@request_url = options[:url]
|
859
|
-
@
|
870
|
+
@body = options[:raw_response]
|
860
871
|
@method = options[:method]
|
861
872
|
end
|
873
|
+
alias_method :body, :raw_response
|
862
874
|
|
863
875
|
def message
|
864
876
|
method = @method.try(:upcase)
|
865
|
-
"The #{method} to '#{@request_url}' returned a #{@status} status, which raised a #{self.class.to_s} with a body of: #{@
|
877
|
+
"The #{method} to '#{@request_url}' returned a #{@status} status, which raised a #{self.class.to_s} with a body of: #{@body}"
|
866
878
|
end
|
867
879
|
|
868
880
|
def to_s
|
data/lib/flexirest/version.rb
CHANGED
@@ -126,6 +126,13 @@ describe Flexirest::BaseWithoutValidation do
|
|
126
126
|
expect(client).to be_dirty
|
127
127
|
end
|
128
128
|
|
129
|
+
it "should not mark attributes that don't change as dirty" do
|
130
|
+
client = EmptyExample.new(test: "Foo")
|
131
|
+
client._clean!
|
132
|
+
client.test = client.test
|
133
|
+
expect(client).to_not be_dirty
|
134
|
+
end
|
135
|
+
|
129
136
|
it "should store attribute set using []= array notation and mark them as dirty" do
|
130
137
|
client = EmptyExample.new()
|
131
138
|
client["test"] = "Something"
|
@@ -171,6 +178,13 @@ describe Flexirest::BaseWithoutValidation do
|
|
171
178
|
expect(client).to be_dirty
|
172
179
|
end
|
173
180
|
|
181
|
+
it "should not mark a freshly retrieved object as changed" do
|
182
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
|
183
|
+
client = AlteringClientExample.find(id: 1)
|
184
|
+
expect(client).to_not be_changed
|
185
|
+
expect(client.changes).to eq({})
|
186
|
+
end
|
187
|
+
|
174
188
|
it 'should respond_to? attributes defined in the response' do
|
175
189
|
client = EmptyExample.new(hello: "World")
|
176
190
|
expect(client.respond_to?(:hello)).to be_truthy
|
data/spec/lib/caching_spec.rb
CHANGED
@@ -184,6 +184,18 @@ describe Flexirest::Caching do
|
|
184
184
|
expect(ret.first_name).to eq("Johnny")
|
185
185
|
end
|
186
186
|
|
187
|
+
it "cache read objects shouldn't be marked as changed" do
|
188
|
+
cached_response = Flexirest::CachedResponse.new(
|
189
|
+
status:200,
|
190
|
+
result:@cached_object,
|
191
|
+
expires:Time.now + 30)
|
192
|
+
expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
|
193
|
+
expect_any_instance_of(Flexirest::Connection).not_to receive(:get)
|
194
|
+
ret = Person.all
|
195
|
+
expect(ret).to_not be_changed
|
196
|
+
expect(ret.changes).to eq({})
|
197
|
+
end
|
198
|
+
|
187
199
|
it "should read from the cache store and restore to the same object" do
|
188
200
|
cached_response = Flexirest::CachedResponse.new(
|
189
201
|
status:200,
|
data/spec/lib/json_api_spec.rb
CHANGED
@@ -312,28 +312,46 @@ describe 'JSON API' do
|
|
312
312
|
expect(subject.find_all).to be_an_instance_of(Flexirest::ResultIterator)
|
313
313
|
end
|
314
314
|
|
315
|
-
|
316
|
-
|
317
|
-
let(:response_body) do
|
318
|
-
{
|
319
|
-
errors: [
|
320
|
-
{
|
321
|
-
title: "Record not found",
|
322
|
-
detail: "The record identified by 123456 could not be found",
|
323
|
-
code: "not_found",
|
324
|
-
status: "404",
|
325
|
-
}
|
326
|
-
]
|
327
|
-
}
|
328
|
-
end
|
315
|
+
describe 'error responses' do
|
316
|
+
subject(:make_request) { JsonAPIExample::Article.real_find(123) }
|
329
317
|
|
330
|
-
|
318
|
+
before do
|
319
|
+
headers = { "Content-Type" => "application/vnd.api+json" }
|
331
320
|
expect_any_instance_of(Flexirest::Connection).
|
332
321
|
to receive(:get).with("/articles/123", an_instance_of(Hash)).
|
333
322
|
and_return(::FaradayResponseMock.new(OpenStruct.new(body: response_body.to_json, response_headers: headers, status: 404)))
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'when no "data" key is present alongside the "errors" key' do
|
326
|
+
let(:response_body) do
|
327
|
+
{
|
328
|
+
errors: [
|
329
|
+
{ detail: "The record identified by 123456 could not be found", }
|
330
|
+
]
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'should raise the relevant Flexirest error' do
|
335
|
+
expect(-> { make_request }).to raise_error(Flexirest::HTTPNotFoundClientException) do |exception|
|
336
|
+
expect(exception.result.first.detail).to eq("The record identified by 123456 could not be found")
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
context 'when a "data" key is present alongside the "errors" key (although this is forbidden by the spec)' do
|
342
|
+
let(:response_body) do
|
343
|
+
{
|
344
|
+
errors: [
|
345
|
+
{ detail: "The record identified by 123456 could not be found", }
|
346
|
+
],
|
347
|
+
data: {}
|
348
|
+
}
|
349
|
+
end
|
334
350
|
|
335
|
-
|
336
|
-
expect(
|
351
|
+
it 'should ignore the "data" key and raise the relevant Flexirest error' do
|
352
|
+
expect(-> { make_request }).to raise_error(Flexirest::HTTPNotFoundClientException) do |exception|
|
353
|
+
expect(exception.result.first.detail).to eq("The record identified by 123456 could not be found")
|
354
|
+
end
|
337
355
|
end
|
338
356
|
end
|
339
357
|
end
|
data/spec/lib/request_spec.rb
CHANGED
@@ -31,6 +31,8 @@ describe Flexirest::Request do
|
|
31
31
|
get :flat, "/", params_encoder: :flat
|
32
32
|
get :array, "/johnny", array: [:likes, :dislikes]
|
33
33
|
get :babies, "/babies", has_many: {children: ExampleOtherClient}
|
34
|
+
get :basket, "/basket", array: [:options]
|
35
|
+
get :dates, "/dates", array: [:dates]
|
34
36
|
get :single_association, "/single", has_one: {single: ExampleSingleClient}, has_many: {children: ExampleOtherClient}
|
35
37
|
get :headers, "/headers"
|
36
38
|
get :cancel_callback, "/cancel-callback"
|
@@ -38,6 +40,7 @@ describe Flexirest::Request do
|
|
38
40
|
put :headers_json, "/headers_json", request_body_type: :json
|
39
41
|
get :find, "/:id", required: [:id]
|
40
42
|
get :find_cat, "/:id/cat"
|
43
|
+
get :fruits, "/fruits"
|
41
44
|
get :change, "/change"
|
42
45
|
get :plain, "/plain/:id", plain: true
|
43
46
|
post :create, "/create", rubify_names: true
|
@@ -46,11 +49,14 @@ describe Flexirest::Request do
|
|
46
49
|
put :update, "/put/:id"
|
47
50
|
put :wrapped, "/put/:id", wrap_root: "example"
|
48
51
|
put :conversion, "/put/:id", parse_fields: [:converted]
|
52
|
+
put :conversion_child, "/put/:id", parse_fields: [:converted_child]
|
49
53
|
delete :remove, "/remove/:id"
|
50
54
|
delete :remove_body, "/remove/:id", send_delete_body: true
|
51
55
|
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}
|
52
|
-
get :
|
53
|
-
get :
|
56
|
+
get :fake_object, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
|
57
|
+
get :fake_proc_object, "/fake", fake:->(request) { "{\"result\":#{request.get_params[:id]}}" }
|
58
|
+
get :fake_array, "/fake", fake:"[1,2,3,{\"test\":true},null]"
|
59
|
+
get :fake_proc_array, "/fake", fake:->(request) { "[{\"result\":#{request.get_params[:id]}},null]" }
|
54
60
|
get :defaults, "/defaults", defaults:{overwrite:"no", persist:"yes"}
|
55
61
|
get :requires, "/requires", requires:[:name, :age]
|
56
62
|
patch :only_changed_1, "/changed1", only_changed: true
|
@@ -143,7 +149,8 @@ describe Flexirest::Request do
|
|
143
149
|
class LazyLoadedExampleClient < ExampleClient
|
144
150
|
base_url "http://www.example.com"
|
145
151
|
lazy_load!
|
146
|
-
get :
|
152
|
+
get :fake_object, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
|
153
|
+
get :fake_array, "/fake", fake:"[1,2,3,{\"test\":true},{\"child\":{\"grandchild\":{\"test\":true}}}]"
|
147
154
|
get :lazy_test, "/does-not-matter", fake:"{\"people\":[\"http://www.example.com/some/url\"]}", lazy: [:people]
|
148
155
|
end
|
149
156
|
|
@@ -190,7 +197,8 @@ describe Flexirest::Request do
|
|
190
197
|
class WhitelistedDateClient < Flexirest::Base
|
191
198
|
base_url "http://www.example.com"
|
192
199
|
put :conversion, "/put/:id"
|
193
|
-
|
200
|
+
put :conversion_child, "/put/:id"
|
201
|
+
parse_date :converted, :converted_child
|
194
202
|
end
|
195
203
|
|
196
204
|
allow_any_instance_of(Flexirest::Request).to receive(:read_cached_response)
|
@@ -336,7 +344,7 @@ describe Flexirest::Request do
|
|
336
344
|
end
|
337
345
|
|
338
346
|
it "should pass URL-encode URL parameters" do
|
339
|
-
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/foo
|
347
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/foo+bar", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:'{"result":true}', response_headers:{})))
|
340
348
|
ExampleClient.find id:"foo bar"
|
341
349
|
end
|
342
350
|
|
@@ -511,33 +519,84 @@ describe Flexirest::Request do
|
|
511
519
|
end
|
512
520
|
end
|
513
521
|
|
514
|
-
it "should only convert date times in JSON if specified" do
|
522
|
+
it "should only convert date times in JSON if specified when converted is root property" do
|
515
523
|
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:"{\"converted\":\"2012-03-04T01:02:03Z\", \"not_converted\":\"2012-03-04T01:02:03Z\"}", response_headers:{})))
|
516
524
|
object = ExampleClient.conversion id:1234, debug:true
|
517
525
|
expect(object.converted).to be_an_instance_of(DateTime)
|
518
526
|
expect(object.not_converted).to be_an_instance_of(String)
|
519
527
|
end
|
520
528
|
|
521
|
-
it "should convert date times in JSON if
|
529
|
+
it "should only convert date times in JSON if specified when converted is child property" do
|
530
|
+
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:"{\"not_converted\":\"2012-03-04T01:02:03Z\", \"child\":{\"converted_child\":\"2012-03-04T01:02:03Z\"}}", response_headers:{})))
|
531
|
+
object = ExampleClient.conversion_child id:1234, debug:true
|
532
|
+
expect(object.child.converted_child).to be_an_instance_of(DateTime)
|
533
|
+
expect(object.not_converted).to be_an_instance_of(String)
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should convert date times in JSON if root property is whitelisted" do
|
522
537
|
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:"{\"converted\":\"2012-03-04T01:02:03Z\", \"not_converted\":\"2012-03-04T01:02:03Z\"}", response_headers:{})))
|
523
538
|
object = WhitelistedDateClient.conversion id:1234, debug:true
|
524
539
|
expect(object.converted).to be_an_instance_of(DateTime)
|
525
540
|
expect(object.not_converted).to be_an_instance_of(String)
|
526
541
|
end
|
527
542
|
|
528
|
-
it "should
|
529
|
-
|
543
|
+
it "should convert date times in JSON if child property is whitelisted" do
|
544
|
+
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:"{\"not_converted\":\"2012-03-04T01:02:03Z\", \"child\":{\"converted_child\":\"2012-03-04T01:02:03Z\"}}", response_headers:{})))
|
545
|
+
object = WhitelistedDateClient.conversion_child id:1234, debug:true
|
546
|
+
expect(object.child.converted_child).to be_an_instance_of(DateTime)
|
547
|
+
expect(object.not_converted).to be_an_instance_of(String)
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should convert date times in JSON from a result iterator response" do
|
551
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/dates", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[\"2012-03-04T01:02:03Z\", \"2012-03-04T01:02:03Z\"]", response_headers:{})))
|
552
|
+
object = ExampleClient.dates
|
553
|
+
expect(object).to be_a(Flexirest::ResultIterator)
|
554
|
+
expect(object.items).to be_a(Array)
|
555
|
+
expect(object.first).to be_an_instance_of(DateTime)
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should convert date times in JSON even in a pure array" do
|
559
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/dates", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"dates\":[\"2012-03-04T01:02:03Z\", \"2012-03-04T01:02:03Z\"]}", response_headers:{})))
|
560
|
+
object = ExampleClient.dates
|
561
|
+
expect(object).to be_a(ExampleClient)
|
562
|
+
expect(object.dates).to be_a(Array)
|
563
|
+
expect(object.dates.first).to be_an_instance_of(DateTime)
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should parse JSON object and return a nice object for faked responses" do
|
567
|
+
object = ExampleClient.fake_object id:1234, debug:true
|
530
568
|
expect(object.result).to eq(true)
|
531
569
|
expect(object.list.first).to eq(1)
|
532
570
|
expect(object.list.last.test).to eq(true)
|
533
571
|
expect(object.child.grandchild.test).to eq(true)
|
534
572
|
end
|
535
573
|
|
536
|
-
it "should parse JSON from a fake response generated by a proc" do
|
537
|
-
object = ExampleClient.
|
574
|
+
it "should parse JSON object from a fake response generated by a proc" do
|
575
|
+
object = ExampleClient.fake_proc_object id:1234
|
538
576
|
expect(object.result).to eq(1234)
|
539
577
|
end
|
540
578
|
|
579
|
+
it "should parse JSON array and return a nice result iterator for faked responses" do
|
580
|
+
object = ExampleClient.fake_array debug:true
|
581
|
+
expect(object).to be_instance_of(Flexirest::ResultIterator)
|
582
|
+
expect(object.size).to eq(5)
|
583
|
+
expect(object.first).to eq(1)
|
584
|
+
expect(object.last).to eq(nil)
|
585
|
+
expect(object[3]).to be_instance_of(ExampleClient)
|
586
|
+
expect(object[3].test).to eq(true)
|
587
|
+
expect(object._status).to eq(200)
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should parse JSON array from a fake response generated by a proc" do
|
591
|
+
object = ExampleClient.fake_proc_array id:1234
|
592
|
+
expect(object).to be_instance_of(Flexirest::ResultIterator)
|
593
|
+
expect(object.size).to eq(2)
|
594
|
+
expect(object.first).to be_instance_of(ExampleClient)
|
595
|
+
expect(object.first.result).to eq(1234)
|
596
|
+
expect(object.last).to eq(nil)
|
597
|
+
expect(object._status).to eq(200)
|
598
|
+
end
|
599
|
+
|
541
600
|
it "should not parse JSON from a plain request" do
|
542
601
|
response_body = "This is another non-JSON string"
|
543
602
|
expect_any_instance_of(Flexirest::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response_body)))
|
@@ -570,19 +629,36 @@ describe Flexirest::Request do
|
|
570
629
|
expect(ExampleClient.all).to be_truthy
|
571
630
|
end
|
572
631
|
|
573
|
-
it "should return a lazy loader object if lazy loading is enabled" do
|
574
|
-
object = LazyLoadedExampleClient.
|
632
|
+
it "should return a lazy loader object if lazy loading is enabled for JSON object" do
|
633
|
+
object = LazyLoadedExampleClient.fake_object id:1234, debug:true
|
575
634
|
expect(object).to be_an_instance_of(Flexirest::LazyLoader)
|
576
635
|
end
|
577
636
|
|
578
|
-
it "should proxy through nice object for lazy loaded responses" do
|
579
|
-
object = LazyLoadedExampleClient.
|
580
|
-
expect(object.result).to
|
637
|
+
it "should proxy through nice object for lazy loaded responses from JSON object" do
|
638
|
+
object = LazyLoadedExampleClient.fake_object id:1234, debug:true
|
639
|
+
expect(object.instance_variable_get(:@result)).to be(nil)
|
640
|
+
expect(object.result).to eq(true) # method call the attribute received in response and never the instance attribute of the LazyLoader class
|
641
|
+
expect(object.instance_variable_get(:@result)).to be_a(LazyLoadedExampleClient)
|
581
642
|
expect(object.list.first).to eq(1)
|
582
643
|
expect(object.list.last.test).to eq(true)
|
583
644
|
expect(object.child.grandchild.test).to eq(true)
|
584
645
|
end
|
585
646
|
|
647
|
+
it "should return a lazy loader object if lazy loading is enabled for JSON array" do
|
648
|
+
object = LazyLoadedExampleClient.fake_array debug:true
|
649
|
+
expect(object).to be_an_instance_of(Flexirest::LazyLoader)
|
650
|
+
end
|
651
|
+
|
652
|
+
it "should proxy through nice result iterator for lazy loaded responses from JSON array" do
|
653
|
+
object = LazyLoadedExampleClient.fake_array debug:true
|
654
|
+
expect(object.instance_variable_get(:@result)).to be(nil)
|
655
|
+
expect(object.items).to be_a(Array)
|
656
|
+
expect(object.instance_variable_get(:@result)).to be_a(Flexirest::ResultIterator)
|
657
|
+
expect(object.first).to eq(1)
|
658
|
+
expect(object[3].test).to eq(true)
|
659
|
+
expect(object.last.child.grandchild.test).to eq(true)
|
660
|
+
end
|
661
|
+
|
586
662
|
it "should return a LazyAssociationLoader for lazy loaded properties" do
|
587
663
|
object = LazyLoadedExampleClient.lazy_test
|
588
664
|
expect(object.people.size).to eq(1)
|
@@ -592,7 +668,7 @@ describe Flexirest::Request do
|
|
592
668
|
it "should log faked responses" do
|
593
669
|
allow(Flexirest::Logger).to receive(:debug)
|
594
670
|
expect(Flexirest::Logger).to receive(:debug).with(/Faked response found/)
|
595
|
-
ExampleClient.
|
671
|
+
ExampleClient.fake_object id:1234, debug:true
|
596
672
|
end
|
597
673
|
|
598
674
|
it "should parse an array within JSON to be a result iterator" do
|
@@ -628,6 +704,51 @@ describe Flexirest::Request do
|
|
628
704
|
#TODO
|
629
705
|
end
|
630
706
|
|
707
|
+
it "should parse an attribute to be an array if attribute included in a nested array" do
|
708
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/johnny", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"Johnny\", \"nested_array\":[[{\"likes\":[\"donuts\", \"bacon\"], \"dislikes\":[\"politicians\", \"lawyers\", \"taxes\"]}]]}", status:200, response_headers:{})))
|
709
|
+
object = ExampleClient.array
|
710
|
+
nested_array = object.nested_array[0][0]
|
711
|
+
expect(nested_array.likes).to be_instance_of(Array)
|
712
|
+
expect(nested_array.likes.size).to eq(2)
|
713
|
+
expect(nested_array.likes[0]).to eq("donuts")
|
714
|
+
expect(nested_array.likes[1]).to eq("bacon")
|
715
|
+
expect(nested_array.dislikes).to be_instance_of(Array)
|
716
|
+
expect(nested_array.dislikes.size).to eq(3)
|
717
|
+
expect(nested_array.dislikes[0]).to eq("politicians")
|
718
|
+
expect(nested_array.dislikes[1]).to eq("lawyers")
|
719
|
+
expect(nested_array.dislikes[2]).to eq("taxes")
|
720
|
+
#TODO - Pasted from the Test above
|
721
|
+
end
|
722
|
+
|
723
|
+
it "should parse an attribute to be either a result iterator or an array and containing simple values like integers" do
|
724
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/basket", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"products\":[101, 55, 37], \"options\":[854, 225, 772]}", status:200, response_headers:{})))
|
725
|
+
object = ExampleClient.basket
|
726
|
+
expect(object).to be_instance_of(ExampleClient)
|
727
|
+
expect(object.products).to be_a(Flexirest::ResultIterator)
|
728
|
+
expect(object.products.size).to eq(3)
|
729
|
+
expect(object.products.first).to eq(101)
|
730
|
+
expect(object.products[1]).to eq(55)
|
731
|
+
expect(object.products.last).to eq(37)
|
732
|
+
expect(object.options).to be_a(Array)
|
733
|
+
expect(object.options.size).to eq(3)
|
734
|
+
expect(object.options.first).to eq(854)
|
735
|
+
expect(object.options[1]).to eq(225)
|
736
|
+
expect(object.options.last).to eq(772)
|
737
|
+
expect(object._status).to eq(200)
|
738
|
+
#TODO - Pasted from the Test above
|
739
|
+
end
|
740
|
+
|
741
|
+
it "should parse the response to be a result iterator and containing simple values like strings" do
|
742
|
+
expect_any_instance_of(Flexirest::Connection).to receive(:get).with("/fruits", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[\"apple\", \"banana\", \"pear\", \"watermelon\"]", status:200, response_headers:{})))
|
743
|
+
object = ExampleClient.fruits
|
744
|
+
expect(object).to be_instance_of(Flexirest::ResultIterator)
|
745
|
+
expect(object.first).to eq('apple')
|
746
|
+
expect(object[1]).to eq('banana')
|
747
|
+
expect(object.last).to eq('watermelon')
|
748
|
+
expect(object._status).to eq(200)
|
749
|
+
#TODO - Pasted from the Test above
|
750
|
+
end
|
751
|
+
|
631
752
|
it "should only send changed attributes if only_changed:true" do
|
632
753
|
expect_any_instance_of(Flexirest::Connection).to receive(:patch).with("/changed1", "debug=true", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(body:"[{\"first_name\":\"Johnny\"}, {\"first_name\":\"Billy\"}, {\"debug\":\"true\"}]", status:200, response_headers:{})))
|
633
754
|
object = ExampleClient.new
|
data/spec/spec_helper.rb
CHANGED
@@ -66,12 +66,12 @@ class FaradayResponseMock < ::Flexirest::FaradayResponseProxy
|
|
66
66
|
@finished = false
|
67
67
|
end
|
68
68
|
|
69
|
-
def on_complete
|
69
|
+
def on_complete(&block)
|
70
70
|
if @auto_resolve
|
71
71
|
@finished = true
|
72
72
|
yield(@response)
|
73
73
|
else
|
74
|
-
@callback =
|
74
|
+
@callback = block
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flexirest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Jeffries
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -129,6 +129,9 @@ dependencies:
|
|
129
129
|
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: 1.3.1
|
132
|
+
- - "<"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '2.4'
|
132
135
|
type: :development
|
133
136
|
prerelease: false
|
134
137
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -136,6 +139,9 @@ dependencies:
|
|
136
139
|
- - ">="
|
137
140
|
- !ruby/object:Gem::Version
|
138
141
|
version: 1.3.1
|
142
|
+
- - "<"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '2.4'
|
139
145
|
- !ruby/object:Gem::Dependency
|
140
146
|
name: typhoeus
|
141
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -364,7 +370,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
364
370
|
- !ruby/object:Gem::Version
|
365
371
|
version: '0'
|
366
372
|
requirements: []
|
367
|
-
rubygems_version: 3.
|
373
|
+
rubygems_version: 3.1.2
|
368
374
|
signing_key:
|
369
375
|
specification_version: 4
|
370
376
|
summary: This gem is for accessing REST services in a flexible way. ActiveResource
|