@diia-inhouse/diia-queue 13.3.3 → 14.0.10

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.
Files changed (148) hide show
  1. package/dist/constants.js +9 -9
  2. package/dist/index.d.ts +27 -0
  3. package/dist/index.js +23 -21
  4. package/dist/interfaces/deps.d.ts +30 -0
  5. package/dist/interfaces/externalCommunicator.d.ts +58 -0
  6. package/dist/interfaces/index.d.ts +95 -0
  7. package/dist/interfaces/index.js +7 -30
  8. package/dist/interfaces/messageBrokerServiceConfig.d.ts +80 -0
  9. package/dist/interfaces/messageBrokerServiceConfig.js +27 -24
  10. package/dist/interfaces/messageHandler.d.ts +6 -0
  11. package/dist/interfaces/metrics/index.d.ts +23 -0
  12. package/dist/interfaces/metrics/index.js +12 -16
  13. package/dist/interfaces/options.d.ts +52 -0
  14. package/dist/interfaces/providers/rabbitmq/amqpConnection.d.ts +33 -0
  15. package/dist/interfaces/providers/rabbitmq/amqpConnection.js +14 -17
  16. package/dist/interfaces/providers/rabbitmq/amqpPublisher.d.ts +20 -0
  17. package/dist/interfaces/providers/rabbitmq/index.d.ts +107 -0
  18. package/dist/interfaces/providers/rabbitmq/index.js +35 -47
  19. package/dist/interfaces/queueConfig/configs.d.ts +47 -0
  20. package/dist/interfaces/queueConfig/configs.js +8 -9
  21. package/dist/interfaces/queueConfig/index.d.ts +1 -0
  22. package/dist/interfaces/queueContext.d.ts +8 -0
  23. package/dist/interfaces/queueStatus.d.ts +15 -0
  24. package/dist/interfaces/queueStatus.js +8 -9
  25. package/dist/metrics/index.js +5 -8
  26. package/dist/providers/index.d.ts +5 -0
  27. package/dist/providers/index.js +6 -22
  28. package/dist/providers/rabbitmq/amqpAsserter.d.ts +53 -0
  29. package/dist/providers/rabbitmq/amqpAsserter.js +369 -416
  30. package/dist/providers/rabbitmq/amqpConnection.d.ts +24 -0
  31. package/dist/providers/rabbitmq/amqpConnection.js +97 -150
  32. package/dist/providers/rabbitmq/amqpListener.d.ts +47 -0
  33. package/dist/providers/rabbitmq/amqpListener.js +218 -225
  34. package/dist/providers/rabbitmq/amqpPublisher.d.ts +38 -0
  35. package/dist/providers/rabbitmq/amqpPublisher.js +184 -191
  36. package/dist/providers/rabbitmq/index.d.ts +56 -0
  37. package/dist/providers/rabbitmq/index.js +161 -186
  38. package/dist/services/communicator.d.ts +66 -0
  39. package/dist/services/communicator.js +164 -186
  40. package/dist/services/eventBus.d.ts +25 -0
  41. package/dist/services/eventBus.js +45 -57
  42. package/dist/services/eventCommunicator.js +75 -124
  43. package/dist/services/eventMessageHandler.d.ts +25 -0
  44. package/dist/services/eventMessageHandler.js +112 -129
  45. package/dist/services/eventMessageValidator.d.ts +13 -0
  46. package/dist/services/eventMessageValidator.js +59 -44
  47. package/dist/services/externalCommunicator.d.ts +114 -0
  48. package/dist/services/externalCommunicator.js +149 -140
  49. package/dist/services/externalEventBus.d.ts +39 -0
  50. package/dist/services/externalEventBus.js +144 -162
  51. package/dist/services/index.d.ts +8 -0
  52. package/dist/services/index.js +10 -26
  53. package/dist/services/metrics.d.ts +15 -0
  54. package/dist/services/metrics.js +46 -49
  55. package/dist/services/optionsBuilder.d.ts +14 -0
  56. package/dist/services/optionsBuilder.js +43 -58
  57. package/dist/services/queue.d.ts +29 -0
  58. package/dist/services/queue.js +78 -93
  59. package/dist/services/scheduledTask.d.ts +30 -0
  60. package/dist/services/scheduledTask.js +60 -68
  61. package/dist/services/task.d.ts +33 -0
  62. package/dist/services/task.js +160 -176
  63. package/dist/utils.js +7 -11
  64. package/package.json +44 -50
  65. package/dist/constants.js.map +0 -1
  66. package/dist/index.js.map +0 -1
  67. package/dist/interfaces/deps.js +0 -3
  68. package/dist/interfaces/deps.js.map +0 -1
  69. package/dist/interfaces/externalCommunicator.js +0 -3
  70. package/dist/interfaces/externalCommunicator.js.map +0 -1
  71. package/dist/interfaces/index.js.map +0 -1
  72. package/dist/interfaces/messageBrokerServiceConfig.js.map +0 -1
  73. package/dist/interfaces/messageHandler.js +0 -3
  74. package/dist/interfaces/messageHandler.js.map +0 -1
  75. package/dist/interfaces/metrics/index.js.map +0 -1
  76. package/dist/interfaces/options.js +0 -3
  77. package/dist/interfaces/options.js.map +0 -1
  78. package/dist/interfaces/providers/rabbitmq/amqpConnection.js.map +0 -1
  79. package/dist/interfaces/providers/rabbitmq/amqpPublisher.js +0 -4
  80. package/dist/interfaces/providers/rabbitmq/amqpPublisher.js.map +0 -1
  81. package/dist/interfaces/providers/rabbitmq/index.js.map +0 -1
  82. package/dist/interfaces/queueConfig/configs.js.map +0 -1
  83. package/dist/interfaces/queueConfig/index.js +0 -18
  84. package/dist/interfaces/queueConfig/index.js.map +0 -1
  85. package/dist/interfaces/queueContext.js +0 -3
  86. package/dist/interfaces/queueContext.js.map +0 -1
  87. package/dist/interfaces/queueStatus.js.map +0 -1
  88. package/dist/interfaces/services/eventMessageHandler.js +0 -3
  89. package/dist/interfaces/services/eventMessageHandler.js.map +0 -1
  90. package/dist/metrics/index.js.map +0 -1
  91. package/dist/providers/index.js.map +0 -1
  92. package/dist/providers/rabbitmq/amqpAsserter.js.map +0 -1
  93. package/dist/providers/rabbitmq/amqpConnection.js.map +0 -1
  94. package/dist/providers/rabbitmq/amqpListener.js.map +0 -1
  95. package/dist/providers/rabbitmq/amqpPublisher.js.map +0 -1
  96. package/dist/providers/rabbitmq/index.js.map +0 -1
  97. package/dist/services/communicator.js.map +0 -1
  98. package/dist/services/eventBus.js.map +0 -1
  99. package/dist/services/eventCommunicator.js.map +0 -1
  100. package/dist/services/eventMessageHandler.js.map +0 -1
  101. package/dist/services/eventMessageValidator.js.map +0 -1
  102. package/dist/services/externalCommunicator.js.map +0 -1
  103. package/dist/services/externalEventBus.js.map +0 -1
  104. package/dist/services/index.js.map +0 -1
  105. package/dist/services/metrics.js.map +0 -1
  106. package/dist/services/optionsBuilder.js.map +0 -1
  107. package/dist/services/queue.js.map +0 -1
  108. package/dist/services/scheduledTask.js.map +0 -1
  109. package/dist/services/task.js.map +0 -1
  110. package/dist/types/constants.d.ts +0 -8
  111. package/dist/types/index.d.ts +0 -4
  112. package/dist/types/interfaces/deps.d.ts +0 -26
  113. package/dist/types/interfaces/externalCommunicator.d.ts +0 -54
  114. package/dist/types/interfaces/index.d.ts +0 -99
  115. package/dist/types/interfaces/messageBrokerServiceConfig.d.ts +0 -79
  116. package/dist/types/interfaces/messageHandler.d.ts +0 -2
  117. package/dist/types/interfaces/metrics/index.d.ts +0 -20
  118. package/dist/types/interfaces/options.d.ts +0 -49
  119. package/dist/types/interfaces/providers/rabbitmq/amqpConnection.d.ts +0 -29
  120. package/dist/types/interfaces/providers/rabbitmq/amqpPublisher.d.ts +0 -16
  121. package/dist/types/interfaces/providers/rabbitmq/index.d.ts +0 -114
  122. package/dist/types/interfaces/queueConfig/configs.d.ts +0 -47
  123. package/dist/types/interfaces/queueConfig/index.d.ts +0 -1
  124. package/dist/types/interfaces/queueContext.d.ts +0 -4
  125. package/dist/types/interfaces/queueStatus.d.ts +0 -11
  126. package/dist/types/interfaces/services/eventMessageHandler.d.ts +0 -5
  127. package/dist/types/metrics/index.d.ts +0 -3
  128. package/dist/types/providers/index.d.ts +0 -5
  129. package/dist/types/providers/rabbitmq/amqpAsserter.d.ts +0 -49
  130. package/dist/types/providers/rabbitmq/amqpConnection.d.ts +0 -20
  131. package/dist/types/providers/rabbitmq/amqpListener.d.ts +0 -42
  132. package/dist/types/providers/rabbitmq/amqpPublisher.d.ts +0 -34
  133. package/dist/types/providers/rabbitmq/index.d.ts +0 -52
  134. package/dist/types/services/communicator.d.ts +0 -57
  135. package/dist/types/services/eventBus.d.ts +0 -20
  136. package/dist/types/services/eventCommunicator.d.ts +0 -15
  137. package/dist/types/services/eventMessageHandler.d.ts +0 -19
  138. package/dist/types/services/eventMessageValidator.d.ts +0 -9
  139. package/dist/types/services/externalCommunicator.d.ts +0 -110
  140. package/dist/types/services/externalEventBus.d.ts +0 -33
  141. package/dist/types/services/index.d.ts +0 -9
  142. package/dist/types/services/metrics.d.ts +0 -11
  143. package/dist/types/services/optionsBuilder.d.ts +0 -10
  144. package/dist/types/services/queue.d.ts +0 -23
  145. package/dist/types/services/scheduledTask.d.ts +0 -25
  146. package/dist/types/services/task.d.ts +0 -28
  147. package/dist/types/utils.d.ts +0 -3
  148. package/dist/utils.js.map +0 -1
@@ -0,0 +1,25 @@
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
+ }
24
+ //#endregion
25
+ export { EventMessageHandler };
@@ -1,129 +1,112 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EventMessageHandler = void 0;
4
- const node_crypto_1 = require("node:crypto");
5
- const api_1 = require("@opentelemetry/api");
6
- const errors_1 = require("@diia-inhouse/errors");
7
- const utils_1 = require("@diia-inhouse/utils");
8
- const interfaces_1 = require("../interfaces");
9
- class EventMessageHandler {
10
- eventMessageValidator;
11
- asyncLocalStorage;
12
- logger;
13
- noRequeueNackOptions = new interfaces_1.NackOptions(false, false);
14
- constructor(eventMessageValidator, asyncLocalStorage, logger) {
15
- this.eventMessageValidator = eventMessageValidator;
16
- this.asyncLocalStorage = asyncLocalStorage;
17
- this.logger = logger;
18
- }
19
- async eventListenersMessageHandler(eventListeners, message) {
20
- if (!message) {
21
- return;
22
- }
23
- const listener = eventListeners[message.data.event];
24
- await this.eventListenerMessageHandler(listener, message);
25
- }
26
- async eventListenerMessageHandler(listener, message) {
27
- const { done, properties, data: { event, payload }, } = message;
28
- const serviceCode = this.getServiceCode(listener, payload);
29
- const context = this.prepareAsyncContext(properties, serviceCode);
30
- await this.asyncLocalStorage.run(context, async () => {
31
- this.logger.info(`Handling event [${event}] with payload`, { payload });
32
- if (!listener) {
33
- this.logger.info(`Not found listener for the event [${event}]`);
34
- return done();
35
- }
36
- return await this.handleMessage(listener, message);
37
- });
38
- }
39
- prepareAsyncContext(properties, serviceCode) {
40
- const activeSpanTraceId = api_1.trace.getActiveSpan()?.spanContext().traceId ?? '';
41
- const traceId = (0, api_1.isValidTraceId)(activeSpanTraceId) ? activeSpanTraceId : properties.headers?.traceId || (0, node_crypto_1.randomUUID)();
42
- return {
43
- logData: {
44
- traceId,
45
- serviceCode,
46
- },
47
- };
48
- }
49
- async handleMessage(listener, message) {
50
- const { data, done, reject, properties: { replyTo, correlationId }, } = message;
51
- const { event, payload, meta } = data;
52
- const useDirectReply = replyTo && correlationId;
53
- let hasErrorOccurred = false;
54
- let result;
55
- const { isValid, error } = await this.validateData(data, listener);
56
- if (!isValid) {
57
- if (useDirectReply && error) {
58
- return this.directReplyDone(message, error, true);
59
- }
60
- return reject(this.noRequeueNackOptions);
61
- }
62
- try {
63
- result = await listener.handler?.(payload, meta);
64
- }
65
- catch (err) {
66
- result = err;
67
- hasErrorOccurred = true;
68
- this.logger.error(`Failed to handle event ${event}`, { err });
69
- }
70
- if (useDirectReply) {
71
- return this.directReplyDone(message, result, hasErrorOccurred);
72
- }
73
- if (result instanceof interfaces_1.NackOptions) {
74
- return reject(result);
75
- }
76
- else if (hasErrorOccurred && listener.nackOptions) {
77
- return reject(listener.nackOptions);
78
- }
79
- return done();
80
- }
81
- async validateData(data, listener) {
82
- const { payload } = data;
83
- const { validationRules } = listener;
84
- try {
85
- this.eventMessageValidator.validateEventMessage(data, validationRules);
86
- return { isValid: true };
87
- }
88
- catch (err) {
89
- this.logger.error('Failed to validate event message', { err });
90
- if (err instanceof errors_1.ValidationError && 'validationErrorHandler' in listener && payload?.uuid) {
91
- const eventBusListener = listener;
92
- await eventBusListener.validationErrorHandler?.(err, payload.uuid).catch((err_) => err_);
93
- }
94
- return err instanceof errors_1.ValidationError ? { isValid: false, error: err } : { isValid: false };
95
- }
96
- }
97
- directReplyDone(receivedMessage, response, error) {
98
- const { done, data: { event, payload: { uuid }, }, } = receivedMessage;
99
- const data = error ? utils_1.utils.handleError(response, (err) => err) : response;
100
- const messageData = {
101
- event,
102
- meta: {
103
- date: new Date(),
104
- },
105
- payload: {
106
- uuid,
107
- ...(data instanceof errors_1.ApiError ? { error: this.prepareQueueMessageError(data) } : { response: data }),
108
- },
109
- };
110
- done(messageData);
111
- }
112
- getServiceCode(listener, payload) {
113
- try {
114
- return listener?.getServiceCode?.(payload);
115
- }
116
- catch (err) {
117
- this.logger.error('Failed to get event listener service code', { err, listener });
118
- }
119
- }
120
- prepareQueueMessageError(err) {
121
- return {
122
- data: err.getData(),
123
- message: err.message,
124
- http_code: err.getCode(),
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 { randomUUID } 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
+ const activeSpanTraceId = trace.getActiveSpan()?.spanContext().traceId ?? "";
38
+ return { logData: {
39
+ traceId: isValidTraceId(activeSpanTraceId) ? activeSpanTraceId : properties.headers?.traceId || randomUUID(),
40
+ serviceCode
41
+ } };
42
+ }
43
+ async handleMessage(listener, message) {
44
+ const { data, done, reject, properties: { replyTo, correlationId } } = message;
45
+ const { event, payload, meta } = data;
46
+ const useDirectReply = replyTo && correlationId;
47
+ let hasErrorOccurred = false;
48
+ let result;
49
+ const { isValid, error } = await this.validateData(data, listener);
50
+ if (!isValid) {
51
+ if (useDirectReply && error) return this.directReplyDone(message, error, true);
52
+ return reject(this.noRequeueNackOptions);
53
+ }
54
+ try {
55
+ result = await listener.handler?.(payload, meta);
56
+ } catch (err) {
57
+ result = err;
58
+ hasErrorOccurred = true;
59
+ this.logger.error(`Failed to handle event ${event}`, { err });
60
+ }
61
+ if (useDirectReply) return this.directReplyDone(message, result, hasErrorOccurred);
62
+ if (result instanceof NackOptions) return reject(result);
63
+ else if (hasErrorOccurred && listener.nackOptions) return reject(listener.nackOptions);
64
+ return done();
65
+ }
66
+ async validateData(data, listener) {
67
+ const { payload } = data;
68
+ const { validationRules } = listener;
69
+ try {
70
+ this.eventMessageValidator.validateEventMessage(data, validationRules);
71
+ return { isValid: true };
72
+ } catch (err) {
73
+ this.logger.error("Failed to validate event message", { err });
74
+ if (err instanceof ValidationError && "validationErrorHandler" in listener && payload?.uuid) await listener.validationErrorHandler?.(err, payload.uuid).catch((err_) => err_);
75
+ return err instanceof ValidationError ? {
76
+ isValid: false,
77
+ error: err
78
+ } : { isValid: false };
79
+ }
80
+ }
81
+ directReplyDone(receivedMessage, response, error) {
82
+ const { done, data: { event, payload: { uuid } } } = receivedMessage;
83
+ const data = error ? utils.handleError(response, (err) => err) : response;
84
+ done({
85
+ event,
86
+ meta: { date: /* @__PURE__ */ new Date() },
87
+ payload: {
88
+ uuid,
89
+ ...data instanceof ApiError ? { error: this.prepareQueueMessageError(data) } : { response: data }
90
+ }
91
+ });
92
+ }
93
+ getServiceCode(listener, payload) {
94
+ try {
95
+ return listener?.getServiceCode?.(payload);
96
+ } catch (err) {
97
+ this.logger.error("Failed to get event listener service code", {
98
+ err,
99
+ listener
100
+ });
101
+ }
102
+ }
103
+ prepareQueueMessageError(err) {
104
+ return {
105
+ data: err.getData(),
106
+ message: err.message,
107
+ http_code: err.getCode()
108
+ };
109
+ }
110
+ };
111
+ //#endregion
112
+ 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
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EventMessageValidator = void 0;
4
- class EventMessageValidator {
5
- validator;
6
- metaValidationSchema = {
7
- date: { type: 'date', convert: true },
8
- traceId: { type: 'string', optional: true },
9
- };
10
- constructor(validator) {
11
- this.validator = validator;
12
- }
13
- validateEventMessage(data, payloadValidationSchema = {}) {
14
- const validationSchema = {
15
- event: { type: 'string' },
16
- payload: { type: 'object', props: payloadValidationSchema },
17
- meta: {
18
- type: 'object',
19
- props: this.metaValidationSchema,
20
- },
21
- };
22
- this.validator.validate(data, validationSchema);
23
- }
24
- validateSyncedEventMessage(data, responseValidationSchema) {
25
- const validation = {
26
- uuid: { type: 'uuid' },
27
- error: {
28
- type: 'object',
29
- optional: true,
30
- props: {
31
- http_code: { type: 'number' },
32
- message: { type: 'string', optional: true },
33
- data: { type: 'object', optional: true },
34
- },
35
- },
36
- };
37
- if (responseValidationSchema) {
38
- validation.response = { type: 'object', optional: true, props: responseValidationSchema };
39
- }
40
- this.validateEventMessage(data, validation);
41
- }
42
- }
43
- exports.EventMessageValidator = EventMessageValidator;
44
- //# sourceMappingURL=eventMessageValidator.js.map
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 };