0

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;
  }
whatwhatwhat
  • 1,991
  • 4
  • 31
  • 50
  • It's not possible to perform an atomic transaction that involves different products. Your code will need to be able to handle errors in each product individually and decide what to do if either one of them fails. – Doug Stevenson Aug 09 '23 at 03:34
  • @DougStevenson dang. Yea I see that other question, but the answer was from 2019. Was hoping this was possible now. Thanks anyway! – whatwhatwhat Aug 09 '23 at 03:39
  • 1
    It's never going to be possible. Transactions are hard enough within a single product. – Doug Stevenson Aug 09 '23 at 03:41

0 Answers0