using System.Diagnostics; using System.Linq.Expressions; using e_suite.API.Common; using e_suite.API.Common.exceptions; using e_suite.API.Common.models; using e_suite.API.Common.repository; using e_suite.Database.Audit; using e_suite.Utilities.Pagination; using eSuite.Core.Clock; using eSuite.Core.Miscellaneous; namespace e_suite.Modules.SequenceManager; public class SequenceManager : ISequenceManager { private readonly ISequenceManagerRepository _sequenceManagerRepository; private readonly IClock _clock; public SequenceManager(ISequenceManagerRepository sequenceManagerRepository, IClock clock) { _sequenceManagerRepository = sequenceManagerRepository; _clock = clock; } public async Task GetSequence(IGeneralIdRef generalIdRef, CancellationToken cancellationToken) { var sequence = await _sequenceManagerRepository.GetSequenceById(generalIdRef, cancellationToken); if (sequence == null) throw new NotFoundException(); if (sequence.Deleted) throw new NotFoundException(); var sequenceModel = new Sequence { GeneralIdRef = new GeneralIdRef { Guid = sequence.Guid, Id = sequence.Id }, Name = sequence.Name, Pattern = sequence.Pattern, Increment = sequence.Increment, Seed = sequence.Seed, RolloverType = sequence.Rollover }; return sequenceModel; } public async Task> GetSequences(Paging paging, CancellationToken cancellationToken) { var sequences = _sequenceManagerRepository.GetSequenceList(); var paginatedData = await PaginatedData.Paginate(sequences, paging, KeySelector, cancellationToken); var paginatedResult = new PaginatedData() { Count = paginatedData.Count, Page = paginatedData.Page, PageSize = paginatedData.PageSize, Data = paginatedData.Data.Select(x => new ReadSequence { Id = x.Id, Guid = x.Guid, Name = x.Name }) }; return paginatedResult; } private Expression> KeySelector(string? sortKey) { return sortKey?.ToLowerInvariant() switch { "id" => x => x.Id, "guid" => x => x.Guid, _ => x => x.Name }; } public async Task CreateSequence(AuditUserDetails auditUserDetails, NewSequence sequence, CancellationToken cancellationToken) { await _sequenceManagerRepository.TransactionAsync(async () => { var existingSequence = sequence.Guid == null ? null : await _sequenceManagerRepository.GetSequenceById(new GeneralIdRef { Guid = sequence.Guid }, cancellationToken); if (existingSequence != null && !existingSequence.Deleted) throw new ExistsException($"Sequence with guid '{sequence.Guid}' already exists"); if (existingSequence == null) { existingSequence = await _sequenceManagerRepository.GetSequenceByName(sequence.Name, cancellationToken); if (existingSequence != null && !existingSequence.Deleted) throw new ExistsException($"Sequence with name '{sequence.Name}' already exists"); } existingSequence ??= new Database.Core.Tables.Sequences.Sequence { Guid = sequence.Guid ?? Guid.NewGuid() }; existingSequence.Name = sequence.Name; existingSequence.Seed = sequence.Seed; existingSequence.Increment = sequence.Increment; existingSequence.Pattern = sequence.Pattern; existingSequence.Rollover = sequence.RolloverType; if (existingSequence.Deleted) { existingSequence.Deleted = false; await _sequenceManagerRepository.EditSequence(auditUserDetails, existingSequence, cancellationToken); } else { await _sequenceManagerRepository.AddSequence(auditUserDetails, existingSequence, cancellationToken); } }); } public async Task EditSequence(AuditUserDetails auditUserDetails, Sequence editSequence, CancellationToken cancellationToken) { await _sequenceManagerRepository.TransactionAsync(async () => { var existingSequence = await _sequenceManagerRepository.GetSequenceById(editSequence.GeneralIdRef!, cancellationToken); if (existingSequence == null || existingSequence.Deleted) throw new NotFoundException("A sequence with this Id doesn't exist"); var identicalNameSequence = await _sequenceManagerRepository.GetSequenceByName(editSequence.Name, cancellationToken); if (identicalNameSequence != null && (identicalNameSequence.Id != editSequence.GeneralIdRef!.Id && identicalNameSequence.Guid != editSequence.GeneralIdRef.Guid)) throw new ExistsException($"Sequence with name '{editSequence.Name}' already exists"); existingSequence.Name = editSequence.Name; existingSequence.Increment = editSequence.Increment; existingSequence.Seed = editSequence.Seed; existingSequence.Pattern = editSequence.Pattern; existingSequence.Rollover = editSequence.RolloverType; await _sequenceManagerRepository.EditSequence(auditUserDetails, existingSequence, cancellationToken); }); } public async Task DeleteSequence(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken) { await _sequenceManagerRepository.TransactionAsync(async () => { var existingSequence = await _sequenceManagerRepository.GetSequenceById(generalIdRef, cancellationToken); if (existingSequence == null || existingSequence.Deleted) throw new NotFoundException("A sequence with this Id doesn't exist"); existingSequence.Deleted = true; await _sequenceManagerRepository.EditSequence(auditUserDetails, existingSequence, cancellationToken); }); } public async Task> NextValue(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken) { return await NextValue( auditUserDetails, generalIdRef, 1, cancellationToken).ConfigureAwait(false); } public async Task> NextValue(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, int number, CancellationToken cancellationToken) { return await _sequenceManagerRepository.TransactionAsync(async () => { var existingSequence = await _sequenceManagerRepository.GetSequenceById(generalIdRef, cancellationToken); if (existingSequence == null || existingSequence.Deleted) throw new NotFoundException("A sequence with this Id doesn't exist"); var result = new List(); var stopwatch = Stopwatch.StartNew(); while (result.Count < number) { if (stopwatch.ElapsedMilliseconds > 2000) throw new TimeoutException($"Took more than 2 seconds to create count list. Created {result.Count} values in the available time"); cancellationToken.ThrowIfCancellationRequested(); result.Add(SequenceNumberBuilder.GetNextSequenceNumber(existingSequence, _clock)); } await _sequenceManagerRepository.EditSequence(auditUserDetails, existingSequence, cancellationToken); return result; }); } }