eventhub-processor2 1.24.1 → 1.26.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/{ci.yml → 01_test.yml} +3 -3
- data/.github/workflows/02_test_build_and_release.yml +57 -0
- data/.tool-versions +1 -1
- data/CHANGELOG.md +14 -0
- data/README.md +318 -8
- data/eventhub-processor2.gemspec +1 -2
- data/example/CHANGELOG.md +8 -0
- data/example/receiver.rb +38 -0
- data/example/router.rb +4 -0
- data/lib/eventhub/actor_heartbeat.rb +2 -1
- data/lib/eventhub/actor_listener_amqp.rb +41 -17
- data/lib/eventhub/actor_listener_http.rb +235 -9
- data/lib/eventhub/actor_publisher.rb +5 -1
- data/lib/eventhub/actor_watchdog.rb +2 -1
- data/lib/eventhub/assets/app.css +132 -0
- data/lib/eventhub/assets/bulma.min.css +3 -0
- data/lib/eventhub/assets/logo.svg +7 -0
- data/lib/eventhub/base.rb +4 -1
- data/lib/eventhub/configuration.rb +10 -0
- data/lib/eventhub/correlation_id.rb +35 -0
- data/lib/eventhub/docs_renderer.rb +184 -0
- data/lib/eventhub/execution_id.rb +20 -0
- data/lib/eventhub/logger.rb +37 -4
- data/lib/eventhub/message.rb +3 -4
- data/lib/eventhub/processor2.rb +11 -1
- data/lib/eventhub/templates/layout.erb +63 -0
- data/lib/eventhub/version.rb +1 -1
- metadata +16 -22
- data/.github/workflows/cd.yml +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a22e1c2fd13d72c1b1a7ca6acd296f6e1a5a6b8fca1398f81d5cb4e2a04c220b
|
|
4
|
+
data.tar.gz: 18ca7a93b97092c0a66c4dde11a1cce29fdc505259bebb7ed951e1638dd04422
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7965f0be501e337c8a0e8d6873417f13e884107016fad8f080550d69be14636cf3cdd31c7f884dda0a811a475cb73bba16ad1c2b2b85fdce76de95fd3785d02f
|
|
7
|
+
data.tar.gz: 70c95a713c4600c748a5e3bf5d625fd848110aa37c94b5bbcf3df2dcff847f1d6bd2c5bc4c1a69e6673a26065c9f42d4c02bbd627af235d53998a4ea81ed2ab0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: 01 - Test
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
@@ -23,11 +23,11 @@ jobs:
|
|
|
23
23
|
strategy:
|
|
24
24
|
fail-fast: false
|
|
25
25
|
matrix:
|
|
26
|
-
ruby: [ '3.4', '3.3', '3.2']
|
|
26
|
+
ruby: [ '4.0', '3.4', '3.3', '3.2']
|
|
27
27
|
|
|
28
28
|
name: Ruby ${{ matrix.ruby }}
|
|
29
29
|
steps:
|
|
30
|
-
- uses: actions/checkout@
|
|
30
|
+
- uses: actions/checkout@v6
|
|
31
31
|
|
|
32
32
|
- name: Set up Ruby
|
|
33
33
|
uses: ruby/setup-ruby@v1
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: 02 - Test, Build and Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
env:
|
|
13
|
+
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: '4.0'
|
|
22
|
+
bundler: latest
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
cache-version: 4
|
|
25
|
+
|
|
26
|
+
- name: Setup RabbitMQ
|
|
27
|
+
run: |
|
|
28
|
+
cd docker && docker build -t processor-rabbitmq .
|
|
29
|
+
docker run -d -p 5672:5672 -p 15672:15672 --name processor-rabbitmq processor-rabbitmq
|
|
30
|
+
|
|
31
|
+
- name: Run default task
|
|
32
|
+
run: |
|
|
33
|
+
bundle exec rake
|
|
34
|
+
|
|
35
|
+
build:
|
|
36
|
+
needs: test
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # Required for OIDC
|
|
40
|
+
contents: write # Required for pushing tags
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Checkout current code
|
|
44
|
+
uses: actions/checkout@v6
|
|
45
|
+
|
|
46
|
+
- name: Set up Ruby
|
|
47
|
+
uses: ruby/setup-ruby@v1
|
|
48
|
+
with:
|
|
49
|
+
ruby-version: '4.0'
|
|
50
|
+
bundler-cache: true
|
|
51
|
+
cache-version: 1
|
|
52
|
+
|
|
53
|
+
- name: Build gem
|
|
54
|
+
run: gem build *.gemspec
|
|
55
|
+
|
|
56
|
+
- name: Push to Rubygems
|
|
57
|
+
uses: rubygems/release-gem@v1
|
data/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby
|
|
1
|
+
ruby 4.0.2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog of EventHub::Processor2
|
|
2
2
|
|
|
3
|
+
# 1.26.0 / 2026-03-18
|
|
4
|
+
|
|
5
|
+
* Add HTTP resources for version, docs, changelog, and configuration with HTML rendering
|
|
6
|
+
* Add processor hooks for HTTP resource customization: `version`, `company_name`, `readme_as_html`, `changelog_as_html`, `configuration_as_html`, `sensitive_keys`, and `http_resources`
|
|
7
|
+
* Heartbeat endpoint returns detailed JSON status (version, pid, uptime, messages)
|
|
8
|
+
* Add distributed tracing with correlation_id and execution_id in structured log output
|
|
9
|
+
* Simplify HTTP configuration with shared `http.bind_address`, `http.port`, and `http.base_path`
|
|
10
|
+
* Add kramdown dependency for markdown to HTML conversion
|
|
11
|
+
* Rename GitHub Actions workflows and add test step to release workflow
|
|
12
|
+
|
|
13
|
+
# 1.25.0 / 2026-01-13
|
|
14
|
+
|
|
15
|
+
* Replace uuidtools dependency with Ruby's built-in SecureRandom
|
|
16
|
+
|
|
3
17
|
# 1.24.1 / 2025-11-09
|
|
4
18
|
|
|
5
19
|
* Enable Trusted Publishing
|
data/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
[](https://badge.fury.io/rb/eventhub-processor2)
|
|
2
|
-
[](https://github.com/thomis/eventhub-processor2/actions/workflows/01_test.yml)
|
|
3
|
+
[](https://github.com/thomis/eventhub-processor2/actions/workflows/02_test_build_and_release.yml)
|
|
3
4
|
|
|
4
5
|
# EventHub::Processor2
|
|
5
6
|
|
|
@@ -10,7 +11,7 @@ Processor2 has currently the following sub-components implemented
|
|
|
10
11
|
* Publisher - responsible for message publishing
|
|
11
12
|
* Watchdog - Checks regularly broker connection and defined listener queue(s)
|
|
12
13
|
* Listener AMQP - Listens to defined AMQP queues, parses recevied message into a EventHub::Message instance and calls handle_message method as defined in derived class.
|
|
13
|
-
* Listener HTTP - Provides
|
|
14
|
+
* Listener HTTP - Provides HTTP endpoints for health checks and monitoring (e.g. /svc/{class_name}/heartbeat, /svc/{class_name}/version)
|
|
14
15
|
|
|
15
16
|
Processor2 is using Bunny http://rubybunny.info a feature complete RabbitMQ Client to interact with message broker. Processor2 can deal with long running message processing.
|
|
16
17
|
|
|
@@ -65,6 +66,7 @@ module EventHub
|
|
|
65
66
|
# => :content_type
|
|
66
67
|
# => :priority
|
|
67
68
|
# => :delivery_tag
|
|
69
|
+
# => :correlation_id (if present in incoming AMQP message)
|
|
68
70
|
|
|
69
71
|
# if an exception is raised in your code
|
|
70
72
|
# it will be automatically catched by
|
|
@@ -114,6 +116,73 @@ I, [2018-02-09T15:22:35.658522 #37966] INFO -- : Listener is starting...
|
|
|
114
116
|
I, [2018-02-09T15:22:35.699161 #37966] INFO -- : Listening to queue [example]
|
|
115
117
|
```
|
|
116
118
|
|
|
119
|
+
## Logging
|
|
120
|
+
|
|
121
|
+
By default, Processor2 logs to both stdout (standard format) and a logstash file. For containerized environments (Docker, Kubernetes), use the `--console-log-only` option to output structured JSON logs to stdout only:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bundle exec ruby example.rb --console-log-only
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This outputs logs in JSON format suitable for log aggregation systems:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{"@timestamp":"2026-01-18T10:00:00.000Z","@version":"1","severity":"INFO","host":"server1","application":"example","environment":"production","message":"example (1.0.0): has been started"}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Correlation ID
|
|
134
|
+
|
|
135
|
+
Processor2 supports automatic propagation of correlation IDs for distributed tracing. While EventHub messages already contain an `execution_id` in the message body for tracing, the AMQP `correlation_id` provides an additional benefit: it's part of the message metadata (envelope), not the payload. This means it's available even when the JSON body is invalid and cannot be parsed - useful for error tracking and debugging malformed messages.
|
|
136
|
+
|
|
137
|
+
When an incoming AMQP message includes a `correlation_id` in its metadata:
|
|
138
|
+
|
|
139
|
+
1. **Automatic logging**: All log messages during message processing will include `correlation_id` as a separate field in structured JSON output
|
|
140
|
+
2. **Automatic publishing**: Any messages published during processing will automatically include the same correlation_id in their AMQP headers
|
|
141
|
+
3. **Available in args**: The correlation_id is passed to `handle_message` via `args[:correlation_id]`
|
|
142
|
+
4. **Consistent execution_id**: When creating new messages, `execution_id` is automatically set to match `correlation_id`, ensuring consistent tracing across both AMQP metadata and message body
|
|
143
|
+
|
|
144
|
+
This happens transparently without any changes to your `handle_message` implementation:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
def handle_message(message, args = {})
|
|
148
|
+
# correlation_id is available in args if needed
|
|
149
|
+
correlation_id = args[:correlation_id]
|
|
150
|
+
|
|
151
|
+
# Logging automatically includes correlation_id in JSON output
|
|
152
|
+
EventHub.logger.info("Processing order")
|
|
153
|
+
|
|
154
|
+
# Publishing automatically includes correlation_id
|
|
155
|
+
publish(message: response.to_json)
|
|
156
|
+
|
|
157
|
+
message.copy
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
You can also explicitly set or override the correlation_id when publishing:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
publish(message: msg.to_json, correlation_id: "550e8400-e29b-41d4-a716-446655440000")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If no `correlation_id` is present in the AMQP metadata, the message body's `execution_id` is used as fallback to ensure tracing continuity.
|
|
168
|
+
|
|
169
|
+
### How it works
|
|
170
|
+
|
|
171
|
+
1. **Received**: When an AMQP message arrives, `correlation_id` is extracted from the message metadata
|
|
172
|
+
2. **Fallback**: If no `correlation_id` in AMQP metadata, the message body's `execution_id` is used instead (ensuring tracing continuity)
|
|
173
|
+
3. **Stored**: The value is stored in thread-local storage (`Thread.current`) for the duration of message processing
|
|
174
|
+
4. **Passed**: The `correlation_id` is passed to `handle_message` via `args[:correlation_id]`
|
|
175
|
+
5. **Logging**: The logger automatically reads from thread-local storage and includes it in JSON output
|
|
176
|
+
6. **Publishing**: The publisher automatically reads from thread-local storage and adds it to outgoing AMQP message headers (can be overwritten by passing `correlation_id:` explicitly)
|
|
177
|
+
7. **New messages**: When creating a new `EventHub::Message`:
|
|
178
|
+
- With `correlation_id` present → `execution_id` is set to match `correlation_id`
|
|
179
|
+
- Without `correlation_id` → `execution_id` is set to a new UUID (default behavior)
|
|
180
|
+
8. **Cleared**: After message processing completes, the stored value is cleared
|
|
181
|
+
|
|
182
|
+
This design ensures correlation_id flows transparently through the entire message processing lifecycle. No code changes are required for existing implementations - just update the processor2 gem dependency.
|
|
183
|
+
|
|
184
|
+
**Note:** We use `correlation_id` (snake_case) to follow Ruby naming conventions, AMQP standard message properties, and stay consistent with other args keys like `:queue_name`, `:content_type`, etc. The `correlation_id` value is typically a UUID (e.g., `550e8400-e29b-41d4-a716-446655440000`).
|
|
185
|
+
|
|
117
186
|
## Configuration
|
|
118
187
|
|
|
119
188
|
If --config option is not provided processor tries to load config/{class_name}.json. If file does not exist it loads default values as specified below.
|
|
@@ -133,10 +202,10 @@ If --config option is not provided processor tries to load config/{class_name}.j
|
|
|
133
202
|
"tls_ca_certificates": [],
|
|
134
203
|
"verify_peer": false,
|
|
135
204
|
"show_bunny_logs": false,
|
|
136
|
-
"
|
|
205
|
+
"http": {
|
|
137
206
|
"bind_address": "localhost",
|
|
138
207
|
"port": 8080,
|
|
139
|
-
"
|
|
208
|
+
"base_path": "/svc/{class_name}"
|
|
140
209
|
}
|
|
141
210
|
},
|
|
142
211
|
"processor": {
|
|
@@ -228,6 +297,239 @@ Final configuration result
|
|
|
228
297
|
|
|
229
298
|
```
|
|
230
299
|
|
|
300
|
+
## HTTP Resources
|
|
301
|
+
|
|
302
|
+
Processor2 exposes HTTP resources for health checks and monitoring. All resources share the same HTTP server configuration.
|
|
303
|
+
|
|
304
|
+
**Configuration:**
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"server": {
|
|
308
|
+
"http": {
|
|
309
|
+
"bind_address": "localhost",
|
|
310
|
+
"port": 8080,
|
|
311
|
+
"base_path": "/svc/{class_name}"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Resources are mounted under the `base_path`:
|
|
318
|
+
- `{base_path}/heartbeat` - Health check
|
|
319
|
+
- `{base_path}/version` - Version info as JSON
|
|
320
|
+
- `{base_path}/docs` - README documentation as HTML
|
|
321
|
+
- `{base_path}/docs/configuration` - Configuration as HTML table
|
|
322
|
+
- `{base_path}/docs/changelog` - CHANGELOG as HTML
|
|
323
|
+
- `{base_path}/assets/*` - Static assets (CSS, images)
|
|
324
|
+
|
|
325
|
+
Accessing `{base_path}` or `{base_path}/` redirects to `{base_path}/docs`.
|
|
326
|
+
|
|
327
|
+
**Backward Compatibility:** If you have existing configuration using the old `heartbeat` block with `bind_address`, `port`, and `path`, it will continue to work. The new `http` configuration takes precedence when both are present.
|
|
328
|
+
|
|
329
|
+
### Heartbeat
|
|
330
|
+
|
|
331
|
+
Returns detailed processor status information as JSON.
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
GET {base_path}/heartbeat
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Response:** `200 OK` with JSON body
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"version": "1.0.0",
|
|
341
|
+
"pid": 12345,
|
|
342
|
+
"environment": "production",
|
|
343
|
+
"heartbeat": {
|
|
344
|
+
"started": "2026-01-18T10:00:00.000Z",
|
|
345
|
+
"stamp_last_request": "2026-01-18T12:30:00.000Z",
|
|
346
|
+
"uptime_in_ms": 9000000,
|
|
347
|
+
"heartbeat_cycle_in_ms": 300000,
|
|
348
|
+
"queues_consuming_from": ["my_processor"],
|
|
349
|
+
"queues_publishing_to": ["event_hub.inbound"],
|
|
350
|
+
"host": "server1.example.com",
|
|
351
|
+
"addresses": [
|
|
352
|
+
{"interface": "en0", "host_name": "server1", "ip_address": "192.168.1.100"}
|
|
353
|
+
],
|
|
354
|
+
"messages": {
|
|
355
|
+
"total": 1000,
|
|
356
|
+
"successful": 995,
|
|
357
|
+
"unsuccessful": 5,
|
|
358
|
+
"average_size": 2048,
|
|
359
|
+
"average_process_time_in_ms": 50,
|
|
360
|
+
"total_process_time_in_ms": 50000
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Version
|
|
367
|
+
|
|
368
|
+
Returns the processor version as JSON.
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
GET {base_path}/version
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Response:** `200 OK` with JSON body
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"version": "1.0.0"
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The version is taken from the `version` method in your derived processor class. If not defined, it returns `"?.?.?"`.
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
class MyProcessor < EventHub::Processor2
|
|
385
|
+
def version
|
|
386
|
+
"1.0.0" # your version
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Docs
|
|
392
|
+
|
|
393
|
+
Serves README.md as HTML with a built-in layout.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
GET {base_path}/docs
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Response:** `200 OK` with HTML page
|
|
400
|
+
|
|
401
|
+
By default, looks for `README.md` in the current directory, then `doc/README.md`. You can customize the path via configuration:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"server": {
|
|
406
|
+
"http": {
|
|
407
|
+
"docs": {
|
|
408
|
+
"readme_path": "/custom/path/to/README.md"
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Or override completely by defining a `readme_as_html` method in your processor:
|
|
416
|
+
|
|
417
|
+
```ruby
|
|
418
|
+
class MyProcessor < EventHub::Processor2
|
|
419
|
+
def readme_as_html
|
|
420
|
+
"<h1>Custom Documentation</h1><p>Your content here.</p>"
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Changelog
|
|
426
|
+
|
|
427
|
+
Serves CHANGELOG.md as HTML with a built-in layout.
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
GET {base_path}/docs/changelog
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Response:** `200 OK` with HTML page
|
|
434
|
+
|
|
435
|
+
By default, looks for `CHANGELOG.md` in the current directory, then `doc/CHANGELOG.md`. You can customize the path via configuration:
|
|
436
|
+
|
|
437
|
+
```json
|
|
438
|
+
{
|
|
439
|
+
"server": {
|
|
440
|
+
"http": {
|
|
441
|
+
"docs": {
|
|
442
|
+
"changelog_path": "/custom/path/to/CHANGELOG.md"
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Or override completely by defining a `changelog_as_html` method in your processor:
|
|
450
|
+
|
|
451
|
+
```ruby
|
|
452
|
+
class MyProcessor < EventHub::Processor2
|
|
453
|
+
def changelog_as_html
|
|
454
|
+
"<h1>Custom Changelog</h1><p>Your changes here.</p>"
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Configuration
|
|
460
|
+
|
|
461
|
+
Displays the active configuration as an HTML table. Sensitive values (passwords, tokens, keys) are automatically redacted.
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
GET {base_path}/docs/configuration
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Response:** `200 OK` with HTML page
|
|
468
|
+
|
|
469
|
+
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:
|
|
470
|
+
|
|
471
|
+
```ruby
|
|
472
|
+
# Override the entire list
|
|
473
|
+
class MyProcessor < EventHub::Processor2
|
|
474
|
+
def sensitive_keys
|
|
475
|
+
%w[password secret token api_key credential connection_string]
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Or extend the default list
|
|
480
|
+
class MyProcessor < EventHub::Processor2
|
|
481
|
+
def sensitive_keys
|
|
482
|
+
EventHub::DocsRenderer::DEFAULT_SENSITIVE_KEYS + %w[connection_string]
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Or override the entire page by defining a `configuration_as_html` method:
|
|
488
|
+
|
|
489
|
+
```ruby
|
|
490
|
+
class MyProcessor < EventHub::Processor2
|
|
491
|
+
def configuration_as_html
|
|
492
|
+
"<h1>Custom Configuration</h1><p>Your content here.</p>"
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Disabling Resources
|
|
498
|
+
|
|
499
|
+
By default, all HTTP resources are enabled. You can control which resources are available by defining an `http_resources` method in your processor. The navbar adapts automatically.
|
|
500
|
+
|
|
501
|
+
```ruby
|
|
502
|
+
class MyProcessor < EventHub::Processor2
|
|
503
|
+
def http_resources
|
|
504
|
+
[:heartbeat, :version, :docs, :changelog, :configuration] # default: all enabled
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
To disable the configuration page for example:
|
|
510
|
+
|
|
511
|
+
```ruby
|
|
512
|
+
class MyProcessor < EventHub::Processor2
|
|
513
|
+
def http_resources
|
|
514
|
+
[:heartbeat, :version, :docs, :changelog]
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Customizing Footer
|
|
520
|
+
|
|
521
|
+
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:
|
|
522
|
+
|
|
523
|
+
```ruby
|
|
524
|
+
class MyProcessor < EventHub::Processor2
|
|
525
|
+
def company_name
|
|
526
|
+
"My Company"
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Future Extension:** A future version could allow overriding the default layout template and CSS assets using convention over configuration (e.g., placing custom files in `doc/layout.erb` or `doc/app.css`).
|
|
532
|
+
|
|
231
533
|
## Development
|
|
232
534
|
|
|
233
535
|
```
|
|
@@ -244,13 +546,21 @@ Final configuration result
|
|
|
244
546
|
bundle exec rake
|
|
245
547
|
```
|
|
246
548
|
|
|
247
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
549
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
248
550
|
|
|
249
551
|
## Publishing
|
|
250
552
|
|
|
251
|
-
This project uses [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) to securely publish gems to RubyGems.org
|
|
252
|
-
|
|
253
|
-
|
|
553
|
+
This project uses [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) to securely publish gems to RubyGems.org via GitHub Actions. To release a new version:
|
|
554
|
+
|
|
555
|
+
1. Update the version number in `lib/eventhub/version.rb`
|
|
556
|
+
2. Merge changes to `main`
|
|
557
|
+
3. Create a version tag either via command line or GitHub:
|
|
558
|
+
```
|
|
559
|
+
git tag v1.x.x
|
|
560
|
+
git push origin v1.x.x
|
|
561
|
+
```
|
|
562
|
+
Or on GitHub: go to **Releases** → **Create a new release** → enter the tag name (e.g. `v1.26.0`), select `main` as target, and publish.
|
|
563
|
+
4. The `02 - Test, Build and Release` workflow triggers automatically, runs tests, builds the gem, and publishes it to [rubygems.org](https://rubygems.org)
|
|
254
564
|
|
|
255
565
|
## Contributing
|
|
256
566
|
|
data/eventhub-processor2.gemspec
CHANGED
|
@@ -26,11 +26,10 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_dependency "webrick", "~> 1.8"
|
|
27
27
|
spec.add_dependency "bunny", "~> 2.23"
|
|
28
28
|
spec.add_dependency "eventhub-components", "~> 0.4"
|
|
29
|
-
spec.add_dependency "uuidtools", "~> 3.0"
|
|
30
29
|
spec.add_dependency "base64", "~> 0.3.0"
|
|
31
30
|
spec.add_dependency "logger", "~> 1.6"
|
|
31
|
+
spec.add_dependency "kramdown", "~> 2.4"
|
|
32
32
|
|
|
33
|
-
spec.add_development_dependency "bundler", "~> 2.3"
|
|
34
33
|
spec.add_development_dependency "rake", "~> 13.2"
|
|
35
34
|
spec.add_development_dependency "rspec", "~> 3.13"
|
|
36
35
|
spec.add_development_dependency "simplecov", "~> 0.21"
|
data/example/receiver.rb
CHANGED
|
@@ -2,6 +2,44 @@ require_relative "../lib/eventhub/base"
|
|
|
2
2
|
|
|
3
3
|
module EventHub
|
|
4
4
|
class Receiver < Processor2
|
|
5
|
+
def version
|
|
6
|
+
"2.0.0"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def company_name
|
|
10
|
+
"Example Company"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Custom README served via method instead of file
|
|
14
|
+
def readme_as_html
|
|
15
|
+
<<~HTML
|
|
16
|
+
<h1>Receiver Processor</h1>
|
|
17
|
+
<p>This processor receives messages and deletes associated files.</p>
|
|
18
|
+
<h2>Features</h2>
|
|
19
|
+
<ul>
|
|
20
|
+
<li>Receives messages from the queue</li>
|
|
21
|
+
<li>Deletes files based on message ID</li>
|
|
22
|
+
<li>Logs all operations</li>
|
|
23
|
+
</ul>
|
|
24
|
+
<h2>Configuration</h2>
|
|
25
|
+
<p>No special configuration required.</p>
|
|
26
|
+
HTML
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Custom CHANGELOG served via method instead of file
|
|
30
|
+
def changelog_as_html
|
|
31
|
+
<<~HTML
|
|
32
|
+
<h1>Changelog</h1>
|
|
33
|
+
<h2>[2.0.0] 2026-01-18</h2><ul>
|
|
34
|
+
<li>Added custom documentation via methods</li>
|
|
35
|
+
<li>Improved error handling</li>
|
|
36
|
+
</ul>
|
|
37
|
+
<h2>[1.0.0] 2025-01-01</h2><ul>
|
|
38
|
+
<li>Initial release</li>
|
|
39
|
+
</ul>
|
|
40
|
+
HTML
|
|
41
|
+
end
|
|
42
|
+
|
|
5
43
|
def handle_message(message, args = {})
|
|
6
44
|
id = message.body["id"]
|
|
7
45
|
EventHub.logger.info("[#{id}] - Received")
|
data/example/router.rb
CHANGED
|
@@ -13,7 +13,8 @@ module EventHub
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def start
|
|
16
|
-
|
|
16
|
+
cycle = Configuration.processor[:heartbeat_cycle_in_s]
|
|
17
|
+
EventHub.logger.info("Heartbeat is starting [cycle: #{cycle}s]...")
|
|
17
18
|
|
|
18
19
|
every(60 * 60 * 24) { EventHub.logger.info("Actual actors: #{Celluloid::Actor.all.size}: #{Celluloid::Actor.all.map { |a| a.class }.join(", ")}") }
|
|
19
20
|
|
|
@@ -16,8 +16,18 @@ module EventHub
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def start
|
|
19
|
-
EventHub.
|
|
20
|
-
EventHub::Configuration.processor[:listener_queues]
|
|
19
|
+
server = EventHub::Configuration.server
|
|
20
|
+
queues = EventHub::Configuration.processor[:listener_queues]
|
|
21
|
+
settings = [
|
|
22
|
+
server[:user],
|
|
23
|
+
server[:host],
|
|
24
|
+
server[:port],
|
|
25
|
+
server[:vhost],
|
|
26
|
+
"tls=#{server[:tls]}",
|
|
27
|
+
queues.join(", ")
|
|
28
|
+
].join(", ")
|
|
29
|
+
EventHub.logger.info("Listener amqp is starting [#{settings}]...")
|
|
30
|
+
queues.each_with_index do |queue_name, index|
|
|
21
31
|
async.listen(queue_name: queue_name, index: index)
|
|
22
32
|
end
|
|
23
33
|
end
|
|
@@ -30,21 +40,26 @@ module EventHub
|
|
|
30
40
|
with_listen(args) do |connection, channel, consumer, queue, queue_name|
|
|
31
41
|
EventHub.logger.info("Listening to queue [#{queue_name}]")
|
|
32
42
|
consumer.on_delivery do |delivery_info, metadata, payload|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
CorrelationId.with(metadata[:correlation_id]) do
|
|
44
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]" \
|
|
45
|
+
" delivery")
|
|
46
|
+
|
|
47
|
+
@processor_instance.statistics.measure(payload.size) do
|
|
48
|
+
handle_payload(payload: payload,
|
|
49
|
+
connection: connection,
|
|
50
|
+
queue_name: queue_name,
|
|
51
|
+
content_type: metadata[:content_type],
|
|
52
|
+
priority: metadata[:priority],
|
|
53
|
+
delivery_tag: delivery_info.delivery_tag,
|
|
54
|
+
correlation_id: metadata[:correlation_id])
|
|
55
|
+
channel.acknowledge(delivery_info.delivery_tag, false)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]" \
|
|
59
|
+
" acknowledged")
|
|
60
|
+
ensure
|
|
61
|
+
ExecutionId.clear
|
|
44
62
|
end
|
|
45
|
-
|
|
46
|
-
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]" \
|
|
47
|
-
" acknowledged")
|
|
48
63
|
end
|
|
49
64
|
queue.subscribe_with(consumer, block: false)
|
|
50
65
|
end
|
|
@@ -77,6 +92,15 @@ module EventHub
|
|
|
77
92
|
# convert to EventHub message
|
|
78
93
|
message = EventHub::Message.from_json(args[:payload])
|
|
79
94
|
|
|
95
|
+
# set execution_id for logging
|
|
96
|
+
ExecutionId.current = message.process_execution_id if message.valid?
|
|
97
|
+
|
|
98
|
+
# use execution_id as correlation_id if not already set from AMQP metadata
|
|
99
|
+
if CorrelationId.current.nil? && message.valid?
|
|
100
|
+
CorrelationId.current = message.process_execution_id
|
|
101
|
+
args[:correlation_id] ||= message.process_execution_id
|
|
102
|
+
end
|
|
103
|
+
|
|
80
104
|
# append to execution history
|
|
81
105
|
message.append_to_execution_history(EventHub::Configuration.name)
|
|
82
106
|
|
|
@@ -105,7 +129,7 @@ module EventHub
|
|
|
105
129
|
end
|
|
106
130
|
|
|
107
131
|
def pass_arguments(args = {})
|
|
108
|
-
keys_to_pass = [:queue_name, :content_type, :priority, :delivery_tag]
|
|
132
|
+
keys_to_pass = [:queue_name, :content_type, :priority, :delivery_tag, :correlation_id]
|
|
109
133
|
args.select { |key| keys_to_pass.include?(key) }
|
|
110
134
|
end
|
|
111
135
|
|