Skip to content

Commit 4df76f5

Browse files
authored
Exporting the per-package RSS/atom feed with the rest of exported APIs. (dart-lang#8707)
1 parent 19625e0 commit 4df76f5

File tree

17 files changed

+93
-18
lines changed

17 files changed

+93
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Important changes to data models, configuration, and migrations between each
22
AppEngine version, listed here to ease deployment and troubleshooting.
33

44
## Next Release (replace with git tag when deployed)
5+
* Note: after the release we should update the load balancer rules to also include the `/api/packages/<package>/feed.atom` URLs.
56

67
## `20250403t085600-all`
78
* Bump runtimeVersion to `2025.04.01`.

app/lib/admin/actions/moderate_package.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ Note: the action may take a longer time to complete as the public archive bucket
7979
return pkg;
8080
});
8181

82+
// make sure visibility cache is updated immediately
83+
await purgePackageCache(package);
84+
8285
// sync exported API(s)
8386
await apiExporter?.synchronizePackage(package, forceDelete: true);
8487

app/lib/admin/actions/moderate_package_versions.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ Set the moderated flag on a package version (updating the flag and the timestamp
112112
return v;
113113
});
114114

115+
// make sure visibility cache is updated immediately
116+
await purgePackageCache(package);
117+
115118
// sync exported API(s)
116119
await apiExporter?.synchronizePackage(package, forceDelete: true);
117120

app/lib/frontend/handlers/atom_feed.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Future<shelf.Response> allPackagesAtomFeedhandler(shelf.Request request) async {
3232
);
3333
}
3434

35-
/// Handles requests for `/packages/<package>/feed.atom`
35+
/// Handles requests for `/api/packages/<package>/feed.atom`
3636
Future<shelf.Response> packageAtomFeedhandler(
3737
shelf.Request request,
3838
String package,

app/lib/frontend/handlers/pubapi.client.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ class PubApiClient {
132132
));
133133
}
134134

135+
Future<List<int>> packageAtomFeed(String package) async {
136+
return await _client.requestBytes(
137+
verb: 'get',
138+
path: '/api/packages/$package/feed.atom',
139+
);
140+
}
141+
135142
Future<_i5.PublisherInfo> createPublisher(String publisherId) async {
136143
return _i5.PublisherInfo.fromJson(await _client.requestJson(
137144
verb: 'post',

app/lib/frontend/handlers/pubapi.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:_pub_shared/data/package_api.dart';
99
import 'package:_pub_shared/data/publisher_api.dart';
1010
import 'package:_pub_shared/data/task_api.dart';
1111
import 'package:api_builder/api_builder.dart';
12+
import 'package:pub_dev/frontend/handlers/atom_feed.dart';
1213
import 'package:shelf/shelf.dart';
1314
import 'package:shelf_router/shelf_router.dart';
1415

@@ -215,6 +216,11 @@ class PubApi {
215216
) async =>
216217
await packageBackend.inviteUploader(package, invite);
217218

219+
/// Renders the Atom XML feed for the package.
220+
@EndPoint.get('/api/packages/<package>/feed.atom')
221+
Future<Response> packageAtomFeed(Request request, String package) =>
222+
packageAtomFeedhandler(request, package);
223+
218224
// ****
219225
// **** Publisher API
220226
// ****

app/lib/frontend/handlers/pubapi.g.dart

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/frontend/handlers/routes.dart

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,6 @@ class PubSiteService {
176176
Future<Response> packageVersions(Request request, String package) =>
177177
packageVersionsListHandler(request, package);
178178

179-
/// Renders the Atom XML feed for the package.
180-
@Route.get('/packages/<package>/feed.atom')
181-
Future<Response> packageAtomFeed(Request request, String package) =>
182-
packageAtomFeedhandler(request, package);
183-
184179
@Route.get('/packages/<package>')
185180
Future<Response> package(Request request, String package) =>
186181
packageVersionHandlerHtml(request, package);

app/lib/frontend/handlers/routes.g.dart

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/package/api_export/api_exporter.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@ final class ApiExporter {
244244
versionListing,
245245
forceWrite: forceWrite,
246246
);
247+
await _api.package(package).feedAtomFile.write(
248+
await buildPackageAtomFeedContent(package),
249+
forceWrite: forceWrite,
250+
);
247251
}
248252

249253
/// Scan for updates from packages until [abort] is resolved, or [claim]

app/lib/package/api_export/exported_api.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,13 @@ final class ExportedPackage {
273273
ExportedJsonFile<ListAdvisoriesResponse> get advisories =>
274274
_suffix<ListAdvisoriesResponse>('/advisories');
275275

276+
/// Interface for writing `/api/packages/<package>/feed.atom`
277+
ExportedAtomFeedFile get feedAtomFile => ExportedAtomFeedFile._(
278+
_owner,
279+
'/api/packages/$_package/feed.atom',
280+
Duration(hours: 12),
281+
);
282+
276283
/// Interace for writing `/api/archives/<package>-<version>.tar.gz`.
277284
ExportedBlob tarball(String version) => ExportedBlob._(
278285
_owner,
@@ -399,6 +406,7 @@ final class ExportedPackage {
399406
await Future.wait([
400407
_owner._pool.withResource(() async => await versions.delete()),
401408
_owner._pool.withResource(() async => await advisories.delete()),
409+
_owner._pool.withResource(() async => await feedAtomFile.delete()),
402410
..._owner._prefixes.map((prefix) async {
403411
await _owner._listBucket(
404412
prefix: prefix + '/api/archives/$_package-',

app/lib/shared/urls.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ String pkgArchiveDownloadUrl(String package, String version, {Uri? baseUri}) {
135135
}
136136

137137
String pkgFeedUrl(String package) {
138-
return '/packages/$package/feed.atom';
138+
return '/api/packages/$package/feed.atom';
139139
}
140140

141141
String pkgDocUrl(

app/test/admin/exported_api_sync_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ void main() {
8282
'$runtimeVersion/api/archives/oxygen-2.0.0-dev.tar.gz',
8383
'$runtimeVersion/api/packages/oxygen',
8484
'$runtimeVersion/api/packages/oxygen/advisories',
85+
'$runtimeVersion/api/packages/oxygen/feed.atom',
8586
'latest/api/archives/oxygen-1.0.0.tar.gz',
8687
'latest/api/archives/oxygen-1.2.0.tar.gz',
8788
'latest/api/archives/oxygen-2.0.0-dev.tar.gz',
8889
'latest/api/packages/oxygen',
8990
'latest/api/packages/oxygen/advisories',
91+
'latest/api/packages/oxygen/feed.atom',
9092
});
9193

9294
final oxygenDataJson = data['latest/api/packages/oxygen'];

app/test/frontend/handlers/atom_feed_test.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ void main() {
7777
});
7878
});
7979

80-
testWithProfile('/packages/<package>/feed.atom', fn: () async {
80+
testWithProfile('/api/packages/<package>/feed.atom', fn: () async {
8181
final content = await expectAtomXmlResponse(
82-
await issueGet('/packages/oxygen/feed.atom'));
82+
await issueGet('/api/packages/oxygen/feed.atom'));
8383
// check if content is valid XML
8484
final root = XmlDocument.parse(content);
8585
final feed = root.rootElement;
@@ -110,11 +110,11 @@ void main() {
110110
entries.forEach((e) => e.parent!.children.remove(e));
111111

112112
final restExp = RegExp('<feed xmlns="http://www.w3.org/2005/Atom">\n'
113-
' <id>${activeConfiguration.primarySiteUri}/packages/oxygen/feed.atom</id>\n'
113+
' <id>${activeConfiguration.primarySiteUri}/api/packages/oxygen/feed.atom</id>\n'
114114
' <title>Recently published versions of package oxygen on pub.dev</title>\n'
115115
' <updated>(.*)</updated>\n'
116116
' <link href="${activeConfiguration.primarySiteUri}/packages/oxygen" rel="alternate"/>\n'
117-
' <link href="${activeConfiguration.primarySiteUri}/packages/oxygen/feed.atom" rel="self"/>\n'
117+
' <link href="${activeConfiguration.primarySiteUri}/api/packages/oxygen/feed.atom" rel="self"/>\n'
118118
' <generator version="0.1.0">Pub Feed Generator</generator>\n'
119119
' <subtitle>oxygen is awesome</subtitle>\n'
120120
'(\\s*)'

app/test/package/api_export/api_exporter_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ Future<void> _testExportedApiSynchronization(
131131
await bucket.readBytes('$runtimeVersion/api/archives/foo-1.0.0.tar.gz'),
132132
isNotNull,
133133
);
134+
expect(
135+
await bucket.readBytes('$runtimeVersion/api/packages/foo/feed.atom'),
136+
isNotNull,
137+
);
134138
expect(
135139
await bucket.readGzippedJson('$runtimeVersion/api/packages/foo'),
136140
{
@@ -152,6 +156,10 @@ Future<void> _testExportedApiSynchronization(
152156
await bucket.readString('$runtimeVersion/feed.atom'),
153157
contains('v1.0.0 of foo'),
154158
);
159+
expect(
160+
await bucket.readString('$runtimeVersion/api/packages/foo/feed.atom'),
161+
contains('v1.0.0 of foo'),
162+
);
155163
}
156164

157165
_log.info('## New package');
@@ -185,6 +193,10 @@ Future<void> _testExportedApiSynchronization(
185193
await bucket.readString('latest/feed.atom'),
186194
contains('v1.0.0 of foo'),
187195
);
196+
expect(
197+
await bucket.readString('latest/api/packages/foo/feed.atom'),
198+
contains('v1.0.0 of foo'),
199+
);
188200
// Note. that name completion data won't be updated until search caches
189201
// are purged, so we won't test that it is updated.
190202

@@ -205,6 +217,10 @@ Future<void> _testExportedApiSynchronization(
205217
await bucket.readString('latest/feed.atom'),
206218
contains('v2.0.0 of bar'),
207219
);
220+
expect(
221+
await bucket.readString('latest/api/packages/bar/feed.atom'),
222+
contains('v2.0.0 of bar'),
223+
);
208224
}
209225

210226
_log.info('## New package version');
@@ -247,6 +263,10 @@ Future<void> _testExportedApiSynchronization(
247263
await bucket.readString('$runtimeVersion/feed.atom'),
248264
contains('v3.0.0 of bar'),
249265
);
266+
expect(
267+
await bucket.readString('latest/api/packages/bar/feed.atom'),
268+
contains('v3.0.0 of bar'),
269+
);
250270
}
251271

252272
_log.info('## Discontinued flipped on');
@@ -424,6 +444,10 @@ Future<void> _testExportedApiSynchronization(
424444
await bucket.readGzippedJson('latest/api/packages/bar'),
425445
isNull,
426446
);
447+
expect(
448+
await bucket.readGzippedJson('latest/api/packages/feed.atom'),
449+
isNull,
450+
);
427451
expect(
428452
await bucket.readBytes('latest/api/archives/bar-2.0.0.tar.gz'),
429453
isNull,

pkg/_pub_shared/lib/src/pubapi.client.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ class PubApiClient {
132132
));
133133
}
134134

135+
Future<List<int>> packageAtomFeed(String package) async {
136+
return await _client.requestBytes(
137+
verb: 'get',
138+
path: '/api/packages/$package/feed.atom',
139+
);
140+
}
141+
135142
Future<_i5.PublisherInfo> createPublisher(String publisherId) async {
136143
return _i5.PublisherInfo.fromJson(await _client.requestJson(
137144
verb: 'post',

pkg/pub_integration/lib/script/public_pages.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ class PublicPagesScript {
7373
'Pub Feed Generator',
7474
);
7575
_contains(
76-
await _pubClient.getContent('/packages/retry/feed.atom'),
77-
'/packages/retry/feed.atom',
76+
await _pubClient.getContent('/api/packages/retry/feed.atom'),
77+
'/api/packages/retry/feed.atom',
7878
);
7979
}
8080

0 commit comments

Comments
 (0)