@onlineapps/service-wrapper 4.1.1 → 4.1.3
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.
- package/CHANGELOG.md +80 -5
- package/package.json +8 -7
- package/src/ConfigLoader.js +7 -1
- package/src/ServiceWrapper.js +88 -50
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,50 @@ All notable changes to this package. Follows [Keep a Changelog](https://keepacha
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Minor.** No exported API moves: `BUSINESS_ERROR_BRAND`, `isBusinessError`, the
|
|
8
8
|
error classes, `ServiceWrapper` and `bootstrap` are untouched. What changes is
|
|
9
|
-
startup behaviour
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
startup behaviour, one field inside `conn-runtime/validation-failure.json` — a
|
|
10
|
+
runtime file this package writes and reads alone — and one field that disappears
|
|
11
|
+
from the loaded config (below).
|
|
12
|
+
|
|
13
|
+
### Removed — the `|| ''` fallback on `specificationEndpoint` (DÁVKA 79 B)
|
|
14
|
+
|
|
15
|
+
`_initializeRegistry` read `this.config.service?.specificationEndpoint || ''`.
|
|
16
|
+
The branch could never fire: `ConfigLoader.js:360-365` refuses a config that does
|
|
17
|
+
not declare the field, and all eight biz services build their config through
|
|
18
|
+
`ConfigLoader.loadAll` (`src/config/index.js` in converter, emailer,
|
|
19
|
+
hello-service, ingest, invoicing, meta, pdfgen, property), with `bootstrap()`
|
|
20
|
+
passing it straight through (`src/index.js:81`).
|
|
21
|
+
|
|
22
|
+
A fallback that cannot fire is not harmless — it tells the reader that an empty
|
|
23
|
+
endpoint is an expected state, while the registry warns about exactly that case
|
|
24
|
+
(`api/infra/api_services_registry/src/listeners/registry.listener.js:474-477`).
|
|
25
|
+
`architecture-principles.md` §3.
|
|
26
|
+
|
|
27
|
+
Behaviour is unchanged, which is the point: the new control suite
|
|
28
|
+
`tests/unit/ServiceWrapper.specificationEndpoint.test.js` is green before and
|
|
29
|
+
after, and asserts the value reaching the registry client verbatim.
|
|
30
|
+
|
|
31
|
+
### Removed — `config.service.port` is no longer parsed (DÁVKA 77 B)
|
|
32
|
+
|
|
33
|
+
ADR 0005 removed the HTTP surface from biz containers. `ConfigLoader` parsed
|
|
34
|
+
`service.port` into the loaded config anyway, and nothing read it back: measured
|
|
35
|
+
2026-08-30, the only `.port` reads in `src/` are the Redis/state URL ports at
|
|
36
|
+
`ServiceWrapper.js:1495`, `:1525`, `:1530`. A declaration nothing reads tells the
|
|
37
|
+
reader something depends on it — `change-discipline.md` § "Removing something
|
|
38
|
+
removes its declaration".
|
|
39
|
+
|
|
40
|
+
- `cfg.service.port` is gone. A config that still declares one loads fine; the
|
|
41
|
+
field is simply not carried forward. `conn-orch-validator` is the component
|
|
42
|
+
that tells the author to delete it (`ORPHANED_CONFIG_FIELD`, warning).
|
|
43
|
+
- `service.specificationEndpoint` stays REQUIRED and is UNCHANGED. It looked like
|
|
44
|
+
the same class of orphan — `ServiceWrapper.js:1295` even says ADR 0005 left it
|
|
45
|
+
without a consumer — but it has one: `api_services_registry` reads it off the
|
|
46
|
+
register payload (`listeners/registry.listener.js:354`, `:474-477`, `:542`,
|
|
47
|
+
`:626`, `:730`, `:813`, `:1108`) and stores it in the Redis summary entry
|
|
48
|
+
(`models/microservice.model.js:166-177`); the heartbeat listener reads it too
|
|
49
|
+
(`listeners/heartbeat.listener.js:30`, `:49`). The comment at `:1295` is wrong
|
|
50
|
+
about that, and the requirement stands.
|
|
12
51
|
|
|
13
52
|
### Fixed — the restart cooldown is keyed on the CAUSE, not on time alone
|
|
14
53
|
|
|
@@ -83,8 +122,44 @@ failure — measured by the lead: five minutes, no output. The test now records
|
|
|
83
122
|
the scheduling instead of arming it, and asserts the success path never reaches
|
|
84
123
|
the scheduler at all. Under the mutation it fails in 2 ms.
|
|
85
124
|
|
|
125
|
+
### Fixed — one owner for "who am I"
|
|
126
|
+
|
|
127
|
+
`this.config.service?.name || 'unnamed-service'` stood in **thirteen** places.
|
|
128
|
+
Each looked harmless; together they were a policy nobody decided — a wrapper with
|
|
129
|
+
no configured name kept running and told the registry, the MQ layer, the
|
|
130
|
+
heartbeat and every log line that it was `unnamed-service`. Not a missing name: a
|
|
131
|
+
WRONG one, and the same defect the heartbeat fix above removed, twelve more
|
|
132
|
+
times (`architecture-principles.md` §3).
|
|
133
|
+
|
|
134
|
+
`_serviceName()` and `_serviceVersion()` are now the only readers and they fail
|
|
135
|
+
fast with `[ServiceWrapper] Missing configuration - service.<key> is required` —
|
|
136
|
+
a message that is defined exactly once. Three duplicate guards that said the same
|
|
137
|
+
thing in their own words (`_initializeOrchestrator`, `startHealthHeartbeat`,
|
|
138
|
+
`_ensureValidationProof`'s "Service name is required for validation") now defer
|
|
139
|
+
to it.
|
|
140
|
+
|
|
141
|
+
Two readers stay direct, and each is named in a test:
|
|
142
|
+
|
|
143
|
+
- `_logPhase` — **the only path that may not throw.**
|
|
144
|
+
`_handleInitializationError` reports a failed phase through it, and a phase can
|
|
145
|
+
fail precisely because the configuration never loaded; throwing there would
|
|
146
|
+
replace the error being reported with a second one and the operator would never
|
|
147
|
+
see the first. It uses `this.config?.service?.name ?? '<config not loaded>'` —
|
|
148
|
+
not an invented identity but a statement ABOUT the configuration, in a message
|
|
149
|
+
whose subject is the configuration failing.
|
|
150
|
+
- `_initializeState` — `state.serviceName || service.name` is a different
|
|
151
|
+
contract; its error names both keys.
|
|
152
|
+
|
|
153
|
+
`service.url || ''` is unchanged: ADR 0005 says a biz service has no URL.
|
|
154
|
+
|
|
86
155
|
### Tests
|
|
87
156
|
|
|
157
|
+
`tests/unit/ServiceWrapper.serviceIdentity.test.js` — 14 cases: the getters
|
|
158
|
+
return and throw by value; the initialization-error path keeps BOTH facts (the
|
|
159
|
+
original cause and that the name was unknown); `unnamed-service` appears nowhere
|
|
160
|
+
in the code; exactly three direct readers, each identified; each message defined
|
|
161
|
+
once; and a control that the ADR 0005 url default survives.
|
|
162
|
+
|
|
88
163
|
`tests/unit/ServiceWrapper.validationFailureFile.test.js` — 18 cases: the
|
|
89
164
|
fingerprint moves for a cookbook, for `operations.json` and for an added
|
|
90
165
|
cookbook and NOT for an unrelated file; the three cooldown branches; the
|
|
@@ -101,7 +176,7 @@ made-up identity, a complete identity starting it under its real name, the older
|
|
|
101
176
|
`mqClient` guard still firing first, and the retired default absent from the
|
|
102
177
|
source.
|
|
103
178
|
|
|
104
|
-
Suite:
|
|
179
|
+
Suite: 17 suites, 228 passed (was 189).
|
|
105
180
|
|
|
106
181
|
## [3.4.3] — 2026-08-17
|
|
107
182
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,24 +25,25 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@onlineapps/conn-base-cache": "1.0.10",
|
|
28
|
-
"@onlineapps/conn-base-monitoring": "1.0.
|
|
28
|
+
"@onlineapps/conn-base-monitoring": "1.0.17",
|
|
29
29
|
"@onlineapps/conn-base-state": "1.0.2",
|
|
30
30
|
"@onlineapps/conn-infra-error-handler": "1.0.15",
|
|
31
|
-
"@onlineapps/conn-infra-mq": "2.0.
|
|
31
|
+
"@onlineapps/conn-infra-mq": "2.0.1",
|
|
32
32
|
"@onlineapps/conn-infra-secrets": "1.1.0",
|
|
33
33
|
"@onlineapps/conn-orch-cookbook": "2.1.5",
|
|
34
|
-
"@onlineapps/conn-orch-orchestrator": "2.1.
|
|
35
|
-
"@onlineapps/conn-orch-registry": "2.0.
|
|
34
|
+
"@onlineapps/conn-orch-orchestrator": "2.1.9",
|
|
35
|
+
"@onlineapps/conn-orch-registry": "2.0.1",
|
|
36
36
|
"@onlineapps/conn-orch-validator": "4.0.1",
|
|
37
|
-
"@onlineapps/infrastructure-tools": "1.2.
|
|
37
|
+
"@onlineapps/infrastructure-tools": "1.2.9",
|
|
38
38
|
"@onlineapps/runtime-config": "1.0.3",
|
|
39
|
-
"@onlineapps/service-common": "2.0.
|
|
39
|
+
"@onlineapps/service-common": "2.0.1",
|
|
40
40
|
"@onlineapps/service-validator-core": "1.0.15",
|
|
41
41
|
"ajv": "8.17.1",
|
|
42
42
|
"ajv-formats": "3.0.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"express": "^5.1.0",
|
|
46
|
+
"ioredis": "^5.3.2",
|
|
46
47
|
"jest": "^29.7.0",
|
|
47
48
|
"jsdoc": "^4.0.2",
|
|
48
49
|
"jsdoc-to-markdown": "^8.0.0"
|
package/src/ConfigLoader.js
CHANGED
|
@@ -368,7 +368,13 @@ class ConfigLoader {
|
|
|
368
368
|
service: {
|
|
369
369
|
name: serviceConfig.service.name,
|
|
370
370
|
version: serviceConfig.service.version,
|
|
371
|
-
port
|
|
371
|
+
// No `port`. ADR 0005 removed the HTTP surface from biz containers, so
|
|
372
|
+
// nothing listens and nothing read this value back — measured
|
|
373
|
+
// 2026-08-30. Parsing it into the loaded config only told the reader
|
|
374
|
+
// that something depends on it (`change-discipline.md` § Removing
|
|
375
|
+
// something removes its declaration). A config that still declares one
|
|
376
|
+
// loads fine; `conn-orch-validator` is what tells the author to delete
|
|
377
|
+
// the field.
|
|
372
378
|
url: serviceConfig.service.url,
|
|
373
379
|
specificationEndpoint: serviceConfig.service.specificationEndpoint,
|
|
374
380
|
description: serviceConfig.service.description,
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -343,7 +343,7 @@ class ServiceWrapper {
|
|
|
343
343
|
* @private
|
|
344
344
|
*/
|
|
345
345
|
_validateNamingAndWorkspaceScoped() {
|
|
346
|
-
const serviceName = this.
|
|
346
|
+
const serviceName = this._serviceName();
|
|
347
347
|
const kebabRegex = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
348
348
|
|
|
349
349
|
// Rule 1: every operation key must be kebab-case
|
|
@@ -606,12 +606,55 @@ class ServiceWrapper {
|
|
|
606
606
|
* Initialize all wrapper components
|
|
607
607
|
* @async
|
|
608
608
|
*/
|
|
609
|
+
/**
|
|
610
|
+
* Who this wrapper is. THE reader of `config.service.name`.
|
|
611
|
+
*
|
|
612
|
+
* Thirteen places used to write `this.config.service?.name ||
|
|
613
|
+
* 'unnamed-service'`. Each looked harmless; together they were a policy
|
|
614
|
+
* nobody decided — a wrapper with no configured name kept running and told
|
|
615
|
+
* the registry, the MQ layer, the heartbeat and every log line that it was
|
|
616
|
+
* `unnamed-service`. That is not a missing name, it is a WRONG one, and it is
|
|
617
|
+
* the same defect dávka 62 removed from `startHealthHeartbeat`
|
|
618
|
+
* (architecture-principles.md §3).
|
|
619
|
+
*
|
|
620
|
+
* @private
|
|
621
|
+
* @returns {string}
|
|
622
|
+
*/
|
|
623
|
+
_serviceName() {
|
|
624
|
+
const name = this.config?.service?.name;
|
|
625
|
+
if (!name) {
|
|
626
|
+
throw new Error('[ServiceWrapper] Missing configuration - service.name is required');
|
|
627
|
+
}
|
|
628
|
+
return name;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* The version this wrapper reports. THE reader of `config.service.version`.
|
|
633
|
+
*
|
|
634
|
+
* @private
|
|
635
|
+
* @returns {string}
|
|
636
|
+
*/
|
|
637
|
+
_serviceVersion() {
|
|
638
|
+
const version = this.config?.service?.version;
|
|
639
|
+
if (!version) {
|
|
640
|
+
throw new Error('[ServiceWrapper] Missing configuration - service.version is required');
|
|
641
|
+
}
|
|
642
|
+
return version;
|
|
643
|
+
}
|
|
644
|
+
|
|
609
645
|
/**
|
|
610
646
|
* Log phase with transparent format
|
|
611
647
|
* @private
|
|
612
648
|
*/
|
|
613
649
|
_logPhase(phase, phaseName, status, error = null, duration = null) {
|
|
614
|
-
|
|
650
|
+
// THE ONE PLACE that does not use _serviceName(), and the only one that may
|
|
651
|
+
// not. `_handleInitializationError` reports a failed phase through here, and
|
|
652
|
+
// a phase can fail precisely because the configuration never loaded — so a
|
|
653
|
+
// throw would replace the error being reported with a second one and the
|
|
654
|
+
// operator would never see the first. `<config not loaded>` is not an
|
|
655
|
+
// invented identity: it is a statement ABOUT the configuration, in a message
|
|
656
|
+
// whose subject is the configuration failing.
|
|
657
|
+
const serviceName = this.config?.service?.name ?? '<config not loaded>';
|
|
615
658
|
const logMsg = `[FÁZE ${phase}] ${phaseName} - ${status}`;
|
|
616
659
|
const logData = {
|
|
617
660
|
phase,
|
|
@@ -651,7 +694,7 @@ class ServiceWrapper {
|
|
|
651
694
|
* @private
|
|
652
695
|
*/
|
|
653
696
|
async _cleanupBeforeRestart() {
|
|
654
|
-
const serviceName = this.
|
|
697
|
+
const serviceName = this._serviceName();
|
|
655
698
|
console.log(`[CLEANUP] Starting cleanup for ${serviceName} before restart...`);
|
|
656
699
|
|
|
657
700
|
try {
|
|
@@ -800,7 +843,7 @@ class ServiceWrapper {
|
|
|
800
843
|
return;
|
|
801
844
|
}
|
|
802
845
|
|
|
803
|
-
const serviceName = this.
|
|
846
|
+
const serviceName = this._serviceName();
|
|
804
847
|
const startTime = Date.now();
|
|
805
848
|
|
|
806
849
|
try {
|
|
@@ -971,7 +1014,7 @@ class ServiceWrapper {
|
|
|
971
1014
|
* @private
|
|
972
1015
|
*/
|
|
973
1016
|
async _initializeMonitoring() {
|
|
974
|
-
const serviceName = this.
|
|
1017
|
+
const serviceName = this._serviceName();
|
|
975
1018
|
const fs = require('fs');
|
|
976
1019
|
const path = require('path');
|
|
977
1020
|
|
|
@@ -1128,7 +1171,7 @@ class ServiceWrapper {
|
|
|
1128
1171
|
throw new Error('MQ URL is required when MQ is enabled. Set wrapper.mq.url in config');
|
|
1129
1172
|
}
|
|
1130
1173
|
|
|
1131
|
-
const serviceName = this.
|
|
1174
|
+
const serviceName = this._serviceName();
|
|
1132
1175
|
|
|
1133
1176
|
// Health monitoring and channel close hooks configuration
|
|
1134
1177
|
// NOTE: Health monitoring is NOT run periodically - it's triggered manually before workflow steps in debug mode
|
|
@@ -1232,10 +1275,7 @@ class ServiceWrapper {
|
|
|
1232
1275
|
throw new Error('Registry URL is required when registry is enabled. Set wrapper.registry.url in config');
|
|
1233
1276
|
}
|
|
1234
1277
|
|
|
1235
|
-
const serviceName = this.
|
|
1236
|
-
if (!serviceName) {
|
|
1237
|
-
throw new Error('[ServiceWrapper] Missing configuration - service.name is required');
|
|
1238
|
-
}
|
|
1278
|
+
const serviceName = this._serviceName();
|
|
1239
1279
|
|
|
1240
1280
|
// ADR 0005: biz has no HTTP — no port, no URL. Kept in serviceInfo
|
|
1241
1281
|
// payload as empty string for Registry-side backward compat with
|
|
@@ -1252,17 +1292,26 @@ class ServiceWrapper {
|
|
|
1252
1292
|
// FÁZE 0.5 a 0.6: Vytvoření front a spuštění konzumerů se provádí v registryClient.init()
|
|
1253
1293
|
// FÁZE 0.7: Registrace u Registry
|
|
1254
1294
|
|
|
1255
|
-
//
|
|
1256
|
-
//
|
|
1257
|
-
//
|
|
1258
|
-
//
|
|
1259
|
-
|
|
1295
|
+
// specificationEndpoint HAS a consumer, despite ADR 0005 leaving no HTTP
|
|
1296
|
+
// surface behind it. The registry destructures it off this register payload
|
|
1297
|
+
// (api/infra/api_services_registry/src/listeners/registry.listener.js:354),
|
|
1298
|
+
// warns when it is empty (:474-477), passes it into every registerFull call
|
|
1299
|
+
// (:542, :626, :730, :813, :1108), and the model writes it into the Redis
|
|
1300
|
+
// summary entry (src/models/microservice.model.js:166-177). The heartbeat
|
|
1301
|
+
// listener reads it too (src/listeners/heartbeat.listener.js:30, :49).
|
|
1302
|
+
// This comment claimed the opposite until 2026-08-30; measured, it was wrong.
|
|
1303
|
+
// ConfigLoader.js:360-365 therefore keeps the field REQUIRED, and every biz
|
|
1304
|
+
// service builds its config through it (measured 2026-08-30: all eight call
|
|
1305
|
+
// ConfigLoader.loadAll from src/config/index.js). A `|| ''` used to sit here;
|
|
1306
|
+
// it could never fire, and a fallback that cannot fire still tells the
|
|
1307
|
+
// reader an empty endpoint is an expected state (architecture-principles §3).
|
|
1308
|
+
const specificationEndpoint = this.config.service.specificationEndpoint;
|
|
1260
1309
|
|
|
1261
1310
|
// Logger may not be initialized yet at this point - RegistryClient handles null gracefully
|
|
1262
1311
|
this.registryClient = new RegistryConnector.ServiceRegistryClient({
|
|
1263
1312
|
amqpUrl: mqUrl,
|
|
1264
1313
|
serviceName: serviceName,
|
|
1265
|
-
version: this.
|
|
1314
|
+
version: this._serviceVersion(),
|
|
1266
1315
|
specificationEndpoint: specificationEndpoint,
|
|
1267
1316
|
registryQueue: 'registry.register', // Use correct queue name
|
|
1268
1317
|
registryUrl: registryUrl,
|
|
@@ -1285,7 +1334,7 @@ class ServiceWrapper {
|
|
|
1285
1334
|
operations: this.operations?.operations || {},
|
|
1286
1335
|
workspaceScoped: this.config.service?.workspaceScoped,
|
|
1287
1336
|
metadata: {
|
|
1288
|
-
version: this.
|
|
1337
|
+
version: this._serviceVersion(),
|
|
1289
1338
|
description: this.config.service?.description || ''
|
|
1290
1339
|
}
|
|
1291
1340
|
};
|
|
@@ -1445,7 +1494,7 @@ class ServiceWrapper {
|
|
|
1445
1494
|
return;
|
|
1446
1495
|
}
|
|
1447
1496
|
|
|
1448
|
-
const serviceName = this.
|
|
1497
|
+
const serviceName = this._serviceName();
|
|
1449
1498
|
|
|
1450
1499
|
let host = cacheUrl;
|
|
1451
1500
|
let port = 6379;
|
|
@@ -1599,14 +1648,8 @@ class ServiceWrapper {
|
|
|
1599
1648
|
// (below), but it runs only when serviceRoot is set and
|
|
1600
1649
|
// wrapper.validation.enabled !== false — a service with validation off used
|
|
1601
1650
|
// to arrive here with neither key ever checked.
|
|
1602
|
-
const serviceName = this.
|
|
1603
|
-
|
|
1604
|
-
throw new Error('[ServiceWrapper] Missing configuration - service.name is required');
|
|
1605
|
-
}
|
|
1606
|
-
const serviceVersion = this.config.service?.version;
|
|
1607
|
-
if (!serviceVersion) {
|
|
1608
|
-
throw new Error('[ServiceWrapper] Missing configuration - service.version is required');
|
|
1609
|
-
}
|
|
1651
|
+
const serviceName = this._serviceName();
|
|
1652
|
+
const serviceVersion = this._serviceVersion();
|
|
1610
1653
|
|
|
1611
1654
|
const { createBaseClientAdapter } = require('@onlineapps/infrastructure-tools');
|
|
1612
1655
|
|
|
@@ -1724,17 +1767,13 @@ class ServiceWrapper {
|
|
|
1724
1767
|
async _initializeOrchestrator() {
|
|
1725
1768
|
assertConnOrchOrchestratorV2Contract();
|
|
1726
1769
|
|
|
1727
|
-
|
|
1728
|
-
|
|
1770
|
+
// Both guards moved into the getters — one owner for the contract and for
|
|
1771
|
+
// its wording (change-discipline.md § one fact, one owner).
|
|
1772
|
+
const serviceName = this._serviceName();
|
|
1773
|
+
const serviceVersion = this._serviceVersion();
|
|
1729
1774
|
const environment = this.config.service?.env;
|
|
1730
1775
|
const serviceUrl = this.config.service?.url;
|
|
1731
1776
|
|
|
1732
|
-
if (!serviceName) {
|
|
1733
|
-
throw new Error('[ServiceWrapper] Missing configuration - service.name is required');
|
|
1734
|
-
}
|
|
1735
|
-
if (!serviceVersion) {
|
|
1736
|
-
throw new Error('[ServiceWrapper] Missing configuration - service.version is required');
|
|
1737
|
-
}
|
|
1738
1777
|
if (!environment) {
|
|
1739
1778
|
throw new Error('[ServiceWrapper] Missing configuration - service.env (NODE_ENV) is required');
|
|
1740
1779
|
}
|
|
@@ -1815,7 +1854,7 @@ class ServiceWrapper {
|
|
|
1815
1854
|
|
|
1816
1855
|
const queueName = 'workflow.init';
|
|
1817
1856
|
this.logger?.info(`[ServiceWrapper] [CONSUMER] Starting workflow.init listener for queue: ${queueName}`);
|
|
1818
|
-
this.logger?.info(`[ServiceWrapper] [CONSUMER] Service: ${this.
|
|
1857
|
+
this.logger?.info(`[ServiceWrapper] [CONSUMER] Service: ${this._serviceName()}`);
|
|
1819
1858
|
this.logger?.info(`[ServiceWrapper] [CONSUMER] Timestamp: ${new Date().toISOString()}`);
|
|
1820
1859
|
|
|
1821
1860
|
try {
|
|
@@ -1847,7 +1886,7 @@ class ServiceWrapper {
|
|
|
1847
1886
|
|
|
1848
1887
|
const queueName = 'workflow.control';
|
|
1849
1888
|
this.logger?.info(`[ServiceWrapper] [CONSUMER] Starting workflow.control listener for queue: ${queueName}`);
|
|
1850
|
-
this.logger?.info(`[ServiceWrapper] [CONSUMER] Service: ${this.
|
|
1889
|
+
this.logger?.info(`[ServiceWrapper] [CONSUMER] Service: ${this._serviceName()}`);
|
|
1851
1890
|
this.logger?.info(`[ServiceWrapper] [CONSUMER] Timestamp: ${new Date().toISOString()}`);
|
|
1852
1891
|
|
|
1853
1892
|
try {
|
|
@@ -1876,7 +1915,7 @@ class ServiceWrapper {
|
|
|
1876
1915
|
throw new Error('[ServiceWrapper] Cannot start service workflow listener — MQ client not initialized. Queue infrastructure is required.');
|
|
1877
1916
|
}
|
|
1878
1917
|
|
|
1879
|
-
const serviceName = this.
|
|
1918
|
+
const serviceName = this._serviceName();
|
|
1880
1919
|
const serviceQueue = `${serviceName}.workflow`;
|
|
1881
1920
|
|
|
1882
1921
|
this.logger?.info(`[ServiceWrapper] [CONSUMER] Starting service-specific workflow listener for queue: ${serviceQueue}`);
|
|
@@ -1984,7 +2023,7 @@ class ServiceWrapper {
|
|
|
1984
2023
|
});
|
|
1985
2024
|
|
|
1986
2025
|
// Check if this message is for our service
|
|
1987
|
-
const serviceName = this.
|
|
2026
|
+
const serviceName = this._serviceName();
|
|
1988
2027
|
if (message.step?.service && message.step.service !== serviceName) {
|
|
1989
2028
|
this.logger?.debug(`Message not for this service (target: ${message.step.service})`);
|
|
1990
2029
|
return;
|
|
@@ -2406,8 +2445,8 @@ class ServiceWrapper {
|
|
|
2406
2445
|
});
|
|
2407
2446
|
|
|
2408
2447
|
try {
|
|
2409
|
-
const serviceName = this.
|
|
2410
|
-
const serviceVersion = this.
|
|
2448
|
+
const serviceName = this._serviceName();
|
|
2449
|
+
const serviceVersion = this._serviceVersion();
|
|
2411
2450
|
const serviceUrl = this.config.service?.url;
|
|
2412
2451
|
|
|
2413
2452
|
const orchestrator = this._createValidationOrchestrator();
|
|
@@ -2599,8 +2638,8 @@ class ServiceWrapper {
|
|
|
2599
2638
|
// INTENTIONAL FALLBACK: bootstrap logger — see docs/standards/FALLBACKS_INVENTORY.md §5.1
|
|
2600
2639
|
return new ValidationOrchestrator({
|
|
2601
2640
|
serviceRoot: this.serviceRoot,
|
|
2602
|
-
serviceName: this.
|
|
2603
|
-
serviceVersion: this.
|
|
2641
|
+
serviceName: this._serviceName(),
|
|
2642
|
+
serviceVersion: this._serviceVersion(),
|
|
2604
2643
|
serviceUrl: this.config.service?.url || '',
|
|
2605
2644
|
logger: this.logger || console
|
|
2606
2645
|
});
|
|
@@ -2684,7 +2723,7 @@ class ServiceWrapper {
|
|
|
2684
2723
|
* @async
|
|
2685
2724
|
*/
|
|
2686
2725
|
async shutdown() {
|
|
2687
|
-
const serviceName = this.
|
|
2726
|
+
const serviceName = this._serviceName();
|
|
2688
2727
|
this.logger?.info(`Shutting down ServiceWrapper for ${serviceName}`);
|
|
2689
2728
|
|
|
2690
2729
|
try {
|
|
@@ -2774,12 +2813,11 @@ class ServiceWrapper {
|
|
|
2774
2813
|
* @private
|
|
2775
2814
|
*/
|
|
2776
2815
|
async _ensureValidationProof() {
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
}
|
|
2816
|
+
// The two guards this method used to carry said the same thing in different
|
|
2817
|
+
// words ("Service name is required for validation"). One contract, one
|
|
2818
|
+
// wording — the getters own both.
|
|
2819
|
+
this._serviceName();
|
|
2820
|
+
this._serviceVersion();
|
|
2783
2821
|
|
|
2784
2822
|
// ADR 0005: biz services have no HTTP surface — no port to bind, no URL to
|
|
2785
2823
|
// advertise. The two guards above are the contract; the values themselves
|
|
@@ -2848,7 +2886,7 @@ class ServiceWrapper {
|
|
|
2848
2886
|
getStatus() {
|
|
2849
2887
|
return {
|
|
2850
2888
|
initialized: this.isInitialized,
|
|
2851
|
-
service: this.
|
|
2889
|
+
service: this._serviceName(),
|
|
2852
2890
|
components: {
|
|
2853
2891
|
mq: this.mqClient?.isConnected() || false,
|
|
2854
2892
|
registry: !!this.registryClient,
|