blob: b529b2bf263d3c8b6bc997d2e1406f153633d917 [file] [log] [blame]
// Copyright 2020 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/appengine/agent.dart';
import 'package:cocoon_service/src/model/appengine/commit.dart';
import 'package:cocoon_service/src/model/appengine/stage.dart';
import 'package:cocoon_service/src/model/appengine/task.dart';
import 'package:cocoon_service/src/service/datastore.dart';
import 'package:meta/meta.dart';
/// Function signature for a [TaskService] provider.
typedef TaskServiceProvider = TaskService Function(
DatastoreService datastoreService);
class TaskService {
TaskService(
this.datastore,
) : assert(datastore != null);
/// The backing datastore. Guaranteed to be non-null.
final DatastoreService datastore;
/// Creates and returns a [TaskService] using [datastore].
static TaskService defaultProvider(DatastoreService datastore) {
return TaskService(datastore);
}
Future<FullTask> findNextTask(Agent agent) async {
await for (Commit commit in datastore.queryRecentCommitsNoBranch()) {
final List<Stage> stages =
await datastore.queryTasksGroupedByStage(commit);
for (Stage stage in stages) {
if (!stage.isManagedByDeviceLab) {
continue;
}
for (Task task in List<Task>.from(stage.tasks)..sort(Task.byAttempts)) {
if (task.requiredCapabilities.isEmpty) {
throw InvalidTaskException(
'Task ${task.name} has no required capabilities');
}
if (task.status == Task.statusNew &&
agent.isCapableOfPerformingTask(task)) {
return FullTask(task, commit);
}
}
}
}
return null;
}
}
@visibleForTesting
class InvalidTaskException implements Exception {
const InvalidTaskException(this.message);
final String message;
@override
String toString() => message;
}