diff --git a/src/NuGetGallery/App_Start/Routes.cs b/src/NuGetGallery/App_Start/Routes.cs index 5a79eccd57..edf51e8ecf 100644 --- a/src/NuGetGallery/App_Start/Routes.cs +++ b/src/NuGetGallery/App_Start/Routes.cs @@ -276,9 +276,14 @@ public static void RegisterUIRoutes(RouteCollection routes) routes.MapRoute( RouteName.AdminDeleteAccount, - "account/delete/{accountName}", + "account/delete/{accountName}", new { controller = "Users", action = "Delete" }); + routes.MapRoute( + RouteName.UserDeleteAccount, + "account/delete", + new { controller = "Users", action = "DeleteRequest" }); + routes.MapRoute( RouteName.Account, "account/{action}", diff --git a/src/NuGetGallery/Areas/Admin/Models/IssueStatusKeys.cs b/src/NuGetGallery/Areas/Admin/Models/IssueStatusKeys.cs index 1bcc1dd881..8e7ef376b2 100644 --- a/src/NuGetGallery/Areas/Admin/Models/IssueStatusKeys.cs +++ b/src/NuGetGallery/Areas/Admin/Models/IssueStatusKeys.cs @@ -1,11 +1,11 @@ namespace NuGetGallery.Areas.Admin.Models { - internal class IssueStatusKeys + public class IssueStatusKeys { - internal const int New = 0; - internal const int Working = 1; - internal const int WaitingForCustomer = 2; - internal const int Resolved = 3; + public const int New = 0; + public const int Working = 1; + public const int WaitingForCustomer = 2; + public const int Resolved = 3; /// /// Does not exist in database. diff --git a/src/NuGetGallery/Areas/Admin/Services/ISupportRequestService.cs b/src/NuGetGallery/Areas/Admin/Services/ISupportRequestService.cs index 0d491b6714..77e263549b 100644 --- a/src/NuGetGallery/Areas/Admin/Services/ISupportRequestService.cs +++ b/src/NuGetGallery/Areas/Admin/Services/ISupportRequestService.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Threading.Tasks; using NuGetGallery.Areas.Admin.Models; @@ -33,7 +34,7 @@ public interface ISupportRequestService /// Returns a that matches the provided filter parameters. IReadOnlyCollection GetIssues(int? assignedTo = null, string reason = null, int? issueStatusId = null, string galleryUsername = null); - Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason, + Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason, User user, Package package = null); Task UpdateIssueAsync(int issueId, int? assignedToId, int issueStatusId, string comment, string editedBy); int GetIssueCount(int? assignedToId, string reason, int? issueStatusId); diff --git a/src/NuGetGallery/Areas/Admin/Services/SupportRequestService.cs b/src/NuGetGallery/Areas/Admin/Services/SupportRequestService.cs index 37b86224bd..ce9b4e6a5f 100644 --- a/src/NuGetGallery/Areas/Admin/Services/SupportRequestService.cs +++ b/src/NuGetGallery/Areas/Admin/Services/SupportRequestService.cs @@ -197,10 +197,11 @@ public async Task UpdateIssueAsync(int issueId, int? assignedToId, int issueStat } } - public async Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason, + public async Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason, User user, Package package = null) { var loggedInUser = user?.Username ?? "Anonymous"; + bool result = true; try { @@ -236,6 +237,7 @@ public async Task AddNewSupportRequestAsync(string subject, string message, stri catch (SqlException sqlException) { QuietLog.LogHandledException(sqlException); + result = false; var packageInfo = "N/A"; if (package != null) @@ -249,8 +251,10 @@ public async Task AddNewSupportRequestAsync(string subject, string message, stri } catch (Exception e) //In case getting data from PagerDuty has failed { + result = false; QuietLog.LogHandledException(e); } + return result; } private async Task AddIssueAsync(Issue issue) diff --git a/src/NuGetGallery/Areas/Admin/ViewModels/DeleteUserAccountViewModel.cs b/src/NuGetGallery/Areas/Admin/ViewModels/DeleteUserAccountViewModel.cs index e28266cc93..4df92987b0 100644 --- a/src/NuGetGallery/Areas/Admin/ViewModels/DeleteUserAccountViewModel.cs +++ b/src/NuGetGallery/Areas/Admin/ViewModels/DeleteUserAccountViewModel.cs @@ -1,24 +1,17 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace NuGetGallery.Areas.Admin.ViewModels { - public class DeleteUserAccountViewModel + public class DeleteUserAccountViewModel : DeleteAccountViewModel { public DeleteUserAccountViewModel() { ShouldUnlist = true; } - public List Packages { get; set; } - - public User User { get; set; } - - public string AccountName { get; set; } - [Required(ErrorMessage = "Please sign using your name.")] [StringLength(1000)] [Display(Name = "Signature")] @@ -26,8 +19,6 @@ public DeleteUserAccountViewModel() [Display(Name = "Unlist the packages with no other owners.")] public bool ShouldUnlist { get; set; } - - public bool HasOrphanPackages { get; set; } } } diff --git a/src/NuGetGallery/Areas/Admin/Views/DeleteAccount/DeleteUserAccount.cshtml b/src/NuGetGallery/Areas/Admin/Views/DeleteAccount/DeleteUserAccount.cshtml index c891c5f679..26b4c63768 100644 --- a/src/NuGetGallery/Areas/Admin/Views/DeleteAccount/DeleteUserAccount.cshtml +++ b/src/NuGetGallery/Areas/Admin/Views/DeleteAccount/DeleteUserAccount.cshtml @@ -29,7 +29,7 @@
- @using (Html.BeginForm("Delete", "Users", new { id = "delete-form" })) + @using (Html.BeginForm("Delete", "Users", FormMethod.Post, new { id = "delete-form" })) { @Html.HttpMethodOverride(HttpVerbs.Delete) @Html.AntiForgeryToken() @@ -71,7 +71,7 @@ @section BottomScripts { +} \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs index d401d996da..14eae9160f 100644 --- a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs @@ -9,9 +9,10 @@ using System.Threading.Tasks; using System.Web.Mvc; using Moq; +using NuGetGallery.Areas.Admin; +using NuGetGallery.Areas.Admin.Models; using NuGetGallery.Areas.Admin.ViewModels; using NuGetGallery.Authentication; -using NuGetGallery.Configuration; using NuGetGallery.Framework; using NuGetGallery.Infrastructure.Authentication; using Xunit; @@ -1767,12 +1768,10 @@ public void DeleteDeletedAccount() // Arrange string userName = "DeletedUser"; var controller = GetController(); - - User testUser = new User() - { - Username = userName, - IsDeleted = true, - }; + + var fakes = Get(); + var testUser = fakes.CreateUser(userName); + testUser.IsDeleted = true; GetMock() .Setup(stub => stub.FindByUsername(userName)) @@ -1791,12 +1790,10 @@ public void DeleteHappyAccount() // Arrange string userName = "DeletedUser"; var controller = GetController(); + var fakes = Get(); + var testUser = fakes.CreateUser(userName); + testUser.IsDeleted = false; - User testUser = new User() - { - Username = userName, - IsDeleted = false, - }; PackageRegistration packageRegistration = new PackageRegistration(); packageRegistration.Owners.Add(testUser); @@ -1829,6 +1826,116 @@ public void DeleteHappyAccount() Assert.Equal(1, model.Packages.Count()); } } + + public class TheDeleteAccountRequestAction : TestContainer + { + [Theory] + [InlineData(false)] + [InlineData(true)] + public void DeleteAccountRequestView(bool withPendingIssues) + { + // Arrange + string userName = "DeletedUser"; + string emailAddress = $"{userName}@coldmail.com"; + + var controller = GetController(); + var fakes = Get(); + var testUser = fakes.CreateUser(userName); + testUser.EmailAddress = emailAddress; + testUser.IsDeleted = false; + + controller.SetCurrentUser(testUser); + PackageRegistration packageRegistration = new PackageRegistration(); + packageRegistration.Owners.Add(testUser); + + Package userPackage = new Package() + { + Description = "TestPackage", + Key = 1, + Version = "1.0.0", + PackageRegistration = packageRegistration + }; + packageRegistration.Packages.Add(userPackage); + + List userPackages = new List() { userPackage }; + List issues = new List(); + if ( withPendingIssues ) + { + issues.Add(new Issue() + { + IssueTitle = Strings.AccountDelete_SupportRequestTitle, + OwnerEmail = emailAddress, + CreatedBy = userName, + IssueStatus = new IssueStatus() { Key = IssueStatusKeys.New, Name = "OneIssue" } + }); + } + + GetMock() + .Setup(stub => stub.FindByUsername(userName)) + .Returns(testUser); + GetMock() + .Setup(stub => stub.FindPackagesByOwner(testUser, It.IsAny())) + .Returns(userPackages); + GetMock() + .Setup(stub => stub.GetIssues(null, null, null, null)) + .Returns(issues); + + // act + var result = controller.DeleteRequest() as ViewResult; + var model = (DeleteAccountViewModel)result.Model; + + // Assert + Assert.Equal(userName, model.AccountName); + Assert.Equal(1, model.Packages.Count()); + Assert.Equal(true, model.HasOrphanPackages); + Assert.Equal(withPendingIssues, model.HasPendingRequests); + } + } + + public class TheRequestAccountDeletionAction : TestContainer + { + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task RequestDeleteAccountAsync(bool successOnSentRequest) + { + // Arrange + string userName = "DeletedUser"; + string emailAddress = $"{userName}@coldmail.com"; + + var controller = GetController(); + + var fakes = Get(); + var testUser = fakes.CreateUser(userName); + testUser.EmailAddress = emailAddress; + controller.SetCurrentUser(testUser); + + List userPackages = new List(); + List issues = new List(); + + GetMock() + .Setup(stub => stub.FindByUsername(userName)) + .Returns(testUser); + GetMock() + .Setup(stub => stub.FindPackagesByOwner(testUser, It.IsAny())) + .Returns(userPackages); + GetMock() + .Setup(stub => stub.GetIssues(null, null, null, userName)) + .Returns(issues); + GetMock() + .Setup(stub => stub.AddNewSupportRequestAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), testUser, null)) + .Returns(Task.FromResult(successOnSentRequest)); + + // act + var result = await controller.RequestAccountDeletion() as RedirectToRouteResult; + + // Assert + Assert.NotNull(result); + Assert.Equal("DeleteRequest", (string)result.RouteValues["action"]); + bool tempData = controller.TempData.ContainsKey("RequestFailedMessage"); + Assert.Equal(!successOnSentRequest, tempData); + } + } } }