@diia-inhouse/diia-queue 13.3.4 → 14.0.11
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/dist/constants.js +9 -9
- package/dist/index.d.ts +27 -0
- package/dist/index.js +23 -21
- package/dist/interfaces/deps.d.ts +30 -0
- package/dist/interfaces/externalCommunicator.d.ts +58 -0
- package/dist/interfaces/index.d.ts +95 -0
- package/dist/interfaces/index.js +7 -30
- package/dist/interfaces/messageBrokerServiceConfig.d.ts +80 -0
- package/dist/interfaces/messageBrokerServiceConfig.js +27 -24
- package/dist/interfaces/messageHandler.d.ts +6 -0
- package/dist/interfaces/metrics/index.d.ts +23 -0
- package/dist/interfaces/metrics/index.js +12 -16
- package/dist/interfaces/options.d.ts +52 -0
- package/dist/interfaces/providers/rabbitmq/amqpConnection.d.ts +33 -0
- package/dist/interfaces/providers/rabbitmq/amqpConnection.js +14 -17
- package/dist/interfaces/providers/rabbitmq/amqpPublisher.d.ts +20 -0
- package/dist/interfaces/providers/rabbitmq/index.d.ts +107 -0
- package/dist/interfaces/providers/rabbitmq/index.js +35 -47
- package/dist/interfaces/queueConfig/configs.d.ts +47 -0
- package/dist/interfaces/queueConfig/configs.js +8 -9
- package/dist/interfaces/queueConfig/index.d.ts +1 -0
- package/dist/interfaces/queueContext.d.ts +8 -0
- package/dist/interfaces/queueStatus.d.ts +15 -0
- package/dist/interfaces/queueStatus.js +8 -9
- package/dist/metrics/index.js +5 -8
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +6 -22
- package/dist/providers/rabbitmq/amqpAsserter.d.ts +53 -0
- package/dist/providers/rabbitmq/amqpAsserter.js +369 -416
- package/dist/providers/rabbitmq/amqpConnection.d.ts +24 -0
- package/dist/providers/rabbitmq/amqpConnection.js +97 -150
- package/dist/providers/rabbitmq/amqpListener.d.ts +47 -0
- package/dist/providers/rabbitmq/amqpListener.js +218 -225
- package/dist/providers/rabbitmq/amqpPublisher.d.ts +38 -0
- package/dist/providers/rabbitmq/amqpPublisher.js +184 -191
- package/dist/providers/rabbitmq/index.d.ts +56 -0
- package/dist/providers/rabbitmq/index.js +161 -186
- package/dist/services/communicator.d.ts +66 -0
- package/dist/services/communicator.js +164 -186
- package/dist/services/eventBus.d.ts +25 -0
- package/dist/services/eventBus.js +45 -57
- package/dist/services/eventCommunicator.js +75 -124
- package/dist/services/eventMessageHandler.d.ts +27 -0
- package/dist/services/eventMessageHandler.js +124 -129
- package/dist/services/eventMessageValidator.d.ts +13 -0
- package/dist/services/eventMessageValidator.js +59 -44
- package/dist/services/externalCommunicator.d.ts +114 -0
- package/dist/services/externalCommunicator.js +149 -140
- package/dist/services/externalEventBus.d.ts +39 -0
- package/dist/services/externalEventBus.js +144 -162
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.js +10 -26
- package/dist/services/metrics.d.ts +15 -0
- package/dist/services/metrics.js +46 -49
- package/dist/services/optionsBuilder.d.ts +14 -0
- package/dist/services/optionsBuilder.js +43 -58
- package/dist/services/queue.d.ts +29 -0
- package/dist/services/queue.js +78 -93
- package/dist/services/scheduledTask.d.ts +30 -0
- package/dist/services/scheduledTask.js +60 -68
- package/dist/services/task.d.ts +33 -0
- package/dist/services/task.js +160 -176
- package/dist/utils.js +7 -11
- package/package.json +44 -50
- package/dist/constants.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/deps.js +0 -3
- package/dist/interfaces/deps.js.map +0 -1
- package/dist/interfaces/externalCommunicator.js +0 -3
- package/dist/interfaces/externalCommunicator.js.map +0 -1
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/messageBrokerServiceConfig.js.map +0 -1
- package/dist/interfaces/messageHandler.js +0 -3
- package/dist/interfaces/messageHandler.js.map +0 -1
- package/dist/interfaces/metrics/index.js.map +0 -1
- package/dist/interfaces/options.js +0 -3
- package/dist/interfaces/options.js.map +0 -1
- package/dist/interfaces/providers/rabbitmq/amqpConnection.js.map +0 -1
- package/dist/interfaces/providers/rabbitmq/amqpPublisher.js +0 -4
- package/dist/interfaces/providers/rabbitmq/amqpPublisher.js.map +0 -1
- package/dist/interfaces/providers/rabbitmq/index.js.map +0 -1
- package/dist/interfaces/queueConfig/configs.js.map +0 -1
- package/dist/interfaces/queueConfig/index.js +0 -18
- package/dist/interfaces/queueConfig/index.js.map +0 -1
- package/dist/interfaces/queueContext.js +0 -3
- package/dist/interfaces/queueContext.js.map +0 -1
- package/dist/interfaces/queueStatus.js.map +0 -1
- package/dist/interfaces/services/eventMessageHandler.js +0 -3
- package/dist/interfaces/services/eventMessageHandler.js.map +0 -1
- package/dist/metrics/index.js.map +0 -1
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/rabbitmq/amqpAsserter.js.map +0 -1
- package/dist/providers/rabbitmq/amqpConnection.js.map +0 -1
- package/dist/providers/rabbitmq/amqpListener.js.map +0 -1
- package/dist/providers/rabbitmq/amqpPublisher.js.map +0 -1
- package/dist/providers/rabbitmq/index.js.map +0 -1
- package/dist/services/communicator.js.map +0 -1
- package/dist/services/eventBus.js.map +0 -1
- package/dist/services/eventCommunicator.js.map +0 -1
- package/dist/services/eventMessageHandler.js.map +0 -1
- package/dist/services/eventMessageValidator.js.map +0 -1
- package/dist/services/externalCommunicator.js.map +0 -1
- package/dist/services/externalEventBus.js.map +0 -1
- package/dist/services/index.js.map +0 -1
- package/dist/services/metrics.js.map +0 -1
- package/dist/services/optionsBuilder.js.map +0 -1
- package/dist/services/queue.js.map +0 -1
- package/dist/services/scheduledTask.js.map +0 -1
- package/dist/services/task.js.map +0 -1
- package/dist/types/constants.d.ts +0 -8
- package/dist/types/index.d.ts +0 -4
- package/dist/types/interfaces/deps.d.ts +0 -26
- package/dist/types/interfaces/externalCommunicator.d.ts +0 -54
- package/dist/types/interfaces/index.d.ts +0 -99
- package/dist/types/interfaces/messageBrokerServiceConfig.d.ts +0 -79
- package/dist/types/interfaces/messageHandler.d.ts +0 -2
- package/dist/types/interfaces/metrics/index.d.ts +0 -20
- package/dist/types/interfaces/options.d.ts +0 -49
- package/dist/types/interfaces/providers/rabbitmq/amqpConnection.d.ts +0 -29
- package/dist/types/interfaces/providers/rabbitmq/amqpPublisher.d.ts +0 -16
- package/dist/types/interfaces/providers/rabbitmq/index.d.ts +0 -114
- package/dist/types/interfaces/queueConfig/configs.d.ts +0 -47
- package/dist/types/interfaces/queueConfig/index.d.ts +0 -1
- package/dist/types/interfaces/queueContext.d.ts +0 -4
- package/dist/types/interfaces/queueStatus.d.ts +0 -11
- package/dist/types/interfaces/services/eventMessageHandler.d.ts +0 -5
- package/dist/types/metrics/index.d.ts +0 -3
- package/dist/types/providers/index.d.ts +0 -5
- package/dist/types/providers/rabbitmq/amqpAsserter.d.ts +0 -49
- package/dist/types/providers/rabbitmq/amqpConnection.d.ts +0 -20
- package/dist/types/providers/rabbitmq/amqpListener.d.ts +0 -42
- package/dist/types/providers/rabbitmq/amqpPublisher.d.ts +0 -34
- package/dist/types/providers/rabbitmq/index.d.ts +0 -52
- package/dist/types/services/communicator.d.ts +0 -57
- package/dist/types/services/eventBus.d.ts +0 -20
- package/dist/types/services/eventCommunicator.d.ts +0 -15
- package/dist/types/services/eventMessageHandler.d.ts +0 -19
- package/dist/types/services/eventMessageValidator.d.ts +0 -9
- package/dist/types/services/externalCommunicator.d.ts +0 -110
- package/dist/types/services/externalEventBus.d.ts +0 -33
- package/dist/types/services/index.d.ts +0 -9
- package/dist/types/services/metrics.d.ts +0 -11
- package/dist/types/services/optionsBuilder.d.ts +0 -10
- package/dist/types/services/queue.d.ts +0 -23
- package/dist/types/services/scheduledTask.d.ts +0 -25
- package/dist/types/services/task.d.ts +0 -28
- package/dist/types/utils.d.ts +0 -3
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QueueMessage } from "../interfaces/providers/rabbitmq/index.js";
|
|
2
|
+
import { QueueContext } from "../interfaces/queueContext.js";
|
|
3
|
+
import { EventBusListener, EventListeners, TaskListener } from "../interfaces/index.js";
|
|
4
|
+
import { EventMessageValidator } from "./eventMessageValidator.js";
|
|
5
|
+
import Logger from "@diia-inhouse/diia-logger";
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
+
|
|
8
|
+
//#region src/services/eventMessageHandler.d.ts
|
|
9
|
+
declare class EventMessageHandler {
|
|
10
|
+
private readonly eventMessageValidator;
|
|
11
|
+
private readonly asyncLocalStorage;
|
|
12
|
+
private readonly logger;
|
|
13
|
+
private readonly noRequeueNackOptions;
|
|
14
|
+
constructor(eventMessageValidator: EventMessageValidator, asyncLocalStorage: AsyncLocalStorage<QueueContext>, logger: Logger);
|
|
15
|
+
eventListenersMessageHandler(this: this, eventListeners: EventListeners, message: QueueMessage | null): Promise<void>;
|
|
16
|
+
eventListenerMessageHandler(listener: EventBusListener | TaskListener | undefined, message: QueueMessage): Promise<void>;
|
|
17
|
+
private prepareAsyncContext;
|
|
18
|
+
private handleMessage;
|
|
19
|
+
private validateData;
|
|
20
|
+
private directReplyDone;
|
|
21
|
+
private getServiceCode;
|
|
22
|
+
private prepareQueueMessageError;
|
|
23
|
+
private resolveTraceId;
|
|
24
|
+
private extractTraceparentTraceId;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { EventMessageHandler };
|
|
@@ -1,129 +1,124 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
exports.EventMessageHandler = EventMessageHandler;
|
|
129
|
-
//# sourceMappingURL=eventMessageHandler.js.map
|
|
1
|
+
import { NackOptions } from "../interfaces/providers/rabbitmq/index.js";
|
|
2
|
+
import "../interfaces/index.js";
|
|
3
|
+
import { ApiError, ValidationError } from "@diia-inhouse/errors";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import { isValidTraceId, trace } from "@opentelemetry/api";
|
|
6
|
+
import { utils } from "@diia-inhouse/utils";
|
|
7
|
+
//#region src/services/eventMessageHandler.ts
|
|
8
|
+
var EventMessageHandler = class {
|
|
9
|
+
eventMessageValidator;
|
|
10
|
+
asyncLocalStorage;
|
|
11
|
+
logger;
|
|
12
|
+
noRequeueNackOptions = new NackOptions(false, false);
|
|
13
|
+
constructor(eventMessageValidator, asyncLocalStorage, logger) {
|
|
14
|
+
this.eventMessageValidator = eventMessageValidator;
|
|
15
|
+
this.asyncLocalStorage = asyncLocalStorage;
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
}
|
|
18
|
+
async eventListenersMessageHandler(eventListeners, message) {
|
|
19
|
+
if (!message) return;
|
|
20
|
+
const listener = eventListeners[message.data.event];
|
|
21
|
+
await this.eventListenerMessageHandler(listener, message);
|
|
22
|
+
}
|
|
23
|
+
async eventListenerMessageHandler(listener, message) {
|
|
24
|
+
const { done, properties, data: { event, payload } } = message;
|
|
25
|
+
const serviceCode = this.getServiceCode(listener, payload);
|
|
26
|
+
const context = this.prepareAsyncContext(properties, serviceCode);
|
|
27
|
+
await this.asyncLocalStorage.run(context, async () => {
|
|
28
|
+
this.logger.info(`Handling event [${event}] with payload`, { payload });
|
|
29
|
+
if (!listener) {
|
|
30
|
+
this.logger.info(`Not found listener for the event [${event}]`);
|
|
31
|
+
return done();
|
|
32
|
+
}
|
|
33
|
+
return await this.handleMessage(listener, message);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
prepareAsyncContext(properties, serviceCode) {
|
|
37
|
+
return { logData: {
|
|
38
|
+
traceId: this.resolveTraceId(properties),
|
|
39
|
+
serviceCode
|
|
40
|
+
} };
|
|
41
|
+
}
|
|
42
|
+
async handleMessage(listener, message) {
|
|
43
|
+
const { data, done, reject, properties: { replyTo, correlationId } } = message;
|
|
44
|
+
const { event, payload, meta } = data;
|
|
45
|
+
const useDirectReply = replyTo && correlationId;
|
|
46
|
+
let hasErrorOccurred = false;
|
|
47
|
+
let result;
|
|
48
|
+
const { isValid, error } = await this.validateData(data, listener);
|
|
49
|
+
if (!isValid) {
|
|
50
|
+
if (useDirectReply && error) return this.directReplyDone(message, error, true);
|
|
51
|
+
return reject(this.noRequeueNackOptions);
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
result = await listener.handler?.(payload, meta);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
result = err;
|
|
57
|
+
hasErrorOccurred = true;
|
|
58
|
+
this.logger.error(`Failed to handle event ${event}`, { err });
|
|
59
|
+
}
|
|
60
|
+
if (useDirectReply) return this.directReplyDone(message, result, hasErrorOccurred);
|
|
61
|
+
if (result instanceof NackOptions) return reject(result);
|
|
62
|
+
else if (hasErrorOccurred && listener.nackOptions) return reject(listener.nackOptions);
|
|
63
|
+
return done();
|
|
64
|
+
}
|
|
65
|
+
async validateData(data, listener) {
|
|
66
|
+
const { payload } = data;
|
|
67
|
+
const { validationRules } = listener;
|
|
68
|
+
try {
|
|
69
|
+
this.eventMessageValidator.validateEventMessage(data, validationRules);
|
|
70
|
+
return { isValid: true };
|
|
71
|
+
} catch (err) {
|
|
72
|
+
this.logger.error("Failed to validate event message", { err });
|
|
73
|
+
if (err instanceof ValidationError && "validationErrorHandler" in listener && payload?.uuid) await listener.validationErrorHandler?.(err, payload.uuid).catch((err_) => err_);
|
|
74
|
+
return err instanceof ValidationError ? {
|
|
75
|
+
isValid: false,
|
|
76
|
+
error: err
|
|
77
|
+
} : { isValid: false };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
directReplyDone(receivedMessage, response, error) {
|
|
81
|
+
const { done, data: { event, payload: { uuid } } } = receivedMessage;
|
|
82
|
+
const data = error ? utils.handleError(response, (err) => err) : response;
|
|
83
|
+
done({
|
|
84
|
+
event,
|
|
85
|
+
meta: { date: /* @__PURE__ */ new Date() },
|
|
86
|
+
payload: {
|
|
87
|
+
uuid,
|
|
88
|
+
...data instanceof ApiError ? { error: this.prepareQueueMessageError(data) } : { response: data }
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
getServiceCode(listener, payload) {
|
|
93
|
+
try {
|
|
94
|
+
return listener?.getServiceCode?.(payload);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
this.logger.error("Failed to get event listener service code", {
|
|
97
|
+
err,
|
|
98
|
+
listener
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
prepareQueueMessageError(err) {
|
|
103
|
+
return {
|
|
104
|
+
data: err.getData(),
|
|
105
|
+
message: err.message,
|
|
106
|
+
http_code: err.getCode()
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
resolveTraceId(properties) {
|
|
110
|
+
const extractedTraceId = this.extractTraceparentTraceId(properties.headers?.traceparent);
|
|
111
|
+
if (isValidTraceId(extractedTraceId)) return extractedTraceId;
|
|
112
|
+
const activeSpanTraceId = trace.getActiveSpan()?.spanContext().traceId ?? "";
|
|
113
|
+
if (isValidTraceId(activeSpanTraceId)) return activeSpanTraceId;
|
|
114
|
+
return properties.headers?.traceId || randomBytes(16).toString("hex");
|
|
115
|
+
}
|
|
116
|
+
extractTraceparentTraceId(raw) {
|
|
117
|
+
if (raw === void 0 || raw === null) return "";
|
|
118
|
+
const [version, traceId] = (Buffer.isBuffer(raw) ? raw.toString("utf8") : raw).split("-");
|
|
119
|
+
if (version !== "00" || !traceId) return "";
|
|
120
|
+
return isValidTraceId(traceId) ? traceId : "";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
//#endregion
|
|
124
|
+
export { EventMessageHandler };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { QueueMessageData } from "../interfaces/providers/rabbitmq/index.js";
|
|
2
|
+
import { AppValidator, ValidationSchema } from "@diia-inhouse/validators";
|
|
3
|
+
|
|
4
|
+
//#region src/services/eventMessageValidator.d.ts
|
|
5
|
+
declare class EventMessageValidator {
|
|
6
|
+
private readonly validator;
|
|
7
|
+
readonly metaValidationSchema: ValidationSchema;
|
|
8
|
+
constructor(validator: AppValidator);
|
|
9
|
+
validateEventMessage(data: QueueMessageData, payloadValidationSchema?: ValidationSchema): void | never;
|
|
10
|
+
validateSyncedEventMessage(data: QueueMessageData, responseValidationSchema?: ValidationSchema): void | never;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { EventMessageValidator };
|
|
@@ -1,44 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
//#region src/services/eventMessageValidator.ts
|
|
2
|
+
var EventMessageValidator = class {
|
|
3
|
+
validator;
|
|
4
|
+
metaValidationSchema = {
|
|
5
|
+
date: {
|
|
6
|
+
type: "date",
|
|
7
|
+
convert: true
|
|
8
|
+
},
|
|
9
|
+
traceId: {
|
|
10
|
+
type: "string",
|
|
11
|
+
optional: true
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
constructor(validator) {
|
|
15
|
+
this.validator = validator;
|
|
16
|
+
}
|
|
17
|
+
validateEventMessage(data, payloadValidationSchema = {}) {
|
|
18
|
+
const validationSchema = {
|
|
19
|
+
event: { type: "string" },
|
|
20
|
+
payload: {
|
|
21
|
+
type: "object",
|
|
22
|
+
props: payloadValidationSchema
|
|
23
|
+
},
|
|
24
|
+
meta: {
|
|
25
|
+
type: "object",
|
|
26
|
+
props: this.metaValidationSchema
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
this.validator.validate(data, validationSchema);
|
|
30
|
+
}
|
|
31
|
+
validateSyncedEventMessage(data, responseValidationSchema) {
|
|
32
|
+
const validation = {
|
|
33
|
+
uuid: { type: "uuid" },
|
|
34
|
+
error: {
|
|
35
|
+
type: "object",
|
|
36
|
+
optional: true,
|
|
37
|
+
props: {
|
|
38
|
+
http_code: { type: "number" },
|
|
39
|
+
message: {
|
|
40
|
+
type: "string",
|
|
41
|
+
optional: true
|
|
42
|
+
},
|
|
43
|
+
data: {
|
|
44
|
+
type: "object",
|
|
45
|
+
optional: true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
if (responseValidationSchema) validation.response = {
|
|
51
|
+
type: "object",
|
|
52
|
+
optional: true,
|
|
53
|
+
props: responseValidationSchema
|
|
54
|
+
};
|
|
55
|
+
this.validateEventMessage(data, validation);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { EventMessageValidator };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ReceiveDirectOps } from "../interfaces/externalCommunicator.js";
|
|
2
|
+
import { ExternalEventBusQueue } from "../interfaces/index.js";
|
|
3
|
+
import { EventMessageValidator } from "./eventMessageValidator.js";
|
|
4
|
+
import Logger from "@diia-inhouse/diia-logger";
|
|
5
|
+
|
|
6
|
+
//#region src/services/externalCommunicator.d.ts
|
|
7
|
+
declare class ExternalCommunicator {
|
|
8
|
+
private readonly externalEventBus;
|
|
9
|
+
private readonly eventMessageValidator;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
constructor(externalEventBus: ExternalEventBusQueue, eventMessageValidator: EventMessageValidator, logger: Logger);
|
|
12
|
+
/**
|
|
13
|
+
* Synchronous request/response over the external event bus.
|
|
14
|
+
*
|
|
15
|
+
* Publishes `event` to the configured external exchange wrapped as
|
|
16
|
+
* `{ uuid, request }` and awaits the subscriber's response on the same call.
|
|
17
|
+
* The response envelope is validated against `ops.validationRules`, and any embedded error is rethrown as
|
|
18
|
+
* {@link ExternalCommunicatorError}.
|
|
19
|
+
*
|
|
20
|
+
* Use this when the caller needs the response inline (e.g. a user-facing
|
|
21
|
+
* request that must block on `AGateway`). For fire-and-forget or fan-out
|
|
22
|
+
* flows, use the `default` publish + listener pattern instead.
|
|
23
|
+
*
|
|
24
|
+
* ### Transport
|
|
25
|
+
*
|
|
26
|
+
* Implemented on top of RabbitMQ's
|
|
27
|
+
* [direct reply-to](https://www.rabbitmq.com/docs/direct-reply-to)
|
|
28
|
+
* feature: the publisher consumes the pseudo-queue
|
|
29
|
+
* `amq.rabbitmq.reply-to` and stamps each request with `replyTo` +
|
|
30
|
+
* `correlationId`. The subscriber publishes its response back to that
|
|
31
|
+
* `replyTo` on the default exchange, and this call resolves when the
|
|
32
|
+
* matching `correlationId` arrives. No reply queue is declared per
|
|
33
|
+
* request, so there is no per-call broker setup overhead.
|
|
34
|
+
*
|
|
35
|
+
* ### Response contract
|
|
36
|
+
*
|
|
37
|
+
* The subscriber must respond with an
|
|
38
|
+
* {@link ExternalCommunicatorResponse} envelope:
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* { uuid: string, response: T, error?: { http_code, message?, data? } }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* On success this method returns **only the `response` field** typed as
|
|
45
|
+
* `T` — the wrapping `uuid` and the envelope itself are not exposed to
|
|
46
|
+
* the caller. Therefore `T` should describe the shape of `response`, not
|
|
47
|
+
* of the full envelope.
|
|
48
|
+
*
|
|
49
|
+
* ### Error contract
|
|
50
|
+
*
|
|
51
|
+
* Errors travel through the same envelope (`error` field on the
|
|
52
|
+
* subscriber's response — this is **not** a transport-level failure),
|
|
53
|
+
* but on the caller side they are surfaced as a thrown
|
|
54
|
+
* {@link ExternalCommunicatorError} instead of being returned. The
|
|
55
|
+
* envelope's `error` payload is mapped onto the exception so no data is
|
|
56
|
+
* lost:
|
|
57
|
+
*
|
|
58
|
+
* - `error.message` → `Error.message` (falls back to `'unknown error'`
|
|
59
|
+
* when missing).
|
|
60
|
+
* - `error.http_code` → exception's status code (read via `getCode()`).
|
|
61
|
+
* - `error.data` → exception's data bag (read via `getData()`), spread
|
|
62
|
+
* alongside `event` (the event name) and `httpCode` (a duplicate of
|
|
63
|
+
* the status code, kept on `data` for downstream consumers).
|
|
64
|
+
*
|
|
65
|
+
* Callers that need to inspect the original status or payload should
|
|
66
|
+
* `catch` the error and read those fields:
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* try {
|
|
70
|
+
* await externalCommunicator.receiveDirect<T>(event, req, ops)
|
|
71
|
+
* } catch (err) {
|
|
72
|
+
* if (err instanceof ExternalCommunicatorError) {
|
|
73
|
+
* err.getCode() // original error.http_code
|
|
74
|
+
* err.getData() // { event, httpCode, ...error.data }
|
|
75
|
+
* err.message // error.message || 'unknown error'
|
|
76
|
+
* }
|
|
77
|
+
* throw err
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @typeParam T - shape of the successful `response` payload returned by the subscriber.
|
|
82
|
+
* @param event - external event name. Must be declared in
|
|
83
|
+
* `serviceRulesConfig.topicsConfig[QueueConfigType.External][topicName].events[]`
|
|
84
|
+
* so the event resolves to an exchange (or pass `ops.exchangeName`
|
|
85
|
+
* to override the lookup).
|
|
86
|
+
* @param request - payload forwarded to the subscriber. Defaults to `{}`.
|
|
87
|
+
* @param ops - direct-call options. See {@link ReceiveDirectOps}.
|
|
88
|
+
* @returns the subscriber's `response` field, typed as `T`. The envelope's
|
|
89
|
+
* `uuid` and `error` fields are not returned.
|
|
90
|
+
*
|
|
91
|
+
* @throws {ExternalCommunicatorError} when the subscriber responds with an
|
|
92
|
+
* `error` envelope. The original `http_code`, `message`, and `data`
|
|
93
|
+
* fields are preserved on the thrown error.
|
|
94
|
+
* @throws {Error} `'Message in a wrong format was received from a direct channel'`
|
|
95
|
+
* when `validationRules` are provided and the response fails validation.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const result = await externalCommunicator.receiveDirect<{ data: string }>(
|
|
100
|
+
* 'notary.lookup',
|
|
101
|
+
* { notaryCertificateNumber: '1234', requestId: '-0' },
|
|
102
|
+
* {
|
|
103
|
+
* // Describes the inner `response` only; the envelope (uuid,
|
|
104
|
+
* // error, meta, ...) is wrapped by the validator.
|
|
105
|
+
* validationRules: { data: { type: 'string' } },
|
|
106
|
+
* timeout: DurationMs.Second * 5,
|
|
107
|
+
* },
|
|
108
|
+
* )
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
receiveDirect<T>(event: string, request: unknown, ops: ReceiveDirectOps): Promise<T>;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { ExternalCommunicator };
|