rester 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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