rester 0.5.1 → 0.5.2
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/lib/rester/client/adapters/adapter.rb +3 -3
- data/lib/rester/client/adapters/http_adapter.rb +6 -1
- data/lib/rester/client/adapters/local_adapter.rb +15 -5
- data/lib/rester/client/adapters/stub_adapter.rb +13 -1
- data/lib/rester/client/middleware/base.rb +15 -0
- data/lib/rester/client/middleware/request_handler.rb +14 -0
- data/lib/rester/client/middleware.rb +8 -0
- data/lib/rester/client.rb +71 -23
- data/lib/rester/errors.rb +6 -0
- data/lib/rester/rspec.rb +17 -12
- data/lib/rester/{middleware → service/middleware}/base.rb +2 -2
- data/lib/rester/{middleware → service/middleware}/error_handling.rb +3 -2
- data/lib/rester/{middleware → service/middleware}/new_relic.rb +1 -1
- data/lib/rester/{middleware → service/middleware}/ping.rb +3 -3
- data/lib/rester/service/middleware/request_handler.rb +30 -0
- data/lib/rester/service/middleware.rb +11 -0
- data/lib/rester/service/request.rb +7 -0
- data/lib/rester/service/resource/params.rb +3 -3
- data/lib/rester/service/resource.rb +1 -1
- data/lib/rester/service.rb +25 -4
- data/lib/rester/utils/logger_wrapper.rb +41 -0
- data/lib/rester/utils/stub_file.rb +1 -1
- data/lib/rester/utils.rb +1 -0
- data/lib/rester/version.rb +1 -1
- data/lib/rester.rb +78 -1
- metadata +27 -8
- data/lib/rester/middleware.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bb523b9b23530a59e8bc2a8fc8463324739b59d
|
4
|
+
data.tar.gz: 26d4a6f4cd0de0417b470357488c0ed941e8f8a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9456f4f73aa5e8516e3731a273954a254735de21025dcdac36e70209e922f1ce4ee335b865a32efb4c01e6225ddfbd4161c3d89cc85096bd02521b09c6c5b3eb
|
7
|
+
data.tar.gz: 1ad4848eb57cd9770a6c9185c4b9f136111083a258ec9bff59addd160801e6e2865c02623ddbae1b4fb6bebe5152080b3566389cf5d3497d2ec1d51406936713
|
@@ -11,7 +11,7 @@ module Rester
|
|
11
11
|
|
12
12
|
attr_reader :timeout
|
13
13
|
|
14
|
-
def initialize(service=nil, opts={})
|
14
|
+
def initialize(service = nil, opts = {})
|
15
15
|
@timeout = opts[:timeout]
|
16
16
|
connect(service) if service
|
17
17
|
end
|
@@ -19,7 +19,7 @@ module Rester
|
|
19
19
|
##
|
20
20
|
# Returns the headers defined for this Adapter. Optionally, you may also
|
21
21
|
# define additional headers you'd like to add/override.
|
22
|
-
def headers(new_headers={})
|
22
|
+
def headers(new_headers = {})
|
23
23
|
(@headers ||= {}).merge!(new_headers)
|
24
24
|
end
|
25
25
|
|
@@ -37,7 +37,7 @@ module Rester
|
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
40
|
-
# Sends
|
40
|
+
# Sends a request (using one of the subclass adapters) to the service.
|
41
41
|
#
|
42
42
|
# `params` should be a hash if specified.
|
43
43
|
def request(verb, path, params = nil)
|
@@ -38,7 +38,12 @@ module Rester
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def _prepare_response(response)
|
41
|
-
|
41
|
+
# We want to format the header keys in the way we would expect it in our
|
42
|
+
# client: X-Rester-Header-Key.
|
43
|
+
#
|
44
|
+
# http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTPHeader.html
|
45
|
+
headers = {}.tap { |h| response.each_capitalized { |k,v| h[k] = v } }
|
46
|
+
[response.code.to_i, headers, response.body]
|
42
47
|
end
|
43
48
|
|
44
49
|
def _require_connection
|
@@ -37,11 +37,13 @@ module Rester
|
|
37
37
|
|
38
38
|
response = Timeout::timeout(timeout) do
|
39
39
|
service.call(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
_headers_to_http_format(opts[:headers]).merge(
|
41
|
+
'REQUEST_METHOD' => verb.to_s.upcase,
|
42
|
+
'PATH_INFO' => path,
|
43
|
+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
44
|
+
'QUERY_STRING' => query,
|
45
|
+
'rack.input' => StringIO.new(body)
|
46
|
+
)
|
45
47
|
)
|
46
48
|
end
|
47
49
|
|
@@ -52,11 +54,19 @@ module Rester
|
|
52
54
|
|
53
55
|
[
|
54
56
|
response.first, # The status code
|
57
|
+
{
|
58
|
+
'X-Rester-Producer-Name' => service.service_name,
|
59
|
+
}, # The header
|
55
60
|
body # The response body.
|
56
61
|
]
|
57
62
|
rescue Timeout::Error
|
58
63
|
fail Errors::TimeoutError
|
59
64
|
end
|
65
|
+
|
66
|
+
|
67
|
+
def _headers_to_http_format(headers={})
|
68
|
+
Hash[headers.map { |k,v| ["HTTP_#{k.to_s.upcase.gsub('-', '_')}", v] }]
|
69
|
+
end
|
60
70
|
end # LocalAdapter
|
61
71
|
end # Client::Adapters
|
62
72
|
end # Rester
|
@@ -30,7 +30,15 @@ module Rester
|
|
30
30
|
!!stub
|
31
31
|
end
|
32
32
|
|
33
|
+
def producer
|
34
|
+
stub['producer']
|
35
|
+
end
|
36
|
+
|
33
37
|
def request!(verb, path, encoded_data)
|
38
|
+
if verb == :get && path == '/ping'
|
39
|
+
return [200, { 'X-Rester-Producer-Name' => producer }, '']
|
40
|
+
end
|
41
|
+
|
34
42
|
params = Rack::Utils.parse_nested_query(encoded_data)
|
35
43
|
_request(verb.to_s.upcase, path, params)
|
36
44
|
end
|
@@ -45,7 +53,11 @@ module Rester
|
|
45
53
|
|
46
54
|
def _request(verb, path, params)
|
47
55
|
spec = _process_request(path, verb, params)
|
48
|
-
[
|
56
|
+
[
|
57
|
+
spec['response_code'],
|
58
|
+
{ 'X-Rester-Producer-Name' => producer },
|
59
|
+
spec['response'].to_json
|
60
|
+
]
|
49
61
|
end
|
50
62
|
|
51
63
|
def _process_request(path, verb, params)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rester
|
2
|
+
module Client::Middleware
|
3
|
+
class RequestHandler < Base
|
4
|
+
def call(env)
|
5
|
+
Rester.begin_request
|
6
|
+
Rester.correlation_id = SecureRandom.uuid
|
7
|
+
Rester.request_info[:consumer_name] = Rester.service_name
|
8
|
+
super
|
9
|
+
ensure
|
10
|
+
Rester.end_request
|
11
|
+
end
|
12
|
+
end # RequestHandlers
|
13
|
+
end # Client::Middleware
|
14
|
+
end # Rester
|
data/lib/rester/client.rb
CHANGED
@@ -4,9 +4,10 @@ require 'logger'
|
|
4
4
|
|
5
5
|
module Rester
|
6
6
|
class Client
|
7
|
-
autoload(:Adapters,
|
8
|
-
autoload(:Resource,
|
9
|
-
autoload(:Response,
|
7
|
+
autoload(:Adapters, 'rester/client/adapters')
|
8
|
+
autoload(:Resource, 'rester/client/resource')
|
9
|
+
autoload(:Response, 'rester/client/response')
|
10
|
+
autoload(:Middleware, 'rester/client/middleware')
|
10
11
|
|
11
12
|
attr_reader :adapter
|
12
13
|
attr_reader :version
|
@@ -19,22 +20,45 @@ module Rester
|
|
19
20
|
self.version = params[:version]
|
20
21
|
@error_threshold = (params[:error_threshold] || 3).to_i
|
21
22
|
@retry_period = (params[:retry_period] || 1).to_f
|
22
|
-
|
23
|
+
self.logger = params[:logger]
|
24
|
+
@_breaker_enabled = params.fetch(:circuit_breaker_enabled,
|
25
|
+
ENV['RACK_ENV'] != 'test' && ENV['RAILS_ENV'] != 'test'
|
26
|
+
)
|
23
27
|
|
24
28
|
@_resource = Resource.new(self)
|
25
|
-
|
26
|
-
end
|
29
|
+
_init_requester
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
# Send a test ping request to the service so we can store the producer's
|
32
|
+
# name for future request logs
|
33
|
+
fail Errors::ConnectionError unless connected?
|
30
34
|
end
|
31
35
|
|
32
36
|
def connected?
|
33
|
-
adapter.connected? &&
|
37
|
+
adapter.connected? && @_requester.call(:get, '/ping', {}).successful?
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def circuit_breaker_enabled?
|
43
|
+
!!@_breaker_enabled
|
44
|
+
end
|
45
|
+
|
46
|
+
def logger=(logger)
|
47
|
+
logger = Utils::LoggerWrapper.new(logger) if logger
|
48
|
+
@logger = logger
|
49
|
+
end
|
50
|
+
|
51
|
+
def logger
|
52
|
+
@logger || Rester.logger
|
53
|
+
end
|
54
|
+
|
55
|
+
def name
|
56
|
+
@_producer_name
|
34
57
|
end
|
35
58
|
|
36
59
|
def request(verb, path, params={})
|
37
|
-
|
60
|
+
path = _path_with_version(path)
|
61
|
+
@_requester.call(verb, path, params)
|
38
62
|
rescue Utils::CircuitBreaker::CircuitOpenError
|
39
63
|
# Translate this error so it's easier handle for clients.
|
40
64
|
# Also, at some point we may want to extract CircuitBreaker into its own
|
@@ -77,31 +101,55 @@ module Rester
|
|
77
101
|
#
|
78
102
|
# When the circuit is opened or closed, a message is sent to the logger for
|
79
103
|
# the client.
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
104
|
+
def _init_requester
|
105
|
+
if circuit_breaker_enabled?
|
106
|
+
@_requester = Utils::CircuitBreaker.new(
|
107
|
+
threshold: error_threshold, retry_period: retry_period
|
108
|
+
) { |*args| _request(*args) }
|
109
|
+
|
110
|
+
@_requester.on_open do
|
111
|
+
logger.error("circuit opened for #{name}")
|
112
|
+
end
|
88
113
|
|
89
|
-
|
90
|
-
|
114
|
+
@_requester.on_close do
|
115
|
+
logger.info("circuit closed for #{name}")
|
116
|
+
end
|
117
|
+
else
|
118
|
+
@_requester = proc { |*args| _request(*args) }
|
91
119
|
end
|
92
120
|
end
|
93
121
|
|
122
|
+
##
|
123
|
+
# Add a correlation ID to the header and send the request to the adapter
|
94
124
|
def _request(verb, path, params)
|
95
|
-
|
96
|
-
|
125
|
+
Rester.request_info[:producer_name] = name
|
126
|
+
Rester.request_info[:path] = path
|
127
|
+
Rester.request_info[:verb] = verb
|
128
|
+
logger.info('sending request')
|
129
|
+
|
130
|
+
_set_default_headers
|
131
|
+
start_time = Time.now.to_f
|
132
|
+
response = adapter.request(verb, path, params)
|
133
|
+
_process_response(start_time, verb, path, *response)
|
134
|
+
end
|
135
|
+
|
136
|
+
def _set_default_headers
|
137
|
+
adapter.headers(
|
138
|
+
'X-Rester-Correlation-ID' => Rester.correlation_id,
|
139
|
+
'X-Rester-Consumer-Name' => Rester.service_name,
|
140
|
+
'X-Rester-Producer-Name' => name
|
141
|
+
)
|
97
142
|
end
|
98
143
|
|
99
144
|
def _path_with_version(path)
|
100
145
|
Utils.join_paths("/v#{version}", path)
|
101
146
|
end
|
102
147
|
|
103
|
-
def _process_response(path, status, body)
|
148
|
+
def _process_response(start_time, verb, path, status, headers, body)
|
149
|
+
elapsed_ms = (Time.now.to_f - start_time) * 1000
|
104
150
|
response = Response.new(status, _parse_json(body))
|
151
|
+
@_producer_name = headers['X-Rester-Producer-Name']
|
152
|
+
logger.info("received status #{status} after %0.3fms" % elapsed_ms)
|
105
153
|
|
106
154
|
unless [200, 201, 400].include?(status)
|
107
155
|
case status
|
data/lib/rester/errors.rb
CHANGED
@@ -23,11 +23,17 @@ module Rester
|
|
23
23
|
# Client Errors
|
24
24
|
class ClientError < Error; end
|
25
25
|
class CircuitOpenError < ClientError; end
|
26
|
+
class ConnectionError < ClientError; end
|
26
27
|
|
27
28
|
#############
|
28
29
|
# Stub Errors
|
29
30
|
class StubError < Error; end
|
30
31
|
|
32
|
+
#############
|
33
|
+
# RSpec Errors
|
34
|
+
class RSpecError < Error; end
|
35
|
+
class TestError < RSpecError; end
|
36
|
+
|
31
37
|
#############
|
32
38
|
# Http Errors
|
33
39
|
class HttpError < Error; end
|
data/lib/rester/rspec.rb
CHANGED
@@ -17,7 +17,7 @@ RSpec::Matchers.define :include_stub_response do |stub|
|
|
17
17
|
end
|
18
18
|
}
|
19
19
|
|
20
|
-
failure_message {
|
20
|
+
failure_message { failure }
|
21
21
|
end
|
22
22
|
|
23
23
|
RSpec.configure do |config|
|
@@ -31,7 +31,7 @@ RSpec.configure do |config|
|
|
31
31
|
raise "invalid service to test"
|
32
32
|
end
|
33
33
|
|
34
|
-
@rester_adapter = Rester::Client::Adapters::LocalAdapter.new(klass
|
34
|
+
@rester_adapter = Rester::Client::Adapters::LocalAdapter.new(klass)
|
35
35
|
|
36
36
|
_validate_test_coverage(ex)
|
37
37
|
end
|
@@ -40,7 +40,7 @@ RSpec.configure do |config|
|
|
40
40
|
_setup_example(ex) unless ex.pending?
|
41
41
|
end
|
42
42
|
|
43
|
-
config.after :each, rester: // do
|
43
|
+
config.after :each, rester: // do
|
44
44
|
if defined?(service_response_code)
|
45
45
|
expect(service_response_code).to eq stub_response_code
|
46
46
|
end
|
@@ -67,10 +67,9 @@ RSpec.configure do |config|
|
|
67
67
|
|
68
68
|
begin
|
69
69
|
spec = @rester_stub[path][verb][context]
|
70
|
+
_stub_path_not_found(path, verb, context) unless spec
|
70
71
|
rescue NoMethodError
|
71
|
-
|
72
|
-
"Could not find path: #{path.inspect} verb: #{verb.inspect} context: "\
|
73
|
-
"#{context.inspect} in #{@rester_stub_filepath}"
|
72
|
+
_stub_path_not_found(path, verb, context)
|
74
73
|
end
|
75
74
|
|
76
75
|
##
|
@@ -106,6 +105,12 @@ RSpec.configure do |config|
|
|
106
105
|
ex.example_group.let(:subject) { service_response }
|
107
106
|
end
|
108
107
|
|
108
|
+
def _stub_path_not_found(path, verb, context)
|
109
|
+
fail Rester::Errors::TestError,
|
110
|
+
"Could not find path: #{path.inspect} verb: #{verb.inspect} context: "\
|
111
|
+
"#{context.inspect} in #{@rester_stub_filepath}"
|
112
|
+
end
|
113
|
+
|
109
114
|
##
|
110
115
|
# Check to see if each stub example has a corresponding test written for it
|
111
116
|
def _validate_test_coverage(ex)
|
@@ -164,24 +169,24 @@ RSpec.configure do |config|
|
|
164
169
|
# }
|
165
170
|
# }
|
166
171
|
def _missing_stub_tests(tests)
|
167
|
-
@rester_stub.reject { |k, _|
|
172
|
+
Hash[@rester_stub.reject { |k, _|
|
168
173
|
['version', 'consumer', 'producer'].include?(k)
|
169
174
|
}.map { |path, verbs|
|
170
175
|
[
|
171
176
|
path,
|
172
|
-
verbs.map { |verb, contexts|
|
177
|
+
Hash[verbs.map { |verb, contexts|
|
173
178
|
[
|
174
179
|
verb,
|
175
|
-
contexts.map { |context, _|
|
180
|
+
Hash[contexts.map { |context, _|
|
176
181
|
[
|
177
182
|
context,
|
178
183
|
!!(tests[path] && tests[path][verb] && tests[path][verb][context])
|
179
184
|
]
|
180
|
-
}.
|
185
|
+
}].reject { |_, v| v }
|
181
186
|
]
|
182
|
-
}.
|
187
|
+
}].reject { |_, v| v.empty? }
|
183
188
|
]
|
184
|
-
}.
|
189
|
+
}].reject { |_, v| v.empty? }
|
185
190
|
end
|
186
191
|
|
187
192
|
def _find_child_with_description(group, description)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Rester
|
2
|
-
module Middleware
|
2
|
+
module Service::Middleware
|
3
3
|
class Base
|
4
4
|
attr_reader :app
|
5
5
|
attr_reader :options
|
@@ -40,5 +40,5 @@ module Rester
|
|
40
40
|
Errors.throw_error!(klass, message)
|
41
41
|
end
|
42
42
|
end # Base
|
43
|
-
end # Middleware
|
43
|
+
end # Service::Middleware
|
44
44
|
end # Rester
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
3
|
module Rester
|
4
|
-
module Middleware
|
4
|
+
module Service::Middleware
|
5
5
|
##
|
6
6
|
# Provides error handling for Rester. Should be mounted above all other
|
7
7
|
# Rester middleware.
|
@@ -15,6 +15,7 @@ module Rester
|
|
15
15
|
end
|
16
16
|
}
|
17
17
|
|
18
|
+
service.logger.error(error.inspect)
|
18
19
|
_error_to_response(error).finish
|
19
20
|
end
|
20
21
|
|
@@ -68,5 +69,5 @@ module Rester
|
|
68
69
|
end
|
69
70
|
end
|
70
71
|
end # ErrorHandling
|
71
|
-
end # Middleware
|
72
|
+
end # Service::Middleware
|
72
73
|
end # Rester
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module Rester
|
2
|
-
module Middleware
|
2
|
+
module Service::Middleware
|
3
3
|
##
|
4
4
|
# Provides a basic status check. Used by the Client#connected? method.
|
5
5
|
class Ping < Base
|
6
6
|
def call(env)
|
7
|
-
if %r{\A/ping\z}.match(
|
7
|
+
if %r{\A/ping\z}.match(Rester.request.path_info)
|
8
8
|
[200, {}, []]
|
9
9
|
else
|
10
10
|
super
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end # Ping
|
14
|
-
end # Middleware
|
14
|
+
end # Service::Middleware
|
15
15
|
end # Rester
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rester
|
2
|
+
module Service::Middleware
|
3
|
+
##
|
4
|
+
# Create a Request object for this thread, store the correlation ID, and
|
5
|
+
# perform the necessary logging. Cleanup the request once it's complete.
|
6
|
+
class RequestHandler < Base
|
7
|
+
def call(env)
|
8
|
+
Rester.begin_request
|
9
|
+
Rester.request = request = Rester::Service::Request.new(env)
|
10
|
+
Rester.correlation_id = request.correlation_id
|
11
|
+
Rester.request_info[:producer_name] = service.name
|
12
|
+
Rester.request_info[:consumer_name] = request.consumer_name
|
13
|
+
Rester.request_info[:path] = request.path_info
|
14
|
+
Rester.request_info[:verb] = request.request_method
|
15
|
+
|
16
|
+
service.logger.info('request received')
|
17
|
+
|
18
|
+
start_time = Time.now.to_f
|
19
|
+
super.tap { |response|
|
20
|
+
elapsed_ms = (Time.now.to_f - start_time) * 1000
|
21
|
+
response[1]["X-Rester-Producer-Name"] = service.name
|
22
|
+
service.logger.info("responding with #{response[0]} after %0.3fms" %
|
23
|
+
elapsed_ms)
|
24
|
+
}
|
25
|
+
ensure
|
26
|
+
Rester.end_request
|
27
|
+
end
|
28
|
+
end # RequestHandler
|
29
|
+
end # Service::Middleware
|
30
|
+
end # Rester
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Rester
|
2
|
+
class Service
|
3
|
+
module Middleware
|
4
|
+
autoload(:Base, 'rester/service/middleware/base')
|
5
|
+
autoload(:ErrorHandling, 'rester/service/middleware/error_handling')
|
6
|
+
autoload(:Ping, 'rester/service/middleware/ping')
|
7
|
+
autoload(:NewRelic, 'rester/service/middleware/new_relic')
|
8
|
+
autoload(:RequestHandler, 'rester/service/middleware/request_handler')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -6,6 +6,9 @@ module Rester
|
|
6
6
|
attr_reader :version
|
7
7
|
attr_reader :object_chain
|
8
8
|
|
9
|
+
CUSTOM_FIELDS = ['correlation_id', 'producer_name',
|
10
|
+
'consumer_name'].freeze
|
11
|
+
|
9
12
|
def initialize(env)
|
10
13
|
super
|
11
14
|
_parse_path if valid?
|
@@ -25,6 +28,10 @@ module Rester
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
CUSTOM_FIELDS.each { |field|
|
32
|
+
define_method(field) { env["HTTP_X_RESTER_#{field.upcase}"] }
|
33
|
+
}
|
34
|
+
|
28
35
|
private
|
29
36
|
|
30
37
|
def _parse_path
|
@@ -51,9 +51,9 @@ module Rester
|
|
51
51
|
_error!("unexpected params: #{unexpected.join(', ')}")
|
52
52
|
end
|
53
53
|
|
54
|
-
validated_params =
|
55
|
-
[key.to_sym, validate!(key.to_sym, value)]
|
56
|
-
|
54
|
+
validated_params = Hash[
|
55
|
+
params.map { |key, value| [key.to_sym, validate!(key.to_sym, value)] }
|
56
|
+
]
|
57
57
|
|
58
58
|
@_defaults.merge(validated_params)
|
59
59
|
end
|
@@ -82,7 +82,7 @@ module Rester
|
|
82
82
|
# determining which instance method to call. For example, if the request
|
83
83
|
# method is GET: the ID being specified will call the `get` method and if
|
84
84
|
# it's not specified then it will call the `search` method.
|
85
|
-
def process(request_method, id_provided, params={})
|
85
|
+
def process(request_method, id_provided, params = {})
|
86
86
|
meth = (id_provided ? REQUEST_METHOD_TO_IDENTIFIED_METHOD
|
87
87
|
: REQUEST_METHOD_TO_UNIDENTIFIED_METHOD)[request_method]
|
88
88
|
|
data/lib/rester/service.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'rack'
|
3
3
|
require 'active_support/inflector'
|
4
|
+
require 'logger'
|
4
5
|
|
5
6
|
module Rester
|
6
7
|
class Service
|
7
|
-
autoload(:Request,
|
8
|
-
autoload(:Resource,
|
8
|
+
autoload(:Request, 'rester/service/request')
|
9
|
+
autoload(:Resource, 'rester/service/resource')
|
10
|
+
autoload(:Middleware, 'rester/service/middleware')
|
11
|
+
|
12
|
+
attr_reader :logger
|
9
13
|
|
10
14
|
##
|
11
15
|
# The base set of middleware to use for every service.
|
12
16
|
# Middleware will be executed in the order specified.
|
13
17
|
BASE_MIDDLEWARE = [
|
14
18
|
Rack::Head,
|
19
|
+
Middleware::RequestHandler,
|
15
20
|
Middleware::ErrorHandling,
|
16
21
|
Middleware::Ping
|
17
22
|
].freeze
|
@@ -57,6 +62,10 @@ module Rester
|
|
57
62
|
}.map(&:downcase).map(&:to_sym)
|
58
63
|
end
|
59
64
|
|
65
|
+
def service_name
|
66
|
+
@__name ||= name.split('::').last
|
67
|
+
end
|
68
|
+
|
60
69
|
def version_module(version)
|
61
70
|
(@__version_modules ||= {})[version.to_sym] ||= _load_version_module(version)
|
62
71
|
end
|
@@ -81,6 +90,19 @@ module Rester
|
|
81
90
|
end
|
82
91
|
end # Class methods
|
83
92
|
|
93
|
+
def logger
|
94
|
+
@_logger || Rester.logger
|
95
|
+
end
|
96
|
+
|
97
|
+
def logger=(new_logger)
|
98
|
+
new_logger = Utils::LoggerWrapper.new(new_logger) if new_logger
|
99
|
+
@_logger = new_logger
|
100
|
+
end
|
101
|
+
|
102
|
+
def name
|
103
|
+
self.class.service_name
|
104
|
+
end
|
105
|
+
|
84
106
|
##
|
85
107
|
# To be called by Rack. Wraps the app in middleware.
|
86
108
|
def rack_call(env)
|
@@ -102,7 +124,7 @@ module Rester
|
|
102
124
|
# Calls methods that may modify instance variables, so the instance should
|
103
125
|
# be dup'd beforehand.
|
104
126
|
def call!(env)
|
105
|
-
_process_request(
|
127
|
+
_process_request(Rester.request)
|
106
128
|
end
|
107
129
|
|
108
130
|
private
|
@@ -142,7 +164,6 @@ module Rester
|
|
142
164
|
# request.
|
143
165
|
def _call_method(request)
|
144
166
|
params = request.params
|
145
|
-
retval = nil
|
146
167
|
resource_obj = nil
|
147
168
|
resource_id = nil
|
148
169
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Rester
|
2
|
+
module Utils
|
3
|
+
class LoggerWrapper
|
4
|
+
attr_reader :logger
|
5
|
+
|
6
|
+
def initialize(logger = Logger.new(STDOUT))
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
(Logger::SEV_LABEL - ['ANY']).map(&:downcase).map(&:to_sym).each do |sev|
|
11
|
+
define_method(sev) { |msg| _log(sev, msg) if logger }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def method_missing(meth, *args, &block)
|
17
|
+
logger.public_send(meth, *args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(*args)
|
21
|
+
logger.respond_to?(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _log(level, msg)
|
25
|
+
if Rester.processing_request?
|
26
|
+
producer_name = Rester.request_info[:producer_name]
|
27
|
+
consumer_name = Rester.request_info[:consumer_name]
|
28
|
+
path = Rester.request_info[:path]
|
29
|
+
verb = Rester.request_info[:verb]
|
30
|
+
verb = verb && verb.upcase
|
31
|
+
|
32
|
+
msg = "Correlation-ID=#{Rester.correlation_id} Consumer=" \
|
33
|
+
"#{consumer_name} Producer=#{producer_name} #{verb} " \
|
34
|
+
"#{path} - #{msg}"
|
35
|
+
end
|
36
|
+
|
37
|
+
logger.public_send(level, msg)
|
38
|
+
end
|
39
|
+
end # LoggerWrapper
|
40
|
+
end # Utils
|
41
|
+
end # Rester
|
@@ -92,7 +92,7 @@ module Rester
|
|
92
92
|
# Takes a response key (e.g., "response[successful=false]") and parses out
|
93
93
|
# the tags (e.g., {"successful" => false})
|
94
94
|
def _parse_tags(path, verb, context, resp_key)
|
95
|
-
DEFAULT_TAGS.merge(resp_key.scan(/(\w+) *= *(\w+)/)
|
95
|
+
DEFAULT_TAGS.merge(Hash[resp_key.scan(/(\w+) *= *(\w+)/)]).tap { |tags|
|
96
96
|
_validate_tags(path, verb, context, tags)
|
97
97
|
}
|
98
98
|
end
|
data/lib/rester/utils.rb
CHANGED
data/lib/rester/version.rb
CHANGED
data/lib/rester.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'rester/version'
|
2
2
|
require 'rack'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'active_support/core_ext/class/subclasses'
|
3
5
|
|
4
6
|
module Rester
|
5
7
|
require 'rester/railtie' if defined?(Rails)
|
@@ -9,17 +11,92 @@ module Rester
|
|
9
11
|
autoload(:Utils, 'rester/utils')
|
10
12
|
autoload(:Middleware, 'rester/middleware')
|
11
13
|
|
14
|
+
@_request_infos ||= ThreadSafe::Cache.new
|
15
|
+
|
16
|
+
# Set up the Client middleware if it's a Rails application
|
17
|
+
if defined?(Rails) && Rails.application
|
18
|
+
Rails.configuration.middleware.use(Client::Middleware::RequestHandler)
|
19
|
+
end
|
20
|
+
|
12
21
|
class << self
|
22
|
+
def logger
|
23
|
+
@_logger ||= Utils::LoggerWrapper.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def logger=(new_logger)
|
27
|
+
@_logger = Utils::LoggerWrapper.new(new_logger)
|
28
|
+
end
|
29
|
+
|
13
30
|
def load_tasks
|
14
31
|
Dir[
|
15
32
|
File.expand_path("../../tasks", __FILE__) + '/**.rake'
|
16
33
|
].each { |rake_file| load rake_file }
|
17
34
|
end
|
18
35
|
|
19
|
-
def connect(service, params={})
|
36
|
+
def connect(service, params = {})
|
20
37
|
adapter_opts = Client::Adapters.extract_opts(params)
|
21
38
|
adapter = Client::Adapters.connect(service, adapter_opts)
|
22
39
|
Client.new(adapter, params)
|
23
40
|
end
|
41
|
+
|
42
|
+
def service_name
|
43
|
+
@_service_name ||= _get_service_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def service_name=(name)
|
47
|
+
@_service_name = name
|
48
|
+
end
|
49
|
+
|
50
|
+
def begin_request
|
51
|
+
self.request_info = {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def end_request
|
55
|
+
self.request_info = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def processing_request?
|
59
|
+
!!request_info
|
60
|
+
end
|
61
|
+
|
62
|
+
def request_info
|
63
|
+
@_request_infos[Thread.current.object_id]
|
64
|
+
end
|
65
|
+
|
66
|
+
def request_info=(info)
|
67
|
+
if info.nil?
|
68
|
+
@_request_infos.delete(Thread.current.object_id)
|
69
|
+
else
|
70
|
+
@_request_infos[Thread.current.object_id] = info
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def request
|
75
|
+
request_info[:request]
|
76
|
+
end
|
77
|
+
|
78
|
+
def request=(request)
|
79
|
+
request_info[:request] = request
|
80
|
+
end
|
81
|
+
|
82
|
+
def correlation_id
|
83
|
+
request_info && request_info[:correlation_id]
|
84
|
+
end
|
85
|
+
|
86
|
+
def correlation_id=(correlation_id)
|
87
|
+
request_info[:correlation_id] = correlation_id
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def _get_service_name
|
93
|
+
if defined?(Rails) && Rails.application
|
94
|
+
Rails.application.class.parent_name
|
95
|
+
else
|
96
|
+
services = Service.descendants
|
97
|
+
fail "Define a service name" if services.empty?
|
98
|
+
services.first.service_name
|
99
|
+
end
|
100
|
+
end
|
24
101
|
end # Class Methods
|
25
102
|
end # Rester
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Honer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-02-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -45,6 +45,20 @@ dependencies:
|
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: thread_safe
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
48
62
|
- !ruby/object:Gem::Dependency
|
49
63
|
name: rspec
|
50
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,22 +159,27 @@ files:
|
|
145
159
|
- lib/rester/client/adapters/http_adapter/connection.rb
|
146
160
|
- lib/rester/client/adapters/local_adapter.rb
|
147
161
|
- lib/rester/client/adapters/stub_adapter.rb
|
162
|
+
- lib/rester/client/middleware.rb
|
163
|
+
- lib/rester/client/middleware/base.rb
|
164
|
+
- lib/rester/client/middleware/request_handler.rb
|
148
165
|
- lib/rester/client/resource.rb
|
149
166
|
- lib/rester/client/response.rb
|
150
167
|
- lib/rester/errors.rb
|
151
|
-
- lib/rester/middleware.rb
|
152
|
-
- lib/rester/middleware/base.rb
|
153
|
-
- lib/rester/middleware/error_handling.rb
|
154
|
-
- lib/rester/middleware/new_relic.rb
|
155
|
-
- lib/rester/middleware/ping.rb
|
156
168
|
- lib/rester/railtie.rb
|
157
169
|
- lib/rester/rspec.rb
|
158
170
|
- lib/rester/service.rb
|
171
|
+
- lib/rester/service/middleware.rb
|
172
|
+
- lib/rester/service/middleware/base.rb
|
173
|
+
- lib/rester/service/middleware/error_handling.rb
|
174
|
+
- lib/rester/service/middleware/new_relic.rb
|
175
|
+
- lib/rester/service/middleware/ping.rb
|
176
|
+
- lib/rester/service/middleware/request_handler.rb
|
159
177
|
- lib/rester/service/request.rb
|
160
178
|
- lib/rester/service/resource.rb
|
161
179
|
- lib/rester/service/resource/params.rb
|
162
180
|
- lib/rester/utils.rb
|
163
181
|
- lib/rester/utils/circuit_breaker.rb
|
182
|
+
- lib/rester/utils/logger_wrapper.rb
|
164
183
|
- lib/rester/utils/rspec.rb
|
165
184
|
- lib/rester/utils/stub_file.rb
|
166
185
|
- lib/rester/version.rb
|
@@ -184,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
203
|
version: '0'
|
185
204
|
requirements: []
|
186
205
|
rubyforge_project:
|
187
|
-
rubygems_version: 2.4.
|
206
|
+
rubygems_version: 2.4.8
|
188
207
|
signing_key:
|
189
208
|
specification_version: 4
|
190
209
|
summary: A framework for creating simple RESTful interfaces between services.
|
data/lib/rester/middleware.rb
DELETED