Skip to content

Commit

Permalink
fix(webapi): Return 410 GONE for sub-resources on soft-deleted dialogs (
Browse files Browse the repository at this point in the history
#1564)

<!--- Provide a general summary of your changes in the Title above -->

## Description

<!--- Describe your changes in detail -->

## Related Issue(s)

- #1563

## Verification

- [x] **Your** code builds clean without any errors or warnings
- [x] Manual testing done (required)
- [ ] Relevant automated test added (if you find this hard, leave it and
we'll help out)

## Documentation

- [ ] Documentation is updated (either in `docs`-directory, Altinnpedia
or a separate linked PR in
[altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if
applicable)
  • Loading branch information
oskogstad authored Dec 6, 2024
1 parent 05cb88a commit bb601a9
Show file tree
Hide file tree
Showing 19 changed files with 101 additions and 28 deletions.
19 changes: 17 additions & 2 deletions docs/schema/V1/swagger.verified.json
Original file line number Diff line number Diff line change
Expand Up @@ -6579,6 +6579,9 @@
}
},
"description": "The given dialog ID was not found or was deleted, or the given activity ID was not found."
},
"410": {
"description": "Entity with the given key(s) is removed."
}
},
"security": [
Expand Down Expand Up @@ -6635,7 +6638,10 @@
}
}
},
"description": "Not Found"
"description": "The given dialog ID was not found or is already deleted."
},
"410": {
"description": "Entity with the given key(s) is removed."
}
},
"security": [
Expand Down Expand Up @@ -6698,7 +6704,10 @@
}
}
},
"description": "Not Found"
"description": "The given dialog ID was not found or is already deleted."
},
"410": {
"description": "Entity with the given key(s) is removed."
}
},
"security": [
Expand Down Expand Up @@ -6753,6 +6762,9 @@
}
},
"description": "The given dialog ID was not found or is already deleted."
},
"410": {
"description": "Entity with the given key(s) is removed."
}
},
"security": [
Expand Down Expand Up @@ -6924,6 +6936,9 @@
}
},
"description": "The given dialog ID was not found or was deleted, or the given transmission ID was not found."
},
"410": {
"description": "Entity with the given key(s) is removed."
}
},
"security": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public sealed class GetActivityQuery : IRequest<GetActivityResult>
}

[GenerateOneOf]
public sealed partial class GetActivityResult : OneOfBase<ActivityDto, EntityNotFound>;
public sealed partial class GetActivityResult : OneOfBase<ActivityDto, EntityNotFound, EntityDeleted>;

internal sealed class GetActivityQueryHandler : IRequestHandler<GetActivityQuery, GetActivityResult>
{
Expand All @@ -44,7 +44,8 @@ public async Task<GetActivityResult> Handle(GetActivityQuery request,
.Include(x => x.Activities.Where(x => x.Id == request.ActivityId))
.ThenInclude(x => x.Description!.Localizations)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

Expand All @@ -53,6 +54,11 @@ public async Task<GetActivityResult> Handle(GetActivityQuery request,
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

var activity = dialog.Activities.FirstOrDefault();

if (activity is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed class SearchActivityQuery : IRequest<SearchActivityResult>
}

[GenerateOneOf]
public sealed partial class SearchActivityResult : OneOfBase<List<ActivityDto>, EntityNotFound>;
public sealed partial class SearchActivityResult : OneOfBase<List<ActivityDto>, EntityNotFound, EntityDeleted>;

internal sealed class SearchActivityQueryHandler : IRequestHandler<SearchActivityQuery, SearchActivityResult>
{
Expand All @@ -38,7 +38,8 @@ public async Task<SearchActivityResult> Handle(SearchActivityQuery request, Canc
var dialog = await _db.Dialogs
.Include(x => x.Activities)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

Expand All @@ -47,6 +48,11 @@ public async Task<SearchActivityResult> Handle(SearchActivityQuery request, Canc
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

return _mapper.Map<List<ActivityDto>>(dialog.Activities);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class GetSeenLogQuery : IRequest<GetSeenLogResult>
}

[GenerateOneOf]
public sealed partial class GetSeenLogResult : OneOfBase<SeenLogDto, EntityNotFound>;
public sealed partial class GetSeenLogResult : OneOfBase<SeenLogDto, EntityNotFound, EntityDeleted>;

internal sealed class GetSeenLogQueryHandler : IRequestHandler<GetSeenLogQuery, GetSeenLogResult>
{
Expand Down Expand Up @@ -45,7 +45,8 @@ public async Task<GetSeenLogResult> Handle(GetSeenLogQuery request,
.Include(x => x.SeenLog.Where(x => x.Id == request.SeenLogId))
.ThenInclude(x => x.SeenBy)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

Expand All @@ -54,6 +55,11 @@ public async Task<GetSeenLogResult> Handle(GetSeenLogQuery request,
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

var seenLog = dialog.SeenLog.FirstOrDefault();
if (seenLog is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed class SearchSeenLogQuery : IRequest<SearchSeenLogResult>
}

[GenerateOneOf]
public sealed partial class SearchSeenLogResult : OneOfBase<List<SeenLogDto>, EntityNotFound>;
public sealed partial class SearchSeenLogResult : OneOfBase<List<SeenLogDto>, EntityNotFound, EntityDeleted>;

internal sealed class SearchSeenLogQueryHandler : IRequestHandler<SearchSeenLogQuery, SearchSeenLogResult>
{
Expand All @@ -43,7 +43,8 @@ public async Task<SearchSeenLogResult> Handle(SearchSeenLogQuery request, Cancel
.Include(x => x.SeenLog)
.ThenInclude(x => x.SeenBy)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

Expand All @@ -52,6 +53,11 @@ public async Task<SearchSeenLogResult> Handle(SearchSeenLogQuery request, Cancel
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

return dialog.SeenLog
.Select(x =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public sealed class GetTransmissionQuery : IRequest<GetTransmissionResult>
}

[GenerateOneOf]
public sealed partial class GetTransmissionResult : OneOfBase<TransmissionDto, EntityNotFound>;
public sealed partial class GetTransmissionResult : OneOfBase<TransmissionDto, EntityNotFound, EntityDeleted>;

internal sealed class GetTransmissionQueryHandler : IRequestHandler<GetTransmissionQuery, GetTransmissionResult>
{
Expand Down Expand Up @@ -51,7 +51,8 @@ public async Task<GetTransmissionResult> Handle(GetTransmissionQuery request,
.Include(x => x.Transmissions)
.ThenInclude(x => x.Sender)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

Expand All @@ -60,6 +61,11 @@ public async Task<GetTransmissionResult> Handle(GetTransmissionQuery request,
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

var transmission = dialog.Transmissions.FirstOrDefault();

return transmission is null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed class SearchTransmissionQuery : IRequest<SearchTransmissionResult>
}

[GenerateOneOf]
public sealed partial class SearchTransmissionResult : OneOfBase<List<TransmissionDto>, EntityNotFound>;
public sealed partial class SearchTransmissionResult : OneOfBase<List<TransmissionDto>, EntityNotFound, EntityDeleted>;

internal sealed class SearchTransmissionQueryHandler : IRequestHandler<SearchTransmissionQuery, SearchTransmissionResult>
{
Expand Down Expand Up @@ -48,12 +48,21 @@ public async Task<SearchTransmissionResult> Handle(SearchTransmissionQuery reque
.Include(x => x.Transmissions)
.ThenInclude(x => x.Sender)
.IgnoreQueryFilters()
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
.WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(),
x => resourceIds.Contains(x.ServiceResource))
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);

return dialog is null
? (SearchTransmissionResult)new EntityNotFound<DialogEntity>(request.DialogId)
: _mapper.Map<List<TransmissionDto>>(dialog.Transmissions);
if (dialog is null)
{
return new EntityNotFound<DialogEntity>(request.DialogId);
}

if (dialog.Deleted)
{
return new EntityDeleted<DialogEntity>(request.DialogId);
}

return _mapper.Map<List<TransmissionDto>>(dialog.Transmissions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ public override void Configure()
Group<ServiceOwnerGroup>();
Description(b => b.ProducesOneOf<ActivityDto>(
StatusCodes.Status200OK,
StatusCodes.Status404NotFound));
StatusCodes.Status404NotFound,
StatusCodes.Status410Gone));
}

public override async Task HandleAsync(GetActivityQuery req, CancellationToken ct)
{
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ Gets a single activity belonging to a dialog. For more information see the docum
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialogForChildEntity.FormatInvariant("get");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogActivityNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public override async Task HandleAsync(SearchActivityQuery req, CancellationToke
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ Gets the list of activities belonging to a dialog
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialog.FormatInvariant("get");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public override void Configure()

Description(d => d.ProducesOneOf<SeenLogDto>(
StatusCodes.Status200OK,
StatusCodes.Status404NotFound));
StatusCodes.Status404NotFound,
StatusCodes.Status410Gone));
}

public override async Task HandleAsync(GetSeenLogQuery req, CancellationToken ct)
{
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ Gets a single dialog seen log record. For more information see the documentation
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log record");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public override void Configure()

Description(d => d.ProducesOneOf<List<SeenLogDto>>(
StatusCodes.Status200OK,
StatusCodes.Status404NotFound));
StatusCodes.Status404NotFound,
StatusCodes.Status410Gone));
}

public override async Task HandleAsync(SearchSeenLogQuery req, CancellationToken ct)
{
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public SearchDialogSeenLogEndpointSummary()
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log records");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public override void Configure()

Description(b => b.ProducesOneOf<TransmissionDto>(
StatusCodes.Status200OK,
StatusCodes.Status404NotFound));
StatusCodes.Status404NotFound,
StatusCodes.Status410Gone));
}

public override async Task HandleAsync(GetTransmissionQuery req, CancellationToken ct)
{
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ Gets a single transmission belonging to a dialog. For more information see the d
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialogForChildEntity.FormatInvariant("get");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogTransmissionNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public override void Configure()

Description(b => b.ProducesOneOf<TransmissionDto>(
StatusCodes.Status200OK,
StatusCodes.Status404NotFound));
StatusCodes.Status404NotFound,
StatusCodes.Status410Gone));
}

public override async Task HandleAsync(SearchTransmissionQuery req, CancellationToken ct)
{
var result = await _sender.Send(req, ct);
await result.Match(
dto => SendOkAsync(dto, ct),
notFound => this.NotFoundAsync(notFound, ct));
notFound => this.NotFoundAsync(notFound, ct),
deleted => this.GoneAsync(deleted, ct));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ Gets the list of transmissions belonging to a dialog
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialog.FormatInvariant("get");
Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound;
Responses[StatusCodes.Status410Gone] = Constants.SwaggerSummary.DialogDeleted;
}
}

0 comments on commit bb601a9

Please sign in to comment.