Add `pipe queries`, which lists every Firestore query a project can issue: scope, collection, path pattern, filters with operators and value types, composites, orderBy, limit, cursors, realtime, aggregation, in-loop, plus a confidence and the reasons it is not exact. Receivers are matched by static type and cloud_firestore library URI, never by name. Queries are followed through chains, local variables (flow-insensitive with two cuts), fields, getters and one level of helper methods with arguments bound. An --adapter-config (YAML or JSON) maps declarative query APIs such as a server-side gateway onto the same schema. Inventory only: no rules, findings or baseline changes. Every emitted name is validated against a safe character set so no source text reaches the output.
129 lines
3.8 KiB
Dart
129 lines
3.8 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
|
|
/// Queries assembled across statements, getters and helpers.
|
|
class NotificationRepository {
|
|
NotificationRepository(this._firestore)
|
|
: _receipts = _firestore.collection('notificationReceipts');
|
|
|
|
final FirebaseFirestore _firestore;
|
|
final CollectionReference<Map<String, dynamic>> _receipts;
|
|
late final CollectionReference<Map<String, dynamic>> _inbox =
|
|
_firestore.collection('inbox');
|
|
|
|
CollectionReference<Map<String, dynamic>> get _collection =>
|
|
_firestore.collection('notifications');
|
|
|
|
Stream<QuerySnapshot<Map<String, dynamic>>> streamUserNotifications(
|
|
String userId, {
|
|
bool unreadOnly = false,
|
|
int? limit,
|
|
DocumentSnapshot<Object?>? startAfter,
|
|
}) {
|
|
Query<Map<String, dynamic>> query = _collection.where(
|
|
'userId',
|
|
isEqualTo: userId,
|
|
);
|
|
|
|
query = query.orderBy('timestamp', descending: true);
|
|
|
|
if (limit != null && !unreadOnly) {
|
|
query = query.limit(limit);
|
|
}
|
|
|
|
if (startAfter != null) {
|
|
query = query.startAfterDocument(startAfter);
|
|
}
|
|
|
|
return query.snapshots();
|
|
}
|
|
|
|
/// A clause assigned inside the same `if` as the terminal is not optional
|
|
/// there; an assignment after a terminal cannot reach it.
|
|
Stream<QuerySnapshot<Map<String, dynamic>>> platform({
|
|
bool onlyActive = false,
|
|
}) {
|
|
Query<Map<String, dynamic>> query = _collection.where(
|
|
'userId',
|
|
isNull: true,
|
|
);
|
|
if (onlyActive) {
|
|
query = query.where('isActive', isEqualTo: true);
|
|
return query.snapshots();
|
|
}
|
|
query = query.orderBy('timestamp', descending: true);
|
|
return query.snapshots();
|
|
}
|
|
|
|
Future<void> fallback(Query<Map<String, dynamic>>? custom) async {
|
|
await (custom ?? _collection.where('kind', isEqualTo: 'default')).get();
|
|
}
|
|
|
|
Future<void> ternaryClause(bool newestFirst) async {
|
|
final base = _collection.where('isRead', isEqualTo: false);
|
|
final ordered = newestFirst
|
|
? base.orderBy('createdAt', descending: true)
|
|
: base.orderBy('createdAt');
|
|
await ordered.get();
|
|
}
|
|
|
|
Future<void> receipts(String userId) async {
|
|
await _receipts.where('userId', isEqualTo: userId).get();
|
|
await _inbox.limit(3).get();
|
|
}
|
|
|
|
Query<Map<String, dynamic>> _buildListingQuery({
|
|
required String userId,
|
|
required String collectionName,
|
|
}) {
|
|
return _firestore
|
|
.collectionGroup(collectionName)
|
|
.where('userId', isEqualTo: userId)
|
|
.orderBy('createdAt', descending: true);
|
|
}
|
|
|
|
Future<void> listingPage(String userId, int pageSize) async {
|
|
final orderedQuery = _buildListingQuery(
|
|
userId: userId,
|
|
collectionName: 'products',
|
|
);
|
|
await orderedQuery.limit(pageSize).get();
|
|
}
|
|
|
|
Query<Map<String, dynamic>> _nested() => _buildListingQuery(
|
|
userId: 'u',
|
|
collectionName: 'services',
|
|
);
|
|
|
|
Future<void> nestedHelper() async {
|
|
await _nested().get();
|
|
}
|
|
|
|
Future<void> fromParameter(Query<Map<String, dynamic>> query) async {
|
|
await query.where('visible', isEqualTo: true).limit(10).get();
|
|
}
|
|
|
|
Future<void> inLoops(List<String> ids) async {
|
|
for (final id in ids) {
|
|
await _firestore.collection('stores').doc(id).collection('items').get();
|
|
}
|
|
await Future.wait(
|
|
ids.map(
|
|
(id) => _firestore
|
|
.collectionGroup('products')
|
|
.where('userId', whereIn: [id]).get(),
|
|
),
|
|
);
|
|
ids.forEach((id) {
|
|
_firestore.collection('audit').where('id', isEqualTo: id).get();
|
|
});
|
|
await Future.wait([_firestore.collection('once').get()]);
|
|
}
|
|
}
|
|
|
|
/// A top-level helper returning a query.
|
|
Query<Map<String, dynamic>> activeStores(FirebaseFirestore firestore) =>
|
|
firestore.collection('stores').where('active', isEqualTo: true);
|
|
|
|
Future<void> countActiveStores(FirebaseFirestore firestore) async {
|
|
await activeStores(firestore).count().get();
|
|
}
|