using System.Text; using MesaFabApproval.Shared.Models; using MesaFabApproval.Shared.Utilities; using Microsoft.Extensions.Caching.Memory; namespace MesaFabApproval.API.Services; public interface IApprovalService { Task GetRoleIdForRoleName(string roleName); Task> GetSubRolesForSubRoleName(string subRoleName, int roleId); Task> GetApprovalGroupMembers(int subRoleId); Task CreateApproval(Approval approval); Task UpdateApproval(Approval approval); Task Approve(Approval approval); Task Deny(Approval approval); Task> GetApprovalsForIssueId(int issueId, bool bypassCache); Task> GetApprovalsForUserId(int userId, bool bypassCache); } public class ApprovalService : IApprovalService { private readonly ILogger _logger; private readonly IMemoryCache _cache; private readonly IDalService _dalService; private readonly IUserService _userService; public ApprovalService(ILogger logger, IMemoryCache cache, IDalService dalService, IUserService userService) { _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); } public async Task CreateApproval(Approval approval) { try { _logger.LogInformation("Attempting to generate new Approval"); if (approval is null) throw new ArgumentNullException("Approval cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, "); queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID) "); queryBuilder.Append($"values ({approval.IssueID}, '{approval.RoleName}', '{approval.SubRole}', {approval.UserID}, "); queryBuilder.Append($"{approval.SubRoleID}, 0, '{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"3, 0, {approval.Step}, {approval.TaskID});"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("Unable to insert approval in database"); await GetApprovalsForIssueId(approval.IssueID, true); } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to create new Approval. Exception: {ex.Message}"); throw; } } public async Task> GetApprovalsForIssueId(int issueId, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all approvals for issue {issueId}"); if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID"); IEnumerable? approvals = new List(); if (!bypassCache) approvals = _cache.Get>($"approvals{issueId}"); if (approvals is null || approvals.Count() == 0) { StringBuilder queryBuilder = new(); queryBuilder.Append("select a.*, src.SubRoleCategoryItem from Approval a "); queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID "); queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); queryBuilder.Append($"where a.IssueID={issueId}"); approvals = (await _dalService.QueryAsync(queryBuilder.ToString())).ToList(); foreach (Approval approval in approvals) { int successfulUpdates = 0; User? user = await _userService.GetUserByUserId(approval.UserID); if (user is not null) { approval.User = user; successfulUpdates++; } if (approval.ItemStatus < 0) approval.StatusMessage = "Denied"; if (approval.ItemStatus == 0) approval.StatusMessage = "Assigned"; if (approval.ItemStatus > 0) approval.StatusMessage = "Approved"; } _cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(5)); } return approvals; } catch (Exception ex) { _logger.LogError($"Unable to fetch approvals for issue {issueId}, because {ex.Message}"); throw; } } public async Task GetRoleIdForRoleName(string roleName) { try { _logger.LogInformation($"Attempting to get role ID by name"); if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Role name cannot be null or empty"); int roleId = _cache.Get($"role{roleName}"); if (roleId <= 0) { string sql = $"select RoleID from Role where RoleName = '{roleName}'"; roleId = (await _dalService.QueryAsync(sql)).ToList().FirstOrDefault(); if (roleId > 0) _cache.Set($"role{roleName}", roleId, DateTimeOffset.Now.AddDays(1)); } if (roleId <= 0) throw new Exception($"Unable to find role with name {roleName}"); return roleId; } catch (Exception ex) { _logger.LogError($"Unable to find role ID, because {ex.Message}"); throw; } } public async Task> GetSubRolesForSubRoleName(string subRoleName, int roleId) { try { _logger.LogInformation($"Attempting to get sub role ID by name for role ID {roleId}"); if (string.IsNullOrWhiteSpace(subRoleName)) throw new ArgumentException("sub role name cannot be null or empty"); if (roleId <= 0) throw new ArgumentException($"{roleId} is not a valid role ID"); IEnumerable? subRoles = _cache.Get>($"subRoles{subRoleName}"); if (subRoles is null || subRoles.Count() <= 0) { StringBuilder queryBuilder = new(); queryBuilder.Append("select src.SubRoleCategoryID, sr.SubRole as SubRoleName, src.SubRoleCategoryItem, sr.SubRoleID "); queryBuilder.Append("from SubRole sr join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}' and sr.Inactive=0"); subRoles = (await _dalService.QueryAsync(queryBuilder.ToString())).ToList(); if (subRoles is not null && subRoles.Count() > 0) _cache.Set($"subRole{subRoleName}", subRoles, DateTimeOffset.Now.AddDays(1)); } if (subRoles is null || subRoles.Count() <= 0) throw new Exception($"Unable to find sub role with name {subRoleName} for role {roleId}"); return subRoles; } catch (Exception ex) { _logger.LogError($"Unable to find sub roles, because {ex.Message}"); throw; } } public async Task> GetApprovalGroupMembers(int subRoleId) { try { _logger.LogInformation($"Attempting to get members of sub role {subRoleId}"); if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID"); List? members = _cache.Get>($"approvalMembers{subRoleId}"); if (members is null || members.Count() <= 0) { IEnumerable? memberIds = _cache.Get>($"approvalMemberIds{subRoleId}"); if (memberIds is null) { string sql = $"select UserID from UserSubRole where SubRoleID = {subRoleId};"; memberIds = await _dalService.QueryAsync(sql); if (memberIds is null || memberIds.Count() <= 0) throw new Exception($"No members found in sub role {subRoleId}"); _cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddMinutes(5)); } members = new(); foreach (int id in memberIds) { User member = await _userService.GetUserByUserId(id); members.Add(member); } if (members.Count() <= 0) throw new Exception("No users found with IDs matching those found in SubRole"); _cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(5)); } return members; } catch (Exception ex) { _logger.LogError($"Unable to get sub role {subRoleId} members, because {ex.Message}"); throw; } } public async Task> GetApprovalsForUserId(int userId, bool bypassCache) { try { _logger.LogInformation($"Attempting to get approvals for user ID {userId}"); if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID"); IEnumerable? approvals = null; if (!bypassCache) approvals = _cache.Get>($"approvalMembers{userId}"); if (approvals is null) { StringBuilder queryBuilder = new(); queryBuilder.Append($"select a.*, src.SubRoleCategoryItem from Approval a "); queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID "); queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); queryBuilder.Append($"where UserID={userId} and "); queryBuilder.Append($"((CompletedDate >= '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}') or "); queryBuilder.Append($"(CompletedDate is null));"); string sql = queryBuilder.ToString(); approvals = (await _dalService.QueryAsync(sql)).ToList(); _cache.Set($"approvalMembers{userId}", approvals, DateTimeOffset.Now.AddHours(1)); } return approvals; } catch (Exception ex) { _logger.LogError($"Unable to get approvals for user ID {userId}, because {ex.Message}"); throw; } } public async Task UpdateApproval(Approval approval) { try { _logger.LogInformation("Attempting to update an approval"); if (approval is null) throw new ArgumentNullException("Approval cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); queryBuilder.Append($"ItemStatus={Convert.ToInt32(approval.ItemStatus)}, Step={approval.Step}, "); queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"CompletedDate='{approval.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"Comments='{approval.Comments.Replace("'", "''")}', "); queryBuilder.Append($"TaskID={approval.TaskID} "); queryBuilder.Append($"where ApprovalID={approval.ApprovalID};"); int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsUpdated <= 0) throw new Exception("Unable to update approval in database"); } catch (Exception ex) { _logger.LogError($"Approval update failed, because {ex.Message}"); throw; } } public async Task Approve(Approval approval) { try { _logger.LogInformation("Attempting to submit approval"); if (approval is null) throw new ArgumentNullException("Approval cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); queryBuilder.Append($"ItemStatus=1, Step={approval.Step}, "); if (approval.NotifyDate < DateTimeUtilities.MIN_DT) approval.NotifyDate = DateTimeUtilities.MIN_DT; queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"Comments='{approval.Comments}', "); queryBuilder.Append($"TaskID={approval.TaskID} "); queryBuilder.Append($"where ApprovalID={approval.ApprovalID};"); int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsUpdated <= 0) throw new Exception("Unable to submit approval in database"); } catch (Exception ex) { _logger.LogError($"Approval failed, because {ex.Message}"); throw; } } public async Task Deny(Approval approval) { try { _logger.LogInformation("Attempting to deny approval"); if (approval is null) throw new ArgumentNullException("Approval cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); queryBuilder.Append($"ItemStatus=-1, Step={approval.Step}, "); if (approval.NotifyDate < DateTimeUtilities.MIN_DT) approval.NotifyDate = DateTimeUtilities.MIN_DT; queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"Comments='{approval.Comments}', "); queryBuilder.Append($"TaskID={approval.TaskID} "); queryBuilder.Append($"where ApprovalID={approval.ApprovalID};"); int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsUpdated <= 0) throw new Exception("Unable to deny approval in database"); } catch (Exception ex) { _logger.LogError($"Approval denial failed, because {ex.Message}"); throw; } } }