Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: purge should accept any content-type and no body #540

Merged
merged 4 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Purge;
public sealed class PurgeDialogCommand : IRequest<PurgeDialogResult>
{
public Guid Id { get; set; }
public Guid DialogId { get; set; }
public Guid? IfMatchDialogRevision { get; set; }
}

[GenerateOneOf]
public partial class PurgeDialogResult : OneOfBase<Success, EntityNotFound, ConcurrencyError>;
public partial class PurgeDialogResult : OneOfBase<Success, EntityNotFound, ConcurrencyError, ValidationError>;

internal sealed class PurgeDialogCommandHandler : IRequestHandler<PurgeDialogCommand, PurgeDialogResult>
{
Expand All @@ -43,11 +43,11 @@ public async Task<PurgeDialogResult> Handle(PurgeDialogCommand request, Cancella
.Include(x => x.Elements)
.Include(x => x.Activities)
.Where(x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);
.FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken);

if (dialog is null)
{
return new EntityNotFound<DialogEntity>(request.Id);
return new EntityNotFound<DialogEntity>(request.DialogId);
}

_db.Dialogs.HardRemove(dialog);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FluentValidation;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Purge;

internal sealed class PurgeDialogCommandValidator : AbstractValidator<PurgeDialogCommand>
{
public PurgeDialogCommandValidator()
{
RuleFor(x => x.DialogId)
.NotEqual(default(Guid));

knuhau marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ public PurgeDialogEndpoint(ISender sender)
public override void Configure()
{
Post("dialogs/{dialogId}/actions/purge");
RequestBinder(new PurgeDialogRequestBinder());
Policies(AuthorizationPolicy.ServiceProvider);
Group<ServiceOwnerGroup>();

Description(b => b
.OperationId("PurgeDialog")
.Accepts<PurgeDialogRequest>()
.ProducesOneOf(
StatusCodes.Status204NoContent,
StatusCodes.Status404NotFound,
Expand All @@ -33,21 +35,22 @@ public override void Configure()

public override async Task HandleAsync(PurgeDialogRequest req, CancellationToken ct)
{
var command = new PurgeDialogCommand { Id = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision };
var command = new PurgeDialogCommand { DialogId = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision };
var result = await _sender.Send(command, ct);
await result.Match(
success => SendNoContentAsync(ct),
notFound => this.NotFoundAsync(notFound, ct),
concurrencyError => this.PreconditionFailed(ct));
concurrencyError => this.PreconditionFailed(ct),
validationError => this.BadRequestAsync(validationError, ct));
}
}

public sealed class PurgeDialogRequest
{
public Guid DialogId { get; set; }
public Guid DialogId { get; init; }

[FromHeader(headerName: Constants.IfMatch, isRequired: false, removeFromSchema: true)]
public Guid? IfMatchDialogRevision { get; set; }
public Guid? IfMatchDialogRevision { get; init; }
}

public sealed class PurgeDialogEndpointSummary : Summary<PurgeDialogEndpoint>
Expand All @@ -67,3 +70,22 @@ Deletes a given dialog (hard delete). For more information see the documentation
Responses[StatusCodes.Status412PreconditionFailed] = Constants.SwaggerSummary.RevisionMismatch;
}
}

// Custom request binder to avoid attempted automatic deserialization of the Request body if the content type is application/json
public class PurgeDialogRequestBinder : IRequestBinder<PurgeDialogRequest>
{
public ValueTask<PurgeDialogRequest> BindAsync(BinderContext ctx, CancellationToken ct)
{
if (!Guid.TryParse(ctx.HttpContext.Request.RouteValues["dialogId"]?.ToString()!, out var dialogId))
return ValueTask.FromResult(new PurgeDialogRequest());

ctx.HttpContext.Request.Headers.TryGetValue(Constants.IfMatch, out var revisionHeader);
var revisionFound = Guid.TryParse(revisionHeader, out var revision);

return ValueTask.FromResult(new PurgeDialogRequest
{
DialogId = dialogId,
IfMatchDialogRevision = revisionFound ? revision : null
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Reflection;
using Digdir.Library.Entity.Abstractions.Features.Aggregate;
using Digdir.Library.Entity.Abstractions.Features.SoftDeletable;
using Digdir.Library.Entity.Abstractions.Features.Updatable;
using Digdir.Library.Entity.Abstractions.Features.Versionable;
using Digdir.Library.Entity.EntityFrameworkCore.Features.SoftDeletable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public async Task Creates_DialogDeletedEvent_When_Dialog_Purged()
// Act
var purgeCommand = new PurgeDialogCommand
{
Id = dialogId
DialogId = dialogId
};

await Application.Send(purgeCommand);
Expand Down Expand Up @@ -273,7 +273,7 @@ public async Task Creates_DialogElementDeleted_CloudEvent_When_Purging_Dialog()
// Act
var purgeCommand = new PurgeDialogCommand
{
Id = dialogId
DialogId = dialogId
};

await Application.Send(purgeCommand);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public async Task Purge_RemovesDialog_FromDatabase()
createResponse.TryPickT0(out _, out _).Should().BeTrue();

// Act
var purgeCommand = new PurgeDialogCommand { Id = expectedDialogId };
var purgeCommand = new PurgeDialogCommand { DialogId = expectedDialogId };
var purgeResponse = await Application.Send(purgeCommand);

// Assert
Expand All @@ -47,7 +47,7 @@ public async Task Purge_ReturnsConcurrencyError_OnIfMatchDialogRevisionMismatch(
createResponse.TryPickT0(out _, out _).Should().BeTrue();

// Act
var purgeCommand = new PurgeDialogCommand { Id = expectedDialogId, IfMatchDialogRevision = Guid.NewGuid() };
var purgeCommand = new PurgeDialogCommand { DialogId = expectedDialogId, IfMatchDialogRevision = Guid.NewGuid() };
var purgeResponse = await Application.Send(purgeCommand);

// Assert
Expand All @@ -61,7 +61,7 @@ public async Task Purge_ReturnsNotFound_OnNonExistingDialog()
var expectedDialogId = Guid.NewGuid();
var createCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
await Application.Send(createCommand);
var purgeCommand = new PurgeDialogCommand { Id = expectedDialogId };
var purgeCommand = new PurgeDialogCommand { DialogId = expectedDialogId };
await Application.Send(purgeCommand);

// Act
Expand Down