Skip to content

Commit 21674fd

Browse files
authored
Reserve the prefix name range when a ReservedPackage.name ends with _ (#8967)
1 parent 105fdd6 commit 21674fd

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

app/lib/admin/actions/package_reservation_create.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:pub_dev/package/backend.dart';
66
import 'package:pub_dev/package/models.dart';
77
import 'package:pub_dev/shared/datastore.dart';
8+
import 'package:pub_dev/shared/redis_cache.dart';
89

910
import 'actions.dart';
1011

@@ -52,6 +53,8 @@ able to claim it.
5253
return entry;
5354
});
5455

56+
await cache.reservedPackagePrefixes().purge();
57+
5558
return {
5659
'ReservedPackage': {
5760
'name': entry.name,

app/lib/admin/actions/package_reservation_delete.dart

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

55
import 'package:pub_dev/package/backend.dart';
66
import 'package:pub_dev/shared/datastore.dart';
7+
import 'package:pub_dev/shared/redis_cache.dart';
78

89
import 'actions.dart';
910

@@ -27,6 +28,7 @@ Deletes a ReservedPackage entity, allowing the package name use by any user.
2728
}
2829

2930
await dbService.commit(deletes: [rp.key]);
31+
await cache.reservedPackagePrefixes().purge();
3032

3133
return {
3234
'ReservedPackage': {

app/lib/package/backend.dart

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,30 @@ class PackageBackend {
198198
return await db.lookupOrNull<ReservedPackage>(packageKey);
199199
}
200200

201+
/// Lists the currently reserved package names. A reserved package prefix is a regular name
202+
/// that ends with `_` (underscore).
203+
///
204+
/// List the prefixes in descending length for easier matching.
205+
Future<List<String>> listReservedPackagePrefixes() async {
206+
return await cache.reservedPackagePrefixes().get(() async {
207+
final list = <String>[];
208+
await for (final p in db.query<ReservedPackage>().run()) {
209+
final name = p.name;
210+
if (name != null && name.endsWith('_')) {
211+
list.add(name);
212+
}
213+
}
214+
list.sort((a, b) {
215+
if (a.length != b.length) {
216+
return -a.length.compareTo(b.length);
217+
}
218+
return a.compareTo(b);
219+
});
220+
return list;
221+
})
222+
as List<String>;
223+
}
224+
201225
/// Looks up a package by name.
202226
Future<List<Package>> lookupPackages(Iterable<String> packageNames) async {
203227
return (await db.lookup(
@@ -1191,7 +1215,18 @@ class PackageBackend {
11911215
required String name,
11921216
required AuthenticatedAgent agent,
11931217
}) async {
1194-
final reservedPackage = await lookupReservedPackage(name);
1218+
// Apply either the exact-name reserved package lookup, or the closest prefix (ending with '_').
1219+
var reservedPackage = await lookupReservedPackage(name);
1220+
if (reservedPackage == null) {
1221+
// lookup prefixes
1222+
final prefixes = await listReservedPackagePrefixes();
1223+
for (final prefix in prefixes) {
1224+
if (name.startsWith(prefix)) {
1225+
reservedPackage = await lookupReservedPackage(prefix);
1226+
break;
1227+
}
1228+
}
1229+
}
11951230

11961231
bool isAllowedUser = false;
11971232
if (agent is AuthenticatedUser) {

app/lib/shared/redis_cache.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,17 @@ class CachePatterns {
500500
PlaylistItemListResponse.fromJson(v as Map<String, dynamic>),
501501
),
502502
)[pageToken];
503+
504+
Entry<List<String>> reservedPackagePrefixes() => _cache
505+
.withPrefix('reserved-package-prefixes/')
506+
.withTTL(Duration(hours: 1))
507+
.withCodec(utf8)
508+
.withCodec(
509+
wrapAsCodec(
510+
encode: (List<String> v) => json.encode(v),
511+
decode: (v) => (json.decode(v) as List).cast<String>(),
512+
),
513+
)[''];
503514
}
504515

505516
/// The active cache.

app/test/admin/package_reservation_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ void main() {
134134
},
135135
);
136136

137+
testWithProfile(
138+
'prefix reserve',
139+
fn: () async {
140+
await _reserve('pkg_');
141+
142+
final pubspecContent = generatePubspecYaml('pkg_foo', '1.0.0');
143+
final bytes = await packageArchiveBytes(pubspecContent: pubspecContent);
144+
await expectApiException(
145+
createPubApiClient(
146+
authToken: userClientToken,
147+
).uploadPackageBytes(bytes),
148+
code: 'PackageRejected',
149+
status: 400,
150+
message: 'Package name pkg_foo is reserved.',
151+
);
152+
},
153+
);
154+
137155
testWithProfile(
138156
'list and delete',
139157
fn: () async {

0 commit comments

Comments
 (0)