blob: b7c148b504039fb1d56ed07177f999042a126e05 [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:appengine/appengine.dart';
import 'package:collection/collection.dart';
import 'package:gcloud/db.dart';
import 'package:googleapis/bigquery/v2.dart';
import 'package:meta/meta.dart';
import '../datastore/cocoon_config.dart';
import '../foundation/providers.dart';
import '../foundation/typedefs.dart';
import '../model/appengine/agent.dart';
import '../request_handling/api_request_handler.dart';
import '../request_handling/authentication.dart';
import '../request_handling/body.dart';
import '../service/datastore.dart';
const Duration maxHealthCheckAge = Duration(minutes: 10);
@immutable
class UpdateAgentHealthHistory extends ApiRequestHandler<Body> {
const UpdateAgentHealthHistory(
Config config,
AuthenticationProvider authenticationProvider, {
@visibleForTesting
this.datastoreProvider = DatastoreService.defaultProvider,
@visibleForTesting LoggingProvider loggingProvider,
}) : loggingProvider = loggingProvider ?? Providers.serviceScopeLogger,
super(config: config, authenticationProvider: authenticationProvider);
final DatastoreServiceProvider datastoreProvider;
final LoggingProvider loggingProvider;
@override
Future<Body> get() async {
// Define const variables for [BigQuery] operations.
const String projectId = 'flutter-dashboard';
const String dataset = 'cocoon';
const String table = 'Agent';
final Logging log = loggingProvider();
final TabledataResourceApi tabledataResourceApi =
await config.createTabledataResourceApi();
final DatastoreService datastore = datastoreProvider(config.db);
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));
final List<Map<String, Object>> tableDataInsertAllRequestRows =
<Map<String, Object>>[];
final int healthCheckTimestamp = DateTime.now().millisecondsSinceEpoch;
for (Agent agent in agents) {
final bool isHealthy = _isAgentHealthy(agent);
/// Consolidate [agents] together.
///
/// Prepare for bigquery [insertAll].
tableDataInsertAllRequestRows.add(<String, Object>{
'json': <String, Object>{
'Timestamp': healthCheckTimestamp,
'AgentID': agent.agentId,
// TODO(keyonghan): add more detailed statuses https://github.com/flutter/flutter/issues/44213
'Status': isHealthy ? 'healthy' : 'unhealthy',
'Detail': isHealthy ? agent.healthDetails : 'out of date',
},
});
}
/// Prepare final [rows] to be inserted to `BigQuery`.
final TableDataInsertAllRequest rows = TableDataInsertAllRequest.fromJson(
<String, Object>{'rows': tableDataInsertAllRequestRows});
/// Insert [agents] to `BigQuery`.
try {
await tabledataResourceApi.insertAll(rows, projectId, dataset, table);
log.info(
'Succeeded to insert ${tableDataInsertAllRequestRows.length} rows to $projectId-$dataset-$table');
} on ApiRequestError {
log.error('Failed to add commits to BigQuery: $ApiRequestError');
}
return Body.forJson(<String, dynamic>{
'AgentStatuses': agents,
});
}
bool _isVisible(Agent agent) => !agent.isHidden;
bool _isAgentHealthy(Agent agent) {
return agent.isHealthy &&
agent.healthCheckTimestamp != null &&
DateTime.now().difference(DateTime.fromMillisecondsSinceEpoch(
agent.healthCheckTimestamp)) <
maxHealthCheckAge;
}
}