Skip to content

Commit 8459db5

Browse files
authored
Migrating pub_server: versions list API. (#3508)
* Migrating pub_server: versions list API. * code review updates
1 parent 7b33421 commit 8459db5

File tree

11 files changed

+117
-96
lines changed

11 files changed

+117
-96
lines changed

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

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

app/lib/frontend/handlers/pubapi.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class PubApi {
2929
/// Getting information about all versions of a package.
3030
/// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#list-all-versions-of-a-package
3131
@EndPoint.get('/api/packages/<package>')
32-
Future<Response> listPackageVersions(Request request, String package) async =>
33-
await packageBackend.pubServer
32+
Future<PackageData> packageData(Request request, String package) async =>
33+
await packageBackend.repository
3434
.listVersions(_replaceHost(request.requestedUri), package);
3535

3636
/// Getting information about a specific (package, version) pair.

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

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

app/lib/package/backend.dart

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -377,14 +377,29 @@ class GCloudPackageRepository extends pub_server.PackageRepository {
377377

378378
GCloudPackageRepository(this.db, this.storage);
379379

380-
// Metadata support.
381-
382-
@override
383-
Stream<pub_server.PackageVersion> versions(String package) {
384-
final packageKey = db.emptyKey.append(Package, id: package);
385-
final query = db.query<PackageVersion>(ancestorKey: packageKey);
386-
return query.run().map((model) => pub_server.PackageVersion(
387-
package, model.version, model.pubspec.jsonString));
380+
/// Returns the known versions of [package].
381+
///
382+
/// Used in `pub` client for finding which versions exist.
383+
Future<api.PackageData> listVersions(Uri baseUri, String package) async {
384+
return await cache.packageData(package).get(() async {
385+
final packageVersions = await packageBackend.versionsOfPackage(package);
386+
if (packageVersions.isEmpty) {
387+
throw NotFoundException.resource('package "$package"');
388+
}
389+
packageVersions
390+
.sort((a, b) => a.semanticVersion.compareTo(b.semanticVersion));
391+
final latest = packageVersions.lastWhere(
392+
(pv) => !pv.semanticVersion.isPreRelease,
393+
orElse: () => packageVersions.last,
394+
);
395+
return api.PackageData(
396+
name: package,
397+
latest: _toApiVersionInfo(baseUri, latest),
398+
versions: packageVersions
399+
.map((pv) => _toApiVersionInfo(baseUri, pv))
400+
.toList(),
401+
);
402+
});
388403
}
389404

390405
/// Lookup and return the API's version info object.
@@ -405,14 +420,17 @@ class GCloudPackageRepository extends pub_server.PackageRepository {
405420
throw NotFoundException.resource('version "$version"');
406421
}
407422

408-
return api.VersionInfo(
409-
version: pv.version,
410-
pubspec: pv.pubspec.asJson,
411-
archiveUrl:
412-
urls.pkgArchiveDownloadUrl(pv.package, pv.version, baseUri: baseUri),
413-
);
423+
return _toApiVersionInfo(baseUri, pv);
414424
}
415425

426+
api.VersionInfo _toApiVersionInfo(Uri baseUri, PackageVersion pv) =>
427+
api.VersionInfo(
428+
version: pv.version,
429+
pubspec: pv.pubspec.asJson,
430+
archiveUrl: urls.pkgArchiveDownloadUrl(pv.package, pv.version,
431+
baseUri: baseUri),
432+
);
433+
416434
// Download support.
417435

418436
@override

app/lib/package/pub_server/repository.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ class PackageVersion {
5050

5151
/// Represents a pub repository.
5252
abstract class PackageRepository {
53-
/// Returns the known versions of [package].
54-
Stream<PackageVersion> versions(String package);
55-
5653
/// Starts a upload.
5754
///
5855
/// The given [redirectUrl] instructs the uploading client to make a GET

app/lib/package/pub_server/shelf_pubserver.dart

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ import 'dart:convert' as convert;
99

1010
import 'package:logging/logging.dart';
1111
import 'package:shelf/shelf.dart' as shelf;
12-
import 'package:yaml/yaml.dart';
1312

1413
import '../../package/backend.dart' show purgePackageCache;
1514
import '../../shared/exceptions.dart';
1615
import '../../shared/redis_cache.dart' show cache;
17-
import '../../shared/urls.dart' as urls;
1816

1917
import 'repository.dart';
2018

@@ -27,51 +25,6 @@ class ShelfPubServer {
2725

2826
ShelfPubServer(this.repository);
2927

30-
// Metadata handlers.
31-
32-
Future<shelf.Response> listVersions(Uri uri, String package) async {
33-
final cachedBinaryJson = await cache.packageData(package).get();
34-
if (cachedBinaryJson != null) {
35-
return _binaryJsonResponse(cachedBinaryJson);
36-
}
37-
38-
final packageVersions = await repository.versions(package).toList();
39-
if (packageVersions.isEmpty) {
40-
return shelf.Response.notFound(null);
41-
}
42-
43-
packageVersions.sort((a, b) => a.version.compareTo(b.version));
44-
45-
// TODO: Add legacy entries (if necessary), such as version_url.
46-
Map packageVersion2Json(PackageVersion version) {
47-
return {
48-
'archive_url': urls.pkgArchiveDownloadUrl(
49-
version.packageName, version.versionString,
50-
baseUri: uri),
51-
'pubspec': loadYaml(version.pubspecYaml),
52-
'version': version.versionString,
53-
};
54-
}
55-
56-
var latestVersion = packageVersions.last;
57-
for (int i = packageVersions.length - 1; i >= 0; i--) {
58-
if (!packageVersions[i].version.isPreRelease) {
59-
latestVersion = packageVersions[i];
60-
break;
61-
}
62-
}
63-
64-
// TODO: The 'latest' is something we should get rid of, since it's
65-
// duplicated in 'versions'.
66-
final binaryJson = convert.json.encoder.fuse(convert.utf8.encoder).convert({
67-
'name': package,
68-
'latest': packageVersion2Json(latestVersion),
69-
'versions': packageVersions.map(packageVersion2Json).toList(),
70-
});
71-
await cache.packageData(package).set(binaryJson);
72-
return _binaryJsonResponse(binaryJson);
73-
}
74-
7528
// Upload async handlers.
7629

7730
Future<shelf.Response> finishUploadAsync(Uri uri) async {
@@ -102,11 +55,6 @@ class ShelfPubServer {
10255

10356
// Helper functions.
10457

105-
shelf.Response _binaryJsonResponse(List<int> d, {int status = 200}) =>
106-
shelf.Response(status,
107-
body: Stream.fromIterable([d]),
108-
headers: {'content-type': 'application/json'});
109-
11058
shelf.Response _jsonResponse(Map json, {int status = 200}) =>
11159
shelf.Response(status,
11260
body: convert.json.encode(json),

app/lib/shared/redis_cache.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import 'dart:async';
66
import 'dart:convert';
77
import 'dart:io';
88

9-
import 'package:neat_cache/neat_cache.dart';
10-
import 'package:gcloud/service_scope.dart' as ss;
119
import 'package:appengine/appengine.dart';
10+
import 'package:client_data/package_api.dart' show PackageData;
11+
import 'package:gcloud/service_scope.dart' as ss;
1212
import 'package:logging/logging.dart';
13+
import 'package:neat_cache/neat_cache.dart';
1314

1415
import '../account/models.dart' show LikeData, UserSessionData;
1516
import '../dartdoc/models.dart' show DartdocEntry, FileInfo;
@@ -113,9 +114,15 @@ class CachePatterns {
113114
.withTTL(Duration(minutes: 60))
114115
.withCodec(utf8)[publisherId];
115116

116-
Entry<List<int>> packageData(String package) => _cache
117-
.withPrefix('package-data')
118-
.withTTL(Duration(minutes: 10))['$package'];
117+
Entry<PackageData> packageData(String package) => _cache
118+
.withPrefix('api-package-data')
119+
.withTTL(Duration(minutes: 10))
120+
.withCodec(utf8)
121+
.withCodec(json)
122+
.withCodec(wrapAsCodec(
123+
encode: (PackageData pd) => pd.toJson(),
124+
decode: (d) => PackageData.fromJson(d as Map<String, dynamic>),
125+
))['$package'];
119126

120127
Entry<PackageView> packageView(String package) => _cache
121128
.withPrefix('package-view')

app/test/package/backend_test.dart

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -347,22 +347,23 @@ void main() {
347347
});
348348
});
349349

350-
group('GCloudRepository.versions', () {
350+
group('GCloudRepository.packageData', () {
351+
final baseUri = Uri.parse('https://pub.dev');
352+
351353
testWithServices('not found', () async {
352-
final versions =
353-
await packageBackend.repository.versions('non_hydrogen').toList();
354-
expect(versions, isEmpty);
354+
final rs =
355+
packageBackend.repository.listVersions(baseUri, 'non_hydrogen');
356+
await expectLater(rs, throwsA(isA<NotFoundException>()));
355357
});
356358

357359
testWithServices('found', () async {
358-
final versions =
359-
await packageBackend.repository.versions('hydrogen').toList();
360-
expect(versions, isNotEmpty);
361-
expect(versions, hasLength(13));
362-
expect(versions.first.packageName, 'hydrogen');
363-
expect(versions.first.versionString, '1.0.0');
364-
expect(versions.last.packageName, 'hydrogen');
365-
expect(versions.last.versionString, '2.0.8');
360+
final pd =
361+
await packageBackend.repository.listVersions(baseUri, 'hydrogen');
362+
expect(pd.versions, isNotEmpty);
363+
expect(pd.versions, hasLength(13));
364+
expect(pd.versions.first.version, '1.0.0');
365+
expect(pd.versions.last.version, '2.0.8');
366+
expect(pd.latest.version, '2.0.8');
366367
});
367368
});
368369

pkg/client_data/lib/package_api.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,42 @@ class Message {
8888
Map<String, dynamic> toJson() => _$MessageToJson(this);
8989
}
9090

91+
/// Used in `pub` client for finding which versions exist.
92+
/// (`listVersions` method in pubapi)
93+
@JsonSerializable()
94+
class PackageData {
95+
/// Package name.
96+
final String name;
97+
98+
/// This is merely a convenience property, because the [VersionInfo] for the
99+
/// latest version also exists in the [versions] list.
100+
///
101+
/// This is the latest that is NOT a pre-release, unless there only is
102+
/// pre-releases in the [versions] list.
103+
final VersionInfo latest;
104+
105+
/// The available versions, sorted by their semantic version number (ascending).
106+
final List<VersionInfo> versions;
107+
108+
PackageData({
109+
@required this.name,
110+
@required this.latest,
111+
@required this.versions,
112+
});
113+
114+
factory PackageData.fromJson(Map<String, dynamic> json) =>
115+
_$PackageDataFromJson(json);
116+
117+
Map<String, dynamic> toJson() => _$PackageDataToJson(this);
118+
}
119+
91120
@JsonSerializable()
92121
class VersionInfo {
93122
final String version;
94123

95124
final Map<String, dynamic> pubspec;
96125

126+
/// As of Dart 2.8 `pub` client uses [archiveUrl] to find the archive.
97127
@JsonKey(name: 'archive_url')
98128
final String archiveUrl;
99129

pkg/client_data/lib/package_api.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.

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

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

0 commit comments

Comments
 (0)