Облачные функции HTTPS: возвращение Обещания, которое находится внутри Обещателя - PullRequest
0 голосов
/ 08 марта 2020

В настоящее время я работаю над облачной функцией HTTPS с использованием Firebase, заключающейся в удалении сообщения, запрошенного моим 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) выполняет обещание удаления файла и пакета (return Promise.all([deleteFile, batch_commit])).

Ожидаемое поведение

Я бы хотел проверить личность пользователя. Если это успешно, чтобы получить запрошенный пост, чтобы удалить данные с помощью Firebase. В случае успеха я бы хотел выполнить пакет Firestore плюс удаление файла Cloud Storage в том же обещании (поэтому я использую Promise.all([deleteFile, batch_commit]).then()). Если проверка идентичности завершится неудачно, или если данные не получатся, или если пакет не пройден, я бы хотел сообщить приложению Android. Если все успешно, то же самое.

Поскольку все эти операции выполняются в облачной HTTPS-функции, я должен вернуть обещание. Это обещание, я думаю, будет соответствовать всем этим операциям, если они будут успешными, или ошибке, если хотя бы одна из них не будет (?).

Фактическое поведение

На данный момент я просто верните обещание проверки личности пользователя Firebase.

Моя проблема и мой вопрос

Я не могу go от фактического поведения к ожидаемому, потому что:

  1. Мне кажется, не очень понятно, должен ли я вернуть обещание, соответствующее «все эти операции выполнены успешно или, по крайней мере, одна из них» в этой функции Cloud HTTPS

  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.');
          });
});

1 Ответ

1 голос
/ 08 марта 2020

Следует отметить, что вы на самом деле определяете функцию вызываемого облака , а не HTTPS one , поскольку вы делаете:

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

Одно из преимуществ функции вызываемого облака через HTTPS, одна из них заключается в том, что она «автоматически десериализует тело запроса и проверяет токены аутентификации».

Таким образом, вы можете просто получить пользователя uid с помощью context.auth.uid;.


Теперь, что касается способа "оркестровки" различных вызовов, IMHO, вы должны просто объединить в цепочку различные Обещания, возвращаемые асинхронными методами Firebase (те из Firestore и те из Cloud Storage), следующим образом:

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() принесет какой-либо интерес, в вашем случае, потому что, как объяснено здесь , "если любое из переданных обещаний отклоняется, Promise.all асинхронно отклоняет со значением отклоненного обещания, независимо от того, разрешены ли другие обещания".

На момент написания не было возможности сгруппировать все эти асинхронные вызовы к различным службам Firebase в одну операцию атома c.

Даже если пакетная запись в конце Atomi c, может случиться так, что файл в облачном хранилище будет правильно удален, но пакетная запись в Firestore не будет выполнена, например, из-за проблемы со службой Firestore.


Также обратите внимание, что вам нужен только один обработчик исключений в конце цепочки Promise. Если вы хотите разграничить причину исключения, таким образом, чтобы вы отправили другое сообщение об ошибке внешнему интерфейсу, вы могли бы использовать подход, представленный в этой статье .

. В статье показано, как определить различные пользовательские классы ошибок (производные от стандартного встроенного объекта Error), которые используются для проверки типа ошибки в обработчике исключений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...