@onlineapps/conn-orch-registry 2.0.2-rc.1 → 3.0.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.
- package/package.json +1 -1
- package/src/defaults.js +4 -0
- package/src/registryClient.js +39 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-registry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
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": [
|
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,61 @@ 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.** A biz container runs no
|
|
719
|
+
* HTTP listener and makes no HTTP call to the registry either: ADR 0005
|
|
720
|
+
* (api/docs/biz/80-decisions/0005-no-http-in-biz-containers.md), applied to
|
|
721
|
+
* discovery by the owner on 2026-09-02 (confirmation biz-discovery-redis-001).
|
|
722
|
+
* Until then this method polled `GET /services`, which is what made every biz
|
|
723
|
+
* container an HTTP client of the registry ~142x a day.
|
|
724
|
+
*
|
|
725
|
+
* There is deliberately no HTTP fallback. A missing Redis client is a wiring
|
|
726
|
+
* error and says so (architecture-principles §3, §4) — falling back would
|
|
727
|
+
* restore the very traffic this change removes, and hide the misconfiguration.
|
|
728
|
+
*
|
|
729
|
+
* cookbook-router expects `{ status }`; the registry writes it lowercase
|
|
730
|
+
* ('active'/'inactive') into the summary, so no normalization happens here.
|
|
718
731
|
*
|
|
719
732
|
* @param {string} serviceName
|
|
720
|
-
* @returns {Promise<{serviceName: string, status:
|
|
733
|
+
* @returns {Promise<{serviceName: string, status: string, version?: string, lastHeartbeatAt?: string} | null>}
|
|
721
734
|
*/
|
|
722
735
|
async getService(serviceName) {
|
|
723
736
|
if (!serviceName || typeof serviceName !== 'string') {
|
|
724
737
|
throw new Error('[RegistryClient] getService - serviceName is required and must be a string');
|
|
725
738
|
}
|
|
726
739
|
|
|
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}`);
|
|
740
|
+
if (!this.redis || typeof this.redis.hGet !== 'function') {
|
|
741
|
+
throw new Error(
|
|
742
|
+
'[RegistryClient] getService - Missing Redis client for service discovery. '
|
|
743
|
+
+ 'Expected/Fix: pass `redis` (a connected client exposing hGet) into the '
|
|
744
|
+
+ 'ServiceRegistryClient constructor; @onlineapps/service-wrapper does this '
|
|
745
|
+
+ 'from its own Redis configuration. Discovery reads the registry projection, '
|
|
746
|
+
+ 'never HTTP (ADR 0005).'
|
|
747
|
+
);
|
|
739
748
|
}
|
|
740
749
|
|
|
741
|
-
|
|
742
|
-
|
|
750
|
+
const key = `${this.registryKeyPrefix}services`;
|
|
751
|
+
const raw = await this.redis.hGet(key, serviceName);
|
|
752
|
+
if (!raw) {
|
|
753
|
+
return null;
|
|
743
754
|
}
|
|
744
755
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
756
|
+
let entry;
|
|
757
|
+
try {
|
|
758
|
+
entry = JSON.parse(raw);
|
|
759
|
+
} catch (err) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
`[RegistryClient] getService - Registry projection entry for "${serviceName}" is not valid JSON `
|
|
762
|
+
+ `(key ${key}). Problem: ${err.message}. Fix: the registry owns this value; `
|
|
763
|
+
+ 'a corrupt entry is a registry-side defect, not something a caller may guess around.'
|
|
764
|
+
);
|
|
749
765
|
}
|
|
750
766
|
|
|
751
|
-
// Registry stores status as 'active'/'inactive' (lowercase).
|
|
752
767
|
return {
|
|
753
768
|
serviceName,
|
|
754
769
|
status: entry.status,
|
|
755
770
|
version: entry.version,
|
|
756
|
-
lastHeartbeatAt: entry.lastHeartbeatAt
|
|
757
|
-
isAvailable: entry.isAvailable
|
|
771
|
+
lastHeartbeatAt: entry.lastHeartbeatAt
|
|
758
772
|
};
|
|
759
773
|
}
|
|
760
774
|
|