Confused over how i should use an observable - javascript

i am trying to make a chat an angular node.js real-time chat application
i am using socket.io with angular and a nodejs backend
Server Side
io.on('connection', (socket) => {
socket.on('new-message', (message) => {
io.emit(message);
});
});
Client Side
service
private url = 'http://localhost:3000';
private socket;
constructor( private httpClient: HttpClient ) {
this.socket = io(this.url);
}
public sendMessage(message:Message){
this.socket.emit('new-message',message);
}
public getMessages = () => {
return new Observable(observer => {
this.socket.on('new-message', (message) => {
observer.next(message);
});
});
}
component
messages:any[]=[];
constructor(private chatService:ChatService){}
ngOnInit(): void {
this.chatService.getMessages()
.subscribe((message:any)=>{
this.messages.push(message);
})
onSubmit(){
//extracting message from the input form (not relevant i think i can add it if suggested)
this.chatService.sendMessage(tosend);
}
component.html
<div *ngFor="let m of messages">
{{m.text}}
</div>
<! -- a form to send messages -->

turns out issue was server side had to change
io.emit(message);
to io.emit('new-message',message)

Related

Broadcasting to everyone except sender socket.io

I am building a chat in my app. I am using socket.io for this. When a user sends a message I send an api request to my server. The api stores the message in the database and only then emits, with a socket service to everyone in the room that there is a new message. I have a SocketService class with this method:
private async broadcast({ type, data, chatId, senderId }: { type: string; data: any; chatId: string; senderId: string }) {
const excludedSocket = await this.getUserSocket(senderId);
if (chatId && excludedSocket) {
excludedSocket.emit;
} else if (excludedSocket) {
excludedSocket.emit(type, data);
} else if (room) {
gIo.to(room).emit(type, data);
} else {
gIo.emit(type, data);
}
}
The problem I have is that getUserSocket returns a RemoteSocket object that doesn't have the broadcast or methods on it. So how can I achieve this?
private async getUserSocket(userId: string) {
const sockets = await this.getAllSockets();
const socket = sockets.find((s) => s.data.uid === userId);
return socket;
}
private async getAllSockets() {
const sockets = await this.io.fetchSockets();
return sockets
}
socket.broadcast.emit equates to a BroadcastOperator that sends to an empty Set of rooms (meaning "all rooms"), excluding the socket id "room" of the sender Socket.
The same BroadcastOperator can be created from the server instance with server.except()
const sockets = await this.io.except(socketIdString).emit('blah', x)

Spring boot: SSE Server Sent Events not working

In Spring Boot, when we try to send a Server Sent Event, it only sends an error event containing data: {"timeout":-1} when we try to connect, and the connection closes. The Spring Boot class is as follows
#RestController
#CrossOrigin(origins = "*")
public class SsePushNotificationRestController {
private static final Logger log = LoggerFactory.getLogger(SsePushNotificationRestController.class);
private SseEmitter emitter;
#GetMapping("/test")
public String getString(){
try {
emitter.send("User connected");
log.info("User connected");
emitter.complete();
} catch (Exception e) {
log.info("Error while sending message to client: " + e.getMessage());
}
return "placeholder";
}
#GetMapping("/emitter")
public SseEmitter eventEmitter(#RequestParam String userId) {
emitter = new SseEmitter(-1L);
return emitter;
}
}
And our client code is as follows:
const eventSource = new EventSource('http://localhost:8080/emitter?userId=testUser');
eventSource.addEventListener("message", (event) => {
console.log(event);
});
eventSource.addEventListener("open", (event) => {
console.log("connection opened");
});
eventSource.addEventListener("error", (e) => {
if (e.readyState === EventSource.CLOSED) {
console.log('closed');
}
else {
console.log(e);
}
e.target.close();
});
document.getElementById("btn").onclick = e => {
fetch('http://localhost:8080/test').then( data => console.log(data)).catch(data => console.log(data));
};
Immediately, an error is created before we can click the button to generate an event.
What could be wrong?
What does your Spring boot terminal say? I think I need that information to address your program's error. By the way allowing cross origin resources sharing for requests from any sources (using wildcard) is a very very bad practice.
One possible reason of error is something's wrong when you create an instance of SSEemitter. (new SSeEmitter(-1L))
SSeEmitter(Long timeout) is creating server side event with set timeout it says. So if timeout is -1, I guess it would immediately be timed out and return timeout response. So it wouldn't be error, just working as written

How to get and send messages asynchronously from one websocket client to antoher

I need to establish connection between client websocket threw my backend on spring java to another websocket where my backend is a client, I established connection as client but can't figure out how to send it back as soon as my client send me message,
My Client Endpoint works as I need
#Service
#ClientEndpoint
public class ClientEndpoint {
Session userSession = null;
private MessageHandler messageHandler;
public WbsClientEndpoint(#Value("${url}") String url) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI(url));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#OnOpen
public void onOpen(Session userSession) {
System.out.println("opening web socket");
this.userSession = userSession;
}
#OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing web socket");
this.userSession = null;
}
#OnMessage
public void onMessage(String message) {
if (this.messageHandler != null) {
this.messageHandler.handleMessage(message);
}
}
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
public void sendMessage(String message) {
this.userSession.getAsyncRemote().sendText(message);
}
public interface MessageHandler {
void handleMessage(String message);
}
}
and example of method when I send my message as client, it does what I need but now I only printing the message cause cannot connect it to my message handler:
#Override
public void addDevice(DeviceTransfer deviceTransfer) {
clientEndPoint.addMessageHandler(message -> {
System.out.println("Response: " + message);
});
clientEndPoint.sendMessage(JSON.toJSONString(deviceTransfer));
}
Also I wrote a websockethandler for messages that comes to my backend:
#Component
public class WebSocketHandler extends AbstractWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
System.out.println("New Text Message Received" + message + " ___ " + session);
String clientMessage = message.getPayload();
if (clientMessage.startsWith("/addDevice")) {
//Here I don't know how to send this clientMessage and wait for result from my addDevice method to return it back
}
}
}
And I need to connect both of the realizations to establish connection in real time.
When client sends message to me I must send this message to another server as client.
My client code on JavaScript as example, when I press button it establish connection to my web socket and send my message:
const connectBtn = document.getElementById('connect');
if (connectBtn) {
connectBtn.addEventListener('click', function () {
window.socket1 = new WebSocket("ws://localhost:8081/ws");
socket1.onopen = function(e) {
console.log("[open]");
socket1.send(JSON.stringify({"command": "subscribe","identifier":"{\"channel\":\"/topic/addDevice\"}"}))
};
socket1.onmessage = function(event) {
console.log(`[message]: ${event.data}`);
};
socket1.onclose = function(event) {
if (event.wasClean) {
console.log(`[close],code=${event.code}reason=${event.reason}`);
} else {
console.log('[close]');
}
};
socket1.onerror = function(error) {
console.log(`[error] ${error.message}`);
};
});
}
It seems to me like you need to keep track of your ClientEndpoint instances:
public class ClientEndpoint {
private HashSet<ClientEndpoint> endpoints = new HashSet<>();
// or perhaps a HashMap using `userSession.id` or similar
private HashMap<string, ClientEndpoint> userToEndpoint = new HahsMap<>();
#OnOpen
public void onOpen(Session userSession) {
System.out.println("opening web socket");
this.userSession = userSession;
this.endpoints.add(this);
this.userToEndpoint.put(userSession.id, this);
}
#OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing web socket");
this.endpoints.remove(this);
this.userToEndpoint.delete(userSession.id);
this.userSession = null;
}
}
You can use endpoints/userToEndpoint to find all connected clients, perhaps filter by which rooms they're in (I assume that's what you're trying to accomplish with that subscribe command), and do whatever you want with that information. E.g. a broadcast to everyone except the sender:
for (ClientEnpoint endpoint : this.endpoints) {
if (endpoint == sender) continue; // Don't send to sender
endpoint.sendMessage("message");
}

Spring Boot + Angular : Link has been blocked by CORS

I have a problem that I don't seem to figure out. I want to send a http request from my
Angular client
export class UsersService {
private baseUrl = 'http://localhost:8095/rest/users';
createUser(user: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}` , user);
}
getUsers(): Observable<any> {
return this.http.get(`${this.baseUrl}/all`);
}
}
create user component.ts :
save() {
this.userService.createUser(this.user)
.subscribe(data => console.log(data), error => console.log(error));
this.user = new User();}
this is the SpringBoot backend
#RestController
#RequestMapping("/rest/users")
public class UsersResource {
private UserRepository userRepository;
public UsersResource(UserRepository userRepository) {
this.userRepository = userRepository;
}
#GetMapping("/all")
public List<Users> getAll() {
return userRepository.findAll();
}
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void create(#RequestBody Users users) {
userRepository.save(users);
}
}
when i call http://localhost:4200/users i got a clean result with all users without any problem
but when i want to add a user from here http://localhost:4200/adduser it show for me some issue
by the way iam using CORS chrome extension
i hope that someone help me for this isse.
thanks

Returning API response rather than data

I have been staring at this for a while, and finally realized that my code is returning the "200 ok." response instead of the actual data itself, and that is why it won't populate my ionic buttons. When I am calling the API via postman, and printing it to the console it is showing the data that I need, so I am assuming the issue is somewhere in the .ts files.
Below is my API code:
app.get('/getAllProvs', function (req,res) {
//var id = req.params.id;
connection.query('SELECT * from Patient', function(err, rows, fields) {
if (!err){
var response = [];
if (rows.length != 0) {
response.push({'result' : 'success', 'data' : rows});
} else {
response.push({'result' : 'error', 'msg' : 'No Results Found'});
}
res.setHeader('Content-Type', 'application/json');
//res.status(200).send(JSON.stringify(rows));
res.send(rows);
console.log(rows);
} else {
res.status(400).send(err);
}
});
});
below is my provider .ts code:
export class RestService {
data1: any;
constructor(public http: Http) {
console.log('Hello RestServiceProvider Provider');
}
getAllProvs(){
if(this.data1){
return Promise.resolve(this.data1);
}
return new Promise(resolve => {
this.http.get('http://lndapp.wpi.edu:5000/getAllProvs')
.map(res => res.json())
.subscribe(data => {
console.log("rest-services.ts subscribe");
this.data1 = data;
console.log(data);
resolve(this.data1);
});
});
}
}
below is my page .ts file:
export class AllPatientsPage {
data1: any;
constructor(public app: App, public loadingCtrl: LoadingController, private toastCtrl: ToastController, public navCtrl: NavController, public restService: RestService){
this.getAllProvs();
}
}
getAllProvs(){
this.restService.getAllProvs()
.then(data => {
console.log("all-patients.ts data");
console.log(data);
this.data1 = data;
}).catch(e => {
console.log(e);
});
}
}
Send a json response from your service:
res.json(rows);
If this is happening during development, it could be because you're using the same url on your front end react website as your server.js site.
Example:
front end running on : localhost:3000
backend listening on : localhost:3000/api
Fix: Change the port numbers.
front end running on : localhost:3000
backend listening on : localhost:5000/api
Additional help:
package.json: add proxy that points to your server: localhost:5000
browser: clear your cookies.
Hope this helps.

Categories