This commit is contained in:
Zeeshaun
2026-03-17 10:03:23 -05:00
parent 1a0702a79a
commit bd1a9744ad
139 changed files with 5388 additions and 0 deletions
@@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace LCU.Api.Internal.ArchiverApi.Configuration;
public class ArchiverApiConfig
{
public string SeqUrl { get; set; }
public string SeqApiKey { get; set; }
public string ApplicationName { get; set; }
public string NotificationApiEmail { get; set; }
public string FileOperationApiBulkMoveFile { get; set; }
public string FileOperationApiBuildDynamicFileList { get; set; }
public string FileOperationApiValidateFileExists { get; set; }
public string CoreApiWrapperGetApplicationHistory { get; set; }
public string DnaBasePath { get; set; }
public string ReportPathDateFormat { get; set; }
public string ErrorMonitoringTemplate { get; set; }
public string ErrorRowTemplate { get; set; }
public string ErrorMonitoringSubject { get; set; }
public List<string> ErrorRecipients { get; set; }
public string TimeZone { get; set; }
public FeatureFlags FeatureFlags { get; set; }
}
@@ -0,0 +1,8 @@
using LCU.Library.Internal.LcuMassTransitModels;
namespace LCU.Api.Internal.ArchiverApi.Configuration;
public class ReceiveEndpointConfigurations
{
public ReceiveEndpointConfiguration ArchiverReceiveEndpoint { get; set; }
public ReceiveEndpointConfiguration ResponseReceiveEndpoint { get; set; }
}
@@ -0,0 +1,7 @@
namespace LCU.Api.Internal.ArchiverApi.Configuration;
public class FeatureFlags
{
public bool RunArchiverConsumer { get; set; }
public bool RunResponseConsumer { get; set; }
}
@@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using Serilog;
using Microsoft.Extensions.DependencyInjection;
using MassTransit;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Api.Internal.ArchiverApi.Services;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Consumers;
public class ArchiverConsumer : IConsumer<ArchiveRequest>
{
private readonly IServiceScopeFactory _scopeFactory;
public ArchiverConsumer(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public async Task Consume(ConsumeContext<ArchiveRequest> context)
{
try
{
using var scope = _scopeFactory.CreateScope();
await Task.Run(() => scope.ServiceProvider.GetRequiredService<IArchiverService>().ExecuteRequest(context.Message));
}
catch (ServiceException)
{
return;
}
catch (Exception exception)
{
Log.Fatal(exception, $"Unexpected Failure");
}
}
}
@@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using Serilog;
using Microsoft.Extensions.DependencyInjection;
using MassTransit;
using LCU.Api.Internal.ArchiverApi.Services;
using ApiResponse = LCU.Library.Internal.ApiResponse.Models;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Consumers;
public class ResponseConsumer : IConsumer<ApiResponse.Response>
{
private readonly IServiceScopeFactory _scopeFactory;
public ResponseConsumer(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public async Task Consume(ConsumeContext<ApiResponse.Response> context)
{
try
{
using var scope = _scopeFactory.CreateScope();
await Task.Run(() => scope.ServiceProvider.GetRequiredService<IArchiveCompletionService>().HandleReportArchiveCompletedRequest(context.Message));
}
catch (ServiceException)
{
return;
}
catch (Exception exception)
{
Log.Fatal(exception, $"Unexpected Failure");
}
}
}
@@ -0,0 +1,140 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Serilog;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using LCU.Api.Internal.ArchiverApi.Services;
using LCU.Api.Internal.ArchiverApi.Extensions;
using LCU.Library.Internal.ArchiverApiModel.Entities;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Controllers;
[ApiController]
[Route("[controller]")]
public class ApplicationMappingController : ControllerBase
{
private readonly IApplicationMappingService _applicationMappingService;
public ApplicationMappingController(IApplicationMappingService applicationMappingService)
{
_applicationMappingService = applicationMappingService;
}
[HttpGet(Name = "GetAllJobs")]
[ProducesResponseType(typeof(IEnumerable<ApplicationMapping>), 200)]
[ProducesResponseType(500)]
public async Task<IActionResult> Get()
{
try
{
var result = await _applicationMappingService.GetAsync();
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("{id:int}", Name = "GetById")]
[ProducesResponseType(201)]
[ProducesResponseType(typeof(ApplicationMapping), 400)]
[ProducesResponseType(500)]
public async Task<IActionResult> GetById(int id)
{
try
{
var result = await _applicationMappingService.GetByIdAsync(id);
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(typeof(ApplicationMapping), 400)]
[ProducesResponseType(500)]
public async Task<IActionResult> Post([FromBody] ApplicationMapping applicationMapping)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState.GetErrorMessages());
}
var result = await _applicationMappingService.SaveAsync(applicationMapping);
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPut("Update", Name = "Update")]
[ProducesResponseType(typeof(ApplicationMapping), 200)]
[ProducesResponseType(500)]
public IActionResult Update([FromBody] ApplicationMapping applicationMapping)
{
try
{
var result = _applicationMappingService.Update(applicationMapping);
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpDelete()]
[ProducesResponseType(typeof(ApplicationMapping), 200)]
[ProducesResponseType(500)]
public IActionResult Delete(int jobId)
{
try
{
if (jobId <= 0)
{
return BadRequest("Invalid Id");
}
_applicationMappingService.DeleteAsync(jobId);
return Ok();
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
@@ -0,0 +1,44 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Serilog;
using LCU.Api.Internal.ArchiverApi.Services;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Controllers;
[ApiController]
[Route("[controller]")]
public class QueueArchiverController : ControllerBase
{
private readonly IQueueArchiverService _queueArchiverService;
public QueueArchiverController(IQueueArchiverService queueArchiverService)
{
_queueArchiverService = queueArchiverService;
}
[HttpPost("QueueArchiver")]
public IActionResult QueueArchiver(ArchiveRequest archiveRequest)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState.Values);
}
_queueArchiverService.QueueArchiver(archiveRequest);
return Ok();
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
@@ -0,0 +1,185 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Serilog;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using LCU.Api.Internal.ArchiverApi.Services;
using LCU.Api.Internal.ArchiverApi.Extensions;
using LCU.Library.Internal.ArchiverApiModel.Entities;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Controllers;
[ApiController]
[Route("[controller]")]
public class RegexMappingController : ControllerBase
{
IRegexMappingService _regexMappingService;
public RegexMappingController(IRegexMappingService regexMappingService)
{
_regexMappingService = regexMappingService;
}
[HttpGet(Name = "GetAllRegex")]
[ProducesResponseType(typeof(IEnumerable<RegexMapping>), 200)]
[ProducesResponseType(500)]
public async Task<IActionResult> Get()
{
try
{
var result = await _regexMappingService.GetAsync();
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("GetByRegexId/{regexMappingId:int}", Name = "GetByRegexId")]
[ProducesResponseType(201)]
[ProducesResponseType(204)]
[ProducesResponseType(typeof(RegexMapping), 400)]
[ProducesResponseType(500)]
public async Task<IActionResult> GetByRegexMappingId(int regexMappingId)
{
try
{
if (regexMappingId <= 0)
{
return BadRequest("Invalid Id");
}
var result = await _regexMappingService.GetByRegexMappingIdAsync(regexMappingId);
if (result == null)
{
return NoContent();
}
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("GetByApplicationMappingId/{applicationMappingid:int}", Name = "GetByApplicationMappingId")]
[ProducesResponseType(201)]
[ProducesResponseType(204)]
[ProducesResponseType(typeof(RegexMapping), 400)]
[ProducesResponseType(500)]
public async Task<IActionResult> GetByApplicationMappingId(int applicationMappingId)
{
try
{
if (applicationMappingId <= 0)
{
return BadRequest("Invalid Id");
}
var result = await _regexMappingService.GetByApplicationMappingIdAsync(applicationMappingId);
if (result == null)
{
return NoContent();
}
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(typeof(RegexMapping), 400)]
[ProducesResponseType(406)]
[ProducesResponseType(500)]
public async Task<IActionResult> Post([FromBody] RegexMapping regexMapping)
{
try
{
var result = await _regexMappingService.SaveAsync(regexMapping);
if (result == null)
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPut("UpdateRegex", Name = "UpdateRegex")]
[ProducesResponseType(typeof(RegexMapping), 200)]
[ProducesResponseType(406)]
[ProducesResponseType(500)]
public IActionResult Update([FromBody] RegexMapping regexMapping)
{
try
{
var result = _regexMappingService.Update(regexMapping);
if (result == null)
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return Ok(result);
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpDelete()]
[ProducesResponseType(typeof(RegexMapping), 200)]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public IActionResult Delete(int regexId)
{
try
{
if (regexId <= 0)
{
return BadRequest("Invalid Id");
}
_regexMappingService.DeleteAsync(regexId);
return Ok();
}
catch (ServiceException)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
catch (Exception exception)
{
Log.Error(exception, "Unexpected Exception");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
@@ -0,0 +1,34 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using LCU.Api.Internal.ArchiverApi.Models;
namespace LCU.Api.Internal.ArchiverApi.Controllers;
[ApiController]
[Route("[controller]")]
public class StatusController : ControllerBase
{
private readonly IEnvironmentView _environmentView;
public StatusController(IEnvironmentView environmentView)
{
_environmentView = environmentView;
}
[HttpGet()]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public IActionResult Get()
{
try
{
Log.Verbose($"{_environmentView.Application}'s status endpoint was hit in {_environmentView.Environment}");
return Ok(_environmentView);
}
catch (Exception e)
{
Log.Error("Failure return status, message: {0} {1}", e.Message, e.StackTrace);
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
}
}
}
@@ -0,0 +1,9 @@
using System;
namespace LCU.Api.Internal.ArchiverApi.Exceptions;
public class FailedToParseException : Exception
{
public FailedToParseException(string message) : base(message)
{
}
}
@@ -0,0 +1,13 @@
using System;
namespace LCU.Api.Internal.ArchiverApi.Exceptions;
/**
* If an entity cannot be found, that specific error can allow for a 404 or 400 with more detail to be
* returned. So specify that to the API layer.
*/
public class NotFoundException : ApplicationException
{
public NotFoundException(string message) : base(message)
{
}
}
@@ -0,0 +1,12 @@
using System;
namespace LCU.Api.Internal.ArchiverApi.Exceptions;
/**
* Do not want to expose database and related exceptions to the controllers / web layer.
* The lower layers log what happened for developers, then return a general error; unless
* a more specific error can be returned in a way to allow the client to act (such as NotFound).
*/
public class ServerErrorException : Exception
{
public ServerErrorException() : base("A server error occured, and a response could not be produced.") { }
}
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;
using System.Linq;
namespace LCU.Api.Internal.ArchiverApi.Extensions
{
public static class ModelStateMessages
{
public static List<string> GetErrorMessages(this ModelStateDictionary dictionary)
{
return dictionary.SelectMany(m => m.Value.Errors)
.Select(m => m.ErrorMessage)
.ToList();
}
}
}
@@ -0,0 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Description>
1.0.0 - Initial Versioning
1.1.0 - Notice, Statments, and Reports
1.1.1 - Fix regex to only look for extensions at end. Fix path for error templates.\
1.2.0 - Added ReturnResponse to ArchiveRequest and updated nugets.
1.3.0 - Change QueueArchiveRequest to use DaysFromDate.
1.4.0 - Update to use MassTransit and ApiResponse upgrade.
1.5.0 - Upgraded FileOperations package version
</Description>
<Version>1.4.0</Version>
<PackageReleaseNotes>
1.0.0 - Initial Versioning
1.1.0 - Notice, Statments, and Reports
1.1.1 - Fix regex to only look for extensions at end. Fix path for error templates.
1.2.0 - Added ReturnResponse to ArchiveRequest and updated nugets.
1.3.0 - Change QueueArchiveRequest to use DaysFromDate.
1.4.0 - Update to use MassTransit and ApiResponse upgrade.
1.5.0 - Upgraded FileOperations package version
</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LCU.Api.Internal.MaidBotLib" Version="1.0.0" />
<PackageReference Include="LCU.API.Internal.NotificationsApiLib" Version="2.1.0" />
<PackageReference Include="LCU.Library.Internal.ApiResponse" Version="3.3.0" />
<PackageReference Include="LCU.Library.Internal.ApiResponse.Models" Version="3.3.0" />
<PackageReference Include="LCU.Library.Internal.CoreApiWrapperModel" Version="2.2.0" />
<PackageReference Include="LCU.Library.Internal.Exceptions" Version="1.0.0" />
<PackageReference Include="LCU.Library.Internal.FileOperationsModels" Version="4.1.0" />
<PackageReference Include="LCU.Library.Internal.LcuMassTransit" Version="1.0.0" />
<PackageReference Include="LCU.Library.Internal.LcuMassTransitModels" Version="1.1.0" />
<PackageReference Include="LCU.Library.Internal.Logging" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.1">
<NoWarn>NU1605</NoWarn>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.9" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="TimeZoneConverter" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LCU.Library.Internal.ArchiverApiLib\LCU.Library.Internal.ArchiverApiLib.csproj" />
<ProjectReference Include="..\LCU.Library.Internal.ArchiverApiModel\LCU.Library.Internal.ArchiverApiModel.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Templates\ErrorMonitoringTemplate.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Templates\ErrorRowTemplate.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,30 @@
using System;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
namespace LCU.Api.Internal.ArchiverApi.Models;
public class EnvironmentView : IEnvironmentView
{
public string Application => _env.ApplicationName;
public DateTime CurrentDate => DateTime.Now;
public string BuildVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
public DateTime BuildDate => AssemblyBuildDate();
public string Environment => _env.EnvironmentName;
private readonly IWebHostEnvironment _env;
public EnvironmentView(IWebHostEnvironment env)
{
_env = env;
}
public static DateTime AssemblyBuildDate()
{
//https://stackoverflow.com/a/804310/266594
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location);
DateTime lastModified = fileInfo.LastWriteTime;
return lastModified;
}
}
@@ -0,0 +1,11 @@
using System;
namespace LCU.Api.Internal.ArchiverApi.Models;
public interface IEnvironmentView
{
string Application { get; }
DateTime CurrentDate { get; }
string BuildVersion { get; }
DateTime BuildDate { get; }
string Environment { get; }
}
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
namespace LCU.Api.Internal.ArchiverApi;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.ConfigureAppConfiguration((context,builder) =>
{
builder.AddJsonFile("/app/config/mapped-appsettings/primary.json", optional: true, reloadOnChange: true);
builder.AddJsonFile("/vault/secrets/archiver-api-database-connection.json", optional: true, reloadOnChange: true);
builder.AddJsonFile("/vault/secrets/archiver-api-seq-api-key.json", optional: true, reloadOnChange: true);
builder.AddJsonFile("/vault/secrets/archiver-api-queue-username.json", optional: true, reloadOnChange: true);
builder.AddJsonFile("/vault/secrets/archiver-api-queue-password.json", optional: true, reloadOnChange: true);
builder.AddJsonFile("/vault/secrets/archiverapi_configuration.json", optional: true, reloadOnChange: true);
var config = builder.Build();
})
.UseSerilog();
}
@@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56414",
"sslPort": 44353
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"LCU.Api.Internal.AutomicApiWrapper": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Serilog;
using LCU.Library.Internal.ArchiverApiLib.Repositories;
using LCU.Library.Internal.ArchiverApiModel.Entities;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class ApplicationMappingService : IApplicationMappingService
{
private readonly IApplicationMappingRepository _applicationMappingRepository;
private readonly IUnitOfWork _unitOfWork;
public ApplicationMappingService(IUnitOfWork unitOfWork,
IApplicationMappingRepository applicationMappingRepository)
{
_unitOfWork = unitOfWork;
_applicationMappingRepository = applicationMappingRepository;
}
public async Task<IEnumerable<ApplicationMapping>> GetAsync()
{
try
{
return await _applicationMappingRepository.ListAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Failure retrieving application mappings.");
throw new ServiceException("Failure retrieving application mappings.");
}
}
public async Task<ApplicationMapping> GetByIdAsync(int id)
{
try
{
var applicationMapping = await _applicationMappingRepository.FindByIdAsync(id);
ArgumentNullException.ThrowIfNull(applicationMapping);
return applicationMapping;
}
catch (Exception exception)
{
Log.Error(exception, $"Failure retrieving application mapping with id {id}.");
throw new ServiceException($"Failure retrieving application mapping with id {id}.");
}
}
public async Task<IEnumerable<ApplicationMapping>> GetByIdentifierAsync(string identifier)
{
try
{
var applicationMapping = await _applicationMappingRepository.FindByIdentifierAsync(identifier);
ArgumentNullException.ThrowIfNull(applicationMapping);
return applicationMapping;
}
catch (Exception exception)
{
Log.Error(exception, $"Failure retrieving application mapping with id {identifier}.");
throw new ServiceException($"Failure retrieving application mapping with id {identifier}.");
}
}
public async Task<ApplicationMapping> SaveAsync(ApplicationMapping applicationMapping)
{
try
{
var result = await _applicationMappingRepository.AddAsync(applicationMapping);
await _unitOfWork.CompleteAsync();
return result;
}
catch (Exception exception)
{
Log.Error(exception, "Failure saving application mapping");
throw new ServiceException("Failure saving application mapping");
}
}
public ApplicationMapping Update(ApplicationMapping applicationMapping)
{
try
{
var existing = GetByIdAsync(applicationMapping.ApplicationMappingId).Result;
ArgumentNullException.ThrowIfNull(existing);
existing.Identifier = applicationMapping.Identifier;
existing.DesignatedName = applicationMapping.DesignatedName;
existing.ApplicationOverrideName = applicationMapping.ApplicationOverrideName;
existing.ParameterMappings = applicationMapping.ParameterMappings;
_applicationMappingRepository.Update(existing);
_unitOfWork.Complete();
return existing;
}
catch (SqlException sqlException)
{
Log.Error(sqlException, "SQL Failure updating application mapping");
throw new ServiceException("SQL Failure updating application mapping");
}
catch (Exception exception)
{
Log.Error(exception, "Failure updating application mapping");
throw new ServiceException("Failure updating application mapping");
}
}
public void DeleteAsync(int applicationMappingId)
{
try
{
var applicationMapping = _applicationMappingRepository.FindByIdAsync(applicationMappingId).Result;
ArgumentNullException.ThrowIfNull(applicationMapping);
_applicationMappingRepository.Remove(applicationMapping);
_unitOfWork.Complete();
}
catch (SqlException sqlException)
{
Log.Error(sqlException, $"SQL failure removing application mapping. ApplicationMappingId: {applicationMappingId}");
throw new ServiceException($"SQL failure removing application mapping. ApplicationMappingId: {applicationMappingId}");
}
catch (Exception exception)
{
Log.Error(exception, $"Failure removing application mapping. ApplicationMappingId: {applicationMappingId}");
throw new ServiceException($"Failure removing application mapping. ApplicationMappingId: {applicationMappingId}");
}
}
}
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using Serilog;
using Newtonsoft.Json;
using LCU.Api.Internal.MaidBotLib.Models;
using LCU.Library.Internal.ApiResponse.Models;
using LCU.Library.Internal.ApiResponse.Services;
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class ArchiveCompletionService : IArchiveCompletionService
{
private readonly IFileOperationsService _fileOperationsService;
private readonly INotificationService _notificationService;
public ArchiveCompletionService(IFileOperationsService fileOperationsService,
INotificationService notificationService)
{
_fileOperationsService = fileOperationsService;
_notificationService = notificationService;
}
public void HandleReportArchiveCompletedRequest(Response response)
{
try
{
var payLoadString = response.ResponsePayload;
var archiveResponsePayload = JsonConvert.DeserializeObject<ArchiveResponsePayload>(payLoadString);
var archiveRequestResponse = archiveResponsePayload.ArchiveRequestResponse;
archiveRequestResponse.IsSuccessful = response.Successful;
archiveRequestResponse.MessageStack.AddRange(response.ResponseStack);
var requestResult = PopulateErrorDetailsAndRequestResponse(archiveResponsePayload, archiveRequestResponse);
QueueReturnResponse.QueueResponse(archiveResponsePayload.ArchiveRequest,
requestResult.archiveRequestResponse.IsSuccessful,
requestResult.archiveRequestResponse.MessageStack);
var errorsSent = _notificationService.SendErrorEmailAsync(requestResult.errorDetails).Result;
if (!errorsSent)
{
Log.ForContext(nameof(PropertyEnum.Group),
nameof(GroupEnum.ITDevelopment))
.Error($"Failure to send error details for {requestResult.errorDetails.Count} errors.");
throw new Exception($"Failure to send error details for {requestResult.errorDetails.Count} errors.");
}
}
catch (Exception exception)
{
Log.Error(exception, $"Error while archiving reports. " +
$"Message:{exception.Message}");
}
}
public (List<ErrorDetail> errorDetails, ArchiveRequestResponse archiveRequestResponse) PopulateErrorDetailsAndRequestResponse(ArchiveResponsePayload archiveResponsePayload,
ArchiveRequestResponse archiveRequestResponse)
{
var errorDetails = new List<ErrorDetail>();
foreach (var request in archiveResponsePayload.BulkOperationRequest.OperationRequests)
{
var errorMessage = "";
try
{
//If file still exist move did not occur.
var fileExists = _fileOperationsService.ValidateFileExists(request.Source).Result;
if (fileExists)
{
errorMessage = $"File {request.SourceFile} was not archived to the path of {request.Destination} ";
archiveRequestResponse.IsSuccessful = false;
archiveRequestResponse.MessageStack.Add(errorMessage);
}
}
catch (Exception exception)
{
var loggingMessage = $"Exception thrown while looking for the file at path {request.Source}.";
errorMessage = $"{loggingMessage}. Message: {exception.Message}";
Log.Error(exception, loggingMessage);
}
if (!string.IsNullOrEmpty(errorMessage))
{
var newErrorDetail = new ErrorDetail()
{
FileLocation = request.SourcePath,
Filename = request.FinalSourceFile,
Destination = request.Destination,
Description = errorMessage
};
errorDetails.Add(newErrorDetail);
}
}
return (errorDetails, archiveRequestResponse);
}
}
@@ -0,0 +1,284 @@
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using Serilog;
using Newtonsoft.Json;
using LCU.Library.Internal.ApiResponse.Models;
using LCU.Library.Internal.FileOperationsModels.Models.NameSettings;
using LCU.Library.Internal.FileOperationsModels.Models;
using LCU.Api.Internal.ArchiverApi.Configuration;
using LCU.Api.Internal.MaidBotLib.Models;
using LCU.Library.Internal.ApiResponse.Services;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Library.Internal.LcuMassTransitModels;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class ArchiverService : IArchiverService
{
private readonly ArchiverApiConfig _ArchiverApiConfig;
private readonly IReportService _reportService;
private readonly IFileOperationsService _fileOperationsService;
private readonly ConnectionConfiguration _connectionConfiguration;
private readonly ReceiveEndpointConfiguration _receiveEndpointsConfiguration;
private const string NameRegexStartTag = "{Regex0}";
private const string NameRegexStart = "^(" + RegexStartCaseInsenstive;
private const string NameRegexEndTag = "{Regex1}";
private const string NameRegexEnd = RegexStopCaseInsenstive + ")";
private const string AnyWordAndPeriodRegexTag = "{Regex2}";
private const string AnyWordAndPeriodRegex = "\\w*\\.";
private const string ExtensionRegexTag = "{Regex3}";
private const string ExtensionRegexStart = "(";
private const string ExtensionRegexEnd = ")$";
private const string RegexPipe = "|";
private const string RegexStartCaseInsenstive = "(?i)";
private const string RegexStopCaseInsenstive = "(?-i)";
public ArchiverService(ArchiverApiConfig ArchiverApiConfig,
IReportService reportService,
IFileOperationsService fileOperationsService,
ConnectionConfiguration connectionConfiguration,
ReceiveEndpointConfigurations receiveEndpointsConfiguration)
{
_ArchiverApiConfig = ArchiverApiConfig;
_reportService = reportService;
_fileOperationsService = fileOperationsService;
_connectionConfiguration = connectionConfiguration;
_receiveEndpointsConfiguration = receiveEndpointsConfiguration.ResponseReceiveEndpoint;
}
public void ExecuteRequest(ArchiveRequest archiveRequest)
{
try
{
var archiveResult = HandleArchiveRequest(archiveRequest);
if (!archiveResult.archiveRequestResponse.IsSuccessful)
{
var errorMessage = String.Join("; ", archiveResult.archiveRequestResponse.MessageStack.ToArray());
Log.Error($"Archive Request was not successful. MessaggeStack:{errorMessage}");
}
//Happy path we queue return response later in ArchivecompletionService after the BulkOperations has been preformed.
if (archiveResult.queueReturnResponse)
{
QueueReturnResponse.QueueResponse(archiveRequest,
archiveResult.archiveRequestResponse.IsSuccessful,
archiveResult.archiveRequestResponse.MessageStack);
}
}
catch (Exception exception)
{
Log.ForContext(nameof(PropertyEnum.Group),
nameof(GroupEnum.ITDevelopment))
.Error(exception, $"Error while archiving reports. Look into failed ArchiveRequest. " +
$"ArchiveRequest: {JsonConvert.SerializeObject(archiveRequest)}");
}
}
public (ArchiveRequestResponse archiveRequestResponse, bool queueReturnResponse) HandleArchiveRequest(ArchiveRequest archiveRequest)
{
var archiveRequestResponse = new ArchiveRequestResponse();
try
{
var reports = _reportService.GetReportsToArchiveAsync(archiveRequest).Result;
if (!reports.Any())
{
Log.Information("No reports to bulk move.");
archiveRequestResponse.MessageStack.Add("No application histories retrieved from DNA database to move.");
archiveRequestResponse.IsSuccessful = archiveRequest.SucceedIfEmpty;
return (archiveRequestResponse, true);
}
var bulkOperationRequest = BuildBulkOperationRequest(reports, archiveRequest);
if (!bulkOperationRequest.OperationRequests.Any())
{
Log.Information("There are no files to bulk move.");
archiveRequestResponse.MessageStack.Add("There are no files to bulk move.");
archiveRequestResponse.IsSuccessful = archiveRequest.SucceedIfEmpty;
return (archiveRequestResponse, true);
}
bulkOperationRequest = SetUpResponse(bulkOperationRequest, archiveRequest, archiveRequestResponse);
_fileOperationsService.SendBulkOperationsAsync(bulkOperationRequest);
}
catch (Exception exception)
{
archiveRequestResponse.IsSuccessful = false;
var anyIdentifiers = archiveRequest.ArchiveIdentifiers != null && archiveRequest.ArchiveIdentifiers.Any();
var identifiers = anyIdentifiers ? String.Join(", ", archiveRequest.ArchiveIdentifiers.ToArray()) : "";
archiveRequestResponse.MessageStack.Add($"Failure to execute archive request. " +
$"EffectiveDate: [{archiveRequest.EffectiveDate}], " +
$"DaysFromDate: [{archiveRequest.DaysFromDate}], " +
$"ArchiveDestinationBase: [{archiveRequest.ArchiveDestinationBase}], " +
$"Identifiers: [{identifiers}], " +
$"Exception: {exception} ");
}
return (archiveRequestResponse, !archiveRequestResponse.IsSuccessful);
}
public BulkOperationRequest<MoveFileRequest> BuildBulkOperationRequest(IEnumerable<Report> reports, ArchiveRequest archiveRequest)
{
try
{
var bulkOperationRequest = new BulkOperationRequest<MoveFileRequest>();
bulkOperationRequest.OperationRequests = new List<MoveFileRequest>();
var extensionRegex = new RegexSetting()
{
Tag = ExtensionRegexTag,
Regex = BuildExtensionRegexString(archiveRequest)
};
var anyWordAndPeriodRegex = new RegexSetting()
{
Tag = AnyWordAndPeriodRegexTag,
Regex = AnyWordAndPeriodRegex
};
foreach (var report in reports)
{
var nameSettings = SetUpNameSettings(report);
nameSettings.RegexSettings.Add(anyWordAndPeriodRegex);
nameSettings.RegexSettings.Add(extensionRegex);
var dynamicallyNamedRequests = CreateOperationRequests(report, nameSettings, archiveRequest);
bulkOperationRequest.OperationRequests.AddRange(dynamicallyNamedRequests);
}
return bulkOperationRequest;
}
catch (AggregateException exception)
{
throw new AggregateException($"Error while attempting to build bulk operation request. " +
$"Exception Message: {exception.Message}");
}
catch (Exception exception)
{
throw new Exception($"Error while attempting to build bulk operation request. " +
$"Exception Message: {exception.Message}");
}
}
public string BuildExtensionRegexString(ArchiveRequest archiveRequest)
{
var regexStringBuilder = new StringBuilder();
regexStringBuilder.Append(ExtensionRegexStart);
for (int index = 0; index < archiveRequest.AllowedExensions.Count; ++index)
{
var pipe = index == archiveRequest.AllowedExensions.Count ? "" : RegexPipe;
regexStringBuilder.Append(RegexStartCaseInsenstive +
archiveRequest.AllowedExensions[index] +
RegexStopCaseInsenstive +
pipe);
}
regexStringBuilder.Append(ExtensionRegexEnd);
return regexStringBuilder.ToString();
}
public List<MoveFileRequest> CreateOperationRequests(Report report, DynamicNameSetting nameSettings, ArchiveRequest archiveRequest)
{
var fileName = report.HasRegexMapping ? report.DisplayReportName : report.FinalReportName;
DateTime effectiveDate = archiveRequest.EffectiveDate == null ? DateTime.MinValue : (DateTime)archiveRequest.EffectiveDate;
var reportPath = $"{_ArchiverApiConfig.DnaBasePath}\\" +
$"{effectiveDate.ToString(_ArchiverApiConfig.ReportPathDateFormat)}\\" +
$"{report.QueueNumber}";
var sourceFile = $"{NameRegexStartTag}{fileName}{NameRegexEndTag}{AnyWordAndPeriodRegexTag}{ExtensionRegexTag}";
var files = _fileOperationsService.BuildDynamicFileListAsync(reportPath, sourceFile, nameSettings).Result;
var dynamicallyNamedRequests = new List<MoveFileRequest>();
foreach (var file in files)
{
var request = new MoveFileRequest()
{
SourcePath = reportPath,
SourceFile = file,
DestinationPath = archiveRequest.ArchiveDestinationBase,
FileNameSetting = nameSettings
};
SetDestinationName(report, request, archiveRequest);
dynamicallyNamedRequests.Add(request);
}
return dynamicallyNamedRequests;
}
public void SetDestinationName(Report report, MoveFileRequest request, ArchiveRequest archiveRequest)
{
var extention = archiveRequest.AllowedExensions
.FirstOrDefault(fe => request.SourceFile.Contains($".{fe}", StringComparison.OrdinalIgnoreCase));
if (extention != null)
{
request.DestinationName = report.BuildDestinationName(request.FinalSourceFile, extention);
}
}
public DynamicNameSetting SetUpNameSettings(Report report) {
var nameSettings = new DynamicNameSetting();
nameSettings.RegexSettings = new List<RegexSetting>()
{
new RegexSetting()
{
Tag = NameRegexStartTag,
Regex = NameRegexStart
},
new RegexSetting()
{
Tag = NameRegexEndTag,
Regex = NameRegexEnd
}
};
if (report.ApplicationMapping == null)
{
return nameSettings;
};
foreach (var regex in report.ApplicationMapping.RegexMappings)
{
nameSettings.RegexSettings.Add(new RegexSetting()
{
Tag = regex.Tag,
Regex = regex.Regex,
});
}
return nameSettings;
}
public BulkOperationRequest<MoveFileRequest> SetUpResponse(BulkOperationRequest<MoveFileRequest> bulkOperationRequest,
ArchiveRequest archiveRequest,
ArchiveRequestResponse archiveRequestResponse)
{
var archiveResponsePayload = new ArchiveResponsePayload()
{
ArchiveRequest = archiveRequest,
BulkOperationRequest = bulkOperationRequest,
ArchiveRequestResponse = archiveRequestResponse
};
var responseItem = new ResponseItem
{
ReturnQueueConsumerConfig = new QueueConsumerConfig()
{
ExchangeName = _receiveEndpointsConfiguration.ReceiveEndpoint
},
ReturnQueueConnectionConfig = new QueueConnectionConfig()
{
Url = _connectionConfiguration.HostUrl,
Username = _connectionConfiguration.UserName,
Password = _connectionConfiguration.Password
},
ResponsePayload = JsonConvert.SerializeObject(archiveResponsePayload)
};
bulkOperationRequest.ResponseItems = new List<ResponseItem>()
{
responseItem
};
return bulkOperationRequest;
}
}
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
using LCU.Api.Internal.ArchiverApi.Configuration;
using LCU.Library.Internal.ArchiverApi.Utilities;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.ApplicationHistories;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class CoreApiWrapperService : ICoreApiWrapperService
{
private readonly ArchiverApiConfig _ArchiverApiConfig;
public CoreApiWrapperService(ArchiverApiConfig ArchiverApiConfig)
{
_ArchiverApiConfig = ArchiverApiConfig;
}
public async Task<IEnumerable<ApplicationHistory>> GetApplicationHistories(ArchiveRequest archiveRequest)
{
try
{
var applicationHistoryRequest = new ApplicationHistoryRequest()
{
ApplicationNumbers = archiveRequest.ArchiveIdentifiers,
EffectiveDate = archiveRequest.EffectiveDate.ToString()
};
var url = SetUpUrl(applicationHistoryRequest);
var client = HttpUtilities.GetClient(url);
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Issue retrieving Application History from Core Api Wrapper. " +
$"Request: {JsonConvert.SerializeObject(applicationHistoryRequest)}");
}
var stringResponse = await response.Content.ReadAsStringAsync();
var applicationHistories = JsonConvert.DeserializeObject<List<ApplicationHistory>>(stringResponse);
return applicationHistories;
}
catch (Exception exception)
{
throw new Exception($"Failure to get application histories. Exception Message: {exception.Message}");
}
}
public string SetUpUrl(ApplicationHistoryRequest applicationHistoryRequest)
{
var endpointBuilder = new StringBuilder();
endpointBuilder.Append(_ArchiverApiConfig.CoreApiWrapperGetApplicationHistory);
var propertyCounter = 0;
var propertyName = nameof(applicationHistoryRequest.ApplicationNumbers);
HttpUtilities.AppendListOfValuesToEndpoint(endpointBuilder, propertyCounter, applicationHistoryRequest.ApplicationNumbers, propertyName);
endpointBuilder.Append($"&{nameof(applicationHistoryRequest.EffectiveDate)}={applicationHistoryRequest.EffectiveDate}");
return endpointBuilder.ToString();
}
}
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using LCU.Library.Internal.FileOperationsModels.Models.NameSettings;
using LCU.Library.Internal.ArchiverApi.Utilities;
using LCU.Library.Internal.FileOperationsModels.Models;
using LCU.Api.Internal.ArchiverApi.Configuration;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class FileOperationsService : IFileOperationsService
{
private readonly ArchiverApiConfig _ArchiverApiConfig;
public FileOperationsService(ArchiverApiConfig ArchiverApiConfig)
{
_ArchiverApiConfig = ArchiverApiConfig;
}
public void SendBulkOperationsAsync(BulkOperationRequest<MoveFileRequest> bulkOperationRequest)
{
try
{
var encoded = JsonConvert.SerializeObject(bulkOperationRequest);
var client = HttpUtilities.GetClient(_ArchiverApiConfig.FileOperationApiBulkMoveFile);
var content = new StringContent(encoded, Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(_ArchiverApiConfig.FileOperationApiBulkMoveFile, content).Result;
response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"IsSuccessStatusCode: {response.IsSuccessStatusCode}");
}
}
catch (Exception ex)
{
throw new Exception($"Failure to use the Bulk Operations. Message{ex.Message}");
}
}
public async Task<List<string>> BuildDynamicFileListAsync(string path, string file, DynamicNameSetting dynamicNameSetting)
{
try
{
var dynamicFileListRequest = new DynamicFileListRequest()
{
Path = path,
Filename = file,
DynamicFileSettings = dynamicNameSetting
};
var encoded = JsonConvert.SerializeObject(dynamicFileListRequest);
var client = HttpUtilities.GetClient(_ArchiverApiConfig.FileOperationApiBuildDynamicFileList);
var content = new StringContent(encoded, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(_ArchiverApiConfig.FileOperationApiBuildDynamicFileList, content);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"IsSuccessStatusCode: {response.IsSuccessStatusCode}");
}
var stringResponse = await response.Content.ReadAsStringAsync();
var fileList = JsonConvert.DeserializeObject<List<string>>(stringResponse);
return fileList;
}
catch (Exception exception)
{
throw new Exception($"Unable to build dynamic list of files. Exception Message: {exception}");
}
}
public async Task<bool> ValidateFileExists(string sourceFilePath)
{
try
{
var encoded = JsonConvert.SerializeObject(sourceFilePath);
var client = HttpUtilities.GetClient(_ArchiverApiConfig.FileOperationApiValidateFileExists);
var content = new StringContent(encoded, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(_ArchiverApiConfig.FileOperationApiValidateFileExists, content);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"IsSuccessStatusCode: {response.IsSuccessStatusCode}");
}
var stringResponse = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<bool>(stringResponse);
return result;
}
catch (Exception exception)
{
throw new Exception($"Failure to validate files exist. " +
$"Exception Message:{exception.Message}");
}
}
}
@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LCU.Library.Internal.ArchiverApiModel.Entities;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IApplicationMappingService
{
Task<IEnumerable<ApplicationMapping>> GetAsync();
Task<ApplicationMapping> GetByIdAsync(int id);
Task<IEnumerable<ApplicationMapping>> GetByIdentifierAsync(string identifier);
Task<ApplicationMapping> SaveAsync(ApplicationMapping applicationMapping);
ApplicationMapping Update(ApplicationMapping applicationMapping);
void DeleteAsync(int applicationMappingId);
}
@@ -0,0 +1,7 @@
using LCU.Library.Internal.ApiResponse.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IArchiveCompletionService
{
void HandleReportArchiveCompletedRequest(Response response);
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using LCU.Library.Internal.FileOperationsModels.Models;
using LCU.Library.Internal.FileOperationsModels.Models.NameSettings;
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IArchiverService
{
void ExecuteRequest(ArchiveRequest archiveRequest);
(ArchiveRequestResponse archiveRequestResponse, bool queueReturnResponse) HandleArchiveRequest(ArchiveRequest archiveRequest);
BulkOperationRequest<MoveFileRequest> BuildBulkOperationRequest(IEnumerable<Report> reports, ArchiveRequest archiveRequest);
string BuildExtensionRegexString(ArchiveRequest archiveRequest);
List<MoveFileRequest> CreateOperationRequests(Report report, DynamicNameSetting nameSettings, ArchiveRequest archiveRequest);
void SetDestinationName(Report report, MoveFileRequest request, ArchiveRequest archiveRequest);
}
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.ApplicationHistories;
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.CoreRequests;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface ICoreApiWrapperService
{
Task<IEnumerable<ApplicationHistory>> GetApplicationHistories(ArchiveRequest archiveRequest);
string SetUpUrl(ApplicationHistoryRequest applicationHistoryRequest);
}
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LCU.Library.Internal.FileOperationsModels.Models;
using LCU.Library.Internal.FileOperationsModels.Models.NameSettings;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IFileOperationsService
{
void SendBulkOperationsAsync(BulkOperationRequest<MoveFileRequest> bulkOperationRequest);
Task<List<string>> BuildDynamicFileListAsync(string path, string file, DynamicNameSetting dynamicNameSetting);
Task<bool> ValidateFileExists(string sourceFilePath);
}
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface INotificationService
{
Task<bool> SendEmailAsync(List<string> recipients, string subject, string emailTemplate);
Task<bool> SendErrorEmailAsync(List<ErrorDetail> errorDetails);
string CreateErrorRow(ErrorDetail errorDetail, string basePath);
string CreateErrorMonitoringEmail(string errorRows, string basePath);
}
@@ -0,0 +1,7 @@
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IQueueArchiverService
{
void QueueArchiver(ArchiveRequest archiveRequest);
}
@@ -0,0 +1,15 @@
using LCU.Library.Internal.ArchiverApiModel.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IRegexMappingService
{
Task<IEnumerable<RegexMapping>> GetAsync();
Task<RegexMapping> GetByApplicationMappingIdAsync(int id);
Task<RegexMapping> SaveAsync(RegexMapping regexMapping);
RegexMapping Update(RegexMapping regexMapping);
void DeleteAsync(int regexMappingId);
public Task<RegexMapping> GetByRegexMappingIdAsync(int id);
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public interface IReportService
{
Task<IEnumerable<Report>> GetReportsToArchiveAsync(ArchiveRequest archiveRequest);
}
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Serilog;
using LCU.Api.Internal.ArchiverApi.Configuration;
using LCU.API.Internal.NotificationsApiLib.Models;
using LCU.Library.Internal.ArchiverApi.Utilities;
using LCU.Library.Internal.ArchiverApiModel.Models;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class NotificationService : INotificationService
{
private readonly ArchiverApiConfig _ArchiverApiConfig;
private const string TableRowsTag = "{TableRows}";
public NotificationService(ArchiverApiConfig ArchiverApiConfig)
{
_ArchiverApiConfig = ArchiverApiConfig;
}
public async Task<bool> SendEmailAsync(List<string> recipients, string subject, string emailTemplate)
{
try
{
var emailRequest = new EmailRequest
{
Recipient = recipients,
Subject = subject,
Body = emailTemplate
};
var encoded = JsonConvert.SerializeObject(emailRequest);
var client = HttpUtilities.GetClient(_ArchiverApiConfig.NotificationApiEmail);
var content = new StringContent(encoded, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(_ArchiverApiConfig.NotificationApiEmail, content);
response.EnsureSuccessStatusCode();
return response.IsSuccessStatusCode;
}
catch (Exception ex)
{
Log.Error(ex, $"Failure to send email from Archiver Api");
return false;
}
}
public async Task<bool> SendErrorEmailAsync(List<ErrorDetail> errorDetails)
{
if (!errorDetails.Any())
{
return true;
}
var basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory);
string rowsOfErrors = "";
foreach (var errorDetail in errorDetails)
{
rowsOfErrors += CreateErrorRow(errorDetail, basePath);
}
var emailTemplate = CreateErrorMonitoringEmail(rowsOfErrors, basePath);
var successfullySent = await SendEmailAsync(_ArchiverApiConfig.ErrorRecipients,
_ArchiverApiConfig.ErrorMonitoringSubject,
emailTemplate);
return successfullySent;
}
public string CreateErrorRow(ErrorDetail errorDetail, string basePath)
{
var errorRowReader = new StreamReader($"{basePath}{_ArchiverApiConfig.ErrorRowTemplate}");
string errorRowOutput = errorRowReader.ReadToEnd();
foreach (var property in errorDetail.GetType().GetProperties())
{
string value = property.GetValue(errorDetail) == null ?
string.Empty :
property.GetValue(errorDetail).ToString();
errorRowOutput = errorRowOutput.Replace($"{{{property.Name}}}", value);
}
return errorRowOutput;
}
public string CreateErrorMonitoringEmail(string errorRows, string basePath)
{
var errorMonitoringReader = new StreamReader($"{basePath}{_ArchiverApiConfig.ErrorMonitoringTemplate}");
string errorMonitoringOutput = errorMonitoringReader.ReadToEnd();
return errorMonitoringOutput.Replace(TableRowsTag, errorRows);
}
}
@@ -0,0 +1,44 @@
using System;
using Serilog;
using MassTransit;
using TimeZoneConverter;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Api.Internal.ArchiverApi.Configuration;
using LCU.Library.Internal.LcuMassTransitModels;
using LCU.Library.Internal.Exceptions;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class QueueArchiverService : IQueueArchiverService
{
private readonly ISendEndpoint _sendEndpoint;
private readonly ReceiveEndpointConfiguration _receiveEndpointsConfiguration;
private readonly ArchiverApiConfig _archiverApiConfig;
public QueueArchiverService(IBusControl busControl,
ReceiveEndpointConfigurations receiveEndpointsConfiguration,
ArchiverApiConfig archiverApiConfig)
{
_receiveEndpointsConfiguration = receiveEndpointsConfiguration.ArchiverReceiveEndpoint;
_archiverApiConfig = archiverApiConfig;
_sendEndpoint = busControl.GetSendEndpoint(_receiveEndpointsConfiguration.ReceiveEndpointUri).Result;
}
public void QueueArchiver(ArchiveRequest archiveRequest)
{
try
{
var dateToConvert = archiveRequest.EffectiveDate == null ?
DateTime.Now.AddDays(archiveRequest.DaysFromDate) :
Convert.ToDateTime(archiveRequest.EffectiveDate).AddDays(archiveRequest.DaysFromDate);
var assumedTimezone = TZConvert.GetTimeZoneInfo(_archiverApiConfig.TimeZone);
archiveRequest.EffectiveDate = TimeZoneInfo.ConvertTime(dateToConvert, assumedTimezone).Date;
_sendEndpoint.Send(archiveRequest);
}
catch (Exception ex)
{
Log.Error(ex, $"Failed to queue archive request with effective date. Effective Date: {archiveRequest.EffectiveDate}");
throw new ServiceException($"Failed to queue archive request with effective date. Effective Date: {archiveRequest.EffectiveDate}");
}
}
}
@@ -0,0 +1,150 @@
using LCU.Library.Internal.ArchiverApiLib.Repositories;
using LCU.Library.Internal.ArchiverApiModel.Entities;
using System.Data.SqlClient;
using LCU.Library.Internal.Exceptions;
using Serilog;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class RegexMappingService : IRegexMappingService
{
private readonly IRegexMappingRepository _regexMappingRepository;
private readonly IUnitOfWork _unitOfWork;
public RegexMappingService(IUnitOfWork unitOfWork,
IRegexMappingRepository regexMappingRepository)
{
_unitOfWork = unitOfWork;
_regexMappingRepository = regexMappingRepository;
}
public void DeleteAsync(int regexMappingId)
{
try
{
var regexMapping = _regexMappingRepository.FindByRegexMappingIdAsync(regexMappingId).Result;
ArgumentNullException.ThrowIfNull(regexMapping);
_regexMappingRepository.Remove(regexMapping);
_unitOfWork.Complete();
}
catch (SqlException sqlException)
{
Log.Error(sqlException, $"SQL failure removing regex mapping. RegexMappingId: {regexMappingId}");
throw new ServiceException($"SQL failure removing regex mapping. RegexMappingId: {regexMappingId}");
}
catch (Exception exception)
{
Log.Error(exception, $"Failure removing regex mapping. RegexMappingId: {regexMappingId}");
throw new ServiceException($"Failure removing regex mapping. RegexMappingId: {regexMappingId}");
}
}
public async Task<IEnumerable<RegexMapping>> GetAsync()
{
try
{
return await _regexMappingRepository.ListAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Failure retrieving regex mappings.");
throw new ServiceException("Failure retrieving regex mappings.");
}
}
public async Task<RegexMapping> GetByApplicationMappingIdAsync(int id)
{
try
{
var regexMapping = await _regexMappingRepository.FindByApplicationMappingIdAsync(id);
ArgumentNullException.ThrowIfNull(regexMapping);
return regexMapping;
}
catch (Exception exception)
{
Log.Error(exception, $"Failure retrieving regex mapping with id {id}.");
throw new ServiceException($"Failure retrieving regex mapping with id {id}.");
}
}
public async Task<RegexMapping> GetByRegexMappingIdAsync(int id)
{
try
{
var regexMapping = await _regexMappingRepository.FindByRegexMappingIdAsync(id);
ArgumentNullException.ThrowIfNull(regexMapping);
return regexMapping;
}
catch (Exception exception)
{
Log.Error(exception, $"Failure retrieving regex mapping with id {id}.");
throw new ServiceException($"Failure retrieving regex mapping with id {id}.");
}
}
public async Task<RegexMapping> SaveAsync(RegexMapping regexMapping)
{
try
{
if (CheckTagInvalid(regexMapping.Tag))
{
throw new Exception("Invalid Tag: "+regexMapping.Tag);
}
var result = await _regexMappingRepository.AddAsync(regexMapping);
await _unitOfWork.CompleteAsync();
return result;
}
catch (Exception exception)
{
Log.Error(exception, "Failure saving regex mapping");
throw new ServiceException("Failure saving regex mapping");
}
}
public RegexMapping Update(RegexMapping regexMapping)
{
try
{
if (CheckTagInvalid(regexMapping.Tag))
{
throw new Exception("Invalid Tag: "+regexMapping.Tag);
}
var existing = GetByRegexMappingIdAsync(regexMapping.RegexMappingId).Result;
ArgumentNullException.ThrowIfNull(existing);
existing.ApplicationMappingId = regexMapping.ApplicationMappingId;
existing.Regex = regexMapping.Regex;
existing.Tag = regexMapping.Tag;
_regexMappingRepository.Update(existing);
_unitOfWork.Complete();
return existing;
}
catch (SqlException sqlException)
{
Log.Error(sqlException, "SQL Failure updating regex mapping");
throw new ServiceException("SQL Failure updating regex mapping");
}
catch (Exception exception)
{
Log.Error(exception, "Failure updating regex mapping");
throw new ServiceException("Failure updating regex mapping");
}
}
private bool CheckTagInvalid(string tag)
{
var endIndex = tag.IndexOf('}');
var sub = tag.Substring(0, 6);
if (tag.Substring(0,6) == "{Regex" && endIndex >= 7)
{
var tagNumber = Int32.Parse(tag.Substring(6, endIndex - 6));
return tagNumber >= 0 && tagNumber <= 3;
}
return true;
}
}
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using LCU.Library.Internal.ArchiverApiModel.Models;
using LCU.Library.Internal.ArchiverApiModel.Entities;
using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.ApplicationHistories;
namespace LCU.Api.Internal.ArchiverApi.Services;
public class ReportService : IReportService
{
private readonly ICoreApiWrapperService _coreApiWrapperService;
private readonly IApplicationMappingService _applicationMappingService;
private readonly IRegexMappingService _regexMappingService;
public ReportService(ICoreApiWrapperService coreApiWrapperService,
IApplicationMappingService applicationMappingService,
IRegexMappingService regexMappingService)
{
_coreApiWrapperService = coreApiWrapperService;
_applicationMappingService = applicationMappingService;
_regexMappingService = regexMappingService;
}
public async Task<IEnumerable<Report>> GetReportsToArchiveAsync(ArchiveRequest archiveRequest)
{
try
{
var applicationHistories = await _coreApiWrapperService.GetApplicationHistories(archiveRequest);
var reports = new List<Report>();
foreach (var applicationHistory in applicationHistories)
{
var applicationMappings = await _applicationMappingService.GetByIdentifierAsync(applicationHistory.ApplicationNumber.ToString());
var report = new Report()
{
ReportName = applicationHistory.ApplicationName,
QueueNumber = applicationHistory.QueueNumber.ToString(),
StopDateTime = applicationHistory.StopDateTime,
ApplicationMapping = DeteriminApplicationMapping(applicationMappings.ToList(), applicationHistory),
};
reports.Add(report);
}
return reports;
}
catch (Exception exception)
{
throw new Exception($"Error while trying to get reports ready for archiving. " +
$"Message: {exception.Message}");
}
}
public ApplicationMapping DeteriminApplicationMapping(List<ApplicationMapping> applicationMappings, ApplicationHistory applicationHistory)
{
var applicationMapping = DeteriminApplicationMappingWithoutParameters(applicationMappings);
if(applicationMapping != null)
{
return applicationMapping;
}
applicationMapping = DeteriminApplicationMappingByParameters(applicationMappings, applicationHistory);
return applicationMapping;
}
public ApplicationMapping DeteriminApplicationMappingWithoutParameters(List<ApplicationMapping> possibleApplicationMappings)
{
var onlyASingleApplicationMapping = possibleApplicationMappings.Count == 1;
if (onlyASingleApplicationMapping)
{
var singleApplicationMapping = possibleApplicationMappings.First();
if (singleApplicationMapping.ParameterMappings == null ||
!singleApplicationMapping.ParameterMappings.Any())
{
return singleApplicationMapping;
}
}
return null;
}
public ApplicationMapping DeteriminApplicationMappingByParameters(List<ApplicationMapping> possibleApplicationMappings, ApplicationHistory applicationHistory)
{
foreach (var applicationMapping in possibleApplicationMappings)
{
bool containsAllParameter = false;
foreach (var parameterMapping in applicationMapping.ParameterMappings)
{
containsAllParameter = applicationHistory.QueueApplicationParameters.Any(qap => qap.ParameterCode == parameterMapping.ParameterCode &&
qap.ParameterValue == parameterMapping.ParameterValue);
if (!containsAllParameter)
{
break;
}
}
if (containsAllParameter)
{
return applicationMapping;
}
}
return null;
}
}
@@ -0,0 +1,151 @@
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Microsoft.EntityFrameworkCore;
using Serilog;
using MassTransit;
using LCU.Library.Internal.Logging.Models;
using LCU.Library.Internal.Logging;
using LCU.Library.Internal.Logging.Middleware;
using LCU.Api.Internal.ArchiverApi.Configuration;
using LCU.Api.Internal.ArchiverApi.Models;
using LCU.Api.Internal.ArchiverApi.Services;
using LCU.Library.Internal.ArchiverApiLib.Repositories;
using LCU.Library.Internal.ArchiverApiLib.Persistence;
using LCU.Library.Internal.LcuMassTransit;
using LCU.Library.Internal.LcuMassTransitModels;
using LCU.Api.Internal.ArchiverApi.Consumers;
namespace LCU.Api.Internal.ArchiverApi;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient();
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(
Configuration.GetSection("Primary").GetSection("Connections").GetSection("SQL").GetSection("ArchiverApiDb").Value
);
});
var archiverApiConfig = new ArchiverApiConfig();
Configuration.GetSection("Primary").Bind(archiverApiConfig);
services.AddSingleton(archiverApiConfig);
var loggerConfiguration = new LoggingConfiguration(archiverApiConfig.ApplicationName,
archiverApiConfig.SeqApiKey,
archiverApiConfig.SeqUrl);
var lcuLogging = new LcuLogging(loggerConfiguration);
lcuLogging.UseLcuLogging();
var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, Configuration);
var receiveEndpointConfigurations = new ReceiveEndpointConfigurations()
{
ArchiverReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(Configuration,
"ArchiverReceiveEndpoint"),
ResponseReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(Configuration,
"ResponseReceiveEndpoint")
};
services.AddSingleton(receiveEndpointConfigurations);
AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfigurations, archiverApiConfig);
services.AddScoped<IEnvironmentView, EnvironmentView>();
//Services
services.AddScoped<IReportService, ReportService>();
services.AddScoped<IApplicationMappingService, ApplicationMappingService>();
services.AddScoped<IRegexMappingService, RegexMappingService>();
services.AddScoped<IArchiverService, ArchiverService>();
services.AddScoped<IQueueArchiverService, QueueArchiverService>();
services.AddScoped<IArchiveCompletionService, ArchiveCompletionService>();
services.AddScoped<IFileOperationsService, FileOperationsService>();
services.AddScoped<INotificationService, NotificationService>();
services.AddScoped<ICoreApiWrapperService, CoreApiWrapperService>();
//Repositories
services.AddScoped<IApplicationMappingRepository, ApplicationMappingRepository>();
services.AddScoped<IRegexMappingRepository, RegexMappingRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{archiverApiConfig.ApplicationName}", Version = "v1" }); });
}
public void AddMassTransitServices(ref IServiceCollection services,
ConnectionConfiguration connectionConfiguration,
ReceiveEndpointConfigurations receiveEndpointConfigurations,
ArchiverApiConfig archiverApiConfig)
{
services.AddMassTransit(x =>
{
x.AddConsumer<ArchiverConsumer>();
x.AddConsumer<ResponseConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host(connectionConfiguration.HostUrl, "/", host =>
{
host.Username(connectionConfiguration.UserName);
host.Password(connectionConfiguration.Password);
});
if (archiverApiConfig.FeatureFlags.RunArchiverConsumer)
{
cfg.ReceiveEndpoint(receiveEndpointConfigurations.ArchiverReceiveEndpoint.ReceiveEndpoint, e =>
{
e.ConfigureConsumer<ArchiverConsumer>(context);
e.PrefetchCount = receiveEndpointConfigurations.ArchiverReceiveEndpoint.PrefetchLimit;
e.SetQuorumQueue();
});
}
if (archiverApiConfig.FeatureFlags.RunResponseConsumer)
{
cfg.ReceiveEndpoint(receiveEndpointConfigurations.ResponseReceiveEndpoint.ReceiveEndpoint, e =>
{
e.ConfigureConsumer<ResponseConsumer>(context);
e.PrefetchCount = receiveEndpointConfigurations.ResponseReceiveEndpoint.PrefetchLimit;
e.SetQuorumQueue();
});
}
});
});
services.AddMassTransitHostedService();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "LCU.Api.Internal.ArchiverApi v1"));
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseSerilogRequestLogging(options => { options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest; });
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<style>
#errors {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
}
#errors td, #errors th {
border: 1px solid #ddd;
padding: 8px;
}
#errors tr:nth-child(even) {
background-color: #f2f2f2;
}
#errors th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
background-color: #4CAF50;
color: white;
}
#emailHeader {
color: #0054a2;
}
#errorTableHeader {
background-color: #0054a2;
color: white;
}
</style>
<meta charset="utf-8">
<title>Archiver Api: Error Monitoring</title>
</head>
<body>
<h1><span id="emailHeader">Archiver Api: Error Monitoring</span></h1>
<table id="errors">
<thead id="errorTableHeader">
<tr style="height: 17px;">
<td style="max-width: 250px;font-weight: bolder;">Filename</td>
<td style="max-width: 200px;font-weight: bolder;">File Location</td>
<td style="max-width: 79px;font-weight: bolder;">Destination</td>
<td style="max-width: 150px;font-weight: bolder;">Description</td>
</tr>
</thead>
<tbody id="errorRows">
{TableRows}
</tbody>
</table>
</body>
</html>
@@ -0,0 +1,6 @@
<tr id="errorRow" style="height: 22px;">
<td style="max-width: 250px;">{Filename}</td>
<td style="max-width: 200px;">{FileLocation}</td>
<td style="max-width: 79px;">{Destination}</td>
<td style="max-width: 150px;">{Description}</td>
</tr>
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
namespace LCU.Library.Internal.ArchiverApi.Utilities;
public static class HttpUtilities
{
private const int ParamCounterDefault = 1;
public static HttpClient GetClient(string url)
{
var handler = new HttpClientHandler
{
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 |
System.Security.Authentication.SslProtocols.Tls11 |
System.Security.Authentication.SslProtocols.Tls,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
var httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(60),
BaseAddress = new Uri(url)
};
return httpClient;
}
public static void AppendListOfValuesToEndpoint<T>(StringBuilder endpointBuilder, int currentIndex, List<T> listOfValues, string propertyName)
{
foreach (var value in listOfValues)
{
var splitter = currentIndex == 0 ? "?" : "&";
endpointBuilder.Append($"{splitter}{propertyName}={value}");
currentIndex++;
}
}
}
@@ -0,0 +1,44 @@
{
"Primary": {
"SeqUrl": "https://qa-seq.lcu-internal.com",
"SeqApiKey": "STORED_IN_VAULT",
"ApplicationName": "ArchiverApi",
"NotificationApiEmail": "https://qa-notifications-api.apps.azure.lcu-internal.com/api/Email",
"FileOperationApiBulkMoveFile": "https://localhost:44323/api/FileOperations/BulkMoveFile",
"FileOperationApiBuildDynamicFileList": "https://qa-fileoperationsapi.lcu-internal.com/DynamicFile/BuildDynamicFileList",
"FileOperationApiValidateFileExists": "https://qa-fileoperationsapi.lcu-internal.com/FileValidation/ValidateFileExists",
"CoreApiWrapperGetApplicationHistory": "https://qa-core-api-wrapper-api.apps.azure.lcu-internal.com/api/core/ApplicationHistory/GetApplicationHistory",
"DnaBasePath": "\\\\zeus\\DataIntegrationsQA\\AutomicArchive\\TEST_DNA",
"ReportPathDateFormat": "yyyyMMdd",
"ErrorMonitoringTemplate": "/Templates/ErrorMonitoringTemplate.html",
"ErrorRowTemplate": "/Templates/ErrorRowTemplate.html",
"ErrorMonitoringSubject": "Archiver Api: Error Monitoring",
"ErrorRecipients": [ "[email protected]", "[email protected]" ],
"TimeZone": "America/Chicago",
"Connections": {
"SQL": {
"ArchiverApiDb": "data source=localhost;initial catalog=ArchiverApi;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework"
}
},
"FeatureFlags": {
"RunArchiverConsumer": true,
"RunResponseConsumer": true
}
},
"AllowedHosts": "*",
"ConnectionConfiguration": {
"HostUrl": "localhost",
"UserName": "guest",
"Password": "guest"
},
"ArchiverReceiveEndpoint": {
"ReceiveEndpoint": "LCU.Archiver",
"ReceiveEndpointType": "exchange",
"PrefetchLimit": 1
},
"ResponseReceiveEndpoint": {
"ReceiveEndpoint": "LCU.Archiver.Response",
"ReceiveEndpointType": "exchange",
"PrefetchLimit": 1
}
}