| | | 1 | | using Elsa.Identity.Contracts; |
| | | 2 | | using Elsa.Identity.Entities; |
| | | 3 | | using Elsa.Identity.Models; |
| | | 4 | | |
| | | 5 | | namespace Elsa.Identity.Services; |
| | | 6 | | |
| | | 7 | | /// <inheritdoc /> |
| | 6 | 8 | | public sealed class UserDeletionCoordinator( |
| | 6 | 9 | | IUserStore userStore, |
| | 6 | 10 | | IEnumerable<IUserDeletionDependencyContributor> contributors) : IUserDeletionCoordinator |
| | | 11 | | { |
| | | 12 | | /// <inheritdoc /> |
| | | 13 | | public async ValueTask<UserDeletionOperationResult> DeleteAsync(string userId, CancellationToken cancellationToken = |
| | | 14 | | { |
| | 1 | 15 | | var user = await userStore.FindAsync(new() { Id = userId }, cancellationToken); |
| | 1 | 16 | | if (user is null) |
| | 0 | 17 | | return new UserDeletionOperationResult.NotFound(); |
| | | 18 | | |
| | 1 | 19 | | var dependencies = await InspectDependenciesAsync(user, cancellationToken); |
| | | 20 | | |
| | 1 | 21 | | if (dependencies.Count > 0) |
| | 0 | 22 | | return new UserDeletionOperationResult.Blocked(dependencies); |
| | | 23 | | |
| | 1 | 24 | | await userStore.DeleteAsync(new() { Id = user.Id }, cancellationToken); |
| | | 25 | | |
| | | 26 | | // A link writer can race the first inspection when Identity and the contributing module use different stores. |
| | | 27 | | // Recheck after deletion and restore the aggregate if that writer committed first. Link writers perform the |
| | | 28 | | // complementary post-commit user check, so either ordering converges without a dangling reference. |
| | | 29 | | try |
| | | 30 | | { |
| | 1 | 31 | | dependencies = await InspectDependenciesAsync(user, cancellationToken); |
| | 0 | 32 | | } |
| | 1 | 33 | | catch |
| | | 34 | | { |
| | 1 | 35 | | await userStore.SaveAsync(user, CancellationToken.None); |
| | 1 | 36 | | throw; |
| | | 37 | | } |
| | | 38 | | |
| | 0 | 39 | | if (dependencies.Count > 0) |
| | | 40 | | { |
| | 0 | 41 | | await userStore.SaveAsync(user, CancellationToken.None); |
| | 0 | 42 | | return new UserDeletionOperationResult.Blocked(dependencies); |
| | | 43 | | } |
| | | 44 | | |
| | 0 | 45 | | return new UserDeletionOperationResult.Deleted(); |
| | 0 | 46 | | } |
| | | 47 | | |
| | | 48 | | private async ValueTask<List<UserDeletionDependency>> InspectDependenciesAsync(User user, CancellationToken cancella |
| | | 49 | | { |
| | 2 | 50 | | var dependencies = new List<UserDeletionDependency>(); |
| | 7 | 51 | | foreach (var contributor in contributors) |
| | | 52 | | { |
| | 2 | 53 | | var dependency = await contributor.InspectAsync(user, cancellationToken); |
| | 1 | 54 | | if (dependency is not null) |
| | 0 | 55 | | dependencies.Add(dependency); |
| | | 56 | | } |
| | | 57 | | |
| | 1 | 58 | | return dependencies; |
| | 1 | 59 | | } |
| | | 60 | | } |