Skip to content

Commit

Permalink
Query recent commits from firestore (flutter#3572)
Browse files Browse the repository at this point in the history
Part of flutter/flutter#142951.

This is to prepare switching get-build-status queries to firestore.

This PR:
1) add function queryRecentCommits
2) add query option: `orders` and `limit`.
  • Loading branch information
keyonghan authored Mar 13, 2024
1 parent de7266b commit 84be1e8
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 36 deletions.
55 changes: 53 additions & 2 deletions app_dart/lib/src/service/firestore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:github/github.dart';
import 'package:googleapis/firestore/v1.dart';
import 'package:http/http.dart';

import '../model/firestore/commit.dart';
import '../model/firestore/github_gold_status.dart';
import '../model/firestore/task.dart';
import 'access_client_provider.dart';
Expand All @@ -18,12 +19,17 @@ const String kDatabase = 'projects/${Config.flutterGcpProjectId}/databases/${Con
const String kDocumentParent = '$kDatabase/documents';
const String kFieldFilterOpEqual = 'EQUAL';
const String kCompositeFilterOpAnd = 'AND';
const String kQueryOrderDescending = 'DESCENDING';

const int kFilterStringSpaceSplitElements = 2;
const int kFilterStringSpaceSplitOpIndex = 1;

const Map<String, String> kRelationMapping = <String, String>{
'=': 'EQUAL',
'<': 'LESS_THAN',
'<=': 'LESS_THAN_OR_EQUAL',
'>': 'GREATER_THAN',
'>=': 'GREATER_THAN_OR_EQUAL',
};

class FirestoreService {
Expand Down Expand Up @@ -77,6 +83,31 @@ class FirestoreService {
return databasesDocumentsResource.commit(commitRequest, kDatabase);
}

/// Queries for recent commits.
///
/// The [limit] argument specifies the maximum number of commits to retrieve.
///
/// The returned commits will be ordered by most recent [Commit.timestamp].
Future<List<Commit>> queryRecentCommits({
int limit = 100,
int? timestamp,
String? branch,
required RepositorySlug slug,
}) async {
timestamp ??= DateTime.now().millisecondsSinceEpoch;
branch ??= Config.defaultBranch(slug);
final Map<String, Object> filterMap = <String, Object>{
'$kCommitBranchField =': branch,
'$kCommitRepositoryPathField =': slug.fullName,
'$kCommitCreateTimestampField <': timestamp,
};
final Map<String, String> orderMap = <String, String>{
kCommitCreateTimestampField: kQueryOrderDescending,
};
final List<Document> documents = await query(kCommitCollectionId, filterMap, orderMap: orderMap, limit: limit);
return documents.map((Document document) => Commit.fromDocument(commitDocument: document)).toList();
}

/// Returns all tasks running against the speificed [commitSha].
Future<List<Task>> queryCommitTasks(String commitSha) async {
final Map<String, Object> filterMap = <String, Object>{
Expand Down Expand Up @@ -161,6 +192,17 @@ class FirestoreService {
);
}

List<Order>? generateOrders(Map<String, String>? orderMap) {
if (orderMap == null || orderMap.isEmpty) {
return null;
}
final List<Order> orders = <Order>[];
orderMap.forEach((field, direction) {
orders.add(Order(field: FieldReference(fieldPath: field), direction: direction));
});
return orders;
}

/// Wrapper to simplify Firestore query.
///
/// The [filterMap] follows format:
Expand All @@ -175,15 +217,24 @@ class FirestoreService {
Future<List<Document>> query(
String collectionId,
Map<String, Object> filterMap, {
int? limit,
Map<String, String>? orderMap,
String compositeFilterOp = kCompositeFilterOpAnd,
}) async {
final ProjectsDatabasesDocumentsResource databasesDocumentsResource = await documentResource();
final List<CollectionSelector> from = <CollectionSelector>[
CollectionSelector(collectionId: collectionId),
];
final Filter filter = generateFilter(filterMap, compositeFilterOp);
final RunQueryRequest runQueryRequest =
RunQueryRequest(structuredQuery: StructuredQuery(from: from, where: filter));
final List<Order>? orders = generateOrders(orderMap);
final RunQueryRequest runQueryRequest = RunQueryRequest(
structuredQuery: StructuredQuery(
from: from,
where: filter,
orderBy: orders,
limit: limit,
),
);
final List<RunQueryResponseElement> runQueryResponseElements =
await databasesDocumentsResource.runQuery(runQueryRequest, kDocumentParent);
return documentsFromQueryResponse(runQueryResponseElements);
Expand Down
18 changes: 18 additions & 0 deletions app_dart/test/service/firestore_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,22 @@ void main() {
expect(documents[0].name, githubGoldStatus.name);
});
});

group('generateOrders', () {
final FirestoreService firestoreService = FirestoreService(AccessClientProvider());
Map<String, String>? orderMap;
test('when there is no orderMap', () async {
final List<Order>? orders = firestoreService.generateOrders(orderMap);
expect(orders, isNull);
});

test('when there is non-null orderMap', () async {
orderMap = <String, String>{'createTimestamp': kQueryOrderDescending};
final List<Order>? orders = firestoreService.generateOrders(orderMap);
expect(orders!.length, 1);
final Order resultOrder = orders[0];
expect(resultOrder.direction, kQueryOrderDescending);
expect(resultOrder.field!.fieldPath, 'createTimestamp');
});
});
}
Loading

0 comments on commit 84be1e8

Please sign in to comment.