From 06b461985cb4aa07ca17bad80db26f27e9f8e7bb Mon Sep 17 00:00:00 2001 From: William Lewis Date: Fri, 29 Mar 2024 12:39:29 -0500 Subject: [PATCH] Added puppeteerConfig. Setup timing logic for keeping users logged in --- .../AAIntegration.SimmonsBank.API.csproj | 1 + .../Configs/EnvelopeFundConfig.cs | 6 - .../Configs/PuppeteerConfig.cs | 15 ++ .../Models/Accounts/AccountCreateRequest.cs | 1 - .../Processes/IPuppeteerProcess.cs | 3 +- .../Processes/PuppeteerProcess.cs | 45 ++---- AAIntegration.SimmonsBank.API/Program.cs | 2 +- .../Services/UserService.cs | 6 + .../Workers/PuppeteerWorker.cs | 128 +++++++++++++++--- .../appsettings.json | 6 +- 10 files changed, 151 insertions(+), 62 deletions(-) delete mode 100644 AAIntegration.SimmonsBank.API/Configs/EnvelopeFundConfig.cs create mode 100644 AAIntegration.SimmonsBank.API/Configs/PuppeteerConfig.cs diff --git a/AAIntegration.SimmonsBank.API/AAIntegration.SimmonsBank.API.csproj b/AAIntegration.SimmonsBank.API/AAIntegration.SimmonsBank.API.csproj index 6e5f1ae..9617359 100644 --- a/AAIntegration.SimmonsBank.API/AAIntegration.SimmonsBank.API.csproj +++ b/AAIntegration.SimmonsBank.API/AAIntegration.SimmonsBank.API.csproj @@ -30,6 +30,7 @@ + diff --git a/AAIntegration.SimmonsBank.API/Configs/EnvelopeFundConfig.cs b/AAIntegration.SimmonsBank.API/Configs/EnvelopeFundConfig.cs deleted file mode 100644 index 7fb0dd5..0000000 --- a/AAIntegration.SimmonsBank.API/Configs/EnvelopeFundConfig.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace AAIntegration.SimmonsBank.API.Configs; - -public class EnvelopeFundConfig -{ - public int CheckIntervalInMinutes { get; set; } -} diff --git a/AAIntegration.SimmonsBank.API/Configs/PuppeteerConfig.cs b/AAIntegration.SimmonsBank.API/Configs/PuppeteerConfig.cs new file mode 100644 index 0000000..907eaea --- /dev/null +++ b/AAIntegration.SimmonsBank.API/Configs/PuppeteerConfig.cs @@ -0,0 +1,15 @@ +namespace AAIntegration.SimmonsBank.API.Configs; + +public class PuppeteerConfig +{ + public int KeepAliveIntervalMinutes { get; set; } + public int CheckForNewDataIntervalMinutes { get; set; } + public int TaskCheckIntervalMinutes { get; set; } +} + +public class PuppeteerConfigConstants +{ + public const string KeepAliveIntervalMinutes = "KeepAliveIntervalMinutes"; + public const string CheckForNewDataIntervalMinutes = "CheckForNewDataIntervalMinutes"; + public const string TaskCheckIntervalMinutes = "TaskCheckIntervalMinutes"; +} \ No newline at end of file diff --git a/AAIntegration.SimmonsBank.API/Models/Accounts/AccountCreateRequest.cs b/AAIntegration.SimmonsBank.API/Models/Accounts/AccountCreateRequest.cs index 24cc71e..71f4285 100644 --- a/AAIntegration.SimmonsBank.API/Models/Accounts/AccountCreateRequest.cs +++ b/AAIntegration.SimmonsBank.API/Models/Accounts/AccountCreateRequest.cs @@ -10,5 +10,4 @@ public class AccountCreateRequest public string InitialBalance { get; set; } public int Currency { get; set; } public string ExternalAccountNumber { get; set; } - public int Owner { get; set; } } \ No newline at end of file diff --git a/AAIntegration.SimmonsBank.API/Processes/IPuppeteerProcess.cs b/AAIntegration.SimmonsBank.API/Processes/IPuppeteerProcess.cs index 9d3ad5e..714f270 100644 --- a/AAIntegration.SimmonsBank.API/Processes/IPuppeteerProcess.cs +++ b/AAIntegration.SimmonsBank.API/Processes/IPuppeteerProcess.cs @@ -1,9 +1,10 @@ using System.Threading.Tasks; +using AAIntegration.SimmonsBank.API.Entities; using AAIntegration.SimmonsBank.API.Services; namespace AAIntegration.SimmonsBank.API.Processes; public interface IPuppeteerProcess { - Task FundEnvelopes(); + Task StayLoggedIn(User user); } diff --git a/AAIntegration.SimmonsBank.API/Processes/PuppeteerProcess.cs b/AAIntegration.SimmonsBank.API/Processes/PuppeteerProcess.cs index f424249..e00ed65 100644 --- a/AAIntegration.SimmonsBank.API/Processes/PuppeteerProcess.cs +++ b/AAIntegration.SimmonsBank.API/Processes/PuppeteerProcess.cs @@ -6,48 +6,33 @@ using AAIntegration.SimmonsBank.API.Entities; using AAIntegration.SimmonsBank.API.Models.Transactions; using AAIntegration.SimmonsBank.API.Services; using Internal; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; namespace AAIntegration.SimmonsBank.API.Processes; public class PuppeteerProcess : IPuppeteerProcess { - private readonly EnvelopeFundConfig _config; + private readonly PuppeteerConfig _config; private readonly ILogger _logger; + private readonly IMemoryCache _memoryCache; public PuppeteerProcess( - IOptions config, - ILogger logger) + IOptions config, + ILogger logger, + IMemoryCache memoryCache) { - _config = config.Value; - _logger = logger; + _config = config.Value; + _logger = logger; + _memoryCache = memoryCache; } - public async Task FundEnvelopes() - { - _logger.LogInformation($"Starting envelope funding..."); - - // Grab envelopes - /*List envelopes = envelopeService.GetAll().ToList(); + public async Task StayLoggedIn(User user) + { + _logger.LogInformation($"... doing work and processing for user {user.Id} ..."); + } - bool anyTriggered = false; - - foreach (Envelope env in envelopes) - { - // Check if needs triggering - if (env.Enabled && !envelopeService.HasBeenFundedThisMonth(env.Id)) - { - _logger.LogInformation($"Envelope '{env.Name}' (id={env.Id}) needs funding - Has now been triggered."); - anyTriggered = true; + // Helper Functions - // Fund Envelope - envelopeService.FundEnvelopeForThisMonth(env.Id); - } - }*/ - - if (false) - _logger.LogInformation($"No action taken."); - else - _logger.LogInformation($"Envelope funding complete."); - } + } diff --git a/AAIntegration.SimmonsBank.API/Program.cs b/AAIntegration.SimmonsBank.API/Program.cs index 450ebfd..b3da98b 100644 --- a/AAIntegration.SimmonsBank.API/Program.cs +++ b/AAIntegration.SimmonsBank.API/Program.cs @@ -70,7 +70,7 @@ internal class Program // Configure strongly typed settings object builder.Services.Configure(builder.Configuration.GetSection("AppSettings")); - builder.Services.Configure(builder.Configuration.GetSection("EnvelopeFund")); + builder.Services.Configure(builder.Configuration.GetSection("Puppeteer")); builder.Services.Configure(builder.Configuration.GetSection("ActiveAllocator:Database")); DatabaseConfig dbConfig = builder.Configuration.GetSection("ActiveAllocator:Database").Get(); diff --git a/AAIntegration.SimmonsBank.API/Services/UserService.cs b/AAIntegration.SimmonsBank.API/Services/UserService.cs index 16d0774..3039985 100644 --- a/AAIntegration.SimmonsBank.API/Services/UserService.cs +++ b/AAIntegration.SimmonsBank.API/Services/UserService.cs @@ -24,6 +24,7 @@ public interface IUserService void Delete(string apiKey); Dictionary GetAllApiKeys(); User GetUser(string ApiKey); + IEnumerable GetAll(); /* Other cringe way AuthenticateResponse Authenticate(AuthenticateRequest model); @@ -116,6 +117,11 @@ public class UserService : IUserService return user; } + public IEnumerable GetAll() + { + return _context.Users; + } + // helper methods private User getUser(int id) diff --git a/AAIntegration.SimmonsBank.API/Workers/PuppeteerWorker.cs b/AAIntegration.SimmonsBank.API/Workers/PuppeteerWorker.cs index c84f5cd..9017a00 100644 --- a/AAIntegration.SimmonsBank.API/Workers/PuppeteerWorker.cs +++ b/AAIntegration.SimmonsBank.API/Workers/PuppeteerWorker.cs @@ -4,50 +4,136 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using AAIntegration.SimmonsBank.API.Processes; using AAIntegration.SimmonsBank.API.Services; +using AAIntegration.SimmonsBank.API.Entities; +using Microsoft.Extensions.Caching.Memory; namespace AAIntegration.SimmonsBank.API.Workers; public class PuppeteerWorker : BackgroundService { - private readonly EnvelopeFundConfig _config; + private readonly PuppeteerConfig _config; private readonly IPuppeteerProcess _puppeteerProcess; private readonly ILogger _logger; + private readonly IMemoryCache _memoryCache; private readonly IServiceScopeFactory _serviceScopeFactory; public PuppeteerWorker( - IOptions config, + IOptions config, IPuppeteerProcess puppeteerProcess, ILogger logger, + IMemoryCache memoryCache, IServiceScopeFactory serviceScopeFactory) { - _config = config.Value; - _puppeteerProcess = puppeteerProcess; - _logger = logger; - _serviceScopeFactory = serviceScopeFactory; + _config = config.Value; + _puppeteerProcess = puppeteerProcess; + _logger = logger; + _memoryCache = memoryCache; + _serviceScopeFactory = serviceScopeFactory; } + private IUserService _userService; + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - DateTime? lastExecutedOn = null; + DateTime? lastExecutedOn = null; + bool operationOccurred = false; - using (var scope = _serviceScopeFactory.CreateScope()) - { - //IEnvelopeService envelopeService = scope.ServiceProvider.GetService(); + using (var scope = _serviceScopeFactory.CreateScope()) + { + _userService = scope.ServiceProvider.GetService(); - // This is how we keep the app running (in the background) - while (!stoppingToken.IsCancellationRequested) - { - if (lastExecutedOn != null) + // This is how we keep the app running (in the background) + while (!stoppingToken.IsCancellationRequested) { - int minutesRemaining = _config.CheckIntervalInMinutes - (int)DateTime.UtcNow.Subtract(lastExecutedOn.Value).TotalMinutes; - await Task.Delay(TimeSpan.FromMinutes(minutesRemaining < 0 ? 0 : minutesRemaining), stoppingToken); + // Keep Alive processing + operationOccurred = await ProcessUsersKeepalive(); + + + _logger.LogInformation($"Operation occurred? {operationOccurred}"); + + // If no operation occurred, waits before looping again + if (lastExecutedOn != null && operationOccurred == false) + { + await Task.Delay(TimeSpan.FromMinutes(_config.TaskCheckIntervalMinutes), stoppingToken); + } + + lastExecutedOn = DateTime.UtcNow; + _logger.LogInformation("PuppeteerWorker executed at: {time}", DateTimeOffset.Now); } + } + } - lastExecutedOn = DateTime.UtcNow; - _logger.LogInformation("EnvelopeFundWorker running at: {time}", DateTimeOffset.Now); + private async Task ProcessUsersKeepalive() + { + bool operationOccurred = false; - //await _puppeteerProcess.FundEnvelopes(envelopeService); - } - } + foreach (User user in _userService.GetAll().ToList()) + { + int ka = _config.KeepAliveIntervalMinutes; + int uka = (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes; + + //_logger.LogInformation($"KeepAlive configured to {ka}. This user hasn't been kept alive in {uka} minute(s)."); + + if (_config.KeepAliveIntervalMinutes < (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes) + { + await _puppeteerProcess.StayLoggedIn(user); + SetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id, DateTime.UtcNow); + operationOccurred = true; + _logger.LogInformation($"Operation set to true because of user {user.Id}"); + } + } + + return operationOccurred; + } + + private DateTime GetUserLastExecution(string cacheKey, int userId) + { + var internalKeys = GetLastExecutionCachedDictionary(cacheKey); + + if (!internalKeys.TryGetValue(userId, out var lastExecution)) + { + _logger.LogInformation($"Could not find userId in '{cacheKey}' cache. Returning '{DateTime.MinValue}'."); + return DateTime.MinValue; + } + + _logger.LogInformation($"Just got user {userId} from cache with value {lastExecution}"); + return lastExecution; + } + + private void SetUserLastExecution(string cacheKey, int userId, DateTime lastExecution) + { + var internalKeys = GetLastExecutionCachedDictionary(cacheKey); + + if (internalKeys.ContainsKey(userId)) + internalKeys[userId] = lastExecution; + else + internalKeys.Add(userId, lastExecution); + + _memoryCache.Set(cacheKey, internalKeys); + _logger.LogInformation($"Just set user {userId} into cache with value {lastExecution}"); + } + + private Dictionary GetLastExecutionCachedDictionary(string cacheKey) + { + // Sets cache if no dictionary is found + if (!_memoryCache.TryGetValue>(cacheKey, out var internalKeys)) + { + internalKeys = _userService.GetAll() + .ToDictionary(u => u.Id, u => DateTime.MinValue); + _memoryCache.Set(cacheKey, internalKeys); + _logger.LogInformation($"Updated users in '{cacheKey}' cache with new id list."); + } + + return internalKeys; + } + + private void PrintCacheValues(string cacheKey) + { + _logger.LogInformation($"Printing cache values from '{cacheKey}'"); + Dictionary internalKeys = GetLastExecutionCachedDictionary(cacheKey); + foreach (KeyValuePair entry in internalKeys) + { + _logger.LogInformation($" {entry.Key} <=> {entry.Value}"); + } } } diff --git a/AAIntegration.SimmonsBank.API/appsettings.json b/AAIntegration.SimmonsBank.API/appsettings.json index cb4ae10..1443388 100644 --- a/AAIntegration.SimmonsBank.API/appsettings.json +++ b/AAIntegration.SimmonsBank.API/appsettings.json @@ -25,7 +25,9 @@ "Secret": "5de80277015f9fd564c4d1cc2cf827dbb1774cd66e7d79aa258d9c35a9f67f32fc6cf0dc24244242bd9501288e0fd69e315b", "APIUrl": "https://localhost:7260" }, - "EnvelopeFund": { - "CheckIntervalInMinutes": 10 + "Puppeteer": { + "TaskCheckIntervalMinutes": 1, + "KeepAliveIntervalMinutes": 10, + "CheckForNewDataIntervalMinutes": 15 } }