restify 1.12.0 → 1.15.1

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.
@@ -26,10 +26,10 @@ describe Restify::Global do
26
26
  let(:options) { {accept: 'application.vnd.github.v3+json'} }
27
27
  let(:context) { Restify::Context.new uri, **options }
28
28
 
29
- subject { global.new name, options }
29
+ subject { global.new(name, **options) }
30
30
 
31
31
  it 'returns relation for stored registry item' do
32
- Restify::Registry.store name, uri, options
32
+ Restify::Registry.store(name, uri, **options)
33
33
 
34
34
  expect(subject).to be_a Restify::Relation
35
35
  expect(subject.pattern).to eq uri
data/spec/restify_spec.rb CHANGED
@@ -5,99 +5,94 @@ require 'spec_helper'
5
5
  describe Restify do
6
6
  context 'as a dynamic HATEOAS client' do
7
7
  before do
8
- stub_request(:get, 'http://localhost/base').to_return do
9
- <<-RESPONSE.gsub(/^ {10}/, '')
8
+ stub_request(:get, 'http://stubserver/base').to_return do
9
+ <<~HTTP
10
10
  HTTP/1.1 200 OK
11
11
  Content-Type: application/json
12
- Transfer-Encoding: chunked
13
- Link: <http://localhost/base/users{/id}>; rel="users"
14
- Link: <http://localhost/base/courses{/id}>; rel="courses"
12
+ Link: <http://localhost:9292/base/users{/id}>; rel="users"
13
+ Link: <http://localhost:9292/base/courses{/id}>; rel="courses"
15
14
 
16
15
  {
17
- "profile_url": "http://localhost/base/profile",
18
- "search_url": "http://localhost/base/search?q={query}",
16
+ "profile_url": "http://localhost:9292/base/profile",
17
+ "search_url": "http://localhost:9292/base/search?q={query}",
19
18
  "mirror_url": null
20
19
  }
21
- RESPONSE
20
+ HTTP
22
21
  end
23
22
 
24
- stub_request(:get, 'http://localhost/base/users').to_return do
25
- <<-RESPONSE.gsub(/^ {10}/, '')
23
+ stub_request(:get, 'http://stubserver/base/users')
24
+ .to_return do
25
+ <<~HTTP
26
26
  HTTP/1.1 200 OK
27
27
  Content-Type: application/json
28
- Transfer-Encoding: chunked
29
28
 
30
29
  [{
31
- "name": "John Smith",
32
- "url": "http://localhost/base/users/john.smith",
33
- "blurb_url": "http://localhost/base/users/john.smith/blurb",
34
- "languages": ["de", "en"]
35
- },
36
- {
37
- "name": "Jane Smith",
38
- "self_url": "http://localhost/base/user/jane.smith"
39
- }]
40
- RESPONSE
30
+ "name": "John Smith",
31
+ "url": "http://localhost:9292/base/users/john.smith",
32
+ "blurb_url": "http://localhost:9292/base/users/john.smith/blurb",
33
+ "languages": ["de", "en"]
34
+ },
35
+ {
36
+ "name": "Jane Smith",
37
+ "self_url": "http://localhost:9292/base/user/jane.smith"
38
+ }]
39
+ HTTP
41
40
  end
42
41
 
43
- stub_request(:post, 'http://localhost/base/users')
42
+ stub_request(:post, 'http://stubserver/base/users')
44
43
  .with(body: {})
45
44
  .to_return do
46
- <<-RESPONSE.gsub(/^ {12}/, '')
47
- HTTP/1.1 422 Unprocessable Entity
48
- Content-Type: application/json
49
- Transfer-Encoding: chunked
45
+ <<~HTTP
46
+ HTTP/1.1 422 Unprocessable Entity
47
+ Content-Type: application/json
50
48
 
51
- {"errors":{"name":["can't be blank"]}}
52
- RESPONSE
49
+ {"errors":{"name":["can't be blank"]}}
50
+ HTTP
53
51
  end
54
52
 
55
- stub_request(:post, 'http://localhost/base/users')
53
+ stub_request(:post, 'http://stubserver/base/users')
56
54
  .with(body: {name: 'John Smith'})
57
55
  .to_return do
58
- <<-RESPONSE.gsub(/^ {12}/, '')
59
- HTTP/1.1 201 Created
60
- Content-Type: application/json
61
- Location: http://localhost/base/users/john.smith
62
- Transfer-Encoding: chunked
63
-
64
- {
65
- "name": "John Smith",
66
- "url": "http://localhost/base/users/john.smith",
67
- "blurb_url": "http://localhost/base/users/john.smith/blurb",
68
- "languages": ["de", "en"]
69
- }
70
- RESPONSE
56
+ <<~HTTP
57
+ HTTP/1.1 201 Created
58
+ Content-Type: application/json
59
+ Location: http://localhost:9292/base/users/john.smith
60
+
61
+ {
62
+ "name": "John Smith",
63
+ "url": "http://localhost:9292/base/users/john.smith",
64
+ "blurb_url": "http://localhost:9292/base/users/john.smith/blurb",
65
+ "languages": ["de", "en"]
66
+ }
67
+ HTTP
71
68
  end
72
69
 
73
- stub_request(:get, 'http://localhost/base/users/john.smith')
70
+ stub_request(:get, 'http://stubserver/base/users/john.smith')
74
71
  .to_return do
75
- <<-RESPONSE.gsub(/^ {10}/, '')
72
+ <<~HTTP
76
73
  HTTP/1.1 200 OK
77
74
  Content-Type: application/json
78
- Link: <http://localhost/base/users/john.smith>; rel="self"
79
- Transfer-Encoding: chunked
75
+ Link: <http://localhost:9292/base/users/john.smith>; rel="self"
80
76
 
81
77
  {
82
78
  "name": "John Smith",
83
- "url": "http://localhost/base/users/john.smith"
79
+ "url": "http://localhost:9292/base/users/john.smith"
84
80
  }
85
- RESPONSE
81
+ HTTP
86
82
  end
87
83
 
88
- stub_request(:get, 'http://localhost/base/users/john.smith/blurb')
84
+ stub_request(:get, 'http://stubserver/base/users/john.smith/blurb')
89
85
  .to_return do
90
- <<-RESPONSE.gsub(/^ {10}/, '')
86
+ <<~HTTP
91
87
  HTTP/1.1 200 OK
92
88
  Content-Type: application/json
93
- Link: <http://localhost/base/users/john.smith>; rel="user"
94
- Transfer-Encoding: chunked
89
+ Link: <http://localhost:9292/base/users/john.smith>; rel="user"
95
90
 
96
91
  {
97
92
  "title": "Prof. Dr. John Smith",
98
93
  "image": "http://example.org/avatar.png"
99
94
  }
100
- RESPONSE
95
+ HTTP
101
96
  end
102
97
  end
103
98
 
@@ -107,7 +102,7 @@ describe Restify do
107
102
 
108
103
  # First request the entry resource usually the
109
104
  # root using GET and wait for it.
110
- root = Restify.new('http://localhost/base').get.value!
105
+ root = Restify.new('http://localhost:9292/base').get.value!
111
106
 
112
107
  # Therefore we need the `users` relations of our root
113
108
  # resource.
@@ -134,7 +129,6 @@ describe Restify do
134
129
  # the result is here.
135
130
  expect { create_user_promise.value! }.to \
136
131
  raise_error(Restify::ClientError) do |e|
137
-
138
132
  # Because we forgot to send a "name" the server complains
139
133
  # with an error code that will lead to a raised error.
140
134
 
@@ -195,7 +189,7 @@ describe Restify do
195
189
  skip 'Seems to be impossible to detect EM scheduled fibers from within'
196
190
 
197
191
  EM.synchrony do
198
- root = Restify.new('http://localhost/base').get.value!
192
+ root = Restify.new('http://localhost:9292/base').get.value!
199
193
 
200
194
  users_relation = root.rel(:users)
201
195
 
@@ -206,7 +200,6 @@ describe Restify do
206
200
 
207
201
  expect { create_user_promise.value! }.to \
208
202
  raise_error(Restify::ClientError) do |e|
209
-
210
203
  expect(e.status).to eq :unprocessable_entity
211
204
  expect(e.code).to eq 422
212
205
  expect(e.errors).to eq 'name' => ["can't be blank"]
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rspec'
4
- require 'webmock/rspec'
4
+ require 'rspec/collection_matchers'
5
5
 
6
6
  require 'simplecov'
7
7
  SimpleCov.start do
@@ -34,17 +34,20 @@ if ENV['ADAPTER']
34
34
  end
35
35
  end
36
36
 
37
- require 'webmock/rspec'
38
- require 'rspec/collection_matchers'
39
- require 'em-synchrony'
40
-
41
- Dir[File.expand_path('spec/support/**/*.rb')].each {|f| require f }
37
+ require_relative 'support/stub_server'
42
38
 
43
39
  RSpec.configure do |config|
44
40
  config.order = 'random'
45
41
 
46
42
  config.before(:suite) do
47
- ::Restify::Timeout.default_timeout = 2
43
+ ::Restify::Timeout.default_timeout = 1.0
44
+ end
45
+
46
+ config.before(:each) do |example|
47
+ next unless (adapter = example.metadata[:adapter])
48
+ next if Restify.adapter.is_a?(adapter)
49
+
50
+ skip 'Spec not enabled for current adapter'
48
51
  end
49
52
 
50
53
  config.before(:each) do
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma'
4
+ require 'rack'
5
+ require 'webmock'
6
+ require 'webmock/rspec/matchers'
7
+
8
+ module Stub
9
+ # This Rack application matches the request received from rack against the
10
+ # webmock stub database and returns the response.
11
+ #
12
+ # A custom server name is used to
13
+ # 1) has a stable name without a dynamic port for easier `#stub_request`
14
+ # calls, and
15
+ # 2) to ensure no actual request is intercepted (they are send to
16
+ # `localhost:<port>`).
17
+ #
18
+ # If no stub is found a special HTTP 599 error code will be returned.
19
+ class Handler
20
+ def call(env) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
21
+ signature = WebMock::RequestSignature.new(
22
+ env['REQUEST_METHOD'].downcase,
23
+ "http://stubserver#{env['REQUEST_URI']}"
24
+ )
25
+
26
+ # Extract request headers from rack env. Most header should start with
27
+ # `HTTP_` but at least content type is present as `CONTENT_TYPE`.
28
+ headers = {}
29
+ env.each_pair do |key, value|
30
+ case key
31
+ when /^HTTP_(.*)$/, /^(CONTENT_.*)$/
32
+ headers[Regexp.last_match(1)] = value
33
+ end
34
+ end
35
+
36
+ # Read request body from socket into string
37
+ signature.body = env['rack.input'].read
38
+ signature.headers = headers
39
+
40
+ WebMock::RequestRegistry.instance.requested_signatures.put(signature)
41
+ response = ::WebMock::StubRegistry.instance.response_for_request(signature)
42
+
43
+ # Return special HTTP 599 with the error message that would normally
44
+ # appear on missing stubs.
45
+ unless response
46
+ return [599, {}, [WebMock::NetConnectNotAllowedError.new(signature).message]]
47
+ end
48
+
49
+ if response.should_timeout
50
+ sleep 10
51
+ return [599, {}, ['Timeout']]
52
+ end
53
+
54
+ status = response.status
55
+ status = status.to_s.split(' ', 2) unless status.is_a?(Array)
56
+ status = Integer(status[0])
57
+
58
+ [status, response.headers || {}, [response.body.to_s]]
59
+ end
60
+ end
61
+
62
+ class Exception < ::StandardError; end
63
+
64
+ # Inject into base adapter to have HTTP 599 (missing stub) error raised as an
65
+ # extra exception, not just a server error.
66
+ module Patch
67
+ def call(request)
68
+ super.then do |response|
69
+ next response unless response.code == 599
70
+
71
+ raise ::Stub::Exception.new(response.body)
72
+ end
73
+ end
74
+
75
+ ::Restify::Adapter::Base.prepend(self)
76
+ end
77
+
78
+ class << self
79
+ def start_server!
80
+ @server = ::Puma::Server.new(Handler.new)
81
+ @server.add_tcp_listener('localhost', 9292)
82
+
83
+ Thread.new do
84
+ @server.run
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ RSpec.configure do |config|
91
+ config.include WebMock::API
92
+ config.include WebMock::Matchers
93
+
94
+ config.before(:suite) do
95
+ Stub.start_server!
96
+
97
+ # Net::HTTP adapter must be enabled, otherwise webmock fails to create mock
98
+ # responses from raw strings.
99
+ WebMock.disable!(except: %i[net_http])
100
+ end
101
+
102
+ config.around(:each) do |example|
103
+ example.run
104
+ WebMock.reset!
105
+ end
106
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restify
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-01 00:00:00.000000000 Z
11
+ date: 2021-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -194,8 +194,10 @@ files:
194
194
  - spec/restify/context_spec.rb
195
195
  - spec/restify/error_spec.rb
196
196
  - spec/restify/features/head_requests_spec.rb
197
+ - spec/restify/features/request_bodies_spec.rb
198
+ - spec/restify/features/request_errors_spec.rb
197
199
  - spec/restify/features/request_headers_spec.rb
198
- - spec/restify/features/response_errors.rb
200
+ - spec/restify/features/response_errors_spec.rb
199
201
  - spec/restify/global_spec.rb
200
202
  - spec/restify/link_spec.rb
201
203
  - spec/restify/processors/base_spec.rb
@@ -208,6 +210,7 @@ files:
208
210
  - spec/restify/timeout_spec.rb
209
211
  - spec/restify_spec.rb
210
212
  - spec/spec_helper.rb
213
+ - spec/support/stub_server.rb
211
214
  homepage: https://github.com/jgraichen/restify
212
215
  licenses:
213
216
  - LGPL-3.0+
@@ -220,14 +223,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
223
  requirements:
221
224
  - - ">="
222
225
  - !ruby/object:Gem::Version
223
- version: '0'
226
+ version: 2.5.0
224
227
  required_rubygems_version: !ruby/object:Gem::Requirement
225
228
  requirements:
226
229
  - - ">="
227
230
  - !ruby/object:Gem::Version
228
231
  version: '0'
229
232
  requirements: []
230
- rubygems_version: 3.1.2
233
+ rubygems_version: 3.1.6
231
234
  signing_key:
232
235
  specification_version: 4
233
236
  summary: An experimental hypermedia REST client.
@@ -236,8 +239,10 @@ test_files:
236
239
  - spec/restify/context_spec.rb
237
240
  - spec/restify/error_spec.rb
238
241
  - spec/restify/features/head_requests_spec.rb
242
+ - spec/restify/features/request_bodies_spec.rb
243
+ - spec/restify/features/request_errors_spec.rb
239
244
  - spec/restify/features/request_headers_spec.rb
240
- - spec/restify/features/response_errors.rb
245
+ - spec/restify/features/response_errors_spec.rb
241
246
  - spec/restify/global_spec.rb
242
247
  - spec/restify/link_spec.rb
243
248
  - spec/restify/processors/base_spec.rb
@@ -250,3 +255,4 @@ test_files:
250
255
  - spec/restify/timeout_spec.rb
251
256
  - spec/restify_spec.rb
252
257
  - spec/spec_helper.rb
258
+ - spec/support/stub_server.rb
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Restify do
6
- let!(:request_stub) do
7
- stub_request(:get, 'http://localhost/base')
8
- .to_return do
9
- <<-RESPONSE.gsub(/^ {8}/, '')
10
- HTTP/1.1 #{http_status}
11
- Content-Length: 333
12
- Transfer-Encoding: chunked
13
- Link: <http://localhost/other>; rel="neat"
14
- RESPONSE
15
- end
16
- end
17
-
18
- let(:http_status) { '200 OK' }
19
-
20
- describe 'Error handling' do
21
- subject(:request) { Restify.new('http://localhost/base').get.value! }
22
-
23
- context 'for 400 status codes' do
24
- let(:http_status) { '400 Bad Request' }
25
-
26
- it 'throws a BadRequest exception' do
27
- expect { request }.to raise_error Restify::BadRequest
28
- end
29
- end
30
-
31
- context 'for 401 status codes' do
32
- let(:http_status) { '401 Unauthorized' }
33
-
34
- it 'throws an Unauthorized exception' do
35
- expect { request }.to raise_error Restify::Unauthorized
36
- end
37
- end
38
-
39
- context 'for 404 status codes' do
40
- let(:http_status) { '404 Not Found' }
41
-
42
- it 'throws a ClientError exception' do
43
- expect { request }.to raise_error Restify::NotFoundError
44
- end
45
- end
46
-
47
- context 'for 406 status codes' do
48
- let(:http_status) { '406 Not Acceptable' }
49
-
50
- it 'throws a NotAcceptable exception' do
51
- expect { request }.to raise_error Restify::NotAcceptable
52
- end
53
- end
54
-
55
- context 'for 422 status codes' do
56
- let(:http_status) { '422 Unprocessable Entity' }
57
-
58
- it 'throws a UnprocessableEntity exception' do
59
- expect { request }.to raise_error Restify::UnprocessableEntity
60
- end
61
- end
62
-
63
- context 'for any other 4xx status codes' do
64
- let(:http_status) { '415 Unsupported Media Type' }
65
-
66
- it 'throws a generic ClientError exception' do
67
- expect { request }.to raise_error Restify::ClientError
68
- end
69
- end
70
-
71
- context 'for any 5xx status codes' do
72
- let(:http_status) { '500 Internal Server Error' }
73
-
74
- it 'throws a generic ServerError exception' do
75
- expect { request }.to raise_error Restify::ServerError
76
- end
77
- end
78
- end
79
- end