From ab40d730c346e861f720505f46e0987f16aa1f75 Mon Sep 17 00:00:00 2001
From: keyonghan <54558023+keyonghan@users.noreply.github.com>
Date: Mon, 15 Apr 2024 10:09:57 -0700
Subject: [PATCH] Add API `api/public/get-status-firestore` (#3635)
Part of https://github.com/flutter/flutter/issues/142951.
This PR adds api entry point for `api/public/get-status-firestore`. This is prepare frontend migration to Firestore.
This change is a no-op, as the frontend is still calling `api/public/get-status`.
---
app_dart/lib/cocoon_service.dart | 1 +
app_dart/lib/server.dart | 6 +
app_dart/lib/src/model/firestore/commit.dart | 24 ++
.../model/firestore/commit_tasks_status.dart | 35 +++
app_dart/lib/src/model/firestore/task.dart | 28 +++
.../get_status_firestore.dart | 85 +++++++
.../src/service/build_status_provider.dart | 18 +-
.../firestore/commit_tasks_status_test.dart | 32 +++
.../test/model/firestore/commit_test.dart | 15 ++
app_dart/test/model/firestore/task_test.dart | 17 ++
.../get_status_firestore_test.dart | 209 ++++++++++++++++++
.../service/fake_build_status_provider.dart | 8 +-
.../test/src/utilities/entity_generators.dart | 6 +
dashboard/lib/service/appengine_cocoon.dart | 4 +-
14 files changed, 467 insertions(+), 21 deletions(-)
create mode 100644 app_dart/lib/src/model/firestore/commit_tasks_status.dart
create mode 100644 app_dart/lib/src/request_handlers/get_status_firestore.dart
create mode 100644 app_dart/test/model/firestore/commit_tasks_status_test.dart
create mode 100644 app_dart/test/request_handlers/get_status_firestore_test.dart
diff --git a/app_dart/lib/cocoon_service.dart b/app_dart/lib/cocoon_service.dart
index b8b3dac2f..f050db2e2 100644
--- a/app_dart/lib/cocoon_service.dart
+++ b/app_dart/lib/cocoon_service.dart
@@ -14,6 +14,7 @@ export 'src/request_handlers/get_build_status_badge.dart';
export 'src/request_handlers/get_release_branches.dart';
export 'src/request_handlers/get_repos.dart';
export 'src/request_handlers/get_status.dart';
+export 'src/request_handlers/get_status_firestore.dart';
export 'src/request_handlers/get_green_commits.dart';
export 'src/request_handlers/github_rate_limit_status.dart';
export 'src/request_handlers/github_webhook.dart';
diff --git a/app_dart/lib/server.dart b/app_dart/lib/server.dart
index 5aa02cfb7..540a3e44b 100644
--- a/app_dart/lib/server.dart
+++ b/app_dart/lib/server.dart
@@ -231,6 +231,12 @@ Server createServer({
delegate: GetStatus(config: config),
),
+ '/api/public/get-status-firestore': CacheRequestHandler
(
+ cache: cache,
+ config: config,
+ delegate: GetStatusFirestore(config: config),
+ ),
+
'/api/public/get-green-commits': GetGreenCommits(config: config),
/// Record GitHub API quota usage in BigQuery.
diff --git a/app_dart/lib/src/model/firestore/commit.dart b/app_dart/lib/src/model/firestore/commit.dart
index a8411d0e5..ccf119a61 100644
--- a/app_dart/lib/src/model/firestore/commit.dart
+++ b/app_dart/lib/src/model/firestore/commit.dart
@@ -18,6 +18,16 @@ const String kCommitMessageField = 'message';
const String kCommitRepositoryPathField = 'repositoryPath';
const String kCommitShaField = 'sha';
+/// Commit Json keys.
+const String kCommitAvatar = 'Avatar';
+const String kCommitAuthor = 'Author';
+const String kCommitBranch = 'Branch';
+const String kCommitCreateTimestamp = 'CreateTimestamp';
+const String kCommitDocumentName = 'DocumentName';
+const String kCommitMessage = 'Message';
+const String kCommitRepositoryPath = 'RepositoryPath';
+const String kCommitSha = 'Sha';
+
class Commit extends Document {
/// Lookup [Commit] from Firestore.
///
@@ -71,6 +81,20 @@ class Commit extends Document {
/// [RepositorySlug] of where this commit exists.
RepositorySlug get slug => RepositorySlug.full(repositoryPath!);
+ @override
+ Map toJson() {
+ return {
+ kCommitDocumentName: name,
+ kCommitRepositoryPath: repositoryPath,
+ kCommitCreateTimestamp: createTimestamp,
+ kCommitSha: sha,
+ kCommitMessage: message,
+ kCommitAuthor: author,
+ kCommitAvatar: avatar,
+ kCommitBranch: branch,
+ };
+ }
+
@override
String toString() {
final StringBuffer buf = StringBuffer()
diff --git a/app_dart/lib/src/model/firestore/commit_tasks_status.dart b/app_dart/lib/src/model/firestore/commit_tasks_status.dart
new file mode 100644
index 000000000..c5bc651d9
--- /dev/null
+++ b/app_dart/lib/src/model/firestore/commit_tasks_status.dart
@@ -0,0 +1,35 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'commit.dart';
+import 'task.dart';
+
+/// Class that holds the status for all tasks corresponding to a particular
+/// commit.
+///
+/// Tasks may still be running, and thus their status is subject to change.
+/// Put another way, this class holds information that is a snapshot in time.
+class CommitTasksStatus {
+ /// Creates a new [CommitTasksStatus].
+ const CommitTasksStatus(this.commit, this.tasks);
+
+ /// The commit against which all the tasks are run.
+ final Commit commit;
+
+ /// Tasks running against the commit.
+ final List tasks;
+}
+
+class SerializableCommitTasksStatus {
+ const SerializableCommitTasksStatus(this.status);
+
+ final CommitTasksStatus status;
+
+ Map toJson() {
+ return {
+ 'Commit': status.commit.toJson(),
+ 'Tasks': status.tasks,
+ };
+ }
+}
diff --git a/app_dart/lib/src/model/firestore/task.dart b/app_dart/lib/src/model/firestore/task.dart
index b0abc6d0e..8af818b60 100644
--- a/app_dart/lib/src/model/firestore/task.dart
+++ b/app_dart/lib/src/model/firestore/task.dart
@@ -26,6 +26,18 @@ const String kTaskStartTimestampField = 'startTimestamp';
const String kTaskStatusField = 'status';
const String kTaskTestFlakyField = 'testFlaky';
+/// Task Json keys.
+const String kTaskAttempts = 'Attempts';
+const String kTaskBringup = 'Bringup';
+const String kTaskBuildNumber = 'BuildNumber';
+const String kTaskCreateTimestamp = 'CreateTimestamp';
+const String kTaskDocumentName = 'DocumentName';
+const String kTaskEndTimestamp = 'EndTimestamp';
+const String kTaskStartTimestamp = 'StartTimestamp';
+const String kTaskStatus = 'Status';
+const String kTaskTaskName = 'TaskName';
+const String kTaskTestFlaky = 'TestFlaky';
+
class Task extends Document {
/// Lookup [Task] from Firestore.
///
@@ -252,6 +264,22 @@ class Task extends Document {
return completedStatuses.contains(status);
}
+ @override
+ Map toJson() {
+ return {
+ kTaskDocumentName: name,
+ kTaskCreateTimestamp: createTimestamp,
+ kTaskStartTimestamp: startTimestamp,
+ kTaskEndTimestamp: endTimestamp,
+ kTaskTaskName: taskName,
+ kTaskAttempts: attempts,
+ kTaskBringup: bringup,
+ kTaskTestFlaky: testFlaky,
+ kTaskBuildNumber: buildNumber,
+ kTaskStatus: status,
+ };
+ }
+
@override
String toString() {
final StringBuffer buf = StringBuffer()
diff --git a/app_dart/lib/src/request_handlers/get_status_firestore.dart b/app_dart/lib/src/request_handlers/get_status_firestore.dart
new file mode 100644
index 000000000..1565f0369
--- /dev/null
+++ b/app_dart/lib/src/request_handlers/get_status_firestore.dart
@@ -0,0 +1,85 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:gcloud/db.dart';
+import 'package:github/github.dart';
+import 'package:meta/meta.dart';
+
+import '../model/appengine/commit.dart';
+import '../model/firestore/commit_tasks_status.dart';
+import '../model/appengine/key_helper.dart';
+import '../request_handling/body.dart';
+import '../request_handling/exceptions.dart';
+import '../request_handling/request_handler.dart';
+import '../service/build_status_provider.dart';
+import '../service/config.dart';
+import '../service/datastore.dart';
+import '../service/firestore.dart';
+
+@immutable
+class GetStatusFirestore extends RequestHandler {
+ const GetStatusFirestore({
+ required super.config,
+ @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
+ @visibleForTesting this.buildStatusProvider = BuildStatusService.defaultProvider,
+ });
+
+ final DatastoreServiceProvider datastoreProvider;
+ final BuildStatusServiceProvider buildStatusProvider;
+
+ static const String kLastCommitKeyParam = 'lastCommitKey';
+ static const String kBranchParam = 'branch';
+ static const String kRepoParam = 'repo';
+
+ @override
+ Future get() async {
+ final String? encodedLastCommitKey = request!.uri.queryParameters[kLastCommitKeyParam];
+ final String repoName = request!.uri.queryParameters[kRepoParam] ?? Config.flutterSlug.name;
+ final RepositorySlug slug = RepositorySlug('flutter', repoName);
+ final String branch = request!.uri.queryParameters[kBranchParam] ?? Config.defaultBranch(slug);
+ final DatastoreService datastore = datastoreProvider(config.db);
+ final FirestoreService firestoreService = await config.createFirestoreService();
+ final BuildStatusService buildStatusService = buildStatusProvider(datastore, firestoreService);
+ final KeyHelper keyHelper = config.keyHelper;
+ final int commitNumber = config.commitNumber;
+ final int lastCommitTimestamp = await _obtainTimestamp(encodedLastCommitKey, keyHelper, datastore);
+
+ final List statuses = await buildStatusService
+ .retrieveCommitStatusFirestore(
+ limit: commitNumber,
+ timestamp: lastCommitTimestamp,
+ branch: branch,
+ slug: slug,
+ )
+ .map(
+ (CommitTasksStatus status) => SerializableCommitTasksStatus(status),
+ )
+ .toList();
+
+ return Body.forJson({
+ 'Statuses': statuses,
+ });
+ }
+
+ Future _obtainTimestamp(String? encodedLastCommitKey, KeyHelper keyHelper, DatastoreService datastore) async {
+ /// [lastCommitTimestamp] is initially set as the current time, which allows query return
+ /// latest commit list. If [owerKeyParam] is not empty, [lastCommitTimestamp] will be set
+ /// as the timestamp of that [commit], and the query will return lastest commit
+ /// list whose timestamp is smaller than [lastCommitTimestamp].
+ int lastCommitTimestamp = DateTime.now().millisecondsSinceEpoch;
+
+ if (encodedLastCommitKey != null) {
+ final Key ownerKey = keyHelper.decode(encodedLastCommitKey) as Key;
+ final Commit commit = await datastore.db.lookupValue(
+ ownerKey,
+ orElse: () => throw NotFoundException('Failed to find commit with key $ownerKey'),
+ );
+
+ lastCommitTimestamp = commit.timestamp!;
+ }
+ return lastCommitTimestamp;
+ }
+}
diff --git a/app_dart/lib/src/service/build_status_provider.dart b/app_dart/lib/src/service/build_status_provider.dart
index cc5439d33..b4c4a4575 100644
--- a/app_dart/lib/src/service/build_status_provider.dart
+++ b/app_dart/lib/src/service/build_status_provider.dart
@@ -12,6 +12,7 @@ import '../model/appengine/commit.dart' as datastore;
import '../model/appengine/github_build_status_update.dart';
import '../model/appengine/stage.dart';
import '../model/firestore/commit.dart';
+import '../model/firestore/commit_tasks_status.dart';
import '../model/firestore/task.dart';
import 'datastore.dart';
@@ -174,23 +175,6 @@ class BuildStatusService {
}
}
-/// Class that holds the status for all tasks corresponding to a particular
-/// commit.
-///
-/// Tasks may still be running, and thus their status is subject to change.
-/// Put another way, this class holds information that is a snapshot in time.
-@immutable
-class CommitTasksStatus {
- /// Creates a new [CommitTasksStatus].
- const CommitTasksStatus(this.commit, this.tasks);
-
- /// The commit against which all the tasks are run.
- final Commit commit;
-
- /// Tasks running against the commit.
- final List tasks;
-}
-
@immutable
class CommitStatus {
/// Creates a new [CommitStatus].
diff --git a/app_dart/test/model/firestore/commit_tasks_status_test.dart b/app_dart/test/model/firestore/commit_tasks_status_test.dart
new file mode 100644
index 000000000..e72d8065a
--- /dev/null
+++ b/app_dart/test/model/firestore/commit_tasks_status_test.dart
@@ -0,0 +1,32 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:cocoon_service/src/model/firestore/commit.dart';
+import 'package:cocoon_service/src/model/firestore/commit_tasks_status.dart';
+import 'package:cocoon_service/src/model/firestore/task.dart';
+import 'package:test/test.dart';
+
+import '../../src/utilities/entity_generators.dart';
+
+void main() {
+ group('CommitTasksStatus', () {
+ test('generates json correctly', () async {
+ final Commit commit = generateFirestoreCommit(1, sha: 'sha1');
+ final CommitTasksStatus commitTasksStatus = CommitTasksStatus(commit, []);
+ expect(SerializableCommitTasksStatus(commitTasksStatus).toJson(), {
+ 'Commit': {
+ 'DocumentName': 'sha1',
+ 'RepositoryPath': 'flutter/flutter',
+ 'CreateTimestamp': 1,
+ 'Sha': 'sha1',
+ 'Message': 'test message',
+ 'Author': 'author',
+ 'Avatar': 'avatar',
+ 'Branch': 'master',
+ },
+ 'Tasks': [],
+ });
+ });
+ });
+}
diff --git a/app_dart/test/model/firestore/commit_test.dart b/app_dart/test/model/firestore/commit_test.dart
index 2734b54f2..5cf5730c2 100644
--- a/app_dart/test/model/firestore/commit_test.dart
+++ b/app_dart/test/model/firestore/commit_test.dart
@@ -51,4 +51,19 @@ void main() {
expect(commitDocument.fields![kCommitRepositoryPathField]!.stringValue, commit.repository);
expect(commitDocument.fields![kCommitShaField]!.stringValue, commit.sha);
});
+
+ test('commit toJson', () {
+ final Commit commitDocument = generateFirestoreCommit(1);
+ final Map expectedResult = {
+ kCommitDocumentName: commitDocument.name,
+ kCommitRepositoryPath: commitDocument.repositoryPath,
+ kCommitCreateTimestamp: commitDocument.createTimestamp,
+ kCommitSha: commitDocument.sha,
+ kCommitMessage: commitDocument.message,
+ kCommitAuthor: commitDocument.author,
+ kCommitAvatar: commitDocument.avatar,
+ kCommitBranch: commitDocument.branch,
+ };
+ expect(commitDocument.toJson(), expectedResult);
+ });
}
diff --git a/app_dart/test/model/firestore/task_test.dart b/app_dart/test/model/firestore/task_test.dart
index ab748e3bf..b4c778dde 100644
--- a/app_dart/test/model/firestore/task_test.dart
+++ b/app_dart/test/model/firestore/task_test.dart
@@ -202,4 +202,21 @@ void main() {
expect(task.testFlaky, false);
});
});
+
+ test('task toJson', () {
+ final Task taskDocument = generateFirestoreTask(1);
+ final Map expectedResult = {
+ kTaskDocumentName: taskDocument.name,
+ kTaskCreateTimestamp: taskDocument.createTimestamp,
+ kTaskStartTimestamp: taskDocument.startTimestamp,
+ kTaskEndTimestamp: taskDocument.endTimestamp,
+ kTaskTaskName: taskDocument.taskName,
+ kTaskAttempts: taskDocument.attempts,
+ kTaskBringup: taskDocument.bringup,
+ kTaskTestFlaky: taskDocument.testFlaky,
+ kTaskBuildNumber: taskDocument.buildNumber,
+ kTaskStatus: taskDocument.status,
+ };
+ expect(taskDocument.toJson(), expectedResult);
+ });
}
diff --git a/app_dart/test/request_handlers/get_status_firestore_test.dart b/app_dart/test/request_handlers/get_status_firestore_test.dart
new file mode 100644
index 000000000..b8bcef0d1
--- /dev/null
+++ b/app_dart/test/request_handlers/get_status_firestore_test.dart
@@ -0,0 +1,209 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:cocoon_service/cocoon_service.dart';
+import 'package:cocoon_service/src/model/appengine/commit.dart';
+import 'package:cocoon_service/src/model/firestore/commit_tasks_status.dart';
+import 'package:cocoon_service/src/model/firestore/task.dart';
+import 'package:cocoon_service/src/service/datastore.dart';
+import 'package:gcloud/db.dart';
+import 'package:test/test.dart';
+
+import '../src/datastore/fake_config.dart';
+import '../src/request_handling/fake_authentication.dart';
+import '../src/request_handling/fake_http.dart';
+import '../src/request_handling/request_handler_tester.dart';
+import '../src/service/fake_build_status_provider.dart';
+import '../src/utilities/entity_generators.dart';
+import '../src/utilities/mocks.dart';
+
+void main() {
+ group('GetStatusFirestore', () {
+ late FakeConfig config;
+ FakeClientContext clientContext;
+ FakeKeyHelper keyHelper;
+ FakeBuildStatusService buildStatusService;
+ late RequestHandlerTester tester;
+ late GetStatusFirestore handler;
+ late MockFirestoreService mockFirestoreService;
+
+ late Commit commit1;
+ late Commit commit2;
+
+ Future decodeHandlerBody() async {
+ final Body body = await tester.get(handler);
+ return await utf8.decoder.bind(body.serialize() as Stream>).transform(json.decoder).single as T?;
+ }
+
+ setUp(() {
+ clientContext = FakeClientContext();
+ mockFirestoreService = MockFirestoreService();
+ keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext);
+ tester = RequestHandlerTester();
+ config = FakeConfig(keyHelperValue: keyHelper, firestoreService: mockFirestoreService);
+ buildStatusService = FakeBuildStatusService(commitTasksStatuses: []);
+ handler = GetStatusFirestore(
+ config: config,
+ datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
+ buildStatusProvider: (_, __) => buildStatusService,
+ );
+ commit1 = Commit(
+ key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'),
+ repository: 'flutter/flutter',
+ sha: 'ea28a9c34dc701de891eaf74503ca4717019f829',
+ timestamp: 3,
+ message: 'test message 1',
+ branch: 'master',
+ );
+ commit2 = Commit(
+ key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'),
+ repository: 'flutter/flutter',
+ sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ timestamp: 1,
+ message: 'test message 2',
+ branch: 'master',
+ );
+ });
+
+ test('no statuses', () async {
+ final Map result = (await decodeHandlerBody())!;
+ expect(result['Statuses'], isEmpty);
+ });
+
+ test('reports statuses without input commit key', () async {
+ buildStatusService = FakeBuildStatusService(
+ commitTasksStatuses: [
+ CommitTasksStatus(generateFirestoreCommit(1), const []),
+ CommitTasksStatus(generateFirestoreCommit(2), const []),
+ ],
+ );
+ handler = GetStatusFirestore(
+ config: config,
+ datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
+ buildStatusProvider: (_, __) => buildStatusService,
+ );
+
+ final Map result = (await decodeHandlerBody())!;
+
+ expect(result['Statuses'].length, 2);
+ });
+
+ test('reports statuses with input commit key', () async {
+ final Commit commit1 = Commit(
+ key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'),
+ repository: 'flutter/flutter',
+ sha: 'ea28a9c34dc701de891eaf74503ca4717019f829',
+ timestamp: 3,
+ message: 'test message 1',
+ branch: 'master',
+ );
+ final Commit commit2 = Commit(
+ key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'),
+ repository: 'flutter/flutter',
+ sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ timestamp: 1,
+ message: 'test message 2',
+ branch: 'master',
+ );
+ config.db.values[commit1.key] = commit1;
+ config.db.values[commit2.key] = commit2;
+ buildStatusService = FakeBuildStatusService(
+ commitTasksStatuses: [
+ CommitTasksStatus(
+ generateFirestoreCommit(
+ 1,
+ sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ ),
+ const [],
+ ),
+ CommitTasksStatus(generateFirestoreCommit(2), const []),
+ ],
+ );
+ handler = GetStatusFirestore(
+ config: config,
+ datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
+ buildStatusProvider: (_, __) => buildStatusService,
+ );
+
+ const String expectedLastCommitKeyEncoded =
+ 'ahNzfmZsdXR0ZXItZGFzaGJvYXJkckcLEglDaGVja2xpc3QiOGZsdXR0ZXIvZmx1dHRlci9lYTI4YTljMzRkYzcwMWRlODkxZWFmNzQ1MDNjYTQ3MTcwMTlmODI5DA';
+
+ tester.request = FakeHttpRequest(
+ queryParametersValue: {
+ GetStatusFirestore.kLastCommitKeyParam: expectedLastCommitKeyEncoded,
+ },
+ );
+ final Map result = (await decodeHandlerBody())!;
+
+ expect(result['Statuses'].first, {
+ 'Commit': {
+ 'DocumentName': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ 'RepositoryPath': 'flutter/flutter',
+ 'CreateTimestamp': 1,
+ 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ 'Message': 'test message',
+ 'Author': 'author',
+ 'Avatar': 'avatar',
+ 'Branch': 'master',
+ },
+ 'Tasks': [],
+ });
+ });
+
+ test('reports statuses with input branch', () async {
+ commit2.branch = 'flutter-1.1-candidate.1';
+ config.db.values[commit1.key] = commit1;
+ config.db.values[commit2.key] = commit2;
+ buildStatusService = FakeBuildStatusService(
+ commitTasksStatuses: [
+ CommitTasksStatus(
+ generateFirestoreCommit(1),
+ const [],
+ ),
+ CommitTasksStatus(
+ generateFirestoreCommit(
+ 2,
+ branch: 'flutter-1.1-candidate.1',
+ sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ ),
+ const [],
+ ),
+ ],
+ );
+ handler = GetStatusFirestore(
+ config: config,
+ datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
+ buildStatusProvider: (_, __) => buildStatusService,
+ );
+
+ const String branch = 'flutter-1.1-candidate.1';
+
+ expect(config.db.values.length, 2);
+
+ tester.request = FakeHttpRequest(
+ queryParametersValue: {
+ GetStatusFirestore.kBranchParam: branch,
+ },
+ );
+ final Map result = (await decodeHandlerBody())!;
+
+ expect(result['Statuses'].length, 1);
+ expect(result['Statuses'].first, {
+ 'Commit': {
+ 'DocumentName': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ 'RepositoryPath': 'flutter/flutter',
+ 'CreateTimestamp': 2,
+ 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
+ 'Message': 'test message',
+ 'Author': 'author',
+ 'Avatar': 'avatar',
+ 'Branch': 'flutter-1.1-candidate.1',
+ },
+ 'Tasks': [],
+ });
+ });
+ });
+}
diff --git a/app_dart/test/src/service/fake_build_status_provider.dart b/app_dart/test/src/service/fake_build_status_provider.dart
index dfe8a201f..37ef6a190 100644
--- a/app_dart/test/src/service/fake_build_status_provider.dart
+++ b/app_dart/test/src/service/fake_build_status_provider.dart
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'package:cocoon_service/src/model/firestore/commit_tasks_status.dart';
import 'package:cocoon_service/src/service/build_status_provider.dart';
import 'package:cocoon_service/src/service/datastore.dart';
import 'package:cocoon_service/src/service/firestore.dart';
@@ -11,6 +12,7 @@ class FakeBuildStatusService implements BuildStatusService {
FakeBuildStatusService({
this.cumulativeStatus,
this.commitStatuses,
+ this.commitTasksStatuses,
});
BuildStatus? cumulativeStatus;
@@ -55,10 +57,12 @@ class FakeBuildStatusService implements BuildStatusService {
String? branch,
required RepositorySlug slug,
}) {
- if (commitStatuses == null) {
+ if (commitTasksStatuses == null) {
throw AssertionError();
}
- commitStatuses!.sort((CommitStatus a, CommitStatus b) => a.commit.timestamp!.compareTo(b.commit.timestamp!));
+ commitTasksStatuses!.sort(
+ (CommitTasksStatus a, CommitTasksStatus b) => a.commit.createTimestamp!.compareTo(b.commit.createTimestamp!),
+ );
return Stream.fromIterable(
commitTasksStatuses!.where(
diff --git a/app_dart/test/src/utilities/entity_generators.dart b/app_dart/test/src/utilities/entity_generators.dart
index 1be5d9bd8..7b9eab83a 100644
--- a/app_dart/test/src/utilities/entity_generators.dart
+++ b/app_dart/test/src/utilities/entity_generators.dart
@@ -139,6 +139,9 @@ firestore_commit.Commit generateFirestoreCommit(
String? owner = 'flutter',
String repo = 'flutter',
int? createTimestamp,
+ String? message = 'test message',
+ String? author = 'author',
+ String? avatar = 'avatar',
}) {
final firestore_commit.Commit commit = firestore_commit.Commit()
..name = sha ?? '$i'
@@ -146,6 +149,9 @@ firestore_commit.Commit generateFirestoreCommit(
firestore_commit.kCommitCreateTimestampField: Value(integerValue: (createTimestamp ?? i).toString()),
firestore_commit.kCommitRepositoryPathField: Value(stringValue: '$owner/$repo'),
firestore_commit.kCommitBranchField: Value(stringValue: branch),
+ firestore_commit.kCommitMessageField: Value(stringValue: message),
+ firestore_commit.kCommitAuthorField: Value(stringValue: author),
+ firestore_commit.kCommitAvatarField: Value(stringValue: avatar),
firestore_commit.kCommitShaField: Value(stringValue: sha ?? '$i'),
};
return commit;
diff --git a/dashboard/lib/service/appengine_cocoon.dart b/dashboard/lib/service/appengine_cocoon.dart
index 2119846df..1189b980f 100644
--- a/dashboard/lib/service/appengine_cocoon.dart
+++ b/dashboard/lib/service/appengine_cocoon.dart
@@ -56,7 +56,7 @@ class AppEngineCocoonService implements CocoonService {
static const String kTaskEndTimestamp = 'EndTimestamp';
static const String kTaskStartTimestamp = 'StartTimestamp';
static const String kTaskStatus = 'Status';
- static const String kTaskTaskNmae = 'TaskName';
+ static const String kTaskTaskName = 'TaskName';
static const String kTaskTestFlaky = 'TestFlaky';
final http.Client _client;
@@ -407,7 +407,7 @@ class AppEngineCocoonService implements CocoonService {
..startTimestamp = Int64(taskData[kTaskStartTimestamp] as int)
..endTimestamp = Int64(taskData[kTaskEndTimestamp] as int)
..documentName = taskData[kTaskDocumentName] as String
- ..taskName = taskData[kTaskTaskNmae] as String
+ ..taskName = taskData[kTaskTaskName] as String
..attempts = taskData[kTaskAttempts] as int
..bringup = taskData[kTaskBringup] as bool
..status = taskData[kTaskStatus] as String