有没有办法从可观察序列中获取过去 2 个结果并用作第三个结果的输入。
这是没有任何可观察量的代码。
public login(...): Promise<any> {
const user: any = ctx.prisma.query.user({ where: { email } });
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
const valid = bcrypt.compare(password, user.password);
if (!valid) {
throw new Error("Invalid password");
}
return {
token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
user,
};
}
这是我的解决方案,它不起作用,让我问这个问题。这不起作用,因为我没有调用ctx.prisma.query.user(...)
user
结果,当我需要检查valid
并执行我的map
时。
public login(...): Promise<any> {
return from(ctx.prisma.query.user({ where: { email } })).pipe(
mergeMap(
(user: User) => {
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
return from(bcrypt.compare(password, user.password));
}
),
map(
(valid: boolean) => {
if (!valid) {
throw new Error("Invalid password");
}
return {
token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
user,
};
}
)
).toPromise();
}
这是我对这个有效解决方案的尝试,但我只是想知道这是否正确或是否有更好的方法。
我需要user
和valid
结果在我的map
,以便我可以创建正确的对象。
我的解决方案只是感觉很奇怪,因为如果我需要继续使用这个序列和之前的结果,它会变得非常深入。
public login(...): Promise<any> {
return from(ctx.prisma.query.user({ where: { email } })).pipe(
switchMap(
(user: User) => {
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
return from(bcrypt.compare(password, user.password)).pipe(
map(
(valid: boolean) => {
if (!valid) {
throw new Error("Invalid password");
}
return {
token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
user,
};
}
)
);
}
)
).toPromise();
}
switchMap 是你想要的运算符,switchMap 的第二个参数是一个 map 函数,它获取内部和外部可观察量的结果,并允许您将它们组合在一起(mergemap 接受相同的第二个参数,但首选 switchMap,因为它是"更安全"的运算符(:
public login(...): Promise<any> {
return from(ctx.prisma.query.user({ where: { email } })).pipe(
switchMap(
(user: User) => {
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
return from(bcrypt.compare(password, user.password));
}
, (user, valid) => {
if (!valid) {
throw new Error("Invalid password");
}
return {
token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
user,
};
}
)).toPromise();
}
在您的解决方案中,您可以做一些事情来避免所有级别的嵌套,但 IMO 您拥有的东西没有错。
我所做的第一个更改是使用tap
进行错误检查。 这实际上只是为了外观,尽管我想检查地图中的错误感觉不对。
接下来,将switchMap
更改为采用一个forkJoin
,该将检索到的用户值和解密结果组合在一起。forkJoin
是我能想到的最干净的方式,可以在序列中进一步传递用户。
其余的都很简单。
from(ctx.prisma.query.user({ where: { email } })).pipe(
tap(user => { if (!user) throw new Error(`No such user found for email: ${email}`); }),
switchMap(user => forkJoin(of(user), from(bcrypt.compare(password, user.password))),
tap(([user, valid]) => { if (!valid) throw new Error('Invalid password'); })
map(([user, valid]) => ({
token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
user,
}))
)
在 @bryan60 和 @Daniel Gimenez 的帮助下,我们得出的结论是,我在问题中提供的解决方案在版本 6 中是最准确的。这是我的最终解决方案。
public login(
source: any,
{email, password},
ctx: IContext,
info: GraphQLResolveInfo): Promise<any> {
return from(ctx.prisma.query.user({ where: { email } })).pipe(
tap(
(user: User) => {
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
}
),
switchMap(
(user: User) => {
return from(bcrypt.compare(password, user.password)).pipe(
map(
(valid: boolean) => {
if (!valid) {
throw new Error("Invalid password");
}
return {
token: jwt.sign(
{ userId: user.id },
process.env.APP_SECRET
),
user,
};
}
)
);
}
)
).toPromise();
}