I'm trying to call an event, but an error occurs: UnhandledPromiseRejectionWar
ning: TypeError: Cannot read property 'emit' of undefined
How it is possible to bypass it? nodejs
import { io } from 'socket.io-client';
import { EventEmitter2, OnEvent } from '#nestjs/event-emitter';
import { SynchronizeService } from './synchronize.service';
import { UserService } from '../user/user.service';
const serverAddress = 'http://localhost:3050'
const socket = io(serverAddress, { reconnection: true })
export class SocketClient {
constructor(
private readonly synchronizeService: SynchronizeService,
private eventEmitter: EventEmitter2,
private readonly userService: UserService
) {}
#OnEvent('order.created')
async handler(req):Promise<void> {
try{
socket.on('synchronize', () => {
});
socket.on('send_from_server', async ({ msg }) => {
await this.eventEmitter.emitAsync('order.came')
});
socket.emit('synchronize', req);
} catch (e) {
console.log(e);
}
}
}
Related
I have a websocket, which receives a message from the client and then sends back the message. When using two browsers, both receive their own messages. But in the server logs, I cannot see both messages.
How can I make it globalized so I can see both messages, is it through creating another websocket which will get these messages and it will send it back?
Frontend:
import { isPlatformBrowser } from '#angular/common';
import { Inject, Injectable } from '#angular/core';
import { PLATFORM_ID } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class WebsocketService {
messages: Array<any> = [];
chatWebSocket: WebSocket | undefined = undefined;
createSocket() {
if (isPlatformBrowser(this.platformId)) {
this.chatWebSocket = new WebSocket('ws://localhost:3000/ws');
}
}
send(msg: string) {
console.log(this.chatWebSocket);
(this.chatWebSocket as unknown as WebSocket).send(msg);
}
recv() {
if (!this.chatWebSocket) return;
(this.chatWebSocket as WebSocket).addEventListener('message', (e) => {
this.messages.push(e.data);
});
}
constructor(#Inject(PLATFORM_ID) public platformId: Object) {}
}
Server:
pub async fn chat_ws(
ws: WebSocketUpgrade,
user_agent: Option<TypedHeader<UserAgent>>,
) -> impl IntoResponse {
if let Some(TypedHeader(user_agent)) = user_agent {
println!("`{}` connected", user_agent.as_str());
}
ws.on_upgrade(handle_chat_socket)
}
pub async fn handle_chat_socket(mut socket: WebSocket) {
loop {
if let Some(msg) = socket.recv().await {
if let Ok(msg) = msg {
match msg {
Message::Text(a) => {
socket.send(Message::Text(String::from(a.clone()))).await;
},
_ => println!("Other"),
}
}
}
}
}
I am not getting any clue how to mock a method. I have to write a unit test for this function:
index.ts
export async function getTenantExemptionNotes(platform: string) {
return Promise.all([(await getCosmosDbInstance()).getNotes(platform)])
.then(([notes]) => {
return notes;
})
.catch((error) => {
return Promise.reject(error);
});
}
api/CosmosDBAccess.ts
import { Container, CosmosClient, SqlQuerySpec } from "#azure/cosmos";
import { cosmosdbConfig } from "config/Config";
import { Workload } from "config/PlatformConfig";
import { fetchSecret } from "./FetchSecrets";
export class CosmoDbAccess {
private static instance: CosmoDbAccess;
private container: Container;
private constructor(client: CosmosClient) {
this.container = client
.database(cosmosdbConfig.database)
.container(cosmosdbConfig.container);
}
static async getInstance() {
if (!CosmoDbAccess.instance) {
try {
const connectionString = await fetchSecret(
"CosmosDbConnectionString"
);
const client: CosmosClient = new CosmosClient(connectionString);
// Deleting to avoid error: Refused to set unsafe header "user-agent"
delete client["clientContext"].globalEndpointManager.options
.defaultHeaders["User-Agent"];
CosmoDbAccess.instance = new CosmoDbAccess(client);
return CosmoDbAccess.instance;
} catch (error) {
// todo - send to app insights
}
}
return CosmoDbAccess.instance;
}
public async getAllNotesForLastSixMonths() {
const querySpec: SqlQuerySpec = {
// Getting data from past 6 months
query: `SELECT * FROM c
WHERE (udf.convertToDate(c["Date"]) > DateTimeAdd("MM", -6, GetCurrentDateTime()))
AND c.IsArchived != true
ORDER BY c.Date DESC`,
parameters: [],
};
const query = this.container.items.query(querySpec);
const response = await query.fetchAll();
return response.resources;
}
}
export const getCosmosDbInstance = async () => {
const cosmosdb = await CosmoDbAccess.getInstance();
return cosmosdb;
};
index.test.ts
describe("getExemptionNotes()", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("makes a network call to getKustoResponse which posts to axios and returns what axios returns", async () => {
const mockNotes = [
{
},
];
const cosmosDBInstance = jest
.spyOn(CosmoDbAccess, "getInstance")
.mockReturnValue(Promise.resolve(CosmoDbAccess.instance));
const kustoResponseSpy = jest
.spyOn(CosmoDbAccess.prototype, "getAllNotesForLastSixMonths")
.mockReturnValue(Promise.resolve([mockNotes]));
const actual = await getExemptionNotes();
expect(kustoResponseSpy).toHaveBeenCalledTimes(1);
expect(actual).toEqual(mockNotes);
});
});
I am not able to get instance of CosmosDB or spyOn just the getAllNotesForLastSixMonths method. Please help me code it or give hints. The complexity is because the class is singleton or the methods are static and private
i have a messaging room application that create a discussion chat foreach room between users signed in with same room ,
i m facing this error :
Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.
i used 3 functions :
getChatMessages() : to get all chat messages from firestore for users
with same room
getCurrentRoom():to get the room of the connected user
getUsers(): return all users with same room
chat.services.ts
import { Injectable } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/compat/auth';
import { AngularFirestore } from '#angular/fire/compat/firestore';
import { Observable } from 'rxjs';
import { Timestamp } from 'rxjs/internal/operators/timestamp';
import { switchMap,map, timestamp, filter } from 'rxjs/operators';
import { query, orderBy, limit, where } from "firebase/firestore";
import firebase from 'firebase/compat/app';
export interface User {
uid: string;
email: string;
displayName:string;
username?:string;
room?:string
}
export interface Message {
createdAt: firebase.firestore.FieldValue;
id: string;
from: string;
msg: string;
fromName: string;
myMsg: boolean;
}
#Injectable({
providedIn: 'root'
})
export class ChatService {
currentUser: User ;
currentRoom:string="";
updatedRoom:string="";
constructor(private afAuth: AngularFireAuth, private afs: AngularFirestore) {
this.afAuth.onAuthStateChanged((user) => {
this.currentUser=user;
console.log("current email"+this.currentUser.email);
});
}
async signup({ username,email, password,room }): Promise<any> {
const credential = await this.afAuth.createUserWithEmailAndPassword(
email,
password
);
const uid = credential.user.uid;
return this.afs.doc(
`users/${uid}`
).set({
uid,
email: credential.user.email,
username:username,
room:room,
})
}
signIn({ email, password }) {
return this.afAuth.signInWithEmailAndPassword(email, password);
}
signOut(): Promise<void> {
return this.afAuth.signOut();
}
addChatMessage(msg) {
return this.afs.collection('messages').add({
createdAt:firebase.firestore.FieldValue.serverTimestamp(),//firebase.default.firestore.Timestamp,
msg: msg,
from: this.currentUser.uid
});
}
async getChatMessages() {
let users = [];
return (await this.getUsers()).pipe(
switchMap(res => {
users = res;
console.log("resssssss"+res);
return this.afs.collection('messages', ref => ref.orderBy('createdAt','asc')).valueChanges({ idField: 'id' }) as Observable<Message[]>;
}),
map(messages => {
console.log("messages"+messages);
// Get the real name for each user
for (let m of messages) {
m.fromName = this.getUserForMsg(m.from, users);
m.myMsg = this.currentUser.uid === m.from;
}
return messages
})
)
}
public async getCurrentRoom() {
await this.afs.collection('users', ref => ref.where("email", "==", this.currentUser.email)).get().toPromise()
.then(snapshot => {
snapshot.forEach(doc => {
this.currentRoom=JSON.parse(JSON.stringify(doc.data())).room;
console.log("current room" + this.currentRoom);
});
});
}
public async getUsers() {
console.log("this room" + this.currentRoom);
return this.afs.collection('users', ref => ref.where("room", "==", this.currentRoom)).valueChanges({
idField: 'uid'
}) as Observable < User[] > ;
}
private getUserForMsg(msgFromId, users: User[]): string {
for (let usr of users) {
if (usr.uid == msgFromId) {
return usr.username ?? 'undefind';
}
}
return 'Deleted';
}
}
my chat.page.ts :
import { Component, OnInit, ViewChild } from '#angular/core';
import { IonContent } from '#ionic/angular';
import { Observable } from 'rxjs';
import { ChatService } from '../chat.service';
import { Router } from '#angular/router';
import { AngularFireStorage, AngularFireUploadTask } from '#angular/fire/compat/storage';
import { AngularFirestore, AngularFirestoreCollection } from '#angular/fire/compat/firestore';
import { finalize, tap } from 'rxjs/operators';
export interface FILE {
name: string;
filepath: string;
size: number;
}
#Component({
selector: 'app-chat',
templateUrl: './chat.page.html',
styleUrls: ['./chat.page.scss'],
})
export class ChatPage implements OnInit {
ngFireUploadTask: AngularFireUploadTask;
progressNum: Observable<number>;
progressSnapshot: Observable<any>;
fileUploadedPath: Observable<string>;
room:any;
files: Observable<FILE[]>;
ImgtoSend:any;
FileName: string;
FileSize: number;
isImgUploading: boolean;
isImgUploaded: boolean;
private ngFirestoreCollection: AngularFirestoreCollection<FILE>;
#ViewChild(IonContent) content: IonContent;
messages:any=[];
newMsg = '';
constructor(private angularFirestore: AngularFirestore,
private angularFireStorage: AngularFireStorage,private chatService: ChatService, private router: Router) {
this.isImgUploading = false;
this.isImgUploaded = false;
this.ngFirestoreCollection = angularFirestore.collection<FILE>('filesCollection');
this.files = this.ngFirestoreCollection.valueChanges();
}
ngOnInit() {
this.messages= this.chatService.getChatMessages();
}
sendMessage() {
this.chatService.addChatMessage(this.newMsg).then(() => {
this.newMsg = '';
this.content.scrollToBottom();
});
}
signOut() {
this.chatService.signOut().then(() => {
this.router.navigateByUrl('/login', { replaceUrl: true });
});
}
fileUpload(event: FileList) {
const file = event.item(0)
if (file.type.split('/')[0] !== 'image') {
console.log('File type is not supported!')
return;
}
this.isImgUploading = true;
this.isImgUploaded = false;
this.FileName = file.name;
const fileStoragePath = `filesStorage/${new Date().getTime()}_${file.name}`;
console.log("filestoragepath"+fileStoragePath);
const imageRef = this.angularFireStorage.ref(fileStoragePath);
console.log("image ref"+imageRef);
this.ngFireUploadTask = this.angularFireStorage.upload(fileStoragePath, file);
this.ImgtoSend=this.FileName;
console.log("image to Send"+this.ImgtoSend);
this.progressNum = this.ngFireUploadTask.percentageChanges();
this.progressSnapshot = this.ngFireUploadTask.snapshotChanges().pipe(
finalize(() => {
this.fileUploadedPath = imageRef.getDownloadURL();
console.log("uploaded path"+this.fileUploadedPath);
this.fileUploadedPath.subscribe(resp=>{
this.fileStorage({
name: file.name,
filepath: resp,
size: this.FileSize
});
this.isImgUploading = false;
this.isImgUploaded = true;
},error => {
console.log(error);
})
}),
tap(snap => {
this.FileSize = snap.totalBytes;
})
)
}
fileStorage(image: FILE) {
const ImgId = this.angularFirestore.createId();
this.ngFirestoreCollection.doc(ImgId).set(image).then(data => {
console.log("data"+data);
}).catch(error => {
console.log(error);
});
}
}
enter code here
First, you have to eleminate all the code that is not relevant to the question to help people help you.
Second, the issue is simple:
What the compiler is telling you here is, Hey Mohammed Amir, *ngFor="" is used to loop through an Array of objects while you are passing to it an object literal.
Check the value of the property you bind to *ngFor="let msg of [YourAssumedArrayFromEndpointResponse]" in your template you will find that YourAssumedArrayFromEndpointResponse is not an array. That's why the compiler is complaining
This is rather a stylistic question. I'm using Pino in some of my Javascript/Typescript microservices. As they're running on AWS I'd like to propagate the RequestId.
When one of my functions is invoked, I'm creating a new child logger like this:
const parentLogger = pino(pinoDefaultConfig)
function createLogger(context) {
return parentLogger.child({
...context,
})
}
function createLoggerForAwsLambda(awsContext) {
const context = {
requestId: awsContext.awsRequestId,
}
return createLogger(context)
}
I'm then passing down the logger instance to all methods. That said, (... , logger) is in almost every method signature which is not too nice. Moreover, I need to provide a logger in my tests.
How do you do it? Is there a better way?
you should implement some sort of Dependency Injection and include your logger there.
if your using microservices and maybe write lambdas in a functional approach, you can handle it by separating the initialization responsibility in a fashion like this:
import { SomeAwsEvent } from 'aws-lambda';
import pino from 'pino';
const createLogger = (event: SomeAwsEvent) => {
return pino().child({
requestId: event.requestContext.requestId
})
}
const SomeUtil = (logger: pinno.Logger) => () => {
logger.info('SomeUtil: said "hi"');
}
const init(event: SomeAwsEvent) => {
const logger = createLogger(event);
someUtil = SomeUtil(logger);
return {
logger,
someUtil
}
}
export const handler = (event: SomeAwsEvent) => {
const { someUtil } = init(event);
someUtil();
...
}
The simplest way is to use some DI library helper to tackle this
import { createContainer } from "iti"
interface Logger {
info: (msg: string) => void
}
class ConsoleLogger implements Logger {
info(msg: string): void {
console.log("[Console]:", msg)
}
}
class PinoLogger implements Logger {
info(msg: string): void {
console.log("[Pino]:", msg)
}
}
interface UserData {
name: string
}
class AuthService {
async getUserData(): Promise<UserData> {
return { name: "Big Lebowski" }
}
}
class User {
constructor(private data: UserData) {}
name = () => this.data.name
}
class PaymentService {
constructor(private readonly logger: Logger, private readonly user: User) {}
sendMoney() {
this.logger.info(`Sending monery to the: ${this.user.name()} `)
return true
}
}
export async function runMyApp() {
const root = createContainer()
.add({
logger: () =>
process.env.NODE_ENV === "production"
? new PinoLogger()
: new ConsoleLogger(),
})
.add({ auth: new AuthService() })
.add((ctx) => ({
user: async () => new User(await ctx.auth.getUserData()),
}))
.add((ctx) => ({
paymentService: async () =>
new PaymentService(ctx.logger, await ctx.user),
}))
const ps = await root.items.paymentService
ps.sendMoney()
}
console.log(" ---- My App START \n\n")
runMyApp().then(() => {
console.log("\n\n ---- My App END")
})
it is easy to write tests too:
import { instance, mock, reset, resetCalls, verify, when } from "ts-mockito"
import { PaymentService } from "./payment-service"
import type { Logger } from "./logger"
const mockedLogger = mock<Logger>()
when(mockedLogger.info).thenReturn(() => null)
describe("Payment service: ", () => {
beforeEach(() => {
resetCalls(mockedLogger)
// reset(mockedLogger)
})
it("should call logger info when sending money", () => {
const paymentService = new PaymentService(instance(mockedLogger))
expect(paymentService.sendMoney()).toBe(true)
})
})
I would not use the requestId as part of the context of the logger, but use it as the payload of the logger, like logger.info({ requestId }, myLogMessage). This was you can have a simple function create a child logger that you can use for the entire module.
So I tried sending an object through my websocket by translating it to json and then back when it returns. Unfortunately it gives me the below error. The console.log shows me that it is valid JSON, but somehow it gives me an error at JSON.parse in the service document. Can anyone see what I did wrong?
The error
core.js?223c:1440 ERROR SyntaxError: Unexpected token c in JSON at position 0
at JSON.parse (<anonymous>)
at WebSocket._this.ws.onmessage [as __zone_symbol__ON_PROPERTYmessage] (movie-chat.service.ts?6086:22)
at WebSocket.wrapFn (zone.js?fad3:1166)
console.log result of event.data (valid json)
{"message":"good boy","extra":"extra"}
movie-chat.service.ts
import {Injectable} from "#angular/core";
import 'rxjs/rx';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs/Observable";
// We need #injectable if we want to use http
#Injectable()
export class MovieChatService {
ws;
constructor(private http: HttpClient) {
}
// receive events
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
console.log(e.data);
var object = JSON.parse(e.data);
observer.next(object);
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}
// send events
sendMessage(message) {
message = JSON.stringify(message);
console.log(message);
this.ws.send(message);
}
}
Back-end handling of messages
var wss = new Websocket.Server({port:3185});
var CLIENTS = [];
wss.on('connection',
function(websocket) {
CLIENTS.push(websocket);
websocket.send('connected to socket');
websocket.on('message', function (message) {
console.log('Server received:', message);
sendAll(message)
});
websocket.on('close', function(client) {
CLIENTS.splice(CLIENTS.indexOf(client), 1);
});
websocket.on('error', function(client) {
CLIENTS.splice(CLIENTS.indexOf(client), 1);
});
});
movie-chat.component.ts
import {Component, OnInit} from "#angular/core";
import { MovieChatService} from "./movie-chat.service";
#Component({
selector: 'app-movie-chat',
templateUrl: './movie-chat.component.html',
styleUrls: ['./movie-chat.component.css']
})
export class MovieChatComponent implements OnInit{
fullName;
messageFromServer;
title = 'Websocket Demo';
url;
ws;
messages = [];
constructor(private movieChatService: MovieChatService){
}
ngOnInit(){
this.fullName = localStorage.getItem('fullName');
this.url = 'ws://localhost:3185';
this.movieChatService.createObservableSocket(this.url)
.subscribe(data => {
this.messageFromServer = data;
},
err => console.log(err),
() => console.log('The observable stream, is complete'));
}
sendMessageToServer(){
console.log('Client sending message to websocket server');
this.movieChatService.sendMessage({
message: 'good boy',
extra: 'extra'
});
}
}
It seems that you are trying to parse a Json Object,
{"message":"good boy","extra":"extra"}
JSON.parse expect string parameter and you are passing an Json Object for that the exception is rised.
We try to surround the Parse with try and catch
import {Injectable} from "#angular/core";
import 'rxjs/rx';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs/Observable";
// We need #injectable if we want to use http
#Injectable()
export class MovieChatService {
ws;
constructor(private http: HttpClient) {
}
// receive events
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
console.log(e.data);
try {
var object = JSON.parse(e.data);
observer.next(object);
} catch (e) {
console.log("Cannot parse data : " + e);
}
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}
// send events
sendMessage(message) {
message = JSON.stringify(message);
console.log(message);
this.ws.send(message);
}
}
So, I didn't actually solve it the way I wanted it, but I found out that it is possible to send arrays through a websocket. I did this and on the receiving end I transferred it into an object in the service file. It does the job for now. If anyone knows a better solution, let me know :)
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
var obj = e.data.split(',');
console.log(obj);
obj = {
name: obj[0],
msg: obj[1]
};
console.log(obj);
observer.next(obj);
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}