eventhub-processor2 1.26.1 → 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/.github/workflows/02_test_build_and_release.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/README.md +13 -17
- 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 +12 -4
- data/lib/eventhub/actor_publisher.rb +1 -7
- data/lib/eventhub/assets/app.css +79 -1
- data/lib/eventhub/configuration.rb +2 -6
- 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,19 @@
|
|
|
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
|
+
|
|
12
|
+
# 1.26.2 / 2026-03-25
|
|
13
|
+
|
|
14
|
+
* Change default `http.bind_address` from `localhost` to `0.0.0.0` for container compatibility (ECS, Docker, K8s)
|
|
15
|
+
* Add deprecation warning when legacy `heartbeat` configuration block is used
|
|
16
|
+
|
|
3
17
|
# 1.26.1 / 2026-03-20
|
|
4
18
|
|
|
5
19
|
* Show only processor name in browser tab title
|
data/README.md
CHANGED
|
@@ -204,7 +204,7 @@ If --config option is not provided processor tries to load config/{class_name}.j
|
|
|
204
204
|
"verify_peer": false,
|
|
205
205
|
"show_bunny_logs": false,
|
|
206
206
|
"http": {
|
|
207
|
-
"bind_address": "
|
|
207
|
+
"bind_address": "0.0.0.0",
|
|
208
208
|
"port": 8080,
|
|
209
209
|
"base_path": "/svc/{class_name}"
|
|
210
210
|
}
|
|
@@ -307,7 +307,7 @@ Processor2 exposes HTTP resources for health checks and monitoring. All resource
|
|
|
307
307
|
{
|
|
308
308
|
"server": {
|
|
309
309
|
"http": {
|
|
310
|
-
"bind_address": "
|
|
310
|
+
"bind_address": "0.0.0.0",
|
|
311
311
|
"port": 8080,
|
|
312
312
|
"base_path": "/svc/{class_name}"
|
|
313
313
|
}
|
|
@@ -315,17 +315,19 @@ Processor2 exposes HTTP resources for health checks and monitoring. All resource
|
|
|
315
315
|
}
|
|
316
316
|
```
|
|
317
317
|
|
|
318
|
+
The default `bind_address` is `0.0.0.0` (all interfaces), which is required for containerized deployments (ECS, Docker, K8s) where health checks come from outside the container.
|
|
319
|
+
|
|
318
320
|
Resources are mounted under the `base_path`:
|
|
319
321
|
- `{base_path}/heartbeat` - Health check
|
|
320
322
|
- `{base_path}/version` - Version info as JSON
|
|
321
323
|
- `{base_path}/docs` - README documentation as HTML
|
|
322
|
-
- `{base_path}/docs/configuration` - Configuration as HTML table
|
|
324
|
+
- `{base_path}/docs/configuration` - Configuration as HTML table (opt-in, see [Enabling Resources](#enabling-resources))
|
|
323
325
|
- `{base_path}/docs/changelog` - CHANGELOG as HTML
|
|
324
326
|
- `{base_path}/assets/*` - Static assets (CSS, images)
|
|
325
327
|
|
|
326
328
|
Accessing `{base_path}` or `{base_path}/` redirects to `{base_path}/docs`.
|
|
327
329
|
|
|
328
|
-
**Backward Compatibility:** If you have existing configuration using the old `heartbeat` block with `bind_address`, `port`, and `path`, it will continue to work.
|
|
330
|
+
**Backward Compatibility:** If you have existing configuration using the old `heartbeat` block with `bind_address`, `port`, and `path`, it will continue to work but will emit a deprecation warning. Please migrate to the `http` configuration block. The `heartbeat` block will be removed in a future major version.
|
|
329
331
|
|
|
330
332
|
### Heartbeat
|
|
331
333
|
|
|
@@ -467,7 +469,7 @@ GET {base_path}/docs/configuration
|
|
|
467
469
|
|
|
468
470
|
**Response:** `200 OK` with HTML page
|
|
469
471
|
|
|
470
|
-
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:
|
|
471
473
|
|
|
472
474
|
```ruby
|
|
473
475
|
# Override the entire list
|
|
@@ -495,28 +497,22 @@ class MyProcessor < EventHub::Processor2
|
|
|
495
497
|
end
|
|
496
498
|
```
|
|
497
499
|
|
|
498
|
-
###
|
|
500
|
+
### Enabling Resources
|
|
499
501
|
|
|
500
|
-
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.
|
|
501
503
|
|
|
502
|
-
|
|
503
|
-
class MyProcessor < EventHub::Processor2
|
|
504
|
-
def http_resources
|
|
505
|
-
[:heartbeat, :version, :docs, :changelog, :configuration] # default: all enabled
|
|
506
|
-
end
|
|
507
|
-
end
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
To disable the configuration page for example:
|
|
504
|
+
To enable the configuration page, define an `http_resources` method in your processor:
|
|
511
505
|
|
|
512
506
|
```ruby
|
|
513
507
|
class MyProcessor < EventHub::Processor2
|
|
514
508
|
def http_resources
|
|
515
|
-
[:heartbeat, :version, :docs, :changelog]
|
|
509
|
+
[:heartbeat, :version, :docs, :changelog, :configuration]
|
|
516
510
|
end
|
|
517
511
|
end
|
|
518
512
|
```
|
|
519
513
|
|
|
514
|
+
You can also use `http_resources` to disable any of the default resources. The navbar adapts automatically.
|
|
515
|
+
|
|
520
516
|
### Customizing Footer
|
|
521
517
|
|
|
522
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,9 +183,17 @@ module EventHub
|
|
|
183
183
|
private
|
|
184
184
|
|
|
185
185
|
def http_config(key)
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
# Prefer deprecated heartbeat config for backward compatibility,
|
|
187
|
+
# fall back to http config. Only warn when values actually differ.
|
|
188
|
+
heartbeat_value = EventHub::Configuration.server.dig(:heartbeat, key)
|
|
189
|
+
http_value = EventHub::Configuration.server.dig(:http, key)
|
|
190
|
+
|
|
191
|
+
if heartbeat_value && http_value && heartbeat_value != http_value
|
|
192
|
+
EventHub.logger.warn("[DEPRECATION] heartbeat.#{key} is deprecated. Please use http.#{key} instead.")
|
|
193
|
+
return heartbeat_value
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
http_value || heartbeat_value
|
|
189
197
|
end
|
|
190
198
|
|
|
191
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;
|
|
@@ -138,7 +138,7 @@ module EventHub
|
|
|
138
138
|
verify_peer: false,
|
|
139
139
|
show_bunny_logs: false,
|
|
140
140
|
http: {
|
|
141
|
-
bind_address: "
|
|
141
|
+
bind_address: "0.0.0.0",
|
|
142
142
|
port: 8080,
|
|
143
143
|
base_path: "/svc/#{@name}",
|
|
144
144
|
docs: {
|
|
@@ -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: "localhost",
|
|
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
|