restful_resource 2.5.3 → 2.10.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
2
  SHA256:
3
- metadata.gz: 4e46af8066f1ca4d4e59dae90cd6283676f65e146ed62ab56c506eeafb189b95
4
- data.tar.gz: fdbb7277f080e8c00ff73e5b01f26954d67dd04189456a4500276b0a435b8a41
3
+ metadata.gz: 88ffc05f6fe383a8d7e885b26498b3d56b18f4ed3e3011b07e01638ebd0b09ba
4
+ data.tar.gz: 9e2e99681e0b464332ec34361c17903ff54ed368974cd52f401fe51ebe011aea
5
5
  SHA512:
6
- metadata.gz: 2039984d78d5131c8a88cb45fd7cc65a2626b2d2bee7977642ce10d84fd19f8d158ef7a7202f07e36dbf5ba4404cbf772f2f56f0f92497d1571f60f052dcfc68
7
- data.tar.gz: c382b0c2d0f1f4901a990ef771104d0350536e6bd773bed632dcfc74d4f090577044b255bf49544fc2939ff9848d60efdbd020d0f55d16f6bb1db5e70e06eb8a
6
+ metadata.gz: 8f1650c811c5ddb2c0d992449e47d2e7c804e3b142963d43a803a252b61884c8e2514aed11e3fcd1123f68cdff3cd7752773471ddd5b8b26a12b9a0dc95688d6
7
+ data.tar.gz: 300be14f9ac06b762c3302f4e71901c82ffc86dc5a74bb3daabe63eb032a72a9ac19cca82b1f337dd37cda31f69d006adfc2cc078fcc42687e850e08d316954f
@@ -7,7 +7,9 @@ jobs:
7
7
  - checkout
8
8
  - run:
9
9
  name: Bundle Install
10
- command: bundle check || bundle install
10
+ command: |
11
+ gem install bundler && \
12
+ bundle check || bundle install
11
13
  - run:
12
14
  name: Run rspec in parallel
13
15
  command: |
@@ -18,3 +20,10 @@ jobs:
18
20
  $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
19
21
  - store_test_results:
20
22
  path: test_results
23
+
24
+
25
+ workflows:
26
+ version: 2
27
+ build:
28
+ jobs:
29
+ - build
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
@@ -18,3 +17,4 @@ tmp
18
17
  .idea
19
18
  *.swp
20
19
  *.swo
20
+ /tags
@@ -1,5 +1,53 @@
1
1
  # Changelog
2
2
 
3
+ 2.10.0
4
+ ---
5
+
6
+ - Support Faraday v1 (#85)
7
+
8
+ 2.9.0
9
+ ---
10
+
11
+ - Add support for DELETE in RestfulResource::RailsValidations (#73)
12
+
13
+ 2.8.1
14
+ ---
15
+
16
+ - Looser Faraday requirement >= 0.15, < 1.1
17
+
18
+ 2.8.0
19
+ ---
20
+
21
+ - Make params hash in methods consistent, and always optional (#61)
22
+
23
+ 2.7.0
24
+ ---
25
+
26
+ - Add `X-Client-Start` header on request containing milliseconds since unix epoch
27
+
28
+ 2.6.1
29
+ ---
30
+
31
+ - Support only `faraday-0.15.x`
32
+ - `0.16.x` breaks `faraday-http-cache`
33
+
34
+ 2.6.0 (yanked)
35
+ ---
36
+
37
+ - Upgrade to `faraday-0.16.x`
38
+ - Add `X-Client-Timeout` header on requests if a client timeout is set
39
+
40
+ 2.5.3
41
+ ---
42
+
43
+ - Raise `RestfulResource::HttpClient::GatewayTimeout` when response status is `504`
44
+
45
+
46
+ 2.5.2
47
+ ---
48
+
49
+ - Add ability to set `timeout` and `open_timeout` options on connection
50
+
3
51
  2.5.1
4
52
  ---
5
53
 
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- gem 'pry'
@@ -0,0 +1,116 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ restful_resource (2.10.0)
5
+ activesupport (~> 6.0)
6
+ faraday (~> 1.0)
7
+ faraday-cdn-metrics (~> 0.2)
8
+ faraday-encoding
9
+ faraday-http-cache (~> 2.2)
10
+ faraday_middleware (~> 1.0)
11
+ link_header
12
+ rack (~> 2.2)
13
+ typhoeus (~> 1.4)
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ activesupport (6.0.3.1)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (>= 0.7, < 2)
21
+ minitest (~> 5.1)
22
+ tzinfo (~> 1.1)
23
+ zeitwerk (~> 2.2, >= 2.2.2)
24
+ ast (2.4.0)
25
+ carwow_rubocop (3.3.0)
26
+ rubocop (>= 0.78)
27
+ rubocop-performance
28
+ rubocop-rspec
29
+ coderay (1.1.2)
30
+ concurrent-ruby (1.1.6)
31
+ diff-lcs (1.3)
32
+ ethon (0.12.0)
33
+ ffi (>= 1.3.0)
34
+ faraday (1.0.1)
35
+ multipart-post (>= 1.2, < 3)
36
+ faraday-cdn-metrics (0.2.0)
37
+ faraday (~> 1)
38
+ faraday-encoding (0.0.5)
39
+ faraday
40
+ faraday-http-cache (2.2.0)
41
+ faraday (>= 0.8)
42
+ faraday_middleware (1.0.0)
43
+ faraday (~> 1.0)
44
+ ffi (1.13.0)
45
+ i18n (1.8.3)
46
+ concurrent-ruby (~> 1.0)
47
+ link_header (0.0.8)
48
+ method_source (1.0.0)
49
+ minitest (5.14.1)
50
+ multipart-post (2.1.1)
51
+ parallel (1.19.1)
52
+ parser (2.7.1.2)
53
+ ast (~> 2.4.0)
54
+ pry (0.13.1)
55
+ coderay (~> 1.1)
56
+ method_source (~> 1.0)
57
+ rack (2.2.2)
58
+ rainbow (3.0.0)
59
+ rake (13.0.1)
60
+ rexml (3.2.4)
61
+ rspec (3.9.0)
62
+ rspec-core (~> 3.9.0)
63
+ rspec-expectations (~> 3.9.0)
64
+ rspec-mocks (~> 3.9.0)
65
+ rspec-core (3.9.0)
66
+ rspec-support (~> 3.9.0)
67
+ rspec-expectations (3.9.0)
68
+ diff-lcs (>= 1.2.0, < 2.0)
69
+ rspec-support (~> 3.9.0)
70
+ rspec-its (1.3.0)
71
+ rspec-core (>= 3.0.0)
72
+ rspec-expectations (>= 3.0.0)
73
+ rspec-mocks (3.9.0)
74
+ diff-lcs (>= 1.2.0, < 2.0)
75
+ rspec-support (~> 3.9.0)
76
+ rspec-support (3.9.0)
77
+ rspec_junit_formatter (0.4.1)
78
+ rspec-core (>= 2, < 4, != 2.12.0)
79
+ rubocop (0.84.0)
80
+ parallel (~> 1.10)
81
+ parser (>= 2.7.0.1)
82
+ rainbow (>= 2.2.2, < 4.0)
83
+ rexml
84
+ rubocop-ast (>= 0.0.3)
85
+ ruby-progressbar (~> 1.7)
86
+ unicode-display_width (>= 1.4.0, < 2.0)
87
+ rubocop-ast (0.0.3)
88
+ parser (>= 2.7.0.1)
89
+ rubocop-performance (1.6.0)
90
+ rubocop (>= 0.71.0)
91
+ rubocop-rspec (1.39.0)
92
+ rubocop (>= 0.68.1)
93
+ ruby-progressbar (1.10.1)
94
+ thread_safe (0.3.6)
95
+ typhoeus (1.4.0)
96
+ ethon (>= 0.9.0)
97
+ tzinfo (1.2.7)
98
+ thread_safe (~> 0.1)
99
+ unicode-display_width (1.7.0)
100
+ zeitwerk (2.3.0)
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ bundler
107
+ carwow_rubocop
108
+ pry
109
+ rake
110
+ restful_resource!
111
+ rspec
112
+ rspec-its
113
+ rspec_junit_formatter
114
+
115
+ BUNDLED WITH
116
+ 2.1.4
@@ -12,7 +12,7 @@ module RestfulResource
12
12
  timeout: nil,
13
13
  open_timeout: nil,
14
14
  faraday_config: nil,
15
- faraday_options: nil)
15
+ faraday_options: {})
16
16
 
17
17
  @base_url = URI.parse(base_url)
18
18
 
@@ -33,14 +33,14 @@ module RestfulResource
33
33
  @resource_path = url
34
34
  end
35
35
 
36
- def self.find(id, params = {})
36
+ def self.find(id, **params)
37
37
  params_without_options, options = format_params(params)
38
38
 
39
39
  response = http.get(member_url(id, params_without_options), **options)
40
40
  new(parse_json(response.body))
41
41
  end
42
42
 
43
- def self.where(params = {})
43
+ def self.where(**params)
44
44
  params_without_options, options = format_params(params)
45
45
 
46
46
  url = collection_url(params_without_options)
@@ -48,7 +48,7 @@ module RestfulResource
48
48
  paginate_response(response)
49
49
  end
50
50
 
51
- def self.get(params = {})
51
+ def self.get(**params)
52
52
  params_without_options, options = format_params(params)
53
53
 
54
54
  response = http.get(collection_url(params_without_options), **options)
@@ -58,7 +58,7 @@ module RestfulResource
58
58
  def self.delete(id, **params)
59
59
  params_without_options, options = format_params(params)
60
60
  response = http.delete(member_url(id, params_without_options), **options)
61
- RestfulResource::OpenObject.new(parse_json(response.body))
61
+ new(parse_json(response.body))
62
62
  end
63
63
 
64
64
  def self.patch(id, data: {}, headers: {}, **params)
@@ -92,7 +92,7 @@ module RestfulResource
92
92
  new(parse_json(response.body))
93
93
  end
94
94
 
95
- def self.all(params = {})
95
+ def self.all(**params)
96
96
  where(params)
97
97
  end
98
98
 
@@ -133,14 +133,14 @@ module RestfulResource
133
133
  result
134
134
  end
135
135
 
136
- def self.collection_url(params)
136
+ def self.collection_url(**params)
137
137
  url = merge_url_paths(base_url, @resource_path, @action_prefix)
138
138
  replace_parameters(url, params)
139
139
  end
140
140
 
141
141
  private
142
142
 
143
- def self.format_params(params = {})
143
+ def self.format_params(**params)
144
144
  headers = params.delete(:headers) || {}
145
145
 
146
146
  headers[:cache_control] = 'no-cache' if params.delete(:no_cache)
@@ -154,7 +154,7 @@ module RestfulResource
154
154
  uri.merge(paths.compact.join('/')).to_s
155
155
  end
156
156
 
157
- def self.member_url(id, params)
157
+ def self.member_url(id, **params)
158
158
  raise ResourceIdMissingError if id.blank?
159
159
 
160
160
  url = merge_url_paths(base_url, @resource_path, CGI.escape(id.to_s), @action_prefix)
@@ -173,7 +173,7 @@ module RestfulResource
173
173
  ActiveSupport::JSON.decode(json)
174
174
  end
175
175
 
176
- def self.replace_parameters(url, params)
176
+ def self.replace_parameters(url, **params)
177
177
  missing_params = []
178
178
  params = params.with_indifferent_access
179
179
 
@@ -88,7 +88,7 @@ module RestfulResource
88
88
  timeout: nil,
89
89
  open_timeout: nil,
90
90
  faraday_config: nil,
91
- faraday_options: nil)
91
+ faraday_options: {})
92
92
  api_name = instrumentation[:api_name] ||= 'api'
93
93
  instrumentation[:request_instrument_name] ||= "http.#{api_name}"
94
94
  instrumentation[:cache_instrument_name] ||= "http_cache.#{api_name}"
@@ -202,7 +202,7 @@ module RestfulResource
202
202
  cache_instrument_name: nil,
203
203
  server_cache_instrument_name: nil,
204
204
  faraday_config: nil,
205
- faraday_options: nil)
205
+ faraday_options: {})
206
206
 
207
207
  @connection = Faraday.new(nil, faraday_options) do |b|
208
208
  b.request :json
@@ -252,7 +252,8 @@ module RestfulResource
252
252
  req.body = request.body unless request.body.nil?
253
253
  req.url request.url
254
254
 
255
- req.headers = req.headers.merge(request.headers)
255
+ req.headers = req.headers.merge(request.headers).merge(x_client_start: time_current_ms)
256
+ req.headers = req.headers.merge(x_client_timeout: req.options[:timeout]) if req.options[:timeout]
256
257
  end
257
258
 
258
259
  Response.new(body: response.body, headers: response.headers, status: response.status)
@@ -264,6 +265,15 @@ module RestfulResource
264
265
  response = e.response
265
266
  raise ClientError, request unless response
266
267
 
268
+ handle_error(request, response)
269
+ rescue Faraday::ServerError => e
270
+ response = e.response
271
+ raise ClientError, request unless response
272
+
273
+ handle_error(request, response)
274
+ end
275
+
276
+ def handle_error(request, response)
267
277
  case response[:status]
268
278
  when 404 then raise HttpClient::ResourceNotFound.new(request, response)
269
279
  when 409 then raise HttpClient::Conflict.new(request, response)
@@ -275,5 +285,9 @@ module RestfulResource
275
285
  else raise HttpClient::OtherHttpError.new(request, response)
276
286
  end
277
287
  end
288
+
289
+ def time_current_ms
290
+ (Time.current.to_f * 1_000.0).to_i
291
+ end
278
292
  end
279
293
  end
@@ -17,13 +17,16 @@ module RestfulResource
17
17
  with_validations { super }
18
18
  end
19
19
 
20
+ def delete(*)
21
+ with_validations { super }
22
+ end
23
+
20
24
  private
21
25
 
22
26
  def with_validations(id = nil, data: {})
23
27
  yield
24
28
  rescue HttpClient::UnprocessableEntity => e
25
29
  errors = parse_json(e.response.body)
26
- result = nil
27
30
  result = if errors.is_a?(Hash) && errors.key?('errors')
28
31
  data.merge(errors)
29
32
  else
@@ -1,3 +1,3 @@
1
1
  module RestfulResource
2
- VERSION = '2.5.3'.freeze
2
+ VERSION = '2.10.0'.freeze
3
3
  end
@@ -20,18 +20,19 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler'
22
22
  spec.add_development_dependency 'carwow_rubocop'
23
+ spec.add_development_dependency 'pry'
23
24
  spec.add_development_dependency 'rake'
24
25
  spec.add_development_dependency 'rspec'
25
- spec.add_development_dependency 'rspec_junit_formatter'
26
26
  spec.add_development_dependency 'rspec-its'
27
+ spec.add_development_dependency 'rspec_junit_formatter'
27
28
 
28
- spec.add_dependency 'activesupport'
29
- spec.add_dependency 'faraday'
30
- spec.add_dependency 'faraday-cdn-metrics'
29
+ spec.add_dependency 'activesupport', '~> 6.0'
30
+ spec.add_dependency 'faraday', '~> 1.0'
31
+ spec.add_dependency 'faraday-cdn-metrics', '~> 0.2'
31
32
  spec.add_dependency 'faraday-encoding'
32
- spec.add_dependency 'faraday-http-cache'
33
- spec.add_dependency 'faraday_middleware'
33
+ spec.add_dependency 'faraday-http-cache', '~> 2.2'
34
+ spec.add_dependency 'faraday_middleware', '~> 1.0'
34
35
  spec.add_dependency 'link_header'
35
- spec.add_dependency 'rack'
36
- spec.add_dependency 'typhoeus'
36
+ spec.add_dependency 'rack', '~> 2.2'
37
+ spec.add_dependency 'typhoeus', '~> 1.4'
37
38
  end
@@ -2,12 +2,8 @@ require_relative '../spec_helper'
2
2
 
3
3
  describe RestfulResource::Base, 'authorization' do
4
4
  before do
5
- class FirstClient < RestfulResource::Base
6
- end
7
-
8
- class SecondClient < RestfulResource::Base
9
- end
10
-
5
+ class FirstClient < RestfulResource::Base; end
6
+ class SecondClient < RestfulResource::Base; end
11
7
  class NotConfiguredClient < RestfulResource::Base; end
12
8
 
13
9
  class FirstTest < FirstClient
@@ -3,8 +3,10 @@ require_relative '../spec_helper'
3
3
  RSpec.describe RestfulResource::Base do
4
4
  before do
5
5
  @mock_http = double('mock_http')
6
- allow(described_class).to receive(:http).and_return(@mock_http)
7
- described_class.configure(base_url: 'http://api.carwow.co.uk/')
6
+ allow(Make).to receive(:http).and_return(@mock_http)
7
+ Make.configure(base_url: 'http://api.carwow.co.uk/')
8
+ allow(Model).to receive(:http).and_return(@mock_http)
9
+ Model.configure(base_url: 'http://api.carwow.co.uk/')
8
10
  end
9
11
 
10
12
  it 'acts as an openobject' do
@@ -143,9 +145,14 @@ RSpec.describe RestfulResource::Base do
143
145
  end
144
146
 
145
147
  describe '#base_url' do
148
+ before do
149
+ @mock_http = double('mock_http')
150
+ allow(TestA).to receive(:http).and_return(@mock_http)
151
+ allow(TestB).to receive(:http).and_return(@mock_http)
152
+ end
153
+
146
154
  it 'is different for each subclass of Base' do
147
155
  BaseA.configure(base_url: 'http://a.carwow.co.uk')
148
-
149
156
  BaseB.configure(base_url: 'http://b.carwow.co.uk')
150
157
 
151
158
  expect_get('http://a.carwow.co.uk/testa/1', RestfulResource::Response.new)
@@ -385,6 +392,7 @@ RSpec.describe RestfulResource::Base do
385
392
  let(:faraday_options) { double }
386
393
 
387
394
  it 'passes arguments to HttpClient' do
395
+ client = Class.new(described_class)
388
396
  expect(RestfulResource::HttpClient).to receive(:new).with(username: username,
389
397
  password: password,
390
398
  auth_token: auth_token,
@@ -397,7 +405,7 @@ RSpec.describe RestfulResource::Base do
397
405
  faraday_options: faraday_options
398
406
  )
399
407
 
400
- described_class.configure(base_url: 'http://foo.bar',
408
+ client.configure(base_url: 'http://foo.bar',
401
409
  username: username,
402
410
  password: password,
403
411
  auth_token: auth_token,
@@ -19,7 +19,7 @@ describe RestfulResource::HttpClient do
19
19
 
20
20
  describe 'Builder configuration' do
21
21
  it 'uses the typhoeus adapter' do
22
- expect(middleware).to include Faraday::Adapter::Typhoeus
22
+ expect(connection.adapter).to eq Faraday::Adapter::Typhoeus
23
23
  end
24
24
 
25
25
  it 'url_encodes requests' do
@@ -81,13 +81,13 @@ describe RestfulResource::HttpClient do
81
81
  described_class.new(instrumentation: { app_name: 'rails', api_name: 'api', metric_class: FakeMetrics })
82
82
 
83
83
  expect(RestfulResource::Instrumentation).to have_received(:new)
84
- .with(app_name: 'rails',
85
- api_name: 'api',
86
- request_instrument_name: 'http.api',
87
- cache_instrument_name: 'http_cache.api',
88
- server_cache_instrument_name: 'cdn_metrics.api',
89
- metric_class: FakeMetrics
90
- )
84
+ .with(app_name: 'rails',
85
+ api_name: 'api',
86
+ request_instrument_name: 'http.api',
87
+ cache_instrument_name: 'http_cache.api',
88
+ server_cache_instrument_name: 'cdn_metrics.api',
89
+ metric_class: FakeMetrics
90
+ )
91
91
  end
92
92
 
93
93
  it 'subscribes to the notifications' do
@@ -1,8 +1,8 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  RSpec.describe RestfulResource::HttpClient do
4
- def faraday_connection
5
- Faraday.new do |builder|
4
+ def faraday_connection(opts = {})
5
+ Faraday.new(opts) do |builder|
6
6
  builder.request :url_encoded
7
7
  builder.response :raise_error
8
8
  builder.adapter :test do |stubs|
@@ -16,175 +16,97 @@ RSpec.describe RestfulResource::HttpClient do
16
16
  end
17
17
 
18
18
  describe 'Basic HTTP' do
19
- it 'executes get' do
20
- connection = faraday_connection do |stubs|
21
- stubs.get('http://httpbin.org/get') { |_env| [200, {}, nil] }
22
- end
23
-
24
- response = http_client(connection).get('http://httpbin.org/get')
25
- expect(response.status).to eq 200
26
- end
27
-
28
- it 'executes patch' do
29
- connection = faraday_connection do |stubs|
30
- # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
31
- stubs.patch('http://httpbin.org/patch', 'name=Alfred') { |_env| [200, {}, nil] }
32
- end
33
-
34
- response = http_client(connection).patch('http://httpbin.org/patch', data: { name: 'Alfred' })
35
- expect(response.status).to eq 200
36
- end
37
-
38
- it 'executes put' do
39
- connection = faraday_connection do |stubs|
40
- # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
41
- stubs.put('http://httpbin.org/put', 'name=Alfred') { |_env| [200, {}, nil] }
42
- end
43
-
44
- response = http_client(connection).put('http://httpbin.org/put', data: { name: 'Alfred' })
45
- expect(response.status).to eq 200
46
- end
47
-
48
- it 'executes post' do
49
- connection = faraday_connection do |stubs|
50
- # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
51
- stubs.post('http://httpbin.org/post', 'name=Alfred') { |_env| [200, {}, %("name": "Alfred")] }
52
- end
53
-
54
- response = http_client(connection).post('http://httpbin.org/post', data: { name: 'Alfred' })
55
-
56
- expect(response.body).to include 'name": "Alfred'
57
- expect(response.status).to eq 200
58
- end
59
-
60
- it 'executes delete' do
61
- connection = faraday_connection do |stubs|
62
- stubs.delete('http://httpbin.org/delete') { |_env| [200, {}, nil] }
63
- end
64
-
65
- response = http_client(connection).delete('http://httpbin.org/delete')
66
-
67
- expect(response.status).to eq 200
68
- end
19
+ shared_examples 'error codes throw exception' do |verb, status, exception_class|
20
+ it "should raise an error #{status}" do
21
+ url = "http://httpbin.org/status/#{status}"
69
22
 
70
- it 'patch should raise error 409' do
71
- connection = faraday_connection do |stubs|
72
- stubs.patch('http://httpbin.org/status/409') { |_env| [409, {}, nil] }
73
- end
74
-
75
- expect { http_client(connection).patch('http://httpbin.org/status/409') }.to raise_error(RestfulResource::HttpClient::Conflict)
76
- end
23
+ connection = faraday_connection do |stubs|
24
+ stubs.send(verb, url) { [status, {}, nil] }
25
+ end
77
26
 
78
- it 'patch should raise error 422' do
79
- connection = faraday_connection do |stubs|
80
- stubs.patch('http://httpbin.org/status/422') { |_env| [422, {}, nil] }
27
+ expect { http_client(connection).send(verb, url) }.to raise_error(exception_class)
81
28
  end
82
-
83
- expect { http_client(connection).patch('http://httpbin.org/status/422') }.to raise_error(RestfulResource::HttpClient::UnprocessableEntity)
84
29
  end
85
30
 
86
- it 'put should raise error 409' do
87
- connection = faraday_connection do |stubs|
88
- stubs.put('http://httpbin.org/status/409') { |_env| [409, {}, nil] }
89
- end
31
+ shared_examples 'raise an exception on error responses' do |verb|
32
+ include_examples 'error codes throw exception', verb, 409, RestfulResource::HttpClient::Conflict
33
+ include_examples 'error codes throw exception', verb, 404, RestfulResource::HttpClient::ResourceNotFound
34
+ include_examples 'error codes throw exception', verb, 422, RestfulResource::HttpClient::UnprocessableEntity
35
+ include_examples 'error codes throw exception', verb, 429, RestfulResource::HttpClient::TooManyRequests
36
+ include_examples 'error codes throw exception', verb, 502, RestfulResource::HttpClient::BadGateway
37
+ include_examples 'error codes throw exception', verb, 503, RestfulResource::HttpClient::ServiceUnavailable
38
+ include_examples 'error codes throw exception', verb, 504, RestfulResource::HttpClient::GatewayTimeout
90
39
 
91
- expect { http_client(connection).put('http://httpbin.org/status/409') }.to raise_error(RestfulResource::HttpClient::Conflict)
40
+ include_examples 'error codes throw exception', verb, 418, RestfulResource::HttpClient::OtherHttpError
92
41
  end
93
42
 
94
- it 'put should raise error 422' do
95
- connection = faraday_connection do |stubs|
96
- stubs.put('http://httpbin.org/status/422') { |_env| [422, {}, nil] }
97
- end
43
+ context 'GET' do
44
+ include_examples 'raise an exception on error responses', :get
98
45
 
99
- expect { http_client(connection).put('http://httpbin.org/status/422') }.to raise_error(RestfulResource::HttpClient::UnprocessableEntity)
100
- end
46
+ it 'executes get' do
47
+ connection = faraday_connection do |stubs|
48
+ stubs.get('http://httpbin.org/get') { |_env| [200, {}, nil] }
49
+ end
101
50
 
102
- it 'post should raise error 422' do
103
- connection = faraday_connection do |stubs|
104
- stubs.post('http://httpbin.org/status/422') { |_env| [422, {}, nil] }
51
+ response = http_client(connection).get('http://httpbin.org/get')
52
+ expect(response.status).to eq 200
105
53
  end
106
-
107
- expect { http_client(connection).post('http://httpbin.org/status/422') }.to raise_error(RestfulResource::HttpClient::UnprocessableEntity)
108
54
  end
109
55
 
110
- it 'post should raise error 429' do
111
- connection = faraday_connection do |stubs|
112
- stubs.post('http://httpbin.org/status/429') { |_env| [429, {}, nil] }
113
- end
56
+ context 'PATCH' do
57
+ include_examples 'raise an exception on error responses', :patch
114
58
 
115
- expect { http_client(connection).post('http://httpbin.org/status/429') }.to raise_error(RestfulResource::HttpClient::TooManyRequests)
116
- end
59
+ it 'executes patch' do
60
+ connection = faraday_connection do |stubs|
61
+ # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
62
+ stubs.patch('http://httpbin.org/patch', 'name=Alfred') { |_env| [200, {}, nil] }
63
+ end
117
64
 
118
- it 'patch should raise error 502' do
119
- connection = faraday_connection do |stubs|
120
- stubs.patch('http://httpbin.org/status/502') { |_env| [502, {}, nil] }
65
+ response = http_client(connection).patch('http://httpbin.org/patch', data: { name: 'Alfred' })
66
+ expect(response.status).to eq 200
121
67
  end
122
-
123
- expect { http_client(connection).patch('http://httpbin.org/status/502') }.to raise_error(RestfulResource::HttpClient::BadGateway)
124
68
  end
125
69
 
126
- it 'put should raise error 502' do
127
- connection = faraday_connection do |stubs|
128
- stubs.put('http://httpbin.org/status/502') { |_env| [502, {}, nil] }
129
- end
130
-
131
- expect { http_client(connection).put('http://httpbin.org/status/502') }.to raise_error(RestfulResource::HttpClient::BadGateway)
132
- end
70
+ context 'PUT' do
71
+ include_examples 'raise an exception on error responses', :put
72
+ it 'executes put' do
73
+ connection = faraday_connection do |stubs|
74
+ # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
75
+ stubs.put('http://httpbin.org/put', 'name=Alfred') { |_env| [200, {}, nil] }
76
+ end
133
77
 
134
- it 'post should raise error 502' do
135
- connection = faraday_connection do |stubs|
136
- stubs.post('http://httpbin.org/status/502') { |_env| [502, {}, nil] }
78
+ response = http_client(connection).put('http://httpbin.org/put', data: { name: 'Alfred' })
79
+ expect(response.status).to eq 200
137
80
  end
138
-
139
- expect { http_client(connection).post('http://httpbin.org/status/502') }.to raise_error(RestfulResource::HttpClient::BadGateway)
140
81
  end
141
82
 
142
- it 'patch should raise error 503' do
143
- connection = faraday_connection do |stubs|
144
- stubs.patch('http://httpbin.org/status/503') { |_env| [503, {}, nil] }
145
- end
83
+ context 'POST' do
84
+ include_examples 'raise an exception on error responses', :post
85
+ it 'executes post' do
86
+ connection = faraday_connection do |stubs|
87
+ # Note: request body is serialized as url-encoded so the stub body must be in the same format to match
88
+ stubs.post('http://httpbin.org/post', 'name=Alfred') { |_env| [200, {}, %("name": "Alfred")] }
89
+ end
146
90
 
147
- expect { http_client(connection).patch('http://httpbin.org/status/503') }.to raise_error(RestfulResource::HttpClient::ServiceUnavailable)
148
- end
91
+ response = http_client(connection).post('http://httpbin.org/post', data: { name: 'Alfred' })
149
92
 
150
- it 'put should raise error 503' do
151
- connection = faraday_connection do |stubs|
152
- stubs.put('http://httpbin.org/status/503') { |_env| [503, {}, nil] }
93
+ expect(response.body).to include 'name": "Alfred'
94
+ expect(response.status).to eq 200
153
95
  end
154
-
155
- expect { http_client(connection).put('http://httpbin.org/status/503') }.to raise_error(RestfulResource::HttpClient::ServiceUnavailable)
156
96
  end
157
97
 
158
- it 'post should raise error 503' do
159
- connection = faraday_connection do |stubs|
160
- stubs.post('http://httpbin.org/status/503') { |_env| [503, {}, nil] }
161
- end
162
-
163
- expect { http_client(connection).post('http://httpbin.org/status/503') }.to raise_error(RestfulResource::HttpClient::ServiceUnavailable)
164
- end
98
+ context 'DELETE' do
99
+ include_examples 'raise an exception on error responses', :delete
165
100
 
166
- it 'post should raise error 504' do
167
- connection = faraday_connection do |stubs|
168
- stubs.post('http://httpbin.org/status/504') { |_env| [504, {}, nil] }
169
- end
101
+ it 'executes delete' do
102
+ connection = faraday_connection do |stubs|
103
+ stubs.delete('http://httpbin.org/delete') { |_env| [200, {}, nil] }
104
+ end
170
105
 
171
- expect { http_client(connection).post('http://httpbin.org/status/504') }.to raise_error(RestfulResource::HttpClient::GatewayTimeout)
172
- end
106
+ response = http_client(connection).delete('http://httpbin.org/delete')
173
107
 
174
- it 'raises error on 404' do
175
- connection = faraday_connection do |stubs|
176
- stubs.get('http://httpbin.org/status/404') { |_env| [404, {}, nil] }
177
- stubs.post('http://httpbin.org/status/404') { |_env| [404, {}, nil] }
178
- stubs.patch('http://httpbin.org/status/404') { |_env| [404, {}, nil] }
179
- stubs.put('http://httpbin.org/status/404') { |_env| [404, {}, nil] }
180
- stubs.delete('http://httpbin.org/status/404') { |_env| [404, {}, nil] }
108
+ expect(response.status).to eq 200
181
109
  end
182
-
183
- expect { http_client(connection).get('http://httpbin.org/status/404') }.to raise_error(RestfulResource::HttpClient::ResourceNotFound)
184
- expect { http_client(connection).delete('http://httpbin.org/status/404') }.to raise_error(RestfulResource::HttpClient::ResourceNotFound)
185
- expect { http_client(connection).patch('http://httpbin.org/status/404', data: { name: 'Mad cow' }) }.to raise_error(RestfulResource::HttpClient::ResourceNotFound)
186
- expect { http_client(connection).put('http://httpbin.org/status/404', data: { name: 'Mad cow' }) }.to raise_error(RestfulResource::HttpClient::ResourceNotFound)
187
- expect { http_client(connection).post('http://httpbin.org/status/404', data: { name: 'Mad cow' }) }.to raise_error(RestfulResource::HttpClient::ResourceNotFound)
188
110
  end
189
111
 
190
112
  it 'raises Faraday::ConnectionFailed errors' do
@@ -210,14 +132,6 @@ RSpec.describe RestfulResource::HttpClient do
210
132
 
211
133
  expect { http_client(connection).get('https://localhost:3005') }.to raise_error(RestfulResource::HttpClient::ClientError)
212
134
  end
213
-
214
- it 'raises OtherHttpError for other status response codes' do
215
- connection = faraday_connection do |stubs|
216
- stubs.get('http://httpbin.org/status/418') { |_env| [418, {}, nil] }
217
- end
218
-
219
- expect { http_client(connection).get('http://httpbin.org/status/418') }.to raise_error(RestfulResource::HttpClient::OtherHttpError)
220
- end
221
135
  end
222
136
 
223
137
  describe 'Authentication' do
@@ -292,4 +206,69 @@ RSpec.describe RestfulResource::HttpClient do
292
206
  expect(response.status).to eq 200
293
207
  end
294
208
  end
209
+
210
+ describe 'X-Client-Timeout' do
211
+ let(:http_client) { described_class.new(connection: connection, timeout: timeout) }
212
+ let(:connection) do
213
+ conn = faraday_connection do |stubs|
214
+ stubs.get('http://httpbin.org/get', required_headers) { |_env| [200, {}, nil] }
215
+ end
216
+
217
+ conn.options[:timeout] = timeout
218
+
219
+ conn
220
+ end
221
+
222
+
223
+ context 'when explicit timeout set on connection' do
224
+ let(:timeout) { 5 }
225
+ let(:required_headers) { { 'X-Client-Timeout' => 5 } }
226
+ it 'sets X-Client-Timeout correctly' do
227
+ response = http_client.get('http://httpbin.org/get')
228
+
229
+ expect(response.status).to eq 200
230
+ end
231
+ end
232
+
233
+ context 'when not explicit timeout set' do
234
+ let(:timeout) { nil }
235
+ let(:required_headers) { {} }
236
+
237
+ it 'sets X-Client-Timeout correctly' do
238
+ response = http_client.get('http://httpbin.org/get')
239
+
240
+ expect(response.status).to eq 200
241
+ end
242
+ end
243
+
244
+ context 'when set on request' do
245
+ let(:timeout) { nil }
246
+ let(:required_headers) { { 'X-Client-Timeout' => 1 } }
247
+
248
+ it 'sets X-Client-Timeout correctly' do
249
+ response = http_client.get('http://httpbin.org/get', timeout: 1)
250
+
251
+ expect(response.status).to eq 200
252
+ end
253
+ end
254
+ end
255
+
256
+ describe 'X-Client-Start' do
257
+ let(:now) { Time.current }
258
+ let(:required_headers) { { 'X-Client-Start' => (now.to_f * 1000.0).to_i } }
259
+ let(:http_client) { described_class.new(connection: connection) }
260
+ let(:connection) do
261
+ conn = faraday_connection do |stubs|
262
+ stubs.get('http://httpbin.org/get', required_headers) { |_env| [200, {}, nil] }
263
+ end
264
+ end
265
+
266
+ before { allow(Time).to receive(:current).and_return(now) }
267
+
268
+ it 'sets X-Client-Start correctly' do
269
+ response = http_client.get('http://httpbin.org/get')
270
+
271
+ expect(response.status).to eq 200
272
+ end
273
+ end
295
274
  end
@@ -3,8 +3,8 @@ require_relative '../spec_helper'
3
3
  RSpec.describe RestfulResource::RailsValidations do
4
4
  before do
5
5
  @mock_http = double('mock_http')
6
- RestfulResource::Base.configure(base_url: 'http://api.carwow.co.uk/')
7
- allow(RestfulResource::Base).to receive(:http).and_return(@mock_http)
6
+ Dealer.configure(base_url: 'http://api.carwow.co.uk/')
7
+ allow(Dealer).to receive(:http).and_return(@mock_http)
8
8
  end
9
9
 
10
10
  context '#patch without errors' do
@@ -72,13 +72,13 @@ RSpec.describe RestfulResource::RailsValidations do
72
72
  end
73
73
  end
74
74
 
75
- context '#patch without errors' do
75
+ context '#put without errors' do
76
76
  before do
77
77
  data = { name: 'Barak' }
78
78
  expected_response = RestfulResource::Response.new(body: { name: 'Barak' }.to_json)
79
- expect_patch('http://api.carwow.co.uk/dealers/1', expected_response, data: data)
79
+ expect_put('http://api.carwow.co.uk/dealers/1', expected_response, data: data)
80
80
 
81
- @object = Dealer.patch(1, data: data)
81
+ @object = Dealer.put(1, data: data)
82
82
  end
83
83
 
84
84
  it 'returns object' do
@@ -239,4 +239,53 @@ RSpec.describe RestfulResource::RailsValidations do
239
239
  expect(@object.errors).to eq @error
240
240
  end
241
241
  end
242
+
243
+ describe '#delete' do
244
+ subject { Dealer.delete(123) }
245
+
246
+ context 'without errors' do
247
+ before do
248
+ expected_response = RestfulResource::Response.new(body: { name: 'Barak' }.to_json)
249
+ expect_delete('http://api.carwow.co.uk/dealers/123', expected_response)
250
+ end
251
+
252
+ it 'returns object' do
253
+ expect(subject.name).to eq 'Barak'
254
+ end
255
+
256
+ it 'returns valid object' do
257
+ expect(subject).to be_valid
258
+ end
259
+ end
260
+
261
+ context 'with errors' do
262
+ let(:errors) { { errors: ['Cannot use Ninja Turtles names'] } }
263
+
264
+ before do
265
+ expected_response = RestfulResource::Response.new(body: errors.to_json)
266
+ expect_delete_with_unprocessable_entity('http://api.carwow.co.uk/dealers/123', expected_response)
267
+ end
268
+
269
+ it 'has an error' do
270
+ expect(subject.errors.count).to eq 1
271
+ end
272
+
273
+ it 'has correct error' do
274
+ expect(subject.errors.first).to eq 'Cannot use Ninja Turtles names'
275
+ end
276
+
277
+ it 'returns not valid object' do
278
+ expect(subject).not_to be_valid
279
+ end
280
+
281
+ context 'when there is a single error' do
282
+ let(:errors) { 'Cannot use Ninja Turtles names' }
283
+
284
+ it 'handles errors returned as root object' do
285
+ expect(subject).not_to be_valid
286
+ expect(subject.errors).to eq errors
287
+ end
288
+ end
289
+ end
290
+ end
242
291
  end
@@ -3,8 +3,8 @@ require_relative '../spec_helper'
3
3
  RSpec.describe RestfulResource::Redirections do
4
4
  before do
5
5
  @mock_http = double('mock_http')
6
- allow(RestfulResource::Base).to receive(:http).and_return(@mock_http)
7
- RestfulResource::Base.configure(base_url: 'http://api.carwow.co.uk/')
6
+ allow(ModelWithRedirections).to receive(:http).and_return(@mock_http)
7
+ ModelWithRedirections.configure(base_url: 'http://api.carwow.co.uk/')
8
8
  end
9
9
 
10
10
  describe '#post' do
@@ -1,3 +1,4 @@
1
+ require 'pry'
1
2
  require 'rspec'
2
3
  require 'rspec/its'
3
4
  require_relative '../lib/restful_resource'
@@ -65,3 +66,10 @@ def expect_post_with_unprocessable_entity(url, response, data: {})
65
66
  exception = RestfulResource::HttpClient::UnprocessableEntity.new(request, rest_client_response)
66
67
  expect(@mock_http).to receive(:post).with(url, data: data, headers: {}, open_timeout: nil, timeout: nil).and_raise(exception)
67
68
  end
69
+
70
+ def expect_delete_with_unprocessable_entity(url, response)
71
+ request = RestfulResource::Request.new(:delete, url)
72
+ rest_client_response = OpenStruct.new(body: response.body, headers: response.headers, code: response.status)
73
+ exception = RestfulResource::HttpClient::UnprocessableEntity.new(request, rest_client_response)
74
+ expect(@mock_http).to receive(:delete).with(url, headers: {}, open_timeout: nil, timeout: nil).and_raise(exception)
75
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restful_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.3
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Santoro
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-09-11 00:00:00.000000000 Z
12
+ date: 2020-06-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -40,7 +40,7 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
- name: rake
43
+ name: pry
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
@@ -54,7 +54,7 @@ dependencies:
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: rspec
57
+ name: rake
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - ">="
@@ -68,7 +68,7 @@ dependencies:
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
- name: rspec_junit_formatter
71
+ name: rspec
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
@@ -96,47 +96,61 @@ dependencies:
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  - !ruby/object:Gem::Dependency
99
- name: activesupport
99
+ name: rspec_junit_formatter
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
- type: :runtime
105
+ type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: activesupport
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '6.0'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '6.0'
112
126
  - !ruby/object:Gem::Dependency
113
127
  name: faraday
114
128
  requirement: !ruby/object:Gem::Requirement
115
129
  requirements:
116
- - - ">="
130
+ - - "~>"
117
131
  - !ruby/object:Gem::Version
118
- version: '0'
132
+ version: '1.0'
119
133
  type: :runtime
120
134
  prerelease: false
121
135
  version_requirements: !ruby/object:Gem::Requirement
122
136
  requirements:
123
- - - ">="
137
+ - - "~>"
124
138
  - !ruby/object:Gem::Version
125
- version: '0'
139
+ version: '1.0'
126
140
  - !ruby/object:Gem::Dependency
127
141
  name: faraday-cdn-metrics
128
142
  requirement: !ruby/object:Gem::Requirement
129
143
  requirements:
130
- - - ">="
144
+ - - "~>"
131
145
  - !ruby/object:Gem::Version
132
- version: '0'
146
+ version: '0.2'
133
147
  type: :runtime
134
148
  prerelease: false
135
149
  version_requirements: !ruby/object:Gem::Requirement
136
150
  requirements:
137
- - - ">="
151
+ - - "~>"
138
152
  - !ruby/object:Gem::Version
139
- version: '0'
153
+ version: '0.2'
140
154
  - !ruby/object:Gem::Dependency
141
155
  name: faraday-encoding
142
156
  requirement: !ruby/object:Gem::Requirement
@@ -155,30 +169,30 @@ dependencies:
155
169
  name: faraday-http-cache
156
170
  requirement: !ruby/object:Gem::Requirement
157
171
  requirements:
158
- - - ">="
172
+ - - "~>"
159
173
  - !ruby/object:Gem::Version
160
- version: '0'
174
+ version: '2.2'
161
175
  type: :runtime
162
176
  prerelease: false
163
177
  version_requirements: !ruby/object:Gem::Requirement
164
178
  requirements:
165
- - - ">="
179
+ - - "~>"
166
180
  - !ruby/object:Gem::Version
167
- version: '0'
181
+ version: '2.2'
168
182
  - !ruby/object:Gem::Dependency
169
183
  name: faraday_middleware
170
184
  requirement: !ruby/object:Gem::Requirement
171
185
  requirements:
172
- - - ">="
186
+ - - "~>"
173
187
  - !ruby/object:Gem::Version
174
- version: '0'
188
+ version: '1.0'
175
189
  type: :runtime
176
190
  prerelease: false
177
191
  version_requirements: !ruby/object:Gem::Requirement
178
192
  requirements:
179
- - - ">="
193
+ - - "~>"
180
194
  - !ruby/object:Gem::Version
181
- version: '0'
195
+ version: '1.0'
182
196
  - !ruby/object:Gem::Dependency
183
197
  name: link_header
184
198
  requirement: !ruby/object:Gem::Requirement
@@ -197,30 +211,30 @@ dependencies:
197
211
  name: rack
198
212
  requirement: !ruby/object:Gem::Requirement
199
213
  requirements:
200
- - - ">="
214
+ - - "~>"
201
215
  - !ruby/object:Gem::Version
202
- version: '0'
216
+ version: '2.2'
203
217
  type: :runtime
204
218
  prerelease: false
205
219
  version_requirements: !ruby/object:Gem::Requirement
206
220
  requirements:
207
- - - ">="
221
+ - - "~>"
208
222
  - !ruby/object:Gem::Version
209
- version: '0'
223
+ version: '2.2'
210
224
  - !ruby/object:Gem::Dependency
211
225
  name: typhoeus
212
226
  requirement: !ruby/object:Gem::Requirement
213
227
  requirements:
214
- - - ">="
228
+ - - "~>"
215
229
  - !ruby/object:Gem::Version
216
- version: '0'
230
+ version: '1.4'
217
231
  type: :runtime
218
232
  prerelease: false
219
233
  version_requirements: !ruby/object:Gem::Requirement
220
234
  requirements:
221
- - - ">="
235
+ - - "~>"
222
236
  - !ruby/object:Gem::Version
223
- version: '0'
237
+ version: '1.4'
224
238
  description: A simple activerecord inspired rest resource base class implemented using
225
239
  rest-client
226
240
  email:
@@ -237,6 +251,7 @@ files:
237
251
  - ".rubocop_todo.yml"
238
252
  - CHANGELOG.md
239
253
  - Gemfile
254
+ - Gemfile.lock
240
255
  - LICENSE.txt
241
256
  - README.md
242
257
  - Rakefile
@@ -288,7 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
303
  - !ruby/object:Gem::Version
289
304
  version: '0'
290
305
  requirements: []
291
- rubygems_version: 3.0.3
306
+ rubygems_version: 3.1.2
292
307
  signing_key:
293
308
  specification_version: 4
294
309
  summary: A simple activerecord inspired rest resource base class implemented using