| | | 1 | | using ArturRios.Heimdall.Command.Auditing; |
| | | 2 | | using ArturRios.Heimdall.Domain.Persistence; |
| | | 3 | | using Microsoft.Extensions.Logging; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Heimdall.Command.Services; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Forgets erased persons in the scope owner invitations (FR-SC-17): voids their unended invitations, |
| | | 9 | | /// clears them as the inviter of every other, and records each void in the audit trail |
| | | 10 | | /// (<c>ScopeOwnerInvitationVoided</c>, NFR-09) on the invitation it ended. |
| | | 11 | | /// </summary> |
| | | 12 | | /// <remarks> |
| | | 13 | | /// <para> |
| | | 14 | | /// Shared by the scheduled anonymisation pass (NFR-20) and the re-application of erasures (NFR-25), |
| | | 15 | | /// so the two cannot disagree about what an erased person's invitations become. |
| | | 16 | | /// </para> |
| | | 17 | | /// <para> |
| | | 18 | | /// <b>Why the voids are audited here, and at once.</b> <see cref="IScopeOwnerInvitations.ForgetPersonsAsync" /> |
| | | 19 | | /// commits on its own. Left to the run's outcome, a later step failing would lose the entries for |
| | | 20 | | /// good: the decorator records a refused run's refusal only, and a retry finds the invitations |
| | | 21 | | /// already ended, with no id left to return. Like the decorator, a failed audit write is logged and |
| | | 22 | | /// the run goes on. |
| | | 23 | | /// </para> |
| | | 24 | | /// </remarks> |
| | | 25 | | public static class ErasedPersonInvitations |
| | | 26 | | { |
| | | 27 | | /// <returns>The public ids of the invitations voided, in no particular order.</returns> |
| | | 28 | | public static async Task<IReadOnlyList<Guid>> ForgetAsync( |
| | | 29 | | IScopeOwnerInvitations invitations, |
| | | 30 | | IAuditLogWriter auditLog, |
| | | 31 | | ILogger logger, |
| | | 32 | | IReadOnlyCollection<long> personIds, |
| | | 33 | | DateTime now) |
| | | 34 | | { |
| | 114 | 35 | | if (personIds.Count == 0) |
| | | 36 | | { |
| | 61 | 37 | | return []; |
| | | 38 | | } |
| | | 39 | | |
| | 53 | 40 | | var voided = await invitations.ForgetPersonsAsync(personIds, now); |
| | | 41 | | |
| | 132 | 42 | | foreach (var invitationId in voided) |
| | | 43 | | { |
| | | 44 | | try |
| | | 45 | | { |
| | 13 | 46 | | await auditLog.WriteAsync(ScopeOwnerInvitationAuditActions.Voided, invitationId, true, null); |
| | 13 | 47 | | } |
| | 0 | 48 | | catch (Exception exception) |
| | | 49 | | { |
| | 0 | 50 | | logger.LogWarning(exception, "Failed to audit the void of invitation {InvitationId}", invitationId); |
| | 0 | 51 | | } |
| | | 52 | | } |
| | | 53 | | |
| | 53 | 54 | | return voided; |
| | 114 | 55 | | } |
| | | 56 | | } |