0

When I'm trying to carry transaction on firestore it throws me a promise error. Actually, I had problem on this same code before and got cleared in this thread Firebase Cloud Functions throws await as an error on firestore Transaction write

Below is the code

This is the Error thrown

=== Deploying to 'library-1be0e'...

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /home/abibv/Downloads/Development/PDL Library/Nec-it-Library-PWA/functions
> eslint .


/home/abibv/Downloads/Development/PDL Library/Nec-it-Library-PWA/functions/index.js
  166:21  error  Each then() should return a value or throw  promise/always-return

✖ 1 problem (1 error, 0 warnings)

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! functions@ lint: `eslint .`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the functions@ lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/abibv/.npm/_logs/2020-04-13T02_51_17_897Z-debug.log

Error: functions predeploy error: Command terminated with non-zero exit code1

Here is the code

 return admin.firestore().runTransaction(transaction => {
            return transaction.get(memberRef).then(async(doc) => {
                    var transactionRef = admin.firestore().collection('transactions').doc();
                    var transId = '';
                    // write Transaction
                    try {
                        var setData = await transaction.set(transactionRef, transactionData);
                        transId = transactionRef.id;
                    } catch (error) {
                        console.log(error);
                        throw new functions.https.HttpsError(
                            'unknown',
                            'New user cannot be created at the moment due to some unknown reasons. Please try again'
                        )
                    }

                    transaction.set(memberRef, {
                        transactionId: transId
                    }, { merge: true })

                    transaction.update(membeRef, {
                        totalBooks: totalBooks - keys.length
                    });

                    transactionBooks.forEach(transBook => {
                        transaction.update(transBook, {
                            status: false
                        })
                    })


                })
                .catch(error => {
                    console.log(error);
                    return error;
                })
        })
        .then(result => {
            console.log(result);
            return { message: 'Issued' };
        })
        .catch(error => {
            console.log(error);
            return error;
        });

Can't I use async/await instead of {then() catch()} ??? What is the reason for this Error?. Thanks in Advance.

Here is the Full code of the Function

exports.issueBook = functions.https.onCall(async(data, context) => {
    if (!(context.auth && context.auth.token.admin)) {
        throw new functions.https.HttpsError(
            'unauthenticated',
            'only authenticated Admins can Issue Books'
        );
    }

    memberData = {
        name: data.issueData.memberName,
        no: data.issueData.memberNo,
        role: data.issueData.memberRole,
    }
    transactionData = {
        books: data.issueData.books,
        creation: new Date(),
        member: memberData,
    }

    var keys = Object.keys(transactionData.books);
    var date = new Date();
    transactionData.books["takenDate"] = date;
    date.setDate(date.getDate() + 7);
    var dueDate = date;
    if (transactionData.memberRole === "student") {
        transactionData.books["dueDate"] = dueDate;
    }

    const membeRef = admin.firestore().collection('users').doc(transactionData.member.no);
    var memberDoc = await membeRef.get();
    if (!memberDoc.exists) {
        try {
            await memberRef.set({
                name: data.issueData.memberName,
                no: data.issueData.memberNo,
                email: data.issueData.memberEmail,
                role: data.issueData.memberRole,
                created: data.issueData.created,
                totalBooks: 5,
            })
        } catch (error) {
            console.log(error);
            throw new functions.https.HttpsError(
                'unknown',
                'New user cannot be created at the moment due to some unknown reasons. Please try again'
            );
        }
    }

    var transactionBooks = [];

    try {
        keys.forEach(async(docNo) => {
            book = await admin.firestore().collection('books').where("no", "==", docNo).limit(1);
            transactionBooks.push(book);
        })
    } catch (error) {
        console.log(error);
        throw new functions.https.HttpsError(
            'unknown',
            'Book Data cannot be read at the moment. Please try again'
        );
    }

    return admin.firestore().runTransaction(transaction => {
            return transaction.get(memberRef).then((doc) => {

                if (doc.data().role === 'student' && keys.length > doc.data().totalBooks) {
                    throw new functions.https.HttpsError(
                        'failed-precondition',
                        'Student cannot have more than 5 Books'
                    );
                }

                const transactionRef = admin.firestore().collection('transactions').doc();
                var transId = '';
                // write Transaction
                transaction.set(transactionRef, transactionData);
                transId = transactionRef.id;

                transaction.set(memberRef, {
                    transactionId: transId
                }, { merge: true });

                transaction.update(membeRef, {
                    totalBooks: totalBooks - keys.length
                });

                transactionBooks.forEach(transBook => {
                    transaction.update(transBook, {
                        status: false
                    })
                });
            });
        })
        .then(result => {
            console.log(result);
            return { message: 'Issued' };
        })
        .catch(error => {
            console.log(error);
            return error;
        });
});
Jerry_0x04bc
  • 41
  • 1
  • 10

1 Answers1

0

The problem line is here.

return transaction.get(memberRef).then(async(doc) => {

You don't need set async. Because transaction.set() does not return Promise. So, You should not use await and try catch in the following code.

                  try {
                        var setData = await transaction.set(transactionRef, transactionData);
                        transId = transactionRef.id;
                    } catch (error) {
                        console.log(error);
                        throw new functions.https.HttpsError(
                            'unknown',
                            'New user cannot be created at the moment due to some unknown reasons. Please try again'
                        )
                    }

See:

Ex.

    return admin.firestore().runTransaction(transaction => {
        return transaction.get(memberRef).then((doc) => {
            const transactionRef = admin.firestore().collection('transactions').doc();
            transaction.set(transactionRef, transactionData);

            transaction.set(memberRef, {
                transactionId: transactionRef.id
            }, {merge: true})

            transaction.update(membeRef, {
                totalBooks: totalBooks - keys.length
            });

            // What are transactionBooks?
            transactionBooks.forEach(transBook => {
                transaction.update(transBook, {
                    status: false
                })
            })


        }).catch(error => {
            console.log(error);
            return error;
        })
    })
        .then(result => {
            console.log(result);
            return {message: 'Issued'};
        })
        .catch(error => {
            console.log(error);
            return error;
        });

Updated

The error message is Each then() should return a value or throw promise/always-return.

See:

So, You should return a value or throw in the following async function.

Ex.

exports.issueBook = functions.https.onCall(async (data, context) => {
    if (!(context.auth && context.auth.token.admin)) {
        throw new functions.https.HttpsError(
            'unauthenticated',
            'only authenticated Admins can Issue Books'
        );
    }
    memberData = {
        name: data.issueData.memberName,
        no: data.issueData.memberNo,
        role: data.issueData.memberRole,
    }
    transactionData = {
        books: data.issueData.books,
        creation: new Date(),
        member: memberData,
    }

    var keys = Object.keys(transactionData.books);
    var date = new Date();
    transactionData.books["takenDate"] = date;
    date.setDate(date.getDate() + 7);
    var dueDate = date;
    if (transactionData.memberRole === "student") {
        transactionData.books["dueDate"] = dueDate;
    }

    const membeRef = admin.firestore().collection('users').doc(transactionData.member.no);
    var memberDoc = await membeRef.get();
    if (!memberDoc.exists) {
        try {
            await memberRef.set({
                name: data.issueData.memberName,
                no: data.issueData.memberNo,
                email: data.issueData.memberEmail,
                role: data.issueData.memberRole,
                created: data.issueData.created,
                totalBooks: 5,
            })
        } catch (error) {
            console.log(error);
            throw new functions.https.HttpsError(
                'unknown',
                'New user cannot be created at the moment due to some unknown reasons. Please try again'
            );
        }
    }

    var transactionBooks = [];

    try {
        // keys.forEach(async(docNo) => {
        //     book = await admin.firestore().collection('books').where("no", "==", docNo).limit(1);
        //     transactionBooks.push(book);
        // })
        transactionBooks = await Promise.all(keys.map((docNo) => {
            return admin.firestore().collection('books').where("no", "==", docNo).limit(1).get();
        }))
    } catch (error) {
        console.log(error);
        throw new functions.https.HttpsError(
            'unknown',
            'Book Data cannot be read at the moment. Please try again'
        );
    }

    return admin.firestore().runTransaction(transaction => {
        return transaction.get(memberRef).then((doc) => {

            if (doc.data().role === 'student' && keys.length > doc.data().totalBooks) {
                throw new functions.https.HttpsError(
                    'failed-precondition',
                    'Student cannot have more than 5 Books'
                );
            }

            const transactionRef = admin.firestore().collection('transactions').doc();
            var transId = '';
            // write Transaction
            transaction.set(transactionRef, transactionData);
            transId = transactionRef.id;

            transaction.set(memberRef, {
                transactionId: transId
            }, {merge: true});

            transaction.update(membeRef, {
                totalBooks: totalBooks - keys.length
            });

            transactionBooks.forEach(transBook => {
                transaction.update(transBook, {
                    status: false
                })
            });

            // Add return
            return null;
        });
    })
        .then(result => {
            console.log(result);
            return {message: 'Issued'};
        })
        .catch(error => {
            console.log(error);
            return error;
        });
});
zkohi
  • 2,486
  • 1
  • 10
  • 20
  • Tried it now. But throws the same error on 2nd line `return transaction.get(memberRef).then((doc) => {...})` The `transactionBooks` is an array of document references of different Books. I've to update `status` field on different document on a same collection. For that I've used `ForEach loop` – Jerry_0x04bc Apr 13 '20 at 10:02
  • In the `transactionBooks` Array, I should've the document References of each books as per the code. But while I'm trying to update the Book data, Firebase CF throws me an error `Error: Value for argument "documentRef" is not a valid DocumentReference.` on the line `transaction.update(transBook, { status: false })` – Jerry_0x04bc Apr 13 '20 at 13:55
  • @ABIB I edited my answer. Add `.get()` like the following code. `return admin.firestore().collection('books').where("no", "==", docNo).limit(1).get();` – zkohi Apr 25 '20 at 23:07