SignalR listener not getting invoked in React Native - react-native

I am building a mobile app in React Native for a backend written in .NET. The backend has implemented a realtime messaging service using SignalR Hubs. I am using the package react-native-signalr. The connection is getting established, and I can send message to SignalR hub by calling proxy.invoke. The problem is in reception of message. I tried with proxy.on but nothing happens.
componentDidMount(){
const { access_token } = this.props;
// Setup connection to signalr hub.
const hubUrl = `${API_URL}/signalr`;
const connection = signalr.hubConnection(hubUrl);
const proxy = connection.createHubProxy('MessagesHub', {queryParams: { token: access_token }});
// Start connection
connection.start();
// Trying to receive message from SignalR Hub
proxy.on('messageReceived', message => {
console.log(message);
})
proxy.on('sendPrivateMessage', message => {
console.log(message);
})
proxy.on('sentPrivateMessage', message => {
console.log(message);
})
}

when you register a listener with your_proxy.on function ,you must define a function with required parameters and bind it on constructor and then pass it to your_proxy.on function ,see bellow:
constructor(props){
super(props);
this.messageReceived=this.messageReceived.bind(this); // <========== **Important**
this.sendPrivateMessage=this.sendPrivateMessage.bind(this); // <========== **Important**
this.sentPrivateMessage=this.sentPrivateMessage.bind(this); // <========== **Important**
}
messageReceived(message ){
console.log(message);
}
sendPrivateMessage(message ){
console.log(message);
}
sentPrivateMessage(message ){
console.log(message);
}
componentDidMount(){
const { access_token } = this.props;
// Setup connection to signalr hub.
const hubUrl = `${API_URL}/signalr`;
const connection = signalr.hubConnection(hubUrl);
const proxy = connection.createHubProxy('MessagesHub', {queryParams: { token: access_token }});
// Trying to receive message from SignalR Hub
proxy.on('messageReceived', this.messageReceived);
proxy.on('sendPrivateMessage', this.sendPrivateMessage);
proxy.on('sentPrivateMessage', this.sentPrivateMessage);
// Start connection
connection.start();
}

Related

Market data routing to frontend: Alpaca WebSocket -> Node.js WebSocket -> React Frontend

I'm trying to use the websocket example from:
https://github.com/alpacahq/alpaca-trade-api-js/blob/master/examples/websocket_example_datav2.js
In order to connect to the Alpaca V2 data stream. Currently, my stream is working but I'm trying to route my data to the client side using Server Sent Events. My data flow seems like it should be:
Alpaca Data Stream API -> My Node.js server -> React Frontend.
The issue I have is using the DataStream object in the example in order to route the data to the frontend. Since, with the object alone, I don't have any route to subscribe to via Server Sent Events, does this mean that I should also be using either express, socket.io, or ws? Since the all of the ".on_xyz" methods are defined within the DataStream object, I'm not sure how to set up the endpoint properly to allow my frontend to subscribe to it. If anyone knows how to route this datastream information forward it would be greatly appreciated- I'm particularly trying to work with the .onStockQuote method but any of them is fine! I'm simply trying to use Node as an inbetween router so that I don't have to subscribe directly from the frontend (and not use the sdk), because that limits scalability of the API's use.
"use strict";
/**
* This examples shows how to use tha alpaca data v2 websocket to subscribe to events.
* You should use the alpaca api's data_steam_v2, also add feed besides the other parameters.
* For subscribing (and unsubscribing) to trades, quotes and bars you should call
* a function for each like below.
*/
import express from 'express';
const app = express()
const Alpaca = require("#alpacahq/alpaca-trade-api");
const API_KEY = "XYZ_Key";
const API_SECRET = "XYZ_Secret";
const PORT = 3000;
// Add a new message and send it to all subscribed clients
const addMessage = (req, res) => {
const message = req.body;
// Return the message as a response for the "/message" call
res.json(message);
return ;
};
class DataStream {
constructor({ apiKey, secretKey, feed }) {
this.alpaca = new Alpaca({
keyId: apiKey,
secretKey,
feed,
});
const socket = this.alpaca.data_stream_v2;
socket.onConnect(function () {
console.log("Connected");
socket.subscribeForQuotes(["AAPL"]);
// socket.subscribeForTrades(["FB"]);
// socket.subscribeForBars(["SPY"]);
// socket.subscribeForStatuses(["*"]);
});
socket.onError((err) => {
console.log(err);
});
socket.onStockTrade((trade) => {
console.log(trade);
});
socket.onStockQuote((quote) => {
console.log(quote);
});
socket.onStockBar((bar) => {
console.log(bar);
});
socket.onStatuses((s) => {
console.log(s);
});
socket.onStateChange((state) => {
console.log(state);
});
socket.onDisconnect(() => {
console.log("Disconnected");
});
socket.connect();
// unsubscribe from FB after a second
// setTimeout(() => {
// socket.unsubscribeFromTrades(["FB"]);
// }, 1000);
}
}
app.post("/message", addMessage);
let stream = new DataStream({
apiKey: API_KEY,
secretKey: API_SECRET,
feed: "sip",
paper: false,
});
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
});

Change rabbitmq exchange with nestjs

I am using rabbitmq with nestjs. I need to replicate a message from one queue to another. I set up an exchange on rabbitmq to make it work. But how can I change the exchange of rabbitmq inside nestjs?
my api gateway
my current rabbitmq configuration inside nestjs:
constructor( ) {
this.rabbitmq = ClientProxyFactory.create({
transport: Transport.RMQ,
options: {
urls: [`amqp://${this.configService.get<string>('RABBITMQ_USER')}:${this.configService.get<string>('RABBITMQ_PASSWORD')}#${this.configService.get<string>('RABBITMQ_URL')}`],
queue: 'students'
}
})
}
createStudent(#Body() body: CreateStudentDto): Observable<any> {
return this.rabbitmq.send('createStudent', body)
}
my client
#MessagePattern('createStudent')
async createStudent(#Payload() student: Student, #Ctx() context: RmqContext) {
const channel = context.getChannelRef()
const originalMsg = context.getMessage()
try {
let response = await this.studentService.createStudent(student)
await channel.ack(originalMsg)
return response;
} catch(error) {
this.logger.log(`error: ${JSON.stringify(error.message)}`)
const filterAckError = ackErrors.filter(ackError => error.message.includes(ackError))
if (filterAckError.length > 0) {
await channel.ack(originalMsg)
}
}
}
I need the message to be sent to two queues.

Using Secure Websocket on 3 different ports in Nest.js

I have a Nest-Service with the following main.ts:
async function bootstrap() {
if (!!environment.production) {
const app = await NestFactory.create(AppModule, {
httpsOptions: {
key: fs.readFileSync(environment.ssl.SSL_KEY_PATH),
cert: fs.readFileSync(environment.ssl.SSL_CERT_PATH)
},
});
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(3077);
} else {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(3077);
}
}
bootstrap();
And two Gateways within the Service:
#WebSocketGateway(3078)
export class ItemsGateway implements OnGatewayConnection, OnGatewayDisconnect { ... }
#WebSocketGateway(3079)
export class UnitsGateway implements OnGatewayConnection, OnGatewayDisconnect { ... }
Without SSL this is working, but when I use the prod mode I canĀ“t establish a secure connection to domain.tld:3078 and :3079.
How can I get the service to listen on all 3 Ports? I think there is the problem, because certs are only attached to the Server listening on Port: 3077, where all my REST-API stuff goes.
Thx, Dom
Edit: This also worked as there was just on WebsocketServer on the same port as the API -> 3077.
Edit 2:
I also tried this, but then comes the error that address is in use on the second attempt to create() a server:
async function bootstrap() {
if (!!environment.production) {
const httpsOptions = {
key: fs.readFileSync(environment.ssl.SSL_KEY_PATH),
cert: fs.readFileSync(environment.ssl.SSL_CERT_PATH)
};
const server = express();
const app = await NestFactory.create(
AppModule,
new ExpressAdapter(server)
);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.init();
https.createServer(httpsOptions, server).listen(environment.app.port);
https.createServer(httpsOptions, server).listen(environment.websocketPorts.units);
https.createServer(httpsOptions, server).listen(environment.websocketPorts.items);
} else {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(environment.app.port);
}
}
bootstrap();
You need to .create() a separate app for each port on which you listen for wss connections.

Connect signalr with vue / vuex

I am trying to connect SignalR hub to a Vue component but I fail doing that. i googled "vue with signalr" and real almost every link up to second page.
I getting a cors origin, but I dont think that this is the main problem, since my post/get call to web api are working well.
c# port number 63213 , client at 8080
I also using vuex and i am wonder if I should connect in at the store.
here are code examples. I use vue/vuex with typescript falvor.
mounted: function() {
//... under mounted, signalR connection. i am using import * as signalR from "#aspnet/signalr";
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:63213/ChatHub")
.build();
// connecting to the hub
this.hubConnection
.start()
.then(() => console.log("connection started"))
.catch(err => console.log("connecting hub failed err is : ", err));
//at the hub there is a function named broadcastMessage, should return string that will be added to an array. should it be at sotr's getter
this.connection.on("broadcastMessage", function(msg: string) {
this.messages.push({ msg });
});
},
c#
public class Startup
{
public void Configuration(IAppBuilder app)
{
var policy = new CorsPolicy()
{
AllowAnyOrigin = true,
AllowAnyHeader = true,
AllowAnyMethod = true,
SupportsCredentials = true
};
policy.Origins.Add("http://localhost:8080");
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
pot get to web api are working well.
hub
public class ChatHub : Hub
{
public static void SendMessage(string msg)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
hubContext.Clients.All.broadcastMessage(msg, " !! !! ");
}
}
error is:
Access to XMLHttpRequest at 'http://localhost:63213/ChatHub/negotiate' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
should i pass the hub connention to the store?
what am i doing wrong?
thank you.
switched to .core object.
under "Configure"
app.UseCors(builder => builder.WithOrigins("http://localhost:8080").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
app.UseSignalR(route => {route.MapHub<UserHub>("/user-hub");} );
under
ConfigureServices
services.AddSignalR();
services.AddCors();
at vue component (ts)
created: function() {
this.$userHub.$on("user-added-event", this.userAddedEvent);
},
beforeDestroy: function() {
//clean SignalR event
this.$userHub.$off("user-added-event", this.userAddedEvent);
},
user-hub.js used to handle connection.
imported as vue plugin
import { HubConnectionBuilder, LogLevel } from "#aspnet/signalr";
export default {
install(Vue) {
const connection = new HubConnectionBuilder()
.withUrl(`${Vue.prototype.$http.defaults.baseURL}/user-hub`)
.configureLogging(LogLevel.Information)
.build();
const userHub = new Vue();
Vue.prototype.$userHub = userHub;
connection.on("AddUserEvent", (userId, userName) => {
userHub.$emit("user-added-event", { userId, userName });
});
// if connection closed, reopen it
let startedPromise = null;
function start() {
startedPromise = connection.start().catch(err => {
return new Promise((resolve, reject) =>
setTimeout(
() =>
start()
.then(resolve)
.catch(reject),
5000
)
);
});
return startedPromise;
}
connection.onclose(() => start());
start();
}
};
full project will be uploaded to git.

how to get socketid in socket.io(nodejs)

In my nodejs application, i am using socket.io for sockets connection.
I am configuring my server side code like this
socket.io configuration in separate file.
//socket_io.js
var socket_io = require('socket.io');
var io = socket_io();
var socketApi = {};
socketApi.io = io;
module.exports = socketApi;
below is my server.js file in which i am attaching my socket io to the server like this
var socketApi = require('./server/socket_io');
// Create HTTP server.
const server = http.createServer(app);
// Attach Socket IO
var io = socketApi.io;
io.attach(server);
// Listen on provided port, on all network interfaces.
server.listen(port, () => console.log(`API running on localhost:${port}`));
and then i am using socket.io in my game.js file to emit updated user coins like this.
//game.js
var socketIO = require('../socket_io');
function updateUserCoins(userBet) {
userId = mongoose.Types.ObjectId(userBet.user);
User.findUserWithId(userId).then((user) => {
user.coins = user.coins - userBet.betAmount;
user.save((err, updatedUser) => {
socketIO.io.sockets.emit('user coins', {
userCoins: updatedUser.coins,
});
});
})
}
and then in my client side, i am doing something like this,
socket.on('user coins', (data) => {
this.coins = data.userCoins;
});
but with the above implementation, updating coins of any user, updates all user coins at the client side, since all the clients are listening to the same socket user coins.
To solve the above problem, i know that i have to do something like this,
// sending to individual socketid (private message)
socketIO.io.sockets.to(<socketid>).emit('user coins', {
userCoins: updatedUser.coins,
});
but my concern is that how will get <socketid> with my current implementation.
You can get it by listening to connection to your socket.io server :
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Here you have a socket object ( io.on('connection', function (socket) ) and you can get his id with socket.id
So you'll probably need to wrap your code with the connection listener.
Source of the exemple for the connection listener
Socket object description