| // 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, |
| }; |
| } |
| } |