javascript - Existe um operador RXJs melhor para usar neste cenário?
Eu tenho um método de serviço que faz o seguinte:
- Encontra o usuário no Banco de Dados pelo seu ID
- Verifica se um usuário foi encontrado
- Usa bcrypt para comparar a senha armazenada no banco de dados com uma senha fornecida como parâmetro
- Lança uma UnauthorizedException se a senha estiver incorreta, retorna o usuário se estiver correta.
Estou apenas tentando descobrir se existe uma maneira melhor de usar os operadores do RxJS para fazer isso porque não gosto de ter que usar pipe a partir do bcrypt.compare:
public validateUser(email: string, pass: string): Promise<UserDto> {
return this.userService
.findOne({ email })
.pipe(
map((user: UserDto) => {
if (!user || !user.password) {
return throwError(new UnauthorizedException());
}
return user;
}),
switchMap((user: UserDto) => {
return from(
bcrypt.compare(pass, user.password) as Promise<boolean>
).pipe(
map((passwordIsCorrect) => ({
passwordIsCorrect,
user
}))
);
}),
switchMap((res) => {
if (!res.passwordIsCorrect) {
return throwError(new UnauthorizedException());
}
return of(res.user);
})
)
.toPromise();
}
Resposta #1
Eu não acho que haja um operador melhor para usar, mas você poderia simplificar o código para estar tudo dentro do mesmo switchMap assim:
public validateUser(email: string, pass: string): Promise<UserDto> {
return this.userService.findOne({ email }).pipe(
switchMap(user => {
if (!user?.password) {
return throwError(new UnauthorizedException());
}
return from(bcrypt.compare(pass, user.password)).pipe(
switchMap(passwordIsCorrect => passwordIsCorrect ? of(user) : throwError(new UnauthorizedException()))
)
})
).toPromise();
}
No entanto, neste caso, parece que você está lutando para usar observáveis, (converter promessa em observável, apenas para converter de volta em promessa).
Mesmo que o userService retorne um observable, por que não convertê-lo diretamente em uma promise? Parece que o código seria muito mais simples:
public async validateUser(email: string, pass: string): Promise<UserDto> {
const user = await this.userService.findOne({ email }).toPromise();
if (!user?.password || !await bcrypt.compare(pass, user.password)) {
throw new UnauthorizedException();
}
return user;
}
Resposta #2
Se você realmente não gosta do pipe interno, você poderia usar combineLatest para passar junto o valor de user assim como passwordIsCorrect:
public validateUser(email: string, pass: string): Promise<UserDto> {
return this.userService
.findOne({ email })
.pipe(
map((user: UserDto) => {
if (!user || !user.password) {
return throwError(new UnauthorizedException());
}
return user;
}),
switchMap((user: UserDto) => {
return combineLatest([
from(bcrypt.compare(pass, user.password)),
of(user)
])
}),
map(([passwordIsCorrect, user]) => ({
passwordIsCorrect,
user
})),
switchMap((res) => {
if (!res.passwordIsCorrect) {
return throwError(new UnauthorizedException());
}
return of(res.user);
})
)
.toPromise();
}
