Skip to content

Commit ca14aca

Browse files
authored
Updated admin removal for package versions: using the flag on the action (#8788)
1 parent edba727 commit ca14aca

File tree

6 files changed

+334
-224
lines changed

6 files changed

+334
-224
lines changed

app/lib/admin/actions/moderate_package_versions.dart

Lines changed: 112 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -38,110 +38,134 @@ Set the moderated flag on a package version (updating the flag and the timestamp
3838
final caseId = options['case'];
3939

4040
final package = options['package'];
41-
InvalidInputException.check(
42-
package != null && package.isNotEmpty,
43-
'package must be given',
44-
);
4541
final version = options['version'];
46-
InvalidInputException.check(
47-
version != null && version.isNotEmpty,
48-
'version must be given',
49-
);
50-
5142
final state = options['state'];
52-
bool? valueToSet;
53-
switch (state) {
54-
case 'true':
55-
valueToSet = true;
56-
break;
57-
case 'false':
58-
valueToSet = false;
59-
break;
60-
}
61-
6243
final note = options['note'];
6344

6445
final refCase =
6546
await adminBackend.loadAndVerifyModerationCaseForAdminAction(caseId);
6647

67-
final p = await packageBackend.lookupPackage(package!);
68-
if (p == null) {
69-
throw NotFoundException.resource(package);
70-
}
71-
final pv = await packageBackend.lookupPackageVersion(package, version!);
72-
if (pv == null) {
73-
throw NotFoundException.resource('$package $version');
74-
}
75-
76-
PackageVersion? pv2;
77-
if (valueToSet != null) {
78-
final currentDartSdk = await getCachedDartSdkVersion(
79-
lastKnownStable: toolStableDartSdkVersion);
80-
final currentFlutterSdk = await getCachedFlutterSdkVersion(
81-
lastKnownStable: toolStableFlutterSdkVersion);
82-
pv2 = await withRetryTransaction(dbService, (tx) async {
83-
final v = await tx.lookupValue<PackageVersion>(pv.key);
84-
v.updateIsModerated(isModerated: valueToSet!);
85-
tx.insert(v);
86-
87-
// Update references to latest versions.
88-
final pkg = await tx.lookupValue<Package>(p.key);
89-
final versions = await tx.query<PackageVersion>(pkg.key).run().toList();
90-
pkg.updateVersions(
91-
versions,
92-
dartSdkVersion: currentDartSdk.semanticVersion,
93-
flutterSdkVersion: currentFlutterSdk.semanticVersion,
94-
replaced: v,
95-
);
96-
if (pkg.latestVersionKey == null) {
97-
throw InvalidInputException('xx');
98-
}
99-
pkg.updated = clock.now().toUtc();
100-
tx.insert(pkg);
48+
return await adminMarkPackageVersionVisibility(
49+
package,
50+
version,
51+
state: state,
52+
whenUpdating: (tx, v, valueToSet) async {
53+
v.updateIsModerated(isModerated: valueToSet);
10154

10255
if (refCase != null) {
10356
final mc = await tx.lookupValue<ModerationCase>(refCase.key);
10457
mc.addActionLogEntry(
105-
ModerationSubject.package(package, version).fqn,
58+
ModerationSubject.package(package!, version!).fqn,
10659
valueToSet ? ModerationAction.apply : ModerationAction.revert,
10760
note,
10861
);
10962
tx.insert(mc);
11063
}
111-
112-
return v;
113-
});
114-
115-
// make sure visibility cache is updated immediately
116-
await purgePackageCache(package);
117-
118-
// sync exported API(s)
119-
await apiExporter.synchronizePackage(package, forceDelete: true);
120-
121-
// retract or re-populate public archive files
122-
await packageBackend.tarballStorage.updatePublicArchiveBucket(
123-
package: package,
124-
ageCheckThreshold: Duration.zero,
125-
deleteIfOlder: Duration.zero,
126-
);
127-
128-
await taskBackend.trackPackage(package);
129-
await purgePackageCache(package);
130-
await purgeScorecardData(package, version, isLatest: true);
131-
}
132-
133-
return {
134-
'package': p.name,
135-
'version': pv.version,
136-
'before': {
137-
'isModerated': pv.isModerated,
138-
'moderatedAt': pv.moderatedAt?.toIso8601String(),
13964
},
140-
if (pv2 != null)
141-
'after': {
142-
'isModerated': pv2.isModerated,
143-
'moderatedAt': pv2.moderatedAt?.toIso8601String(),
144-
},
145-
};
65+
valueFn: (v) => {
66+
'isModerated': v.isModerated,
67+
'moderatedAt': v.moderatedAt?.toIso8601String(),
68+
},
69+
);
14670
},
14771
);
72+
73+
/// Changes the moderated or the admin-deleted flag and timestamp on a [package] [version].
74+
Future<Map<String, dynamic>> adminMarkPackageVersionVisibility(
75+
String? package,
76+
String? version, {
77+
/// `true`, `false` or `null`
78+
required String? state,
79+
80+
/// The updates to apply during the transaction.
81+
required Future<void> Function(
82+
TransactionWrapper tx,
83+
PackageVersion v,
84+
bool valueToSet,
85+
) whenUpdating,
86+
87+
/// The debug information to return.
88+
required Map Function(PackageVersion v) valueFn,
89+
}) async {
90+
InvalidInputException.check(
91+
package != null && package.isNotEmpty,
92+
'package must be given',
93+
);
94+
InvalidInputException.check(
95+
version != null && version.isNotEmpty,
96+
'version must be given',
97+
);
98+
99+
bool? valueToSet;
100+
switch (state) {
101+
case 'true':
102+
valueToSet = true;
103+
break;
104+
case 'false':
105+
valueToSet = false;
106+
break;
107+
}
108+
109+
final p = await packageBackend.lookupPackage(package!);
110+
if (p == null) {
111+
throw NotFoundException.resource(package);
112+
}
113+
final pv = await packageBackend.lookupPackageVersion(package, version!);
114+
if (pv == null) {
115+
throw NotFoundException.resource('$package $version');
116+
}
117+
118+
PackageVersion? pv2;
119+
if (valueToSet != null) {
120+
final currentDartSdk = await getCachedDartSdkVersion(
121+
lastKnownStable: toolStableDartSdkVersion);
122+
final currentFlutterSdk = await getCachedFlutterSdkVersion(
123+
lastKnownStable: toolStableFlutterSdkVersion);
124+
pv2 = await withRetryTransaction(dbService, (tx) async {
125+
final v = await tx.lookupValue<PackageVersion>(pv.key);
126+
await whenUpdating(tx, v, valueToSet!);
127+
tx.insert(v);
128+
129+
// Update references to latest versions.
130+
final pkg = await tx.lookupValue<Package>(p.key);
131+
final versions = await tx.query<PackageVersion>(pkg.key).run().toList();
132+
pkg.updateVersions(
133+
versions,
134+
dartSdkVersion: currentDartSdk.semanticVersion,
135+
flutterSdkVersion: currentFlutterSdk.semanticVersion,
136+
replaced: v,
137+
);
138+
if (pkg.latestVersionKey == null) {
139+
throw InvalidInputException('xx');
140+
}
141+
pkg.updated = clock.now().toUtc();
142+
tx.insert(pkg);
143+
144+
return v;
145+
});
146+
147+
// make sure visibility cache is updated immediately
148+
await purgePackageCache(package);
149+
150+
// sync exported API(s)
151+
await apiExporter.synchronizePackage(package, forceDelete: true);
152+
153+
// retract or re-populate public archive files
154+
await packageBackend.tarballStorage.updatePublicArchiveBucket(
155+
package: package,
156+
ageCheckThreshold: Duration.zero,
157+
deleteIfOlder: Duration.zero,
158+
);
159+
160+
await taskBackend.trackPackage(package);
161+
await purgePackageCache(package);
162+
await purgeScorecardData(package, version, isLatest: true);
163+
}
164+
165+
return {
166+
'package': p.name,
167+
'version': pv.version,
168+
'before': valueFn(pv),
169+
if (pv2 != null) 'after': valueFn(pv2),
170+
};
171+
}

app/lib/admin/actions/package_version_delete.dart

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,38 @@
44

55
import '../../account/backend.dart';
66
import '../../shared/configuration.dart';
7-
import '../backend.dart';
87
import 'actions.dart';
8+
import 'moderate_package_versions.dart';
99

1010
final packageVersionDelete = AdminAction(
11-
name: 'package-version-delete',
12-
options: {
13-
'package': 'name of package to delete',
14-
'version': 'version of package',
15-
},
16-
summary: 'Deletes package <package> version <version>.',
17-
description: '''
18-
Deletes package <package> version <version>.
19-
20-
Deletes all associated resources:
21-
22-
* PackageVersions
23-
* PackageVersionAsset
24-
* archives (might be retrievable from backup)
25-
26-
The package version will be "tombstoned" and same version cannot be published
27-
later.
11+
name: 'package-version-delete',
12+
summary:
13+
'Set the admin-deleted flag on a package version (making it not visible).',
14+
description: '''
15+
Set the admin-deleted flag on a package version (updating the flag and the timestamp). After 2 months it will be fully deleted.
2816
''',
29-
invoke: (args) async {
30-
await requireAuthenticatedAdmin(AdminPermission.removePackage);
31-
final packageName = args['package'];
32-
if (packageName == null) {
33-
throw InvalidInputException('Missing `package` argument');
34-
}
35-
final version = args['version'];
36-
if (version == null) {
37-
throw InvalidInputException('Missing `version` argument');
38-
}
39-
final result =
40-
await adminBackend.removePackageVersion(packageName, version);
41-
42-
return {
43-
'message': 'Package version and all associated resources deleted.',
44-
'package': packageName,
45-
'version': version,
46-
'deletedPackageVersions': result.deletedPackageVersions,
47-
'deletedPackageVersionInfos': result.deletedPackageVersionInfos,
48-
'deletedPackageVersionAssets': result.deletedPackageVersionAssets,
49-
};
50-
});
17+
options: {
18+
'package': 'The package name to be deleted',
19+
'version': 'The version to be deleted',
20+
'state':
21+
'Set admin-deleted state true / false. Returns current state if omitted.',
22+
},
23+
invoke: (args) async {
24+
await requireAuthenticatedAdmin(AdminPermission.removePackage);
25+
final package = args['package'];
26+
final version = args['version'];
27+
final state = args['state'];
28+
return await adminMarkPackageVersionVisibility(
29+
package,
30+
version,
31+
state: state,
32+
whenUpdating: (tx, v, valueToSet) async {
33+
v.updateIsAdminDeleted(isAdminDeleted: valueToSet);
34+
},
35+
valueFn: (v) => {
36+
'isAdminDeleted': v.isAdminDeleted,
37+
'adminDeletedAt': v.adminDeletedAt?.toIso8601String(),
38+
},
39+
);
40+
},
41+
);

0 commit comments

Comments
 (0)