FirebaseFirestoreFake.stateful constructor

FirebaseFirestoreFake.stateful({
  1. Map<String, CollectionReferenceFake>? collections,
  2. void onCollectionChanged(
    1. String path,
    2. Map<String, DocumentReferenceFake> collectionDocuments,
    3. List<QueryFakeAndStreamController> queries
    )?,
})

Creates a stateful FirebaseFirestore fake that maintains its own state and aims to mimic firebase as closely as possible

Implementation

factory FirebaseFirestoreFake.stateful({
  Map<String, CollectionReferenceFake>? collections,
  void Function(
    String path,
    Map<String, DocumentReferenceFake> collectionDocuments,
    List<QueryFakeAndStreamController> queries,
  )?
      onCollectionChanged,
}) {
  collections ??= <String, CollectionReferenceFake>{};

  onCollectionChanged ??= (path, collectionDocuments, queries) async {
    for (final queryFakeAndController in queries) {
      final futures = collectionDocuments.values.map((ref) => ref.get());
      final documentSnapshots = (await Future.wait(futures))
          .where(
            (snapshot) {
              final whereClause =
                  queryFakeAndController.queryFake.whereClause;
              if (whereClause == null) {
                return true;
              }
              if (whereClause.isEqualTo != null) {
                return snapshot.data()![whereClause.field] ==
                    whereClause.isEqualTo;
              } else if (whereClause.isNull != null) {
                return whereClause.isNull! &&
                        snapshot.data()![whereClause.field] == null ||
                    !whereClause.isNull! &&
                        snapshot.data()![whereClause.field] != null;
              } else {
                throw UnimplementedError('Where clauses of this type '
                    'have not been implemented. Please log a GitHub '
                    'issue and  hopefully contribute with a PR');
              }
            },
          )
          .map((snapshot) => QueryDocumentSnapshotFake(snapshot.data()!))
          .toList();
      queryFakeAndController.controller.add(
        QuerySnapshotFake(documentSnapshots),
      );
    }
  };

  return FirebaseFirestoreFake(
    collection: (collectionPath) {
      //TODO: what is the standard behaviour in firestore?
      //will it add the collection automatically?

      final queriesByCollection =
          <String, List<QueryFakeAndStreamController>>{};

      collections!.putIfAbsent(
        collectionPath,
        () => CollectionReferenceFake.stateful(
          collectionPath,
          onChanged: (documents) {
            onCollectionChanged?.call(
              collectionPath,
              documents,
              queriesByCollection[collectionPath] ?? [],
            );
          },
          where: (
            field, {
            isEqualTo,
            isNotEqualTo,
            isLessThan,
            isLessThanOrEqualTo,
            isGreaterThan,
            isGreaterThanOrEqualTo,
            arrayContains,
            arrayContainsAny,
            whereIn,
            whereNotIn,
            isNull,
          }) {
            // ignore: close_sinks
            final controller = StreamController<
                QuerySnapshot<Map<String, dynamic>>>.broadcast();

            final queryFake = QueryFake(
              snapshots: controller.stream,
              whereClause: WhereClause(
                field,
                isEqualTo: isEqualTo,
                isNotEqualTo: isNotEqualTo,
                isLessThan: isLessThan,
                isLessThanOrEqualTo: isLessThanOrEqualTo,
                isGreaterThan: isGreaterThan,
                isGreaterThanOrEqualTo: isGreaterThanOrEqualTo,
                arrayContains: arrayContains,
                arrayContainsAny: arrayContainsAny,
                whereIn: whereIn,
                whereNotIn: whereNotIn,
                isNull: isNull,
              ),
            );

            if (queriesByCollection[collectionPath] == null) {
              queriesByCollection[collectionPath] = [];
            }
            queriesByCollection[collectionPath]!.add(
              QueryFakeAndStreamController(
                queryFake,
                controller,
              ),
            );

            return queryFake;
          },
        ),
      );

      return collections[collectionPath]!;
    },
  );
}