pink-bears
Version:
Intelligent rate limiting middleware with MongoDB integration and caching for Node.js applications
132 lines (118 loc) • 4.75 kB
JavaScript
const Axios = require('axios')
const amqp = require('amqp-connection-manager');
const { emitEvent: emit } = require('./errorEmitter');
const { errorConstants } = require('./constants');
const { v4: uuidv4 } = require('uuid');
class Event {
constructor({logger, domain, rabbitMQurl, rabbitMQExchange, rabbitMQRoutingKey, heartbeatIntervalInSeconds}) {
this.domain = domain;
this.rabbitMQurl = rabbitMQurl;
this.rabbitMQExchange = rabbitMQExchange;
this.rabbitMQRoutingKey = rabbitMQRoutingKey;
this.logger = logger || console
this.heartbeatIntervalInSeconds = heartbeatIntervalInSeconds || 60;
this.connection = null;
this.channel = null;
this.flag = false;
}
createConnection() {
this.connection = amqp.connect([this.rabbitMQurl], {
heartbeatIntervalInSeconds: this.heartbeatIntervalInSeconds
});
this.createChannel(this.connection);
}
createChannel(connection){
this.channel = connection.createChannel({
json: true,
setup: () => {
// No need to assert exchange and queue since they already exist
},
});
}
isConnectionAvailable(){
this.flag = this.connection.isConnected()
return this.flag;
}
async getConnection() {
if (this.connection !== null && this.channel !== null) {
return true;
}
try{
if(this.connection){
this.createChannel(this.connection);
return true;
}
this.createConnection();
return true;
}catch(error){
await emit(errorConstants.event, error);
this.logger.error("error occured while creating channel...", error);
return false;
}
}
async call(productEvent) {
if(!productEvent?.traceId){
productEvent.traceId = uuidv4();
}
try {
if(this.isConnectionAvailable()){
this.logger.info(`Sending the payload with traceID ${productEvent.traceId} via rabbitMQ`);
try {
const isPublishedPromise = new Promise((resolve, reject) => {
this.channel.publish(this.rabbitMQExchange, this.rabbitMQRoutingKey, productEvent, {
contentType: 'application/json', persistent: true
}, function(err, ok){
if (err !== null) {
reject(err);
} else {
resolve(ok);
}
});
});
const isPublished = await Promise.race([
isPublishedPromise,
new Promise((_, reject) => setTimeout(() => reject(new Error('Publish timeout')), 5000)),
]);
if(!isPublished) throw new Error('Error while publishing');
} catch (error) {
this.logger.error(`error while pushing to rabbitMQ for traceID ${productEvent.traceId}, hence trying with API call`, error)
await Axios.post(`${this.domain}/api/sparrow-apps/event-listener`, productEvent);
}
}else{
this.logger.info(`connection not available to send messages via queue, hence retrying with api call for the traceID : ${productEvent.traceId}`);
await Axios.post(`${this.domain}/api/sparrow-apps/event-listener`, productEvent);
}
return { success: true };
} catch (error) {
await emit(errorConstants.event, error);
this.logger.error(`Error occurred while sending message for traceID : ${productEvent.traceId}`, error);
throw error;
}
}
closeConnection() {
this.channel.close();
this.connection.close();
}
emitEvent = async (event, userId, productName, payload, queueEnabled) => {
const dispatchData = {
"Event": event,
"userId": userId,
"productName": productName,
"payload": payload
}
try{
if(queueEnabled){
return await this.call(dispatchData);
}
else{
return await Axios.post(`${this.domain}/api/sparrow-apps/event-listener`, dispatchData);
}
}
catch(error){
await emit(errorConstants.event, error);
this.logger.error("Error while pushing to sparrow apps", error);
throw error;
}
}
}
module.exports = Event;