@onlineapps/conn-orch-registry 2.0.2 → 3.0.1
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/package.json +1 -5
- package/src/defaults.js +4 -0
- package/src/registryClient.js +41 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-registry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Connector-registry-client provides the core communication mechanism for microservices in this environment. It enables them to interact with a services_registry to receive and fulfill tasks by submitting heartbeats or their API descriptions.",
|
|
6
6
|
"keywords": [
|
|
@@ -48,10 +48,6 @@
|
|
|
48
48
|
"@semantic-release/npm": "^11.0.3",
|
|
49
49
|
"@semantic-release/release-notes-generator": "^12.1.0",
|
|
50
50
|
"eslint": "^8.44.0",
|
|
51
|
-
"eslint-config-airbnb-base": "^15.0.0",
|
|
52
|
-
"eslint-config-prettier": "^10.1.5",
|
|
53
|
-
"eslint-plugin-import": "^2.30.1",
|
|
54
|
-
"eslint-plugin-prettier": "^5.4.0",
|
|
55
51
|
"ioredis": "^5.8.0",
|
|
56
52
|
"jest": "^29.6.1",
|
|
57
53
|
"semantic-release": "^22.0.12"
|
package/src/defaults.js
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
12
|
+
// Key prefix the registry builds its Redis keys with (its own
|
|
13
|
+
// REDIS_REGISTRY_KEY_PREFIX). Module-owned default, overridable per instance —
|
|
14
|
+
// never a literal inside a method (architecture-principles §2).
|
|
15
|
+
registryKeyPrefix: 'registry:',
|
|
12
16
|
// `specificationEndpoint`, `specProtocol` and `specHost` lived here until
|
|
13
17
|
// 2026-08-29. Their only reader was src/config.js, whose only readers were its
|
|
14
18
|
// own tests — a declaration nothing runtime read, which forced every
|
package/src/registryClient.js
CHANGED
|
@@ -50,6 +50,7 @@ class ServiceRegistryClient extends EventEmitter {
|
|
|
50
50
|
constructor({ amqpUrl, serviceName, version, specificationEndpoint = '/api/v1/specification',
|
|
51
51
|
heartbeatInterval = 10000, apiQueue = 'api_services_queuer', registryQueue = 'registry.register',
|
|
52
52
|
registryUrl = null, redis = null, storageConfig = {}, validationProof = null,
|
|
53
|
+
registryKeyPrefix = DEFAULTS.registryKeyPrefix,
|
|
53
54
|
registrationTimeoutMs = DEFAULTS.registrationTimeoutMs }) {
|
|
54
55
|
super();
|
|
55
56
|
if (!amqpUrl || !serviceName || !version) {
|
|
@@ -77,6 +78,7 @@ class ServiceRegistryClient extends EventEmitter {
|
|
|
77
78
|
// Event consumer (optional, activated via subscribeToChanges)
|
|
78
79
|
this.eventConsumer = null;
|
|
79
80
|
this.redis = redis;
|
|
81
|
+
this.registryKeyPrefix = registryKeyPrefix;
|
|
80
82
|
this.storageConfig = storageConfig;
|
|
81
83
|
|
|
82
84
|
// Validation proof (injected via constructor - DEPENDENCY INJECTION)
|
|
@@ -712,49 +714,63 @@ class ServiceRegistryClient extends EventEmitter {
|
|
|
712
714
|
/**
|
|
713
715
|
* Get basic service state for service discovery.
|
|
714
716
|
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
*
|
|
717
|
+
* Reads the projection the registry already maintains — the `<prefix>services`
|
|
718
|
+
* hash, one JSON summary per service name. **Not HTTP.** The owner decided that
|
|
719
|
+
* on 2026-09-02 (confirmation biz-discovery-redis-001). It is its own decision,
|
|
720
|
+
* not a reading of ADR 0005: that ADR
|
|
721
|
+
* (api/docs/biz/80-decisions/0005-no-http-in-biz-containers.md) bans an HTTP
|
|
722
|
+
* LISTENER in a biz container and is silent on outbound calls — this extends the
|
|
723
|
+
* same reasoning to the calls a biz container makes.
|
|
724
|
+
* Until then this method polled `GET /services`, which is what made every biz
|
|
725
|
+
* container an HTTP client of the registry ~142x a day.
|
|
726
|
+
*
|
|
727
|
+
* There is deliberately no HTTP fallback. A missing Redis client is a wiring
|
|
728
|
+
* error and says so (architecture-principles §3, §4) — falling back would
|
|
729
|
+
* restore the very traffic this change removes, and hide the misconfiguration.
|
|
730
|
+
*
|
|
731
|
+
* cookbook-router expects `{ status }`; the registry writes it lowercase
|
|
732
|
+
* ('active'/'inactive') into the summary, so no normalization happens here.
|
|
718
733
|
*
|
|
719
734
|
* @param {string} serviceName
|
|
720
|
-
* @returns {Promise<{serviceName: string, status:
|
|
735
|
+
* @returns {Promise<{serviceName: string, status: string, version?: string, lastHeartbeatAt?: string} | null>}
|
|
721
736
|
*/
|
|
722
737
|
async getService(serviceName) {
|
|
723
738
|
if (!serviceName || typeof serviceName !== 'string') {
|
|
724
739
|
throw new Error('[RegistryClient] getService - serviceName is required and must be a string');
|
|
725
740
|
}
|
|
726
741
|
|
|
727
|
-
if (!this.
|
|
728
|
-
throw new Error(
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
try {
|
|
736
|
-
response = await fetch(url, { method: 'GET' });
|
|
737
|
-
} catch (err) {
|
|
738
|
-
throw new Error(`[RegistryClient] getService - Registry HTTP request failed (${url}). Problem: ${err.message}`);
|
|
742
|
+
if (!this.redis || typeof this.redis.hGet !== 'function') {
|
|
743
|
+
throw new Error(
|
|
744
|
+
'[RegistryClient] getService - Missing Redis client for service discovery. '
|
|
745
|
+
+ 'Expected/Fix: pass `redis` (a connected client exposing hGet) into the '
|
|
746
|
+
+ 'ServiceRegistryClient constructor; @onlineapps/service-wrapper does this '
|
|
747
|
+
+ 'from its own Redis configuration. Discovery reads the registry projection, '
|
|
748
|
+
+ 'never HTTP (owner decision biz-discovery-redis-001, 2026-09-02).'
|
|
749
|
+
);
|
|
739
750
|
}
|
|
740
751
|
|
|
741
|
-
|
|
742
|
-
|
|
752
|
+
const key = `${this.registryKeyPrefix}services`;
|
|
753
|
+
const raw = await this.redis.hGet(key, serviceName);
|
|
754
|
+
if (!raw) {
|
|
755
|
+
return null;
|
|
743
756
|
}
|
|
744
757
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
758
|
+
let entry;
|
|
759
|
+
try {
|
|
760
|
+
entry = JSON.parse(raw);
|
|
761
|
+
} catch (err) {
|
|
762
|
+
throw new Error(
|
|
763
|
+
`[RegistryClient] getService - Registry projection entry for "${serviceName}" is not valid JSON `
|
|
764
|
+
+ `(key ${key}). Problem: ${err.message}. Fix: the registry owns this value; `
|
|
765
|
+
+ 'a corrupt entry is a registry-side defect, not something a caller may guess around.'
|
|
766
|
+
);
|
|
749
767
|
}
|
|
750
768
|
|
|
751
|
-
// Registry stores status as 'active'/'inactive' (lowercase).
|
|
752
769
|
return {
|
|
753
770
|
serviceName,
|
|
754
771
|
status: entry.status,
|
|
755
772
|
version: entry.version,
|
|
756
|
-
lastHeartbeatAt: entry.lastHeartbeatAt
|
|
757
|
-
isAvailable: entry.isAvailable
|
|
773
|
+
lastHeartbeatAt: entry.lastHeartbeatAt
|
|
758
774
|
};
|
|
759
775
|
}
|
|
760
776
|
|