Files
itworx-knowledge-hub-Public/src/backend/ItWorx.KnowledgeHub.Api/Endpoints/SourceBatchPurgeEndpoints.cs
T

78 lines
3.8 KiB
C#

using System.Security.Claims;
using ItWorx.KnowledgeHub.Api.Authorization;
using ItWorx.KnowledgeHub.Api.Security;
using ItWorx.KnowledgeHub.Application.Migration;
using ItWorx.KnowledgeHub.Application.Migration.UseCases;
using ItWorx.KnowledgeHub.Contracts.Errors;
using ItWorx.KnowledgeHub.Contracts.Migration;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
namespace ItWorx.KnowledgeHub.Api.Endpoints;
/// <summary>
/// Explicit administrative purge for an externally published source batch. It accepts
/// only a reviewed list of target page identifiers and refuses ambiguous scope before
/// it can mutate production data.
/// </summary>
public static class SourceBatchPurgeEndpoints
{
public static IEndpointRouteBuilder MapSourceBatchPurgeEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/v1/admin/source-batches/purge")
.WithTags("Source batch purge")
.RequireAuthorization(AuthorizationPolicies.RequireAdministrator);
group.MapPost("/dry-run", DryRunAsync).AddEndpointFilter<AntiforgeryEndpointFilter>();
group.MapPost("/execute", ExecuteAsync).AddEndpointFilter<AntiforgeryEndpointFilter>();
return app;
}
private static async Task<Ok<SourceBatchPurgeDryRunResponse>> DryRunAsync(
[FromBody] SourceBatchPurgeRequest request,
PurgeSourceBatchUseCase useCase,
CancellationToken cancellationToken) =>
TypedResults.Ok(ToResponse(await useCase.DryRunAsync(request.BatchId, request.Source, request.Pages, cancellationToken)));
private static async Task<Results<Ok<SourceBatchPurgeResponse>, ProblemHttpResult>> ExecuteAsync(
[FromBody] ExecuteSourceBatchPurgeRequest request,
PurgeSourceBatchUseCase useCase,
ClaimsPrincipal principal,
HttpContext httpContext,
CancellationToken cancellationToken)
{
var correlationId = httpContext.Items[CorrelationIdMiddleware.ItemKey] as string;
var result = await useCase.ExecuteAsync(
new PurgeSourceBatchCommand(
request.BatchId, request.Source, request.Pages, request.ConfirmationHash,
Guid.Parse(principal.FindFirstValue(ClaimTypes.NameIdentifier)!), correlationId),
cancellationToken);
if (!result.Succeeded)
{
return TypedResults.Problem(
statusCode: StatusCodes.Status409Conflict,
type: ProblemTypes.Validation,
title: result.StaleDryRun ? "Dry-run is niet langer actueel." : "Bronbatch voldoet niet aan de veiligheidscontroles.",
detail: result.StaleDryRun
? "Voer opnieuw een dry-run uit; de geselecteerde gegevens zijn intussen gewijzigd."
: string.Join(" ", result.Inspection.Blockers));
}
return TypedResults.Ok(new SourceBatchPurgeResponse(
"purged", ToResponse(result.Inspection), result.RemovedPages, result.RemovedRevisions,
result.RemovedDrafts, result.RemovedLinks, result.RemovedAttachmentUsages,
result.RemovedAttachmentReferences, result.RemovedImportJobs, result.RemovedImportItems,
result.RemovedProvenanceRecords, result.RemovedBlobs));
}
private static SourceBatchPurgeDryRunResponse ToResponse(SourceBatchPurgeInspection value) =>
new(
value.IsSafe, value.ConfirmationHash, value.PageCount, value.DescendantPageCount,
value.RevisionCount, value.DraftCount, value.LinkCount, value.AttachmentUsageCount,
value.AttachmentReferenceCount, value.OrphanedBlobCount, value.SharedBlobCount,
value.ImportJobCount, value.ImportItemCount, value.ProvenanceCount,
value.SourceTagAssignmentCount, value.SearchIndexRecordCount,
value.GeneratedSourceDerivedPageCount, value.RetainedPageCount, value.Blockers);
}