blob: 6dc1847d912dcc46c56375a486735d66ceb54d45 [file] [log] [blame]
// 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:collection/collection.dart';
import 'package:gcloud/db.dart';
import 'package:meta/meta.dart';
import '../datastore/cocoon_config.dart';
import '../model/appengine/agent.dart';
import '../model/appengine/commit.dart';
import '../model/appengine/key_helper.dart';
import '../request_handling/body.dart';
import '../request_handling/request_handler.dart';
import '../service/build_status_provider.dart';
import '../service/datastore.dart';
@immutable
class GetStatus extends RequestHandler<Body> {
const GetStatus(
Config config, {
@visibleForTesting
this.datastoreProvider = DatastoreService.defaultProvider,
@visibleForTesting BuildStatusServiceProvider buildStatusProvider,
}) : buildStatusProvider =
buildStatusProvider ?? BuildStatusService.defaultProvider,
super(config: config);
final DatastoreServiceProvider datastoreProvider;
final BuildStatusServiceProvider buildStatusProvider;
static const String lastCommitKeyParam = 'lastCommitKey';
static const String branchParam = 'branch';
@override
Future<Body> get() async {
final String encodedLastCommitKey =
request.uri.queryParameters[lastCommitKeyParam];
final String branch = request.uri.queryParameters[branchParam] ?? 'master';
final DatastoreService datastore = datastoreProvider(config.db);
final BuildStatusService buildStatusService =
buildStatusProvider(datastore);
final KeyHelper keyHelper = config.keyHelper;
final int commitNumber = config.commitNumber;
final int lastCommitTimestamp =
await _obtainTimestamp(encodedLastCommitKey, keyHelper, datastore);
final List<SerializableCommitStatus> statuses = await buildStatusService
.retrieveCommitStatus(
limit: commitNumber, timestamp: lastCommitTimestamp, branch: branch)
.map<SerializableCommitStatus>((CommitStatus status) =>
SerializableCommitStatus(
status, keyHelper.encode(status.commit.key)))
.toList();
final Query<Agent> agentQuery = datastore.db.query<Agent>()
..order('agentId');
final List<Agent> agents =
await agentQuery.run().where(_isVisible).toList();
agents.sort((Agent a, Agent b) =>
compareAsciiLowerCaseNatural(a.agentId, b.agentId));
return Body.forJson(<String, dynamic>{
'Statuses': statuses,
'AgentStatuses': agents,
});
}
Future<int> _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);
final Commit commit =
await datastore.db.lookupValue<Commit>(ownerKey, orElse: () => null);
lastCommitTimestamp = commit?.timestamp;
}
return lastCommitTimestamp;
}
static bool _isVisible(Agent agent) => !agent.isHidden;
}
/// The serialized representation of a [CommitStatus].
// TODO(tvolkert): Directly serialize [CommitStatus] once frontends migrate to new format.
class SerializableCommitStatus {
const SerializableCommitStatus(this.status, this.key);
final CommitStatus status;
final String key;
Map<String, dynamic> toJson() {
return <String, dynamic>{
'Checklist': <String, dynamic>{
'Key': key,
'Checklist': SerializableCommit(status.commit).facade,
},
'Stages': status.stages,
};
}
}