| | | 1 | | using Intentum.Core.Behavior; |
| | | 2 | | using Intentum.Persistence.Repositories; |
| | | 3 | | |
| | | 4 | | namespace Intentum.MultiTenancy; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Behavior space repository that scopes all operations to the current tenant. |
| | | 8 | | /// Injects TenantId into metadata on save and filters by tenant on read. |
| | | 9 | | /// </summary> |
| | | 10 | | public sealed class TenantAwareBehaviorSpaceRepository : IBehaviorSpaceRepository |
| | | 11 | | { |
| | | 12 | | private const string TenantIdKey = "TenantId"; |
| | | 13 | | |
| | | 14 | | private readonly IBehaviorSpaceRepository _inner; |
| | | 15 | | private readonly ITenantProvider _tenantProvider; |
| | | 16 | | |
| | 8 | 17 | | public TenantAwareBehaviorSpaceRepository(IBehaviorSpaceRepository inner, ITenantProvider tenantProvider) |
| | 8 | 18 | | { |
| | 8 | 19 | | _inner = inner ?? throw new ArgumentNullException(nameof(inner)); |
| | 8 | 20 | | _tenantProvider = tenantProvider ?? throw new ArgumentNullException(nameof(tenantProvider)); |
| | 8 | 21 | | } |
| | | 22 | | |
| | | 23 | | /// <inheritdoc /> |
| | | 24 | | public async Task<string> SaveAsync(BehaviorSpace behaviorSpace, CancellationToken cancellationToken = default) |
| | 4 | 25 | | { |
| | 4 | 26 | | var tenantId = _tenantProvider.GetCurrentTenantId(); |
| | 4 | 27 | | if (!string.IsNullOrEmpty(tenantId)) |
| | 4 | 28 | | behaviorSpace.SetMetadata(TenantIdKey, tenantId); |
| | 4 | 29 | | return await _inner.SaveAsync(behaviorSpace, cancellationToken).ConfigureAwait(false); |
| | 4 | 30 | | } |
| | | 31 | | |
| | | 32 | | /// <inheritdoc /> |
| | | 33 | | public async Task<BehaviorSpace?> GetByIdAsync(string id, CancellationToken cancellationToken = default) |
| | 1 | 34 | | { |
| | 1 | 35 | | var space = await _inner.GetByIdAsync(id, cancellationToken).ConfigureAwait(false); |
| | 1 | 36 | | return IsCurrentTenant(space) ? space : null; |
| | 1 | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <inheritdoc /> |
| | | 40 | | public async Task<IReadOnlyList<BehaviorSpace>> GetByMetadataAsync( |
| | | 41 | | string key, |
| | | 42 | | object value, |
| | | 43 | | CancellationToken cancellationToken = default) |
| | 2 | 44 | | { |
| | 2 | 45 | | var tenantId = _tenantProvider.GetCurrentTenantId(); |
| | 2 | 46 | | if (string.IsNullOrEmpty(tenantId)) |
| | 1 | 47 | | return await _inner.GetByMetadataAsync(key, value, cancellationToken).ConfigureAwait(false); |
| | | 48 | | |
| | 1 | 49 | | var byTenant = await _inner.GetByMetadataAsync(TenantIdKey, tenantId, cancellationToken).ConfigureAwait(false); |
| | 1 | 50 | | return byTenant |
| | 1 | 51 | | .Where(s => s.Metadata.TryGetValue(key, out var v) && Equals(v, value)) |
| | 1 | 52 | | .ToList(); |
| | 2 | 53 | | } |
| | | 54 | | |
| | | 55 | | /// <inheritdoc /> |
| | | 56 | | public async Task<IReadOnlyList<BehaviorSpace>> GetByTimeWindowAsync( |
| | | 57 | | DateTimeOffset start, |
| | | 58 | | DateTimeOffset end, |
| | | 59 | | CancellationToken cancellationToken = default) |
| | 2 | 60 | | { |
| | 2 | 61 | | var tenantId = _tenantProvider.GetCurrentTenantId(); |
| | 2 | 62 | | if (string.IsNullOrEmpty(tenantId)) |
| | 1 | 63 | | return await _inner.GetByTimeWindowAsync(start, end, cancellationToken).ConfigureAwait(false); |
| | | 64 | | |
| | 1 | 65 | | var byTenant = await _inner.GetByMetadataAsync(TenantIdKey, tenantId, cancellationToken).ConfigureAwait(false); |
| | 1 | 66 | | return byTenant |
| | 3 | 67 | | .Where(s => s.Events.Count > 0 && s.Events.Min(e => e.OccurredAt) <= end && s.Events.Max(e => e.OccurredAt) |
| | 1 | 68 | | .ToList(); |
| | 2 | 69 | | } |
| | | 70 | | |
| | | 71 | | /// <inheritdoc /> |
| | | 72 | | public async Task<bool> DeleteAsync(string id, CancellationToken cancellationToken = default) |
| | 2 | 73 | | { |
| | 2 | 74 | | var space = await _inner.GetByIdAsync(id, cancellationToken).ConfigureAwait(false); |
| | 2 | 75 | | if (space is null || !IsCurrentTenant(space)) |
| | 1 | 76 | | return false; |
| | 1 | 77 | | return await _inner.DeleteAsync(id, cancellationToken).ConfigureAwait(false); |
| | 2 | 78 | | } |
| | | 79 | | |
| | | 80 | | private bool IsCurrentTenant(BehaviorSpace? space) |
| | 3 | 81 | | { |
| | 3 | 82 | | if (space is null) return false; |
| | 3 | 83 | | var tenantId = _tenantProvider.GetCurrentTenantId(); |
| | 3 | 84 | | if (string.IsNullOrEmpty(tenantId)) return true; |
| | 3 | 85 | | return space.Metadata.TryGetValue(TenantIdKey, out var v) && Equals(v, tenantId); |
| | 3 | 86 | | } |
| | | 87 | | } |