I am using the link below for implementing SignalR within react
ASP NET Core Signal R Tutorial
However, this code appears to not follow the current standards and #aspnet/signalr-client has now been marked as obselete with a message saying that #aspnet/signalr must be used
I managed to figure out that the accepted way for creating a hub connection is
// create the connection instance
var hubConnection = new signalR.HubConnectionBuilder()
.withUrl("URL", options)
.withHubProtocol(protocol)
.build();
HOwever, I dont know how to call this within react?
I tried
import signalR, {} from '#aspnet/signalr';
but that gives the error
./src/components/widgets/Chat.js
Attempted import error: '#aspnet/signalr' does not contain a default export (imported as 'signalR').
Does anyone have an updated sample for Signal R with react or know how to do this now?
The package wont install as its obselete
Paul
You can create custom middleware, you dont 'NEED' websockets per se`
This is my current application:
configureStore.js:
import * as SignalR from '#aspnet/signalr';
//to server
export default function configureStore(history, initialState) {
const middleware = [
thunk,
routerMiddleware(history),
SignalrInvokeMiddleware
];
const rootReducer = combineReducers({
...reducers,
router: connectRouter(history)
});
const enhancers = [];
const windowIfDefined = typeof window === 'undefined' ? null : window;
if (windowIfDefined && windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__) {
enhancers.push(windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__());
}
return createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), ...enhancers)
);
}
const connection = new SignalR.HubConnectionBuilder()
.withUrl("/notificationHub")
.configureLogging(SignalR.LogLevel.Information)
.build();
//from server
export function SignalrInvokeMiddleware(store, callback) {
return (next) => (action) => {
switch (action.type) {
case "SIGNALR_GET_CONNECTIONID":
const user = JSON.parse(localStorage.getItem('user'));
connection.invoke('getConnectionId', user.userid)
.then(conid => action.callback());
break;
case "SIGNALR_USER_JOIN_REQUEST":
let args = action.joinRequest;
connection.invoke('userJoinRequest', args.clubId, args.userId);
break;
default:
}
return next(action);
}}
export function signalrRegisterCommands(store, callback) {
connection.on('NotifyUserJoinRequest', data => {
store.dispatch({ type: 'SIGNALR_NOTIFY_USERJOIN_REQUEST', notification: data });
})
connection.start()
.then(() => {
callback();
})
.catch(err => console.error('SignalR Connection Error: ', err));
}
index.jsx:
const store = configureStore(history);
const callback = () => {
console.log('SignalR user added to group');
}
signalrRegisterCommands(store, () => {
console.log('SignalR Connected');
store.dispatch({ type: 'SIGNALR_GET_CONNECTIONID', callback });
});
Related
I'm new to nextjs and I'm creating API on next.js to perform db update using the pg-promise. However, it always hit the WARNING: Creating a duplicate database object for the same connection on console when the app is calling the API.
I tried browsing the docs but couldn't find a solution. I also tried solution (update-2) mentioned on stackoverflow page below, but the warning still exists.
Where should I initialize pg-promise
I think the problem is on the method I used to set the columnset. However I can't find proper way to do it. How should I fix it with pg-promise ?
Db setting code:
import ConfigEnv from 'utils/configuration';
import * as pgLib from 'pg-promise';
const initOptions = {
capSQL: true,
};
const pgp = require('pg-promise')(initOptions);
interface IDatabaseScope {
db: pgLib.IDatabase<any>;
pgp: pgLib.IMain;
}
export function createSingleton<T>(name: string, create: () => T): T {
const s = Symbol.for(name);
let scope = (global as any)[s];
if (!scope) {
scope = {...create()};
(global as any)[s] = scope;
}
return scope;
}
export function getDB(): IDatabaseScope {
return createSingleton<IDatabaseScope>('my-app-db-space', () => {
return {
db: pgp(ConfigEnv.pgp),
pgp
};
});
}
API code:
import {getDB} from 'db/pgpdb';
const {db, pgp} = getDB();
const cs = new pgp.helpers.ColumnSet([
'?detail_id',
'age',
'name'
// 'last_modified_date',
], {
table: 'user_detail',
})
export default async (req, res) => {
try {
// generating the update query where it is needed:
const update = pgp.helpers.update(req.body.content, cs) + ` WHERE v.detail_id = t.detail_id`;
// executing the query
await db
.none(update)
.then(() => {
return res.status(200).end();
})
.catch((error) => {
console.log('error', error);
return res.status(500).send(error);
});
} catch (error) {
console.log(error);
}
};
I'm developing NestJS app which asks Binance Websocket API for some data. And also created a WebSocket server that sends received data to Front. On the Back side I got all data in console.log. But on the Front I got only the first item. I can't understand what's wrong. Can you help me please?
Coin.gateway.ts
import { MessageBody, SubscribeMessage, WebSocketGateway, WebSocketServer } from '#nestjs/websockets';
import { Server } from 'socket.io';
import { from, of, take, map, Observable } from 'rxjs';
import { Coin } from './classes/coin';
import * as coinlist from './list/coins.json'
#WebSocketGateway(811, {transports: ['websocket', 'polling'], cors: true})
export class CoinGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('events')
handleMessage(#MessageBody() data: any) {
console.log('data',data)
const coins = new Coin(coinlist, 'usdt', 'miniTicker')
return coins.getCryptoData().pipe(map((c) => {
return c
}))
}
}
Coin.ts
import { GetCryptocurrencies } from "./abstract/get-cryptocurrencies";
import { WebSocket } from "ws";
import { Logger } from "#nestjs/common";
import { Observable } from "rxjs";
export class Coin extends GetCryptocurrencies {
private readonly logger = new Logger(Coin.name)
private baseUrl: string
private url: string
constructor(coin: { name: string, symbol: string }[], pair: string, method: string) {
super(coin, pair, method)
this.baseUrl = 'wss://stream.binance.com:9443/stream?streams='
this.url = coin.map((c) => {
return `${c.symbol.toLowerCase()}${pair}#${method}`
}).join('/')
}
getCryptoData(): any {
const stream$ = new Observable((observer) => {
const ws = new WebSocket(`${this.baseUrl}${this.url}`)
ws.on('open', () => {
this.logger.log('Connection established')
})
ws.onmessage = (msg: any) => {
const message = JSON.parse(msg.data)
observer.next(message)
}
ws.on('close', () => {
this.logger.log('Connection closed')
})
})
return stream$
}
}
Client UI useEffect hook
useEffect(() => {
const socket = io('ws://localhost:811', {transports: ['websocket']})
socket.on('connect', () => {
console.log('Connection established from client')
socket.emit('events', '', (res: any) => {
console.log(res)
})
const engine = socket.io.engine;
console.log(engine.transport.name); // in most cases, prints "polling"
engine.once("upgrade", () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log(engine.transport.name); // in most cases, prints "websocket"
});
engine.on("packetCreate", ({ type, data }) => {
// called for each packet sent
console.log('Stype', type)
console.log('Sdata', data)
});
})
}, [])
Okay after some hours of researching and I learned that I need just to return this:
return coins.getCryptoData().pipe(map((c) => {
this.server.emit('msg', c)
}))
And receive this message on Front
I am trying to test an axios request, and I need to use an auth token in order to access the endpoint, however my test fails because I am getting "Bearer null" and inputting this into my headers.Authorization. Here is my actual code below
File I'm testing:
this.$axios.get(url, { headers: { Authorization: `Bearer ${localStorage.getItem("access-token")}` } })
.then((response) => {
this.loading = true;
// Get latest barcode created and default it to our "from" input
this.barcodeFrom = response.data.data[response.data.data.length - 1]['i_end_uid'] + 1;
this.barcodeTo = this.barcodeFrom + 1;
this.barcodeRanges = response.data.data;
// Here we add to the data array to make printed barcodes more obvious for the user
this.barcodeRanges.map(item => item['range'] = `${item['i_start_uid']} - ${item['i_end_uid']}`);
// Make newest barcodes appear at the top
this.barcodeRanges.sort((a, b) => new Date(b['created_at']) - new Date(a['created_at']));
})
.catch((error) => {
console.log('Barcode retrieval error:', error);
this.barcodeFrom === 0 ? null : this.snackbarError = true;
})
.finally(() => {
// Edge case when there's no barcode records
this.barcodeFrom === 0 ? this.barcodeTo = 1 : null;
this.loading = false
});
console.log('bcr', this.barcodeRanges);
Test file:
import Vuetify from "vuetify";
import Vuex from "vuex";
import { createLocalVue, shallowMount } from "#vue/test-utils";
import VueMobileDetection from "vue-mobile-detection";
import axios from 'axios';
import index from "#/pages/barcode_logs/index";
describe('/pages/barcode_logs/index.vue', () => {
// Initialize our 3rd party stuff
const localVue = createLocalVue();
localVue.use(Vuetify);
localVue.use(Vuex);
localVue.use(axios);
localVue.use(VueMobileDetection);
// Initialize store
let store;
// Create store
store = new Vuex.Store({
modules: {
core: {
state: {
labgroup:{
current: {
id: 1
}
}
}
}
}
});
// Set-up wrapper options
const wrapperOptions = {
localVue,
store,
mocks: {
$axios: {
get: jest.fn(() => Promise.resolve({ data: {} }))
}
}
};
// Prep spies for our component methods we want to validate
const spycreateBarcodes = jest.spyOn(index.methods, 'createBarcodes');
const createdHook = jest.spyOn(index, 'created');
// Mount the component we're testing
const wrapper = shallowMount(index, wrapperOptions);
test('if barcode logs were retrieved', () => {
expect(createdHook).toHaveBeenCalled();
expect(wrapper.vm.barcodeRanges).toHaveLength(11);
});
});
How do I mock or get the actual auth token in to work in my test?
const setItem = jest.spyOn(Storage.prototype, 'setItem')
const getItem = jest.spyOn(Storage.prototype, 'getItem')
expect(setItem).toHaveBeenCalled()
expect(getItem).toHaveBeenCalled()
You can try to mock localStorage before creating instance of a wrapper like this:
global.localStorage = {
state: {
'access-token': 'superHashedString'
},
setItem (key, item) {
this.state[key] = item
},
getItem (key) {
return this.state[key]
}
}
You can also spy on localStorage functions to check what arguments they were called with:
jest.spyOn(global.localStorage, 'setItem')
jest.spyOn(global.localStorage, 'getItem')
OR
You can delete localVue.use(axios) to let your $axios mock work correctly.
This
mocks: {
$axios: {
get: jest.fn(() => Promise.resolve({ data: {} }))
}
}
is not working because of that
localVue.use(axios)
I have some component which is executing some function from api file. For example :
class Modal extends React.Component {
componentDidMount() {
this.saveModel();
}
saveModel = () => {
return this.setState({loadingRequest: true}, async () => {
await Model.graphqlMutation(request).then(() => {
return this.closeAfterSave();
});
});
}
render() {
return <div>Some divs...</div>
}
}
I am using Apollo Client so I defined my client instance with links
const errorLink = onError(({networkError, graphQLErrors, operation, forward}) => {
if (graphQLErrors && graphQLErrors[0].message === 'Unauthorized' && localStorage.getItem('authToken')) {
return promiseToObservable(refreshToken()).flatMap((res) => {
return forward(operation);
});
}
});
const link = ApolloLink.from([
errorLink,
authLink,
batchHttpLink,
]);
export const client = new ApolloClient({
link,
cache: new InMemoryCache(),
defaultOptions: defaultOptions,
});
The problem: in errorLink I am handling errors but I can't pass it down to component which sent request. I would p.ex. display some message. I know React Context API and I did it, connected with my components, but I don't know how to connect to it from client.
Thanks for suggestions.
How can I transform this websocket as promised in redux ? I wanted to use this library https://www.npmjs.com/package/websocket-as-promised , but I don't know how to implement this in my weksocket.
I need your help PLEASEEE.
This websocket for redux has been created by "exec64" and it's functional in my application but I want that it return a promise.
source: https://exec64.co.uk/blog/websockets_with_redux/
My middleware
import actions from './actions'
const socketMiddleware = (function(){
var socket = null;
const onOpen = (ws,store,token) => evt => {
//Send a handshake, or authenticate with remote end
//Tell the store we're connected
store.dispatch(actions.connected());
}
const onClose = (ws,store) => evt => {
//Tell the store we've disconnected
store.dispatch(actions.disconnected());
}
const onMessage = (ws,store) => evt => {
//Parse the JSON message received on the websocket
var msg = JSON.parse(evt.data);
switch(msg.type) {
case "CHAT_MESSAGE":
//Dispatch an action that adds the received message to our state
store.dispatch(actions.messageReceived(msg));
break;
default:
console.log("Received unknown message type: '" + msg.type + "'");
break;
}
}
return store => next => action => {
switch(action.type) {
//The user wants us to connect
case 'CONNECT':
//Start a new connection to the server
if(socket != null) {
socket.close();
}
//Send an action that shows a "connecting..." status for now
store.dispatch(actions.connecting());
//Attempt to connect (we could send a 'failed' action on error)
socket = new WebSocket(action.url);
socket.onmessage = onMessage(socket,store);
socket.onclose = onClose(socket,store);
socket.onopen = onOpen(socket,store,action.token);
break;
//The user wants us to disconnect
case 'DISCONNECT':
if(socket != null) {
socket.close();
}
socket = null;
//Set our state to disconnected
store.dispatch(actions.disconnected());
break;
//Send the 'SEND_MESSAGE' action down the websocket to the server
case 'SEND_CHAT_MESSAGE':
socket.send(JSON.stringify(action));
break;
//This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);
}
}
})();
export default socketMiddleware
My Action types
export const WS_CONNECTING = 'WS_CONNECTING'
export const WS_CONNECTED = 'WS_CONNECTED'
export const WS_DISCONNECTED = 'WS_DISCONNECTED'
export const WS_CONNECT = 'WS_CONNECT'
export const WS_DISCONNECT = 'WS_DISCONNECT
My app Actions (actions creator)
import * as types from './actionsTypes'
export function wsConnecting() {
return {
type: types.WS_CONNECTING
}
}
export function wsConnected() {
return {
type: types.WS_CONNECTED
}
}
export function wsDisconnected(reason = "No reason") {
return {
type: types.WS_DISCONNECTED,
reason
}
}
export function wsConnect(url) {
return {
type: types.WS_CONNECT,
url: url
}
}
And my reducer
import * as types from '../actions/actionsTypes'
function appReducers(state = [], action) {
switch (action.type) {
case types.WS_CONNECTING:
return Object.assign({}, state, { status: types.WS_CONNECTING });
case types.WS_CONNECTED:
return Object.assign({}, state, { status: types.WS_CONNECTED });
case types.WS_DISCONNECTED:
return Object.assign({}, state, { status: types.WS_DISCONNECTED });
default:
return state
}
}
export default appReducers
I tried to install and import the library websocket-as-promised (https://www.npmjs.com/package/websocket-as-promised), then I replaced just this code in my websocket :
socket = new WebSocket(action.url);
**By this but it not functional **
socket = new WebSocketAsPromised(action.url);
Thanks, I found the solution I wanted.
I share my custom websocket for those who want a websocket as promised with redux:
import actions from './actions'
import WebSocketAsPromised from 'websocket-as-promised';
const socketMiddleware = (function() {
var socket = null;
const onOpen = (ws, store, token) => {
//Send a handshake, or authenticate with remote end
//Tell the store we're connected
store.dispatch(actions.connected());
}
const onClose = (ws, store) => {
//Tell the store we've disconnected
store.dispatch(actions.disconnected());
}
const onMessage = (msg, store) => {
//Parse the JSON message received on the websocket
var msg = msg;
switch (msg.type) {
case "CHAT_MESSAGE":
//Dispatch an action that adds the received message to our state
store.dispatch(actions.messageReceived(msg));
break;
default:
console.log("Received unknown message type: '" + msg.type + "'");
break;
}
}
return store => next => action => {
switch (action.type) {
//The user wants us to connect
case 'CONNECT':
//Start a new connection to the server
if (socket != null) {
socket.close();
}
//Send an action that shows a "connecting..." status for now
store.dispatch(actions.connecting());
//Attempt to connect (we could send a 'failed' action on error)
socket = new WebSocketAsPromised(action.url);
socket.onMessage.addListener((data, jsonData) => onMessage(jsonData, store))
socket.onClose.addListener(() => onClose(socket, store))
socket.open()
.then(() => {onOpen(socket, store, action.token)})
break;
//The user wants us to disconnect
case 'DISCONNECT':
if (socket != null) {
socket.close();
}
socket = null;
//Set our state to disconnected
store.dispatch(actions.disconnected());
break;
//Send the 'SEND_MESSAGE' action down the websocket to the server
case 'SEND_CHAT_MESSAGE':
socket.send(JSON.stringify(action));
break;
//This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);
}
}
})();
export default socketMiddleware
You can also look at redux-websocket-bridge, which can unfold all Web Socket messages into your Redux store, and relay all Redux actions into Web Socket. It would save you some work on your CHAT_MESSAGE and SEND_CHAT_MESSAGE.
On top of unfold/relay actions, it also expose Web Socket events thru as Redux actions.