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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +9 -12
- data/lib/restify/adapter/em.rb +6 -8
- data/lib/restify/adapter/pooled_em.rb +36 -40
- data/lib/restify/adapter/typhoeus.rb +74 -52
- data/lib/restify/context.rb +4 -4
- data/lib/restify/error.rb +24 -0
- data/lib/restify/global.rb +2 -2
- data/lib/restify/processors/base.rb +2 -6
- data/lib/restify/processors/base/parsing.rb +2 -6
- data/lib/restify/promise.rb +1 -3
- data/lib/restify/request.rb +13 -5
- data/lib/restify/resource.rb +1 -1
- data/lib/restify/timeout.rb +1 -3
- data/lib/restify/version.rb +2 -2
- data/spec/restify/context_spec.rb +2 -2
- data/spec/restify/error_spec.rb +0 -1
- data/spec/restify/features/head_requests_spec.rb +5 -6
- data/spec/restify/features/request_bodies_spec.rb +83 -0
- data/spec/restify/features/request_errors_spec.rb +19 -0
- data/spec/restify/features/request_headers_spec.rb +6 -7
- data/spec/restify/features/response_errors_spec.rb +127 -0
- data/spec/restify/global_spec.rb +2 -2
- data/spec/restify_spec.rb +50 -57
- data/spec/spec_helper.rb +10 -7
- data/spec/support/stub_server.rb +106 -0
- metadata +12 -6
- data/spec/restify/features/response_errors.rb +0 -79
data/spec/restify/global_spec.rb
CHANGED
@@ -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
|
29
|
+
subject { global.new(name, **options) }
|
30
30
|
|
31
31
|
it 'returns relation for stored registry item' do
|
32
|
-
Restify::Registry.store
|
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://
|
9
|
-
|
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
|
-
|
13
|
-
Link: <http://localhost/base/
|
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
|
-
|
20
|
+
HTTP
|
22
21
|
end
|
23
22
|
|
24
|
-
stub_request(:get, 'http://
|
25
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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://
|
42
|
+
stub_request(:post, 'http://stubserver/base/users')
|
44
43
|
.with(body: {})
|
45
44
|
.to_return do
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Transfer-Encoding: chunked
|
45
|
+
<<~HTTP
|
46
|
+
HTTP/1.1 422 Unprocessable Entity
|
47
|
+
Content-Type: application/json
|
50
48
|
|
51
|
-
|
52
|
-
|
49
|
+
{"errors":{"name":["can't be blank"]}}
|
50
|
+
HTTP
|
53
51
|
end
|
54
52
|
|
55
|
-
stub_request(:post, 'http://
|
53
|
+
stub_request(:post, 'http://stubserver/base/users')
|
56
54
|
.with(body: {name: 'John Smith'})
|
57
55
|
.to_return do
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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://
|
70
|
+
stub_request(:get, 'http://stubserver/base/users/john.smith')
|
74
71
|
.to_return do
|
75
|
-
|
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
|
-
|
81
|
+
HTTP
|
86
82
|
end
|
87
83
|
|
88
|
-
stub_request(:get, 'http://
|
84
|
+
stub_request(:get, 'http://stubserver/base/users/john.smith/blurb')
|
89
85
|
.to_return do
|
90
|
-
|
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
|
-
|
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 '
|
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
|
-
|
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 =
|
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.
|
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:
|
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/
|
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:
|
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.
|
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/
|
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
|