ddy_remote_resource 1.0.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 53853b2bc923849611145a783562290e1f352db4
4
- data.tar.gz: 68fe3d657f0635f6ce22131cd6d61b356b005c49
2
+ SHA256:
3
+ metadata.gz: d0032d56e8df7f6e90e146abb65b29a41a59a63e39747c6b0b7585c02f577069
4
+ data.tar.gz: 4454b9226626e5a2028dc871c83d15347da8a373d104e641810c3f002e8e94c0
5
5
  SHA512:
6
- metadata.gz: 36a1aac2f46e915fc618f9d3dddfca865c9682845c017069bf6fb7508d0b7429f0eb98e950bf62f18d1cae9790fa11b01407a4e66110d7ea5a03298bd0efd599
7
- data.tar.gz: 2ccaff6087c60d3629fd94889fd7d50c34b9c07fa822c0a0eeb1ce4f98ada89a220200a1cfb774f7dfeb7db30ee317d21f07e7734c4715f9355f5e6e03585db1
6
+ metadata.gz: 327330942d08e782894d10b46be1c6a6fc560f4cab2a4f7bb77a2b2958853ad929a024ccc00c560a3672e48c55a5844e9064e9c7fc00ae9839228d904038f2b6
7
+ data.tar.gz: f6d6b6c025c695cd68f4697b9a001046d54a29b32f51aabbec187ff9359bf32da6db83510cc5a3a251729159100e06b11054986a009d55386ece26e5f372ed8b
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .idea/
6
7
  Gemfile.lock
7
8
  InstalledFiles
8
9
  _yardoc
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.5.3
@@ -1,3 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.1
3
+ - 2.5.3
@@ -2,6 +2,7 @@ require 'active_support/all'
2
2
  require 'active_model'
3
3
  require 'virtus'
4
4
  require 'typhoeus'
5
+ require 'request_store'
5
6
 
6
7
  require_relative 'extensions/ethon/easy/queryable'
7
8
 
@@ -23,16 +23,17 @@ module RemoteResource
23
23
  attr_writer :destroyed, :persisted, :success
24
24
 
25
25
  class_attribute :root_element, instance_accessor: false
26
+ class_attribute :json_spec, instance_accessor: false
26
27
 
27
28
  attribute :id
28
29
  end
29
30
 
30
31
  def self.global_headers=(headers)
31
- Thread.current[:global_headers] = headers
32
+ RequestStore.store[:global_headers] = headers
32
33
  end
33
34
 
34
35
  def self.global_headers
35
- Thread.current[:global_headers] ||= {}
36
+ RequestStore.store[:global_headers] ||= {}
36
37
  end
37
38
 
38
39
  module ClassMethods
@@ -42,15 +43,15 @@ module RemoteResource
42
43
  end
43
44
 
44
45
  def threaded_connection_options
45
- Thread.current[threaded_connection_options_thread_name] ||= {}
46
+ RequestStore.store[threaded_connection_options_thread_name] ||= {}
46
47
  end
47
48
 
48
49
  def with_connection_options(connection_options = {})
49
50
  begin
50
- Thread.current[threaded_connection_options_thread_name] = threaded_connection_options.merge(connection_options)
51
+ RequestStore.store[threaded_connection_options_thread_name] = threaded_connection_options.merge(connection_options)
51
52
  yield
52
53
  ensure
53
- Thread.current[threaded_connection_options_thread_name] = nil
54
+ RequestStore.store[threaded_connection_options_thread_name] = nil
54
55
  end
55
56
  end
56
57
 
@@ -1,7 +1,7 @@
1
1
  module RemoteResource
2
2
  class ConnectionOptions
3
3
 
4
- AVAILABLE_OPTIONS = [:site, :headers, :default_headers, :version, :path_prefix, :path_postfix, :collection_prefix, :extension, :collection, :collection_name, :root_element].freeze
4
+ AVAILABLE_OPTIONS = [:site, :headers, :default_headers, :version, :path_prefix, :path_postfix, :collection_prefix, :extension, :collection, :collection_name, :root_element, :json_spec].freeze
5
5
 
6
6
  attr_reader :base_class
7
7
 
@@ -14,6 +14,9 @@ module RemoteResource
14
14
 
15
15
  DEFAULT_EXTENSION = '.json'.freeze
16
16
 
17
+ DEFAULT_CONNECT_TIMEOUT = 30
18
+ DEFAULT_READ_TIMEOUT = 120
19
+
17
20
  attr_reader :resource, :resource_klass, :http_action, :attributes
18
21
 
19
22
  def initialize(resource, http_action, attributes = {}, connection_options = {})
@@ -41,7 +44,7 @@ module RemoteResource
41
44
  def perform
42
45
  SUPPORTED_HTTP_METHODS.include?(http_action) || raise(RemoteResource::HTTPMethodUnsupported, "Requested HTTP method=#{http_action.to_s} is NOT supported, the HTTP action MUST be a supported HTTP action=#{SUPPORTED_HTTP_METHODS.join(', ')}")
43
46
 
44
- connection_response = connection.public_send(http_action, request_url, params: query, body: body, headers: headers)
47
+ connection_response = connection.public_send(http_action, request_url, params: query, body: body, headers: headers, **timeout_options)
45
48
  response = RemoteResource::Response.new(connection_response, connection_options.merge(request: self, connection_request: connection_response.request))
46
49
 
47
50
  if response.success? || response.unprocessable_entity?
@@ -76,7 +79,7 @@ module RemoteResource
76
79
  def body
77
80
  @body ||= begin
78
81
  if [:put, :patch, :post].include?(http_action)
79
- JSON.generate(attributes)
82
+ attributes.to_json
80
83
  else
81
84
  nil
82
85
  end
@@ -84,12 +87,20 @@ module RemoteResource
84
87
  end
85
88
 
86
89
  def attributes
87
- root_element = connection_options[:root_element]
88
-
89
- if root_element.present?
90
- { root_element => @attributes }
90
+ if connection_options[:json_spec] == :json_api
91
+ if @attributes
92
+ { data: { id: @attributes[:id], type: resource_klass.name.demodulize, attributes: @attributes.except(:id) } }
93
+ else
94
+ { data: {} }
95
+ end
91
96
  else
92
- @attributes || {}
97
+ root_element = connection_options[:root_element]
98
+
99
+ if root_element.present?
100
+ { root_element => @attributes }
101
+ else
102
+ @attributes || {}
103
+ end
93
104
  end
94
105
  end
95
106
 
@@ -106,33 +117,62 @@ module RemoteResource
106
117
  def conditional_headers
107
118
  headers = {}
108
119
  headers = headers.merge(DEFAULT_CONTENT_TYPE) if body.present?
109
- headers = headers.merge({ 'X-Request-Id' => Thread.current[:request_id] }) if Thread.current[:request_id].present?
120
+ headers = headers.merge({ 'X-Request-Id' => RequestStore.store[:request_id] }) if RequestStore.store[:request_id].present?
110
121
  headers
111
122
  end
112
123
 
124
+ def timeout_options
125
+ connecttimeout = connection_options[:connecttimeout].presence || DEFAULT_CONNECT_TIMEOUT
126
+ timeout = connection_options[:timeout].presence || DEFAULT_READ_TIMEOUT
127
+
128
+ { connecttimeout: connecttimeout, timeout: timeout }
129
+ end
130
+
113
131
  private
114
132
 
115
133
  def raise_http_error(request, response)
134
+ # Special case if a request has a time out, as Typhoeus does not set a 408 response_code
135
+ raise RemoteResource::HTTPRequestTimeout.new(request, response) if response.timed_out?
136
+
116
137
  case response.try(:response_code)
117
- when 301, 302, 303, 307 then raise RemoteResource::HTTPRedirectionError.new(request, response)
118
- when 400 then raise RemoteResource::HTTPBadRequest.new(request, response)
119
- when 401 then raise RemoteResource::HTTPUnauthorized.new(request, response)
120
- when 403 then raise RemoteResource::HTTPForbidden.new(request, response)
121
- when 404 then raise RemoteResource::HTTPNotFound.new(request, response)
122
- when 405 then raise RemoteResource::HTTPMethodNotAllowed.new(request, response)
123
- when 406 then raise RemoteResource::HTTPNotAcceptable.new(request, response)
124
- when 408 then raise RemoteResource::HTTPRequestTimeout.new(request, response)
125
- when 409 then raise RemoteResource::HTTPConflict.new(request, response)
126
- when 410 then raise RemoteResource::HTTPGone.new(request, response)
127
- when 418 then raise RemoteResource::HTTPTeapot.new(request, response)
128
- when 444 then raise RemoteResource::HTTPNoResponse.new(request, response)
129
- when 494 then raise RemoteResource::HTTPRequestHeaderTooLarge.new(request, response)
130
- when 495 then raise RemoteResource::HTTPCertError.new(request, response)
131
- when 496 then raise RemoteResource::HTTPNoCert.new(request, response)
132
- when 497 then raise RemoteResource::HTTPToHTTPS.new(request, response)
133
- when 499 then raise RemoteResource::HTTPClientClosedRequest.new(request, response)
134
- when 400..499 then raise RemoteResource::HTTPClientError.new(request, response)
135
- when 500..599 then raise RemoteResource::HTTPServerError.new(request, response)
138
+ when 301, 302, 303, 307 then
139
+ raise RemoteResource::HTTPRedirectionError.new(request, response)
140
+ when 400
141
+ raise RemoteResource::HTTPBadRequest.new(request, response)
142
+ when 401
143
+ raise RemoteResource::HTTPUnauthorized.new(request, response)
144
+ when 403
145
+ raise RemoteResource::HTTPForbidden.new(request, response)
146
+ when 404
147
+ raise RemoteResource::HTTPNotFound.new(request, response)
148
+ when 405
149
+ raise RemoteResource::HTTPMethodNotAllowed.new(request, response)
150
+ when 406
151
+ raise RemoteResource::HTTPNotAcceptable.new(request, response)
152
+ when 408
153
+ raise RemoteResource::HTTPRequestTimeout.new(request, response)
154
+ when 409
155
+ raise RemoteResource::HTTPConflict.new(request, response)
156
+ when 410
157
+ raise RemoteResource::HTTPGone.new(request, response)
158
+ when 418
159
+ raise RemoteResource::HTTPTeapot.new(request, response)
160
+ when 444
161
+ raise RemoteResource::HTTPNoResponse.new(request, response)
162
+ when 494
163
+ raise RemoteResource::HTTPRequestHeaderTooLarge.new(request, response)
164
+ when 495
165
+ raise RemoteResource::HTTPCertError.new(request, response)
166
+ when 496
167
+ raise RemoteResource::HTTPNoCert.new(request, response)
168
+ when 497
169
+ raise RemoteResource::HTTPToHTTPS.new(request, response)
170
+ when 499
171
+ raise RemoteResource::HTTPClientClosedRequest.new(request, response)
172
+ when 400..499
173
+ raise RemoteResource::HTTPClientError.new(request, response)
174
+ when 500..599
175
+ raise RemoteResource::HTTPServerError.new(request, response)
136
176
  else
137
177
  raise RemoteResource::HTTPError.new(request, response)
138
178
  end
@@ -16,6 +16,10 @@ module RemoteResource
16
16
  @connection_response.success?
17
17
  end
18
18
 
19
+ def timed_out?
20
+ @connection_response.timed_out?
21
+ end
22
+
19
23
  def unprocessable_entity?
20
24
  response_code == 422
21
25
  end
@@ -44,19 +48,29 @@ module RemoteResource
44
48
 
45
49
  def attributes
46
50
  @attributes ||= begin
47
- root_element = @connection_options[:root_element].to_s
48
- data = nil
49
-
50
- if root_element.present?
51
- data = parsed_body.try(:key?, root_element) && parsed_body[root_element]
51
+ if @connection_options[:json_spec] == :json_api
52
+ data = parsed_body.fetch("data", {})
53
+ if data.is_a?(Array)
54
+ data.map { |row| row["attributes"].merge({ "id" => row["id"] }) }
55
+ elsif data.key?("attributes")
56
+ data["attributes"].merge({ "id" => data["id"] })
57
+ else
58
+ data
59
+ end
52
60
  else
53
- data = parsed_body
54
- end
61
+ root_element = @connection_options[:root_element].to_s
55
62
 
56
- if data.is_a?(Array)
57
- data
58
- else
59
- data.presence || {}
63
+ if root_element.present?
64
+ data = parsed_body.try(:key?, root_element) && parsed_body[root_element]
65
+ else
66
+ data = parsed_body
67
+ end
68
+
69
+ if data.is_a?(Array)
70
+ data
71
+ else
72
+ data.presence || {}
73
+ end
60
74
  end
61
75
  end
62
76
  end
@@ -1,3 +1,3 @@
1
1
  module RemoteResource
2
- VERSION = '1.0.4'.freeze
2
+ VERSION = '1.2.0'.freeze
3
3
  end
@@ -6,8 +6,8 @@ require 'remote_resource/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'ddy_remote_resource'
8
8
  spec.version = RemoteResource::VERSION
9
- spec.authors = ['Jan van der Pas']
10
- spec.email = ['jvanderpas@digidentity.eu']
9
+ spec.authors = ['Digidentity', 'Jan van der Pas']
10
+ spec.email = ['development@digidentity.com']
11
11
  spec.summary = %q{RemoteResource, a gem to use resources with REST services.}
12
12
  spec.description = %q{RemoteResource, a gem to use resources with REST services. A replacement for ActiveResource gem.}
13
13
  spec.homepage = ''
@@ -27,10 +27,11 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'guard-rspec', '~> 4.7'
28
28
  spec.add_development_dependency 'terminal-notifier-guard', '~> 1.6'
29
29
 
30
- spec.add_runtime_dependency 'activesupport', '>= 4.1', '< 6'
31
- spec.add_runtime_dependency 'activemodel', '>= 4.1', '< 6'
30
+ spec.add_runtime_dependency 'activesupport', '>= 4.1', '< 7'
31
+ spec.add_runtime_dependency 'activemodel', '>= 4.1', '< 7'
32
32
  spec.add_runtime_dependency 'virtus', '~> 1.0', '>= 1.0.4'
33
33
  spec.add_runtime_dependency 'mime-types', '~> 3.0'
34
34
  spec.add_runtime_dependency 'ethon', '~> 0.7', '>= 0.7.1'
35
35
  spec.add_runtime_dependency 'typhoeus', '~> 0.7', '>= 0.7.0'
36
+ spec.add_runtime_dependency 'request_store', '~> 1.4.1'
36
37
  end
@@ -44,7 +44,7 @@ RSpec.describe '.create' do
44
44
 
45
45
  let!(:expected_request) do
46
46
  mock_request = stub_request(:post, 'https://www.example.com/posts.json')
47
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers)
47
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers)
48
48
  mock_request.to_return(status: 201, body: JSON.generate(response_body))
49
49
  mock_request
50
50
  end
@@ -81,7 +81,7 @@ RSpec.describe '.create' do
81
81
 
82
82
  let!(:expected_request) do
83
83
  mock_request = stub_request(:post, 'https://www.example.com/posts.json')
84
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
84
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
85
85
  mock_request.to_return(status: 201, body: JSON.generate(response_body))
86
86
  mock_request
87
87
  end
@@ -114,7 +114,7 @@ RSpec.describe '.create' do
114
114
 
115
115
  let!(:expected_request) do
116
116
  mock_request = stub_request(:post, 'https://www.example.com/posts.json')
117
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers)
117
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers)
118
118
  mock_request.to_return(status: 422, body: JSON.generate(response_body))
119
119
  mock_request
120
120
  end
@@ -48,7 +48,7 @@ RSpec.describe '#save' do
48
48
  describe 'default behaviour' do
49
49
  let!(:expected_request) do
50
50
  mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
51
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers)
51
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers)
52
52
  mock_request.to_return(status: 200, body: JSON.generate(response_body))
53
53
  mock_request
54
54
  end
@@ -77,7 +77,7 @@ RSpec.describe '#save' do
77
77
  describe 'with connection_options[:headers]' do
78
78
  let!(:expected_request) do
79
79
  mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
80
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
80
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
81
81
  mock_request.to_return(status: 200, body: JSON.generate(response_body))
82
82
  mock_request
83
83
  end
@@ -111,7 +111,7 @@ RSpec.describe '#save' do
111
111
 
112
112
  let!(:expected_request) do
113
113
  mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
114
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers)
114
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers)
115
115
  mock_request.to_return(status: 422, body: JSON.generate(response_body))
116
116
  mock_request
117
117
  end
@@ -148,7 +148,7 @@ RSpec.describe '#save' do
148
148
  describe 'with a 500 response' do
149
149
  let!(:expected_request) do
150
150
  mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
151
- mock_request.with(query: nil, body: JSON.generate(expected_request_body), headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
151
+ mock_request.with(query: nil, body: expected_request_body.to_json, headers: expected_default_headers.merge({ 'X-Pseudonym' => 'pseudonym' }))
152
152
  mock_request.to_return(status: 500)
153
153
  mock_request
154
154
  end
@@ -81,7 +81,7 @@ RSpec.describe RemoteResource::Base do
81
81
  end
82
82
 
83
83
  it 'sets the name of Thread variable with the implemented class' do
84
- expect(dummy_class.threaded_connection_options).to eql Thread.current['remote_resource.dummy.threaded_connection_options']
84
+ expect(dummy_class.threaded_connection_options).to eql RequestStore.store['remote_resource.dummy.threaded_connection_options']
85
85
  end
86
86
  end
87
87
 
@@ -24,7 +24,7 @@ RSpec.describe RemoteResource::ConnectionOptions do
24
24
  end
25
25
 
26
26
  let(:dummy_class) { RemoteResource::ConnectionOptionsDummy }
27
- let(:dummy) { dummy_class.new }
27
+ let(:dummy) { dummy_class.new }
28
28
 
29
29
  let(:connection_options) { described_class.new dummy_class }
30
30
 
@@ -59,8 +59,8 @@ RSpec.describe RemoteResource::ConnectionOptions do
59
59
  describe '#merge' do
60
60
  let(:custom_connection_options) do
61
61
  {
62
- site: 'https://dummy.foobar.com',
63
- version: '/api/v2',
62
+ site: 'https://dummy.foobar.com',
63
+ version: '/api/v2',
64
64
  root_element: :test_dummy_api
65
65
  }
66
66
  end
@@ -91,7 +91,8 @@ RSpec.describe RemoteResource::ConnectionOptions do
91
91
  collection_prefix: '/parent/:parent_id',
92
92
  collection: true,
93
93
  collection_name: nil,
94
- root_element: :test_dummy
94
+ root_element: :test_dummy,
95
+ json_spec: nil
95
96
  }
96
97
  end
97
98
 
@@ -18,10 +18,10 @@ RSpec.describe RemoteResource::Request do
18
18
  end
19
19
 
20
20
  let(:dummy_class) { RemoteResource::RequestDummy }
21
- let(:dummy) { dummy_class.new id: '12' }
21
+ let(:dummy) { dummy_class.new id: '12' }
22
22
 
23
- let(:resource) { dummy_class }
24
- let(:http_action) { :get }
23
+ let(:resource) { dummy_class }
24
+ let(:http_action) { :get }
25
25
  let(:connection_options) { {} }
26
26
  let(:attributes) do
27
27
  { name: 'Mies' }
@@ -68,6 +68,7 @@ RSpec.describe RemoteResource::Request do
68
68
 
69
69
  let(:block_connection_options) do
70
70
  {
71
+ json_spec: 'From .with_connection_options',
71
72
  root_element: 'From .with_connection_options',
72
73
  version: 'From .with_connection_options',
73
74
  path_prefix: 'From .with_connection_options'
@@ -85,6 +86,7 @@ RSpec.describe RemoteResource::Request do
85
86
  {
86
87
  site: 'From Klass.site',
87
88
  root_element: 'From connection_options[]',
89
+ json_spec: 'From .with_connection_options',
88
90
  version: 'From .with_connection_options',
89
91
  path_prefix: 'From .with_connection_options',
90
92
  path_postfix: 'From connection_options[]',
@@ -166,7 +168,9 @@ RSpec.describe RemoteResource::Request do
166
168
  let(:expected_connection_options) { request.connection_options }
167
169
 
168
170
  it 'makes a GET request with the connection_options[:params] as query' do
169
- expect(connection).to receive(:get).with(expected_request_url, params: expected_params, body: expected_body, headers: expected_headers).and_call_original
171
+ expect(connection).to receive(:get).with(expected_request_url, params: expected_params,
172
+ body: expected_body, headers: expected_headers,
173
+ connecttimeout: 30, timeout: 120).and_call_original
170
174
  request.perform
171
175
  end
172
176
 
@@ -189,7 +193,9 @@ RSpec.describe RemoteResource::Request do
189
193
  let(:expected_connection_options) { request.connection_options }
190
194
 
191
195
  it 'makes a PUT request with the attributes as body' do
192
- expect(connection).to receive(:put).with(expected_request_url, params: expected_params, body: expected_body, headers: expected_headers).and_call_original
196
+ expect(connection).to receive(:put).with(expected_request_url, params: expected_params,
197
+ body: expected_body, headers: expected_headers,
198
+ connecttimeout: 30, timeout: 120).and_call_original
193
199
  request.perform
194
200
  end
195
201
 
@@ -212,7 +218,9 @@ RSpec.describe RemoteResource::Request do
212
218
  let(:expected_connection_options) { request.connection_options }
213
219
 
214
220
  it 'makes a PATCH request with the attributes as body' do
215
- expect(connection).to receive(:patch).with(expected_request_url, params: expected_params, body: expected_body, headers: expected_headers).and_call_original
221
+ expect(connection).to receive(:patch).with(expected_request_url, params: expected_params,
222
+ body: expected_body, headers: expected_headers,
223
+ connecttimeout: 30, timeout: 120).and_call_original
216
224
  request.perform
217
225
  end
218
226
 
@@ -235,7 +243,9 @@ RSpec.describe RemoteResource::Request do
235
243
  let(:expected_connection_options) { request.connection_options }
236
244
 
237
245
  it 'makes a POST request with the attributes as body' do
238
- expect(connection).to receive(:post).with(expected_request_url, params: expected_params, body: expected_body, headers: expected_headers).and_call_original
246
+ expect(connection).to receive(:post).with(expected_request_url, params: expected_params,
247
+ body: expected_body, headers: expected_headers,
248
+ connecttimeout: 30, timeout: 120).and_call_original
239
249
  request.perform
240
250
  end
241
251
 
@@ -258,7 +268,9 @@ RSpec.describe RemoteResource::Request do
258
268
  let(:expected_connection_options) { request.connection_options }
259
269
 
260
270
  it 'makes a DELETE request with the connection_options[:params] as query' do
261
- expect(connection).to receive(:delete).with(expected_request_url, params: expected_params, body: expected_body, headers: expected_headers).and_call_original
271
+ expect(connection).to receive(:delete).with(expected_request_url, params: expected_params,
272
+ body: expected_body, headers: expected_headers,
273
+ connecttimeout: 30, timeout: 120).and_call_original
262
274
  request.perform
263
275
  end
264
276
 
@@ -270,7 +282,7 @@ RSpec.describe RemoteResource::Request do
270
282
  let(:expected_request_url) { '' }
271
283
 
272
284
  it 'raises the RemoteResource::HTTPMethodUnsupported error' do
273
- expect{ request.perform }.to raise_error RemoteResource::HTTPMethodUnsupported, 'Requested HTTP method=foo is NOT supported, the HTTP action MUST be a supported HTTP action=get, put, patch, post, delete'
285
+ expect { request.perform }.to raise_error RemoteResource::HTTPMethodUnsupported, 'Requested HTTP method=foo is NOT supported, the HTTP action MUST be a supported HTTP action=get, put, patch, post, delete'
274
286
  end
275
287
  end
276
288
  end
@@ -367,7 +379,7 @@ RSpec.describe RemoteResource::Request do
367
379
 
368
380
  context 'when connection_options does NOT include collection_options' do
369
381
  it 'raises error' do
370
- expect{ request.request_url }.to raise_error(RemoteResource::CollectionOptionKeyError)
382
+ expect { request.request_url }.to raise_error(RemoteResource::CollectionOptionKeyError)
371
383
  end
372
384
  end
373
385
  end
@@ -473,6 +485,30 @@ RSpec.describe RemoteResource::Request do
473
485
  end
474
486
  end
475
487
  end
488
+
489
+ context 'when connection_options[:json_spec] == :json_api' do
490
+ let(:connection_options) do
491
+ { json_spec: :json_api }
492
+ end
493
+
494
+ let(:attributes) do
495
+ { id: 1, name: 'Mies', featured: true, labels: [1, '2', 'three'] }
496
+ end
497
+
498
+ it 'returns the given attributes wrapped in the json api spec' do
499
+ expect(request.attributes).to eql({ data: { id: 1, type: "RequestDummy", attributes: { name: 'Mies', featured: true, labels: [1, '2', 'three'] } } })
500
+ end
501
+
502
+ context 'and there are NO given attributes' do
503
+ let(:attributes) do
504
+ nil
505
+ end
506
+
507
+ it 'returns nil wrapped in the connection_options[:root_element]' do
508
+ expect(request.attributes).to eql({ data: {} })
509
+ end
510
+ end
511
+ end
476
512
  end
477
513
 
478
514
  describe '#headers' do
@@ -506,7 +542,7 @@ RSpec.describe RemoteResource::Request do
506
542
  end
507
543
 
508
544
  before { RemoteResource::Base.global_headers = { 'User-Agent' => 'From RemoteResource::Base.global_headers', 'X-Locale' => 'From RemoteResource::Base.global_headers' } }
509
- after { RemoteResource::Base.global_headers = nil }
545
+ after { RemoteResource::Base.global_headers = nil }
510
546
 
511
547
  it 'returns the default headers while overwriting the headers according to the correct precedence' do
512
548
  expect(request.headers).to eql expected_headers
@@ -540,13 +576,13 @@ RSpec.describe RemoteResource::Request do
540
576
  end
541
577
  end
542
578
 
543
- context 'when Thread.current[:request_id] is present' do
579
+ context 'when RequestStore.store[:request_id] is present' do
544
580
  before do
545
- Thread.current[:request_id] = 'CASCADING-REQUEST-ID'
581
+ RequestStore.store[:request_id] = 'CASCADING-REQUEST-ID'
546
582
  end
547
583
 
548
584
  after do
549
- Thread.current[:request_id] = nil
585
+ RequestStore.store[:request_id] = nil
550
586
  end
551
587
 
552
588
  let(:expected_headers) do
@@ -560,9 +596,36 @@ RSpec.describe RemoteResource::Request do
560
596
  end
561
597
  end
562
598
 
599
+ describe '#timeout_options' do
600
+ it 'is not given by default' do
601
+ expect(request.connection_options).not_to include :connecttimeout, :timeout
602
+ end
603
+
604
+ context 'with custom timeouts' do
605
+ let(:connection_options) do
606
+ { connecttimeout: 1, timeout: 2 }
607
+ end
608
+
609
+ it 'sets the timeouts from connection_options' do
610
+ aggregate_failures do
611
+ expect(request.connection_options[:connecttimeout]).to eq 1
612
+ expect(request.connection_options[:timeout]).to eq 2
613
+ end
614
+ end
615
+ end
616
+ end
617
+
563
618
  describe '#raise_http_error' do
564
- let(:connection_response) { instance_double(Typhoeus::Response, request: instance_double(Typhoeus::Request)) }
565
- let(:response) { RemoteResource::Response.new(connection_response, connection_options) }
619
+ let(:connection_response) { instance_double(Typhoeus::Response, request: instance_double(Typhoeus::Request), timed_out?: false) }
620
+ let(:response) { RemoteResource::Response.new(connection_response, connection_options) }
621
+
622
+ context 'when the response has timed out' do
623
+ let(:connection_response) { instance_double(Typhoeus::Response, request: instance_double(Typhoeus::Request), timed_out?: true) }
624
+
625
+ it 'raises a RemoteResource::HTTPRequestTimeout' do
626
+ expect { request.send(:raise_http_error, request, response) }.to raise_error RemoteResource::HTTPRequestTimeout
627
+ end
628
+ end
566
629
 
567
630
  context 'when the response code is 301, 302, 303 or 307' do
568
631
  response_codes = [301, 302, 303, 307]
@@ -14,14 +14,14 @@ RSpec.describe RemoteResource::Response do
14
14
  end
15
15
 
16
16
  let(:dummy_class) { RemoteResource::ResponseDummy }
17
- let(:dummy) { dummy_class.new(id: '12') }
17
+ let(:dummy) { dummy_class.new(id: '12') }
18
18
 
19
19
  let(:connection_options) do
20
20
  { collection: true }
21
21
  end
22
- let(:request) { RemoteResource::Request.new(dummy_class, :post, { name: 'Mies' }, connection_options) }
22
+ let(:request) { RemoteResource::Request.new(dummy_class, :post, { name: 'Mies' }, connection_options) }
23
23
  let(:connection_response) { Typhoeus::Response.new(mock: true, code: 201, body: { id: 12, name: 'Mies' }.to_json, headers: { 'Content-Type' => 'application/json', 'Server' => 'nginx/1.4.6 (Ubuntu)' }) }
24
- let(:connection_request) { Typhoeus::Request.new('http://www.foobar.com/response_dummies.json', method: :post, body: { name: 'Mies' }.to_json, headers: { 'Content-Type' => 'application/json' }) }
24
+ let(:connection_request) { Typhoeus::Request.new('http://www.foobar.com/response_dummies.json', method: :post, body: { name: 'Mies' }.to_json, headers: { 'Content-Type' => 'application/json' }) }
25
25
 
26
26
  let(:response) { described_class.new(connection_response, connection_options.merge(request: request, connection_request: connection_request)) }
27
27
 
@@ -156,6 +156,36 @@ RSpec.describe RemoteResource::Response do
156
156
  expect(response.attributes).to eql({})
157
157
  end
158
158
  end
159
+
160
+ context 'with the json_api spec' do
161
+ let(:connection_options) do
162
+ { root_element: :data, json_spec: :json_api }
163
+ end
164
+
165
+ context "single response" do
166
+ let(:connection_response) { Typhoeus::Response.new(mock: true, code: 201, body: { data: { id: 12, attributes: { name: 'Mies' } } }.to_json, headers: { 'Content-Type' => 'application/json', 'Server' => 'nginx/1.4.6 (Ubuntu)' }) }
167
+
168
+ it 'parses the attributes from the nested hash' do
169
+ expect(response.attributes).to eql({ 'id' => 12, 'name' => 'Mies' })
170
+ end
171
+ end
172
+
173
+ context "empty response" do
174
+ let(:connection_response) { Typhoeus::Response.new(mock: true, code: 204, body: {}.to_json, headers: { 'Content-Type' => 'application/json', 'Server' => 'nginx/1.4.6 (Ubuntu)' }) }
175
+
176
+ it 'parses the attributes from the nested hash' do
177
+ expect(response.attributes).to eql({})
178
+ end
179
+ end
180
+
181
+ context "collection response" do
182
+ let(:connection_response) { Typhoeus::Response.new(mock: true, code: 201, body: { data: [{ id: 12, attributes: { name: 'Mies' } }] }.to_json, headers: { 'Content-Type' => 'application/json', 'Server' => 'nginx/1.4.6 (Ubuntu)' }) }
183
+
184
+ it 'parses the attributes from the nested hash' do
185
+ expect(response.attributes).to eql(['id' => 12, 'name' => 'Mies'])
186
+ end
187
+ end
188
+ end
159
189
  end
160
190
 
161
191
  describe '#errors' do
@@ -1,7 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe RemoteResource::VERSION do
4
- it { is_expected.to eql '1.0.4' }
4
+ it { is_expected.to eql '1.2.0' }
5
5
  end
6
-
7
-
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddy_remote_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
+ - Digidentity
7
8
  - Jan van der Pas
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-06-18 00:00:00.000000000 Z
12
+ date: 2021-01-08 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -117,7 +118,7 @@ dependencies:
117
118
  version: '4.1'
118
119
  - - "<"
119
120
  - !ruby/object:Gem::Version
120
- version: '6'
121
+ version: '7'
121
122
  type: :runtime
122
123
  prerelease: false
123
124
  version_requirements: !ruby/object:Gem::Requirement
@@ -127,7 +128,7 @@ dependencies:
127
128
  version: '4.1'
128
129
  - - "<"
129
130
  - !ruby/object:Gem::Version
130
- version: '6'
131
+ version: '7'
131
132
  - !ruby/object:Gem::Dependency
132
133
  name: activemodel
133
134
  requirement: !ruby/object:Gem::Requirement
@@ -137,7 +138,7 @@ dependencies:
137
138
  version: '4.1'
138
139
  - - "<"
139
140
  - !ruby/object:Gem::Version
140
- version: '6'
141
+ version: '7'
141
142
  type: :runtime
142
143
  prerelease: false
143
144
  version_requirements: !ruby/object:Gem::Requirement
@@ -147,7 +148,7 @@ dependencies:
147
148
  version: '4.1'
148
149
  - - "<"
149
150
  - !ruby/object:Gem::Version
150
- version: '6'
151
+ version: '7'
151
152
  - !ruby/object:Gem::Dependency
152
153
  name: virtus
153
154
  requirement: !ruby/object:Gem::Requirement
@@ -222,10 +223,24 @@ dependencies:
222
223
  - - ">="
223
224
  - !ruby/object:Gem::Version
224
225
  version: 0.7.0
226
+ - !ruby/object:Gem::Dependency
227
+ name: request_store
228
+ requirement: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - "~>"
231
+ - !ruby/object:Gem::Version
232
+ version: 1.4.1
233
+ type: :runtime
234
+ prerelease: false
235
+ version_requirements: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - "~>"
238
+ - !ruby/object:Gem::Version
239
+ version: 1.4.1
225
240
  description: RemoteResource, a gem to use resources with REST services. A replacement
226
241
  for ActiveResource gem.
227
242
  email:
228
- - jvanderpas@digidentity.eu
243
+ - development@digidentity.com
229
244
  executables: []
230
245
  extensions: []
231
246
  extra_rdoc_files: []
@@ -306,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
306
321
  version: '0'
307
322
  requirements: []
308
323
  rubyforge_project:
309
- rubygems_version: 2.5.1
324
+ rubygems_version: 2.7.6
310
325
  signing_key:
311
326
  specification_version: 4
312
327
  summary: RemoteResource, a gem to use resources with REST services.