Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions app/lib/frontend/handlers/pubapi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,9 @@ class PubApi {
@EndPoint.get('/api/packages/versions/newUploadFinish/<uploadId>')
Future<SuccessMessage> finishPackageUpload(
Request request, String uploadId) async {
final pv = await packageBackend.publishUploadedBlob(uploadId);
final messages = await packageBackend.publishUploadedBlob(uploadId);
return SuccessMessage(
success: Message(
message: 'Successfully uploaded '
'${urls.pkgPageUrl(pv.package, includeHost: true)} '
'version ${pv.version}, '
'it may take up-to 10 minutes before the new version is available.',
),
success: Message(message: messages.join('\n')),
);
}

Expand Down
55 changes: 38 additions & 17 deletions app/lib/package/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -882,8 +882,9 @@ class PackageBackend {
);
}

/// Finishes the upload of a package.
Future<PackageVersion> publishUploadedBlob(String guid) async {
/// Finishes the upload of a package and returns the list of messages
/// related to the publishing.
Future<List<String>> publishUploadedBlob(String guid) async {
final restriction = await getUploadRestrictionStatus();
if (restriction == UploadRestrictionStatus.noUploads) {
throw PackageRejectedException.uploadRestricted();
Expand Down Expand Up @@ -984,7 +985,7 @@ class PackageBackend {
sw.reset();
final entities = await _createUploadEntities(db, agent, archive,
sha256Hash: sha256Hash);
final version = await _performTarballUpload(
final (version, uploadMessages) = await _performTarballUpload(
entities: entities,
agent: agent,
archive: archive,
Expand All @@ -998,7 +999,13 @@ class PackageBackend {
sw.reset();
await _incomingBucket.deleteWithRetry(tmpObjectName(guid));
_logger.info('Temporary object removed in ${sw.elapsed}.');
return version;
return [
'Successfully uploaded '
'${urls.pkgPageUrl(version.package, includeHost: true)} '
'version ${version.version}, '
'it may take up-to 10 minutes before the new version is available.',
...uploadMessages,
];
});
}

Expand Down Expand Up @@ -1057,14 +1064,15 @@ class PackageBackend {
}
}

Future<PackageVersion> _performTarballUpload({
Future<(PackageVersion, List<String>)> _performTarballUpload({
required _UploadEntities entities,
required AuthenticatedAgent agent,
required PackageSummary archive,
required String guid,
required bool hasCanonicalArchiveObject,
}) async {
final sw = Stopwatch()..start();
final uploadMessages = <String>[];
final newVersion = entities.packageVersion;
final [currentDartSdk, currentFlutterSdk] = await Future.wait([
getCachedDartSdkVersion(lastKnownStable: toolStableDartSdkVersion),
Expand Down Expand Up @@ -1100,14 +1108,6 @@ class PackageBackend {
package: newVersion.package,
isNew: isNew,
);
final email = createPackageUploadedEmail(
packageName: newVersion.package,
packageVersion: newVersion.version!,
displayId: agent.displayId,
authorizedUploaders:
uploaderEmails.map((email) => EmailAddress(email)).toList(),
);
final outgoingEmail = emailBackend.prepareEntity(email);
Package? package;
final existingVersions = await db
.query<PackageVersion>(ancestorKey: newVersion.packageKey!)
Expand All @@ -1116,7 +1116,7 @@ class PackageBackend {

// Add the new package to the repository by storing the tarball and
// inserting metadata to datastore (which happens atomically).
final pv = await withRetryTransaction(db, (tx) async {
final (pv, outgoingEmail) = await withRetryTransaction(db, (tx) async {
_logger.info('Starting datastore transaction.');

final tuple = (await tx.lookup([
Expand Down Expand Up @@ -1156,10 +1156,21 @@ class PackageBackend {

final maxVersionCount = maxVersionsPerPackageOverrides[package!.name] ??
maxVersionsPerPackage;
if (package!.versionCount >= maxVersionCount) {
final remainingVersionCount = maxVersionCount - package!.versionCount;
if (remainingVersionCount <= 0) {
throw PackageRejectedException.maxVersionCountReached(
newVersion.package, maxVersionCount);
}
if (remainingVersionCount <= 100) {
// We need to decrease the remaining version count as the newly uploaded
// version is not yet in it.
final limitAfterUpload = remainingVersionCount - 1;
final s = limitAfterUpload == 1 ? '' : 's';
uploadMessages.add(
'The package "${package!.name!}" has $limitAfterUpload version$s left '
'before reaching the limit of $maxVersionCount. '
'Please contact [email protected]');
}

if (package!.deletedVersions != null &&
package!.deletedVersions!.contains(newVersion.version!)) {
Expand Down Expand Up @@ -1196,6 +1207,16 @@ class PackageBackend {
await tarballStorage.copyArchiveFromCanonicalToPublicBucket(
newVersion.package, newVersion.version!);

final email = createPackageUploadedEmail(
packageName: newVersion.package,
packageVersion: newVersion.version!,
displayId: agent.displayId,
authorizedUploaders:
uploaderEmails.map((email) => EmailAddress(email)).toList(),
uploadMessages: uploadMessages,
);
final outgoingEmail = emailBackend.prepareEntity(email);

final inserts = <Model>[
package!,
newVersion,
Expand All @@ -1221,7 +1242,7 @@ class PackageBackend {

_logger.info('Trying to commit datastore changes.');
tx.queueMutations(inserts: inserts);
return newVersion;
return (newVersion, outgoingEmail);
});
_logger.info('Upload successful. [package-uploaded]');
_logger.info('Upload transaction completed in ${sw.elapsed}.');
Expand All @@ -1237,7 +1258,7 @@ class PackageBackend {
.addAsyncFn(() => _postUploadTasks(package, newVersion, outgoingEmail));

_logger.info('Post-upload tasks completed in ${sw.elapsed}.');
return pv;
return (pv, uploadMessages);
}

/// The post-upload tasks are not critical and could fail without any impact on
Expand Down
22 changes: 11 additions & 11 deletions app/lib/service/email/email_templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -204,21 +204,21 @@ EmailMessage createPackageUploadedEmail({
required String packageVersion,
required String displayId,
required List<EmailAddress> authorizedUploaders,
required List<String> uploadMessages,
}) {
final url =
pkgPageUrl(packageName, version: packageVersion, includeHost: true);
final subject = 'Package uploaded: $packageName $packageVersion';
final bodyText = '''Dear package maintainer,

$displayId has published a new version ($packageVersion) of the $packageName package to the Dart package site ($primaryHost).

For details, go to $url

${_footer('package')}
''';

return EmailMessage(
_notificationsFrom, authorizedUploaders, subject, bodyText);
final paragraphs = [
'Dear package maintainer,',
'$displayId has published a new version ($packageVersion) of the $packageName package to the Dart package site ($primaryHost).',
'For details, go to $url',
...uploadMessages,
_footer('package'),
];

return EmailMessage(_notificationsFrom, authorizedUploaders, subject,
paragraphs.join('\n\n'));
}

/// Creates the [EmailMessage] that will be sent to users about new invitations
Expand Down
25 changes: 24 additions & 1 deletion app/test/package/upload_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,30 @@ void main() {
],
),
fn: () async {
packageBackend.maxVersionsPerPackage = 100;
packageBackend.maxVersionsPerPackage = 102;

final tarball101 = await packageArchiveBytes(
pubspecContent: generatePubspecYaml('busy_pkg', '1.0.101'));
final rs101 = await createPubApiClient(authToken: adminClientToken)
.uploadPackageBytes(tarball101);
expect(
rs101.success.message,
contains(
'The package "busy_pkg" has 1 version left before reaching the limit of 102. '
'Please contact [email protected]'));

final tarball102 = await packageArchiveBytes(
pubspecContent: generatePubspecYaml('busy_pkg', '1.0.102'));
final rs102 = await createPubApiClient(authToken: adminClientToken)
.uploadPackageBytes(tarball102);
expect(
rs102.success.message,
contains(
'The package "busy_pkg" has 0 versions left before reaching the limit of 102. '
'Please contact [email protected]'));
expect(fakeEmailSender.sentMessages.last.bodyText,
contains('has 0 versions left before reaching the limit'));

final tarball = await packageArchiveBytes(
pubspecContent: generatePubspecYaml('busy_pkg', '2.0.0'));
final rs = createPubApiClient(authToken: adminClientToken)
Expand Down
1 change: 1 addition & 0 deletions app/test/service/email/email_templates_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ void main() {
EmailAddress(name: 'Joe', '[email protected]'),
EmailAddress('[email protected]')
],
uploadMessages: [],
);
expect(message.from.toString(), contains('@pub.dev'));
expect(message.recipients.map((e) => e.toString()).toList(), [
Expand Down
Loading