diff --git a/FabApprovalWorkerService/Models/UserCertificationRecord.cs b/FabApprovalWorkerService/Models/UserCertificationRecord.cs new file mode 100644 index 0000000..240aa3c --- /dev/null +++ b/FabApprovalWorkerService/Models/UserCertificationRecord.cs @@ -0,0 +1,13 @@ +namespace FabApprovalWorkerService.Models; + +public class UserCertificationRecord { + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required string Email { get; set; } + public bool IsCleansCertified { get; set; } + public bool IsAnyLevelCertified { get; set; } + public bool IsPackagingLabelingCertified { get; set; } + public bool IsEpiProCertified { get; set; } + public bool IsFqaCertified { get; set; } + public bool IsFqaAssessmentCertified { get; set; } +} \ No newline at end of file diff --git a/FabApprovalWorkerService/Program.cs b/FabApprovalWorkerService/Program.cs index 37e09f1..5d39d68 100644 --- a/FabApprovalWorkerService/Program.cs +++ b/FabApprovalWorkerService/Program.cs @@ -90,6 +90,20 @@ builder.Services.AddQuartz(q => { .WithIdentity("CA follow up trigger") .WithCronSchedule(CronScheduleBuilder.DailyAtHourAndMinute(6, 0)) ); + + JobKey userCertJob = new JobKey("User certification job"); + q.AddJob(opts => opts + .WithIdentity(userCertJob) + ); + q.AddTrigger(opts => opts + .ForJob(userCertJob) + .WithIdentity("User certification trigger") + .WithSimpleSchedule(x => x + .WithIntervalInMinutes(10) + .RepeatForever() + ) + .StartNow() + ); }); builder.Services.AddQuartzHostedService(opt => { diff --git a/FabApprovalWorkerService/Services/UserService.cs b/FabApprovalWorkerService/Services/UserService.cs index d6f0c50..d34f1c8 100644 --- a/FabApprovalWorkerService/Services/UserService.cs +++ b/FabApprovalWorkerService/Services/UserService.cs @@ -1,6 +1,9 @@ using FabApprovalWorkerService.Models; +using Microsoft.IdentityModel.Tokens; + using System.Text; +using System.Text.Json; namespace FabApprovalWorkerService.Services; @@ -20,6 +23,8 @@ public interface IUserService { Task> GetAllExpiredOOOUsersAsync(); Task GetUserEmail(int userId); Task GetUserById(int userId); + Task> GetUserCertificationRecords(); + Task UpdateUserCertificationData(IEnumerable certRecords); } public class UserService : IUserService { @@ -29,6 +34,8 @@ public class UserService : IUserService { private readonly ILogger _logger; private readonly IDalService _dalService; + private readonly string _userCertRecordsFilePath; + public UserService(ILogger logger, IDalService dalService) { _logger = logger; if (_logger is null) @@ -37,6 +44,9 @@ public class UserService : IUserService { _dalService = dalService; if (_dalService is null) throw new ArgumentNullException("IDalService not injected"); + + _userCertRecordsFilePath = Environment.GetEnvironmentVariable("UserCertificationRecordsFilePath") ?? + throw new ArgumentNullException("UserCertificationRecordsFilePath environment variable not found"); } public async Task> GetAllExpiredOOOUsersAsync() { @@ -176,7 +186,7 @@ public class UserService : IUserService { throw new ArgumentException($"User Id {userId} is not a valid user Id"); if (delegatedUserId <= 0) throw new ArgumentException($"Delegated user Id {delegatedUserId} is not a valid user Id"); - + string sql = $"update UserSubRole set UserID = {delegatedUserId}, Delegated = 1 where UserID = {userId}"; await _dalService.ExecuteAsync(sql); @@ -236,7 +246,7 @@ public class UserService : IUserService { throw new ArgumentException($"User Id {userId} is not a valid user Id"); if (delegatedUserId <= 0) throw new ArgumentException($"Delegated user Id {delegatedUserId} is not a valid user Id"); - + StringBuilder queryBuilder = new(); queryBuilder.Append($"update Approval set UserID = {delegatedUserId}, "); queryBuilder.Append($"Delegated = 1 where UserID = {userId} "); @@ -393,4 +403,60 @@ public class UserService : IUserService { throw; } } + + public async Task> GetUserCertificationRecords() { + try { + _logger.LogInformation("Attempting to get user certification records"); + + string jsonUserCertRecords = await File.ReadAllTextAsync(_userCertRecordsFilePath); + + IEnumerable? records = + JsonSerializer.Deserialize>(jsonUserCertRecords); + + if (records is null) throw new Exception("No user certification records found"); + + return records; + } catch (Exception ex) { + StringBuilder errMsgBuilder = new(); + errMsgBuilder.Append("An exception occurred when attempting to get user certification records. "); + errMsgBuilder.Append($"Exception: {ex.Message}"); + _logger.LogError(errMsgBuilder.ToString()); + throw; + } + } + + public async Task UpdateUserCertificationData(IEnumerable certRecords) { + try { + _logger.LogInformation("Attempting to update user certification data"); + + if (certRecords.IsNullOrEmpty()) + throw new ArgumentNullException("certRecords cannot be null or empty"); + + List queryTasks = new(); + foreach (UserCertificationRecord record in certRecords) { + StringBuilder queryBuilder = new(); + queryBuilder.Append("update Users "); + queryBuilder.Append($"set IsCleansCertified = {Convert.ToInt32(record.IsCleansCertified)}, "); + queryBuilder.Append($"IsAnyLevelCertified = {Convert.ToInt32(record.IsAnyLevelCertified)}, "); + queryBuilder.Append($"IsPackagingLabelingCertified = {Convert.ToInt32(record.IsPackagingLabelingCertified)}, "); + queryBuilder.Append($"IsEpiProCertified = {Convert.ToInt32(record.IsEpiProCertified)}, "); + queryBuilder.Append($"IsFqaCertified = {Convert.ToInt32(record.IsFqaCertified)}, "); + queryBuilder.Append($"IsFqaAssessmentCertified = {Convert.ToInt32(record.IsFqaAssessmentCertified)} "); + queryBuilder.Append($"where Email = '{record.Email}' collate SQL_Latin1_General_CP1_CI_AS;"); + + queryTasks.Add(_dalService.ExecuteAsync(queryBuilder.ToString())); + } + + await Task.WhenAll(queryTasks); + + return true; + + } catch (Exception ex) { + StringBuilder errMsgBuilder = new(); + errMsgBuilder.Append("An exception occurred when attempting to update user certification data. "); + errMsgBuilder.Append($"Exception: {ex.Message}"); + _logger.LogError(errMsgBuilder.ToString()); + throw; + } + } } diff --git a/FabApprovalWorkerService/Workers/UserCertificationWorker.cs b/FabApprovalWorkerService/Workers/UserCertificationWorker.cs new file mode 100644 index 0000000..b599b1e --- /dev/null +++ b/FabApprovalWorkerService/Workers/UserCertificationWorker.cs @@ -0,0 +1,69 @@ +using FabApprovalWorkerService.Models; +using FabApprovalWorkerService.Services; + +using Infineon.Monitoring.MonA; + +using Quartz; + +using System.Text; + +namespace FabApprovalWorkerService.Workers; + +public class UserCertificationWorker : IJob { + private static readonly int MAX_RETRIES = 3; + private static readonly int BACKOFF_SECONDS = 30; + + private readonly ILogger _logger; + private readonly IUserService _userService; + private readonly IMonInClient _monInClient; + + public UserCertificationWorker(ILogger logger, + IUserService userService, + IMonInClient monInClient) { + _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); + _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); + _monInClient = monInClient ?? throw new ArgumentNullException("IMonInClient not injected"); + } + + public async Task Execute(IJobExecutionContext context) { + DateTime start = DateTime.Now; + bool isInternalError = false; + string metricName = "UserCertificationWorker"; + + try { + int remainingRetries = MAX_RETRIES; + bool isSuccessful = false; + + while (!isSuccessful && remainingRetries > 0) { + await Task.Delay((MAX_RETRIES - remainingRetries--) * BACKOFF_SECONDS); + + _logger.LogInformation($"Attempting to update user certification data. Remaining retries: {remainingRetries}"); + + IEnumerable certRecords = await _userService.GetUserCertificationRecords(); + isSuccessful = await _userService.UpdateUserCertificationData(certRecords); + } + + if (isSuccessful) { + _logger.LogInformation("Successfully updated user certification data."); + } else { + throw new Exception("Unable to update user certification data."); + } + } catch (Exception ex) { + StringBuilder errMsgBuilder = new(); + errMsgBuilder.Append("An exception occurred when attempting to update user certification data. "); + errMsgBuilder.Append($"Exception: {ex.Message}"); + _logger.LogError(errMsgBuilder.ToString()); + isInternalError = true; + } finally { + DateTime end = DateTime.Now; + double latencyInMS = (end - start).TotalMilliseconds; + _monInClient.PostMetric(metricName + "Latency", latencyInMS); + + if (isInternalError) { + _monInClient.PostStatus(metricName, State.Critical); + } else { + _monInClient.PostStatus(metricName, State.Ok); + } + } + } +} \ No newline at end of file