Created PendingOOOStatusWorker

This commit is contained in:
Chase Tucker
2024-03-19 13:50:30 -07:00
parent 41a01fcf14
commit 27f78da969
184 changed files with 1930 additions and 2762 deletions

View File

@ -1,15 +1,21 @@
using Dapper;
using Dapper.Contrib.Extensions;
using FabApprovalWorkerService.Models;
using System.Data;
namespace FabApprovalWorkerService.Services;
public interface IDalService {
Task<IEnumerable<T>> QueryAsync<T>(string sql);
Task<bool> UpdateAsync<T>(ICollection<T> collection);
Task<int> ExecuteAsync(string sql);
}
public class DalService : IDalService {
private static readonly int RETRIES = 3;
private static readonly int BACKOFF_SECONDS_INTERVAL = 30;
private readonly IDbConnection _dbConnection;
private readonly ILogger<DalService> _logger;
@ -22,35 +28,73 @@ public class DalService : IDalService {
public async Task<IEnumerable<T>> QueryAsync<T>(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
try {
_logger.LogInformation("Attempting to perform query with {sql}", sql);
_dbConnection.Open();
return await _dbConnection.QueryAsync<T>(sql);
} catch (Exception ex) {
_logger.LogError("An exception occurred while attempting to perform a query. Exception: {ex}",
ex.Message);
int remainingRetries = RETRIES;
bool queryWasSuccessful = false;
Exception exception = null;
IEnumerable<T> result = new List<T>();
while (!queryWasSuccessful && remainingRetries > 0) {
int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL;
Task.Delay(backoffSeconds * 1000).Wait();
throw;
} finally {
_dbConnection.Close();
try {
_logger.LogInformation("Attempting to perform query with {sql}. Remaining retries: {remainingRetries}",
sql,
remainingRetries);
using (_dbConnection) {
result = await _dbConnection.QueryAsync<T>(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError("An exception occurred while attempting to perform a query. Exception: {ex}",
ex.Message);
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return result;
}
public async Task<bool> UpdateAsync<T>(ICollection<T> collection) {
if (collection is null) throw new ArgumentNullException("collection cannot be null");
try {
_logger.LogInformation("Attempting to perform a bulk update");
public async Task<int> ExecuteAsync(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
_dbConnection.Open();
return await _dbConnection.UpdateAsync<ICollection<T>>(collection);
} catch (Exception ex) {
_logger.LogError("An exception occurred while attempting to perform a bulk update. Exception: {ex}",
ex.Message);
int remainingRetries = RETRIES;
bool queryWasSuccessful = false;
Exception exception = null;
int rowsAffected = 0;
while (!queryWasSuccessful && remainingRetries > 0) {
int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL;
Task.Delay(backoffSeconds * 1000).Wait();
throw;
} finally {
_dbConnection.Close();
try {
_logger.LogInformation("Attempting to execute {sql}. Remaining retries: {remainingRetries}",
sql,
remainingRetries);
using (_dbConnection) {
rowsAffected = await _dbConnection.ExecuteAsync(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError("An exception occurred while attempting to execute a query. Exception: {ex}",
ex.Message);
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return rowsAffected;
}
}

View File

@ -0,0 +1,3 @@
using FabApprovalWorkerService.Models;
namespace FabApprovalWorkerService.Services;

View File

@ -0,0 +1,181 @@
using System.Text;
using System.Text.Json;
using FabApprovalWorkerService.Models;
using static System.Net.Mime.MediaTypeNames;
namespace FabApprovalWorkerService.Services;
public interface IMonInWorkerClient {
void PostAverage(string metricName, double metricValue);
void PostCount(string metricName, double metricValue);
void PostStatus(string statusName, StatusValue statusValue);
}
public class MonInWorkerClient : IMonInWorkerClient {
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _config;
private readonly ILogger<MonInWorkerClient> _logger;
private readonly string _baseUrl;
private readonly int _retryLimit = -1;
private readonly int _backoffInSeconds = -1;
private readonly string _resource;
public MonInWorkerClient(IHttpClientFactory httpClientFactory,
IConfiguration config,
ILogger<MonInWorkerClient> logger) {
_httpClientFactory = httpClientFactory;
if (_httpClientFactory is null) throw new ArgumentNullException("IHttpClientFactory not injected");
_config = config;
if (_config is null) throw new ArgumentNullException("IConfiguration not injected");
_logger = logger;
if (_logger is null) throw new ArgumentNullException("ILogger not injected");
_baseUrl = _config["MonIn:workerUrl"];
if (_baseUrl is null) throw new ArgumentNullException("MonIn:workerUrl not found in config");
Int32.TryParse(_config["MonIn:retries"], out _retryLimit);
if (_retryLimit == -1) throw new ArgumentNullException("MonIn:retries not found in config");
Int32.TryParse(_config["MonIn:backoffInSeconds"], out _backoffInSeconds);
if (_backoffInSeconds == -1) throw new ArgumentNullException("MonIn:backoffInSeconds not found in config");
_resource = _config["MonIn:resource"];
if (_resource is null) throw new ArgumentNullException("MonIn:resource not found in config");
}
public async void PostStatus(string statusName, StatusValue statusValue) {
string url = _baseUrl + "status";
_logger.LogInformation("Attempting to send MonIn status request for resource {0} with name {1} and value {2} to url {3}",
_resource,
statusName,
statusValue.ToString(),
url);
try {
bool success = false;
int retries = _retryLimit;
while (!success && retries > 0) {
Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait();
HttpClient httpClient = _httpClientFactory.CreateClient();
RawMonInStatusRequest statusRequest = new RawMonInStatusRequest() {
resource = _resource,
dateTime = DateTime.Now,
statusName = statusName,
statusValue = statusValue.ToString()
};
StringContent statusRequestJson = new StringContent(JsonSerializer.Serialize(statusRequest),
Encoding.UTF8,
Application.Json);
using HttpResponseMessage httpResponseMessage =
await httpClient.PostAsync(url, statusRequestJson);
success = httpResponseMessage.IsSuccessStatusCode;
retries--;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to send MonIn status request for resource {0} with name {1} and value {2} to url {3}. Exception: {4}",
_resource,
statusName,
statusValue.ToString(),
url,
ex.Message);
}
}
public async void PostCount(string metricName, double metricValue) {
string url = _baseUrl + "count";
_logger.LogInformation("Attempting to send MonIn count request for resource {0} with name {1} and value {2} to url {3}",
_resource,
metricName,
metricValue,
url);
try {
bool success = false;
int retries = _retryLimit;
while (!success && retries > 0) {
Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait();
HttpClient httpClient = _httpClientFactory.CreateClient();
MonInMetricRequest metricRequest = new MonInMetricRequest() {
resource = _resource,
dateTime = DateTime.Now,
metricName = metricName,
metricValue = metricValue
};
StringContent metricRequestJson = new StringContent(JsonSerializer.Serialize(metricRequest),
Encoding.UTF8,
Application.Json);
using HttpResponseMessage httpResponseMessage =
await httpClient.PostAsync(url, metricRequestJson);
success = httpResponseMessage.IsSuccessStatusCode;
retries--;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to send MonIn count request for resource {0} with name {1} and value {2} to url {3}. Exception: {4}",
_resource,
metricName,
metricValue,
url,
ex.Message);
}
}
public async void PostAverage(string metricName, double metricValue) {
string url = _baseUrl + "average";
_logger.LogInformation("Attempting to send MonIn average request for resource {0} with name {1} and value {2} to url {3}",
_resource,
metricName,
metricValue,
url);
try {
bool success = false;
int retries = _retryLimit;
while (!success && retries > 0) {
Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait();
HttpClient httpClient = _httpClientFactory.CreateClient();
MonInMetricRequest metricRequest = new MonInMetricRequest() {
resource = _resource,
dateTime = DateTime.Now,
metricName = metricName,
metricValue = metricValue
};
StringContent metricRequestJson = new StringContent(JsonSerializer.Serialize(metricRequest),
Encoding.UTF8,
Application.Json);
using HttpResponseMessage httpResponseMessage =
await httpClient.PostAsync(url, metricRequestJson);
success = httpResponseMessage.IsSuccessStatusCode;
retries--;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to send MonIn average request for resource {0} with name {1} and value {2} to url {3}. Exception: {4}",
_resource,
metricName,
metricValue,
url,
ex.Message);
}
}
}

View File

@ -1,64 +0,0 @@
using CsvHelper;
using FabApprovalWorkerService.Models;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
namespace FabApprovalWorkerService.Services;
public interface ITrainingRecordService {
IEnumerable<TrainingRecord> ScrapeRecordsFromCsvFile(CsvReader csvReader);
ConcurrentDictionary<(string, string), TrainingRecord> SortAndFilterTrainingRecordsByCompletionDate(IEnumerable<TrainingRecord> trainingRecords);
}
public class TrainingRecordService {
private readonly ILogger<TrainingRecordService> _logger;
public TrainingRecordService(ILogger<TrainingRecordService> logger) {
_logger = logger;
if (_logger is null) throw new ArgumentNullException("ILogger not injected");
}
public IEnumerable<TrainingRecord> ScrapeTrainingRecordsFromCsvFile([DisallowNull] CsvReader csvReader) {
if (csvReader is null) throw new ArgumentNullException("csvReader cannot be null");
try {
_logger.LogInformation("Attempting to scrape training records from CSV file");
using (csvReader) {
return csvReader.GetRecords<TrainingRecord>();
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to scrape training records from CSV file. Exception: {ex}",
ex.Message);
throw;
}
}
public ConcurrentDictionary<(string, string), TrainingRecord> SortAndFilterTrainingRecordsByCompletionDate([DisallowNull] IEnumerable<TrainingRecord> trainingRecords) {
if (trainingRecords is null) throw new ArgumentNullException("trainingRecords cannot be null");
ConcurrentDictionary<(string, string), TrainingRecord> recordsMap = new();
try {
_logger.LogInformation("Attempting to sort and filter training records");
IOrderedEnumerable<TrainingRecord> sortedRecords = trainingRecords.OrderByDescending(t => t.UserId)
.ThenBy(t => t.ItemId)
.ThenByDescending(t => t.CompletionDate);
foreach (TrainingRecord trainingRecord in sortedRecords) {
if (!recordsMap.TryGetValue((trainingRecord.FirstName + trainingRecord.LastName, trainingRecord.ItemId), out TrainingRecord? existingRecord)) {
recordsMap[(trainingRecord.FirstName + trainingRecord.LastName, trainingRecord.ItemId)] = trainingRecord;
}
}
return recordsMap;
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to sort and filter training records. Exception: {ex}", ex.Message);
return recordsMap;
}
}
}

View File

@ -1,65 +1,224 @@
using Dapper;
using Dapper.Contrib.Extensions;
using FabApprovalWorkerService.Models;
using FabApprovalWorkerService.Models;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace FabApprovalWorkerService.Services;
public interface IUserService {
Task<Dictionary<string, User>> GetAllUsers();
Task<bool> UpdateUserCertificationData([DisallowNull] ICollection<User> users);
Task<List<OOOTemp>> GetAllPendingOOOUsersAsync();
Task<bool> IsUserAlreadyOOO(int userId);
Task<bool> IsDelegatorAlreadyDelegatedTo(int userId);
Task<bool> InsertDelegatedRoles(int userId);
Task<bool> UpdateUserSubRoles(int userId, int delegatedUserId);
Task<bool> DelegateApprovalsForUser(int userId, int delegatedUserId);
Task<bool> FlagUserAsOOO(OOOTemp oooTemp);
Task<bool> SetOOOTempProcessed(OOOTemp oOOTemp);
Task<List<User>> GetAllActiveOOOUsersAsync();
}
public class UserService : IUserService {
private static readonly int PENDING_ITEM_STATUS = 0;
private static readonly int DENITED_ITEM_STATUS = 2;
private readonly ILogger<UserService> _logger;
private IDalService _dalService;
private readonly IDalService _dalService;
public UserService(ILogger<UserService> logger, IDalService dalService) {
_logger = logger;
if (_logger is null)
throw new ArgumentNullException("ILogger not injected");
_dalService = dalService;
if (_dalService is null)
throw new ArgumentNullException("IDalService not injected");
}
public async Task<Dictionary<string, User>> GetAllUsers() {
Dictionary<string, User> users = new Dictionary<string, User>();
public async Task<List<User>> GetAllActiveOOOUsersAsync() {
try {
_logger.LogInformation("Attempting to get all users");
_logger.LogInformation("Attempting to get all active OOO users.");
string sql = "select * from users;";
string sql = "select * from Users where OOO = 1;";
IEnumerable<User> enumerableUsers = await _dalService.QueryAsync<User>(sql);
users = enumerableUsers.ToDictionary(u => u.FirstName + u.LastName, u => u);
if (!users.Any()) {
throw new Exception("No users returned from the database");
} else {
_logger.LogInformation("Successfully retrieved the users");
}
return users;
return (await _dalService.QueryAsync<User>(sql)).ToList();
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to get all users. Exception: {ex}", ex.Message);
return users;
_logger.LogError("An exception occurred when attempting to get all active OOO users. Exception: {0}",
ex.Message);
throw;
}
}
public async Task<bool> UpdateUserCertificationData([DisallowNull] ICollection<User> users) {
if (users is null) throw new ArgumentNullException("users cannot be null");
public async Task<List<OOOTemp>> GetAllPendingOOOUsersAsync() {
try {
_logger.LogInformation("Attempting to update user cert data");
_logger.LogInformation("Attempting to get all pending OOO users.");
bool result = await _dalService.UpdateAsync(users);
string sql = string.Format("select * from OOOTemp where Processed = 0 and OOOStartDate <= '{0}';", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
_logger.LogInformation("User cert data updated successfully: {result}", result);
return result;
return (await _dalService.QueryAsync<OOOTemp>(sql)).ToList();
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to update user cert data. Exception: {ex}", ex.Message);
return false;
_logger.LogError("An exception occurred when attempting to get all pending OOO users. Exception: {0}",
ex.Message);
throw;
}
}
public async Task<bool> IsUserAlreadyOOO(int userId) {
try {
_logger.LogInformation("Attempting to determine if user {userId} is already OOO", userId);
if (userId <= 0) {
throw new ArgumentException(string.Format("User Id {0} is not a valid user Id", userId));
} else {
string sql = string.Format("select * from Users where OOO = 1 and UserID = {0}", userId);
return (await _dalService.QueryAsync<User>(sql)).Count() > 0;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to determine if user {userId} is already OOO. Exception: {ex}",
userId,
ex.Message);
throw;
}
}
public async Task<bool> IsDelegatorAlreadyDelegatedTo(int userId) {
try {
_logger.LogInformation("Attempting to determine if user {userId} is already OOO.", userId);
if (userId <= 0) {
_logger.LogInformation("DelegatedTo is {id}, which is not an active user Id", userId);
return false;
} else {
string sql = string.Format("select * from Users where DelegatedTo = {0}", userId);
return (await _dalService.QueryAsync<User>(sql)).Count() > 0;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to determine if user {userId} is already delegated to. Exception: {ex}",
userId,
ex.Message);
throw;
}
}
public async Task<bool> InsertDelegatedRoles(int userId) {
try {
_logger.LogInformation("Attempting to add delegated roles for OOO user {id}", userId);
if (userId <= 0) {
throw new ArgumentException(string.Format("User Id {0} is not a valid user Id", userId));
} else {
StringBuilder queryBuilder = new StringBuilder();
string sql = string.Format("select * from UserSubRole where UserID = {0}", userId);
List<UserSubRole> userSubRoles = (await _dalService.QueryAsync<UserSubRole>(sql)).ToList();
foreach (UserSubRole role in userSubRoles) {
queryBuilder.Clear();
queryBuilder.Append("insert into OOODelegatedRoles (UserID, DelegatedSubRoleID, InsertTimeStamp, Active) ");
queryBuilder.AppendFormat("values ({0}, {1}, '{2}', 1);", userId, role.SubRoleID, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
await _dalService.ExecuteAsync(queryBuilder.ToString());
}
return true;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to add delegated roles for OOO user {userId}. Exception: {ex}",
userId,
ex.Message);
throw;
}
}
public async Task<bool> UpdateUserSubRoles(int userId, int delegatedUserId) {
try {
_logger.LogInformation("Attempting to update sub roles for user {userId} to delegated user {delegatedUserId}",
userId,
delegatedUserId);
if (userId <= 0) {
throw new ArgumentException(string.Format("User Id {0} is not a valid user Id", userId));
} else if (delegatedUserId <= 0) {
throw new ArgumentException(string.Format("Delegated user Id {0} is not a valid user Id", delegatedUserId));
} else {
string sql = String.Format("update UserSubRole set UserID = {0}, Delegated = 1 where UserID = {1}", delegatedUserId, userId);
await _dalService.ExecuteAsync(sql);
return true;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to update sub roles for user {userId} to delegated user {delegatedUserId}. Exception: {ex}",
userId,
delegatedUserId,
ex.Message);
throw;
}
}
public async Task<bool> DelegateApprovalsForUser(int userId, int delegatedUserId) {
try {
_logger.LogInformation("Attempting to delegate approvals for user {userId} to delegated user {delegatedUserId}",
userId,
delegatedUserId);
if (userId <= 0) {
throw new ArgumentException(string.Format("User Id {0} is not a valid user Id", userId));
} else if (delegatedUserId <= 0) {
throw new ArgumentException(string.Format("Delegated user Id {0} is not a valid user Id", delegatedUserId));
} else {
string sql = String.Format("update Approval set UserID = {0}, Delegated = 1 where UserID = {1} and (ItemStatus = {2} or ItemStatus = {3})",
delegatedUserId,
userId,
PENDING_ITEM_STATUS,
DENITED_ITEM_STATUS);
await _dalService.ExecuteAsync(sql);
return true;
}
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to delegate approvals for user {userId} to delegated user {delegatedUserId}. Exception: {ex}",
userId,
delegatedUserId,
ex.Message);
throw;
}
}
public async Task<bool> FlagUserAsOOO(OOOTemp oooTemp) {
try {
if (oooTemp is null) throw new ArgumentNullException("oooTemp cannot be null");
_logger.LogInformation("Attempting to flag user {id} as OOO", oooTemp.OOOUserID);
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.Append("update Users set OOO = 1, ");
queryBuilder.AppendFormat("OOOStartDate = '{0}', ", oooTemp.OOOStartDate);
queryBuilder.AppendFormat("OOOExpirationDate = '{0}', ", oooTemp.OOOExpirationDate);
queryBuilder.AppendFormat("DelegatedTo = {0} ", oooTemp.DelegatedTo);
queryBuilder.AppendFormat("where UserID = {0}", oooTemp.OOOUserID);
return (await _dalService.ExecuteAsync(queryBuilder.ToString())) > 0;
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to flag user as OOO. Exception: {ex}",
ex.Message);
throw;
}
}
public async Task<bool> SetOOOTempProcessed(OOOTemp oooTemp) {
try {
if (oooTemp is null) throw new ArgumentNullException("oooTemp cannot be null");
_logger.LogInformation("Attempting to set OOOTemp {id} Processed to {processed}", oooTemp.ID, oooTemp.Processed);
string sql = string.Format("update OOOTemp set Processed = {0} where ID = {1}", oooTemp.Processed, oooTemp.ID);
return (await _dalService.ExecuteAsync(sql)) > 0;
} catch (Exception ex) {
_logger.LogError("An exception occurred when attempting to flag user as OOO. Exception: {ex}",
ex.Message);
throw;
}
}
}