How can I resolve this problem?(nestjs, rabbitmq) - rabbitmq

I have created a Rabbitmq service.
However, I get the following error.
I want to save the connection of the service in nestjs always in memory.
■ error
{ "timestamp": "2022-11-01T05:15:45.754Z", "message": "Cannot read properties of undefined (reading 'assertQueue')", "level": "error", "stack": "TypeError: Cannot read properties of undefined (reading 'assertQueue')
■ code
import { Injectable } from '#nestjs/common';
import * as amqplib from "amqplib";
#Injectable()
export class RabbitmqService {
connection: any;
channel: any;
constructor() {
const virtualHost = '/';
const portNumber = process.env.RABBITMQ_PORT;
const hostAddress = process.env.RABBITMQ_HOST;
const url = `amqp://guest:guest#${hostAddress}:${portNumber}/${virtualHost}`;
try {
this.connection = amqplib.connect(url);
this.channel = this.connection.createChannel();
console.log('created rabbitmq connection', url);
} catch (e) {
console.log(e);
}
}
async sendToQueue(queueName: string, msg: Object) {
console.log(queueName, msg);
console.log(this.connection.channels)
// const channel = await this.connection.createChannel();
await this.channel.assertQueue(queueName, {durable: true});
this.channel.sendToQueue(queueName, Buffer.from(JSON.stringify(msg)));
}
}

Related

Vue Apollo Upload file crashes Node Maximum call stack size exceeded at _openReadFs

I am trying to setup front end for graphQl file upload with Apollo-boost-upload. The backend code is based on this link
https://dev.to/dnature/handling-file-uploads-with-apollo-server-2-0-14n7.
It's now reaching the resolver breakpoint after adding the following line in the server.js file
const { apolloUploadExpress } = require("apollo-upload-server");
app.use(apolloUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
And after modifying the schema for the upload type
scalar Upload
Here is the Vue component
<input
type="file"
style="display:none"
ref="fileInput"
accept="image/*"
#change="upload"
>
//Upload method
upload({ target: { files = [] } }) {
if (!files.length) {
return;
}
this.logoImage = files[0];
},
//Dispatching action from vue component
this.$store.dispatch("uploadLogo", { image: this.logoImage });
//Vuex action
const uploadLogo = async (context, payload) => {
context.commit("setLoading", true);
try {
const { data } = await apolloClient.mutate({
mutation: UPLOAD_LOGO,
variables: {file: payload.image},
context: {
hasUpload: true,
},
});
context.commit("setLoading", false);
console.log("Logo:", data.uploadLogo);
} catch (error) {
context.commit("setLoading", false);
console.log(error);
}
};
//Mutation
export const UPLOAD_LOGO = gql`
mutation uploadLogo($file: Upload!) {
uploadLogo(file: $file) {
_id
path
filename
mimetype
user {
_id
}
}
}
`;
// Apolloclient config on main.js
import ApolloClient from "apollo-boost-upload";
import { InMemoryCache } from "apollo-boost";
import VueApollo from "vue-apollo";
// Set up Apollo Client
export const defaultClient = new ApolloClient({
uri: "http://localhost:4000/graphql",
cache: new InMemoryCache({
addTypename: false,
}),
fetchOptions: {
credentials: "include",
},
request: (operation) => {
// if no token in local storage, add it
if (!localStorage.someToken) {
localStorage.setItem("someToken", "");
}
// operation adds the token to authorizatrion header, which is sent o backend
operation.setContext({
headers: {
authorization: "Bearer " + localStorage.getItem("someToken"),
},
});
},
onError: ({ graphQLErrors, networkError }) => {
if (networkError) {
console.log("[networkError]", networkError);
}
if (graphQLErrors) {
for (const error of graphQLErrors) {
console.dir(error);
if (error.name === "AuthenticationError") {
// set auth errir in state
store.commit("setError", error);
// signout user to clear error
store.dispatch("signUserOut");
}
}
}
},
});
Here is the updated typedef (old code commented out) from backend if that helps to identify the issue
const logoUploadTypeDefs = gql`
type File {
_id: ID!
path: String!
filename: String!
mimetype: String!
encoding: String!
user: User
}
# input Upload {
# name: String!
# type: String!
# size: Int!
# path: String!
# }
scalar Upload
type Mutation {
uploadLogo(file: Upload!): File
}
type Query {
info: String
logo: File!
}
`;
Now, the Node app crashes with the following log
I had to change "apollo-upload-server" to "graphql-upload"
change 1:
Commented out "apollo-upload-server" and used "graphql-upload"
// const { apolloUploadExpress } = require("apollo-upload-server");]
const {
graphqlUploadExpress, // A Koa implementation is also exported.
} = require("graphql-upload");
And in the middleware, used this
change 2:
app.use(graphqlUploadExpress());
await apolloServer.start();
instead of old code
app.use(apolloUploadExpress());// Not to be used
await apolloServer.start();
Also, in the resolver, I added this
change 3:
Import Upload from graphql-upload in the resolver file
const { GraphQLUpload } = require("graphql-upload");
....
....
const resolvers = {
// This maps the `Upload` scalar to the implementation provided
// by the `graphql-upload` package.
Upload: GraphQLUpload,
Query: {
....
}
Mutations: {
....
}
}
Refer to Apollo Docs for more details. This fixed the issue of Node crashing with error "Maximum call stack size exceeded at _openReadFs..."

Buid Group video calling using WebRTC

WebSocket
Using WebRTC easy to implement peer to peer video calling. But how to implement group video calling using WebRTC and WebSocket in Spring Boot.?
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.call.event.ConnectionHandler;
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ConnectionHandler(), "/call").setAllowedOrigins("*");
}
}
ConnectionHandler
Here Socket Handler.We handle all user session and socket and payoads.
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
System.out.println("Got signal - " + message.getPayload());
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
#Component
public class ConnectionHandler extends TextWebSocketHandler {
List<WebSocketSession>sessions = new CopyOnWriteArrayList<>();
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
System.out.println("Got signal - " + message.getPayload());
for (WebSocketSession webSocketSession : sessions) {
if (webSocketSession.isOpen() && !session.getId().equals(webSocketSession.getId())) {
webSocketSession.sendMessage(message);
}
}
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("-----------------------------------------------------------------------------");
System.out.println("Connection opened!");
System.out.println("-----------------------------------------------------------------------------");
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("-----------------------------------------------------------------------------");
System.out.println("Connection Closed!");
System.out.println("-----------------------------------------------------------------------------");
sessions.add(session);
}
}
WebRTC code
Here WebRTC code for Peer to Peer connection. We using webRTC for getUserMedia and DataChanells.
let peerConnection=null;
let localVideo;
let localStream;
let videoTrack;
let audioTrack;
let stream;
let signal;
var signalingWebsocket = new WebSocket("ws://localhost:8080/call");
signalingWebsocket.onmessage = function(msg) {
console.log("Got message", msg.data);
signal = JSON.parse(msg.data);
switch (signal.type) {
case "offer":
handleOffer(signal);
break;
case "answer":
handleAnswer(signal);
break;
// In local network, ICE candidates might not be generated.
case "candidate":
handleCandidate(signal);
break;
default:
break;
}
};
signalingWebsocket.onopen = init();
function sendSignal(signal) {
if (signalingWebsocket.readyState == 1) {
signalingWebsocket.send(JSON.stringify(signal));
}
};
/*
* Initialize
*/
function init() {
console.log("Connected to signaling endpoint. Now initializing.");
preparePeerConnection();
displayLocalStream(true);
};
/*
* Prepare RTCPeerConnection & setup event handlers.
*/
function preparePeerConnection() {
// Using free public google STUN server.
const configuration = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
};
// Prepare peer connection object
peerConnection = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]});
peerConnection.onnegotiationneeded = async () => {
console.log('onnegotiationneeded');
sendOfferSignal();
};
peerConnection.onicecandidate = function(event) {
if (event.candidate) {
sendSignal(event);
}
};
peerConnection.addEventListener('track', displayRemoteStream);
};
async function displayLocalStream(firstTime) {
console.log('Requesting local stream');
localVideo = document.getElementById('localVideo');
try {
stream = await navigator.mediaDevices.getUserMedia({
video: {
width: 500,
height: 300
},
audio: false
});
console.log('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
videoAudioTrackInfo(localStream);
console.log(stream);
// For first time, add local stream to peer connection.
if (firstTime) {
setTimeout(
function() {
addLocalStreamToPeerConnection(localStream);
}, 2000);
}
// Send offer signal to signaling server endpoint.
sendOfferSignal();
} catch (e) {
alert(`getUserMedia() error: ${e.name}`);
throw e;
}
console.log('Start complete');
}
function videoAudioTrackInfo(localStream) {
videoTrack = localStream.getVideoTracks();
audioTrack = localStream.getAudioTracks();
if (videoTrack.length > 0) {
console.log(`Using video device: ${videoTrack[0].label}`);
}
if (audioTrack.length > 0) {
console.log(`Using audio device: ${audioTrack[0].label}`);
}
}
async function addLocalStreamToPeerConnection(localStream) {
console.log('Starting addLocalStreamToPeerConnection');
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
console.log('localStream tracks added');
};
function sendOfferSignal() {
peerConnection.createOffer(function(offer) {
sendSignal(offer);
peerConnection.setLocalDescription(offer);
}, function(error) {
alert("Error creating an offer");
});
};
function handleOffer(offer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
peerConnection.createAnswer(function(answer) {
peerConnection.setLocalDescription(answer);
sendSignal(answer);
}, function(error) {
alert("Error creating an answer");
});
};
function handleAnswer(answer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
console.log("connection established successfully!!");
};
function handleCandidate(candidate) {
alert("handleCandidate");
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
};
function displayRemoteStream(e) {
console.log('displayRemoteStream');
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
console.log('pc2 received remote stream');
}
};
function leaveMeeting() {
let cnf=confirm("Are you sure for leave meeting!")
if(cnf==true){
console.log('Ending call');
peerConnection.close();
signalingWebsocket.close();
window.location.href = '/';
}else{
console.log('you are in meeting....');
}
};

How to handle message sent from server to client with RSocket?

I try to use RSocketRequester to send a message from the server to the specific client, but I don't know how to handle it on the frontend. The server is Spring Webflux with the controller like this:
data class Message(val message: String)
#Controller
class RSocketController {
private val log = LoggerFactory.getLogger(RSocketController::class.java)
#MessageMapping("say.hello")
fun sayHello(message: String): Flux<Message> {
log.info("say hello {}", message)
return Flux.just(Message("server says hello"))
}
#MessageMapping("say.hi")
fun sayHi(message: String, rSocketRequester: RSocketRequester): Flux<Message> {
log.info("say hi {}", message)
rSocketRequester
.route("say.hello")
.data(Message("server says hi hello ;)"))
.send()
.subscribe()
return Flux.just(Message("server says hi!!"))
}
}
On the frontend I use rsocket-js. The sayHello method works just fine (request-stream), but when I call the sayHi method I want to send two messages from the server. The first one to say.hello endpoint, and the second to say.hi endpoint. I've got rsocket-js implementation like this:
sayHello() {
console.log("say hello");
this.requestStream("say.hello");
},
sayHi() {
console.log("say hi");
this.requestStream("say.hi");
},
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
transport
});
client.connect().subscribe({
onComplete: socket => {
this.socket = socket;
console.log("complete connection");
},
onError: error => {
console.log("got connection error");
console.error(error);
},
onSubscribe: cancel => {
console.log("subscribe connection");
console.log(cancel);
}
});
},
requestStream(url) {
if (this.socket) {
this.socket
.requestStream({
data: url + " from client",
metadata: String.fromCharCode(url.length) + url
})
.subscribe({
onComplete: () => console.log("requestStream done"),
onError: error => {
console.log("got error with requestStream");
console.error(error);
},
onNext: value => {
// console.log("got next value in requestStream..");
console.log("got data from sever");
console.log(value.data);
},
// Nothing happens until `request(n)` is called
onSubscribe: sub => {
console.log("subscribe request Stream!");
sub.request(2147483647);
// sub.request(3);
}
});
} else {
console.log("not connected...");
}
}
I can see both messages in Google Chrome DevTools -> Network -> rsocket. So the client receives them but I can't catch in the code the one sent by RSocketRequester.
It seems that the server uses fireAndForget method. How to handle it on the client side?
As #VladMamaev said, we can provide a responder to the client like in this example https://github.com/rsocket/rsocket-js/blob/master/packages/rsocket-examples/src/LeaseClientExample.js#L104
For me, fireAndForget method is enough.
export class EchoResponder {
constructor(callback) {
this.callback = callback;
}
fireAndForget(payload) {
this.callback(payload);
}
}
import { EchoResponder } from "~/assets/EchoResponder";
...
const messageReceiver = payload => {
//do what you want to do with received message
console.log(payload)
};
const responder = new EchoResponder(messageReceiver);
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
responder: responder,
transport
});

Apollo Server & 4xx status codes

Currently, my Apollo Server(running on HapiJS) returns HTTP 200 for every request, including failed ones.
I would like the GraphQL server to return HTTP 4xx for unsuccessful requests. The primary reason for it is that I want to set up monitoring for my ELB.
I know that Apollo Server has an engine platform, but I want to implement it using my current infrastructure.
Any ideas of how I could accomplish that? I tried to capture 'onPreResponse' event for my HapiJS server but I couldn't modify status code there.
After reading this answer. Here is a solution by modifying the hapijs plugin graphqlHapi of hapiApollo.ts file.
server.ts:
import { makeExecutableSchema } from 'apollo-server';
import { ApolloServer, gql } from 'apollo-server-hapi';
import Hapi from 'hapi';
import { graphqlHapi } from './hapiApollo';
const typeDefs = gql`
type Query {
_: String
}
`;
const resolvers = {
Query: {
_: () => {
throw new Error('some error');
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const port = 3000;
async function StartServer() {
const app = new Hapi.Server({ port });
graphqlHapi.register(app, { path: '/graphql', graphqlOptions: { schema } });
app.ext('onPreResponse', (request: any, h: any) => {
const response = request.response;
if (!response.isBoom) {
return h.continue;
}
return h.response({ message: response.message }).code(400);
});
await app.start();
}
StartServer()
.then(() => {
console.log(`apollo server is listening on http://localhost:${port}/graphql`);
})
.catch((error) => console.log(error));
hapiApollo.ts:
import Boom from 'boom';
import { Server, Request, RouteOptions } from 'hapi';
import { GraphQLOptions, runHttpQuery, convertNodeHttpToRequest } from 'apollo-server-core';
import { ValueOrPromise } from 'apollo-server-types';
export interface IRegister {
(server: Server, options: any, next?: Function): void;
}
export interface IPlugin {
name: string;
version?: string;
register: IRegister;
}
export interface HapiOptionsFunction {
(request?: Request): ValueOrPromise<GraphQLOptions>;
}
export interface HapiPluginOptions {
path: string;
vhost?: string;
route?: RouteOptions;
graphqlOptions: GraphQLOptions | HapiOptionsFunction;
}
const graphqlHapi: IPlugin = {
name: 'graphql',
register: (server: Server, options: HapiPluginOptions, next?: Function) => {
if (!options || !options.graphqlOptions) {
throw new Error('Apollo Server requires options.');
}
server.route({
method: ['GET', 'POST'],
path: options.path || '/graphql',
vhost: options.vhost || undefined,
options: options.route || {},
handler: async (request, h) => {
try {
const { graphqlResponse, responseInit } = await runHttpQuery([request, h], {
method: request.method.toUpperCase(),
options: options.graphqlOptions,
query:
request.method === 'post'
? // TODO type payload as string or Record
(request.payload as any)
: request.query,
request: convertNodeHttpToRequest(request.raw.req),
});
// add our custom error handle logic
const graphqlResponseObj = JSON.parse(graphqlResponse);
if (graphqlResponseObj.errors && graphqlResponseObj.errors.length) {
throw new Error(graphqlResponseObj.errors[0].message);
}
const response = h.response(graphqlResponse);
Object.keys(responseInit.headers as any).forEach((key) =>
response.header(key, (responseInit.headers as any)[key]),
);
return response;
} catch (error) {
// handle our custom error
if (!error.name) {
throw Boom.badRequest(error.message);
}
if ('HttpQueryError' !== error.name) {
throw Boom.boomify(error);
}
if (true === error.isGraphQLError) {
const response = h.response(error.message);
response.code(error.statusCode);
response.type('application/json');
return response;
}
const err = new Boom(error.message, { statusCode: error.statusCode });
if (error.headers) {
Object.keys(error.headers).forEach((header) => {
err.output.headers[header] = error.headers[header];
});
}
// Boom hides the error when status code is 500
err.output.payload.message = error.message;
throw err;
}
},
});
if (next) {
next();
}
},
};
export { graphqlHapi };
Now, when the GraphQL resolver throws an error, the client-side will receive our custom response with Http status code 400 instead of 200 status code with GraphQL errors response.
General from the browser:
Request URL: http://localhost:3000/graphql
Request Method: POST
Status Code: 400 Bad Request
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade
The response body is: {"message":"some error"}

Possible Unhandled Promise Rejection(id:0)

I meet something strange and found no answer.
I met the warning as below:
Possible Unhandled Promise Rejection(id:0)
TypeError: undefined is not a function (evaluating '_api2.default.getResp(URL, "GET")'....
Below is shortened code like what my source code is.
The "Pass Code" works fine and "Warning Code" shows warning as above.
Why Api.getResp goes wrong but Api.loggedin.getResp works OK? They are so similar!!
Warning Code
const GetData = {
async fetchFeed() {
console.log(`Api = ${Api}`); // Api = [Object Object]
console.log(`Api.getResp = ${Api.getResp}`); // Api.getRest = undefined
const { success, content } = await Api.getResp(`${URL}`, 'GET');
if (success) {
.....
}
},
};
module.exports = FetchApi;
Pass Code
const GetData2 = {
async syncData(): void {
const { success, content } = await Api.loggedin.getResp(`${URL}`, 'GET');
.....
}
}
API
const Api = {
async getResp(url: string, method: string): Object {
try {
const response = await fetch(url,
{ method,
null,
{ 'content-type' : 'application/json' },
});
......
} catch (error) {
console.log(`fetch url: ${url}, error: ${error}`);
}
return result;
},
loggedin: {
async getResp(url: string, method: string): Object {
const accessToken = await this._getAccessToken();
....
return result;
},
},