blob: 1578a689bcc6073726e4664dd149888c3fba2ea1 [file] [log] [blame]
// Copyright 2023 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:auto_submit/request_handling/pubsub.dart';
import 'package:auto_submit/requests/check_request.dart';
import 'package:auto_submit/requests/github_pull_request_event.dart';
import 'package:auto_submit/service/approver_service.dart';
import 'package:auto_submit/service/log.dart';
import 'package:auto_submit/service/revert_request_validation_service.dart';
import 'package:github/github.dart';
import 'package:googleapis/pubsub/v1.dart' as pub;
import 'package:shelf/shelf.dart';
/// Handler for processing pull requests with 'revert' label.
///
/// For pull requests where an 'revert' label was added in pubsub,
/// check if the revert request is mergable.
class CheckRevertRequest extends CheckRequest {
const CheckRevertRequest({
required super.config,
required super.cronAuthProvider,
super.approverProvider = ApproverService.defaultProvider,
super.pubsub = const PubSub(),
});
@override
Future<Response> get() async {
/// Currently this is unused and cannot be called.
return process(
config.pubsubRevertRequestSubscription,
config.kPubsubPullNumber,
config.kPullMesssageBatchSize,
);
}
/// Process pull request messages from Pubsub.
Future<Response> process(
String pubSubSubscription,
int pubSubPulls,
int pubSubBatchSize,
) async {
final Set<int> processingLog = <int>{};
final List<pub.ReceivedMessage> messageList = await pullMessages(
pubSubSubscription,
pubSubPulls,
pubSubBatchSize,
);
if (messageList.isEmpty) {
log.info('No messages are pulled.');
return Response.ok('No messages are pulled.');
}
log.info('Processing ${messageList.length} messages');
final RevertRequestValidationService validationService = RevertRequestValidationService(config);
final List<Future<void>> futures = <Future<void>>[];
for (pub.ReceivedMessage message in messageList) {
log.info(message.toJson());
assert(message.message != null);
assert(message.message!.data != null);
final String messageData = message.message!.data!;
final Map<String, dynamic> rawBody =
json.decode(String.fromCharCodes(base64.decode(messageData))) as Map<String, dynamic>;
final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent.fromJson(rawBody);
final PullRequest pullRequest = githubPullRequestEvent.pullRequest!;
log.info('Processing message ackId: ${message.ackId}');
log.info('Processing mesageId: ${message.message!.messageId}');
log.info('Processing PR: $rawBody');
if (processingLog.contains(pullRequest.number) || githubPullRequestEvent.action != 'labeled') {
// Ack duplicate.
log.info('Ack the duplicated message : ${message.ackId!}.');
log.info('duplicate pull request #${pullRequest.number}');
await pubsub.acknowledge(pubSubSubscription, message.ackId!);
continue;
} else {
// Use the auto approval as we do not want to allow non bot reverts to
// be processed throught the service.
log.info('new pull request #${pullRequest.number}');
if (pullRequest.labels!.any((element) => element.name == 'revert of')) {
final ApproverService approver = approverProvider(config);
log.info('Checking auto approval of "revert of" pull request: $rawBody');
await approver.autoApproval(pullRequest);
} else {
// These should be closed requests that do not need to be reviewed.
log.info('Processing "revert" request : ${pullRequest.number}.');
}
processingLog.add(pullRequest.number!);
}
futures.add(
validationService.processMessage(githubPullRequestEvent, message.ackId!, pubsub),
);
}
await Future.wait(futures);
return Response.ok('Finished processing changes');
}
}