I'm using Firestore runTransaction
method in a https Cloud function (running Express).
What I want is ensure that if any of the read or write fails the other transaction reads or writes won't run or will rollback if needed.
Transaction
admin.firestore().runTransaction(async (t) => {
const { uid, artworkUid, tagLabel } = req.body;
if (!tagLabel) {
return Promise.reject('Missing parameters: "tagLabel"');
}
if (!artworkUid) {
return Promise.reject('Missing parameters: "artworkUid"');
}
if (tagLabel.length < 3) {
return Promise.reject('The tag must be at least 3 characters long');
}
const [ user, artwork ] = await Promise.all([
getUser(uid),
getArtwork(artworkUid)
]);
return Promise.all([
addArtworkTag({
artwork,
tagLabel,
proposer: user
}, t),
giveXpFor({
user,
action: 'add-artwork-tags',
type: user.can('add-artwork-tags') ? 'effective' : 'temporary'
}, t)
])
})
.catch(err => res.status(403).send(err))
.then(() => res.status(200).send());
As you can see the addArtworkTag
and giveXpFor
functions take the t
in parameter.
addArtworkTag
export const addArtworkTag = function(params: { artwork: any, tagLabel: string, proposer: ShadraUser }, t?: FirebaseFirestore.Transaction): Promise<any> {
const { artwork, tagLabel, proposer } = params;
if (!artwork || !proposer || typeof tagLabel !== 'string') {
return Promise.reject('Can\'t add the tag. Bad/missing datas');
}
const tag = <any>{
slug: slugify(tagLabel),
label: tagLabel,
status: proposer.can('add-artwork-tags') ? 'validated' : 'proposed',
proposerUid: proposer.uid
};
const tags = artwork.tags || [];
tags.push(tag);
const artworkRef = admin.firestore().doc(`artworks/${artwork.uid}`);
t.set(artworkRef, { tags }, { merge: true });
return Promise.resolve();
}
My question is: If the addArtworkTag
function fails (because of a bad parameter for instance) how can I abort (or even rollback) the transaction so giveXpFor
is not called
Thanks a lot
PS: I think I've misused transactions... What I should do is probably just use sequential promises instead of Promise.all, right ?
The API documentation for runTransaction states:
If the transaction completed successfully or was explicitly aborted (by the updateFunction returning a failed Promise), the Promise returned by the updateFunction will be returned here. Else if the transaction failed, a rejected Promise with the corresponding failure error will be returned.
All you have to do the fail a transaction correctly is return a rejected promise. It doesn't matter what you've done in that transaction - all of it will be rolled back.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With