I have 3 functions, shown below. I need either all of them to work or none of them to work, otherwise the data would be messy and I would need to run a cloud function on some frequency to do cleanup, which I'd like to avoid.
The reason I can't use a trigger to run the next function in sequence is because my app needs to know when the 2 functions have been completed before switching the user to a different screen. If I switch the user to another screen while a trigger is completing the background work in some unknown amount of time, I get errors on the client side. So I need to know when this background work has been completed.
I know that Firestore and Realtime Database have transactions, but how would I use both of these together from the client side (Flutter)? If the 3rd function fails, I would need to rollback the work of the first two functions and show the user an error message. Is this possible?
Firestore Function 1:
Future<String> createGroupFS({required String groupName, required String groupSlogan}) async {
await globals.userGlobals(); //Required if using globals
final timestamp = getFirestoreTimestamp();
FirebaseFirestore firestore = FirebaseFirestore.instance;
final mainGroupsCollectionPath = globals.mainGroupsCollection();
final newGroupDoc = firestore.collection(mainGroupsCollectionPath).doc();
var groupID = newGroupDoc.id;
final adminUID = globals.currentUID;
final adminUsername = globals.currentUsername;
final data = ModelMainGroup(
groupID: groupID,
groupName: groupName,
adminUID: adminUID,
adminUsername: adminUsername,
createDate: timestamp,
creatorUID: adminUID,
creatorUsername: adminUsername,
initialGroupCreate: true,
fireTriggers: true,
).toJson();
newGroupDoc.set(data).onError((error, stackTrace) {
groupID = '';
return groupID;
});
//Add current user as the first member
final mainGroupMembersCollectionRef = globals.mainGroupMembersCollection(groupID: groupID);
final mainGroupMembersDocRef = firestore.collection(mainGroupMembersCollectionRef).doc(globals.currentUID);
final membersData = ModelGroupMember(
username: adminUsername,
dateAdded: timestamp
).toJson();
mainGroupMembersDocRef.set(membersData);
return groupID;
}
Firestore Function 2:
Future<void> addGroupToCollection({required String groupID}) async {
await globals.userGlobals(); //Required if using globals
final groupsCollectionPath = globals.groupsCollection();
FirebaseFirestore firestore = FirebaseFirestore.instance;
final newDoc = await firestore.collection(groupsCollectionPath).doc(groupID).get();
if (newDoc.exists) {
return;
}
final timestamp = getFirestoreTimestamp();
final data = ModelGroup(dateAdded: timestamp).toJson();
await firestore.collection(groupsCollectionPath).doc(groupID).set(data);
}
Realtime Database Callable:
Future<Map<String, dynamic>> createGroupRTDB ({required String groupID, required String adminUID, required String adminUsername}) async {
//Callable that does the RTDB group setup
Map<String, String> createdTimestamp = getRTDBTimestamp();
Map<String, dynamic> returnData = {};
Map<String, dynamic> parms = {
'groupID': groupID,
'adminUID': adminUID,
'adminUsername': adminUsername,
'createdTimestamp': createdTimestamp,
};
try {
final callResult = await FirebaseFunctions.instance.httpsCallable('callGroupRTDBSetup').call(parms);
returnData = {
'returnVal': 1,
'returnMsg': '',
};
} on FirebaseFunctionsException catch (e) {
returnData = {
'returnVal': 0,
'returnMsg': e.toString(),
};
} catch (e) {
returnData = {
'returnVal': 0,
'returnMsg': e.toString(),
};
}
return returnData;
}