Skip to content

Commit 736fb59

Browse files
knuhauoskogstad
andauthored
fix: purge should accept any content-type and no body (#540)
Issue #538 --------- Co-authored-by: Ole Jørgen Skogstad <skogstad@softis.net>
1 parent eabd708 commit 736fb59

File tree

6 files changed

+47
-14
lines changed

6 files changed

+47
-14
lines changed

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Purge/PurgeDialogCommand.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Purge;
1313
public sealed class PurgeDialogCommand : IRequest<PurgeDialogResult>
1414
{
15-
public Guid Id { get; set; }
15+
public Guid DialogId { get; set; }
1616
public Guid? IfMatchDialogRevision { get; set; }
1717
}
1818

1919
[GenerateOneOf]
20-
public partial class PurgeDialogResult : OneOfBase<Success, EntityNotFound, ConcurrencyError>;
20+
public partial class PurgeDialogResult : OneOfBase<Success, EntityNotFound, ConcurrencyError, ValidationError>;
2121

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

4848
if (dialog is null)
4949
{
50-
return new EntityNotFound<DialogEntity>(request.Id);
50+
return new EntityNotFound<DialogEntity>(request.DialogId);
5151
}
5252

5353
_db.Dialogs.HardRemove(dialog);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using FluentValidation;
2+
3+
namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Purge;
4+
5+
internal sealed class PurgeDialogCommandValidator : AbstractValidator<PurgeDialogCommand>
6+
{
7+
public PurgeDialogCommandValidator()
8+
{
9+
RuleFor(x => x.DialogId)
10+
.NotEqual(default(Guid));
11+
}
12+
}

src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/PurgeDialogEndpoint.cs

+26-4
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ public PurgeDialogEndpoint(ISender sender)
1919
public override void Configure()
2020
{
2121
Post("dialogs/{dialogId}/actions/purge");
22+
RequestBinder(new PurgeDialogRequestBinder());
2223
Policies(AuthorizationPolicy.ServiceProvider);
2324
Group<ServiceOwnerGroup>();
2425

2526
Description(b => b
2627
.OperationId("PurgeDialog")
28+
.Accepts<PurgeDialogRequest>()
2729
.ProducesOneOf(
2830
StatusCodes.Status204NoContent,
2931
StatusCodes.Status404NotFound,
@@ -33,21 +35,22 @@ public override void Configure()
3335

3436
public override async Task HandleAsync(PurgeDialogRequest req, CancellationToken ct)
3537
{
36-
var command = new PurgeDialogCommand { Id = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision };
38+
var command = new PurgeDialogCommand { DialogId = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision };
3739
var result = await _sender.Send(command, ct);
3840
await result.Match(
3941
success => SendNoContentAsync(ct),
4042
notFound => this.NotFoundAsync(notFound, ct),
41-
concurrencyError => this.PreconditionFailed(ct));
43+
concurrencyError => this.PreconditionFailed(ct),
44+
validationError => this.BadRequestAsync(validationError, ct));
4245
}
4346
}
4447

4548
public sealed class PurgeDialogRequest
4649
{
47-
public Guid DialogId { get; set; }
50+
public Guid DialogId { get; init; }
4851

4952
[FromHeader(headerName: Constants.IfMatch, isRequired: false, removeFromSchema: true)]
50-
public Guid? IfMatchDialogRevision { get; set; }
53+
public Guid? IfMatchDialogRevision { get; init; }
5154
}
5255

5356
public sealed class PurgeDialogEndpointSummary : Summary<PurgeDialogEndpoint>
@@ -67,3 +70,22 @@ Deletes a given dialog (hard delete). For more information see the documentation
6770
Responses[StatusCodes.Status412PreconditionFailed] = Constants.SwaggerSummary.RevisionMismatch;
6871
}
6972
}
73+
74+
// Custom request binder to avoid attempted automatic deserialization of the Request body if the content type is application/json
75+
public class PurgeDialogRequestBinder : IRequestBinder<PurgeDialogRequest>
76+
{
77+
public ValueTask<PurgeDialogRequest> BindAsync(BinderContext ctx, CancellationToken ct)
78+
{
79+
if (!Guid.TryParse(ctx.HttpContext.Request.RouteValues["dialogId"]?.ToString()!, out var dialogId))
80+
return ValueTask.FromResult(new PurgeDialogRequest());
81+
82+
ctx.HttpContext.Request.Headers.TryGetValue(Constants.IfMatch, out var revisionHeader);
83+
var revisionFound = Guid.TryParse(revisionHeader, out var revision);
84+
85+
return ValueTask.FromResult(new PurgeDialogRequest
86+
{
87+
DialogId = dialogId,
88+
IfMatchDialogRevision = revisionFound ? revision : null
89+
});
90+
}
91+
}

src/Digdir.Library.Entity.EntityFrameworkCore/Features/Aggregate/AggregateExtensions.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Diagnostics;
22
using System.Reflection;
33
using Digdir.Library.Entity.Abstractions.Features.Aggregate;
4-
using Digdir.Library.Entity.Abstractions.Features.SoftDeletable;
54
using Digdir.Library.Entity.Abstractions.Features.Updatable;
65
using Digdir.Library.Entity.Abstractions.Features.Versionable;
76
using Digdir.Library.Entity.EntityFrameworkCore.Features.SoftDeletable;

tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/Events/DomainEventsTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ public async Task Creates_DialogDeletedEvent_When_Dialog_Purged()
242242
// Act
243243
var purgeCommand = new PurgeDialogCommand
244244
{
245-
Id = dialogId
245+
DialogId = dialogId
246246
};
247247

248248
await Application.Send(purgeCommand);
@@ -273,7 +273,7 @@ public async Task Creates_DialogElementDeleted_CloudEvent_When_Purging_Dialog()
273273
// Act
274274
var purgeCommand = new PurgeDialogCommand
275275
{
276-
Id = dialogId
276+
DialogId = dialogId
277277
};
278278

279279
await Application.Send(purgeCommand);

tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Dialogs/Commands/PurgeDialogTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task Purge_RemovesDialog_FromDatabase()
2121
createResponse.TryPickT0(out _, out _).Should().BeTrue();
2222

2323
// Act
24-
var purgeCommand = new PurgeDialogCommand { Id = expectedDialogId };
24+
var purgeCommand = new PurgeDialogCommand { DialogId = expectedDialogId };
2525
var purgeResponse = await Application.Send(purgeCommand);
2626

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

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

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

6767
// Act

0 commit comments

Comments
 (0)