eventhub-processor2 1.26.2 → 1.27.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +8 -14
- data/eventhub-processor2.gemspec +1 -1
- data/example/config/example.json +15 -0
- data/example/config/receiver.json +4 -1
- data/example/config/router.json +4 -1
- data/example/example.rb +4 -0
- data/example/publisher.rb +5 -8
- data/example/receiver.rb +4 -0
- data/example/router.rb +4 -0
- data/lib/eventhub/actor_heartbeat.rb +1 -7
- data/lib/eventhub/actor_listener_http.rb +5 -4
- data/lib/eventhub/actor_publisher.rb +1 -7
- data/lib/eventhub/assets/app.css +79 -1
- data/lib/eventhub/configuration.rb +1 -5
- data/lib/eventhub/docs_renderer.rb +140 -23
- data/lib/eventhub/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71103595a699bfa32e3c771073f3b870a5e10bba02a9191585e5bd2369a4fb8b
|
|
4
|
+
data.tar.gz: 82045e01a8f8c136ba5413c6a1153093fd441db66704352d58034f371602dbba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99f35f36aacf9e25df8c7ecafc8c26dfd41f4dac7e3d486ae904aa8555b6af7b2c60688aab5b68c1cc1285117212c9b7b6e10ef1ce6b38489860bdd21094188e
|
|
7
|
+
data.tar.gz: a6011ab9c3345016e4f3b3c35d00685ea64635683f61ff38e7d6a555a8b1edd1993ce8e6be38460be45251d1e371b0810277a9df78d93891d155239aefe5f040
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog of EventHub::Processor2
|
|
2
2
|
|
|
3
|
+
# 1.27.0 / 2026-04-01
|
|
4
|
+
|
|
5
|
+
* Adapt to Bunny 3.0 publisher confirms: use `confirm_select(tracking: true)` for automatic backpressure, remove manual `wait_for_confirms` calls
|
|
6
|
+
* Remove `:configuration` from default HTTP resources to prevent automatic exposure of sensitive config on upgrade (opt-in via `http_resources` method)
|
|
7
|
+
* Improve configuration page: compact rendering for nested hashes and arrays, show `(not set)` for empty values, add client-side filter with reset button, visual distinction for top-level sections
|
|
8
|
+
* Fix deprecation logic to only warn when `heartbeat.*` and `http.*` values actually differ; remove defaults from deprecated `heartbeat` config block
|
|
9
|
+
* Fix example processors: add unique HTTP ports, create missing `data/` directory in publisher
|
|
10
|
+
* Fix flaky sleeper specs by widening timing tolerance and using idiomatic RSpec `be_within` matcher
|
|
11
|
+
|
|
3
12
|
# 1.26.2 / 2026-03-25
|
|
4
13
|
|
|
5
14
|
* Change default `http.bind_address` from `localhost` to `0.0.0.0` for container compatibility (ECS, Docker, K8s)
|
data/README.md
CHANGED
|
@@ -321,7 +321,7 @@ Resources are mounted under the `base_path`:
|
|
|
321
321
|
- `{base_path}/heartbeat` - Health check
|
|
322
322
|
- `{base_path}/version` - Version info as JSON
|
|
323
323
|
- `{base_path}/docs` - README documentation as HTML
|
|
324
|
-
- `{base_path}/docs/configuration` - Configuration as HTML table
|
|
324
|
+
- `{base_path}/docs/configuration` - Configuration as HTML table (opt-in, see [Enabling Resources](#enabling-resources))
|
|
325
325
|
- `{base_path}/docs/changelog` - CHANGELOG as HTML
|
|
326
326
|
- `{base_path}/assets/*` - Static assets (CSS, images)
|
|
327
327
|
|
|
@@ -469,7 +469,7 @@ GET {base_path}/docs/configuration
|
|
|
469
469
|
|
|
470
470
|
**Response:** `200 OK` with HTML page
|
|
471
471
|
|
|
472
|
-
By default, the following keys are redacted: `password`, `secret`, `token`, `api_key`, `credential`. You can customize the list by defining a `sensitive_keys` method in your processor:
|
|
472
|
+
By default, the following keys are redacted: `password`, `secret`, `token`, `api_key`, `credential`, `username`, `user`, `login`. You can customize the list by defining a `sensitive_keys` method in your processor:
|
|
473
473
|
|
|
474
474
|
```ruby
|
|
475
475
|
# Override the entire list
|
|
@@ -497,28 +497,22 @@ class MyProcessor < EventHub::Processor2
|
|
|
497
497
|
end
|
|
498
498
|
```
|
|
499
499
|
|
|
500
|
-
###
|
|
500
|
+
### Enabling Resources
|
|
501
501
|
|
|
502
|
-
By default,
|
|
502
|
+
By default, the following HTTP resources are enabled: `:heartbeat`, `:version`, `:docs`, and `:changelog`. The `:configuration` resource is **disabled by default** because it displays server configuration which may contain sensitive values. Although passwords, tokens, and keys are automatically redacted, we prefer a secure-by-default approach where processors must explicitly opt in to exposing configuration.
|
|
503
503
|
|
|
504
|
-
|
|
505
|
-
class MyProcessor < EventHub::Processor2
|
|
506
|
-
def http_resources
|
|
507
|
-
[:heartbeat, :version, :docs, :changelog, :configuration] # default: all enabled
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
To disable the configuration page for example:
|
|
504
|
+
To enable the configuration page, define an `http_resources` method in your processor:
|
|
513
505
|
|
|
514
506
|
```ruby
|
|
515
507
|
class MyProcessor < EventHub::Processor2
|
|
516
508
|
def http_resources
|
|
517
|
-
[:heartbeat, :version, :docs, :changelog]
|
|
509
|
+
[:heartbeat, :version, :docs, :changelog, :configuration]
|
|
518
510
|
end
|
|
519
511
|
end
|
|
520
512
|
```
|
|
521
513
|
|
|
514
|
+
You can also use `http_resources` to disable any of the default resources. The navbar adapts automatically.
|
|
515
|
+
|
|
522
516
|
### Customizing Footer
|
|
523
517
|
|
|
524
518
|
The documentation pages display company name, version, and environment in the footer. Company name defaults to "Novartis" but can be customized by defining a `company_name` method in your processor:
|
data/eventhub-processor2.gemspec
CHANGED
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
|
|
25
25
|
spec.add_dependency "celluloid", "~> 0.18"
|
|
26
26
|
spec.add_dependency "webrick", "~> 1.8"
|
|
27
|
-
spec.add_dependency "bunny", "~>
|
|
27
|
+
spec.add_dependency "bunny", "~> 3.0"
|
|
28
28
|
spec.add_dependency "eventhub-components", "~> 0.4"
|
|
29
29
|
spec.add_dependency "base64", "~> 0.3.0"
|
|
30
30
|
spec.add_dependency "logger", "~> 1.6"
|
data/example/config/router.json
CHANGED
data/example/example.rb
CHANGED
|
@@ -6,6 +6,10 @@ module EventHub
|
|
|
6
6
|
"1.0.0" # define your version
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def http_resources
|
|
10
|
+
[:heartbeat, :version, :docs, :changelog, :configuration]
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
def handle_message(message, args = {})
|
|
10
14
|
# deal with your parsed EventHub message
|
|
11
15
|
# message.class => EventHub::Message
|
data/example/publisher.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "bunny"
|
|
2
2
|
require "celluloid"
|
|
3
|
+
require "fileutils"
|
|
3
4
|
require "json"
|
|
4
5
|
require "securerandom"
|
|
5
6
|
require "eventhub/components"
|
|
@@ -38,6 +39,7 @@ module Publisher
|
|
|
38
39
|
@files_sent = 0
|
|
39
40
|
|
|
40
41
|
@filename = "data/store.json"
|
|
42
|
+
FileUtils.mkdir_p(File.dirname(@filename))
|
|
41
43
|
if File.exist?(@filename)
|
|
42
44
|
cleanup
|
|
43
45
|
else
|
|
@@ -119,7 +121,7 @@ module Publisher
|
|
|
119
121
|
logger: Logger.new(File::NULL))
|
|
120
122
|
@connection.start
|
|
121
123
|
@channel = @connection.create_channel
|
|
122
|
-
@channel.confirm_select
|
|
124
|
+
@channel.confirm_select(tracking: true)
|
|
123
125
|
@exchange = @channel.direct("example.outbound", durable: true)
|
|
124
126
|
end
|
|
125
127
|
|
|
@@ -135,13 +137,8 @@ module Publisher
|
|
|
135
137
|
Publisher.logger.info("[#{id}] - Message/File created")
|
|
136
138
|
|
|
137
139
|
@exchange.publish(data, persistent: true)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
Celluloid::Actor[:transaction_store]&.stop(id)
|
|
141
|
-
Publisher&.logger&.info("[#{id}] - Message sent")
|
|
142
|
-
else
|
|
143
|
-
Publisher&.logger&.error("[#{id}] - Published message not confirmed")
|
|
144
|
-
end
|
|
140
|
+
Celluloid::Actor[:transaction_store]&.stop(id)
|
|
141
|
+
Publisher&.logger&.info("[#{id}] - Message sent")
|
|
145
142
|
end
|
|
146
143
|
end
|
|
147
144
|
|
data/example/receiver.rb
CHANGED
data/example/router.rb
CHANGED
|
@@ -38,15 +38,9 @@ module EventHub
|
|
|
38
38
|
connection = create_bunny_connection
|
|
39
39
|
connection.start
|
|
40
40
|
channel = connection.create_channel
|
|
41
|
-
channel.confirm_select
|
|
41
|
+
channel.confirm_select(tracking: true)
|
|
42
42
|
exchange = channel.direct(EventHub::EH_X_INBOUND, durable: true)
|
|
43
43
|
exchange.publish(message, persistent: true)
|
|
44
|
-
success = channel.wait_for_confirms
|
|
45
|
-
|
|
46
|
-
unless success
|
|
47
|
-
raise "Published heartbeat message has " \
|
|
48
|
-
"not been confirmed by the server"
|
|
49
|
-
end
|
|
50
44
|
ensure
|
|
51
45
|
connection&.close
|
|
52
46
|
end
|
|
@@ -10,7 +10,7 @@ module EventHub
|
|
|
10
10
|
include Helper
|
|
11
11
|
|
|
12
12
|
DEFAULT_VERSION = "?.?.?"
|
|
13
|
-
DEFAULT_HTTP_RESOURCES = [:heartbeat, :version, :docs, :changelog
|
|
13
|
+
DEFAULT_HTTP_RESOURCES = [:heartbeat, :version, :docs, :changelog].freeze
|
|
14
14
|
CONTENT_TYPES = {
|
|
15
15
|
".css" => "text/css",
|
|
16
16
|
".svg" => "image/svg+xml",
|
|
@@ -183,16 +183,17 @@ module EventHub
|
|
|
183
183
|
private
|
|
184
184
|
|
|
185
185
|
def http_config(key)
|
|
186
|
-
#
|
|
186
|
+
# Prefer deprecated heartbeat config for backward compatibility,
|
|
187
|
+
# fall back to http config. Only warn when values actually differ.
|
|
187
188
|
heartbeat_value = EventHub::Configuration.server.dig(:heartbeat, key)
|
|
188
189
|
http_value = EventHub::Configuration.server.dig(:http, key)
|
|
189
190
|
|
|
190
|
-
if heartbeat_value && http_value !=
|
|
191
|
+
if heartbeat_value && http_value && heartbeat_value != http_value
|
|
191
192
|
EventHub.logger.warn("[DEPRECATION] heartbeat.#{key} is deprecated. Please use http.#{key} instead.")
|
|
192
193
|
return heartbeat_value
|
|
193
194
|
end
|
|
194
195
|
|
|
195
|
-
http_value
|
|
196
|
+
http_value || heartbeat_value
|
|
196
197
|
end
|
|
197
198
|
|
|
198
199
|
def resource_enabled?(name)
|
|
@@ -25,7 +25,7 @@ module EventHub
|
|
|
25
25
|
exchange_name = args[:exchange_name] || EH_X_INBOUND
|
|
26
26
|
|
|
27
27
|
channel = @connection.create_channel
|
|
28
|
-
channel.confirm_select
|
|
28
|
+
channel.confirm_select(tracking: true)
|
|
29
29
|
exchange = channel.direct(exchange_name, durable: true)
|
|
30
30
|
|
|
31
31
|
publish_options = {persistent: true}
|
|
@@ -33,12 +33,6 @@ module EventHub
|
|
|
33
33
|
publish_options[:correlation_id] = correlation_id if correlation_id
|
|
34
34
|
|
|
35
35
|
exchange.publish(message, publish_options)
|
|
36
|
-
success = channel.wait_for_confirms
|
|
37
|
-
|
|
38
|
-
unless success
|
|
39
|
-
raise "Published message from Listener actor " \
|
|
40
|
-
"has not been confirmed by the server"
|
|
41
|
-
end
|
|
42
36
|
ensure
|
|
43
37
|
channel&.close
|
|
44
38
|
end
|
data/lib/eventhub/assets/app.css
CHANGED
|
@@ -94,6 +94,54 @@ body {
|
|
|
94
94
|
width: auto;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/* Config filter */
|
|
98
|
+
.config-filter {
|
|
99
|
+
margin-bottom: 1rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.config-filter-row {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
max-width: 400px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.config-filter .input {
|
|
109
|
+
flex: 1;
|
|
110
|
+
padding: 0.5rem 0.75rem;
|
|
111
|
+
font-size: 0.95rem;
|
|
112
|
+
border: 1px solid #dbdbdb;
|
|
113
|
+
border-radius: 4px 0 0 4px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.config-filter .input:focus {
|
|
117
|
+
border-color: hsl(212, 55%, 48%);
|
|
118
|
+
outline: none;
|
|
119
|
+
box-shadow: 0 0 0 2px hsla(212, 55%, 48%, 0.2);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.config-filter-reset {
|
|
123
|
+
padding: 0 0.75rem;
|
|
124
|
+
font-size: 1.1rem;
|
|
125
|
+
border: 1px solid #dbdbdb;
|
|
126
|
+
border-left: none;
|
|
127
|
+
border-radius: 0 4px 4px 0;
|
|
128
|
+
background: #f5f5f5;
|
|
129
|
+
color: #7a7a7a;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
align-self: stretch;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.config-filter-reset:hover {
|
|
135
|
+
background: #e8e8e8;
|
|
136
|
+
color: #363636;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.config-filter-count {
|
|
140
|
+
margin-top: 0.25rem;
|
|
141
|
+
font-size: 0.85rem;
|
|
142
|
+
color: #7a7a7a;
|
|
143
|
+
}
|
|
144
|
+
|
|
97
145
|
/* Config table */
|
|
98
146
|
.config-table thead th {
|
|
99
147
|
background-color: #1a1a1a !important;
|
|
@@ -107,6 +155,10 @@ body {
|
|
|
107
155
|
font-weight: 600;
|
|
108
156
|
}
|
|
109
157
|
|
|
158
|
+
.config-table .is-section-top td {
|
|
159
|
+
background-color: #d5d5d5;
|
|
160
|
+
}
|
|
161
|
+
|
|
110
162
|
.config-table tbody tr:not(.is-section):hover td {
|
|
111
163
|
background-color: hsl(212, 55%, 93%) !important;
|
|
112
164
|
}
|
|
@@ -116,11 +168,37 @@ body {
|
|
|
116
168
|
padding-left: 2rem;
|
|
117
169
|
}
|
|
118
170
|
|
|
119
|
-
.config-table .redacted
|
|
171
|
+
.config-table .redacted,
|
|
172
|
+
.config-table .not-set {
|
|
120
173
|
color: #b5b5b5;
|
|
121
174
|
font-style: italic;
|
|
122
175
|
}
|
|
123
176
|
|
|
177
|
+
.config-array {
|
|
178
|
+
list-style: none;
|
|
179
|
+
margin: 0;
|
|
180
|
+
padding: 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.config-array > li {
|
|
184
|
+
padding: 0.25rem 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.config-array > li + li {
|
|
188
|
+
border-top: 1px solid #e8e8e8;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.config-subtable {
|
|
192
|
+
margin: 0 !important;
|
|
193
|
+
font-size: 0.9rem;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.config-subtable td:first-child {
|
|
197
|
+
font-weight: 600;
|
|
198
|
+
white-space: nowrap;
|
|
199
|
+
width: 1%;
|
|
200
|
+
}
|
|
201
|
+
|
|
124
202
|
/* Footer */
|
|
125
203
|
.footer {
|
|
126
204
|
padding: 1.5rem;
|
|
@@ -147,11 +147,7 @@ module EventHub
|
|
|
147
147
|
}
|
|
148
148
|
},
|
|
149
149
|
# deprecated: use http instead (kept for backward compatibility)
|
|
150
|
-
heartbeat: {
|
|
151
|
-
bind_address: "0.0.0.0",
|
|
152
|
-
port: 8080,
|
|
153
|
-
path: "/svc/#{@name}/heartbeat"
|
|
154
|
-
}
|
|
150
|
+
heartbeat: {}
|
|
155
151
|
},
|
|
156
152
|
processor: {
|
|
157
153
|
heartbeat_cycle_in_s: 300,
|
|
@@ -10,7 +10,7 @@ module EventHub
|
|
|
10
10
|
DEFAULT_CHANGELOG_LOCATIONS = ["CHANGELOG.md", "doc/CHANGELOG.md"].freeze
|
|
11
11
|
DEFAULT_COMPANY_NAME = "Novartis"
|
|
12
12
|
|
|
13
|
-
DEFAULT_HTTP_RESOURCES = [:heartbeat, :version, :docs, :changelog
|
|
13
|
+
DEFAULT_HTTP_RESOURCES = [:heartbeat, :version, :docs, :changelog].freeze
|
|
14
14
|
|
|
15
15
|
def initialize(processor:, base_path:)
|
|
16
16
|
@processor = processor
|
|
@@ -91,19 +91,101 @@ module EventHub
|
|
|
91
91
|
"<p>Active configuration for the <strong>#{ERB::Util.html_escape(EventHub::Configuration.environment)}</strong> environment. " \
|
|
92
92
|
"Sensitive values such as passwords, tokens, and keys are automatically redacted.</p>"
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
filter = '<div class="config-filter">' \
|
|
95
|
+
'<div class="config-filter-row">' \
|
|
96
|
+
'<input type="text" id="config-filter-input" class="input" placeholder="Filter configuration keys..." autocomplete="off">' \
|
|
97
|
+
'<button type="button" id="config-filter-reset" class="config-filter-reset" title="Reset filter">×</button>' \
|
|
98
|
+
"</div>" \
|
|
99
|
+
'<p id="config-filter-count" class="config-filter-count"></p>' \
|
|
100
|
+
"</div>"
|
|
101
|
+
|
|
102
|
+
script = <<~JS
|
|
103
|
+
<script>
|
|
104
|
+
(function() {
|
|
105
|
+
var input = document.getElementById('config-filter-input');
|
|
106
|
+
var reset = document.getElementById('config-filter-reset');
|
|
107
|
+
var count = document.getElementById('config-filter-count');
|
|
108
|
+
var table = document.querySelector('.config-table');
|
|
109
|
+
if (!input || !table) return;
|
|
110
|
+
|
|
111
|
+
input.addEventListener('input', function() {
|
|
112
|
+
var term = this.value.toLowerCase();
|
|
113
|
+
var rows = table.querySelectorAll('tbody tr');
|
|
114
|
+
var visible = 0;
|
|
115
|
+
var total = 0;
|
|
116
|
+
|
|
117
|
+
// Filter individual rows by content (includes sub-tables)
|
|
118
|
+
rows.forEach(function(row) {
|
|
119
|
+
if (row.classList.contains('is-section')) {
|
|
120
|
+
row.style.display = '';
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
total++;
|
|
124
|
+
if (!term || row.textContent.toLowerCase().indexOf(term) !== -1) {
|
|
125
|
+
row.style.display = '';
|
|
126
|
+
visible++;
|
|
127
|
+
} else {
|
|
128
|
+
row.style.display = 'none';
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Hide section headers with no visible rows after them
|
|
133
|
+
var sections = table.querySelectorAll('tbody tr.is-section');
|
|
134
|
+
sections.forEach(function(section) {
|
|
135
|
+
var next = section.nextElementSibling;
|
|
136
|
+
var hasVisible = false;
|
|
137
|
+
while (next && !next.classList.contains('is-section')) {
|
|
138
|
+
if (next.style.display !== 'none') hasVisible = true;
|
|
139
|
+
next = next.nextElementSibling;
|
|
140
|
+
}
|
|
141
|
+
section.style.display = hasVisible ? '' : 'none';
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (term) {
|
|
145
|
+
count.textContent = visible + ' of ' + total + ' entries';
|
|
146
|
+
} else {
|
|
147
|
+
count.textContent = '';
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
reset.addEventListener('click', function() {
|
|
151
|
+
input.value = '';
|
|
152
|
+
input.dispatchEvent(new Event('input'));
|
|
153
|
+
input.focus();
|
|
154
|
+
});
|
|
155
|
+
})();
|
|
156
|
+
</script>
|
|
157
|
+
JS
|
|
158
|
+
|
|
159
|
+
intro + filter + config_to_html_table(config) + script
|
|
95
160
|
end
|
|
96
161
|
|
|
97
|
-
def config_to_html_table(hash, depth = 0)
|
|
162
|
+
def config_to_html_table(hash, depth = 0, prefix = "")
|
|
98
163
|
rows = hash.map do |key, value|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"
|
|
164
|
+
full_key = prefix.empty? ? key.to_s : "#{prefix}.#{key}"
|
|
165
|
+
if depth == 0 && value.is_a?(Hash) && !value.empty?
|
|
166
|
+
"<tr class=\"is-section is-section-top\"><td colspan=\"2\"><strong>#{ERB::Util.html_escape(full_key)}</strong></td></tr>\n" \
|
|
167
|
+
"#{config_to_html_table(value, 1, full_key)}"
|
|
168
|
+
elsif value.is_a?(Hash) && value.empty?
|
|
169
|
+
"<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td><span class=\"not-set\">(empty)</span></td></tr>"
|
|
170
|
+
elsif value.is_a?(Hash) && value.values.all? { |v| v.is_a?(Hash) && v.empty? }
|
|
171
|
+
items = value.keys.map { |k| "<li>#{ERB::Util.html_escape(k)}</li>" }.join("\n")
|
|
172
|
+
"<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td><ul class=\"config-array\">#{items}</ul></td></tr>"
|
|
173
|
+
elsif value.is_a?(Hash) && compact_hash?(value)
|
|
174
|
+
"<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td>#{format_nested_value(value)}</td></tr>"
|
|
175
|
+
elsif value.is_a?(Hash)
|
|
176
|
+
"<tr class=\"is-section\"><td colspan=\"2\"><strong>#{ERB::Util.html_escape(full_key)}</strong></td></tr>\n" \
|
|
177
|
+
"#{config_to_html_table(value, depth + 1, full_key)}"
|
|
102
178
|
elsif value.is_a?(Array)
|
|
103
|
-
format_array_rows(key, value, depth)
|
|
179
|
+
format_array_rows(full_key, key, value, depth)
|
|
104
180
|
else
|
|
105
|
-
display_value = sensitive_key?(key)
|
|
106
|
-
|
|
181
|
+
display_value = if sensitive_key?(key)
|
|
182
|
+
"<span class=\"redacted\">***</span>"
|
|
183
|
+
elsif value.nil? || value.to_s.strip.empty?
|
|
184
|
+
"<span class=\"not-set\">(not set)</span>"
|
|
185
|
+
else
|
|
186
|
+
ERB::Util.html_escape(value.to_s)
|
|
187
|
+
end
|
|
188
|
+
"<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td>#{display_value}</td></tr>"
|
|
107
189
|
end
|
|
108
190
|
end.join("\n")
|
|
109
191
|
|
|
@@ -114,24 +196,59 @@ module EventHub
|
|
|
114
196
|
end
|
|
115
197
|
end
|
|
116
198
|
|
|
117
|
-
def format_array_rows(key, array,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
199
|
+
def format_array_rows(full_key, key, array, _depth)
|
|
200
|
+
return "<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td><span class=\"not-set\">(empty)</span></td></tr>" if array.empty?
|
|
201
|
+
|
|
202
|
+
if sensitive_key?(key)
|
|
203
|
+
return "<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td><span class=\"redacted\">***</span></td></tr>"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
inner = array.map { |item| format_array_item(item) }.join("\n")
|
|
207
|
+
"<tr><td class=\"config-key\">#{ERB::Util.html_escape(full_key)}</td><td><ul class=\"config-array\">#{inner}</ul></td></tr>"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def format_array_item(item)
|
|
211
|
+
if item.is_a?(Hash)
|
|
212
|
+
rows = item.map do |k, v|
|
|
213
|
+
value = format_nested_value(v)
|
|
214
|
+
"<tr><td>#{ERB::Util.html_escape(k)}</td><td>#{value}</td></tr>"
|
|
215
|
+
end.join
|
|
216
|
+
"<li><table class=\"table is-bordered is-narrow config-subtable\">#{rows}</table></li>"
|
|
217
|
+
elsif item.is_a?(Array)
|
|
218
|
+
inner = item.map { |i| format_array_item(i) }.join("\n")
|
|
219
|
+
"<li><ul class=\"config-array\">#{inner}</ul></li>"
|
|
128
220
|
else
|
|
129
|
-
|
|
130
|
-
|
|
221
|
+
"<li>#{ERB::Util.html_escape(item.to_s)}</li>"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def format_nested_value(value)
|
|
226
|
+
if value.is_a?(Hash)
|
|
227
|
+
rows = value.map do |k, v|
|
|
228
|
+
"<tr><td>#{ERB::Util.html_escape(k)}</td><td>#{format_nested_value(v)}</td></tr>"
|
|
229
|
+
end.join
|
|
230
|
+
"<table class=\"table is-bordered is-narrow config-subtable\">#{rows}</table>"
|
|
231
|
+
elsif value.is_a?(Array)
|
|
232
|
+
items = value.map { |i| format_array_item(i) }.join("\n")
|
|
233
|
+
"<ul class=\"config-array\">#{items}</ul>"
|
|
234
|
+
elsif value.nil? || value.to_s.strip.empty?
|
|
235
|
+
"<span class=\"not-set\">(not set)</span>"
|
|
236
|
+
else
|
|
237
|
+
ERB::Util.html_escape(value.to_s)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def compact_hash?(hash)
|
|
242
|
+
hash.values.all? do |v|
|
|
243
|
+
if v.is_a?(Hash)
|
|
244
|
+
compact_hash?(v)
|
|
245
|
+
else
|
|
246
|
+
!v.is_a?(Array)
|
|
247
|
+
end
|
|
131
248
|
end
|
|
132
249
|
end
|
|
133
250
|
|
|
134
|
-
DEFAULT_SENSITIVE_KEYS = %w[password secret token api_key credential].freeze
|
|
251
|
+
DEFAULT_SENSITIVE_KEYS = %w[password secret token api_key credential username user login].freeze
|
|
135
252
|
|
|
136
253
|
def sensitive_key?(key)
|
|
137
254
|
keys = if @processor&.class&.method_defined?(:sensitive_keys)
|
data/lib/eventhub/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eventhub-processor2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steiner, Thomas
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '3.0'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '3.0'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: eventhub-components
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -192,6 +192,7 @@ files:
|
|
|
192
192
|
- eventhub-processor2.gemspec
|
|
193
193
|
- example/CHANGELOG.md
|
|
194
194
|
- example/README.md
|
|
195
|
+
- example/config/example.json
|
|
195
196
|
- example/config/receiver.json
|
|
196
197
|
- example/config/router.json
|
|
197
198
|
- example/crasher.rb
|