javascript - 云HTTPS函数: returning a Promise which is inside a Promisee

标签 javascript android firebase google-cloud-firestore google-cloud-functions

我目前正在使用 Firebase 开发 HTTPS 云功能,包括删除我的 Android 用户请求的帖子。

总体思路

工作流程是(整个代码在此SO问题的末尾可用):1)Firebase检查用户身份( admin.auth().verifyIdToken ); 2) Firestore 从必须删除的帖子中获取数据 ( deleteDbEntry.get().then() ) ; 3) 云存储准备删除获取数据中找到的文件(.file(filePath).delete()); 4) Firestore 准备一批删除帖子 ( batch.delete(deleteDbEntry); ) 并使用获取的数据更新喜欢/不喜欢的内容 ( batch.update(updateUserLikes, ) ; 5) 执行删除文件和批处理的 promise ( return Promise.all([deleteFile, batch_commit]) )。

预期行为

我想检查用户身份。如果成功,则使用 Firebase 获取请求的帖子以删除数据。如果成功,我希望在同一个 promise 中执行 Firestore 批处理和云存储文件删除(这就是我使用 Promise.all([deleteFile, batch_commit]).then() 的原因)。如果身份检查失败,或者数据获取失败,或者批处理失败,我想告诉Android应用程序。如果一切成功,同上。

由于所有这些操作都在 Cloud HTTPS Function 中,因此我必须返回一个 promise 。我认为,这个 promise 将对应于所有这些操作(如果它们成功),或者如果至少有一个操作不成功(?),则对应于错误(?)。

实际行为

目前,我只是返回 Firebase 用户身份检查的 promise 。

我的问题和我的问题

我无法从实际行为转向预期行为,因为:

  1. 我觉得自己不太清楚是否应该在这个 Cloud HTTPS Function 中返回“所有这些操作都成功,或者至少有一个不成功”对应的 Promise

  2. 由于这些操作是嵌套的(批量中存在的 Firestorage 文件删除 + Firestore 后删除除外),我无法返回类似 Promise.all() 的内容.

我的问题

您能告诉我我是否正确(第 1 点),如果不正确:我该怎么办?如果是:由于第 2 点,我该怎么做?

整个 Firebase Cloud HTTPS 函数代码

注意:我已经删除了输入数据控件以使我的代码更易于理解。

exports.deletePost = functions.https.onCall((data, context) => {

    return admin.auth().verifyIdToken(idToken)
          .then(function(decodedToken) {
            const uid = decodedToken.uid;

            const type_of_post = data.type_of_post;
            const the_post = data.the_post;
            const deleteDbEntry = admin_firestore.collection('list_of_' + type_of_post).doc(the_post);
            const promise = deleteDbEntry.get().then(function(doc) {

                const filePath = type_of_post + '/' + uid + '/' + data.stored_image_name;
                const deleteFile = storage.bucket('android-f.appspot.com').file(filePath).delete();

                const batch = admin.firestore().batch();
                batch.delete(deleteDbEntry);
                if(doc.data().number_of_likes > 0) {
                    const updateUserLikes = admin_firestore.collection("users").doc(uid);
                    batch.update(updateUserLikes, "likes", FieldValue.increment(-doc.data().number_of_likes));
                }
                const batch_commit = batch.commit();

                return Promise.all([deleteFile, batch_commit]).then(function() {
                    return 1;
                }).catch(function(error) {
                    console.log(error);
                    throw new functions.https.HttpsError('unknown', 'Unable to delete the post. (2)');
                });

            }).catch(function(error) { 
                console.log(error);
                throw new functions.https.HttpsError('unknown', 'Unable to delete the post. (1)');
            });

            return promise;


          }).catch(function(error) {
                console.log(error);
            throw new functions.https.HttpsError('unknown', 'An error occurred while verifying the token.');
          });
});

最佳答案

您应该注意,您实际上正在定义 Callable Cloud Function而不是 HTTPS one ,既然你这样做了:

exports.deletePost = functions.https.onCall((data, context) => {..});

可调用云函数相对于 HTTPS 的优势之一是它“自动反序列化请求正文并验证身份验证 token ”。

因此,您可以简单地使用 context.auth.uid; 获取用户 uid

<小时/>

现在,关于“编排”不同调用的方式,恕我直言,您应该链接异步 Firebase 方法(Firestore 的方法和 Cloud Storage 的方法)返回的不同 Promise,如下所示:

exports.deletePost = functions.https.onCall((data, context) => {

    //....
    const uid = context.auth.uid;

    let number_of_likes;

    const type_of_post = data.type_of_post;
    const the_post = data.the_post;
    const deleteDbEntry = admin_firestore.collection('list_of_' + type_of_post).doc(the_post);

    return deleteDbEntry.get()
        .then(doc => {
            number_of_likes = doc.data().number_of_likes;
            const filePath = type_of_post + '/' + uid + '/' + data.stored_image_name;
            return storage.bucket('android-f.appspot.com').file(filePath).delete();

        })
        .then(() => {

            const batch = admin.firestore().batch();
            batch.delete(deleteDbEntry);

            if (number_of_likes > 0) {
                const updateUserLikes = admin_firestore.collection("users").doc(uid);
                batch.update(updateUserLikes, "likes", FieldValue.increment(-doc.data().number_of_likes));
            }

            return batch.commit();

        }).catch(function (error) {
            console.log(error);
            throw new functions.https.HttpsError('....', '.....');
        });

});

我认为在您的情况下使用 Promise.all() 不会带来任何兴趣,因为,正如所解释的 here ,“如果任何传入的 Promise 被拒绝,Promise.all 会以被拒绝的 Promise 的值异步拒绝,无论其他 Promise 是否已解决”。

在撰写本文时,无法将对不同 Firebase 服务的所有异步调用分组到一个原子操作中。

即使最后的批量写入是原子的,也可能会发生 Cloud Storage 中的文件被正确删除但对 Firestore 的批量写入未执行的情况,例如因为 Firestore 服务出现问题。

<小时/>

另外,请注意,您只需要 Promise 链末尾的一个异常处理程序。如果您想区分异常的原因,通过向前端发送不同的错误消息的方式,您可以使用此 article 中介绍的方法。 .

本文展示了如何定义不同的自定义错误类(派生自标准内置错误对象),这些类用于检查异常处理程序中的错误类型。

关于javascript - 云HTTPS函数: returning a Promise which is inside a Promisee,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60587876/

相关文章:

java - 无法从静态上下文中引用非静态字段

java - 使用 FirebaseRecyclerAdapter 将 Firebase 实时数据库中的数据检索到 Recycler View 中

java - Android firebase - 显示特定数据

javascript - 更改parentNode.innerHTML后,无法访问parentNode

VS2008中的JavaScript调试

android - 将图像 URI 传递给另一个 Activity

javascript - 重绘元素会忽略 Android 浏览器中的 user-scalable=0。为什么?

javascript - React Hot Loader 似乎可以工作但不会更新。 [babel-preset-env]

javascript - 打印提交表单

ios - Swift 中的 Firebase。无法将类型 'NSNull' 的值转换为 'NSDictionary'