Skip to content

Commit 8eb3dce

Browse files
committed
Cleanup ModerationCase entities after 3 years.
1 parent d6555db commit 8eb3dce

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

app/lib/admin/backend.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,34 @@ class AdminBackend {
779779
return refCase;
780780
}
781781

782+
/// Scans datastore and deletes [ModerationCase] entities where the last
783+
/// action was more than 3 years ago.
784+
Future<void> deleteModerationCases({
785+
@visibleForTesting DateTime? before,
786+
}) async {
787+
before ??= clock.ago(days: 3 * 366).toUtc(); // extra buffer days
788+
789+
/// Querying the cases that were opened before the threshold,
790+
/// as the resolved timestamp may be null for ongoing cases.
791+
final query = _db.query<ModerationCase>()
792+
..filter('opened <', before)
793+
..order('opened');
794+
await for (final mc in query.run()) {
795+
// sanity check that both timestamps are before the threshold
796+
if (mc.opened.isAfter(before)) {
797+
continue;
798+
}
799+
final resolved = mc.resolved;
800+
if (resolved == null || resolved.isAfter(before)) {
801+
continue;
802+
}
803+
// delete the entity
804+
_logger.info('Deleting ModerationCase: ${mc.caseId}');
805+
await _db.commit(deletes: [mc.key]);
806+
_logger.info('Deleted ModerationCase: ${mc.caseId}');
807+
}
808+
}
809+
782810
/// Scans datastore and deletes moderated subjects where the last action
783811
/// was more than 3 years ago.
784812
Future<void> deleteModeratedSubjects({

app/lib/tool/neat_task/pub_dev_tasks.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ List<NeatPeriodicTaskScheduler> createPeriodicTaskSchedulers({
143143
task: () async => adminBackend.deleteAdminDeletedEntities(),
144144
),
145145

146+
// Deletes ModerationCase entities.
147+
_weekly(
148+
name: 'delete-moderation-cases',
149+
isRuntimeVersioned: false,
150+
task: () async => adminBackend.deleteModerationCases(),
151+
),
152+
146153
// Deletes moderated packages, versions, publishers and users.
147154
_weekly(
148155
name: 'delete-moderated-subjects',

app/test/admin/moderation_case_resolve_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:_pub_shared/data/account_api.dart';
66
import 'package:_pub_shared/data/admin_api.dart';
7+
import 'package:clock/clock.dart';
78
import 'package:pub_dev/admin/backend.dart';
89
import 'package:pub_dev/admin/models.dart';
910
import 'package:pub_dev/shared/datastore.dart';
@@ -71,6 +72,15 @@ void main() {
7172
return mc!.status!;
7273
}
7374

75+
Future<void> _verifyCaseExistence(String caseId, bool exists) async {
76+
final mc = await adminBackend.lookupModerationCase(caseId);
77+
if (exists) {
78+
expect(mc, isNotNull);
79+
} else {
80+
expect(mc, isNull);
81+
}
82+
}
83+
7484
testWithProfile('notification: no action', fn: () async {
7585
final mc = await _prepare(apply: null);
7686
expect(await _close(mc.caseId), 'no-action');
@@ -82,13 +92,25 @@ void main() {
8292
'SHOUT Deleting object from public bucket: "packages/oxygen-2.0.0-dev.tar.gz".',
8393
], fn: () async {
8494
final mc = await _prepare(apply: true);
95+
96+
// cleanup doesn't remove case prematurely
97+
await _verifyCaseExistence(mc.caseId, true);
98+
await adminBackend.deleteModerationCases(before: clock.now().toUtc());
99+
await _verifyCaseExistence(mc.caseId, true);
100+
101+
// close case
85102
expect(
86103
await _close(
87104
mc.caseId,
88105
reason: 'The package violated our policy.',
89106
),
90107
'moderation-applied',
91108
);
109+
110+
// cleanup does remove case after the threshold is reached
111+
await _verifyCaseExistence(mc.caseId, true);
112+
await adminBackend.deleteModerationCases(before: clock.now().toUtc());
113+
await _verifyCaseExistence(mc.caseId, false);
92114
});
93115

94116
testWithProfile('appeal no action: revert', expectedLogMessages: [

0 commit comments

Comments
 (0)