import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { Post } from 'src/app/sign-in/post';
#Injectable({
providedIn: 'root'
})
export class NewUserService {
constructor(private router:Router,private http: HttpClient) { }
async signIn(email:any,password:any){
const userDate = {
email:email,
password:password
}
console.log("Before request");
await this.http.post<{
Status: string;
StatusDetails: string;
token: string;
}>("http://localhost:5000/user/signin", userDate)
.subscribe(respond => {
console.log("respond");
if (respond.token) {
//sign susess
}else {
//fail
}
});
console.log("After request")
}
}
This is my servise.ts class. In hear I try to make a post request. Before completing the request other code execute. I expect output like
Before request
respond
After request
But the actual output is
Before request
After request
respond
How can I execute code like
Before request
respond
After request
Related
I've tried to implement an oauth method using GraphQL with Google auth and for some reason I'm getting the following error
"res.setHeader is not a function" from within the authenticate method in Google Strategy
I've used passport-google-oauth20 strategy
this is my google-auth.guard.ts
import { ExecutionContext, Injectable } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
const { token } = ctx.getArgs();
gqlReq.body = { token };
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
this is my google.strategy.ts
import { PassportStrategy } from '#nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { Profile } from 'passport';
#Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: process.env.GOOGLE_REDIRECT_URL,
prompt: 'consent',
scope: ['email', 'profile'],
})
}
async validate(
accessToken: string,
refreshToken: string,
profile: Profile,
done: VerifyCallback,
): Promise<any> {
if (!profile) {
return done(new UnauthorizedException(), false);
}
return done(null, profile);
}
}
it's important to point out that since my app is a react SPA, the callbackURL value is the main page of the client and not another path in the server.
and the resolver which I intend to use to generate a jwt token and a refresh token, but the code never gets to this part due to the error in the strategy
#UseGuards(GoogleAuthGuard)
#Query(() => LoginResponseApi)
async googleLogin(
#Args({ name: 'token', type: () => String }) token: string,
#Req() req,
#Context() context
): Promise<LoginResponseApi> {
const res: Response = context.req.res;
const loginResponse: any = await this.authService.googleLogin(req)
const jwtToken = this.authService.createRefreshToken(loginResponse.user)
if (loginResponse.accessToken)
this.authService.sendRefreshToken(res, jwtToken)
return loginResponse;
}
I have a hard time passing the right angular request to the header. This is my service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpHeaders }
from '#angular/common/http';
import { Utente } from '../model/Utente ';
import { Prodotto } from '../model/Prodotto ';
import { OktaAuthService } from '#okta/okta-angular';
import { Observable, from } from 'rxjs';
import { Carrello } from '../model/Carrello ';
import { userInfo } from 'node:os';
import { getLocaleCurrencyCode } from '#angular/common';
const headers = new HttpHeaders().set('Accept', 'application/json');
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
constructor(
private httpClient:HttpClient, private oktaAuth:OktaAuthService ) {}
getCarr(){
return this.httpClient.get<Carrello[]>('http://localhost:8080/prodotti/utente/vedicarrelloo', {headers} );
}
}
This is my spring method:
#Transactional(readOnly = true)
public List<Carrello> getCarrello(#AuthenticationPrincipal OidcUser utente){
Utente u= utenteRepository.findByEmail(utente.getEmail());
return carrelloRepository.findByUtente(u);
}
In console I get this error (error 500):
https://i.stack.imgur.com/BiONS.png
this error corresponds in my console to "java.lang.NullPointerException: null.
But if I access localhost: 8080, I can see the answer correctly, so I assume there is a problem in passing the request header in angular, can anyone tell me where am I wrong, please? I specify that I get this error only in the methods where the OidcUser is present, the rest works perfectly. Thank you!
You need to send an access token with your request. Like this:
import { Component, OnInit } from '#angular/core';
import { OktaAuthService } from '#okta/okta-angular';
import { HttpClient } from '#angular/common/http';
import sampleConfig from '../app.config';
interface Message {
date: string;
text: string;
}
#Component({
selector: 'app-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
failed: Boolean;
messages: Array<Message> [];
constructor(public oktaAuth: OktaAuthService, private http: HttpClient) {
this.messages = [];
}
async ngOnInit() {
const accessToken = await this.oktaAuth.getAccessToken();
this.http.get(sampleConfig.resourceServer.messagesUrl, {
headers: {
Authorization: 'Bearer ' + accessToken,
}
}).subscribe((data: any) => {
let index = 1;
const messages = data.messages.map((message) => {
const date = new Date(message.date);
const day = date.toLocaleDateString();
const time = date.toLocaleTimeString();
return {
date: `${day} ${time}`,
text: message.text,
index: index++
};
});
[].push.apply(this.messages, messages);
}, (err) => {
console.error(err);
this.failed = true;
});
}
}
On the Spring side, if you want it to accept a JWT, you'll need to change to use Jwt instead of OidcUser. Example here.
#GetMapping("/")
public String index(#AuthenticationPrincipal Jwt jwt) {
return String.format("Hello, %s!", jwt.getSubject());
}
How can NestJS be use as a websocket client? I want to connect to a remote websocket server as a client using NestJS, but I didn't find any information about this implementation in the framework.
As Nestjs is simply a framework for Nodejs, so you need to find an NPM package that supports Websocket. For example, I use ws with #types/ws type definition, and create a Websocket client as a Nestjs service class:
// socket-client.ts
import { Injectable } from "#nestjs/common";
import * as WebSocket from "ws";
#Injectable()
export class WSService {
// wss://echo.websocket.org is a test websocket server
private ws = new WebSocket("wss://echo.websocket.org");
constructor() {
this.ws.on("open", () => {
this.ws.send(Math.random())
});
this.ws.on("message", function(message) {
console.log(message);
});
}
send(data: any) {
this.ws.send(data);
}
onMessage(handler: Function) {
// ...
}
// ...
}
// app.module.ts
import { Module } from "#nestjs/common";
import { WSService } from "./socket-client";
#Module({
providers: [WSService]
})
export class AppModule {}
I try it by another way. I write an adapter with socket.io-client. Then use this adapter in boostrap by method useWebSocketAdapter. After that i can write handle websocket event in gateway like the way working with socket server (use decorator #SubscribeMessage)
My Adapter file
import { WebSocketAdapter, INestApplicationContext } from '#nestjs/common';
import { MessageMappingProperties } from '#nestjs/websockets'
import * as SocketIoClient from 'socket.io-client';
import { isFunction, isNil } from '#nestjs/common/utils/shared.utils';
import { fromEvent, Observable } from 'rxjs';
import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';
export class IoClientAdapter implements WebSocketAdapter {
private io;
constructor(private app: INestApplicationContext) {
}
create(port: number, options?: SocketIOClient.ConnectOpts) {
const client = SocketIoClient("http://localhost:3000" , options || {})
this.io = client;
return client;
}
bindClientConnect(server: SocketIOClient.Socket, callback: Function) {
this.io.on('connect', callback);
}
bindClientDisconnect(client: SocketIOClient.Socket, callback: Function) {
console.log("it disconnect")
//client.on('disconnect', callback);
}
public bindMessageHandlers(
client: any,
handlers: MessageMappingProperties[],
transform: (data: any) => Observable<any>,
) {
const disconnect$ = fromEvent(this.io, 'disconnect').pipe(
share(),
first(),
);
handlers.forEach(({ message, callback }) => {
const source$ = fromEvent(this.io, message).pipe(
mergeMap((payload: any) => {
const { data, ack } = this.mapPayload(payload);
return transform(callback(data, ack)).pipe(
filter((response: any) => !isNil(response)),
map((response: any) => [response, ack]),
);
}),
takeUntil(disconnect$),
);
source$.subscribe(([response, ack]) => {
if (response.event) {
return client.emit(response.event, response.data);
}
isFunction(ack) && ack(response);
});
});
}
public mapPayload(payload: any): { data: any; ack?: Function } {
if (!Array.isArray(payload)) {
return { data: payload };
}
const lastElement = payload[payload.length - 1];
const isAck = isFunction(lastElement);
if (isAck) {
const size = payload.length - 1;
return {
data: size === 1 ? payload[0] : payload.slice(0, size),
ack: lastElement,
};
}
return { data: payload };
}
close(server: SocketIOClient.Socket) {
this.io.close()
}
}
main.js
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import {IoClientAdapter} from './adapters/ioclient.adapter'
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new IoClientAdapter(app))
await app.listen(3006);
console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
then Gateway
import {
MessageBody,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
} from '#nestjs/websockets';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Server } from 'socket.io';
#WebSocketGateway()
export class EventsGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('hello')
async identity(#MessageBody() data: number): Promise<number> {
console.log(data)
return data;
}
}
It a trick, but look so cool. Message handler can write more like nestjs style.
I'm using angular6.
When getHello gets called it returns a 404 error.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({ providedIn: 'root' })
export class RequisicaoTransporteService {
constructor(private http: HttpClient) { }
getHello() : Observable<any> {
return this.http.post('http://localhost:17844/api/requisicaotransporte/getHello',{},httpOptions)
};
}
This is how i call it:
this.requisicaoTransporteService.getHello()
.subscribe(x => this.hello = x);
When i do the following, it works fine:
$.ajax({
type: "POST",
url: "http://localhost:17844/api/requisicaotransporte/gethello",
success:function(o){console.log(o);}
});
The server allows CORS.
I'm following the step by step tutorial of NativeScript but I'm facing the following issue:
When I try to make a post request through my Angular service I get the following error:
JS: {"message":"The specified content type was not found.","errorCode":611}
My code looks pretty much the same as the one in the official docs from NativeScript:
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { User } from './user';
import { Config } from '../config';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
constructor(private _http: Http) { }
register(user: User): Observable<Response> {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const url = `${Config.apiUrl}/Users`;
const body = JSON.stringify({
Username: user.email,
Email: user.email,
Password: user.password
});
return this._http.post(url, body, { headers })
.catch(this.handleErrors);
}
handleErrors(error: Response) {
console.error(JSON.stringify(error.json()));
return Observable.throw(error);
}
}
Component class:
signUp() {
this._user.register(this.user).subscribe(
() => {
alert('Your account has been successfully created.');
this.toggleDisplay();
},
() => {
// errors here...
alert('Unfortunately we were unable to create your account.');
}
);
}
Tested under Android and IOs, both throws the same error.
Isn't application/json the expected Content-Type in this case?
If I change the type I get another error saying that the type is missing or is invalid.
Can someone please point out what am I missing?