@platform-x/hep-message-broker-client 1.0.1 → 1.1.2

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/src/Util/commonUtil.js.map +1 -0
  3. package/dist/src/Util/constants.js +8 -0
  4. package/dist/src/Util/constants.js.map +1 -0
  5. package/dist/src/Util/logger.js.map +1 -0
  6. package/dist/src/Util/requestTracer.js.map +1 -0
  7. package/dist/src/config/ConfigManager.js.map +1 -0
  8. package/dist/src/config/index.js.map +1 -0
  9. package/dist/src/index.js +22 -1
  10. package/dist/src/index.js.map +1 -0
  11. package/dist/src/messageBroker/BaseRabbitMQClient.js.map +1 -0
  12. package/dist/src/messageBroker/ConnectionManager.js.map +1 -0
  13. package/dist/src/messageBroker/MessageBrokerClient.js.map +1 -0
  14. package/dist/src/messageBroker/MessageConsumer.js.map +1 -0
  15. package/dist/src/messageBroker/MessageProducer.js.map +1 -0
  16. package/dist/src/messageBroker/RabbitMQClient.js.map +1 -0
  17. package/dist/src/messageBroker/RetryManager.js.map +1 -0
  18. package/dist/src/messageBroker/interface/ConnectionWrapper.js.map +1 -0
  19. package/dist/src/messageBroker/interface/IMessageBrokerClient.js.map +1 -0
  20. package/dist/src/messageBroker/rabbitmq/MessageBrokerClient.js +90 -53
  21. package/dist/src/messageBroker/rabbitmq/MessageBrokerClient.js.map +1 -0
  22. package/dist/src/messageBroker/types/ActionType.js.map +1 -0
  23. package/dist/src/messageBroker/types/PublishMessageInputType.js.map +1 -0
  24. package/dist/src/models/MessageModel.js.map +1 -0
  25. package/dist/src/models/NotificationMessageModel.js.map +1 -0
  26. package/package.json +43 -40
  27. package/src/Util/commonUtil.ts +41 -0
  28. package/src/Util/constants.ts +4 -0
  29. package/src/Util/logger.ts +219 -0
  30. package/src/Util/requestTracer.ts +28 -0
  31. package/src/config/ConfigManager.ts +35 -0
  32. package/src/config/index.ts +31 -0
  33. package/src/index.ts +73 -0
  34. package/src/messageBroker/BaseRabbitMQClient.ts +30 -0
  35. package/src/messageBroker/ConnectionManager.ts +182 -0
  36. package/src/messageBroker/MessageBrokerClient.ts +88 -0
  37. package/src/messageBroker/MessageConsumer.ts +85 -0
  38. package/src/messageBroker/MessageProducer.ts +142 -0
  39. package/src/messageBroker/RabbitMQClient.ts +47 -0
  40. package/src/messageBroker/RetryManager.ts +64 -0
  41. package/src/messageBroker/interface/ConnectionWrapper.ts +7 -0
  42. package/src/messageBroker/interface/IMessageBrokerClient.ts +11 -0
  43. package/src/messageBroker/rabbitmq/MessageBrokerClient.ts +680 -0
  44. package/src/messageBroker/types/ActionType.ts +1 -0
  45. package/src/messageBroker/types/PublishMessageInputType.ts +8 -0
  46. package/src/models/MessageModel.ts +14 -0
  47. package/tsconfig.json +73 -0
  48. package/dist/src/Util/commonUtil.d.ts +0 -16
  49. package/dist/src/Util/logger.d.ts +0 -68
  50. package/dist/src/Util/requestTracer.d.ts +0 -7
  51. package/dist/src/config/ConfigManager.d.ts +0 -9
  52. package/dist/src/config/index.d.ts +0 -29
  53. package/dist/src/index.d.ts +0 -2
  54. package/dist/src/messageBroker/BaseRabbitMQClient.d.ts +0 -16
  55. package/dist/src/messageBroker/ConnectionManager.d.ts +0 -60
  56. package/dist/src/messageBroker/MessageBrokerClient.d.ts +0 -34
  57. package/dist/src/messageBroker/MessageConsumer.d.ts +0 -26
  58. package/dist/src/messageBroker/MessageProducer.d.ts +0 -29
  59. package/dist/src/messageBroker/RabbitMQClient.d.ts +0 -12
  60. package/dist/src/messageBroker/RetryManager.d.ts +0 -23
  61. package/dist/src/messageBroker/interface/ConnectionWrapper.d.ts +0 -6
  62. package/dist/src/messageBroker/interface/IMessageBrokerClient.d.ts +0 -7
  63. package/dist/src/messageBroker/rabbitmq/MessageBrokerClient.d.ts +0 -114
  64. package/dist/src/messageBroker/types/ActionType.d.ts +0 -1
  65. package/dist/src/messageBroker/types/PublishMessageInputType.d.ts +0 -7
  66. package/dist/src/models/MessageModel.d.ts +0 -5
  67. /package/{dist/src/models/NotificationMessageModel.d.ts → src/models/NotificationMessageModel.ts} +0 -0
@@ -0,0 +1,219 @@
1
+ import bunyan from 'bunyan';
2
+ import config from '../config/index';
3
+ import bformat from 'bunyan-format';
4
+ import { getTraceId, getSpanId } from './requestTracer';
5
+ import _ from 'lodash';
6
+ export interface payLoadInterface {
7
+ statusCode?: number;
8
+ data?: any;
9
+ }
10
+
11
+ export interface LoggerInterface {
12
+ info(message: string, method: string): void;
13
+ warn(message: string, method: string, payload?: object): void;
14
+ error(message: string, method: string, payload?: object): void;
15
+ debug(message: string, method: string, payload?: object): void;
16
+ }
17
+
18
+ export class LoggerClass implements LoggerInterface {
19
+ private logger: bunyan;
20
+ private infoLevel: boolean = false;
21
+ private debugLevel: boolean = false;
22
+ private errorLevel: boolean = false;
23
+ private warnLevel: boolean = false;
24
+ private blockedKeywords = ['currentPassword', 'clientSecret', 'newPassword'];
25
+
26
+ constructor() {
27
+ this.mapLogLevels();
28
+ this.logger = bunyan.createLogger({
29
+ name: config.APP_NAME,
30
+ level: 'trace', // by default enabling all levels, controlled through env separately
31
+ stream: bformat({
32
+ outputMode: config.NODE_ENV === 'development' ? 'short' : 'bunyan',
33
+ levelInString: true,
34
+ color: true,
35
+ }),
36
+ });
37
+ }
38
+ /**
39
+ * It's written to handle JSON.circular error
40
+ * @param obj
41
+ * @returns
42
+ */
43
+ public customstringify(obj: any) {
44
+ let cache: any = [];
45
+ let str = JSON.stringify(obj, (key, value) => {
46
+ {
47
+ this.info(`Logger customstringify ${key}`, 'customstringify');
48
+ if (typeof value === 'object' && value !== null) {
49
+ if (cache.indexOf(value) !== -1) {
50
+ // Circular reference found, discard key
51
+ return;
52
+ }
53
+ // Store value in our collection
54
+ cache.push(value);
55
+ }
56
+ return value;
57
+ }
58
+ });
59
+ cache = null; // reset the cache
60
+ return str;
61
+ }
62
+ /**
63
+ * for info log
64
+ * @param message
65
+ * @param payload
66
+ * @param method
67
+ * @returns
68
+ */
69
+ public info(message: string, method: string) {
70
+ if (!this.infoLevel) {
71
+ return;
72
+ }
73
+ let loggerParams = {
74
+ message,
75
+ method,
76
+ traceId: getTraceId(),
77
+ };
78
+ this.logger.info(` start ${JSON.stringify(loggerParams)}==end`);
79
+ }
80
+ /**
81
+ * for warn log
82
+ * @param message
83
+ * @param payload
84
+ * @param method
85
+ * @returns
86
+ */
87
+ public warn(message: string, method: string, payload?: any) {
88
+ if (!this.warnLevel) {
89
+ return;
90
+ }
91
+ let loggerParams = {
92
+ message,
93
+ method,
94
+ traceId: getTraceId(),
95
+ statusCode: payload?.statusCode,
96
+ };
97
+
98
+ let finalPayload = payload?.length ? payload : {};
99
+ this.logger.warn(
100
+ ` start ${JSON.stringify(loggerParams)} payload: ${this.customstringify(
101
+ this.sanitizeLog(finalPayload)
102
+ )}==end`
103
+ );
104
+ }
105
+ /**
106
+ * for error log
107
+ * @param message
108
+ * @param payload
109
+ * @param method
110
+ * @returns
111
+ */
112
+ public error(message: string, method: string, payload?: any) {
113
+ if (!this.errorLevel) {
114
+ return;
115
+ }
116
+
117
+ if (payload instanceof Error) {
118
+ payload = {
119
+ message,
120
+ method,
121
+ traceId: getTraceId(),
122
+ stack: payload?.stack,
123
+ };
124
+ }
125
+ let loggerParams = {
126
+ message,
127
+ method,
128
+ traceId: getTraceId(),
129
+ spanId: getSpanId(),
130
+ statusCode: payload?.statusCode,
131
+ };
132
+
133
+ let finalPayload = payload?.length ? payload : {};
134
+ this.logger.error(
135
+ ` start ${JSON.stringify(loggerParams)} payload: ${this.customstringify(
136
+ this.sanitizeLog(finalPayload)
137
+ )}==end`
138
+ );
139
+ }
140
+ /**
141
+ * for debug log
142
+ * @param message
143
+ * @param payload
144
+ * @param method
145
+ * @returns
146
+ */
147
+ public debug(message: string, method: string, payload?: any) {
148
+ if (!this.debugLevel) {
149
+ return;
150
+ }
151
+
152
+ let loggerParams = {
153
+ message,
154
+ method,
155
+ traceId: getTraceId(),
156
+ statusCode: payload?.statusCode,
157
+ };
158
+
159
+ let finalPayload = payload?.length ? payload : {};
160
+ this.logger.debug(
161
+ ` start ${JSON.stringify(loggerParams)} payload: ${this.customstringify(
162
+ this.sanitizeLog(finalPayload)
163
+ )}==end`
164
+ );
165
+ }
166
+ /**
167
+ * to map log levels and handle from config
168
+ */
169
+ private mapLogLevels() {
170
+ if (config.LOG_LEVELS.length) {
171
+ config.LOG_LEVELS.forEach((element: string) => {
172
+ const logLevel = element.trim();
173
+ if (logLevel === 'info') {
174
+ this.infoLevel = true;
175
+ } else if (logLevel === 'debug') {
176
+ this.debugLevel = true;
177
+ } else if (logLevel === 'error') {
178
+ this.errorLevel = true;
179
+ } else if (logLevel === 'warn') {
180
+ this.warnLevel = true;
181
+ }
182
+ });
183
+ }
184
+ }
185
+ /**
186
+ * to sanitize logs from unwanted fields to be shown
187
+ * @param request
188
+ * @returns
189
+ */
190
+ private sanitizeLog(request: any) {
191
+ try {
192
+ if (request) {
193
+ // let obj: any = {};
194
+ Object.keys(request).forEach((key) => {
195
+ let value = request[key];
196
+ if (request[key] === 'options') {
197
+ delete request.options;
198
+ } else {
199
+ if (
200
+ this.blockedKeywords.find(
201
+ (blockedKey: any) =>
202
+ key.toLowerCase() === blockedKey.toLowerCase()
203
+ )
204
+ ) {
205
+ request[key] = 'XXXXXXXXXX';
206
+ } else {
207
+ request[key] = value;
208
+ }
209
+ }
210
+ });
211
+ return request;
212
+ }
213
+ } catch (err: any) {
214
+ this.error('Logger sanitize error', err);
215
+ }
216
+ }
217
+ }
218
+
219
+ export const Logger = new LoggerClass();
@@ -0,0 +1,28 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ import cls from 'cls-hooked';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+
5
+ const TRACE_ID_HEADER_NAME = 'Platform-X-Trace-Id';
6
+ const nsid = `rtracer:${uuidv4()}`;
7
+ const ns = cls.createNamespace(nsid);
8
+ const traceIdKey = 'traceId';
9
+ const spanIdKey = 'spanId';
10
+ /**
11
+ * setTracers - to set the request tracers on the request
12
+ */
13
+ export const setTracers = (req: Request, res: Response, next: NextFunction) => {
14
+ const headerName = TRACE_ID_HEADER_NAME.toLocaleLowerCase();
15
+ ns.bindEmitter(req);
16
+ ns.bindEmitter(res);
17
+ const traceId = req.headers[headerName] || uuidv4(); // to be passed in headers
18
+ const spanId = uuidv4(); // will be unique for services
19
+
20
+ ns.run(() => {
21
+ ns.set(traceIdKey, traceId);
22
+ ns.set(spanIdKey, spanId);
23
+ next();
24
+ });
25
+ };
26
+
27
+ export const getTraceId = () => ns.get(traceIdKey);
28
+ export const getSpanId = () => ns.get(spanIdKey);
@@ -0,0 +1,35 @@
1
+ import * as fs from "fs";
2
+ import config from "./index";
3
+ import { Logger } from '../Util/logger';
4
+
5
+ class ConfigManager {
6
+ private static instance: ConfigManager;
7
+ private config: any;
8
+ private constructor() {
9
+ this.config = {};
10
+ this.loadConfig(config.RABBITMQ.CONFIG_PATH);
11
+ }
12
+
13
+ public static getInstance(): ConfigManager {
14
+ if (!ConfigManager.instance) {
15
+ ConfigManager.instance = new ConfigManager();
16
+ }
17
+ return ConfigManager.instance;
18
+ }
19
+
20
+ public loadConfig(configPath: string): void {
21
+ if (fs.existsSync(configPath)) {
22
+ // Do something
23
+ this.config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
24
+ }else{
25
+ Logger.error("Config file missing!", "loadConfig")
26
+ }
27
+
28
+ }
29
+
30
+ public getConfig(): any {
31
+ return this.config;
32
+ }
33
+ }
34
+
35
+ export default ConfigManager;
@@ -0,0 +1,31 @@
1
+ import dotenv from 'dotenv';
2
+ dotenv.config();
3
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
4
+
5
+ export default {
6
+ LOG_LEVELS: ['info', 'error', 'warn', 'debug'],
7
+ NODE_ENV: 'development',
8
+ APP_NAME: 'hep-message-broker-client',
9
+ RABBITMQ: {
10
+ USER: process.env.RABBITMQ_USER??'root',
11
+ PASS: process.env.RABBITMQ_PASS??'root',
12
+ HOST: process.env.RABBITMQ_HOST??'127.0.0.1',
13
+ PORT: process.env.RABBITMQ_PORT??'5672',
14
+ MAIN_QUEUE: 'main_queue',
15
+ MAIN_EXCHANGE: 'assets_metadata_generate_exchange',
16
+ DLX_QUEUE: 'dead_letter_queue',
17
+ DLX_EXCHANGE: 'assets_dlx_exchange',
18
+ RETRY_QUEUE: 'retry_queue',
19
+ RETRY_EXCHANGE: 'assets_metadata_generate_retry_exchange',
20
+ MAX_RETRIES: Number(process.env.RABBITMQ_MAX_RETRIES?.trim() ?? 1),
21
+ HEARTBEAT: Number(process.env.RABBITMQ_HEARTBEAT?.trim() ?? 5),
22
+ TTL:process.env.RABBITMQ_TTL??"3000|6000|9000",
23
+ BACKOFF_TIME:Number(process.env.BACKOFF_TIME?.trim() ?? 1000),
24
+ IS_ENABLED: process.env.RABBITMQ_IS_ENABLED ?? 'true',
25
+ RABBITMQ_CONFIG: process.env.RABBITMQ_CONFIG ?? '',
26
+ PREFETCH_COUNT: Number(process.env.RABBITMQ_PREFETCH ?? 50),
27
+ CONNECTION_ERROR:process.env.RABBITMQ_CONNECTION_ERROR ?? false,
28
+ PUBLISHER_ERROR:process.env.RABBITMQ_PUBLISHER_ERROR ?? false,
29
+ CONSUMER_ERROR:process.env.RABBITMQ_CONSUMER_ERROR ?? false,
30
+ CONFIG_PATH: process.env.CONFIG_PATH ?? '/etc/rabbitmq/rabbitMQConfig.json',
31
+ }}
package/src/index.ts ADDED
@@ -0,0 +1,73 @@
1
+
2
+ import { MessageBrokerClient } from "./messageBroker/rabbitmq/MessageBrokerClient";
3
+ // const messageBrokerClient = new MessageBrokerClient();
4
+ // let initial_message = {
5
+ // message: {
6
+ // 'queue_name': 'image_thumbnail_request_queue',
7
+ // 'body': {
8
+ // 'url': 'https://dev.dam.hcl-x.com/server/api/core/bitstreams/43817aff-ebb7-47d2-be0e-fb4db1ab267a/content',
9
+ // 'file_name': 'Kudos (1)',
10
+ // 'destination_path': '1721365841379/public/png',
11
+ // 'visibility': 'public',
12
+ // 'urgency': 0,
13
+ // 'folder_name': '',
14
+ // 'bitstream_id': '43817aff-ebb7-47d2-be0e-fb4db1ab267a',
15
+ // 'ext': 'png',
16
+ // 'asset_type': 'Image',
17
+ // 'item_id': 'b1ff157e-a04f-4f8c-83b5-3a2ead732101',
18
+ // },
19
+ // 'item_id': 'b1ff157e-a04f-4f8c-83b5-3a2ead732101',
20
+ // "language": "en",
21
+ // 'retries': 0,
22
+ // },
23
+ // corelationId: 'lgkdkfgkkfd',
24
+ // version: 0,
25
+ // trace: '',
26
+ // };
27
+
28
+ // let initial_message = {
29
+ // "message":{
30
+ // "queue_name":"summary_request_queue",
31
+ // "body":{
32
+ // "input": "€ !~!@#$%^&*()_++_<><?:{}[];,./ Hi everyone, good afternoon. Hope you all are doing good. Myself Akshima, Daitishin from Hetzial Healthcare. So today I am going to discuss about weight loss platy. We all must be aware about what is a weight loss platy. This is a term which we all come across when we are focusing on our weight loss journey. ",
33
+ // "language": "hi",
34
+ // "max_length": 20,
35
+ // "min_length": 10
36
+ // },
37
+ // "language": "en",
38
+ // "correlationId":"",
39
+ // "retries": 0
40
+ // }
41
+ // };
42
+
43
+ // console.log(initial_message)
44
+ // async function testConnection() {
45
+ // await messageBrokerClient.initialize().then(async () => {
46
+ // return '';
47
+ // setInterval(() => {
48
+ // messageBrokerClient.initializeConsumers();
49
+ // messageBrokerClient.publishMessage( { queueName: 'summary_request_queue', exchangeName:"assets_metadata_generate_exchange", message: initial_message })
50
+
51
+ // }, 1);
52
+ // });
53
+ // }
54
+
55
+ // testConnection();
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+ export { MessageBrokerClient };
64
+ let injectedSecretService: any = null;
65
+ export const initIAMSecretsMessageBroker = (secretServiceIns: any) =>{
66
+ injectedSecretService = secretServiceIns;
67
+ }
68
+ export const getIAMSecrets = async() =>{
69
+ if (!injectedSecretService) {
70
+ throw new Error("IAM secrets not initialized");
71
+ }
72
+ return await injectedSecretService.getSecretKeys(); // returns cached data
73
+ }
@@ -0,0 +1,30 @@
1
+ import * as amqp from 'amqplib';
2
+ import ConfigManager from '../config/ConfigManager';
3
+ import config from '../config';
4
+ import ConnectionManager from './ConnectionManager';
5
+
6
+ class BaseRabbitMQClient{
7
+ public readonly configManager: ConfigManager;
8
+ public readonly connectionManager: ConnectionManager = new ConnectionManager();
9
+ protected connection: amqp.Connection | undefined;
10
+ protected channel: amqp.Channel | undefined;
11
+ protected configData: any = {};
12
+ constructor() {
13
+ this.configManager = ConfigManager.getInstance();
14
+ this.configData = this.configManager.getConfig();
15
+ this.channel = this.connectionManager.getChannel();
16
+ this.connection = this.connectionManager.getConnection();
17
+ this.init();
18
+ }
19
+
20
+ /**
21
+ * Function for load the config data
22
+ */
23
+ private init() {
24
+ this.configManager.loadConfig(config?.RABBITMQ?.CONFIG_PATH);
25
+ this.configData = this.configManager.getConfig();
26
+ }
27
+
28
+ }
29
+
30
+ export default BaseRabbitMQClient;
@@ -0,0 +1,182 @@
1
+ // Import the amqplib library for RabbitMQ
2
+ import * as amqp from 'amqplib';
3
+ import { Logger } from '../Util/logger';
4
+ import ConfigManager from '../config/ConfigManager';
5
+ import config from '../config';
6
+ let isConnectionOpen = false;
7
+ let MAX_RETRIES: number = 1;
8
+
9
+
10
+ interface ConnectionWrapper {
11
+ connection: amqp.Connection,
12
+ channel: amqp.Channel
13
+ }
14
+
15
+ class ConnectionManager {
16
+ private static instance: ConnectionManager;
17
+ private readonly configManager: ConfigManager;
18
+ protected connection: amqp.Connection | undefined;
19
+ protected channel: amqp.Channel | undefined;
20
+ protected configData: any = {};
21
+ constructor() {
22
+ this.configManager = ConfigManager.getInstance();
23
+ this.init();
24
+ }
25
+
26
+ /**
27
+ * Function for get channel
28
+ */
29
+ public getChannel():amqp.Channel | undefined{
30
+ return this.channel;
31
+ }
32
+
33
+ /**
34
+ * Function for get channel
35
+ */
36
+ public getConnection():amqp.Connection | undefined{
37
+ return this.connection;
38
+ }
39
+
40
+ /**
41
+ * Function for create and return intance
42
+ * @returns
43
+ */
44
+ public static async getInstance(): Promise<ConnectionManager> {
45
+ if (!ConnectionManager.instance) {
46
+ ConnectionManager.instance = new ConnectionManager();
47
+ }
48
+ return ConnectionManager.instance;
49
+ }
50
+
51
+ /**
52
+ * Function to connect create to RabbitMQ
53
+ */
54
+ public async createConnection(): Promise<ConnectionWrapper> {
55
+ try {
56
+ Logger.info('Reached to createConnection', 'createConnection');
57
+ this.configManager.loadConfig(config?.RABBITMQ?.CONFIG_PATH);
58
+ let url: string = `amqp://${this.configData?.username}:${this.configData?.password}@${this.configData?.host}`;
59
+ // Create a connection to RabbitMQ server
60
+ this.connection = await amqp.connect(url, { heartbeat: this.configData?.heartbeat });
61
+ isConnectionOpen = true;
62
+ // check the connection status
63
+ this.bindConnectionEventHandler();
64
+ // create channel
65
+ this.channel = await this.connection.createConfirmChannel();
66
+ Logger.info('RabbitMQ connected successfully! ', 'createConnection');
67
+ // create exchange and queue and bind with exchange
68
+ await this.setupQueuesAndExchanges();
69
+ const connectionWrapper: ConnectionWrapper = { connection: this.connection, channel: this.channel }
70
+ return connectionWrapper;
71
+ } catch (error) {
72
+ // Log and rethrow any errors that occur during connection
73
+ Logger.error('Failed to connect to RabbitMQ', 'connection', (error as Error)?.message);
74
+ return { connection: null, channel: null } as unknown as ConnectionWrapper;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Function for bind connection event handler on close and error setupQueuesAndExchanges
80
+ * @returns
81
+ */
82
+ private bindConnectionEventHandler():boolean {
83
+ try {
84
+ this.connection?.on('close', (err: any) => {
85
+ Logger.error(`RabbitMQ: bindConnectionEventHandler:close`, `Connection closed`, err);
86
+ isConnectionOpen = false;
87
+ });
88
+ this.connection?.on('error', (err: any) => {
89
+ Logger.error(`RabbitMQ: bindConnectionEventHandler:error`, `Connection error`, err);
90
+ isConnectionOpen = false;
91
+ });
92
+ return isConnectionOpen;
93
+ } catch (error) {
94
+ isConnectionOpen = false;
95
+ return isConnectionOpen;
96
+ }
97
+
98
+ }
99
+
100
+ /**
101
+ * Function to check the status of the connection
102
+ * @returns boolean
103
+ */
104
+ public async isConnected(): Promise<boolean> {
105
+ return isConnectionOpen;
106
+ };
107
+
108
+
109
+ /**
110
+ * Function to initialize the connection, channel and setup the queues and exchanges
111
+ * @param reqData
112
+ * @returns
113
+ */
114
+ public async initialize() {
115
+ Logger.info('Reached to initialize', 'initialize');
116
+ let retries: number = 0;
117
+ MAX_RETRIES = this.configData?.max_retries;
118
+ // Retrie times connection
119
+ while (retries < MAX_RETRIES) {
120
+ try {
121
+ await this.createConnection();
122
+ return { connection:this.connection, channel:this.channel };
123
+ } catch (err) {
124
+ retries++;
125
+ Logger.error('initialize:Failed to connect to RabbitMQ', `Attempt ${retries} of ${MAX_RETRIES}.`);
126
+ if (retries >= MAX_RETRIES) {
127
+ Logger.error('Max retries reached, cannot connect to RabbitMQ.', 'initialize');
128
+ return { connection:this.connection, channel:this.channel };
129
+ }
130
+ // Wait before retrying (e.g., 10 seconds)
131
+ await new Promise(resolve => setTimeout(resolve, 10000));
132
+ }
133
+ }
134
+ };
135
+
136
+ /**
137
+ * Function to setup the queues and exchanges
138
+ * @params array
139
+ * @rtrun
140
+ */
141
+ private async setupQueuesAndExchanges():Promise<void> {
142
+ Logger.info('Reached to setupQueuesAndExchanges', 'setupQueuesAndExchanges');
143
+ try {
144
+ let exchanges: any = this.configData.exchanges;
145
+ let queues: any = this.configData.queues;
146
+ let bindings: any = this.configData.bindings;
147
+ // Declare the exchange with dlx
148
+ for (let data of exchanges) {
149
+ await this.channel?.assertExchange(data?.name, data.type, { durable: data.durable, autoDelete: data.autoDelete });
150
+ Logger.info(`${data?.name} exchange created`, 'setupQueuesAndExchanges');
151
+ }
152
+
153
+ // Declare the queues with dlx
154
+ for (let data of queues) {
155
+ await this.channel?.assertQueue(data?.name, { durable: data.durable, exclusive: data.exclusive, autoDelete: data.autoDelete, arguments: data.arguments });
156
+ Logger.info(`${data?.name} queue created `, 'setupQueuesAndExchanges');
157
+ }
158
+
159
+ // Declare the bindings for queues and exchanges with routing keys
160
+ for (let obj in bindings) {
161
+ bindings[obj]?.forEach(async (key: any) => {
162
+ await this.channel?.bindQueue(key?.queue, obj, key?.routingKey);
163
+ Logger.info(`${key?.queue} bind with exchange name ${obj} and routing key ${key?.routingKey}`, 'setupQueuesAndExchanges');
164
+ })
165
+ }
166
+ } catch (error) {
167
+ Logger.error("Error on setup queues, exchanges and binding", 'setupQueuesAndExchanges', (error as Error).message);
168
+ throw error;
169
+ }
170
+ };
171
+
172
+ /**
173
+ * Function for load the config data
174
+ */
175
+ private init() {
176
+ this.configManager.loadConfig(config?.RABBITMQ?.CONFIG_PATH);
177
+ this.configData = this.configManager.getConfig();
178
+ }
179
+
180
+ }
181
+
182
+ export default ConnectionManager;
@@ -0,0 +1,88 @@
1
+ import config from '../config';
2
+ import ConfigManager from '../config/ConfigManager';
3
+ import ConnectionManager from './ConnectionManager';
4
+ import { IMessageBrokerClient } from './interface/IMessageBrokerClient';
5
+ import MessageProducer from './MessageProducer';
6
+ import { RabbitMQClient } from './RabbitMQClient';
7
+ import RetryManager from './RetryManager';
8
+ import { PublishMessageInputType } from './types/PublishMessageInputType';
9
+ const connectionManager = ConnectionManager.getInstance();
10
+ export class MessageBrokerClient implements IMessageBrokerClient {
11
+ private readonly rabbitMQClient: RabbitMQClient;
12
+ private readonly messageProducer: MessageProducer;
13
+ private readonly configManager: ConfigManager;
14
+ // private readonly retryManager: RetryManager<T>;
15
+ private readonly isEnabledRabbitMQ: boolean = false;
16
+ private readonly configData: any = {};
17
+ constructor() {
18
+ this.rabbitMQClient = new RabbitMQClient();
19
+ this.messageProducer = new MessageProducer();
20
+ this.isEnabledRabbitMQ = config?.RABBITMQ?.IS_ENABLED === 'true';
21
+ this.configManager = ConfigManager.getInstance();
22
+ this.configData = this.configManager.getConfig();
23
+ }
24
+
25
+ /**
26
+ * Function to initialize the RabbitMQ connection
27
+ */
28
+ async initialize(): Promise<void> {
29
+ if(this.isEnabledRabbitMQ){
30
+ this.rabbitMQClient.initialize();
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Function to close the connection and channel
36
+ */
37
+ disconnect(): void {
38
+ if(this.isEnabledRabbitMQ){
39
+ this.rabbitMQClient.disconnect();
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Function to publish a message with retry
45
+ * @param request
46
+ * @returns
47
+ */
48
+ publishMessage(request:PublishMessageInputType): Promise<boolean> {
49
+ if(this.isEnabledRabbitMQ){
50
+ const boundPublishMessage = this.messageProducer.publishMessage.bind(this);
51
+ const boundConsumerHandler = this.messageProducer.messageHandler.bind(this);
52
+ let retry_delay = this.configData?.retry_delay;
53
+ let max_retries = this.configData?.max_retries;
54
+ // Instantiate RetryManager
55
+ const retryManager = RetryManager.getInstance<PublishMessageInputType>(max_retries, retry_delay);
56
+ retryManager.performActionWithRetry(boundPublishMessage, request, boundConsumerHandler)
57
+ }
58
+ return Promise.resolve(false);
59
+ }
60
+
61
+ /**
62
+ * Function to publish a message with retry to exchange
63
+ * @param request
64
+ * @returns
65
+ */
66
+ publishMessageToExchange(request:PublishMessageInputType): Promise<boolean> {
67
+ if(this.isEnabledRabbitMQ){
68
+ const boundPublishMessage = this.messageProducer.publishMessageToExchange.bind(this);
69
+ const boundConsumerHandler = this.messageProducer.messageHandler.bind(this);
70
+ let retry_delay = this.configData?.retry_delay;
71
+ let max_retries = this.configData?.max_retries;
72
+ // Instantiate RetryManager
73
+ const retryManager = RetryManager.getInstance<PublishMessageInputType>(max_retries, retry_delay);
74
+ retryManager.performActionWithRetry(boundPublishMessage, request, boundConsumerHandler)
75
+ }
76
+ return Promise.resolve(false);
77
+ }
78
+ /***
79
+ * Function to check if the connection is established
80
+ */
81
+ async isConnected(): Promise<boolean> {
82
+ if(this.isEnabledRabbitMQ){
83
+ return (await connectionManager) ? (await connectionManager).isConnected() : Promise.resolve(false);
84
+ }
85
+ return Promise.resolve(false);
86
+ }
87
+
88
+ }