marlowe 1.0.3 → 3.0
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 +5 -5
- data/.github/workflows/ruby.yml +55 -0
- data/.standard.yml +4 -0
- data/Contributing.md +71 -0
- data/History.md +43 -0
- data/Licence.md +25 -0
- data/Manifest.txt +8 -4
- data/README.rdoc +168 -50
- data/Rakefile +51 -44
- data/lib/marlowe/config.rb +116 -0
- data/lib/marlowe/faraday.rb +20 -0
- data/lib/marlowe/formatter.rb +4 -4
- data/lib/marlowe/middleware.rb +50 -20
- data/lib/marlowe/rails.rb +20 -5
- data/lib/marlowe/simple_formatter.rb +3 -4
- data/lib/marlowe.rb +40 -5
- data/test/minitest_config.rb +8 -19
- data/test/test_marlowe.rb +90 -19
- data/test/test_marlowe_config.rb +109 -0
- metadata +127 -64
- data/.autotest +0 -25
- data/Gemfile +0 -9
- data/History.rdoc +0 -17
- data/Licence.rdoc +0 -27
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configuration object for Marlowe.
|
4
|
+
class Marlowe::Config
|
5
|
+
# The name of the default header to look for and put the correlation id in.
|
6
|
+
CORRELATION_HEADER = "X-Request-Id" # :nodoc:
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# The global Marlowe configuration.
|
10
|
+
def global
|
11
|
+
@global ||= new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override the global Marlowe configuration.
|
15
|
+
def override(opts)
|
16
|
+
new(global, opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure(&block) # :nodoc:
|
20
|
+
@global = new(global, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def clear_global!
|
26
|
+
@global = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The name of the header to inspect. Defaults to 'X-Request-Id'.
|
31
|
+
attr_accessor :header
|
32
|
+
# The HTTP formatted version of the header name to inspect. Defaults to
|
33
|
+
# 'HTTP_X_REQUEST_ID'.
|
34
|
+
attr_reader :http_header
|
35
|
+
# The handler for request correlation IDs. Defaults to sanitizing provided
|
36
|
+
# request IDs or generating a UUID. If <tt>:simple</tt> is provided, provided
|
37
|
+
# request IDs will not be sanitized. A callable (expecting a single input of
|
38
|
+
# any possible existing request ID) may be provided to introduce more complex
|
39
|
+
# request ID handling.
|
40
|
+
attr_accessor :handler
|
41
|
+
# If +true+ (the default), the request correlation ID will be returned as
|
42
|
+
# part of the response headers. Only affects Marlowe::Middleware.
|
43
|
+
attr_accessor :return
|
44
|
+
# If +true+, Marlowe will add code to behave like
|
45
|
+
# <tt>ActionDispatch::RequestId</tt>. Depends on
|
46
|
+
# <tt>ActionDispatch::Request</tt>. Only affects Marlowe::Middleware.
|
47
|
+
attr_accessor :action_dispatch
|
48
|
+
|
49
|
+
# === Option Values
|
50
|
+
#
|
51
|
+
# <tt>:header</tt>:: The name of the header to inspect. Defaults to
|
52
|
+
# 'X-Request-Id'. Also available as
|
53
|
+
# <tt>:correlation_header</tt>.
|
54
|
+
# <tt>:handler</tt>:: The handler for request correlation IDs. Defaults to
|
55
|
+
# sanitizing provided request IDs or generating a UUID.
|
56
|
+
# If <tt>:simple</tt> is provided, provided request IDs
|
57
|
+
# will not be sanitized. A callable (expecting a single
|
58
|
+
# input of any possible existing request ID) may be
|
59
|
+
# provided to introduce more complex request ID
|
60
|
+
# handling.
|
61
|
+
# <tt>:return</tt>:: If +true+ (the default), the request correlation ID
|
62
|
+
# will be returned as part of the response headers.
|
63
|
+
# <tt>:action_dispatch</tt>:: If +true+, Marlowe will add code to behave
|
64
|
+
# like <tt>ActionDispatch::RequestId</tt>.
|
65
|
+
# Depends on <tt>ActionDispatch::Request</tt>.
|
66
|
+
def initialize(base = nil, opts = nil) # :yields: self
|
67
|
+
opts =
|
68
|
+
if base.nil? && opts.nil?
|
69
|
+
{}
|
70
|
+
elsif base.nil? && opts.is_a?(Hash)
|
71
|
+
opts
|
72
|
+
elsif base.is_a?(Hash) && opts.nil?
|
73
|
+
base
|
74
|
+
elsif base.is_a?(self.class) && opts.nil?
|
75
|
+
base.to_hash
|
76
|
+
elsif (base.is_a?(Hash) || base.is_a?(self.class)) && opts.is_a?(Hash)
|
77
|
+
hash =
|
78
|
+
if base.is_a?(self.class)
|
79
|
+
base.to_hash
|
80
|
+
else
|
81
|
+
base
|
82
|
+
end
|
83
|
+
hash.update(opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
@header, @http_header = format_header_name(
|
87
|
+
opts[:header] || opts[:correlation_header] || CORRELATION_HEADER
|
88
|
+
)
|
89
|
+
|
90
|
+
@handler = opts.fetch(:handler, :clean)
|
91
|
+
@return = opts.fetch(:return, true)
|
92
|
+
@action_dispatch = opts.fetch(:action_dispatch, false)
|
93
|
+
|
94
|
+
yield self if block_given?
|
95
|
+
|
96
|
+
freeze
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_hash
|
100
|
+
{
|
101
|
+
header: header,
|
102
|
+
handler: handler,
|
103
|
+
return: self.return,
|
104
|
+
action_dispatch: action_dispatch
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def format_header_name(header)
|
111
|
+
[
|
112
|
+
header.to_s.tr("_", "-").freeze,
|
113
|
+
"HTTP_#{header.to_s.tr("-", "_").upcase}"
|
114
|
+
]
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "marlowe"
|
4
|
+
|
5
|
+
# Marlowe correlation ID middleware for Faraday. Including this into your
|
6
|
+
# request middleware stack will use the captured correlation ID.
|
7
|
+
class Marlowe::Faraday < Faraday::Middleware
|
8
|
+
def initialize(app, opts = {})
|
9
|
+
super(app)
|
10
|
+
@config = Marlowe::Config.override(opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
env[:request_headers][@config.header] =
|
15
|
+
Marlowe.make_request_id(RequestStore[:correlation_id], @config)
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Faraday::Request.register_middleware marlowe: -> { Marlowe::Faraday }
|
data/lib/marlowe/formatter.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "request_store"
|
4
4
|
|
5
|
+
module Marlowe
|
5
6
|
# Marlowe::Formatter is a subclass of +ActiveSupport::Logger::Formatter+
|
6
7
|
# that adds a correlation id string to a rails log.
|
7
8
|
class Formatter < ActiveSupport::Logger::Formatter
|
8
|
-
|
9
9
|
# Overrides the formatter return to add the correlation id.
|
10
|
-
def call(
|
10
|
+
def call(_severity, _timestamp, _progname, _msg)
|
11
11
|
"[#{RequestStore.store[:correlation_id]}] #{super}"
|
12
12
|
end
|
13
13
|
end
|
data/lib/marlowe/middleware.rb
CHANGED
@@ -1,36 +1,66 @@
|
|
1
|
-
|
2
|
-
require 'request_store'
|
3
|
-
require 'securerandom'
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# request, and save that id in a request session variable.
|
9
|
-
|
10
|
-
# Name of the default header to look for and put the correlation id in.
|
11
|
-
CORRELATION_HEADER = 'Correlation-Id'.freeze
|
3
|
+
require "rack"
|
4
|
+
require "request_store"
|
5
|
+
require "securerandom"
|
12
6
|
|
7
|
+
module Marlowe
|
8
|
+
# Marlowe correlation id middleware. Including this into your middleware
|
9
|
+
# stack will capture or add a correlation id header on an incoming request,
|
10
|
+
# and save that id in a request session variable.
|
13
11
|
class Middleware
|
14
|
-
#
|
15
|
-
|
12
|
+
# The name of the default header to look for and put the correlation id in.
|
13
|
+
CORRELATION_HEADER = Marlowe::Config::CORRELATION_HEADER # :nodoc:
|
14
|
+
|
15
|
+
# Configure the Marlowe middleware to call +app+ with options +opts+.
|
16
|
+
#
|
17
|
+
# === Options
|
18
|
+
#
|
19
|
+
# <tt>:header</tt>:: The name of the header to inspect. Defaults to
|
20
|
+
# 'X-Request-Id'. Also available as
|
21
|
+
# <tt>:correlation_header</tt>.
|
22
|
+
# <tt>:handler</tt>:: The handler for request correlation IDs. Defaults to
|
23
|
+
# sanitizing provided request IDs or generating a UUID.
|
24
|
+
# If <tt>:simple</tt> is provided, provided request IDs
|
25
|
+
# will not be sanitized. A callable (expecting a single
|
26
|
+
# input of any possible existing request ID) may be
|
27
|
+
# provided to introduce more complex request ID
|
28
|
+
# handling.
|
29
|
+
# <tt>:return</tt>:: If +true+ (the default), the request correlation ID
|
30
|
+
# will be returned as part of the response headers.
|
31
|
+
# <tt>:action_dispatch</tt>:: If +true+, Marlowe will add code to behave
|
32
|
+
# like <tt>ActionDispatch::RequestId</tt>.
|
33
|
+
# Depends on <tt>ActionDispatch::Request</tt>.
|
34
|
+
def initialize(app, opts = nil)
|
16
35
|
@app = app
|
17
|
-
@
|
36
|
+
@config = Marlowe::Config.override(opts)
|
18
37
|
end
|
19
38
|
|
20
39
|
# Stores the incoming correlation id from the +env+ hash. If the correlation
|
21
40
|
# id has not been sent, a new UUID is generated and the +env+ is modified.
|
22
41
|
def call(env)
|
23
|
-
env[
|
24
|
-
RequestStore.store[:correlation_id] = env[
|
42
|
+
req_id = Marlowe.make_request_id(env[config.http_header], config)
|
43
|
+
RequestStore.store[:correlation_id] = env[config.http_header] = req_id
|
44
|
+
|
45
|
+
if config.action_dispatch
|
46
|
+
req = ActionDispatch::Request.new(env)
|
47
|
+
req.request_id = req_id
|
48
|
+
end
|
25
49
|
|
26
|
-
|
27
|
-
|
50
|
+
app.call(env).tap { |_status, headers, _body|
|
51
|
+
if config.return
|
52
|
+
headers[config.header] =
|
53
|
+
if config.action_dispatch
|
54
|
+
req.request_id
|
55
|
+
else
|
56
|
+
RequestStore.store[:correlation_id]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
}
|
28
60
|
end
|
29
61
|
|
30
62
|
private
|
31
63
|
|
32
|
-
|
33
|
-
("HTTP_" + header.gsub(/-/, '_').upcase).freeze
|
34
|
-
end
|
64
|
+
attr_reader :app, :config
|
35
65
|
end
|
36
66
|
end
|
data/lib/marlowe/rails.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Marlowe
|
2
|
-
class Railtie < Rails::Railtie
|
3
|
-
initializer
|
4
|
-
|
4
|
+
class Railtie < Rails::Railtie # :nodoc:
|
5
|
+
initializer "marlowe.configure_rails_initialization" do
|
6
|
+
config = app.config
|
7
|
+
|
8
|
+
opts = {
|
9
|
+
header: config&.marlowe_header || config&.marlowe_correlation_header,
|
10
|
+
handler: config&.marlowe_request_id_handler,
|
11
|
+
return: config&.marlowe_return_request_id,
|
12
|
+
action_dispatch: config&.marlowe_replace_action_dispatch_request_id
|
13
|
+
}.compact
|
14
|
+
|
15
|
+
if opts[:action_dispatch]
|
16
|
+
app.middleware.insert_before ActionDispatch::RequestId, Marlowe::Middleware, opts
|
17
|
+
app.middleware.delete ActionDispatch::RequestId
|
18
|
+
else
|
19
|
+
app.middleware.insert_before Rails::Rack::Logger, Marlowe::Middleware, opts
|
20
|
+
end
|
5
21
|
end
|
6
22
|
|
7
|
-
|
8
|
-
def app
|
23
|
+
def app # :nodoc:
|
9
24
|
Rails.application
|
10
25
|
end
|
11
26
|
end
|
@@ -1,16 +1,15 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "request_store"
|
2
4
|
|
3
5
|
module Marlowe
|
4
6
|
# Marlowe::SimpleFormatter is a subclass of
|
5
7
|
# +ActiveSupport::Logger::SimpleFormatter+ that adds a correlation id
|
6
8
|
# string to a rails log.
|
7
9
|
class SimpleFormatter < ActiveSupport::Logger::SimpleFormatter
|
8
|
-
|
9
10
|
# Overrides the formatter return to add the correlation id.
|
10
11
|
def call(severity, timestamp, progname, msg)
|
11
12
|
"[#{RequestStore.store[:correlation_id]}] #{super}"
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
15
|
-
|
16
|
-
|
data/lib/marlowe.rb
CHANGED
@@ -1,10 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Marlowe, a correlation id injector.
|
2
4
|
module Marlowe
|
3
|
-
VERSION =
|
5
|
+
VERSION = "3.0" # :nodoc:
|
6
|
+
|
7
|
+
require "marlowe/config"
|
8
|
+
require "marlowe/middleware"
|
9
|
+
require "marlowe/rails" if defined? Rails::Railtie
|
10
|
+
|
11
|
+
autoload :Formatter, "marlowe/formatter"
|
12
|
+
autoload :SimpleFormatter, "marlowe/simple_formatter"
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Configure Marlowe
|
16
|
+
def configure(&block)
|
17
|
+
Marlowe::Config.configure(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Make a Marlowe request ID
|
21
|
+
def make_request_id(request_id, config = Marlowe::Config.global)
|
22
|
+
if config.handler == :simple
|
23
|
+
simple(request_id)
|
24
|
+
elsif config.handler.is_a?(Proc)
|
25
|
+
simple(config.handler.call(request_id))
|
26
|
+
else
|
27
|
+
clean(request_id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
4
32
|
|
5
|
-
|
6
|
-
|
33
|
+
def clean(request_id)
|
34
|
+
simple(request_id).gsub(/[^\w\-]/, "")[0, 255]
|
35
|
+
end
|
7
36
|
|
8
|
-
|
9
|
-
|
37
|
+
def simple(request_id)
|
38
|
+
if request_id && !request_id.empty? && request_id !~ /\A[[:space]]*\z/
|
39
|
+
request_id
|
40
|
+
else
|
41
|
+
SecureRandom.uuid
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
10
45
|
end
|
data/test/minitest_config.rb
CHANGED
@@ -1,22 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem "minitest"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require 'minitest/bisect'
|
5
|
+
require "rack/test"
|
6
|
+
require "rack/mock"
|
7
|
+
require "minitest/autorun"
|
8
|
+
require "minitest/focus"
|
9
|
+
require "minitest/moar"
|
11
10
|
|
12
|
-
require
|
13
|
-
require 'marlowe'
|
14
|
-
|
15
|
-
class RackApp
|
16
|
-
def call(env)
|
17
|
-
end
|
18
|
-
|
19
|
-
def coordination_id
|
20
|
-
RequestStore[:correlation_id]
|
21
|
-
end
|
22
|
-
end
|
11
|
+
require "marlowe"
|
data/test/test_marlowe.rb
CHANGED
@@ -1,32 +1,103 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest_config"
|
2
4
|
|
3
5
|
class TestMarlowe < Minitest::Test
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
attr_reader :marlowe_options
|
9
|
+
|
4
10
|
def setup
|
5
|
-
@
|
6
|
-
|
11
|
+
@marlowe_options = {}
|
12
|
+
Marlowe::Config.send(:clear_global!)
|
13
|
+
end
|
14
|
+
|
15
|
+
def app
|
16
|
+
options = marlowe_options
|
17
|
+
Rack::Builder.new do
|
18
|
+
use Marlowe::Middleware, options
|
19
|
+
|
20
|
+
run lambda { |_env|
|
21
|
+
[
|
22
|
+
200,
|
23
|
+
{"Content-Type" => "text/plain"},
|
24
|
+
[RequestStore[:correlation_id]]
|
25
|
+
]
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_default_config_no_header_value
|
31
|
+
get "/"
|
32
|
+
assert last_response.header.key?("X-Request-Id")
|
33
|
+
refute_empty last_response.header["X-Request-Id"]
|
34
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_default_config_with_header_value
|
38
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "testvalue"}
|
39
|
+
assert last_response.header.key?("X-Request-Id")
|
40
|
+
refute_empty last_response.header["X-Request-Id"]
|
41
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
42
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_header_config_no_header_value
|
46
|
+
marlowe_options[:header] = "Correlation-Id"
|
47
|
+
get "/"
|
48
|
+
assert last_response.header.key?("Correlation-Id")
|
49
|
+
refute_empty last_response.header["Correlation-Id"]
|
50
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_header_config_no_header_with_header_value
|
54
|
+
marlowe_options[:header] = "Correlation-Id"
|
55
|
+
get "/", {}, {"HTTP_CORRELATION_ID" => "testvalue"}
|
56
|
+
assert last_response.header.key?("Correlation-Id")
|
57
|
+
refute_empty last_response.header["Correlation-Id"]
|
58
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
59
|
+
assert_equal "testvalue", last_response.header["Correlation-Id"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_handler_config_default_handler
|
63
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
64
|
+
assert last_response.header.key?("X-Request-Id")
|
65
|
+
refute_empty last_response.header["X-Request-Id"]
|
66
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
67
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
7
68
|
end
|
8
69
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
70
|
+
def test_handler_config_with_simple_handler
|
71
|
+
marlowe_options[:handler] = :simple
|
72
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
73
|
+
assert last_response.header.key?("X-Request-Id")
|
74
|
+
refute_empty last_response.header["X-Request-Id"]
|
75
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
76
|
+
assert_equal "test+value", last_response.header["X-Request-Id"]
|
12
77
|
end
|
13
78
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
79
|
+
def test_handler_config_with_proc_handler
|
80
|
+
marlowe_options[:handler] = ->(item) { item && item.reverse || SecureRandom.uuid }
|
81
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
82
|
+
assert last_response.header.key?("X-Request-Id")
|
83
|
+
refute_empty last_response.header["X-Request-Id"]
|
84
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
85
|
+
assert_equal "eulav+tset", last_response.header["X-Request-Id"]
|
18
86
|
end
|
19
87
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
88
|
+
def test_handler_config_with_proc_handler_returning_nil
|
89
|
+
marlowe_options[:handler] = ->(_item) {}
|
90
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
91
|
+
assert last_response.header.key?("X-Request-Id")
|
92
|
+
refute_empty last_response.header["X-Request-Id"]
|
93
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
94
|
+
assert_match(/\A[-\w]+\z/, last_response.header["X-Request-Id"])
|
24
95
|
end
|
25
96
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
assert_equal
|
97
|
+
def test_return_config_false
|
98
|
+
marlowe_options[:return] = false
|
99
|
+
get "/"
|
100
|
+
refute last_response.header.key?("X-Request-Id")
|
101
|
+
assert_equal RequestStore[:correlation_id], last_response.body
|
31
102
|
end
|
32
103
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest_config"
|
4
|
+
|
5
|
+
class TestMarloweConfig < Minitest::Test
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
attr_reader :marlowe_options
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@marlowe_options = {}
|
12
|
+
Marlowe::Config.send(:clear_global!)
|
13
|
+
end
|
14
|
+
|
15
|
+
def app
|
16
|
+
Marlowe.configure do |config|
|
17
|
+
marlowe_options.each do |k, v|
|
18
|
+
config.send(:"#{k}=", v) if config.respond_to?(:"#{k}=")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
options = marlowe_options
|
23
|
+
Rack::Builder.new do
|
24
|
+
use Marlowe::Middleware, options
|
25
|
+
|
26
|
+
run lambda { |_env|
|
27
|
+
[
|
28
|
+
200,
|
29
|
+
{"Content-Type" => "text/plain"},
|
30
|
+
[RequestStore[:correlation_id]]
|
31
|
+
]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_default_config_no_header_value
|
37
|
+
get "/"
|
38
|
+
assert last_response.header.key?("X-Request-Id")
|
39
|
+
refute_empty last_response.header["X-Request-Id"]
|
40
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_default_config_with_header_value
|
44
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "testvalue"}
|
45
|
+
assert last_response.header.key?("X-Request-Id")
|
46
|
+
refute_empty last_response.header["X-Request-Id"]
|
47
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
48
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_header_config_no_header_value
|
52
|
+
marlowe_options[:header] = "Correlation-Id"
|
53
|
+
get "/"
|
54
|
+
assert last_response.header.key?("Correlation-Id")
|
55
|
+
refute_empty last_response.header["Correlation-Id"]
|
56
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_header_config_no_header_with_header_value
|
60
|
+
marlowe_options[:header] = "Correlation-Id"
|
61
|
+
get "/", {}, {"HTTP_CORRELATION_ID" => "testvalue"}
|
62
|
+
assert last_response.header.key?("Correlation-Id")
|
63
|
+
refute_empty last_response.header["Correlation-Id"]
|
64
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
65
|
+
assert_equal "testvalue", last_response.header["Correlation-Id"]
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_handler_config_default_handler
|
69
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
70
|
+
assert last_response.header.key?("X-Request-Id")
|
71
|
+
refute_empty last_response.header["X-Request-Id"]
|
72
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
73
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_handler_config_with_simple_handler
|
77
|
+
marlowe_options[:handler] = :simple
|
78
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
79
|
+
assert last_response.header.key?("X-Request-Id")
|
80
|
+
refute_empty last_response.header["X-Request-Id"]
|
81
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
82
|
+
assert_equal "test+value", last_response.header["X-Request-Id"]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_handler_config_with_proc_handler
|
86
|
+
marlowe_options[:handler] = ->(item) { item && item.reverse || SecureRandom.uuid }
|
87
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
88
|
+
assert last_response.header.key?("X-Request-Id")
|
89
|
+
refute_empty last_response.header["X-Request-Id"]
|
90
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
91
|
+
assert_equal "eulav+tset", last_response.header["X-Request-Id"]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_handler_config_with_proc_handler_returning_nil
|
95
|
+
marlowe_options[:handler] = ->(_item) {}
|
96
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
97
|
+
assert last_response.header.key?("X-Request-Id")
|
98
|
+
refute_empty last_response.header["X-Request-Id"]
|
99
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
100
|
+
assert_match(/\A[-\w]+\z/, last_response.header["X-Request-Id"])
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_return_config_false
|
104
|
+
marlowe_options[:return] = false
|
105
|
+
get "/"
|
106
|
+
refute last_response.header.key?("X-Request-Id")
|
107
|
+
assert_equal RequestStore[:correlation_id], last_response.body
|
108
|
+
end
|
109
|
+
end
|