Node/RabbitMQ - Send consumer response to nodejs route - node.js

I am handling nodejs requests through RabbitMQ. My producer receives requests through nodejs route and sends them to consumer, which then creates a document in the db by the data received from the request.
Here is my route
router.post("/create-user", async(req: Request, res: Response) => {
const msg = JSON.stringify(req.body);
const send = await Producer(msg);
});
Here is my Producer class
import amqp from "amqplib/callback_api";
export async function Producer(message: string) {
amqp.connect("amqp://localhost", (error0, connection) => {
if (error0) {
throw error0;
}
connection.createChannel((error1, channel) => {
if (error1) {
throw error1;
}
let queue = "hello";
channel.assertQueue(queue, {
durable: false,
});
channel.sendToQueue(queue, Buffer.from(message));
console.log(" [x] Sent %s", message);
});
});
}
And my consumer
import amqp from "amqplib/callback_api";
import {
User
} from "../models/user";
export class ConsumerClass {
public async ConsumerConnection() {
amqp.connect("amqp://localhost", (error0, connection) => {
if (error0) {
throw error0;
} else {
this.ConsumerTask(connection);
}
});
}
public async ConsumerTask(connection: amqp.Connection) {
connection.createChannel((error1, channel) => {
if (error1) {
throw error1;
}
let queue = "hello";
channel.assertQueue(queue, {
durable: false,
});
channel.prefetch(1);
console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);
channel.consume(queue, async(msg) => {
console.log(" [x] Received %s", msg.content.toString());
const data = JSON.parse(msg.content.toString());
const user = new User({
name: data.name,
phone: data.phone,
company: data.company,
});
await user.save();
}, {
noAck: true,
});
});
}
}
I want to send the Json document of the user created from the consumer to the route so that the client can get the created user as a response. How can i achieve this and what am i doing wrong?

What you want is a response event from the consumer to the producer. Now, this is where you can create a function that acts as a Remote Procedure Call.
Thus instead of two events, there will be 2 events e1 and e2. Here's a small diagram to explain this stuff ( disclaimer - I am bad at drawing). I guess you can manage the coding part of this.

Related

NodeJS net.socket pipeline transform while loop not wait

My transform pipeline not wait until message buffer loop end and not receive message from destination.
It can send out only when another message trigger and send as two messages together. The destination can receive two messages at that time.
How will I make it await until message buffer read until the end to able to send out individual messages, please ?
Start.js
import Proxy from "./Proxy.js";
const proxy = Proxy();
proxy.listen('4000');
Proxy.js
import net from "net";
import {filter} from '../utils/transformStream.js';
export default () =>
net.createServer((con1) => {
const con2 = net.connect(
'1234',
'127.0.0.1'
);
pipeline(con1, con2, filter(), con1, (err) => {
console.log("pipeline closed", err);
});
con2.on("data", async (data) => {
try {
console.log("sent from con2:", data);
}
}
}
con1.on("data", async (data) => {
try {
console.log("sent from con1:", data);
}
}
}
transformStream.js
import { Transform } from 'stream';
import { messageBuffer2 } from './msgBuffer2.js';
const filter = () => {
return new Transform({
async transform(chunk, encoding, cb) {
if (chunk.toString("utf-8").includes("Unprocessed")) {
cb(null, '');
} else {
messageBuffer2.push(chunk.toString("utf-8"));
while (!messageBuffer2.isFinished()) {
const message = messageBuffer2.handleData();
cb(null, message);
}
}
}
});
};
export { filter };
Thanks.

Timeout on socket io listeners

I saw this documentation for socketio emit() with timeout implementation
https://socket.io/docs/v3/emitting-events/#acknowledgements
I am implementing it on listeners.
I have a code as follows:
app.post('/submitmessage', async (req, res) => {
const reqBody = req.body;
const roomId = reqBody['custom-roomid'];
socketConnection.join(roomId);
const hasRoom = io.sockets.adapter.rooms.has(roomId);
if (hasRoom) {
io.to(roomId).emit("room created", {
roomId: roomId,
data: reqBody.sendData
});
}
// this emit has a listener on client displaying a modal with message and buttons
// like SAVE, OK, CANCEL
io.to(roomId).emit("question", {
data: reqBody.sendData,
browserId: reqBody.browserId,
sessionId: reqBody.sessionId,
roomId: roomId
});
let reply = '';
// this listener is listening for which button was clicked by the user on the
// client passed on by variable warres
socketConnection.on('return-warning-response', ({ warres }) => {
reply = warres;
});
// waiting for 2 secs
await sleep(20000);
socketConnection.leave(roomId);
if (reply === '') {
res.sendStatus(404);
}
res.send(reply);
});
When I implement it on the listener like below:
socketConnection.on('return-warning-response', ({ warres }), withTimeout(() => {
console.log("success!");
}, () => {
console.log("timeout!");
}, 20000));
It gives error: UnhandledPromiseRejectionWarning: ReferenceError: warres is not defined
Is there an alternative for a timeout functionality on listeners?

Acknowledge RabbitMQ message after socket IO event received from React browser

I have a Node server which consumes messages from a RabbitMQ queue and forwards them to a React frontend as a socket.io event. In the frontend, I have a button click which sends a socket.io event back to the Node server.
Currently, the Node server only logs the receipt of the socket.io event. In addition to logging, I would like to send a message ack to the RabbitMQ server upon receipt of the socket.io event.
The logging is working fine, but I've been struggling with the message acknowledgement part.
My node server looks like this:
server.js
const io = require('./socket');
const amqp = require('amqplib/callback_api');
const CONFIG = require('./config.json');
amqp.connect(`amqp://${CONFIG.host}`, (err, connection) => {
if (err) {
throw err;
}
connection.createChannel((err, channel) => {
if (err) {
throw err;
}
const queue = CONFIG.queueName;
channel.assertQueue(queue, {
durable: true
});
console.log(` [*] Waiting for messages in ${queue}.`);
channel.consume(queue, function(msg) {
console.log(' [x] Request received from RabbitMQ: %s', msg.content.toString());
io.client.emit('sendReview', msg.content.toString());
}, {
noAck: false
});
})
});
socket.js
const io = require('socket.io')();
const port = process.env.PORT || 8000;
module.exports = {
client : any = io.on('connection', (client) => {
console.log(' [*] New client connected with ID: ' + client.id);
client.on('reportReview', (msg) => {console.log(` [x] Response received from browser: ${msg}`)});
client.on('disconnect', () => console.log(` [*] User ${client.id} disconnected.`));
})
};
io.listen(port);
console.log(`Listening on port ${port}`);
My frontend looks like this:
App.js
import React, { Component } from "react";
import * as API from './api';
export default class App extends Component {
constructor(props, context) {
super(props, context);
this.state = {
data: ["Whoops - no reviews available"],
};
this.updateReview = this.updateReview.bind(this);
this.onMessageReceived = this.onMessageReceived.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
API.reportClick(this.state.data[0]);
this.updateReview()
}
updateReview() {
const newArray = this.state.data.slice(1);
if (newArray.length === 0) {
this.setState({data: ["Whoops - no reviews available"]})
} else {
this.setState({data: newArray})
}
}
onMessageReceived(msg) {
console.log(`Request for review received: ${msg}`);
const updatedData = this.state.data.concat(msg);
this.setState({data: updatedData});
if (this.state.data[0] === "Whoops - no reviews available") {
this.updateReview()
}
}
componentDidMount() {
API.subscribe(this.onMessageReceived)
}
render() {
return (
<div className="App">
<p className="App-intro">
Click to confirm review #: {this.state.data[0]}
</p>
<button onClick={this.handleClick}>Click</button>
</div>
);
}
}
Api.js
import clientSocket from 'socket.io-client';
const socket = clientSocket('http://localhost:8000');
function subscribe(onMessageReceived) {
socket.on('sendReview', onMessageReceived);
}
function reportClick(msg) {
socket.emit('reportReview', msg);
}
export { reportClick, subscribe };
As far as I understand, in order to send a message ack I would have to call channel.ack(msg); somewhere on the Node server. However, I am not sure how to pass the channel object to the io module? I have also tried having the socket.io code in server.js so I would have access to the channel object but have not been able to get this to work, either - I have not been able to get the amqp connection and socket.io connection to work together other than using my current approach of having an io module.
Any help would be very much appreciated.
I ended up getting it to work by having the socket code in server.js like this:
const io = require('socket.io')();
function socketIOHandler(callback) {
io.on('connection', (socket) => {
socket.on('error', function(err) {
console.log(err.stack);
});
callback(socket);
});
}
var amqpConn = null;
// start amqp connection to rabbit mq
function start() {
amqp.connect(`amqp://${CONFIG.host}`, (err, connection) => {
if (err) {
throw err;
}
amqpConn = connection;
// start consume worker when connected
startWorker();
});
}
function startWorker() {
socketIOHandler((socket) => {
amqpConn.createChannel((error, channel) => {
... <---- all the bits as before
socket.on('msgSent', (msg) => {
channel.ack(msg);
});
})
});
io.listen(port);
}
start();

Fetching data from MQTT to Vue app instance

How do can I fetch data from MQTT to Vue app, I've established a properly working connection and I can console log the data but I'm not able to load the data to component's data property.
created() {
client.on("connect", function() {
console.log("MQTT Connected");
client.subscribe("#", function(err) {
console.log(err);
});
});
client.on("message", (topic, message) => {
console.log("topic:", topic);
console.log(message.toString());
this.mqttData = JSON.parse(message.toString());
});
},
data() {
return {
mqttData: {}
}
};
Whenever I try to log the mqttData in console it seems to be a empty object. When I printed this inside of the client.on function I've got the correct Vue instance with all of it's fields and methods. This really bothers me because I can access the Vue object but I cannot modify it's contents.
Maybe try this in the "mounted" lifecycle hook. Here's an example of something I use that's listening to a websocket. It should be similar implementation to your application
mounted() {
let connection = new WebSocket('wss://somesocket.net/ws')
connection.onmessage = (event) => {
console.log("Data received!")
console.log(event.data)
const data = JSON.parse(event.data)
this.ws_data = data
}
connection.onopen = (event) => {
console.log("Connected! Waiting for data...")
}
},
This is how I did it using vue-mqtt package.
export default {
data () {
return {
sensor: ''
}
},
mqtt: {
/** Read incoming messages from topic test*/
'test' (data) {
this.sensor = data
console.log(data)
}
},
created () {
},
async mounted () {
this.$mqtt = await this.$mqtt
this.$mqtt.publish('test', 'hello from vuemqtt yo !!!!')
this.$mqtt.subscribe('test')
}
}

Can not emit event with socketid

I am trying to write an integration test for socket io. I am using try-catch for server event when the error is catched I emit an event for the client to handle the error
io.of('/rooms').on('connection', socket => {
const socketId = socket.id;
console.log('server socketid', socketId);
const userRepository = getCustomRepository(UserRepository);
const conversationToUserRepository = getCustomRepository(ConversationToUserRepository);
socket.on('initGroupChat', async (users_id, fn) => {
try {
const [user, listConversationToUser] = await Promise.all([
userRepository.findOne({
where: {
users_id,
},
}),
conversationToUserRepository.find({
where: {
users_id,
},
}),
]);
if (!user) {
throw new NotFoundError('User not found with id: ' + users_id);
}
console.log(2);
user.socket_id = socket.id;
await userRepository.save(user);
for (const item of listConversationToUser) {
socket.join(item.conversation_id.toString());
}
fn('init group chat success');
} catch (error) {
io.to(socketId).emit('error', errorHandlerForSocket(error));
console.log(10);
}
});
});
but on the socket client, nothing happens. here is the code socket client:
it.only('Init Group Chat with error', done => {
socket = io.connect(`http://localhost:${env.app.port}/rooms`, {
transports: ['websocket']
});
const id = 11111;
socket.emit('initGroupChat', id, (response: any) => {
console.log('response', response);
});
socket.on('error', (error: any) => {
console.log('error3', error);
done();
});
});
on the error event, the console.log did not show on the terminate. it didn't catch the event I emit on the server.
can anyone help me fix this issue
Every time the client Refresh socketId changes.
Server :
io.emit("send_to_client", {
userId: 112233,
data: "Hello user 112233"
});
Client :
var userLogin = 112233;
socket.on("send_to_client", function(res) {
if(res.userId === userLogin)
//somethingElse
});

Resources