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: Return 410 Gone when updating deleted dialog #464

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,13 @@
using FluentValidation.Results;

namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;

public record BadRequest(List<string> Reasons)
{
private const string BadRequestMessage = "BadRequest";

public BadRequest(params string[] reasons) : this(reasons.ToList()) { }

public List<ValidationFailure> ToValidationResults() =>
Reasons.Select(x => new ValidationFailure(BadRequestMessage, x)).ToList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public sealed class UpdateDialogCommand : IRequest<UpdateDialogResult>
}

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

internal sealed class UpdateDialogCommandHandler : IRequestHandler<UpdateDialogCommand, UpdateDialogResult>
{
Expand Down Expand Up @@ -67,6 +67,7 @@ public async Task<UpdateDialogResult> Handle(UpdateDialogCommand request, Cancel
.ThenInclude(x => x.Title!.Localizations)
.Include(x => x.ApiActions)
.ThenInclude(x => x.Endpoints)
.IgnoreQueryFilters()
.Where(x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);

Expand All @@ -75,6 +76,13 @@ public async Task<UpdateDialogResult> Handle(UpdateDialogCommand request, Cancel
return new EntityNotFound<DialogEntity>(request.Id);
}

if (dialog.Deleted)
{
// TODO: When restoration is implemented, add a hint to the error message.
// https://github.com/digdir/dialogporten/pull/406
return new BadRequest($"Entity '{nameof(DialogEntity)}' with key '{request.Id}' is removed, and cannot be updated.");
}

// Update primitive properties
_mapper.Map(request.Dto, dialog);
ValidateTimeFields(dialog);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public static Task BadRequestAsync(this IEndpoint ep, ValidationError failure, C
=> ep.BadRequestAsync(failure.Errors, cancellationToken);
public static Task BadRequestAsync(this IEndpoint ep, IEnumerable<ValidationFailure> failures, CancellationToken cancellationToken = default)
=> ep.HttpContext.Response.SendErrorsAsync(failures.ToList() ?? [], StatusCodes.Status400BadRequest, cancellation: cancellationToken);
public static Task BadRequestAsync(this IEndpoint ep, BadRequest badRequest, CancellationToken cancellationToken = default)
=> ep.HttpContext.Response.SendErrorsAsync(
badRequest.ToValidationResults(),
cancellation: cancellationToken);

public static Task PreconditionFailed(this IEndpoint ep, CancellationToken cancellationToken = default)
=> ep.HttpContext.Response.SendErrorsAsync([], StatusCodes.Status412PreconditionFailed, cancellation: cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public override async Task HandleAsync(CreateDialogActivityRequest req, Cancella
await result.Match(
success => SendCreatedAtAsync<GetDialogActivityEndpoint>(new GetDialogActivityQuery { DialogId = dialog.Id, ActivityId = req.Id.Value }, req.Id, cancellation: ct),
notFound => this.NotFoundAsync(notFound, ct),
badRequest => this.BadRequestAsync(badRequest, ct),
validationError => this.BadRequestAsync(validationError, ct),
domainError => this.UnprocessableEntityAsync(domainError, ct),
concurrencyError => this.PreconditionFailed(cancellationToken: ct));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public override async Task HandleAsync(CreateDialogElementRequest req, Cancellat
await result.Match(
success => SendCreatedAtAsync<GetDialogElementEndpoint>(new GetDialogElementQuery { DialogId = dialog.Id, ElementId = req.Id.Value }, req.Id, cancellation: ct),
notFound => this.NotFoundAsync(notFound, ct),
badRequest => this.BadRequestAsync(badRequest, ct),
validationError => this.BadRequestAsync(validationError, ct),
domainError => this.UnprocessableEntityAsync(domainError, ct),
concurrencyError => this.PreconditionFailed(cancellationToken: ct));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public override async Task HandleAsync(DeleteDialogElementRequest req, Cancellat
await result.Match(
success => SendNoContentAsync(ct),
notFound => this.NotFoundAsync(notFound, ct),
badRequest => this.BadRequestAsync(badRequest, ct),
validationError => this.BadRequestAsync(validationError, ct),
domainError => this.UnprocessableEntityAsync(domainError, ct),
concurrencyError => this.PreconditionFailed(cancellationToken: ct));
Expand Down Expand Up @@ -102,4 +103,3 @@ Deletes a given dialog element (hard delete). For more information see the docum
Responses[StatusCodes.Status412PreconditionFailed] = Constants.SwaggerSummary.RevisionMismatch;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ await this.NotFoundAsync(
await result.Match(
success => SendNoContentAsync(ct),
notFound => this.NotFoundAsync(notFound, ct),
badRequest => this.BadRequestAsync(badRequest, ct),
validationError => this.BadRequestAsync(validationError, ct),
domainError => this.UnprocessableEntityAsync(domainError, ct),
concurrencyError => this.PreconditionFailed(cancellationToken: ct));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public async Task<IActionResult> Patch(
var result = await _sender.Send(command, ct);
return result.Match(
success => (IActionResult)NoContent(),
entityNotFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound, entityNotFound.ToValidationResults())),
notFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound, notFound.ToValidationResults())),
badRequest => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())),
validationFailed => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())),
domainError => UnprocessableEntity(HttpContext.ResponseBuilder(StatusCodes.Status422UnprocessableEntity, domainError.ToValidationResults())),
concurrencyError => new ObjectResult(HttpContext.ResponseBuilder(StatusCodes.Status412PreconditionFailed)) { StatusCode = StatusCodes.Status412PreconditionFailed }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public override async Task HandleAsync(UpdateDialogRequest req, CancellationToke
var updateDialogResult = await _sender.Send(command, ct);
await updateDialogResult.Match(
success => SendNoContentAsync(ct),
entityNotFound => this.NotFoundAsync(entityNotFound, ct),
notFound => this.NotFoundAsync(notFound, ct),
badRequest => this.BadRequestAsync(badRequest, ct),
validationFailed => this.BadRequestAsync(validationFailed, ct),
domainError => this.UnprocessableEntityAsync(domainError, ct),
concurrencyError => this.PreconditionFailed(ct));
Expand Down
Loading