flexirest 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::ConnectionManager do
4
+ before(:each) do
5
+ Flexirest::ConnectionManager.reset!
6
+ end
7
+
8
+ it "should have a get_connection method" do
9
+ expect(Flexirest::ConnectionManager).to respond_to("get_connection")
10
+ end
11
+
12
+ it "should return a connection for a given base url" do
13
+ connection = Flexirest::ConnectionManager.get_connection("http://www.example.com")
14
+ expect(connection).to be_kind_of(Flexirest::Connection)
15
+ end
16
+
17
+ it "should return the same connection for each base url when re-requested" do
18
+ connection = Flexirest::ConnectionManager.get_connection("http://www.example.com")
19
+ expect(Flexirest::ConnectionManager.get_connection("http://www.example.com")).to eq(connection)
20
+ end
21
+
22
+ it "should return different connections for each base url when requested" do
23
+ base_url = "http://www.example.com"
24
+ other_base_url = "http://other.example.com"
25
+ expect(Flexirest::ConnectionManager.get_connection(base_url).base_url).to eq(base_url)
26
+ expect(Flexirest::ConnectionManager.get_connection(other_base_url).base_url).to eq(other_base_url)
27
+ expect(Thread.current[:_connections].size).to eq(2)
28
+ end
29
+
30
+ it "should find a connection if you pass in URLs containing an existing connection's base_url" do
31
+ base_url = "http://www.example.com"
32
+ connection = Flexirest::ConnectionManager.get_connection(base_url)
33
+ found_connection = Flexirest::ConnectionManager.find_connection_for_url("#{base_url}:8080/people/test")
34
+ expect(found_connection).to eq(connection)
35
+ end
36
+
37
+ it "should call 'in_parllel' for a session and yield procedure inside that block" do
38
+ Flexirest::Base.adapter = :typhoeus
39
+ session = Flexirest::ConnectionManager.get_connection("http://www.example.com").session
40
+ expect { |b| Flexirest::ConnectionManager.in_parallel("http://www.example.com", &b)}.to yield_control
41
+ Flexirest::Base._reset_configuration!
42
+ end
43
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::Connection do
4
+ before do
5
+ @connection = Flexirest::Connection.new("http://www.example.com")
6
+ end
7
+
8
+ after do
9
+ Flexirest::Base._reset_configuration!
10
+ @connection.reconnect
11
+ end
12
+
13
+ it "should contain a Farday connection" do
14
+ expect(@connection.session).to be_a_kind_of(Faraday::Connection)
15
+ end
16
+
17
+ it "should set the Base URL to be the one passed in" do
18
+ expect(@connection.session.url_prefix.to_s).to eq("http://www.example.com/")
19
+ end
20
+
21
+ it "should set a user agent for the session" do
22
+ expect(@connection.headers["User-Agent"]).to match(/^Flexirest\/[0-9.]+$/)
23
+ end
24
+
25
+ it "should try to Keep-Alive session connections" do
26
+ expect(@connection.headers["Connection"]).to match(/Keep-Alive/)
27
+ end
28
+
29
+ it "should pass a GET request through to Faraday" do
30
+ stub_request(:get, "www.example.com/foo").to_return(body: "{result:true}")
31
+ result = @connection.get("/foo")
32
+ expect(result.body).to eq("{result:true}")
33
+ end
34
+
35
+ it "should pass a PUT request through to Faraday" do
36
+ stub_request(:put, "www.example.com/foo").with(body: "body").to_return(body: "{result:true}")
37
+ result = @connection.put("/foo", "body")
38
+ expect(result.body).to eq("{result:true}")
39
+ end
40
+
41
+ it "should pass a POST request through to Faraday" do
42
+ stub_request(:post, "www.example.com/foo").with(body: "body").to_return(body: "{result:true}")
43
+ result = @connection.post("/foo", "body")
44
+ expect(result.body).to eq("{result:true}")
45
+ end
46
+
47
+ it "should pass a DELETE request through to Faraday" do
48
+ stub_request(:delete, "www.example.com/foo").to_return(body: "{result:true}")
49
+ result = @connection.delete("/foo")
50
+ expect(result.body).to eq("{result:true}")
51
+ end
52
+
53
+ describe "with default Faraday headers" do
54
+ before do
55
+ @default_headers = { "User-Agent" => "Custom" }
56
+
57
+ Flexirest::Base.faraday_config do |faraday|
58
+ faraday.adapter Flexirest::Base.adapter
59
+ faraday.headers.update(@default_headers)
60
+ end
61
+ @connection.reconnect
62
+ end
63
+
64
+ it "should pass a GET request through to Faraday preserving headers" do
65
+ stub_request(:get, "www.example.com/foo").
66
+ with(:headers => @default_headers).
67
+ to_return(body: "{result:true}")
68
+
69
+ result = @connection.get("/foo")
70
+ expect(result.body).to eq("{result:true}")
71
+ end
72
+
73
+ it "should pass a PUT request through to Faraday" do
74
+ stub_request(:put, "www.example.com/foo").
75
+ with(body: "body").
76
+ to_return(body: "{result:true}", :headers => @default_headers)
77
+
78
+ result = @connection.put("/foo", "body")
79
+ expect(result.body).to eq("{result:true}")
80
+ end
81
+
82
+ it "should pass a POST request through to Faraday" do
83
+ stub_request(:post, "www.example.com/foo").
84
+ with(body: "body", :headers => @default_headers).
85
+ to_return(body: "{result:true}")
86
+
87
+ result = @connection.post("/foo", "body")
88
+ expect(result.body).to eq("{result:true}")
89
+ end
90
+
91
+ it "should pass a DELETE request through to Faraday" do
92
+ stub_request(:delete, "www.example.com/foo").
93
+ with(:headers => @default_headers).
94
+ to_return(body: "{result:true}")
95
+
96
+ result = @connection.delete("/foo")
97
+ expect(result.body).to eq("{result:true}")
98
+ end
99
+ end
100
+
101
+ context 'with api auth signing requests' do
102
+ before(:each) do
103
+ # Need to still call this to load the api_auth library so tests work
104
+ Flexirest::Base.api_auth_credentials('id123', 'secret123')
105
+
106
+ @options = {
107
+ :api_auth => {
108
+ :api_auth_access_id => 'id123',
109
+ :api_auth_secret_key => 'secret123'
110
+ }
111
+ }
112
+
113
+ @default_headers = {'Date' => 'Sat, 14 Mar 2015 15:13:24 GMT'}
114
+
115
+ Flexirest::Base.faraday_config do |faraday|
116
+ faraday.adapter Flexirest::Base.adapter
117
+ faraday.headers.update(@default_headers)
118
+ end
119
+ @connection.reconnect
120
+ end
121
+
122
+ it 'should have an Authorization header' do
123
+ stub_request(:get, "www.example.com/foo")
124
+ .with(:headers => @default_headers)
125
+ .to_return(body: "{result:true}")
126
+ result = @connection.get("/foo", @options)
127
+ expect(result.env.request_headers['Authorization']).to eq("APIAuth id123:PMWBThkB8vKbvUccHvoqu9G3eVk=")
128
+ end
129
+
130
+ it 'should have an Content-MD5 header' do
131
+ stub_request(:put, "www.example.com/foo").
132
+ with(body: "body", :headers => @default_headers).
133
+ to_return(body: "{result:true}")
134
+
135
+ result = @connection.put("/foo", "body", @options)
136
+ expect(result.env.request_headers['Content-MD5']).to eq("hBotaJrYa9FhFEdFPCLG/A==")
137
+ end
138
+ end
139
+
140
+ it "should retry once in the event of a connection failed" do
141
+ stub_request(:get, "www.example.com/foo").to_raise(Faraday::Error::ConnectionFailed.new("Foo"))
142
+ expect { @connection.get("/foo") }.to raise_error(Flexirest::ConnectionFailedException)
143
+ end
144
+
145
+ it "should raise an exception on timeout" do
146
+ stub_request(:get, "www.example.com/foo").to_timeout
147
+ expect { @connection.get("/foo") }.to raise_error(Flexirest::TimeoutException)
148
+ end
149
+
150
+ it "should raise an exception on timeout" do
151
+ stub_request(:get, "www.example.com/foo").to_timeout
152
+ begin
153
+ @connection.get("foo")
154
+ fail
155
+ rescue Flexirest::TimeoutException => timeout
156
+ expect(timeout.message).to eq("Timed out getting http://www.example.com/foo")
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flexirest::HeadersList do
4
+ let(:headers_list) { Flexirest::HeadersList.new }
5
+
6
+ it "should remember stored headers" do
7
+ url = "http://www.google.com"
8
+ headers_list["X-My-Header"] = url
9
+ expect(headers_list["X-My-Header"]).to eq(url)
10
+ end
11
+
12
+ it "should remember overwrite normal headers" do
13
+ url = "http://www.google.com"
14
+ headers_list["X-My-Header"] = "SHOULD NEVER BE SEEN"
15
+ headers_list["X-My-Header"] = url
16
+ expect(headers_list["X-My-Header"]).to eq(url)
17
+ end
18
+
19
+ it "should append to specific headers, such as Set-cookie" do
20
+ headers_list["Set-Cookie"] = "first_value"
21
+ headers_list["Set-Cookie"] = "second_value"
22
+ expect(headers_list["Set-Cookie"]).to eq(%w{first_value second_value})
23
+ end
24
+
25
+ it "should not be case sensitive on header names when setting headers" do
26
+ url = "http://www.google.com"
27
+ headers_list["X-My-Header"] = "SHOULD NEVER BE SEEN"
28
+ headers_list["X-MY-HEADER"] = url
29
+ expect(headers_list["X-My-Header"]).to eq(url)
30
+ end
31
+
32
+ it "should not be case sensitive on header names when getting headers" do
33
+ url = "http://www.google.com"
34
+ headers_list["X-My-Header"] = url
35
+ expect(headers_list["X-MY-HEADER"]).to eq(url)
36
+ end
37
+
38
+ it "should allow iterating over headers set, by default with array items returned whole" do
39
+ headers_list["X-My-Header"] = "http://www.google.com"
40
+ headers_list["Set-Cookie"] = "first_value"
41
+ headers_list["SET-COOKIE"] = "second_value"
42
+ values = []
43
+ headers_list.each do |name, value|
44
+ values << "#{name}=#{value.to_s}"
45
+ end
46
+ expect(values.size).to eq(2)
47
+ expect(values).to eq(["X-My-Header=http://www.google.com", "Set-Cookie=[\"first_value\", \"second_value\"]"])
48
+ end
49
+
50
+ it "should allow iterating over headers set splitting array headers in to individual ones" do
51
+ headers_list["X-My-Header"] = "http://www.google.com"
52
+ headers_list["Set-Cookie"] = "first_value"
53
+ headers_list["SET-COOKIE"] = "second_value"
54
+ values = []
55
+ headers_list.each(true) do |name, value|
56
+ values << "#{name}=#{value.to_s}"
57
+ end
58
+ expect(values.size).to eq(3)
59
+ expect(values).to eq(["X-My-Header=http://www.google.com", "Set-Cookie=first_value", "Set-Cookie=second_value"])
60
+ end
61
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ class InstrumentationExampleClient < Flexirest::Base
4
+ base_url "http://www.example.com"
5
+ get :fake, "/fake", fake:"{\"result\":true, \"list\":[1,2,3,{\"test\":true}], \"child\":{\"grandchild\":{\"test\":true}}}"
6
+ get :real, "/real"
7
+ end
8
+
9
+ describe Flexirest::Instrumentation do
10
+ it "should save a load hook to include the instrumentation" do
11
+ hook_tester = double("HookTester")
12
+ expect(hook_tester).to receive(:include).with(Flexirest::ControllerInstrumentation)
13
+ ActiveSupport.run_load_hooks(:action_controller, hook_tester)
14
+ end
15
+
16
+ it "should call ActiveSupport::Notifications.instrument when making any request" do
17
+ expect(ActiveSupport::Notifications).to receive(:instrument).with("request_call.flexirest", {:name=>"InstrumentationExampleClient#fake"})
18
+ InstrumentationExampleClient.fake
19
+ end
20
+
21
+ it "should call ActiveSupport::Notifications#request_call when making any request" do
22
+ expect_any_instance_of(Flexirest::Instrumentation).to receive(:request_call).with(an_instance_of(ActiveSupport::Notifications::Event))
23
+ InstrumentationExampleClient.fake
24
+ end
25
+
26
+
27
+ it "should log time spent in each API call" do
28
+ expect_any_instance_of(Flexirest::Connection).
29
+ to receive(:get).
30
+ with("/real", an_instance_of(Hash)).
31
+ and_return(::FaradayResponseMock.new(OpenStruct.new(body:"{\"first_name\":\"John\", \"id\":1234}", response_headers:{}, status:200)))
32
+ expect(Flexirest::Logger).to receive(:debug).with(/Flexirest.*ms\)/)
33
+ expect(Flexirest::Logger).to receive(:debug).at_least(:once).with(any_args)
34
+ InstrumentationExampleClient.real
35
+ end
36
+
37
+
38
+ it "should report the total time spent" do
39
+ # Create a couple of classes to fake being part of ActionController (that would normally call this method)
40
+ class InstrumentationTimeSpentExampleClientParent
41
+ def append_info_to_payload(payload) ; {} ; end
42
+ def self.log_process_action(payload) ; [] ; end
43
+ end
44
+
45
+ class InstrumentationTimeSpentExampleClient < InstrumentationTimeSpentExampleClientParent
46
+ include Flexirest::ControllerInstrumentation
47
+
48
+ def test
49
+ payload = {}
50
+ append_info_to_payload(payload)
51
+ self.class.log_process_action(payload)
52
+ end
53
+ end
54
+
55
+ messages = InstrumentationTimeSpentExampleClient.new.test
56
+ expect(messages.first).to match(/Flexirest.*ms.*call/)
57
+ end
58
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ class MonthExample < Flexirest::Base
4
+ base_url "http://www.example.com"
5
+
6
+ get :find, "/month/:id", fake:"{\"name\":\"january\"}"
7
+ end
8
+
9
+ class YearExample < Flexirest::Base
10
+ base_url "http://www.example.com"
11
+
12
+ get :find, "/year/:id", lazy: { months: MonthExample }, fake: "{\"months\": [\"http://www.example.com/months/1\"] }"
13
+ end
14
+
15
+ describe Flexirest::LazyAssociationLoader do
16
+ let(:url1) { "http://www.example.com/some/url" }
17
+ let(:url2) { "http://www.example.com/some/other" }
18
+ let(:calling_object) { o = double("Object").as_null_object }
19
+ let(:request) { Flexirest::Request.new({:method => :get, url:"http://api.example.com/v1/foo"}, calling_object) }
20
+
21
+ it "should raise an exception if you initialize it with a value that is not a string, hash or array" do
22
+ expect do
23
+ Flexirest::LazyAssociationLoader.new(:person, OpenStruct.new, nil)
24
+ end.to raise_error(Flexirest::InvalidLazyAssociationContentException)
25
+ end
26
+
27
+ it "should store a URL passed as a string to the new object during creation" do
28
+ loader = Flexirest::LazyAssociationLoader.new(:person, url1, nil)
29
+ expect(loader.instance_variable_get(:@url)).to eq(url1)
30
+ end
31
+
32
+ it "should store a URL from a hash passed to the new object during creation" do
33
+ loader = Flexirest::LazyAssociationLoader.new(:person, {"url" => url1}, nil)
34
+ expect(loader.instance_variable_get(:@url)).to eq(url1)
35
+ end
36
+
37
+ it "should store a list of URLs from an array passed to the new object during creation" do
38
+ loader = Flexirest::LazyAssociationLoader.new(:person, [url1, url2], nil)
39
+ array = loader.instance_variable_get(:@subloaders)
40
+ expect(array[0].instance_variable_get(:@url)).to eq(url1)
41
+ expect(array[1].instance_variable_get(:@url)).to eq(url2)
42
+ expect(array[2]).to be_nil
43
+ end
44
+
45
+ it "should store a hash of URLs from a hash passed to the new object during creation" do
46
+ loader = Flexirest::LazyAssociationLoader.new(:person, {"main" => url1, "thumb" => url2}, request)
47
+ expect(loader.main.instance_variable_get(:@url)).to eq(url1)
48
+ expect(loader.thumb.instance_variable_get(:@url)).to eq(url2)
49
+ expect(loader.size).to eq(2)
50
+ end
51
+
52
+ it "should still be able to iterate over a hash of URLs from a hash passed to the new object during creation" do
53
+ loader = Flexirest::LazyAssociationLoader.new(:person, {"main" => url1, "thumb" => url2}, request)
54
+ output = []
55
+ loader.each do |k, v|
56
+ output << v.instance_variable_get(:@url)
57
+ end
58
+ expect(output.size).to eq(2)
59
+ expect(output[0]).to eq(url1)
60
+ expect(output[1]).to eq(url2)
61
+ expect(output[2]).to be_nil
62
+ end
63
+
64
+ it "should be able to list the keys from a hash passed to the new object during creation" do
65
+ loader = Flexirest::LazyAssociationLoader.new(:person, {"main" => url1, "thumb" => url2}, request)
66
+ expect(loader.keys[0]).to eq(:main)
67
+ expect(loader.keys[1]).to eq(:thumb)
68
+ expect(loader.keys.size).to eq(2)
69
+ end
70
+
71
+ it "should report the size of a list of stored URLs" do
72
+ loader = Flexirest::LazyAssociationLoader.new(:person, [url1, url2], nil)
73
+ expect(loader.size).to eq(2)
74
+ end
75
+
76
+ it "should respond to each and iterate through the list of stored URLs" do
77
+ loader = Flexirest::LazyAssociationLoader.new(:person, [url1, url2], nil)
78
+ output = []
79
+ loader.each do |o|
80
+ output << o.instance_variable_get(:@url)
81
+ end
82
+ expect(output.size).to eq(2)
83
+ expect(output[0]).to eq(url1)
84
+ expect(output[1]).to eq(url2)
85
+ expect(output[2]).to be_nil
86
+ end
87
+
88
+ it "should return a LazyAssociationLoader for each stored URL in a list" do
89
+ loader = Flexirest::LazyAssociationLoader.new(:person, [url1, url2], nil)
90
+ output = []
91
+ loader.each do |o|
92
+ expect(o).to be_an_instance_of(Flexirest::LazyAssociationLoader)
93
+ end
94
+ end
95
+
96
+ it "should make the request for a URL if it's accessed" do
97
+ method_data = {options:{url:"foo"}}
98
+ request = double("Request").as_null_object
99
+ allow(request).to receive(:method).and_return(method_data)
100
+ expect(request).to receive(:object).with(any_args).and_return(Array.new)
101
+ expect(request).to receive(:call).with(any_args).and_return("")
102
+ expect(Flexirest::Request).to receive(:new).with(any_args).and_return(request)
103
+ loader = Flexirest::LazyAssociationLoader.new(:person, url1, request)
104
+ loader.length
105
+ end
106
+
107
+ it "should proxy methods to the underlying object if the request has been made" do
108
+ loader = Flexirest::LazyAssociationLoader.new(:person, url1, request)
109
+ object = double("Object")
110
+ expect(object).to receive(:length).and_return(1)
111
+ loader.instance_variable_set(:@object, object)
112
+ expect(loader.length).to eq(1)
113
+ end
114
+
115
+ it "should be able to iterate underlying object if it's an array" do
116
+ loader = Flexirest::LazyAssociationLoader.new(:person, url1, request)
117
+ expect_any_instance_of(Flexirest::Request).to receive(:call).with(any_args).and_return([1,2,3])
118
+ test = []
119
+ loader.each do |item|
120
+ test << item
121
+ end
122
+ expect(test).to eq([1,2,3])
123
+ end
124
+
125
+ it "should be able to return the size of the underlying object if it's an array" do
126
+ loader = Flexirest::LazyAssociationLoader.new(:person, url1, request)
127
+ expect_any_instance_of(Flexirest::Request).to receive(:call).with(any_args).and_return([1,2,3])
128
+ expect(loader.size).to eq(3)
129
+ end
130
+
131
+ it "should use the class specified in the 'lazy' declaration to parse the response rather than the class of the object the lazy loader is attached to" do
132
+ association = YearExample.find(1)
133
+ expect(association.months.instance_variable_get('@request').instance_variable_get('@object').class).to eq(MonthExample)
134
+ end
135
+ end