ddy_remote_resource 0.4.2
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +182 -0
- data/Rakefile +7 -0
- data/lib/extensions/ethon/easy/queryable.rb +36 -0
- data/lib/remote_resource.rb +64 -0
- data/lib/remote_resource/base.rb +126 -0
- data/lib/remote_resource/builder.rb +53 -0
- data/lib/remote_resource/collection.rb +31 -0
- data/lib/remote_resource/connection.rb +24 -0
- data/lib/remote_resource/connection_options.rb +41 -0
- data/lib/remote_resource/http_errors.rb +33 -0
- data/lib/remote_resource/querying/finder_methods.rb +34 -0
- data/lib/remote_resource/querying/persistence_methods.rb +38 -0
- data/lib/remote_resource/request.rb +106 -0
- data/lib/remote_resource/response.rb +69 -0
- data/lib/remote_resource/response_handeling.rb +48 -0
- data/lib/remote_resource/rest.rb +29 -0
- data/lib/remote_resource/url_naming.rb +34 -0
- data/lib/remote_resource/url_naming_determination.rb +39 -0
- data/lib/remote_resource/version.rb +3 -0
- data/remote_resource.gemspec +32 -0
- data/spec/lib/extensions/ethon/easy/queryable_spec.rb +135 -0
- data/spec/lib/remote_resource/base_spec.rb +388 -0
- data/spec/lib/remote_resource/builder_spec.rb +245 -0
- data/spec/lib/remote_resource/collection_spec.rb +148 -0
- data/spec/lib/remote_resource/connection_options_spec.rb +124 -0
- data/spec/lib/remote_resource/connection_spec.rb +61 -0
- data/spec/lib/remote_resource/querying/finder_methods_spec.rb +105 -0
- data/spec/lib/remote_resource/querying/persistence_methods_spec.rb +174 -0
- data/spec/lib/remote_resource/request_spec.rb +594 -0
- data/spec/lib/remote_resource/response_spec.rb +196 -0
- data/spec/lib/remote_resource/rest_spec.rb +98 -0
- data/spec/lib/remote_resource/url_naming_determination_spec.rb +225 -0
- data/spec/lib/remote_resource/url_naming_spec.rb +72 -0
- data/spec/lib/remote_resource/version_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- metadata +242 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteResource::Connection do
|
4
|
+
|
5
|
+
module RemoteResource
|
6
|
+
class ConnectionDummy
|
7
|
+
include RemoteResource::Base
|
8
|
+
|
9
|
+
self.site = 'https://foobar.com'
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:dummy_class) { RemoteResource::ConnectionDummy }
|
15
|
+
let(:dummy) { dummy_class.new }
|
16
|
+
|
17
|
+
describe '.connection' do
|
18
|
+
it 'uses Typhoeus::Request' do
|
19
|
+
expect(dummy_class.connection).to eql Typhoeus::Request
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.content_type' do
|
24
|
+
let!(:original_content_type) { dummy_class.content_type }
|
25
|
+
|
26
|
+
context 'when content_type is set' do
|
27
|
+
it 'returns the given content_type' do
|
28
|
+
dummy_class.content_type = '.html'
|
29
|
+
|
30
|
+
expect(dummy_class.content_type).to eql '.html'
|
31
|
+
|
32
|
+
dummy_class.content_type = original_content_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when NO content_type is set' do
|
37
|
+
it 'returns the default content_type' do
|
38
|
+
expect(dummy_class.content_type).to eql '.json'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.headers' do
|
44
|
+
context 'when .extra_headers are set' do
|
45
|
+
it 'returns the default headers merged with the set .extra_headers' do
|
46
|
+
dummy_class.extra_headers = { "Foo" => "Bar" }
|
47
|
+
|
48
|
+
expect(dummy_class.headers).to eql({ "Accept" => "application/json", "Foo" => "Bar" })
|
49
|
+
|
50
|
+
dummy_class.extra_headers = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when NO .extra_headers are set' do
|
55
|
+
it 'returns the default headers' do
|
56
|
+
expect(dummy_class.headers).to eql({ "Accept" => "application/json" })
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteResource::Querying::FinderMethods do
|
4
|
+
|
5
|
+
module RemoteResource
|
6
|
+
module Querying
|
7
|
+
class FinderMethodsDummy
|
8
|
+
include RemoteResource::Base
|
9
|
+
|
10
|
+
self.site = 'https://foobar.com'
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:dummy_class) { RemoteResource::Querying::FinderMethodsDummy }
|
17
|
+
let(:dummy) { dummy_class.new }
|
18
|
+
|
19
|
+
describe '.find' do
|
20
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
21
|
+
|
22
|
+
before do
|
23
|
+
allow(dummy_class).to receive(:build_resource_from_response) { dummy }
|
24
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'performs a RemoteResource::Request with the connection_options no_params' do
|
28
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, { id: '12' }, { no_params: true }).and_call_original
|
29
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
30
|
+
dummy_class.find '12'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'builds the resource from the RemoteResource::Response' do
|
34
|
+
expect(dummy_class).to receive(:build_resource_from_response).with response
|
35
|
+
dummy_class.find '12'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.find_by' do
|
40
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
41
|
+
let(:params) do
|
42
|
+
{ id: '12' }
|
43
|
+
end
|
44
|
+
|
45
|
+
before do
|
46
|
+
allow(dummy_class).to receive(:build_resource_from_response) { dummy }
|
47
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'performs a RemoteResource::Request' do
|
51
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, {}).and_call_original
|
52
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
53
|
+
dummy_class.find_by params
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'builds the resource from the RemoteResource::Response' do
|
57
|
+
expect(dummy_class).to receive(:build_resource_from_response).with response
|
58
|
+
dummy_class.find_by params
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.all' do
|
63
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
64
|
+
|
65
|
+
before do
|
66
|
+
allow(dummy_class).to receive(:build_collection_from_response) { dummy }
|
67
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'performs a RemoteResource::Request with the connection_options collection' do
|
71
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, {}, { collection: true }).and_call_original
|
72
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
73
|
+
dummy_class.all
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'builds the resources from the RemoteResource::Response' do
|
77
|
+
expect(dummy_class).to receive(:build_collection_from_response).with response
|
78
|
+
dummy_class.all
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '.where' do
|
83
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
84
|
+
let(:params) do
|
85
|
+
{ username: 'mies' }
|
86
|
+
end
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(dummy_class).to receive(:build_collection_from_response) { dummy }
|
90
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'performs a RemoteResource::Request with the connection_options collection' do
|
94
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, { collection: true }).and_call_original
|
95
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
96
|
+
dummy_class.where params
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'builds the resources from the RemoteResource::Response' do
|
100
|
+
expect(dummy_class).to receive(:build_collection_from_response).with response
|
101
|
+
dummy_class.where params
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteResource::Querying::PersistenceMethods do
|
4
|
+
|
5
|
+
module RemoteResource
|
6
|
+
module Querying
|
7
|
+
class PersistenceMethodsDummy
|
8
|
+
include RemoteResource::Base
|
9
|
+
|
10
|
+
self.site = 'https://foobar.com'
|
11
|
+
|
12
|
+
attribute :name
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:dummy_class) { RemoteResource::Querying::PersistenceMethodsDummy }
|
19
|
+
let(:dummy) { dummy_class.new }
|
20
|
+
|
21
|
+
describe '.create' do
|
22
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
23
|
+
let(:attributes) do
|
24
|
+
{ name: 'Mies' }
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
allow_any_instance_of(dummy_class).to receive(:handle_response)
|
29
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'instantiates the resource with the attributes' do
|
33
|
+
expect(dummy_class).to receive(:new).with(attributes).and_call_original
|
34
|
+
dummy_class.create attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'performs a RemoteResource::Request' do
|
38
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy_class, :post, attributes, {}).and_call_original
|
39
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
40
|
+
dummy_class.create attributes
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'handles the RemoteResource::Response' do
|
44
|
+
expect_any_instance_of(dummy_class).to receive(:handle_response).with response
|
45
|
+
dummy_class.create attributes
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#update_attributes' do
|
50
|
+
let(:dummy) { dummy_class.new id: 10 }
|
51
|
+
|
52
|
+
let(:attributes) do
|
53
|
+
{ name: 'Noot' }
|
54
|
+
end
|
55
|
+
|
56
|
+
before do
|
57
|
+
allow(dummy).to receive(:create_or_update) { dummy }
|
58
|
+
allow(dummy).to receive(:success?)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'rebuilds the resource with the attributes' do
|
62
|
+
expect(dummy).to receive(:rebuild_resource).with(attributes).and_call_original
|
63
|
+
dummy.update_attributes attributes
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when the id is given in the attributes' do
|
67
|
+
let(:attributes) do
|
68
|
+
{ id: 14, name: 'Noot' }
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'calls #create_or_update with the attributes and given id' do
|
72
|
+
expect(dummy).to receive(:create_or_update).with(attributes, {})
|
73
|
+
dummy.update_attributes attributes
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when the id is NOT given in the attributes' do
|
78
|
+
it 'calls #create_or_update with the attributes and #id of resource' do
|
79
|
+
expect(dummy).to receive(:create_or_update).with(attributes.merge(id: dummy.id), {})
|
80
|
+
dummy.update_attributes attributes
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when the save was successful' do
|
85
|
+
it 'returns the resource' do
|
86
|
+
allow(dummy).to receive(:success?) { true }
|
87
|
+
|
88
|
+
expect(dummy.update_attributes attributes).to eql dummy
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the save was NOT successful' do
|
93
|
+
it 'returns false' do
|
94
|
+
allow(dummy).to receive(:success?) { false }
|
95
|
+
|
96
|
+
expect(dummy.update_attributes attributes).to eql false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#save' do
|
102
|
+
let(:attributes) { dummy.attributes }
|
103
|
+
|
104
|
+
before do
|
105
|
+
allow(dummy).to receive(:create_or_update) { dummy }
|
106
|
+
allow(dummy).to receive(:success?)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'calls #create_or_update with the attributes' do
|
110
|
+
expect(dummy).to receive(:create_or_update).with(attributes, {})
|
111
|
+
dummy.save
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'when the save was successful' do
|
115
|
+
it 'returns the resource' do
|
116
|
+
allow(dummy).to receive(:success?) { true }
|
117
|
+
|
118
|
+
expect(dummy.save).to eql dummy
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when the save was NOT successful' do
|
123
|
+
it 'returns false' do
|
124
|
+
allow(dummy).to receive(:success?) { false }
|
125
|
+
|
126
|
+
expect(dummy.save).to eql false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#create_or_update' do
|
132
|
+
let(:response) { instance_double(RemoteResource::Response) }
|
133
|
+
|
134
|
+
before do
|
135
|
+
allow(dummy).to receive(:handle_response) { dummy }
|
136
|
+
allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'when the attributes contain an id' do
|
140
|
+
let(:attributes) do
|
141
|
+
{ id: 10, name: 'Kees' }
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'performs a RemoteResource::Request with rest_action :patch' do
|
145
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy, :patch, attributes, {}).and_call_original
|
146
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
147
|
+
dummy.create_or_update attributes
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'handles the RemoteResource::Response' do
|
151
|
+
expect(dummy).to receive(:handle_response).with response
|
152
|
+
dummy.create_or_update attributes
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'when the attributes do NOT contain an id' do
|
157
|
+
let(:attributes) do
|
158
|
+
{ name: 'Mies' }
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'performs a RemoteResource::Request with rest_action :post' do
|
162
|
+
expect(RemoteResource::Request).to receive(:new).with(dummy, :post, attributes, {}).and_call_original
|
163
|
+
expect_any_instance_of(RemoteResource::Request).to receive(:perform)
|
164
|
+
dummy.create_or_update attributes
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'handles the RemoteResource::Response' do
|
168
|
+
expect(dummy).to receive(:handle_response).with response
|
169
|
+
dummy.create_or_update attributes
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,594 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteResource::Request do
|
4
|
+
|
5
|
+
module RemoteResource
|
6
|
+
class RequestDummy
|
7
|
+
include RemoteResource::Base
|
8
|
+
|
9
|
+
self.site = 'http://www.foobar.com'
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:dummy_class) { RemoteResource::RequestDummy }
|
17
|
+
let(:dummy) { dummy_class.new id: '12' }
|
18
|
+
|
19
|
+
let(:resource) { dummy_class }
|
20
|
+
let(:rest_action) { :get }
|
21
|
+
let(:connection_options) { {} }
|
22
|
+
let(:attributes) do
|
23
|
+
{ name: 'Mies' }
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:request) { described_class.new resource, rest_action, attributes, connection_options.dup }
|
27
|
+
|
28
|
+
specify { expect(described_class).to include RemoteResource::HTTPErrors }
|
29
|
+
|
30
|
+
describe '#connection' do
|
31
|
+
it 'uses the connection of the resource_klass' do
|
32
|
+
expect(request.connection).to eql Typhoeus::Request
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#connection_options' do
|
37
|
+
let(:threaded_connection_options_thread_name) { 'remote_resource.request_dummy.threaded_connection_options' }
|
38
|
+
|
39
|
+
before { Thread.current[threaded_connection_options_thread_name] = threaded_connection_options }
|
40
|
+
after { Thread.current[threaded_connection_options_thread_name] = nil }
|
41
|
+
|
42
|
+
context 'when the given connection_options contain other values than the resource threaded_connection_options or connection_options' do
|
43
|
+
let(:connection_options) do
|
44
|
+
{
|
45
|
+
site: 'http://www.barbaz.com',
|
46
|
+
collection: true,
|
47
|
+
path_prefix: '/api',
|
48
|
+
root_element: :bazbar
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:threaded_connection_options) do
|
53
|
+
{
|
54
|
+
site: 'http://www.bazbazbaz.com',
|
55
|
+
path_prefix: '/registration',
|
56
|
+
path_postfix: '/promotion',
|
57
|
+
root_element: :bazbazbaz
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'merges the given connection_options with the resource connection_options while taking precedence over the resource connection_options after the threaded_connection_options' do
|
62
|
+
expect(request.connection_options[:site]).to eql 'http://www.barbaz.com'
|
63
|
+
expect(request.connection_options[:collection]).to eql true
|
64
|
+
expect(request.connection_options[:path_prefix]).to eql '/api'
|
65
|
+
expect(request.connection_options[:path_postfix]).to eql '/promotion'
|
66
|
+
expect(request.connection_options[:root_element]).to eql :bazbar
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when the given connection_options do NOT contain other values than the resource threaded_connection_options or connection_options' do
|
71
|
+
let(:connection_options) do
|
72
|
+
{
|
73
|
+
collection: true,
|
74
|
+
path_prefix: '/api',
|
75
|
+
root_element: :bazbar
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
let(:threaded_connection_options) do
|
80
|
+
{
|
81
|
+
site: 'http://www.bazbazbaz.com',
|
82
|
+
path_prefix: '/api',
|
83
|
+
path_postfix: '/promotion'
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'merges the given connection_options with the resource threaded_connection_options and connection_options' do
|
88
|
+
expect(request.connection_options[:site]).to eql 'http://www.bazbazbaz.com'
|
89
|
+
expect(request.connection_options[:collection]).to eql true
|
90
|
+
expect(request.connection_options[:path_prefix]).to eql '/api'
|
91
|
+
expect(request.connection_options[:path_postfix]).to eql '/promotion'
|
92
|
+
expect(request.connection_options[:root_element]).to eql :bazbar
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#original_connection_options' do
|
98
|
+
let(:threaded_connection_options_thread_name) { 'remote_resource.request_dummy.threaded_connection_options' }
|
99
|
+
|
100
|
+
before { Thread.current[threaded_connection_options_thread_name] = threaded_connection_options }
|
101
|
+
after { Thread.current[threaded_connection_options_thread_name] = nil }
|
102
|
+
|
103
|
+
context 'when the given connection_options (original_connection_options) contain other values than the resource threaded_connection_options' do
|
104
|
+
let(:connection_options) do
|
105
|
+
{
|
106
|
+
site: 'http://www.barbaz.com',
|
107
|
+
collection: true,
|
108
|
+
path_prefix: '/api',
|
109
|
+
root_element: :bazbar
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
let(:threaded_connection_options) do
|
114
|
+
{
|
115
|
+
site: 'http://www.bazbazbaz.com',
|
116
|
+
path_prefix: '/registration',
|
117
|
+
path_postfix: '/promotion',
|
118
|
+
root_element: :bazbazbaz
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'merges the given connection_options (original_connection_options) with the resource threaded_connection_options while taking precedence over the resource threaded_connection_options' do
|
123
|
+
expect(request.original_connection_options[:site]).to eql 'http://www.barbaz.com'
|
124
|
+
expect(request.original_connection_options[:collection]).to eql true
|
125
|
+
expect(request.original_connection_options[:path_prefix]).to eql '/api'
|
126
|
+
expect(request.original_connection_options[:path_postfix]).to eql '/promotion'
|
127
|
+
expect(request.original_connection_options[:root_element]).to eql :bazbar
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when the given connection_options (original_connection_options) do NOT contain other values than the resource threaded_connection_options' do
|
132
|
+
let(:connection_options) do
|
133
|
+
{
|
134
|
+
collection: true,
|
135
|
+
path_prefix: '/api',
|
136
|
+
root_element: :bazbar
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
let(:threaded_connection_options) do
|
141
|
+
{
|
142
|
+
site: 'http://www.bazbazbaz.com',
|
143
|
+
path_prefix: '/api',
|
144
|
+
path_postfix: '/promotion'
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'merges the given connection_options (original_connection_options) with the resource threaded_connection_options' do
|
149
|
+
expect(request.original_connection_options[:site]).to eql 'http://www.bazbazbaz.com'
|
150
|
+
expect(request.original_connection_options[:collection]).to eql true
|
151
|
+
expect(request.original_connection_options[:path_prefix]).to eql '/api'
|
152
|
+
expect(request.original_connection_options[:path_postfix]).to eql '/promotion'
|
153
|
+
expect(request.original_connection_options[:root_element]).to eql :bazbar
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#perform' do
|
159
|
+
let(:connection) { Typhoeus::Request }
|
160
|
+
let(:determined_request_url) { 'http://www.foobar.com/request_dummy.json' }
|
161
|
+
let(:determined_params) { attributes }
|
162
|
+
let(:determined_attributes) { attributes }
|
163
|
+
let(:determined_headers) { { "Accept"=>"application/json" } }
|
164
|
+
|
165
|
+
let(:typhoeus_request) { Typhoeus::Request.new determined_request_url }
|
166
|
+
let(:typhoeus_response) do
|
167
|
+
response = Typhoeus::Response.new
|
168
|
+
response.request = typhoeus_request
|
169
|
+
response
|
170
|
+
end
|
171
|
+
|
172
|
+
let(:determined_connection_options) { request.connection_options }
|
173
|
+
|
174
|
+
before do
|
175
|
+
allow_any_instance_of(Typhoeus::Request).to receive(:run) { typhoeus_response }
|
176
|
+
allow(typhoeus_response).to receive(:response_code)
|
177
|
+
allow(typhoeus_response).to receive(:success?) { true }
|
178
|
+
end
|
179
|
+
|
180
|
+
shared_examples 'a conditional construct for the response' do
|
181
|
+
context 'when the response is successful' do
|
182
|
+
it 'makes a RemoteResource::Response object with the Typhoeus::Response object and the connection_options' do
|
183
|
+
expect(RemoteResource::Response).to receive(:new).with(typhoeus_response, determined_connection_options).and_call_original
|
184
|
+
request.perform
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'returns a RemoteResource::Response object' do
|
188
|
+
expect(request.perform).to be_a RemoteResource::Response
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'when the response_code of the response is 422' do
|
193
|
+
before { allow(typhoeus_response).to receive(:response_code) { 422 } }
|
194
|
+
|
195
|
+
it 'makes a RemoteResource::Response object with the Typhoeus::Response object and the connection_options' do
|
196
|
+
expect(RemoteResource::Response).to receive(:new).with(typhoeus_response, determined_connection_options).and_call_original
|
197
|
+
request.perform
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'returns a RemoteResource::Response object' do
|
201
|
+
expect(request.perform).to be_a RemoteResource::Response
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'when the response is NOT successful' do
|
206
|
+
before { allow(typhoeus_response).to receive(:success?) { false } }
|
207
|
+
|
208
|
+
it 'calls #raise_http_errors to raise an error' do
|
209
|
+
expect(request).to receive(:raise_http_errors).with typhoeus_response
|
210
|
+
request.perform
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'when the rest_action is :get' do
|
216
|
+
let(:rest_action) { 'get' }
|
217
|
+
|
218
|
+
it 'makes a GET request with the attributes as params' do
|
219
|
+
expect(connection).to receive(:get).with(determined_request_url, params: determined_params, headers: determined_headers).and_call_original
|
220
|
+
request.perform
|
221
|
+
end
|
222
|
+
|
223
|
+
it_behaves_like 'a conditional construct for the response'
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'when the rest_action is :put' do
|
227
|
+
let(:rest_action) { 'put' }
|
228
|
+
|
229
|
+
it 'makes a PUT request with the attributes as body' do
|
230
|
+
expect(connection).to receive(:put).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
|
231
|
+
request.perform
|
232
|
+
end
|
233
|
+
|
234
|
+
it_behaves_like 'a conditional construct for the response'
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'when the rest_action is :put' do
|
238
|
+
let(:rest_action) { 'put' }
|
239
|
+
|
240
|
+
it 'makes a PUT request with the attributes as body' do
|
241
|
+
expect(connection).to receive(:put).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
|
242
|
+
request.perform
|
243
|
+
end
|
244
|
+
|
245
|
+
it_behaves_like 'a conditional construct for the response'
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'when the rest_action is :patch' do
|
249
|
+
let(:rest_action) { 'patch' }
|
250
|
+
|
251
|
+
it 'makes a PATCH request with the attributes as body' do
|
252
|
+
expect(connection).to receive(:patch).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
|
253
|
+
request.perform
|
254
|
+
end
|
255
|
+
|
256
|
+
it_behaves_like 'a conditional construct for the response'
|
257
|
+
end
|
258
|
+
|
259
|
+
context 'when the rest_action is :post' do
|
260
|
+
let(:rest_action) { 'post' }
|
261
|
+
|
262
|
+
it 'makes a POST request with the attributes as body' do
|
263
|
+
expect(connection).to receive(:post).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
|
264
|
+
request.perform
|
265
|
+
end
|
266
|
+
|
267
|
+
it_behaves_like 'a conditional construct for the response'
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'when the rest_action is unknown' do
|
271
|
+
let(:rest_action) { 'foo' }
|
272
|
+
|
273
|
+
it 'raises the RemoteResource::RESTActionUnknown error' do
|
274
|
+
expect{ request.perform }.to raise_error RemoteResource::RESTActionUnknown, "for action: 'foo'"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe '#determined_request_url' do
|
280
|
+
context 'the attributes contain an id' do
|
281
|
+
let(:attributes) do
|
282
|
+
{ id: 12, name: 'Mies' }
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'uses the id for the request url' do
|
286
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy/12.json'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
context 'the attributes do NOT contain an id' do
|
291
|
+
it 'does NOT use the id for the request url' do
|
292
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'the given connection_options (original_connection_options) contain a base_url' do
|
297
|
+
let(:connection_options) do
|
298
|
+
{ base_url: 'http://www.foo.com/api' }
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'uses the base_url for the request url' do
|
302
|
+
expect(request.determined_request_url).to eql 'http://www.foo.com/api.json'
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context 'the given connection_options (original_connection_options) do NOT contain a base_url' do
|
307
|
+
it 'does NOT use the base_url for the request url' do
|
308
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context 'the given connection_options contain a collection' do
|
313
|
+
let(:connection_options) do
|
314
|
+
{ collection: true }
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'uses the collection to determine the base_url for the request url' do
|
318
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummies.json'
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context 'the connection_options contain a content_type' do
|
323
|
+
let(:connection_options) do
|
324
|
+
{ content_type: '' }
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'uses the content_type for the request url' do
|
328
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy'
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'the connection_options do NOT contain a content_type' do
|
333
|
+
it 'does NOT use the content_type for the request url' do
|
334
|
+
expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe '#determined_params' do
|
340
|
+
context 'the connection_options contain no_params' do
|
341
|
+
let(:connection_options) do
|
342
|
+
{
|
343
|
+
params: { page: 5, limit: 15 },
|
344
|
+
no_params: true
|
345
|
+
}
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'returns nil' do
|
349
|
+
expect(request.determined_params).to be_nil
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
context 'the connection_options do NOT contain a no_params' do
|
354
|
+
context 'and the connection_options contain no_attributes' do
|
355
|
+
let(:connection_options) do
|
356
|
+
{
|
357
|
+
params: { page: 5, limit: 15 },
|
358
|
+
no_params: false,
|
359
|
+
no_attributes: true
|
360
|
+
}
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'returns the params' do
|
364
|
+
expect(request.determined_params).to eql({ page: 5, limit: 15 })
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'and the connection_options do NOT contain no_attributes' do
|
369
|
+
let(:connection_options) do
|
370
|
+
{
|
371
|
+
params: { page: 5, limit: 15 },
|
372
|
+
no_params: false,
|
373
|
+
no_attributes: false
|
374
|
+
}
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'returns the params merge with the attributes' do
|
378
|
+
expect(request.determined_params).to eql({ name: 'Mies', page: 5, limit: 15 })
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe '#determined_attributes' do
|
385
|
+
context 'the connection_options contain no_attributes' do
|
386
|
+
let(:connection_options) do
|
387
|
+
{ no_attributes: true }
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'returns an empty Hash' do
|
391
|
+
expect(request.determined_attributes).to eql({})
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
context 'the connection_options do NOT contain a no_attributes' do
|
396
|
+
it 'does NOT return an empty Hash' do
|
397
|
+
expect(request.determined_attributes).not_to eql({})
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context 'the connection_options contain a root_element' do
|
402
|
+
let(:connection_options) do
|
403
|
+
{ root_element: :foobar }
|
404
|
+
end
|
405
|
+
|
406
|
+
let(:packed_up_attributes) do
|
407
|
+
{ 'foobar' => { name: 'Mies' } }
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'packs up the attributes with the root_element' do
|
411
|
+
expect(request.determined_attributes).to eql packed_up_attributes
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
context 'the connection_options do NOT contain a root_element' do
|
416
|
+
it 'does NOT pack up the attributes with the root_element' do
|
417
|
+
expect(request.determined_attributes).to eql attributes
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
describe '#determined_headers' do
|
423
|
+
let(:global_headers) do
|
424
|
+
{
|
425
|
+
'X-Locale' => 'en',
|
426
|
+
'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
427
|
+
}
|
428
|
+
end
|
429
|
+
|
430
|
+
before { RemoteResource::Base.global_headers = global_headers }
|
431
|
+
after { RemoteResource::Base.global_headers = nil }
|
432
|
+
|
433
|
+
let(:headers) do
|
434
|
+
{ 'Baz' => 'FooBar' }
|
435
|
+
end
|
436
|
+
|
437
|
+
let(:default_headers) do
|
438
|
+
dummy_class.default_headers
|
439
|
+
end
|
440
|
+
|
441
|
+
context 'the connection_options contain a default_headers' do
|
442
|
+
let(:default_headers) do
|
443
|
+
{ 'Foo' => 'Bar' }
|
444
|
+
end
|
445
|
+
|
446
|
+
context 'and the given connection_options (original_connection_options) contain a headers' do
|
447
|
+
let(:connection_options) do
|
448
|
+
{ default_headers: default_headers, headers: headers }
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'uses the default_headers for the request headers' do
|
452
|
+
expect(request.determined_headers).to eql default_headers.merge(global_headers)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
context 'and the given connection_options (original_connection_options) do NOT contain a headers' do
|
457
|
+
let(:connection_options) do
|
458
|
+
{ default_headers: default_headers }
|
459
|
+
end
|
460
|
+
|
461
|
+
it 'uses the default_headers for the request headers' do
|
462
|
+
expect(request.determined_headers).to eql default_headers.merge(global_headers)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
context 'the connection_options do NOT contain a default_headers' do
|
468
|
+
context 'and the given connection_options (original_connection_options) contain a headers' do
|
469
|
+
let(:connection_options) do
|
470
|
+
{ headers: headers }
|
471
|
+
end
|
472
|
+
|
473
|
+
it 'uses the headers for the request headers' do
|
474
|
+
expect(request.determined_headers).to eql default_headers.merge(headers).merge(global_headers)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context 'and the given connection_options (original_connection_options) do NOT contain a headers' do
|
479
|
+
context 'and the resource contains a extra_headers' do
|
480
|
+
let(:extra_headers) do
|
481
|
+
{ 'BarBaz' => 'Baz' }
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'uses the headers of the resource for the request headers' do
|
485
|
+
dummy_class.extra_headers = extra_headers
|
486
|
+
dummy_class.connection_options.reload!
|
487
|
+
|
488
|
+
expect(request.determined_headers).to eql default_headers.merge(extra_headers).merge(global_headers)
|
489
|
+
|
490
|
+
dummy_class.extra_headers = nil
|
491
|
+
dummy_class.connection_options.reload!
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
context 'and the resource does NOT contain a extra_headers' do
|
496
|
+
it 'does NOT use the headers for the request headers' do
|
497
|
+
expect(request.determined_headers).to eql default_headers.merge(global_headers)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
describe '#resource_klass' do
|
505
|
+
context 'when the resource is a RemoteResource class' do
|
506
|
+
let(:resource) { dummy_class }
|
507
|
+
|
508
|
+
it 'returns the resource' do
|
509
|
+
expect(request.send :resource_klass).to eql RemoteResource::RequestDummy
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
context 'when the resource is a RemoteResource object' do
|
514
|
+
let(:resource) { dummy }
|
515
|
+
|
516
|
+
it 'returns the Class of the resource' do
|
517
|
+
expect(request.send :resource_klass).to eql RemoteResource::RequestDummy
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
describe '#raise_http_errors' do
|
523
|
+
let(:response) { instance_double Typhoeus::Response }
|
524
|
+
let(:raise_http_errors) { request.send :raise_http_errors, response }
|
525
|
+
|
526
|
+
before { allow(response).to receive(:response_code) { response_code } }
|
527
|
+
|
528
|
+
context 'when the response code is 301, 302, 303 or 307' do
|
529
|
+
response_codes = [301, 302, 303, 307]
|
530
|
+
response_codes.each do |response_code|
|
531
|
+
|
532
|
+
it "raises a RemoteResource::HTTPRedirectionError with response code #{response_code}" do
|
533
|
+
allow(response).to receive(:response_code) { response_code }
|
534
|
+
|
535
|
+
expect{ raise_http_errors }.to raise_error RemoteResource::HTTPRedirectionError, "with HTTP response status: #{response_code} and response: #{response}"
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
context 'when the response code is in the 4xx range' do
|
541
|
+
response_codes_with_error_class = {
|
542
|
+
400 => RemoteResource::HTTPBadRequest,
|
543
|
+
401 => RemoteResource::HTTPUnauthorized,
|
544
|
+
403 => RemoteResource::HTTPForbidden,
|
545
|
+
404 => RemoteResource::HTTPNotFound,
|
546
|
+
405 => RemoteResource::HTTPMethodNotAllowed,
|
547
|
+
406 => RemoteResource::HTTPNotAcceptable,
|
548
|
+
408 => RemoteResource::HTTPRequestTimeout,
|
549
|
+
409 => RemoteResource::HTTPConflict,
|
550
|
+
410 => RemoteResource::HTTPGone,
|
551
|
+
418 => RemoteResource::HTTPTeapot,
|
552
|
+
444 => RemoteResource::HTTPNoResponse,
|
553
|
+
494 => RemoteResource::HTTPRequestHeaderTooLarge,
|
554
|
+
495 => RemoteResource::HTTPCertError,
|
555
|
+
496 => RemoteResource::HTTPNoCert,
|
556
|
+
497 => RemoteResource::HTTPToHTTPS,
|
557
|
+
499 => RemoteResource::HTTPClientClosedRequest,
|
558
|
+
}
|
559
|
+
response_codes_with_error_class.each do |response_code, error_class|
|
560
|
+
|
561
|
+
it "raises a #{error_class} with response code #{response_code}" do
|
562
|
+
allow(response).to receive(:response_code) { response_code }
|
563
|
+
|
564
|
+
expect{ raise_http_errors }.to raise_error error_class, "with HTTP response status: #{response_code} and response: #{response}"
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
context 'when the response code is in the 4xx range and no other error is raised' do
|
570
|
+
let(:response_code) { 430 }
|
571
|
+
|
572
|
+
it 'raises a RemoteResource::HTTPClientError' do
|
573
|
+
expect{ raise_http_errors }.to raise_error RemoteResource::HTTPClientError, "with HTTP response status: #{response_code} and response: #{response}"
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
context 'when the response code is in the 5xx range and no other error is raised' do
|
578
|
+
let(:response_code) { 501 }
|
579
|
+
|
580
|
+
it 'raises a RemoteResource::HTTPServerError' do
|
581
|
+
expect{ raise_http_errors }.to raise_error RemoteResource::HTTPServerError, "with HTTP response status: #{response_code} and response: #{response}"
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
context 'when the response code is nothing and no other error is raised' do
|
586
|
+
let(:response_code) { nil }
|
587
|
+
|
588
|
+
it 'raises a RemoteResource::HTTPError' do
|
589
|
+
expect{ raise_http_errors }.to raise_error RemoteResource::HTTPError, "with HTTP response: #{response}"
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|