MRB webassembly

This commit is contained in:
Chase Tucker
2024-05-13 14:33:27 -07:00
parent ba8d92ea01
commit 9b7e3ef897
109 changed files with 11731 additions and 1024 deletions

View File

@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.6",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

View File

@ -0,0 +1,20 @@
using System.Net.Mail;
namespace MesaFabApproval.API.Clients;
public interface ISmtpClientWrapper {
void Send(MailMessage message);
}
public class SmtpClientWrapper : ISmtpClientWrapper {
private SmtpClient _client;
public SmtpClientWrapper(SmtpClient client) {
_client = client ??
throw new ArgumentNullException("SmtpClient not injected");
}
public void Send(MailMessage message) {
_client.Send(message);
}
}

View File

@ -0,0 +1,401 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class ApprovalController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly IApprovalService _approvalService;
private readonly IMonInWorkerClient _monInClient;
public ApprovalController(ILogger<ApprovalController> logger, IApprovalService approvalService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpPost]
[Route("approval")]
public async Task<IActionResult> CreateApproval(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
await _approvalService.CreateApproval(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new approval, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateApproval";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/issue")]
public async Task<IActionResult> GetApprovalsForIssueId(int issueId, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get approvals for issue {issueId}");
if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(issueId, bypassCache);
return Ok(approvals);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approvals, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsForIssueId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/user")]
public async Task<IActionResult> GetApprovalsForUserId(int userId, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
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<Approval> approvals = await _approvalService.GetApprovalsForUserId(userId, bypassCache);
return Ok(approvals);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approvals, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsForUserId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/members")]
public async Task<IActionResult> GetApprovalGroupMembers(int subRoleId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get approval group members for group {subRoleId}");
if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID");
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRoleId);
return Ok(members);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approval group members, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsGroupMembers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval")]
public async Task<IActionResult> UpdateApproval(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to update approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.UpdateApproval(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update approval, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateApproval";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval/approve")]
public async Task<IActionResult> Approve(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"attempting to submit approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.Approve(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot approve, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Approve";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval/deny")]
public async Task<IActionResult> Deny(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"attempting to deny approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.Deny(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Approval denial failed, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Deny";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/roleId")]
public async Task<IActionResult> GetRoleIdForRoleName(string roleName) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get role ID by role name");
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("role name cannot be null or empty");
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
return Ok(roleId);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get role ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetRoleIdForRoleName";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/subRoles")]
public async Task<IActionResult> GetSubRolesForSubRoleName(string subRoleName, int roleId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get sub roles by sub role name");
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<SubRole> subRoles = await _approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
return Ok(subRoles);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get role ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetSubRoleIdForSubRoleName";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,178 @@
using System.Security.Authentication;
using System.Security.Principal;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using MesaFabApprovalAPI.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[AllowAnonymous]
public class AuthenticationController : ControllerBase {
private readonly ILogger<AuthenticationController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IAuthenticationService _authenticationService;
public AuthenticationController(ILogger<AuthenticationController> logger,
IMonInWorkerClient monInClient,
IAuthenticationService authenticationService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_authenticationService = authenticationService ??
throw new ArgumentNullException("IAuthenticationService not injected");
}
[HttpPost]
[Route("auth/login")]
public async Task<IActionResult> Login(AuthAttempt login) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to perform authentication");
if (login is null) throw new ArgumentNullException("Login cannot be null");
LoginResult loginResult = await _authenticationService.AuthenticateUser(login);
if (loginResult.IsAuthenticated)
return Ok(loginResult);
return Unauthorized();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot authenticate user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Login";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("auth/login/localWindows")]
public async Task<IActionResult> LoginLocalWindows(WindowsIdentity identity) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to perform local Windows authentication");
if (identity is null) throw new ArgumentNullException("identity cannot be null");
LoginResult loginResult = await _authenticationService.AttemptLocalUserAuth(identity);
if (loginResult.IsAuthenticated)
return Ok(loginResult);
return Unauthorized();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot authenticate local Windows user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "LoginLocalWindows";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("auth/refresh")]
public async Task<IActionResult> Refresh(AuthAttempt authAttempt) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to refresh auth tokens");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (authAttempt.AuthTokens is null) throw new ArgumentNullException("AuthTokens cannot be null");
LoginResult loginResult = await _authenticationService.RefreshAuthTokens(authAttempt);
return Ok(loginResult);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (AuthenticationException ex) {
_logger.LogInformation($"Unable to refresh tokens, because {ex.Message}");
return Unauthorized();
} catch (Exception ex) {
isArgumentError = true;
errorMessage = $"Cannot authenticate user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "RefreshTokens";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpOptions]
[Route("auth/refresh")]
public IActionResult RefreshOptions() {
try {
_logger.LogInformation("Auth refresh options");
return Ok();
} catch (Exception ex) {
_logger.LogError($"Error in auth refresh options. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,56 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class CAController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly ICAService _caService;
private readonly IMonInWorkerClient _monInClient;
public CAController(ILogger<ApprovalController> logger, ICAService caService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_caService = caService ?? throw new ArgumentNullException("ICAService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpGet]
[Route("ca/isValidCANumber")]
public async Task<IActionResult> IsValidCANumber(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid CA#");
if (number <= 0) return Ok(false);
bool isValid = await _caService.IsValidCANumber(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot determine if {number} is a valid CA#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidCANumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,57 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class CustomerController : ControllerBase {
private readonly ILogger<CustomerController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IMemoryCache _cache;
private readonly ICustomerService _customerService;
public CustomerController(ILogger<CustomerController> logger, IMonInWorkerClient monInClient, IMemoryCache cache, ICustomerService customerService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_customerService = customerService ?? throw new ArgumentNullException("ICustomerService");
}
[HttpGet]
[Route("customer/all")]
public async Task<IActionResult> GetAllCustomerNames() {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all customer names");
IEnumerable<string> customerNames = await _customerService.GetCustomerNames();
return Ok(customerNames);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by LoginID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllCustomerNames";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,56 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class ECNController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly IECNService _ecnService;
private readonly IMonInWorkerClient _monInClient;
public ECNController(ILogger<ApprovalController> logger, IECNService ecnService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_ecnService = ecnService ?? throw new ArgumentNullException("IECNService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpGet]
[Route("ecn/isValidEcnNumber")]
public async Task<IActionResult> IsValidEcnNumber(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid ECN#");
if (number <= 0) return Ok(false);
bool isValid = await _ecnService.IsValidECNNumber(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot determine if {number} is a valid ECN#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidEcnNumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,910 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class MRBController : ControllerBase {
private readonly ILogger<MRBController> _logger;
private readonly IMRBService _mrbService;
private readonly IMonInWorkerClient _monInClient;
public MRBController(ILogger<MRBController> logger, IMRBService mrbService, IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_mrbService = mrbService ?? throw new ArgumentNullException("IMRBService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpPost]
[Route("mrb/new")]
public async Task<IActionResult> CreateNewMRB(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.CreateNewMRB(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new MRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateNewMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrb/delete")]
public async Task<IActionResult> DeleteMRB(int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to delete MRB# {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
await _mrbService.DeleteMRB(mrbNumber);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete MRB {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/all")]
public async Task<IActionResult> GetAllMRBs(bool bypassCache) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all MRBs");
IEnumerable<MRB> allMrbs = await _mrbService.GetAllMRBs(bypassCache);
return Ok(allMrbs);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all MRBs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllMRBs";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/numberIsValid")]
public async Task<IActionResult> NumberIsValid(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid MRB#");
bool isValid = await _mrbService.MRBNumberIsValid(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to determine if {number} is a valid MRB#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidMRBNumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/getById")]
public async Task<IActionResult> GetMRBById(int id, bool bypassCache = false) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an MRB by Id");
if (id <= 0) throw new ArgumentException("Invalid MRB number");
MRB mrb = await _mrbService.GetMRBById(id, bypassCache);
return Ok(mrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB by Id, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBbyId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/getByTitle")]
public async Task<IActionResult> GetMRBByTitle(string title, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an MRB by Title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
MRB mrb = await _mrbService.GetMRBByTitle(title, bypassCache);
return Ok(mrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB by title, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBbyTitle";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("mrb")]
public async Task<IActionResult> UpdateMRB(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.UpdateMRB(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update MRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrbAction")]
public async Task<IActionResult> CreateMRBAction(MRBAction mrbAction) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new MRB");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
await _mrbService.CreateMRBAction(mrbAction);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateNewMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrbAction")]
public async Task<IActionResult> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all MRBs");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAction> mrbActions = await _mrbService.GetMRBActionsForMRB(mrbNumber, bypassCache);
return Ok(mrbActions);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all MRBs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionsForMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("mrbAction")]
public async Task<IActionResult> UpdateMRBAction(MRBAction mrbAction) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
await _mrbService.UpdateMRBAction(mrbAction);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrbAction")]
public async Task<IActionResult> DeleteMRBAction(int mrbActionID, int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to delete MRB action {mrbActionID}");
if (mrbActionID <= 0) throw new ArgumentException($"{mrbActionID} is not a valid MRB ActionID");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRBNumber");
await _mrbService.DeleteMRBAction(mrbActionID, mrbNumber);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/attachments")]
public async Task<IActionResult> GetAttachmentsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get MRB attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
List<MRBAttachment> attachments = (await _mrbService.GetAllAttachmentsForMRB(mrbNumber, bypassCache)).ToList();
return Ok(attachments);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get attachments for MRB {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/action/attachments")]
public async Task<IActionResult> GetActionAttachmentsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get action attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
List<MRBActionAttachment> attachments =
(await _mrbService.GetAllActionAttachmentsForMRB(mrbNumber, bypassCache)).ToList();
return Ok(attachments);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get action attachments for MRB# {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/attach")]
public async Task<IActionResult> SaveMRBAttachment([FromForm] IEnumerable<IFormFile> files, int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to save MRB attachments");
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<UploadResult> uploadResults = (await _mrbService.UploadAttachments(files, mrbNumber)).ToList();
return Ok(uploadResults);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot save MRB attachments, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "SaveMRBAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/action/attach")]
public async Task<IActionResult> SaveMRBActionAttachment([FromForm] IEnumerable<IFormFile> files, int actionId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to save MRB action attachments");
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
IEnumerable<UploadResult> uploadResults = (await _mrbService.UploadActionAttachments(files, actionId)).ToList();
return Ok(uploadResults);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot save MRB action attachments, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "SaveMRBActionAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[AllowAnonymous]
[HttpGet]
[Route("mrb/attachmentFile")]
public async Task<IActionResult> GetMRBAttachmentFile(string path, string fileName) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get MRB attachment file");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (!System.IO.File.Exists(path)) throw new ArgumentException("No file exists at provided path");
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("Filename cannot be null or empty");
byte[] fs = System.IO.File.ReadAllBytes(path);
const string defaultContentType = "application/octet-stream";
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
contentType = defaultContentType;
}
return new FileContentResult(fs, contentType) {
FileDownloadName = fileName
};
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB attachment file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBAttachmentFile";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrb/attach")]
public async Task<IActionResult> DeleteMRBAttachment(MRBAttachment attachment) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to delete MRB attachment");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
await _mrbService.DeleteAttachment(attachment);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB attachment file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRBAttachment";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/new-approvals")]
public async Task<IActionResult> NotifyNewApprovals(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify new approvers");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.NotifyNewApprovals(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify new approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyNewMRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/approvers")]
public async Task<IActionResult> NotifyApprovers(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
await _mrbService.NotifyApprovers(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyMRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/originator")]
public async Task<IActionResult> NotifyOriginator(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify originator");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _mrbService.NotifyOriginator(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify originator, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyMRBOriginator";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/qa-pre-approver")]
public async Task<IActionResult> NotifyQAPreApprover(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify QA pre approver");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _mrbService.NotifyQAPreApprover(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify QA pre approver, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyQAPreApprover";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,264 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class PCRBController : ControllerBase {
private readonly ILogger<MRBController> _logger;
private readonly IPCRBService _pcrbService;
private readonly IMonInWorkerClient _monInClient;
public PCRBController(ILogger<MRBController> logger, IPCRBService pcrbService, IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_pcrbService = pcrbService ?? throw new ArgumentNullException("IPCRBService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpPost]
[Route("pcrb")]
public async Task<IActionResult> CreateNewPCRB(PCRB pcrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
await _pcrbService.CreateNewPCRB(pcrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new PCRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateNewPCRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("pcrb/all")]
public async Task<IActionResult> GetAllPCRBs(bool bypassCache) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all PCRBs");
IEnumerable<PCRB> allPCRBs = await _pcrbService.GetAllPCRBs(bypassCache);
return Ok(allPCRBs);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all PCRBs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllPCRBs";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("pcrb/getByTitle")]
public async Task<IActionResult> GetPCRBByTitle(string title, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an PCRB by Title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
PCRB pcrb = await _pcrbService.GetPCRBByTitle(title, bypassCache);
return Ok(pcrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get PCRB by title, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBbyTitle";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("pcrb/getByPlanNumber")]
public async Task<IActionResult> GetPCRBByPlanNumber(int planNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an PCRB by Title");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");
PCRB pcrb = await _pcrbService.GetPCRBByPlanNumber(planNumber, bypassCache);
return Ok(pcrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get PCRB by plan number, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBbyPlanNumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("pcrb")]
public async Task<IActionResult> UpdatePCRB(PCRB pcrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update a PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
await _pcrbService.UpdatePCRB(pcrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update PCRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdatePCRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("pcrb")]
public async Task<IActionResult> DeletePCRB(int planNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to delete PCRB# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");
await _pcrbService.DeletePCRB(planNumber);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete PCRB# {planNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeletePCRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,223 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Controllers;
[ApiController]
public class UserController : ControllerBase {
private readonly ILogger<UserController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
public UserController(ILogger<UserController> logger,
IMonInWorkerClient monInClient,
IMemoryCache cache,
IUserService userService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
[HttpGet]
[Route("/user/loginId")]
[Authorize]
public async Task<IActionResult> GetUserByLoginId(string loginId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get user by LoginID");
if (string.IsNullOrWhiteSpace(loginId))
throw new ArgumentException("LoginID cannot be null or empty");
User? user = _cache.Get<User>($"user{loginId}");
if (user is null) {
user = await _userService.GetUserByLoginId(loginId);
_cache.Set($"user{loginId}", user, DateTimeOffset.Now.AddDays(1));
}
if (user is not null) return Ok(user);
throw new Exception($"User with LoginID {loginId} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by LoginID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetUserByLoginId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/user/userId")]
[Authorize]
public async Task<IActionResult> GetUserByUserId(int userId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get user by LoginID");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
User? user = _cache.Get<User>($"user{userId}");
if (user is null) {
user = await _userService.GetUserByUserId(userId);
_cache.Set($"user{userId}", user, DateTimeOffset.Now.AddDays(1));
}
if (user is not null) return Ok(user);
throw new Exception($"User with UserID {userId} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by User ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetUserByUserId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/users/active")]
[Authorize]
public async Task<IActionResult> GetAllActiveUsers() {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all active users");
IEnumerable<User>? activeUsers = _cache.Get<IEnumerable<User>>($"activeUsers");
if (activeUsers is null) {
activeUsers = await _userService.GetAllActiveUsers();
_cache.Set($"activeUsers", activeUsers, DateTimeOffset.Now.AddDays(1));
}
if (activeUsers is not null) return Ok(activeUsers);
throw new Exception($"No active users found");
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all active users, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllActiveUsers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/approver")]
[Authorize]
public async Task<IActionResult> GetApproverUserIdsForSubRoleCategoryItem(string subRoleCategoryItem) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get approver user IDs");
if (string.IsNullOrWhiteSpace(subRoleCategoryItem))
throw new ArgumentException("SubRoleCategoryItem cannot be null or empty");
IEnumerable<int>? approverUserIds = _cache.Get<IEnumerable<int>>($"approvers{subRoleCategoryItem}");
if (approverUserIds is null) {
approverUserIds = await _userService.GetApproverUserIdsBySubRoleCategoryItem(subRoleCategoryItem);
_cache.Set($"approvers{subRoleCategoryItem}", approverUserIds, DateTimeOffset.Now.AddDays(1));
}
if (approverUserIds is not null) return Ok(approverUserIds);
throw new Exception($"Approvers for SubRoleCategoryItem {subRoleCategoryItem} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approver user IDs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApproverUserIds";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AuthenticateUser(MesaFabApproval.Shared.Models.AuthAttempt)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")]
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AttemptLocalUserAuth(System.Security.Principal.WindowsIdentity)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")]

View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
<PackageReference Include="dotenv.net" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.3" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.12" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MesaFabApproval.Shared\MesaFabApproval.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.DirectoryServices.AccountManagement">
<HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.DirectoryServices.AccountManagement.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,145 @@
using MesaFabApproval.Shared.Services;
using NLog.Web;
using MesaFabApprovalAPI.Services;
using Microsoft.OpenApi.Models;
using dotenv.net;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using MesaFabApproval.API.Services;
using NLog.Extensions.Logging;
using MesaFabApproval.API.Clients;
using System.Net.Mail;
using Microsoft.AspNetCore.HttpLogging;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
DotEnv.Load();
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
builder.Logging.AddNLog();
/*builder.Services.AddHttpLogging(o => {
o.LoggingFields = HttpLoggingFields.All;
o.RequestHeaders.Add("bearer");
o.MediaTypeOptions.AddText("application/javascript");
o.RequestBodyLogLimit = 4096;
o.ResponseBodyLogLimit = 4096;
o.CombineLogs = true;
});*/
builder.Services.AddMemoryCache();
string jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ??
throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found");
string jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ??
throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found");
string jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ??
throw new ArgumentNullException("FabApprovalJwtKey environment variable not found");
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
ValidIssuer = jwtIssuer,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization(options => {
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
builder.Services.AddHttpClient();
builder.Services.AddCors(options => {
options.AddDefaultPolicy(options => {
options.AllowAnyOrigin();
options.AllowAnyMethod();
options.AllowAnyHeader();
});
});
builder.Services.AddScoped<IDbConnectionService, DbConnectionService>();
builder.Services.AddScoped<IDalService, DalService>();
builder.Services.AddScoped<SmtpClient>((serviceProvider) => {
return new SmtpClient("mailrelay-external.infineon.com");
});
builder.Services.AddScoped<ISmtpClientWrapper, SmtpClientWrapper>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<ICAService, CAService>();
builder.Services.AddScoped<IECNService, ECNService>();
builder.Services.AddScoped<ISmtpService, SmtpService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IMonInWorkerClient, MonInWorkerClient>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<IMRBService, MRBService>();
builder.Services.AddScoped<IPCRBService, PCRBService>();
builder.Services.AddScoped<IApprovalService, ApprovalService>();
builder.Services.AddControllers();
#if DEBUG
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Mesa Fab Approval API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
#endif
WebApplication app = builder.Build();
app.UseCors();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// app.UseHttpLogging();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
NLog.LogManager.Flush();
NLog.LogManager.Shutdown();

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net8.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>852e528d-015a-43b5-999d-f281e3359e5e</ProjectGuid>
<SelfContained>true</SelfContained>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,320 @@
using System.Text;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IApprovalService {
Task<int> GetRoleIdForRoleName(string roleName);
Task<IEnumerable<SubRole>> GetSubRolesForSubRoleName(string subRoleName, int roleId);
Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId);
Task CreateApproval(Approval approval);
Task UpdateApproval(Approval approval);
Task Approve(Approval approval);
Task Deny(Approval approval);
Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache);
Task<IEnumerable<Approval>> GetApprovalsForUserId(int userId, bool bypassCache);
}
public class ApprovalService : IApprovalService {
private readonly ILogger<ApprovalService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
private readonly IUserService _userService;
public ApprovalService(ILogger<ApprovalService> 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, '{DateTime.Now.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<IEnumerable<Approval>> 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<Approval>? approvals = new List<Approval>();
if (!bypassCache)
approvals = _cache.Get<IEnumerable<Approval>>($"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<Approval>(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<int> 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<int>($"role{roleName}");
if (roleId <= 0) {
string sql = $"select RoleID from Role where RoleName = '{roleName}'";
roleId = (await _dalService.QueryAsync<int>(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<IEnumerable<SubRole>> 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<SubRole>? subRoles = _cache.Get<IEnumerable<SubRole>>($"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}'");
subRoles = (await _dalService.QueryAsync<SubRole>(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<IEnumerable<User>> 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<User>? members = _cache.Get<List<User>>($"approvalMembers{subRoleId}");
if (members is null || members.Count() <= 0) {
IEnumerable<int>? memberIds = _cache.Get<IEnumerable<int>>($"approvalMemberIds{subRoleId}");
if (memberIds is null) {
string sql = $"select UserID from UserSubRole where SubRoleID = {subRoleId};";
memberIds = await _dalService.QueryAsync<int>(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.AddHours(1));
}
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.AddHours(1));
}
return members;
} catch (Exception ex) {
_logger.LogError($"Unable to get sub role {subRoleId} members, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<Approval>> 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<Approval>? approvals = null;
if (!bypassCache) approvals = _cache.Get<IEnumerable<Approval>>($"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<Approval>(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}', ");
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;
}
}
}

View File

@ -0,0 +1,254 @@
using System.DirectoryServices.AccountManagement;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens;
namespace MesaFabApprovalAPI.Services;
public interface IAuthenticationService {
public Task<LoginResult> AuthenticateUser(AuthAttempt login);
public Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity);
public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles);
public Task<LoginResult> RefreshAuthTokens(AuthAttempt authAttempt);
}
public class AuthenticationService : IAuthenticationService {
private readonly ILogger<AuthenticationService> _logger;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
private readonly string _jwtIssuer;
private readonly string _jwtAudience;
private readonly string _jwtKey;
public AuthenticationService(ILogger<AuthenticationService> logger, IMemoryCache cache, IUserService userService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ??
throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found");
_jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ??
throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found");
_jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ??
throw new ArgumentNullException("FabApprovalJwtKey environment variable not found");
}
public async Task<LoginResult> AuthenticateUser(AuthAttempt login) {
try {
_logger.LogInformation("Attempting to authenticate user");
if (login is null) throw new ArgumentNullException("Login cannot be null");
string domain = "infineon.com";
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain)) {
bool isValid = pc.ValidateCredentials(login.LoginID, login.Password);
if (isValid) {
User? user = _cache.Get<User>($"user{login.LoginID}");
if (user is null) {
user = await _userService.GetUserByLoginId(login.LoginID);
_cache.Set<User>($"user{login.LoginID}", user, DateTimeOffset.Now.AddDays(1));
}
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthTokens tokens = GenerateAuthTokens(login, roles);
return new LoginResult {
IsAuthenticated = true,
AuthTokens = tokens,
User = user
};
} else {
return new LoginResult() {
IsAuthenticated= false,
AuthTokens = new() {
JwtToken = "",
RefreshToken = ""
},
User = null
};
}
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to authenticate user. Exception: {ex.Message}");
throw;
}
}
public async Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity) {
try {
_logger.LogInformation("Attempting to authenticate local Windows system user");
if (identity is null) throw new ArgumentNullException("WindowsIdentity cannot be null");
User user = await _userService.GetUserByLoginId(identity.Name);
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthAttempt authAttempt = new() {
LoginID = user.LoginID,
};
AuthTokens tokens = GenerateAuthTokens(authAttempt, roles);
return new LoginResult {
IsAuthenticated = true,
AuthTokens = tokens,
User = user
};
} catch (Exception ex) {
_logger.LogError($"Unable to authenticate local Windows system user, because {ex.Message}");
throw;
}
}
public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles) {
try {
_logger.LogInformation("Attempting to generate JWT");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (string.IsNullOrWhiteSpace(authAttempt.LoginID)) throw new ArgumentException("UserName cannot be null or empty");
if (roles is null) throw new ArgumentNullException("roles cannot be null");
byte[] key = Encoding.ASCII.GetBytes(_jwtKey);
List<Claim> claims = new() {
new Claim(nameof(authAttempt.LoginID), authAttempt.LoginID)
};
foreach (string role in roles) {
claims.Add(new Claim(ClaimTypes.Role, role));
}
ClaimsIdentity identity = new ClaimsIdentity(claims);
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor {
Issuer = _jwtIssuer,
Audience = _jwtAudience,
Subject = identity,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
JwtSecurityToken token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
string jwt = tokenHandler.WriteToken(token);
string refreshToken = GenerateRefreshToken();
List<string>? refreshTokensForUser = _cache.Get<List<string>>(authAttempt.LoginID);
if (refreshTokensForUser is null)
refreshTokensForUser = new List<string>();
if (refreshTokensForUser.Count > 9)
refreshTokensForUser.RemoveRange(9, refreshTokensForUser.Count - 9);
refreshTokensForUser.Insert(0, refreshToken);
_cache.Set<List<string>>(authAttempt.LoginID, refreshTokensForUser, DateTimeOffset.Now.AddHours(4));
return new AuthTokens {
JwtToken = jwt,
RefreshToken = refreshToken
};
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to generate JWT. Exception: {ex.Message}");
throw;
}
}
public async Task<LoginResult> RefreshAuthTokens(AuthAttempt authAttempt) {
try {
_logger.LogInformation("Attempting to refresh auth tokens");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (authAttempt.AuthTokens is null) throw new ArgumentNullException("AuthTokens cannot be null");
bool refreshTokenIsValid = IsRefreshTokenValid(authAttempt.LoginID, authAttempt.AuthTokens.RefreshToken);
if (refreshTokenIsValid) {
User? user = _cache.Get<User>($"user{authAttempt.LoginID}");
if (user is null) {
user = await _userService.GetUserByLoginId(authAttempt.LoginID);
_cache.Set<User>($"user{authAttempt.LoginID}", user, DateTimeOffset.Now.AddDays(1));
}
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthTokens refreshedTokens = GenerateAuthTokens(authAttempt, roles);
LoginResult loginResult = new LoginResult() {
IsAuthenticated = true,
AuthTokens = refreshedTokens,
User = user
};
return loginResult;
} else {
throw new AuthenticationException("Invalid refresh token");
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to refresh auth tokens. Exception: {ex.Message}");
throw;
}
}
private string GenerateRefreshToken() {
byte[] randomNumber = new byte[32];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) {
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
private bool IsRefreshTokenValid(string loginId, string refreshToken) {
try {
_logger.LogInformation("Attempting to determine if refresh token is valid");
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("LoginID cannot be null or empty");
if (string.IsNullOrWhiteSpace(refreshToken))
throw new ArgumentNullException("Refresh token cannot be null or empty");
List<string>? cachedRefreshTokensForUser = _cache.Get<List<string>>(loginId);
if (cachedRefreshTokensForUser is null || !cachedRefreshTokensForUser.Contains(refreshToken)) {
_logger.LogInformation($"Could not find cached refresh tokens for user {loginId}");
return false;
}
return true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to validate refresh token. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface ICAService {
Task<bool> IsValidCANumber(int number);
}
public class CAService : ICAService {
private readonly ILogger<CAService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
public CAService(ILogger<CAService> logger, IMemoryCache cache, IDalService dalService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
}
public async Task<bool> IsValidCANumber(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid CA#");
if (number <= 0) return false;
IEnumerable<int> caNumbers = _cache.Get<IEnumerable<int>>("caNumbers") ?? new HashSet<int>();
if (caNumbers.Contains(number)) return true;
string sql = $"select count(CANo) as count from _8DCorrectiveAction where CANo={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
caNumbers.Append(number);
_cache.Set("caNumbers", caNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid CA#, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface ICustomerService {
Task<IEnumerable<string>> GetCustomerNames();
}
public class CustomerService : ICustomerService {
private readonly ILogger<CustomerService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
public CustomerService(ILogger<CustomerService> logger, IDalService dalService, IMemoryCache cache) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
}
public async Task<IEnumerable<string>> GetCustomerNames() {
try {
_logger.LogInformation("Attempting to get customer names");
IEnumerable<string>? customerNames = _cache.Get<IEnumerable<string>>("allCustomerNames");
if (customerNames is null) {
string sql = "select ProductFamily from ProductFamilies;";
customerNames = (await _dalService.QueryAsync<string>(sql)).ToList();
}
return customerNames;
} catch (Exception ex) {
_logger.LogError($"Unable to get customer names, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,89 @@
using System.Data;
using Dapper;
namespace MesaFabApproval.API.Services;
public interface IDalService {
Task<IEnumerable<T>> QueryAsync<T>(string sql);
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 ILogger<DalService> _logger;
private readonly IDbConnectionService _dbConnectionService;
public DalService(IDbConnectionService dbConnectionService, ILogger<DalService> logger) {
_dbConnectionService = dbConnectionService ??
throw new ArgumentNullException("IDbConnectionService not injected");
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
}
public async Task<IEnumerable<T>> QueryAsync<T>(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
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();
try {
_logger.LogInformation($"Attempting to perform query with {sql}. Remaining retries: {remainingRetries}");
using (IDbConnection conn = _dbConnectionService.GetConnection()) {
result = await conn.QueryAsync<T>(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred while attempting to perform a query. Exception: {ex.Message}");
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return result;
}
public async Task<int> ExecuteAsync(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
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();
try {
_logger.LogInformation($"Attempting to execute {sql}. Remaining retries: {remainingRetries}");
using (IDbConnection conn = _dbConnectionService.GetConnection()) {
rowsAffected = await conn.ExecuteAsync(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred while attempting to execute a query. Exception: {ex.Message}");
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return rowsAffected;
}
}

View File

@ -0,0 +1,22 @@
using System.Data;
using Microsoft.Data.SqlClient;
namespace MesaFabApproval.API.Services;
public interface IDbConnectionService {
IDbConnection GetConnection();
}
public class DbConnectionService : IDbConnectionService {
private readonly string _dbConnectionString;
public DbConnectionService() {
_dbConnectionString = Environment.GetEnvironmentVariable("FabApprovalDbConnectionString") ??
throw new ArgumentNullException("FabApprovalDbConnectionString environment variable not found");
}
public IDbConnection GetConnection() {
return new SqlConnection(_dbConnectionString);
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IECNService {
Task<bool> IsValidECNNumber(int number);
}
public class ECNService : IECNService {
private readonly ILogger<ECNService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
public ECNService(ILogger<ECNService> logger, IMemoryCache cache, IDalService dalService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
}
public async Task<bool> IsValidECNNumber(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid ECN#");
if (number <= 0) return false;
IEnumerable<int> ecnNumbers = _cache.Get<IEnumerable<int>>("ecnNumbers") ?? new HashSet<int>();
if (ecnNumbers.Contains(number)) return true;
string sql = $"select count(ECNNumber) as count from ECN where ECNNumber={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
ecnNumbers.Append(number);
_cache.Set("ecnNumbers", ecnNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid ECN#, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,825 @@
using System.Net;
using System.Net.Mail;
using System.Text;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IMRBService {
Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache);
Task<bool> MRBNumberIsValid(int number);
Task<MRB> GetMRBById(int id, bool bypassCache = false);
Task<MRB> GetMRBByTitle(string title, bool bypassCache);
Task CreateNewMRB(MRB mrb);
Task UpdateMRB(MRB mrb);
Task CreateMRBAction(MRBAction mrbAction);
Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache);
Task UpdateMRBAction(MRBAction mrbAction);
Task DeleteMRBAction(int mrbActionID, int mrbNumber);
Task<IEnumerable<UploadResult>> UploadAttachments(IEnumerable<IFormFile> files, int mrbNumber);
Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId);
Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task DeleteAttachment(MRBAttachment attachment);
Task NotifyNewApprovals(MRB mrb);
Task NotifyApprovers(MRBNotification notification);
Task NotifyOriginator(MRBNotification notification);
Task NotifyQAPreApprover(MRBNotification notification);
Task DeleteMRB(int mrbNumber);
}
public class MRBService : IMRBService {
private readonly ILogger<MRBService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
private readonly IApprovalService _approvalService;
private readonly ISmtpService _smtpService;
private readonly string _siteBaseUrl;
private readonly string _mrbAttachmentPath;
public MRBService(ILogger<MRBService> logger,
IDalService dalService,
IMemoryCache cache,
IUserService userService,
IApprovalService approvalService,
ISmtpService smtpService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
_smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected");
_siteBaseUrl = Environment.GetEnvironmentVariable("NewFabApprovalBaseUrl") ??
throw new ArgumentNullException("FabApprovalBaseUrl environment variable not found");
_mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ??
throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found");
}
public async Task CreateNewMRB(MRB mrb) {
try {
_logger.LogInformation("Attempting to generate new MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRB (OriginatorID, Title, SubmittedDate, ");
queryBuilder.Append("CloseDate, CancelDate, NumberOfLotsAffected, ApprovalDate, ");
queryBuilder.Append("IssueDescription, CustomerImpacted, Department, Process, Val, RMANo, ");
queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo, ");
queryBuilder.Append("CustomerImpactedName, ProcessECNNumber, Tool, Category) values (");
queryBuilder.Append($"{mrb.OriginatorID}, '{mrb.Title}', ");
if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT)
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CloseDate > DateTimeUtilities.MAX_DT)
mrb.CloseDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CancelDate > DateTimeUtilities.MAX_DT)
mrb.CancelDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"{mrb.NumberOfLotsAffected}, ");
if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT)
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"'{mrb.IssueDescription}', {Convert.ToUInt32(mrb.CustomerImpacted)}, ");
queryBuilder.Append($"'{mrb.Department}', '{mrb.Process}', {mrb.Val}, {mrb.RMANo}, '{mrb.PCRBNo}', ");
queryBuilder.Append($"{Convert.ToInt32(mrb.SpecsImpacted)}, {Convert.ToInt32(mrb.TrainingRequired)}, ");
queryBuilder.Append($"'{mrb.Status}', {mrb.StageNo}, '{mrb.CustomerImpactedName}', ");
queryBuilder.Append($"{mrb.ProcessECNNumber}, '{mrb.Tool}', '{mrb.Category}');");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("Unable to create new MRB");
mrb = await GetMRBByTitle(mrb.Title, true);
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to create new MRB. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache) {
try {
_logger.LogInformation("Attempting to get all MRBs");
IEnumerable<MRB>? allMrbs = null;
if (!bypassCache) allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID;");
allMrbs = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).ToList();
_cache.Set("allMrbs", allMrbs, DateTimeOffset.Now.AddHours(1));
}
if (allMrbs is null || allMrbs.Count() == 0)
throw new Exception("No MRBs found");
return allMrbs;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}");
throw;
}
}
public async Task<bool> MRBNumberIsValid(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid MRB#");
if (number <= 0) return false;
IEnumerable<int> mrbNumbers = _cache.Get<IEnumerable<int>>("mrbNumbers") ??
new HashSet<int>();
if (mrbNumbers.Contains(number)) return true;
string sql = $"select count(MRBNumber) as count from MRB where MRBNumber={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
mrbNumbers.Append(number);
_cache.Set("mrbNumbers", mrbNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid MRB#, because {ex.Message}");
throw;
}
}
public async Task<MRB> GetMRBById(int id, bool bypassCache = false) {
try {
_logger.LogInformation("Attempting to get an MRB by ID");
if (id < 0) throw new ArgumentException("Invalid MRB number");
MRB? mrb = null;
if (!bypassCache)
mrb = _cache.Get<MRB>($"mrb{id}");
if (mrb is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID ");
queryBuilder.Append($"where m.MRBNumber = {id}");
mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).FirstOrDefault();
_cache.Set($"mrb{id}", mrb, DateTimeOffset.Now.AddHours(1));
}
if (mrb is null) throw new Exception($"Unable to get MRB {id}");
return mrb;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task<MRB> GetMRBByTitle(string title, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get an MRB by title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
MRB? mrb = null;
if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}");
if (mrb is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID ");
queryBuilder.Append($"where m.Title = '{title}'");
mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).FirstOrDefault();
_cache.Set($"mrb{title}", mrb, DateTimeOffset.Now.AddHours(1));
}
if (mrb is null) throw new Exception($"Unable to get MRB {title}");
return mrb;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task UpdateMRB(MRB mrb) {
try {
_logger.LogInformation("Attempting to update an MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRB set OriginatorID = {mrb.OriginatorID}, ");
queryBuilder.Append($"Title = '{mrb.Title}', ");
if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT)
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CloseDate > DateTimeUtilities.MAX_DT)
mrb.CloseDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CancelDate > DateTimeUtilities.MAX_DT)
mrb.CancelDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"NumberOfLotsAffected = {mrb.NumberOfLotsAffected}, ");
if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT)
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription}', ");
queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, ");
queryBuilder.Append($"Department = '{mrb.Department}', ");
queryBuilder.Append($"Process = '{mrb.Process}', ");
queryBuilder.Append($"Val = {mrb.Val}, ");
queryBuilder.Append($"RMANo = {mrb.RMANo}, ");
queryBuilder.Append($"PCRBNo = '{mrb.PCRBNo}', ");
queryBuilder.Append($"SpecsImpacted = {Convert.ToInt32(mrb.SpecsImpacted)}, ");
queryBuilder.Append($"TrainingRequired = {Convert.ToInt32(mrb.TrainingRequired)}, ");
queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo}, ");
queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName}', ");
queryBuilder.Append($"ProcessECNNumber = '{mrb.ProcessECNNumber}', ");
queryBuilder.Append($"Tool = '{mrb.Tool}', Category = '{mrb.Category}' ");
queryBuilder.Append($"where MRBNumber = {mrb.MRBNumber};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception($"Unable to update MRB {mrb.MRBNumber}");
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.RemoveAll(m => m.MRBNumber ==mrb.MRBNumber);
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to update an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task CreateMRBAction(MRBAction mrbAction) {
try {
_logger.LogInformation("Attempting to generate new MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBAction (Action, Customer, Quantity, PartNumber, LotNumber, MRBNumber, ");
queryBuilder.Append("ConvertFrom, ConvertTo, Justification) ");
queryBuilder.Append($"values ('{mrbAction.Action}', '{mrbAction.Customer}', {mrbAction.Quantity}, ");
queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber}, ");
queryBuilder.Append($"'{mrbAction.ConvertFrom}', '{mrbAction.ConvertTo}', '{mrbAction.Justification}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("Unable to create MRB action in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to create new MRB action. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get MRB actions for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAction>? mrbActions = null;
if (!bypassCache)
_cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}");
if (mrbActions is null) {
string sql = $"select * from MRBAction where MRBNumber = {mrbNumber}";
mrbActions = (await _dalService.QueryAsync<MRBAction>(sql)).ToList();
if (mrbActions is not null) {
foreach (MRBAction action in mrbActions) {
if (action.CompletedDate < DateTime.MaxValue && action.CompletedByUserID > 0) {
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
}
}
_cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
}
if (mrbActions is null) throw new Exception($"Unable to find MRB actions for MRB {mrbNumber}");
return mrbActions;
} catch (Exception ex) {
_logger.LogError($"Unable to get MRB actions for MRB {mrbNumber}, because {ex.Message}");
throw;
}
}
public async Task UpdateMRBAction(MRBAction mrbAction) {
try {
_logger.LogInformation("Attempting to update MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRBAction set Action = '{mrbAction.Action}', ");
queryBuilder.Append($"Customer = '{mrbAction.Customer}', ");
queryBuilder.Append($"Quantity = {mrbAction.Quantity}, ");
queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber}', ");
queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber}', ");
if (mrbAction.AssignedDate < DateTimeUtilities.MIN_DT)
mrbAction.AssignedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrbAction.CompletedDate > DateTimeUtilities.MAX_DT)
mrbAction.CompletedDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID}, ");
queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom}', ");
queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo}', ");
queryBuilder.Append($"Justification='{mrbAction.Justification}' ");
queryBuilder.Append($"where ActionID={mrbAction.ActionID};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception($"There were no MRB actions found with MRB# {mrbAction.MRBNumber}");
List<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbAction.MRBNumber}")?.ToList();
if (mrbActions is not null) {
mrbActions.RemoveAll(m => m.ActionID == mrbAction.ActionID);
mrbActions.Add(mrbAction);
_cache.Set($"mrbActions{mrbAction.MRBNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
} catch (Exception ex) {
_logger.LogError($"Unable to update MRB action, because {ex.Message}");
throw;
}
}
public async Task DeleteMRBAction(int mrbActionID, int mrbNumber) {
try {
_logger.LogInformation($"Attempting to delete MRB action {mrbActionID}");
if (mrbActionID <= 0) throw new ArgumentException($"{mrbActionID} is not a valid MRBActionID");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRBNumber");
string sql = $"delete from MRBAction where ActionID = {mrbActionID};";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception($"No MRB action was found to delete for ActionID {mrbActionID}");
List<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}")?.ToList();
if (mrbActions is not null) {
mrbActions.RemoveAll(m => m.ActionID == mrbActionID);
_cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to delete MRB action {mrbActionID}. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<UploadResult>> UploadAttachments(IEnumerable<IFormFile> files, int mrbNumber) {
try {
_logger.LogInformation("Attempting to upload attachments");
List<UploadResult> uploadResults = new();
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
foreach (IFormFile file in files) {
try {
if (file is null) throw new ArgumentException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\{encodedName}";
await SaveFileToFileSystem(file, path);
await SaveAttachmentInDb(file, path, mrbNumber);
UploadResult uploadResult = new() {
UploadSuccessful = true,
FileName = file.FileName
};
uploadResults.Add(uploadResult);
} catch (Exception ex) {
UploadResult uploadResult = new() {
UploadSuccessful = false,
FileName = file.FileName,
Error = ex.Message
};
uploadResults.Add(uploadResult);
}
}
return uploadResults;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to upload attachment. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId) {
try {
_logger.LogInformation("Attempting to upload action attachments");
List<UploadResult> uploadResults = new();
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
List<Task> taskList = new();
foreach (IFormFile file in files) {
try {
if (file is null) throw new ArgumentException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{actionId}\\{encodedName}";
taskList.Add(SaveFileToFileSystem(file, path));
taskList.Add(SaveActionAttachmentInDb(file, path, actionId));
UploadResult uploadResult = new() {
UploadSuccessful = true,
FileName = file.Name
};
uploadResults.Add(uploadResult);
} catch (Exception ex) {
UploadResult uploadResult = new() {
UploadSuccessful = false,
FileName = file.Name,
Error = ex.Message
};
uploadResults.Add(uploadResult);
}
}
Task.WaitAll(taskList.ToArray());
return uploadResults;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to upload action attachment. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAttachment>? attachments = null;
if (!bypassCache)
_cache.Get<IEnumerable<MRBAttachment>>($"mrbAttachments{mrbNumber}");
if (attachments is null) {
string sql = $"select * from MRBAttachment where MRBNumber = {mrbNumber};";
attachments = (await _dalService.QueryAsync<MRBAttachment>(sql)).ToList();
_cache.Set($"mrbAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15));
}
return attachments;
} catch (Exception ex) {
StringBuilder errMsgBuilder = new();
errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB {mrbNumber}. ");
errMsgBuilder.Append($"Exception: {ex.Message}");
_logger.LogError(errMsgBuilder.ToString());
throw;
}
}
public async Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all action attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
List<MRBActionAttachment>? attachments = null;
if (!bypassCache)
attachments = _cache.Get<List<MRBActionAttachment>>($"mrbActionAttachments{mrbNumber}");
if (attachments is null) {
attachments = new List<MRBActionAttachment>();
foreach (MRBAction action in await GetMRBActionsForMRB(mrbNumber, false)) {
string sql = $"select * from MRBActionAttachment where ActionID = {action.ActionID};";
IEnumerable<MRBActionAttachment> newAttachments =
(await _dalService.QueryAsync<MRBActionAttachment>(sql)).ToList();
attachments.AddRange(newAttachments);
}
_cache.Set($"mrbActionAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15));
}
return attachments;
} catch (Exception ex) {
StringBuilder errMsgBuilder = new();
errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB action {mrbNumber}. ");
errMsgBuilder.Append($"Exception: {ex.Message}");
_logger.LogError(errMsgBuilder.ToString());
throw;
}
}
public async Task DeleteAttachment(MRBAttachment attachment) {
try {
_logger.LogInformation("Attempting to delete an attachment");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
if (!File.Exists(attachment.Path)) throw new FileNotFoundException("No file found at provided path");
File.Delete(attachment.Path);
string sql = $"delete from MRBAttachment where AttachmentID = {attachment.AttachmentID};";
int rowsDeleted = await _dalService.ExecuteAsync(sql);
if (rowsDeleted <= 0)
throw new Exception($"No attachments found in the database with attachment ID {attachment.AttachmentID}");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to delete an attachment. Exception: {ex.Message}");
throw;
}
}
public async Task NotifyNewApprovals(MRB mrb) {
try {
_logger.LogInformation("Attempting to notify approvers");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true);
List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTimeUtilities.MIN_DT).ToList();
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvalsNeedingNotification) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email)) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[New Task] Mesa Fab Approval - MRB# {mrb.MRBNumber} - {mrb.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"MRB# {mrb.MRBNumber} [{mrb.Title}] is ready for your approval. ");
bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the task.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to notify approvers, because {ex.Message}");
throw;
}
}
public async Task NotifyApprovers(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(notification.MRB.MRBNumber, true);
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvals) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email)) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyOriginator(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to originator");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
User user = await _userService.GetUserByUserId(notification.MRB.OriginatorID);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyQAPreApprover(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to QA pre approver");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
string roleName = "QA_PRE_APPROVAL";
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
SubRole? subRole = (await _approvalService.GetSubRolesForSubRoleName(roleName, roleId)).FirstOrDefault();
if (subRole is null)
throw new Exception("Unable to find QA pre approver role");
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
List<MailAddress> toAddresses = new();
foreach (User member in members)
toAddresses.Add(new MailAddress(member.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to QA pre approver, because {ex.Message}");
throw;
}
}
public async Task DeleteMRB(int mrbNumber) {
try {
_logger.LogInformation($"Attempting to delete MRB# {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
string sql = $"delete from MRB where MRBNumber={mrbNumber}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception($"MRB {mrbNumber} not deleted from database");
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.RemoveAll(m => m.MRBNumber == mrbNumber);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to delete MRB {mrbNumber}, because {ex.Message}");
throw;
}
}
private async Task SaveFileToFileSystem(IFormFile file, string path) {
try {
_logger.LogInformation($"Attempting to save file to file system");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}");
string? directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directoryPath))
Directory.CreateDirectory(directoryPath);
using (Stream stream = File.Create(path)) {
await file.CopyToAsync(stream);
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to file system. Exception: {ex.Message}");
throw;
}
}
private async Task SaveAttachmentInDb(IFormFile file, string path, int mrbNumber) {
try {
_logger.LogInformation($"Attempting to save attachment to database");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBAttachment (MRBNumber, FileName, UploadDate, Path) ");
queryBuilder.Append($"values ({mrbNumber}, '{file.FileName}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0)
throw new Exception("Unable to insert attachment in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}");
throw;
}
}
private async Task SaveActionAttachmentInDb(IFormFile file, string path, int actionId) {
try {
_logger.LogInformation($"Attempting to save action attachment to database");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBActionAttachment (ActionID, FileName, UploadDate, Path) ");
queryBuilder.Append($"values ({actionId}, '{file.FileName}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0)
throw new Exception("Unable to insert action attachment in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,214 @@
using System.Text;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IPCRBService {
public Task CreateNewPCRB(PCRB pcrb);
public Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache);
public Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache);
public Task<PCRB> GetPCRBByTitle(string title, bool bypassCache);
public Task UpdatePCRB(PCRB pcrb);
public Task DeletePCRB(int planNumber);
}
public class PCRBService : IPCRBService {
private readonly ILogger<PCRBService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
public PCRBService(ILogger<PCRBService> logger,
IDalService dalService,
IMemoryCache cache,
IUserService userService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
public async Task CreateNewPCRB(PCRB pcrb) {
try {
_logger.LogInformation("Attempting to create new PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCChangeControl (OwnerID, Title, ChangeLevel, ReasonForChange, ");
queryBuilder.Append("ChangeDescription, IsITAR, CurrentStep, InsertTimeStamp, LastUpdateDate) ");
queryBuilder.Append($"values ({pcrb.OwnerID}, '{pcrb.Title}', '{pcrb.ChangeLevel}', ");
queryBuilder.Append($"'{pcrb.ReasonForChange}', '{pcrb.ChangeDescription}', ");
queryBuilder.Append($"{Convert.ToInt32(pcrb.IsITAR)}, {pcrb.CurrentStep}, ");
queryBuilder.Append($"'{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("unable to insert new PCRB in the database");
} catch (Exception ex) {
_logger.LogError($"Unable to create new PCRB, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) {
try {
_logger.LogInformation("Attempting to get all PCRBs");
IEnumerable<PCRB>? allPCRBs = null;
if (!bypassCache) allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is null) {
string sql = "select * from CCChangeControl";
allPCRBs = (await _dalService.QueryAsync<PCRB>(sql)).ToList();
foreach (PCRB pcrb in allPCRBs) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
}
_cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1));
}
if (allPCRBs is null || allPCRBs.Count() == 0)
throw new Exception("no PCRBs found");
return allPCRBs;
} catch (Exception ex) {
_logger.LogError($"Unable to get all PCRBs, because {ex.Message}");
throw;
}
}
public async Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get a PCRB by plan#");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB#");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{planNumber}");
if (pcrb is null) {
string sql = $"select * from CCChangeControl where PlanNumber={planNumber}";
pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault();
if (pcrb is not null) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
_cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
}
}
if (pcrb is null) throw new Exception($"unable to find PCRB {planNumber}");
return pcrb;
} catch (Exception ex) {
_logger.LogError($"Unable to get PCRB by Plan #, because {ex.Message}");
throw;
}
}
public async Task<PCRB> GetPCRBByTitle(string title, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get a PCRB by title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}");
if (pcrb is null) {
string sql = $"select * from CCChangeControl where Title='{title}'";
pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault();
if (pcrb is not null) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
_cache.Set($"pcrb{title}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
}
}
if (pcrb is null) throw new Exception($"unable to find PCRB {title}");
return pcrb;
} catch (Exception ex) {
_logger.LogError($"Unable to get PCRB by title, because {ex.Message}");
throw;
}
}
public async Task UpdatePCRB(PCRB pcrb) {
try {
_logger.LogInformation($"Attempting to update PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCChangeControl set OwnerID={pcrb.OwnerID}, ");
queryBuilder.Append($"Title='{pcrb.Title}', ChangeLevel='{pcrb.ChangeLevel}', ");
queryBuilder.Append($"CurrentStep={pcrb.CurrentStep}, ReasonForChange='{pcrb.ReasonForChange}', ");
queryBuilder.Append($"ChangeDescription='{pcrb.ChangeDescription}', ");
queryBuilder.Append($"IsITAR={Convert.ToInt32(pcrb.IsITAR)}, ");
queryBuilder.Append($"InsertTimeStamp='{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"LastUpdateDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' ");
queryBuilder.Append($"where PlanNumber={pcrb.PlanNumber}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("unable to perform update in database");
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber);
pcrbList.Add(pcrb);
_cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to update PCRB, because {ex.Message}");
throw;
}
}
public async Task DeletePCRB(int planNumber) {
try {
_logger.LogInformation($"Attempting to delete PCRB {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan #");
string sql = $"delete from CCChangeControl where PlanNumber={planNumber}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception("delete operation failed in database");
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == planNumber);
_cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to delete PCRB {planNumber}, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,79 @@
using MesaFabApproval.API.Clients;
using Microsoft.IdentityModel.Tokens;
using System.Net.Mail;
namespace MesaFabApproval.API.Services;
public interface ISmtpService {
Task<bool> SendEmail(IEnumerable<MailAddress> recipients, IEnumerable<MailAddress> ccRecipients, string subject, string body);
}
public class SmtpService : ISmtpService {
private readonly ILogger<SmtpService> _logger;
private readonly ISmtpClientWrapper _smtpClient;
private readonly bool _shouldSendEmail;
public SmtpService(ILogger<SmtpService> logger, ISmtpClientWrapper smtpClient) {
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
_smtpClient = smtpClient ??
throw new ArgumentNullException("SmtpClient not injected");
if (!Boolean.TryParse(Environment.GetEnvironmentVariable("FabApprovalShouldSendEmail"), out _shouldSendEmail))
throw new ArgumentNullException("FabApprovalShouldSendEmail environment variable not found");
}
public async Task<bool> SendEmail(IEnumerable<MailAddress> recipients,
IEnumerable<MailAddress> ccRecipients,
string subject,
string body) {
if (recipients.IsNullOrEmpty()) throw new ArgumentNullException("recipients cannot be null or empty!");
if (ccRecipients is null) throw new ArgumentNullException("ccRecipients cannot be null!");
if (subject.IsNullOrEmpty()) throw new ArgumentNullException("subject cannot be null or empty!");
if (body.IsNullOrEmpty()) throw new ArgumentNullException("body cannot be null or empty!");
return await Task.Run(() => {
int maxRetries = 3;
int backoffSeconds = 30;
bool messageWasSent = false;
try {
if (_shouldSendEmail) {
int remainingRetries = maxRetries;
while (!messageWasSent && remainingRetries > 0) {
try {
Task.Delay((maxRetries - remainingRetries--) * backoffSeconds * 1000);
_logger.LogInformation($"Attempting to send notification. Remaining retries: {remainingRetries}");
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;
msg.From = new MailAddress("MesaFabApproval@infineon.com", "Mesa Fab Approval");
msg.Sender = new MailAddress("MesaFabApproval@infineon.com", "Mesa Fab Approval");
foreach (MailAddress recipient in recipients) msg.To.Add(recipient);
msg.Bcc.Add("chase.tucker@infineon.com");
foreach (MailAddress ccRecipient in ccRecipients) msg.CC.Add(ccRecipient);
msg.Subject = subject;
msg.Body = body;
_smtpClient.Send(msg);
messageWasSent = true;
} catch (Exception ex) {
_logger.LogError($"Message not sent successfully. Exception: {ex.Message}");
}
}
} else {
_logger.LogInformation("Not sending email per local configuration");
messageWasSent = true;
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to send notification. Exception: {ex.Message}");
}
return messageWasSent;
});
}
}

View File

@ -0,0 +1,147 @@
using System.Text;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IUserService {
Task<IEnumerable<User>> GetAllActiveUsers();
Task<User> GetUserByLoginId(string loginId);
Task<User> GetUserByUserId(int userId);
Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item);
}
public class UserService : IUserService {
private readonly ILogger<UserService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
public UserService(ILogger<UserService> logger, IDalService dalService, IMemoryCache cache) {
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ??
throw new ArgumentNullException("IDalService not injected");
_cache = cache ??
throw new ArgumentNullException("IMemoryCache not injected");
}
public async Task<IEnumerable<User>> GetAllActiveUsers() {
try {
_logger.LogInformation("Attempting to get all active users");
IEnumerable<User>? allActiveUsers = _cache.Get<IEnumerable<User>>("allActiveUsers");
if (allActiveUsers is null) {
string sql = "select * from Users where IsActive = 1";
allActiveUsers = (await _dalService.QueryAsync<User>(sql)).ToList();
_cache.Set("allActiveUsers", allActiveUsers, DateTimeOffset.Now.AddHours(1));
}
if (allActiveUsers is null || allActiveUsers.Count() == 0) {
throw new Exception("No users found");
}
return allActiveUsers;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get all users. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<User> GetUserByLoginId(string loginId) {
try {
_logger.LogInformation("Attempting to get user by LoginId");
if (string.IsNullOrWhiteSpace(loginId))
throw new ArgumentException("LoginId cannot be null or empty");
User? user = _cache.Get<User>($"userByLoginId{loginId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.LoginID == loginId);
if (user is null) {
string sql = $"select * from Users where LoginID = '{loginId}';";
user = (await _dalService.QueryAsync<User>(sql)).FirstOrDefault();
_cache.Set($"userByLoginId{loginId}", user, DateTimeOffset.Now.AddHours(1));
}
if (user is null) throw new Exception($"No user found with LoginID {loginId}");
return user;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get user for LoginID {loginId}. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<User> GetUserByUserId(int userId) {
try {
_logger.LogInformation("Attempting to get user by user ID");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
User? user = _cache.Get<User>($"userByUserId{userId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.UserID == userId);
if (user is null) {
string sql = $"select * from Users where UserID = '{userId}';";
user = (await _dalService.QueryAsync<User>(sql)).FirstOrDefault();
_cache.Set($"userByUserId{userId}", user, DateTimeOffset.Now.AddHours(1));
}
if (user is null) throw new Exception($"No user found with UserID {userId}");
return user;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get user for UserID {userId}. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item) {
try {
_logger.LogInformation("Attempting to get approver user IDs");
if (string.IsNullOrWhiteSpace(item)) throw new ArgumentException("SubRoleCategoryItem cannot be null or empty");
IEnumerable<int>? userIds = _cache.Get<IEnumerable<int>>($"approverUserIdsBySubRollCategory{item}");
if (userIds is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select us.UserID ");
queryBuilder.Append("from SubRole as sr ");
queryBuilder.Append("join UserSubRole as us on sr.SubRoleID=us.SubRoleID ");
queryBuilder.Append("join SubRoleCategory as sc on sr.SubRoleCategoryID=sc.SubRoleCategoryID ");
queryBuilder.Append($"where sc.SubRoleCategoryItem='{item}'");
userIds = (await _dalService.QueryAsync<int>(queryBuilder.ToString())).ToList();
_cache.Set($"approverUserIdsBySubRollCategory{item}", userIds, DateTimeOffset.Now.AddHours(1));
}
if (userIds is null || userIds.Count() == 0) {
throw new Exception($"No users found for SubRoleCategoryItem {item}");
}
return userIds;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get approver user IDs. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
}

View File

@ -0,0 +1,3 @@
{
"AllowedHosts": "*"
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets>
<target name="asyncLog" xsi:type="AsyncWrapper">
<target
name="appLog"
xsi:type="File"
fileName="d:\logs\MesaFabApproval.API\log.txt"
archiveFilename="d:\logs\MesaFabApproval.API\archive\log-${shortdate}.txt"
maxArchiveFiles="30"
archiveEvery="Day"
/>
<target
name="consoleLog"
xsi:type="Console"
/>
</target>
</targets>
<rules>
<logger name="Microsoft.*" finalMinLevel="Warn" />
<logger name="Microsoft.AspNetCore.HttpLogging.*" finalMinLevel="Info" />
<logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" />
<logger name="*" minlevel="Info" writeTo="asyncLog" />
</rules>
</nlog>