Skip to content

Commit

Permalink
Add bulk revalidate buttons to Validation admin panel (#8738)
Browse files Browse the repository at this point in the history
* Fix bug causing only first package to revalidate to work
* Redirect back to admin panel when revalidating there
* Add revalidate pending packages and symbols to admin panel
* Add unit tests for new service method

Fix #8702
  • Loading branch information
joelverhagen authored Aug 11, 2021
1 parent 1039cd8 commit 3759c27
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 12 deletions.
3 changes: 3 additions & 0 deletions src/Bootstrap/dist/css/bootstrap-theme.css

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

4 changes: 4 additions & 0 deletions src/Bootstrap/less/theme/base.less
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ body {
}
}

.form-inline-empty {
display: inline-block;
}

.footer {
background-color: @panel-footer-bg;
color: @panel-footer-color;
Expand Down
5 changes: 4 additions & 1 deletion src/NuGetGallery/App_Code/ViewHelpers.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -653,14 +653,15 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page);
string controllerName,
string role,
string area = "",
string classes = null,
Dictionary<string, string> formValues = null)
{
using (page.Html.BeginForm(
actionName,
controllerName,
new { area = area },
FormMethod.Post,
new { id = formId }))
new { id = formId, @class = classes }))
{
@page.Html.AntiForgeryToken();
if (formValues != null)
Expand All @@ -682,6 +683,7 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page);
string controllerName,
string role,
string area = "",
string classes = null,
Dictionary<string, string> formValues = null)
{
@PostLink(
Expand All @@ -692,6 +694,7 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page);
controllerName,
role,
area,
classes,
formValues);
}

Expand Down
21 changes: 21 additions & 0 deletions src/NuGetGallery/Areas/Admin/Controllers/ValidationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using NuGet.Services.Entities;
using NuGet.Services.Validation;
using NuGet.Versioning;
using NuGetGallery.Areas.Admin.Services;
Expand Down Expand Up @@ -40,6 +42,25 @@ public virtual ActionResult Pending()
return View(nameof(Index), new ValidationPageViewModel(query, packageValidations));
}

[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<RedirectToRouteResult> RevalidatePending(ValidatingType validatingType)
{
var revalidatedCount = await _validationAdminService.RevalidatePendingAsync(validatingType);

if (revalidatedCount == 0)
{
TempData["Message"] = $"There are no {validatingType} instances that are in the {PackageStatus.Validating} state so no validations were enqueued.";
}
else
{
TempData["Message"] = $"{revalidatedCount} validations were enqueued for {validatingType} instances that are in the {PackageStatus.Validating} state. " +
$"It may take some time for the new validations to appear as the validation subsystem reacts to the enqueued messages.";
}

return RedirectToAction(nameof(Pending));
}

[HttpGet]
public virtual ActionResult Search(string q)
{
Expand Down
45 changes: 44 additions & 1 deletion src/NuGetGallery/Areas/Admin/Services/ValidationAdminService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using NuGet.Services.Entities;
using NuGet.Services.Validation;
using NuGet.Versioning;
Expand All @@ -20,17 +21,20 @@ public class ValidationAdminService
private readonly IEntityRepository<PackageValidation> _validations;
private readonly IEntityRepository<Package> _packages;
private readonly IEntityRepository<SymbolPackage> _symbolPackages;
private readonly IValidationService _validationService;

public ValidationAdminService(
IEntityRepository<PackageValidationSet> validationSets,
IEntityRepository<PackageValidation> validations,
IEntityRepository<Package> packages,
IEntityRepository<SymbolPackage> symbolPackages)
IEntityRepository<SymbolPackage> symbolPackages,
IValidationService validationService)
{
_validationSets = validationSets ?? throw new ArgumentNullException(nameof(validationSets));
_validations = validations ?? throw new ArgumentNullException(nameof(validations));
_packages = packages ?? throw new ArgumentNullException(nameof(packages));
_symbolPackages = symbolPackages ?? throw new ArgumentNullException(nameof(symbolPackages));
_validationService = validationService ?? throw new ArgumentNullException(nameof(validationService));
}

/// <summary>
Expand Down Expand Up @@ -83,6 +87,45 @@ public IReadOnlyList<PackageValidationSet> GetPending()
return pendingValidations;
}

public async Task<int> RevalidatePendingAsync(ValidatingType validatingType)
{
if (validatingType == ValidatingType.Package)
{
var pendingPackages = _packages
.GetAll()
.Include(p => p.PackageRegistration)
.Where(p => p.PackageStatusKey == PackageStatus.Validating)
.ToList();

foreach (var package in pendingPackages)
{
await _validationService.RevalidateAsync(package);
}

return pendingPackages.Count;
}
else if (validatingType == ValidatingType.SymbolPackage)
{
var pendingSymbolPackages = _symbolPackages
.GetAll()
.Include(p => p.Package)
.Include(p => p.Package.PackageRegistration)
.Where(s => s.StatusKey == PackageStatus.Validating)
.ToList();

foreach (var symbolPackage in pendingSymbolPackages)
{
await _validationService.RevalidateAsync(symbolPackage);
}

return pendingSymbolPackages.Count;
}
else
{
throw new NotSupportedException("The validating type " + validatingType + " is not supported.");
}
}

public PackageDeletedStatus GetDeletedStatus(int key, ValidatingType validatingType)
{
switch (validatingType)
Expand Down
55 changes: 50 additions & 5 deletions src/NuGetGallery/Areas/Admin/Views/Validation/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,37 @@
<section role="main" class="container main-container">
<h2>Package Validations</h2>

<p>Get <a href="@Url.Action(actionName:"Pending")">pending validations</a>.</p>
<div>
<a href="@Url.Action(actionName:"Pending")">Get pending validations</a>
|
@ViewHelpers.PostLink(
this,
formId: "revalidate-pending-packages",
linkText: "Revalidate pending packages",
actionName: "RevalidatePending",
controllerName: "Validation",
role: string.Empty,
area: "Admin",
classes: "form-inline-empty",
formValues: new Dictionary<string, string>
{
{ "validatingType", ValidatingType.Package.ToString() },
})
|
@ViewHelpers.PostLink(
this,
formId: "revalidate-pending-symbol-packages",
linkText: "Revalidate pending symbol packages",
actionName: "RevalidatePending",
controllerName: "Validation",
role: string.Empty,
area: "Admin",
classes: "form-inline-empty",
formValues: new Dictionary<string, string>
{
{ "validatingType", ValidatingType.SymbolPackage.ToString() },
})
</div>

<form method="get" action="@Url.Action(actionName: "Search")">
<p>
Expand Down Expand Up @@ -78,7 +108,7 @@
{
@ViewHelpers.PostLink(
this,
formId: "revalidate-package-form",
formId: "revalidate-package-form-" + package.PackageKey,
linkText: "Revalidate package",
actionName: "Revalidate",
controllerName: "Packages",
Expand All @@ -87,14 +117,15 @@
{
{ "id", package.Id },
{ "version", package.NormalizedVersion },
{ "returnUrl", Url.Current() },
})
<br />
}
else if (package.ValidatingType == ValidatingType.SymbolPackage)
{
@ViewHelpers.PostLink(
this,
formId: "revalidate-symbols-form",
formId: "revalidate-symbols-form-" + package.PackageKey,
linkText: "Revalidate symbols",
actionName: "RevalidateSymbols",
controllerName: "Packages",
Expand All @@ -103,6 +134,7 @@
{
{ "id", package.Id },
{ "version", package.NormalizedVersion },
{ "returnUrl", Url.Current() },
})
<br />
}
Expand Down Expand Up @@ -193,8 +225,21 @@
</table>
}
</div>

}
</div>
}
</section>
</section>

@section BottomScripts {
<script>
$(document).ready(function () {
$('#revalidate-pending-packages, #revalidate-pending-symbol-packages').submit(function (e) {
var operation = $(".post-link[data-form-id='" + e.target.id + "']").text().toLowerCase();
if (!operation || !confirm('Are you sure you want to ' + operation + '?')) {
e.preventDefault();
}
});
});
</script>
}
22 changes: 18 additions & 4 deletions src/NuGetGallery/Controllers/PackagesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1911,7 +1911,7 @@ public virtual async Task<ActionResult> Reflow(string id, string version)
[ValidateAntiForgeryToken]
[UIAuthorize(Roles = "Admins")]
[RequiresAccountConfirmation("revalidate a package")]
public virtual async Task<ActionResult> Revalidate(string id, string version)
public virtual async Task<ActionResult> Revalidate(string id, string version, string returnUrl = null)
{
var package = _packageService.FindPackageByIdAndVersionStrict(id, version);

Expand All @@ -1933,14 +1933,21 @@ public virtual async Task<ActionResult> Revalidate(string id, string version)
TempData["Message"] = $"An error occurred while revalidating the package. {ex.Message}";
}

return SafeRedirect(Url.Package(id, version));
if (string.IsNullOrEmpty(returnUrl))
{
return SafeRedirect(Url.Package(id, version));
}
else
{
return SafeRedirect(returnUrl);
}
}

[HttpPost]
[ValidateAntiForgeryToken]
[UIAuthorize(Roles = "Admins")]
[RequiresAccountConfirmation("revalidate a symbols package")]
public virtual async Task<ActionResult> RevalidateSymbols(string id, string version)
public virtual async Task<ActionResult> RevalidateSymbols(string id, string version, string returnUrl = null)
{
var package = _packageService.FindPackageByIdAndVersionStrict(id, version);

Expand Down Expand Up @@ -1988,7 +1995,14 @@ public virtual async Task<ActionResult> RevalidateSymbols(string id, string vers
TempData["Message"] = $"An error occurred while revalidating the symbols package. {ex.Message}";
}

return SafeRedirect(Url.Package(id, version));
if (string.IsNullOrEmpty(returnUrl))
{
return SafeRedirect(Url.Package(id, version));
}
else
{
return SafeRedirect(returnUrl);
}
}

/// <summary>
Expand Down
Loading

0 comments on commit 3759c27

Please sign in to comment.