Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/NuGet/NuGetGallery into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenba committed Apr 4, 2016
2 parents b99cddb + 900b0c8 commit 095c7df
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 8 deletions.
43 changes: 37 additions & 6 deletions src/NuGetGallery/Controllers/ApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Web.Mvc;
using System.Web.UI;
using Newtonsoft.Json.Linq;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Versioning;
using NuGetGallery.Configuration;
Expand All @@ -36,6 +37,7 @@ public partial class ApiController
public IIndexingService IndexingService { get; set; }
public IAutomaticallyCuratePackageCommand AutoCuratePackage { get; set; }
public IStatusService StatusService { get; set; }
public IMessageService MessageService { get; set; }

protected ApiController()
{
Expand All @@ -52,6 +54,7 @@ public ApiController(
ISearchService searchService,
IAutomaticallyCuratePackageCommand autoCuratePackage,
IStatusService statusService,
IMessageService messageService,
IAppConfiguration config)
{
EntitiesContext = entitiesContext;
Expand All @@ -65,6 +68,7 @@ public ApiController(
SearchService = searchService;
AutoCuratePackage = autoCuratePackage;
StatusService = statusService;
MessageService = messageService;
_config = config;
}

Expand All @@ -80,8 +84,9 @@ public ApiController(
IAutomaticallyCuratePackageCommand autoCuratePackage,
IStatusService statusService,
IStatisticsService statisticsService,
IMessageService messageService,
IAppConfiguration config)
: this(entitiesContext, packageService, packageFileService, userService, nugetExeDownloaderService, contentService, indexingService, searchService, autoCuratePackage, statusService, config)
: this(entitiesContext, packageService, packageFileService, userService, nugetExeDownloaderService, contentService, indexingService, searchService, autoCuratePackage, statusService, messageService, config)
{
StatisticsService = statisticsService;
}
Expand Down Expand Up @@ -247,7 +252,8 @@ private async Task<ActionResult> CreatePackageInternal()
{
if (!packageRegistration.IsOwner(user))
{
return new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized);
return new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden,
Strings.ApiKeyNotAuthorized);
}

// Check if a particular Id-Version combination already exists. We eventually need to remove this check.
Expand Down Expand Up @@ -275,7 +281,10 @@ private async Task<ActionResult> CreatePackageInternal()
Size = packageStream.Length,
};

var package = await PackageService.CreatePackageAsync(packageToPush, packageStreamMetadata, user, commitChanges: false);
var package =
await
PackageService.CreatePackageAsync(packageToPush, packageStreamMetadata, user,
commitChanges: false);
await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges: false);
await EntitiesContext.SaveChangesAsync();

Expand All @@ -286,18 +295,40 @@ private async Task<ActionResult> CreatePackageInternal()
IndexingService.UpdatePackage(package);
}

MessageService.SendPackageAddedNotice(package,
Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme),
Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme),
Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme));

return new HttpStatusCodeResult(HttpStatusCode.Created);
}
}
catch (InvalidPackageException ex)
{
return BadRequestForExceptionMessage(ex);
}
catch (InvalidDataException ex)
{
return new HttpStatusCodeWithBodyResult(
HttpStatusCode.BadRequest,
string.Format(CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidPackage, ex.Message));
return BadRequestForExceptionMessage(ex);
}
catch (EntityException ex)
{
return BadRequestForExceptionMessage(ex);
}
catch (FrameworkException ex)
{
return BadRequestForExceptionMessage(ex);
}
}
}

private static ActionResult BadRequestForExceptionMessage(Exception ex)
{
return new HttpStatusCodeWithBodyResult(
HttpStatusCode.BadRequest,
string.Format(CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidPackage, ex.Message));
}

[HttpDelete]
[RequireSsl]
[ApiAuthorize]
Expand Down
5 changes: 5 additions & 0 deletions src/NuGetGallery/Controllers/PackagesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,11 @@ public virtual async Task<ActionResult> VerifyPackage(VerifyPackageRequest formD

// tell Lucene to update index for the new package
_indexingService.UpdateIndex();

_messageService.SendPackageAddedNotice(package,
Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme),
Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme),
Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme));
}

// delete the uploaded binary in the Uploads container
Expand Down
1 change: 1 addition & 0 deletions src/NuGetGallery/Services/IMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public interface IMessageService
void SendCredentialRemovedNotice(User user, Credential removed);
void SendCredentialAddedNotice(User user, Credential added);
void SendContactSupportEmail(ContactSupportRequest request);
void SendPackageAddedNotice(Package package, string packageUrl, string packageSupportUrl, string emailSettingsUrl);
}
}
38 changes: 38 additions & 0 deletions src/NuGetGallery/Services/MessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,44 @@ private void SendMessage(MailMessage mailMessage, bool copySender = false)
}
}

public void SendPackageAddedNotice(Package package, string packageUrl, string packageSupportUrl, string emailSettingsUrl)
{
string subject = "[{0}] Package published - {1} {2}";
string body = @"The package [{1} {2}]({3}) was just published on {0}. If this was not intended, please [contact support]({4}).
-----------------------------------------------
<em style=""font-size: 0.8em;"">
To stop receiving emails as an owner of this package, sign in to the {0} and
[change your email notification settings]({5}).
</em>";

body = String.Format(
CultureInfo.CurrentCulture,
body,
Config.GalleryOwner.DisplayName,
package.PackageRegistration.Id,
package.Version,
packageUrl,
packageSupportUrl,
emailSettingsUrl);

subject = String.Format(CultureInfo.CurrentCulture, subject, Config.GalleryOwner.DisplayName, package.PackageRegistration.Id, package.Version);

using (var mailMessage = new MailMessage())
{
mailMessage.Subject = subject;
mailMessage.Body = body;
mailMessage.From = Config.GalleryOwner;

AddOwnersToMailMessage(package.PackageRegistration, mailMessage);

if (mailMessage.To.Any())
{
SendMessage(mailMessage);
}
}
}

private static void AddOwnersToMailMessage(PackageRegistration packageRegistration, MailMessage mailMessage)
{
foreach (var owner in packageRegistration.Owners.Where(o => o.EmailAllowed))
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetGallery/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/NuGetGallery/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ The {2} Team</value>
<value>You canceled your email address change request.</value>
</data>
<data name="UploadPackage_InvalidPackage" xml:space="preserve">
<value>The NuGet is invalid. The error encountered was:'{0}'. Correct the error and try again.</value>
<value>The NuGet package is invalid. The error encountered was:'{0}'. Correct the error and try again.</value>
</data>
<data name="UploadPackage_InvalidNuspec" xml:space="preserve">
<value>The NuGet package contains an invalid .nuspec file. The error encountered was:'{0}'. Correct the error and try again.</value>
Expand Down
40 changes: 40 additions & 0 deletions tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class TestableApiController : ApiController
public Mock<IStatisticsService> MockStatisticsService { get; private set; }
public Mock<IIndexingService> MockIndexingService { get; private set; }
public Mock<IAutomaticallyCuratePackageCommand> MockAutoCuratePackage { get; private set; }
public Mock<IMessageService> MockMessageService { get; private set; }

private Stream PackageFromInputStream { get; set; }

Expand All @@ -51,6 +52,8 @@ public TestableApiController(MockBehavior behavior = MockBehavior.Default)
.Returns(Task.CompletedTask);
PackageFileService = MockPackageFileService.Object;

MessageService = (MockMessageService = new Mock<IMessageService>()).Object;

TestUtility.SetupHttpContextMockForUrlGeneration(new Mock<HttpContextBase>(), this);
}

Expand Down Expand Up @@ -78,14 +81,21 @@ public async Task CreatePackageWillSavePackageFileToFileStorage()
// Arrange
var user = new User() { EmailAddress = "confirmed@email.com" };
var packageRegistration = new PackageRegistration();
packageRegistration.Id = "theId";
packageRegistration.Owners.Add(user);
var package = new Package();
package.PackageRegistration = packageRegistration;
package.Version = "1.0.42";
packageRegistration.Packages.Add(package);

var controller = new TestableApiController();
controller.SetCurrentUser(user);
controller.MockPackageFileService.Setup(p => p.SavePackageFileAsync(It.IsAny<Package>(), It.IsAny<Stream>()))
.Returns(Task.CompletedTask).Verifiable();
controller.MockPackageService.Setup(p => p.FindPackageRegistrationById(It.IsAny<string>()))
.Returns(packageRegistration);
controller.MockPackageService.Setup(p => p.CreatePackageAsync(It.IsAny<PackageArchiveReader>(), It.IsAny<PackageStreamMetadata>(), It.IsAny<User>(), false))
.Returns(Task.FromResult(package));

var nuGetPackage = TestPackage.CreateTestPackageStream("theId", "1.0.42");
controller.SetupPackageFromInputStream(nuGetPackage);
Expand All @@ -97,6 +107,36 @@ public async Task CreatePackageWillSavePackageFileToFileStorage()
controller.MockPackageFileService.Verify();
}

[Fact]
public async Task CreatePackageWillSendPackageAddedNotice()
{
// Arrange
var user = new User() { EmailAddress = "confirmed@email.com" };
var packageRegistration = new PackageRegistration();
packageRegistration.Id = "theId";
packageRegistration.Owners.Add(user);
var package = new Package();
package.PackageRegistration = packageRegistration;
package.Version = "1.0.42";
packageRegistration.Packages.Add(package);

var controller = new TestableApiController();
controller.SetCurrentUser(user);
controller.MockMessageService.Setup(p => p.SendPackageAddedNotice(package, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Verifiable();
controller.MockPackageService.Setup(p => p.CreatePackageAsync(It.IsAny<PackageArchiveReader>(), It.IsAny<PackageStreamMetadata>(), It.IsAny<User>(), false))
.Returns(Task.FromResult(package));

var nuGetPackage = TestPackage.CreateTestPackageStream("theId", "1.0.42");
controller.SetupPackageFromInputStream(nuGetPackage);

// Act
await controller.CreatePackagePut();

// Assert
controller.MockMessageService.Verify();
}

[Fact]
public async Task CreatePackageWillReturn400IfPackageIsInvalid()
{
Expand Down
97 changes: 97 additions & 0 deletions tests/NuGetGallery.Facts/Services/MessageServiceFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,103 @@ public void UsesTypeCaptionToDescribeCredentialIfNoProviderNounPresent()
}
}

public class TheSendPackageAddedNoticeMethod
{
[Fact]
public void WillSendEmailToAllOwners()
{
// Arrange
var packageRegistration = new PackageRegistration
{
Id = "smangit",
Owners = new[]
{
new User { EmailAddress = "yung@example.com", EmailAllowed = true },
new User { EmailAddress = "flynt@example.com", EmailAllowed = true }
}
};
var package = new Package
{
Version = "1.2.3",
PackageRegistration = packageRegistration
};
packageRegistration.Packages.Add(package);

// Act
var messageService = new TestableMessageService();
messageService.SendPackageAddedNotice(package, "http://dummy1", "http://dummy2", "http://dummy3");

// Assert
var message = messageService.MockMailSender.Sent.Last();

Assert.Equal("yung@example.com", message.To[0].Address);
Assert.Equal("flynt@example.com", message.To[1].Address);
Assert.Equal(TestGalleryOwner, message.From);
Assert.Contains("[Joe Shmoe] Package published - smangit 1.2.3", message.Subject);
Assert.Contains(
"The package [smangit 1.2.3](http://dummy1) was just published on Joe Shmoe. If this was not intended, please [contact support](http://dummy2).", message.Body);
}

[Fact]
public void WillNotSendEmailToOwnerThatOptsOut()
{
// Arrange
var packageRegistration = new PackageRegistration
{
Id = "smangit",
Owners = new[]
{
new User { EmailAddress = "yung@example.com", EmailAllowed = true },
new User { EmailAddress = "flynt@example.com", EmailAllowed = false }
}
};
var package = new Package
{
Version = "1.2.3",
PackageRegistration = packageRegistration
};
packageRegistration.Packages.Add(package);

// Act
var messageService = new TestableMessageService();
messageService.SendPackageAddedNotice(package, "http://dummy1", "http://dummy2", "http://dummy3");

// Assert
var message = messageService.MockMailSender.Sent.Last();

Assert.Equal("yung@example.com", message.To[0].Address);
Assert.Equal(1, message.To.Count);
}

[Fact]
public void WillNotAttemptToSendIfNoOwnersAllow()
{
// Arrange
var packageRegistration = new PackageRegistration
{
Id = "smangit",
Owners = new[]
{
new User { EmailAddress = "yung@example.com", EmailAllowed = false },
new User { EmailAddress = "flynt@example.com", EmailAllowed = false }
}
};
var package = new Package
{
Version = "1.2.3",
PackageRegistration = packageRegistration
};
packageRegistration.Packages.Add(package);

// Act
var messageService = new TestableMessageService();
messageService.SendPackageAddedNotice(package, "http://dummy1", "http://dummy2", "http://dummy3");

// Assert
Assert.Empty(messageService.MockMailSender.Sent);
}
}

public class TestableMessageService : MessageService
{
public Mock<AuthenticationService> MockAuthService { get; protected set; }
Expand Down

0 comments on commit 095c7df

Please sign in to comment.