From 7f686fa8c69c06b244872d6b567a4a898b0bed64 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 28 Oct 2026 17:01:28 +0200 Subject: [PATCH] Implemented scheduling service and first two jobs --- .../Events/LateProjectReminderEvent.cs | 77 +++++++++++ .../Events/ProcessEmailEvent.cs | 122 ++++++++++++++++++ .../CommunicationsCacheService.cs | 12 +- .../Communications/Events/SyncUserEvent.cs | 21 --- .../Services/Evidence/EvidenceCacheService.cs | 14 +- .../Execution/ExecutionCacheService.cs | 12 +- .../Governance/GovernanceCacheService.cs | 30 ++--- .../Services/Grants/GrantsCacheService.cs | 14 +- .../Services/Projects/ProjectCacheService.cs | 50 +++---- .../Services/Users/UserCacheService.cs | 10 +- .../Abstractions/IDomain.cs | 3 + .../Configuration/Email/SmtpSettings.cs | 2 - .../Extensions/Constants.cs | 8 +- .../PostFundManagement.Domain.csproj | 7 + .../Templates/lateprojectsmilestones.html | 0 .../Templates/noncompliantprojects.html | 0 PostFundManagement.Jobs/JobsWorker.cs | 22 +++- .../appsettings.Development.json | 8 -- PostFundManagement.Jobs/appsettings.json | 2 +- docker-compose.yml | 2 +- 20 files changed, 304 insertions(+), 112 deletions(-) create mode 100644 PostFundManagement.Application/Events/LateProjectReminderEvent.cs create mode 100644 PostFundManagement.Application/Events/ProcessEmailEvent.cs delete mode 100644 PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs create mode 100644 PostFundManagement.Domain/Abstractions/IDomain.cs create mode 100644 PostFundManagement.Domain/Templates/lateprojectsmilestones.html create mode 100644 PostFundManagement.Domain/Templates/noncompliantprojects.html delete mode 100644 PostFundManagement.Jobs/appsettings.Development.json diff --git a/PostFundManagement.Application/Events/LateProjectReminderEvent.cs b/PostFundManagement.Application/Events/LateProjectReminderEvent.cs new file mode 100644 index 0000000..50a252a --- /dev/null +++ b/PostFundManagement.Application/Events/LateProjectReminderEvent.cs @@ -0,0 +1,77 @@ +using PostFundManagement.Application.Abstractions.Services; +using PostFundManagement.Application.Services.Communications; +using PostFundManagement.Domain; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Infrastructure.Database; + +namespace PostFundManagement.Application.Events; + +public sealed class LateProjectEvent : IEvent +{ + public Guid Id { get; set; } = Guid.CreateVersion7(); + + public string Name { get; set; } = nameof(LateProjectEvent); + + public DateTimeOffset EnqueueAt { get; set; } = DateTimeOffset.UtcNow; + + public string CorrelationId { get; set; } = "lateproject-reminder"; + + public static LateProjectEvent Create() => new(); +} + +public sealed class LateProjectEventHandler(IDbContextFactory contextFactory, + ICommunicationsService service) : INotificationHandler +{ + readonly int lateDays = 3; + + public async ValueTask Handle(LateProjectEvent notification, CancellationToken cancellationToken) + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var lateMilestones = await context.Milestones + .AsNoTracking() + .Include(i => i.Project!.Programme!.Portfolio!.Organisation!.User) + .Where(m => (DateTime.UtcNow - m.Project!.StartedAt!).Value.TotalDays < lateDays) + .OrderBy(o => o.CreatedAt) + .GroupBy(g => g.ProjectId) + .AsSplitQuery() + .ToListAsync(cancellationToken); + + if (lateMilestones is null) return; + + using var htmlTemplateReader = new StreamReader(Assembly.GetAssembly(typeof(IDomain))? + .GetManifestResourceStream("PostFundManagement.Domain.Templates.lateprojectsmilestones.html")!); + + var template = await htmlTemplateReader.ReadToEndAsync(cancellationToken); + + foreach (var group in lateMilestones) + { + if (!group.Any()) continue; + + var project = group.FirstOrDefault()?.Project; + + if (project is null) continue; + + var mostLateMilestone = group.OrderBy(m => m.DueAt).FirstOrDefault(); + var missedMilestone = group.Where(m => DateTime.UtcNow > m.DueAt).FirstOrDefault(); + + var messageBody = template; + messageBody = messageBody.Replace("[ProjectName]", project.Name); + messageBody = messageBody.Replace("[ProjectStartDate]", $"{project.StartedAt:yyyy-MM-dd}"); + messageBody = messageBody.Replace("[ProjectEndDate]", $"{project.EndedAt:yyyy-MM-dd}"); + messageBody = messageBody.Replace("[TotalLateMiletones]", $"{group.Count()}"); + + // TODO: output information of most late milestone + // TODO: output information of milestones already missed + + var organisation = mostLateMilestone?.Project?.Programme?.Portfolio?.Organisation; + + if (organisation is null) continue; + + var notificationRequest = new CreateNotificationRequest(organisation.Id, organisation.User!.Id, NotificationPlatform.Email, + "Project Status Update", organisation.User!.Email, messageBody, NotificationStatus.Queued); + + _ = await service.CreateNotificationAsync(notificationRequest, SystemUser, cancellationToken); + } + } +} \ No newline at end of file diff --git a/PostFundManagement.Application/Events/ProcessEmailEvent.cs b/PostFundManagement.Application/Events/ProcessEmailEvent.cs new file mode 100644 index 0000000..d516867 --- /dev/null +++ b/PostFundManagement.Application/Events/ProcessEmailEvent.cs @@ -0,0 +1,122 @@ +using PostFundManagement.Application.Services.Shared; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Infrastructure.Database; + +namespace PostFundManagement.Application.Events; + +public sealed class ProcessEmailEvent : IEvent +{ + public Guid Id { get; set; } = Guid.CreateVersion7(); + + public string Name { get; set; } = nameof(ProcessEmailEvent); + + public DateTimeOffset EnqueueAt { get; set; } = DateTimeOffset.UtcNow; + + public string CorrelationId { get; set; } = "email-processor"; + + public static ProcessEmailEvent Create() => new(); +} + +public sealed class ProcessEmailEventHandler(IDbContextFactory contextFactory, + IFeatureManager featureManager, EmailService emailService, ISkorubaUserService skorubaUserService) : INotificationHandler +{ + const int maxBatch = 100; + + public async ValueTask Handle(ProcessEmailEvent notification, CancellationToken cancellationToken) + { + if (!await featureManager.IsEnabledAsync(FeatureEmailProcessor)) return; + + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var outgoing = await context.Notifications.AsNoTracking() + .Include(i => i.Organisation) + .Include(i => i.RecipientUser) + .Include(i => i.Tenant) + .Where(n => n.Status == Domain.NotificationStatus.Queued) + .OrderByDescending(o => o.CreatedAt) + .Take(maxBatch) + .ToListAsync(cancellationToken); + + if (outgoing?.Count == 0) return; + + if (emailService.Status is not (Domain.EmailStatuses.Connected or Domain.EmailStatuses.Success)) + await emailService.ConnectAsync(cancellationToken); + + var failedTransmissions = new List<(long notificationId, string? error)>(); + + foreach (var enqueuedMessage in outgoing!) + { + var userClaimResult = await skorubaUserService.GetUserClaimsAsync(enqueuedMessage.RecipientUser!.Id, cancellationToken: cancellationToken); + + if (userClaimResult.IsFailed) + { + failedTransmissions.Add((enqueuedMessage.Id, "Failed to retrieve user claims")); + + continue; + } + + var name = userClaimResult.Value.FirstOrDefault(c => c.ClaimType == "given_name")?.ClaimValue ?? enqueuedMessage.Organisation!.Name; + var surname = userClaimResult.Value.FirstOrDefault(c => c.ClaimType == "family_name")?.ClaimValue ?? string.Empty; + var email = enqueuedMessage.RecipientUser?.Email ?? enqueuedMessage.Organisation?.Email ?? enqueuedMessage.Destination ?? string.Empty; + + if (string.IsNullOrWhiteSpace(email)) + { + failedTransmissions.Add((enqueuedMessage.Id, "Could not resolve destination email address")); + + continue; + } + + var message = new Domain.Models.Email.Message + { + Sender = new Domain.Models.Email.Party + { + Address = "noreply@ifritechnologies.co.za", + Name = "Post Fund Management" + }, + Recipient = new Domain.Models.Email.Party + { + Address = email, + Name = string.IsNullOrWhiteSpace(surname) + ? name : $"{name} {surname}" + }, + Subject = enqueuedMessage.Subject, + Body = new Domain.Models.Email.Body + { + Properties = new Domain.Models.Email.BodyProperties + { + HasAttachments = false, + IsHtml = true + }, + Message = enqueuedMessage.Message + } + }; + + var response = await emailService.SendEmailAsync(message, cancellationToken); + + if (response.IsFailed || response.Value.Status is not (Domain.EmailStatuses.Connected or Domain.EmailStatuses.Success)) + { + failedTransmissions.Add((enqueuedMessage.Id, $"Error: {response.Value.Error}")); + + continue; + } + } + + foreach (var (notificationId, error) in failedTransmissions) + await context.Notifications.Where(n => n.Id == notificationId) + .ExecuteUpdateAsync(p => p + .SetProperty(u => u.Status, Domain.NotificationStatus.Failed) + .SetProperty(u => u.Error, error), cancellationToken); + + var failedIds = failedTransmissions.Select(f => f.notificationId).ToList(); + + foreach (var successMessage in outgoing) + { + if (failedIds.Contains(successMessage.Id)) continue; + + await context.Notifications.Where(n => n.Id == successMessage.Id) + .ExecuteUpdateAsync(p => p + .SetProperty(u => u.Status, Domain.NotificationStatus.Sent) + .SetProperty(u => u.Error, string.Empty), cancellationToken); + } + } +} diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs index d2c9f6d..d54448b 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs @@ -15,7 +15,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetApplicantDataAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetApplicantDataAsync(applicantUserId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getapplicantdata-{applicantUserId}").Value; @@ -37,7 +37,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetApplicantInviteAsync(applicantUserId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getapplicantinvite-{applicantUserId}").Value; @@ -59,7 +59,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getapplicantinvites-{applicantUserId}").Value; @@ -83,7 +83,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetInvitesAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getinvites-{pagination.Page}-{pagination.PageSize}").Value; @@ -107,7 +107,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetNotificationAsync(notificationIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getnotification-{notificationIdHash}").Value; @@ -131,7 +131,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID public async Task> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetNotificationsAsync(organisationIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getnotifications-{organisationIdHash}").Value; diff --git a/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs b/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs deleted file mode 100644 index 3aa9e86..0000000 --- a/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using PostFundManagement.Application.Abstractions; -using PostFundManagement.Domain.Abstractions; - -namespace PostFundManagement.Application.Services.Communications.Events; - -public class SyncUserEvent : EventBase, IEvent -{ - public string Name { get; set; } = nameof(SyncUserEvent); - - public static SyncUserEvent Create() => new(); -} - -public class SyncUserEventHandler(ILogger logger) : INotificationHandler -{ - public ValueTask Handle(SyncUserEvent notification, CancellationToken cancellationToken) - { - logger.LogInformation("Integration Sync executed. Correlation ID: {CorrelationId}", notification.CorrelationId); - - return ValueTask.CompletedTask; - } -} diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs index 1269095..cec7c02 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs @@ -21,7 +21,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetEvidenceAsync(evidenceId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getevidence-{evidenceId}").Value; @@ -45,7 +45,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetEvidenceFilesAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getevidencefiles-{pagination.Page}-{pagination.PageSize}").Value; @@ -69,7 +69,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetEvidenceUrlAsync(evidenceIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getevidenceurl-{evidenceIdHash}").Value; @@ -93,7 +93,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetIndicatorAsync(indicatorId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getindicator-{indicatorId}").Value; @@ -117,7 +117,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetIndicatorsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getindicators-{pagination.Page}-{pagination.PageSize}").Value; @@ -141,7 +141,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetMilestoneEvidencesAsync(milestoneIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getmilestoneevidences-{milestoneIdHash}").Value; @@ -165,7 +165,7 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa public async Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectEvidencesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojectevidences-{projectIdHash}").Value; diff --git a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs index c229cd4..4a0ce3a 100644 --- a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs +++ b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs @@ -24,7 +24,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetDisbursementAsync(disbursementIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getdisbursement-{disbursementIdHash}").Value; @@ -48,7 +48,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetDisbursementsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getdisbursements-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value; @@ -72,7 +72,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetRiskAsync(riskIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getrisk-{riskIdHash}").Value; @@ -96,7 +96,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetRisksAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getrisks-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value; @@ -120,7 +120,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetRuleAsync(string ruleIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetRuleAsync(ruleIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getrisk-{ruleIdHash}").Value; @@ -144,7 +144,7 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed public async Task> GetRulesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetRulesAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getrules-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value; diff --git a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs index fa5d154..e55dfa9 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs @@ -27,7 +27,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetDashboardAsync(CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetDashboardAsync(cancellationToken); var cacheKey = HashService.ToMd5Hash($"getdashboard").Value; @@ -51,7 +51,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetInstrumentsAsync(programmeIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getinstruments").Value; @@ -75,7 +75,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetOrganisationAsync(organisationIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getorganisation-{organisationIdHash}").Value; @@ -99,7 +99,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetOrganisationsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getorganisations-{pagination.Page}-{pagination.PageSize}").Value; @@ -123,7 +123,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetOrganisationPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetOrganisationPorfoliosAsync(organisationIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getorganisationportfolios-{organisationIdHash}").Value; @@ -147,7 +147,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetPortfolioAsync(portfolioIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getportfolio-{portfolioIdHash}").Value; @@ -171,7 +171,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetPortfolioProgrammesAsync(portfolioIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getportfolioprogrammes-{portfolioIdHash}").Value; @@ -195,7 +195,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProgrammesAsync(portfolioIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprogrammes-{portfolioIdHash}").Value; @@ -219,7 +219,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetTenantAsync(tenantIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"gettenant-{tenantIdHash}").Value; @@ -243,7 +243,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetTenantOrganisationAsync(tenantOrganisationIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"gettenantorganisation-{tenantOrganisationIdHash}").Value; @@ -267,7 +267,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetTenantOrganisationsAsync(string tenantIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetTenantOrganisationsAsync(tenantIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"gettenantorganisations-{tenantIdHash}").Value; @@ -294,7 +294,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetTenantsAsync(CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetTenantsAsync(cancellationToken); var cacheKey = HashService.ToMd5Hash($"gettenants").Value; @@ -336,7 +336,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetPorfoliosAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetPorfoliosAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getportfolios-{pagination.Page}-{pagination.PageSize}").Value; @@ -360,7 +360,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetProgrammeValueAsync(string programmeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProgrammeValueAsync(programmeIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprogrammevalue-{programmeIdHash}").Value; @@ -386,7 +386,7 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task> GetPorfolioValueAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetPorfolioValueAsync(portfolioIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getportfoliovalue-{portfolioIdHash}").Value; diff --git a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs index bb71e7d..a3aba6d 100644 --- a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs +++ b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs @@ -27,7 +27,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetAwardAsync(awardIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getaward-{awardIdHash}").Value; @@ -51,7 +51,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetAwardsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getawards-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value; @@ -75,7 +75,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetBudgetAsync(budgetIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getbudget-{budgetIdHash}").Value; @@ -99,7 +99,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetBudgetsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getbudgets-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value; @@ -123,7 +123,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetContractAsync(contractIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getctract-{contractIdHash}").Value; @@ -147,7 +147,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetContractsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getcontracts-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value; @@ -171,7 +171,7 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache public async Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetContractUrlAsync(contractIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getcontracturl-{contractIdHash}").Value; diff --git a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs index 801cdd1..1624d9e 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs @@ -42,7 +42,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetActivitiesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getactivities-{projectIdHash}").Value; @@ -66,7 +66,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetActivityAsync(activityIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getactivity-{activityIdHash}").Value; @@ -90,7 +90,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetBeneficiariesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getbeneficiaries-{projectIdHash}").Value; @@ -114,7 +114,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetBeneficiaryAsync(beneficiaryIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getbeneficiary-{beneficiaryIdHash}").Value; @@ -138,7 +138,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetChangeRequestAsync(changeRequestIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getchangerequest-{changeRequestIdHash}").Value; @@ -162,7 +162,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetChangeRequestsAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getchangerequests-{projectIdHash}").Value; @@ -186,7 +186,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetComplianceItemAsync(complianceItemIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getcomplianceitem-{complianceItemIdHash}").Value; @@ -210,7 +210,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetComplianceItemsAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getcomplianceitems-{projectIdHash}").Value; @@ -234,7 +234,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetIssueAsync(issueIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getissue-{issueIdHash}").Value; @@ -258,7 +258,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetIssuesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getissues-{projectIdHash}").Value; @@ -282,7 +282,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetMilestoneAsync(milestoneIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getmilestone-{milestoneIdHash}").Value; @@ -306,7 +306,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetMilestonesAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getmilestones-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value; @@ -330,7 +330,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetOutcomeAsync(outcomeIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getoutcome-{outcomeIdHash}").Value; @@ -354,7 +354,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetOutcomesAsync(beneficiaryIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getoutcomes-{beneficiaryIdHash}").Value; @@ -378,7 +378,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectOutcomesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectOutcomesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojectoutcomes-{projectIdHash}").Value; @@ -402,7 +402,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProgrammeAsync(programmeIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprogramme-{programmeIdHash}").Value; @@ -426,7 +426,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProgrammeProjectsAsync(programmeIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprogrammeprojects-{programmeIdHash}").Value; @@ -450,7 +450,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProgrammesAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprogrammes-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value; @@ -474,7 +474,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getproject-{projectIdHash}").Value; @@ -498,7 +498,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectIndicatorsAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojectindicators-{projectIdHash}").Value; @@ -522,7 +522,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectMilestonesAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojectmilestones-{projectIdHash}").Value; @@ -546,7 +546,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectRisksAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojectrisks-{projectIdHash}").Value; @@ -570,7 +570,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetProjectsAsync(pagination, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getprojects-{pagination.Index}-{pagination.Page}-{pagination.PageSize}").Value; @@ -594,7 +594,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetSiteVisitAsync(string siteVisitIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetSiteVisitAsync(siteVisitIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getsitevisit-{siteVisitIdHash}").Value; @@ -618,7 +618,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetSiteVisitsAsync(projectIdHash, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getsitevisits-{projectIdHash}").Value; diff --git a/PostFundManagement.Application/Services/Users/UserCacheService.cs b/PostFundManagement.Application/Services/Users/UserCacheService.cs index 03d4890..f636c4e 100644 --- a/PostFundManagement.Application/Services/Users/UserCacheService.cs +++ b/PostFundManagement.Application/Services/Users/UserCacheService.cs @@ -29,7 +29,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task> GetRoleClaimsAsync(Guid roleId, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetRoleClaimsAsync(roleId, userId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getsystemroles-{userId}").Value; @@ -54,7 +54,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task> GetSystemRolesAsync(Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetSystemRolesAsync(userId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getsystemroles-{userId}").Value; @@ -79,7 +79,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetUserAsync(userId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getuser-{userId}").Value; @@ -103,7 +103,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task> GetUserClaimsAsync(Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetUserClaimsAsync(targetUserId, userId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getuserclaims-{userId}").Value; @@ -128,7 +128,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task> GetUserRolesAsync(Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + if (!await featureManager.IsEnabledAsync(FeatureServiceCaching)) return await service.GetUserRolesAsync(targetUserId, userId, cancellationToken); var cacheKey = HashService.ToMd5Hash($"getuserroles-{userId}").Value; diff --git a/PostFundManagement.Domain/Abstractions/IDomain.cs b/PostFundManagement.Domain/Abstractions/IDomain.cs new file mode 100644 index 0000000..8e3aace --- /dev/null +++ b/PostFundManagement.Domain/Abstractions/IDomain.cs @@ -0,0 +1,3 @@ +namespace PostFundManagement.Domain.Abstractions; + +public interface IDomain; diff --git a/PostFundManagement.Domain/Configuration/Email/SmtpSettings.cs b/PostFundManagement.Domain/Configuration/Email/SmtpSettings.cs index 4c816e7..9ba2ac5 100644 --- a/PostFundManagement.Domain/Configuration/Email/SmtpSettings.cs +++ b/PostFundManagement.Domain/Configuration/Email/SmtpSettings.cs @@ -9,6 +9,4 @@ public sealed class SmtpSettings public string? Host { get; set; } public bool UseSsl { get; set; } - - public string? ProcessorJobCron { get; set; } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Extensions/Constants.cs b/PostFundManagement.Domain/Extensions/Constants.cs index 43fc433..e3745a6 100644 --- a/PostFundManagement.Domain/Extensions/Constants.cs +++ b/PostFundManagement.Domain/Extensions/Constants.cs @@ -2,8 +2,6 @@ namespace PostFundManagement.Domain.Extensions; public static class Constants { - public const string ServiceCachingFeature = "EnableServiceCaching"; - public const string SkorubaCredentialClient = nameof(SkorubaCredentialClient); public const string SkorubaHttpClient = nameof(SkorubaHttpClient); public const string MainScope = "ApiScope"; @@ -28,4 +26,10 @@ public static class Constants public const int TextLength = 1024; public const int MediumTextLength = 2048; public const int LargeTextLength = 1_048_576; // 1 MB — accommodates branded HTML emails with embedded logo + + public const string FeatureEmailProcessor = "EmailProcessorJobEnabled"; + public const string FeatureLateProjectReminder = "LateProjectReminderEnabled"; + public const string FeatureServiceCaching = "ServiceCaching"; + + public static Guid SystemUser { get; } = new(0x6059f1e4, 0x6a41, 0x4447, 0xa5, 0x46, 0x86, 0xec, 0xe5, 0x10, 0x20, 0x4) /* 6059f1e4-6a41-4447-a546-86ece5102004 */; } \ No newline at end of file diff --git a/PostFundManagement.Domain/PostFundManagement.Domain.csproj b/PostFundManagement.Domain/PostFundManagement.Domain.csproj index 8e3861b..0e13618 100644 --- a/PostFundManagement.Domain/PostFundManagement.Domain.csproj +++ b/PostFundManagement.Domain/PostFundManagement.Domain.csproj @@ -1,5 +1,12 @@ + + + + + + + diff --git a/PostFundManagement.Domain/Templates/lateprojectsmilestones.html b/PostFundManagement.Domain/Templates/lateprojectsmilestones.html new file mode 100644 index 0000000..e69de29 diff --git a/PostFundManagement.Domain/Templates/noncompliantprojects.html b/PostFundManagement.Domain/Templates/noncompliantprojects.html new file mode 100644 index 0000000..e69de29 diff --git a/PostFundManagement.Jobs/JobsWorker.cs b/PostFundManagement.Jobs/JobsWorker.cs index 8806a7d..f8e57fd 100644 --- a/PostFundManagement.Jobs/JobsWorker.cs +++ b/PostFundManagement.Jobs/JobsWorker.cs @@ -1,21 +1,31 @@ +using PostFundManagement.Application.Events; using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Configuration.Email; namespace PostFundManagement.Jobs; -public class JobsWorker(IJobOrchestrator jobOrchestrator, IFeatureManager featureManager, - ILogger logger) : BackgroundService +public class JobsWorker(IJobOrchestrator jobOrchestrator, IFeatureManager featureManager, + ILogger logger, IOptions emailOptions) : BackgroundService { + readonly SmtpSettings emailSettings = emailOptions.Value; + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - if(!await featureManager.IsEnabledAsync(nameof(JobsWorker))) + if (!await featureManager.IsEnabledAsync(nameof(JobsWorker))) { - logger.LogInformation("JobsWorker is disabled."); + logger.LogInformation("Recurring job automatic registration disabled."); return; } - // TODO: scheduler reccuring jobs + if (await featureManager.IsEnabledAsync(FeatureEmailProcessor)) + await jobOrchestrator.ScheduleAsync(ProcessEmailEvent.Create(), "0 */5 * * * ?", stoppingToken); - logger.LogInformation("JobsWorker is stopping."); + if (await featureManager.IsEnabledAsync(FeatureLateProjectReminder)) + await jobOrchestrator.ScheduleAsync(LateProjectEvent.Create(), "", stoppingToken); + + // TODO: Add job to check for non-compliant projects once a week + + logger.LogInformation("Recurring jobs have been registered or updated."); } } diff --git a/PostFundManagement.Jobs/appsettings.Development.json b/PostFundManagement.Jobs/appsettings.Development.json deleted file mode 100644 index b2dcdb6..0000000 --- a/PostFundManagement.Jobs/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/PostFundManagement.Jobs/appsettings.json b/PostFundManagement.Jobs/appsettings.json index 5c41809..492a93a 100644 --- a/PostFundManagement.Jobs/appsettings.json +++ b/PostFundManagement.Jobs/appsettings.json @@ -2,6 +2,7 @@ "FeatureManagement": { "JobsWorker": true, "EmailProcessorJobEnabled": true, + "LateProjectReminderEnabled": true, "ServiceCaching": false }, "GeneralS3Settings": { @@ -28,7 +29,6 @@ } }, "Email": { - "ProcessorJobCron": "0 */5 * * * ?", "SmtpServer": "smtp.example.com", "SmtpPort": 587, "SenderEmail": "noreply@example.com", diff --git a/docker-compose.yml b/docker-compose.yml index d5c14cf..511a8ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: - FeatureManagement__JobsWorker=${FEATURE_JOBS_WORKER} - FeatureManagement__EmailProcessorJobEnabled=${FEATURE_EMAIL_PROCESSOR_JOB_ENABLED} - FeatureManagement__ServiceCaching=${FEATURE_SERVICE_CACHING} + - FeatureManagement__LateProjectReminderEnabled=${FEATURE_LATE_PROJECT_REMINDER} # Header forwarding overrides - DOTNET_ENVIRONMENT=${ENVIRONMENT:-Development} @@ -48,7 +49,6 @@ services: - Monitoring__ApiKey=${MONITORING_API_KEY:-} # Email Processor Overrides - - Email__ProcessorJobCron=${EMAIL_PROCESSOR_CRON:-0 */5 * * * ?} - Email__Credentials__Password=${EMAIL_CREDENTIALS_PASSWORD:-} - Email__Credentials__Username=${EMAIL_CREDENTIALS_USERNAME:-} - Email__SmtpServer=${EMAIL_SMTP_SERVER:-}