Nest js read events from rabbitmq in websockets microservice - websocket

I have created a microservice(notify-microservice) with rabbitmq connection. I have a product microservice from there i will be pushing an event to rabbitmq to notify the client about new product. In notify-microservice micro service i have a websocket in it. How can i read the events from rabbitmq to notify the clients.
Right now i am only able to read the events from rabbitmq from controller of microservice. How can i read from websocket file.
microservice main.ts file:
import { Logger } from '#nestjs/common';
import { NestFactory } from '#nestjs/core';
import { Transport } from '#nestjs/microservices';
import { AppModule } from './app.module';
const logger = new Logger('Main');
const microserviceOptions = {
transport: Transport.RMQ,
options: {
urls: ['amqp://<user_name>:<password>#<host>:<port>/<vhost>'],
queue: '<queue_name>',
queueOptions: {
durable: false
},
}
}
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, microserviceOptions);
app.listen(() => {
logger.log("Socket micro service is listening...");
})
}
bootstrap();
app.gateway.ts file
import { Logger } from '#nestjs/common';
import { OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WsResponse } from '#nestjs/websockets';
import { Socket,Server } from 'socket.io';
import { EventPattern } from '#nestjs/microservices';
#WebSocketGateway(3001)
export class AppGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect{
private Logger = new Logger('AppGateway');
afterInit(server: Server) {
this.Logger.log("App Gateway Initialized");
}
handleConnection(client: Socket, ...args: any[]){
this.Logger.log(`New client connected...: ${client.id}`);
client.emit('connected', 'Successfully connected to the server.');
}
handleDisconnect(client: Socket) {
this.Logger.log(`Client disconnected: ${client.id}`);
}
#EventPattern('notify_new_product')
notifyNewProduct(client:Socket, text:string):WsResponse<string> {
this.Logger.log(`got new event`);
return {event: 'notify_new_product', 'data': text};
}
}
app.module.ts:
import { Module } from '#nestjs/common';
import { AppGateway } from './app.gateway';
import { NotifyController } from './notify.controller';
import { ClientsModule, Transport } from '#nestjs/microservices';
#Module({
imports: [],
controllers: [NotifyController],
providers: [AppGateway],
})
export class AppModule {}
notify.controller.ts
import { Body, Controller, Get, Logger, Post } from '#nestjs/common';
import { EventPattern } from '#nestjs/microservices';
#Controller()
export class NotifyController {
private logger = new Logger('NotifyController');
#EventPattern('notify_new_product')
async notify_new_product(product_id: string) {
this.logger.log('Received new event to notify');
}
}

The message Pattern Decorator should be used only within the controller classes since they are the entry points for your application.Using them inside providers won't have any effect as they are simply ignored by Nest.

Related

CORS policy issue while hitting the rest service(Spring Boot) from Angular5 Application

I am getting below error when I try to hit post service from Angular 5
Failed to load resource: the server responded with a status of 403
(Forbidden) Access to XMLHttpRequest at
'https://xxxx/xxxx/services/exportVarianceHome' from origin
'http://localhost:4200' 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.
Below is my interceptor configuration in Angular 5
import { Injectable, NgModule} from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse} from '#angular/common/http';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import 'rxjs/add/operator/do';
#Injectable()
export class HttpsRequestInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const dupReq = req.clone({ headers: req.headers
.set('Access-Control-Allow-Origin','*')
.set('Content-Type', 'application/json')
.set('Authorization', 'Basic XXXXXXXXXXXXXXXXXXXXXX')
.set('Access-Control-Allow-Credentials','true') });
return next.handle(dupReq);
}
};
#NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpsRequestInterceptor, multi: true }
]
})
And Angular Post Call
import { HttpClient} from '#angular/common/http';
constructor(private httpClient:HttpClient){
getLookupDetails();
}
getLookupDetails(){
this.httpClient.get(this.servicsUrl).subscribe(
(data:any) => {
console.log("JSON Response " + JSON.stringify(data));
}
)
}
And the Cross origin Setup at server side (SpringBoot)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication(scanBasePackages = {"com.controller.gtm"})
public class BrokerValidationApplication extends SpringBootServletInitializer implements WebMvcConfigurer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BrokerValidationApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(BrokerValidationApplication.class, args);
}
#Override
public void addCorsMappings(CorsRegistry registry) {
System.out.println(">=== Inside Cors Orgin Mapping addCorsMappings ===>");
registry.addMapping("/services/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(4800);
}
}

Spring Get Mapping isn't working

I'm using Angular 4 with Spring backend. And.... My post request working fine. But the get request don't work. Look the code:
Frontend Service:
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/toPromise';
import { Aluno } from '../models/aluno.model';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs/Observable";
#Injectable()
export class UserDataService {
private url = 'http://localhost:8080/getAluno';
constructor(private http: HttpClient){}
getAluno():Observable<Aluno>{
return this.http.get<Aluno>(this.url);
}
}
My spring backend provider:
import com.google.gson.Gson;
import com.ifmg.tcc.TADs.Aluno;
import com.ifmg.tcc.TADs.LoginObject;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
#CrossOrigin(origins = "http://localhost:4200")
#RestController
public class LoginProvider {
Gson gson = new Gson();
private boolean verify(LoginObject lo){
if(lo.getCode().equals("lucas") && lo.getPassword().equals("12345")){
return true;
}else{
return false;
}
}
#PostMapping(value = "login")
public ResponseEntity<?> verifyLogin(#RequestBody String login){
System.out.println("LOGIN REALIZADO");
LoginObject loginO = gson.fromJson(login, LoginObject.class);
if(verify(loginO)){
return new ResponseEntity<Boolean>(true,HttpStatus.OK);
}else{
return new ResponseEntity<Boolean>(false,HttpStatus.OK);
}
}
#GetMapping(value = "/getAluno")
public #ResponseBody ResponseEntity<?> getAluno() {
System.out.println("PEGANDO ALUNO");
Aluno aluno = new Aluno("0001","Lucas Alves de Faria","2018/1","10/12/1995",true,"luke#email.com","(37) 999597899","127.831.956-58","MG-19.319.265");
return new ResponseEntity<Aluno>(aluno, HttpStatus.OK);
}
}
The function verify login in my backend working fine(I'm call this function in other service). But the fontend don't call the other function (getAluno)
I'm not sure about what you mean when you say "But the fontend don't call the other function (getAluno)". You may look at your network calls and verify the calls.
In your front component which calls your service, you need to have a subscribe, something like this :
this.yourService.getAluno().subscribe(
(res: ResponseEntity) => {
this.aluno = res.json;
},
(res: ResponseEntity) => this.onError(res.json)
);
Hope it helps you.

How to Configure a valid #ResponseBody for a POST with spring and angular 4

I'm getting this error:
core.js:1448 ERROR SyntaxError: Unexpected end of JSON input
I think the error is in the response , how i can correct create a #responseBody
for the front to understand that the object in the back end is created properly
the input is (license:"gd345") without the parentheses
Component file:
import { Component, OnInit } from '#angular/core';
import { Http, Headers } from '#angular/http';
#Component({
selector: 'posts',
templateUrl: './posts.component.html',
styleUrls: ['./posts.component.css']
})
export class PostsComponent {
posts: any[];
letter: any;
constructor(private http: Http) {
http.get('http://localhost:8183/api/vehiculespark')
.subscribe(response => {
this.posts = response.json();
});
}
createPost(input: HTMLInputElement) {
debugger;
let post = { license: input.value };
this.letter = post;
const headers = new Headers({ 'Content-Type': 'application/json' });
this.http.post('http://localhost:8183/api/vehicules', this.letter, { headers: headers })
.subscribe(response => {
console.log(response.json())
});
debugger;
}
}
spring Controller:
package com.api.kingspark.parking.controller;
import com.api.kingspark.parking.domain.Tickect;
import com.api.kingspark.parking.domain.Vehicule;
import com.api.kingspark.parking.repositories.TicketRepository;
import com.api.kingspark.parking.repositories.VehiculoRepository;
import com.api.kingspark.parking.services.PorteroServices;
import org.reactivestreams.Publisher;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.APPLICATION_XML_VALUE;
#RestController
public class ParkingController {
private PorteroServices porteroServices;
private final VehiculoRepository vehiculoRepository;
private final TicketRepository ticketRepository;
public ParkingController(PorteroServices porteroServices, VehiculoRepository vehiculoRepository, TicketRepository ticketRepository) {
this.porteroServices = porteroServices;
this.vehiculoRepository = vehiculoRepository;
this.ticketRepository = ticketRepository;
}
#CrossOrigin
#GetMapping("/api/vehiculespark")
List<Tickect> listIn(){
return porteroServices.getInVehicules();
}
#GetMapping("/api/vehicules")
Flux<Vehicule> listAll(){
return vehiculoRepository.findAll();
}
#GetMapping("/api/vehicules/{id}")
Mono<Vehicule> getById(#PathVariable String id){
return vehiculoRepository.findById(id);
}
#CrossOrigin
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/api/vehicules",consumes="application/json",produces={"application/json","application/xml"})
#ResponseBody
Mono<Void> createVehicule(#RequestBody Publisher<Vehicule> carstream){
Vehicule ca= Mono.fromDirect(carstream).cache().block();
return porteroServices.saveVehicule(ca);
//return ResponseEntity.created(location).build();
}
#PutMapping("/api/vehicules/{id}")
Mono<Tickect> update(#PathVariable String id, #RequestBody Vehicule vehicule){
return ticketRepository.save(porteroServices.drawVehicule(id));
}
}

How can I register JSR-356 Websocket in PAX-Web? (In bundle, not WAR)

I have a problem with the PAX-Web. I've tried to register a Websocket service as declrarative, but it is unaccessible from web. I've tried the given websocket-jsr356-6.0.3.war and it works fine. As I see the WAR file handles differently the org.osgi.service.http.HttpContext. I've tried the following scenarios:
Scenario 1 - OSGi R6 Whiteboard HTTP method
Creating a ServletContextHelper:
package hu.blackbelt.judo.common.rest.regular;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.http.context.ServletContextHelper;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
#Component(immediate = true)
#Service(ServletContextHelper.class)
#Properties(value = {
#Property(name = HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, value = "chat"),
#Property(name = HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, value = "/test")
})
public class ChatServletContext extends ServletContextHelper {
}
And adding the Websocket Endpoint:
package hu.blackbelt.judo.common.rest.regular;
import lombok.extern.slf4j.Slf4j;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import javax.websocket.EncodeException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#Component(immediate = true)
#Service(Object.class)
#Properties(value = {
#Property(name = HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
value = "=(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=chat)")
})
#Slf4j
public class ChatEndpoint {
public static final String ROOM = "room";
#OnOpen
public void onOpen(final Session session, #PathParam(ROOM) final String room) {
LOGGER.info("session openend and bound to room: " + room);
session.getUserProperties().put(ROOM, room);
}
#OnMessage
public void onMessage(final Session session, final ChatMessage chatMessage) {
String room = (String) session.getUserProperties().get(ROOM);
try {
for (Session s : session.getOpenSessions()) {
if (s.isOpen()
&& room.equals(s.getUserProperties().get(ROOM))) {
s.getBasicRemote().sendObject(chatMessage);
}
}
} catch (IOException | EncodeException e) {
LOGGER.warn("onMessage failed", e);
}
}
}
The logs show me that the Endpoint is catched. I've debugged and Pax-Web is registering it.
The log shows the following line:
2017-05-04 02:36:02,698 | INFO | Thread-70 | WebSocketTracker | 330 - org.ops4j.pax.web.pax-web-extender-whiteboard - 6.0.3 | found websocket endpoint!!
But the websocket is unaccessible with the following URL: ws://localost:8181/test/chat/testroom
Scenario 2 - Pax-Web properties on registered HttpContext (with JAX-RS it works)
Creating HttpContext instance: (Utilizing the OSGi given Helper abstract class):
package hu.blackbelt.judo.common.rest.regular;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.context.ServletContextHelper;
#Component(immediate = true)
#Service(HttpContext.class)
#Properties(value = {
#Property(name = "httpContext.id", value = "chat"),
#Property(name = "httpContext.path", value = "test")
})
public class ChatHttpContext extends ServletContextHelper implements HttpContext {
}
And the Websocket Endpoint:
package hu.blackbelt.judo.common.rest.regular;
import lombok.extern.slf4j.Slf4j;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
import javax.websocket.EncodeException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#SuppressWarnings({"checkstyle:missingctor", "checkstyle:illegaltoken"})
#Component(immediate = true)
#Service(Object.class)
#Properties(value = {
#Property(name = "httpContext.id", value = "chat")
})
#ServerEndpoint(value = "/chat/{room}", encoders = ChatMessageEncoder.class, decoders = ChatMessageDecoder.class)
#Slf4j
public class ChatEndpoint {
public static final String ROOM = "room";
#OnOpen
public void onOpen(final Session session, #PathParam(ROOM) final String room) {
LOGGER.info("session openend and bound to room: " + room);
session.getUserProperties().put(ROOM, room);
}
#OnMessage
public void onMessage(final Session session, final ChatMessage chatMessage) {
String room = (String) session.getUserProperties().get(ROOM);
try {
for (Session s : session.getOpenSessions()) {
if (s.isOpen()
&& room.equals(s.getUserProperties().get(ROOM))) {
s.getBasicRemote().sendObject(chatMessage);
}
}
} catch (IOException | EncodeException e) {
LOGGER.warn("onMessage failed", e);
}
}
}
But the websocket is unaccessible with the following URL: ws://localost:8181/test/chat/testroom
How can I achive that webcsocket be available? I do not want to repackage my bundle as WAB. Is there any way?

Subsequent websocket session from Angular 2 to Spring backend stops previous websocket sessions from working

I am using Spring Boot v1.5.1 to implement a websocket backend and Angular 2 for the front end. Basically, the backend (Spring) polls a database for changes, if there are any, pushes the changes to the frontend (Angular).
If the first user, A, accesses the website, then she sees all the changes. As soon as another user, B, accesses the website, she can see the changes but user A no longer does. This behavior can be replicated on the same computer by opening up 2 browsers (one after the other; the latter will see the changes but the former will have stopped).
On the backend, my websocket handler looks like the following.
package demo.web;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
#Component
public class TimestampWsHandler extends TextWebSocketHandler {
private WebSocketSession session;
#Override
public void afterConnectionEstablished(WebSocketSession session) {
this.session = session;
start();
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
if ("CLOSE".equalsIgnoreCase(payload)) {
session.close();
return;
}
}
void push() {
if (null != session && session.isOpen()) {
String s = IntStream.range(0, 10)
.mapToObj(i -> System.currentTimeMillis())
.map(i -> i.toString())
.collect(Collectors.joining(","));
try {
session.sendMessage(new TextMessage(s));
} catch (Exception e) {
e.printStackTrace();
}
}
}
void start() {
new Thread(() -> {
while (true) {
push();
try {
Thread.sleep(500L);
} catch (Exception e) { }
}
}).start();
}
}
My Spring Boot application entry point looks like the following.
package demo;
import demo.web.TimestampWsHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
#SpringBootApplication
#EnableScheduling
#EnableWebSocket
#EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class})
public class App implements WebSocketConfigurer {
#Autowired
private TimestampWsHandler timestampWsHandler;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(timestampWsHandler, "/api/ws/topic/timestamp").setAllowedOrigins("*");
}
}
On the frontend (after using ng cli to create the project), my app.component.ts has been modified to look like the following.
import { Component, OnInit, OnDestroy, AfterViewInit } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { IntervalObservable } from 'rxjs/observable/IntervalObservable';
import { $WebSocket, WebSocketSendMode } from 'angular2-websocket/angular2-websocket';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
data: Array<String>;
ws: $WebSocket;
wsSubscription: Subscription;
ngOnInit() {
}
ngAfterViewInit() {
this.initWebSocket();
}
ngOnDestroy() {
this.destroyWebSocket();
}
private initWebSocket(): void {
this.ws = new $WebSocket('ws://localhost:8080/api/ws/topic/timestamp');
this.wsSubscription = this.ws.getDataStream().subscribe(
msgEvent => {
this.data = msgEvent.data.split(',');
},
err => {
console.error(err);
}
);
}
private destroyWebSocket(): void {
if (this.wsSubscription) {
try {
this.wsSubscription.unsubscribe();
this.wsSubscription = null;
} catch (err) { }
}
if (this.ws) {
try {
this.ws.close(true);
this.ws = null;
} catch (err) { }
}
}
}
And the corresponding HTML, app.component.html looks like the following.
<h1>
{{title}}
</h1>
<table *ngIf="data">
<thead>
<tr>
<th>timestamp</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let t of data">
<td>{{t}}</td>
</tr>
</tbody>
</table>
Note, that after I created the Angular application, I had to install angular2-websocket: npm install angular2-websocket --save
Any ideas on what I'm doing wrong?
I do see the following IllegalStateException being repeated over and over. I'm not sure if these problems are related.
java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1224)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textPartialStart(WsRemoteEndpointImplBase.java:1182)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:222)
at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:49)
at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendTextMessage(StandardWebSocketSession.java:203)
at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:101)
at demo.web.TimestampWsHandler.push(TimestampWsHandler.java:37)
at demo.web.TimestampWsHandler.lambda$start$2(TimestampWsHandler.java:47)
at java.lang.Thread.run(Thread.java:745)
Any help is appreciated.

Resources