ddy_remote_resource 1.0.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.