Adding MaidBotApi
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
namespace LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
|
||||
public class ApiConfiguration
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
public string SeqUrl { get; set; }
|
||||
public string SeqApiKey { get; set; }
|
||||
public string NotificationsApiEmail { get; set; }
|
||||
public string BatchSchedulerCron { get; set; }
|
||||
public FeatureFlags FeatureFlags { get; set; }
|
||||
public string[] CorsAllowedOrigins { get; set; }
|
||||
public string LogEmailTemplate { get; set; }
|
||||
public string LogEmailRowTemplate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
|
||||
public class FeatureFlags
|
||||
{
|
||||
public bool LoggingEventConsumer { get; set; }
|
||||
public bool RunScheduler { get; set; }
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MassTransit;
|
||||
using Serilog;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Consumers;
|
||||
|
||||
public class LoggingEventConsumer : IConsumer<EventLog>
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
|
||||
public LoggingEventConsumer(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<EventLog> context)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
await Task.Run(async () => await scope.ServiceProvider.GetRequiredService<IEventLogService>().HandleIncomingEventLog(context.Message));
|
||||
}
|
||||
catch (ServiceException serviceException)
|
||||
{
|
||||
Log.Fatal(serviceException, $"Handle Incoming EventLog Failure. " +
|
||||
$"The Handle Incoming EventLog is placed on LCU.MaidBot_error.");
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Fatal(exception, $"Unexpected Handle Incoming EventLog Failure. " +
|
||||
$"The Handle Incoming EventLog is placed on LCU.MaidBot_error.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
namespace LCU.API.Internal.MaidBotApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
||||
public class EventLogController : ControllerBase
|
||||
{
|
||||
private readonly IEventLogService _eventLogService;
|
||||
|
||||
public EventLogController(IEventLogService eventLogService)
|
||||
{
|
||||
_eventLogService = eventLogService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(204)]
|
||||
[ProducesResponseType(typeof(EventLog), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _eventLogService.GetAsync();
|
||||
if (results == null || !results.Any())
|
||||
{
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
return Ok(results);
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete()]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(500)]
|
||||
public IActionResult DeleteMany([FromBody] List<EventLog> eventLogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_eventLogService.DeleteMany(eventLogs);
|
||||
return Ok();
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.Exceptions;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
namespace LCU.API.Internal.MaidBotApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
||||
public class LoggingMatrixController : ControllerBase
|
||||
{
|
||||
private readonly ILoggingMatrixService _loggingMatrixService;
|
||||
|
||||
public LoggingMatrixController(ILoggingMatrixService loggingMatrixService)
|
||||
{
|
||||
_loggingMatrixService = loggingMatrixService;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetAllLoggingMatrixes")]
|
||||
[ProducesResponseType(typeof(IEnumerable<LoggingMatrix>), 200)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<ActionResult> Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _loggingMatrixService.GetAsync();
|
||||
return Ok(result);
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(LoggingMatrix), 201)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<ActionResult> Post([FromBody] LoggingMatrix loggingMatrix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _loggingMatrixService.SaveAsync(loggingMatrix);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
Log.Error($"Result is null could not create Logging Matrix");
|
||||
return StatusCode((StatusCodes.Status400BadRequest), "Could not create Logging Matrix.");
|
||||
}
|
||||
|
||||
return StatusCode(StatusCodes.Status201Created, result);
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[ProducesResponseType(typeof(LoggingMatrix), 200)]
|
||||
[ProducesResponseType(400)]
|
||||
[ProducesResponseType(404)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Put([FromBody] LoggingMatrix loggingMatrix)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (loggingMatrix.LoggingMatrixId <= 0)
|
||||
{
|
||||
return BadRequest("Invalid ID");
|
||||
}
|
||||
|
||||
var result = await _loggingMatrixService.UpdateAsync(loggingMatrix);
|
||||
|
||||
if(result == null)
|
||||
{
|
||||
return NotFound($"The LoggingMatrix with LoggingMatrixId: {loggingMatrix.LoggingMatrixId} " +
|
||||
"could not be found");
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
namespace LCU.API.Internal.MaidBotApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
||||
public class QueueEventLogController : ControllerBase
|
||||
{
|
||||
private readonly IQueueEventLogService _queueEventLogService;
|
||||
|
||||
public QueueEventLogController(IQueueEventLogService queueEventLogService)
|
||||
{
|
||||
_queueEventLogService = queueEventLogService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(202)]
|
||||
[ProducesResponseType(typeof(List<string>), 400)]
|
||||
[ProducesResponseType(500)]
|
||||
public async Task<IActionResult> Post([FromBody] EventLog EventLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _queueEventLogService.QueueEventLog(EventLog);
|
||||
return Accepted();
|
||||
}
|
||||
catch (ServiceException exception)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Unexpected Exception");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.EnvironmentView;
|
||||
namespace LCU.API.Internal.MaidBotApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
||||
public class StatusController : ControllerBase
|
||||
{
|
||||
private readonly IEnvironmentView _environmentView;
|
||||
|
||||
public StatusController(IEnvironmentView environmentView)
|
||||
{
|
||||
_environmentView = environmentView;
|
||||
}
|
||||
|
||||
[HttpGet()]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(500)]
|
||||
public IActionResult Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(_environmentView);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failure to return status");
|
||||
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Shared.Common.NotificationApiLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.DataAccesses;
|
||||
|
||||
public interface INotificationsApiDao
|
||||
{
|
||||
Task SendEmailRequest(EmailRequest emailRequest);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||
using LCU.Shared.Common.NotificationApiLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.DataAccesses;
|
||||
|
||||
public class NotificationsApiDao : INotificationsApiDao
|
||||
{
|
||||
private readonly ApiConfiguration _apiConfiguration;
|
||||
private readonly IPostRequest _postRequest;
|
||||
|
||||
public NotificationsApiDao(ApiConfiguration apiConfiguration, IPostRequest postRequest)
|
||||
{
|
||||
_apiConfiguration = apiConfiguration;
|
||||
_postRequest = postRequest;
|
||||
}
|
||||
|
||||
public async Task SendEmailRequest(EmailRequest emailRequest)
|
||||
{
|
||||
await _postRequest.Async(new Uri(_apiConfiguration.NotificationsApiEmail), emailRequest);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
namespace LCU.Api.Internal.MaidBotApi.EnvironmentView;
|
||||
|
||||
public class EnvironmentView : IEnvironmentView
|
||||
{
|
||||
public string Application => _env.ApplicationName;
|
||||
public DateTime CurrentDate => DateTime.Now;
|
||||
public string BuildVersion => AssembleBuildVersion();
|
||||
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;
|
||||
}
|
||||
|
||||
public static string AssembleBuildVersion()
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
return version == null ? "-.-.-" : version.ToString();
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LCU.Api.Internal.MaidBotApi.EnvironmentView
|
||||
{
|
||||
public interface IEnvironmentView
|
||||
{
|
||||
string Application { get; }
|
||||
DateTime CurrentDate { get; }
|
||||
string BuildVersion { get; }
|
||||
DateTime BuildDate { get; }
|
||||
string Environment { get; }
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.Api.Internal.MaidBotApi.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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace LCU.Api.Internal.MaidBotApi.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.") { }
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using NCrontab;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
using LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
namespace LCU.API.Internal.MaidBotApi.HostedService;
|
||||
|
||||
public class SchedulerHostedService : BackgroundService
|
||||
{
|
||||
private readonly ApiConfiguration _apiConfiguration;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
|
||||
private readonly CrontabSchedule _schedule;
|
||||
private DateTime _nextScheduleRun;
|
||||
public const string DefaultScheduleCronPattern = "*/5 * * * *";
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
public SchedulerHostedService(IServiceScopeFactory scopeFactory,
|
||||
ApiConfiguration apiConfiguration)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
_apiConfiguration = apiConfiguration;
|
||||
|
||||
var cron = (_apiConfiguration?.BatchSchedulerCron) ?? DefaultScheduleCronPattern;
|
||||
_schedule = CrontabSchedule.Parse(cron);
|
||||
_nextScheduleRun = _schedule.GetNextOccurrence(DateTime.Now);
|
||||
|
||||
_cancellationToken = new CancellationToken();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Information("MaidBot Scheduler Hosted Service is starting.");
|
||||
_cancellationToken = cancellationToken;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
if (_apiConfiguration.FeatureFlags.RunScheduler && DateTime.Now > _nextScheduleRun)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
await Task.Run(async () => await scope.ServiceProvider.GetRequiredService<ILogDistributionService>().HandleLogDistribution());
|
||||
|
||||
_nextScheduleRun = _schedule.GetNextOccurrence(DateTime.Now);
|
||||
}
|
||||
|
||||
await Task.Delay(5000, cancellationToken); //5 seconds delay
|
||||
} while (!cancellationToken.IsCancellationRequested);
|
||||
}
|
||||
catch (TaskCanceledException taskCanceledException) when (_cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Log.Information(taskCanceledException, "MaidBot Scheduler Hosted Service was shut down normally.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Error trying to startup main thread for MaidBot Scheduler Hosted Service");
|
||||
}
|
||||
|
||||
Log.Information("MaidBot Scheduler Hosted Service is stopping.");
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>..\LCU.API.Internal.MaidBotApi\LCU.Api.Internal.MaidBotApi.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>..\LCU.API.Internal.MaidBotApi\LCU.Api.Internal.MaidBotApi.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LCU.Library.Internal.Exceptions" Version="1.0.0" />
|
||||
<PackageReference Include="LCU.Library.Internal.LcuHttpClient" Version="2.0.1" />
|
||||
<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="2.1.0" />
|
||||
<PackageReference Include="LCU.Shared.Common.NotificationApiLib" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.14" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LCU.Api.Internal.MaidBotLib\LCU.Api.Internal.MaidBotLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Templates\LogEmailRowTemplate.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Templates\LogEmailTemplate.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>LCU.Api.Internal.MaidBotApi</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:LCU.Api.Internal.MaidBotApi.Exceptions.NotFoundException">
|
||||
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.
|
||||
</member>
|
||||
<member name="T:LCU.Api.Internal.MaidBotApi.Exceptions.ServerErrorException">
|
||||
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).
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
using MassTransit;
|
||||
using Serilog;
|
||||
using LCU.API.Internal.MaidBotApi.HostedService;
|
||||
using LCU.Api.Internal.MaidBotApi.Services;
|
||||
using LCU.Api.Internal.MaidBotApi.EnvironmentView;
|
||||
using LCU.Api.Internal.MaidBotLib.Repositories;
|
||||
using LCU.Api.Internal.MaidBotLib.Persistence;
|
||||
using LCU.Library.Internal.LcuMassTransitModels;
|
||||
using LCU.Library.Internal.LcuMassTransit;
|
||||
using LCU.Api.Internal.MaidBotApi.Consumers;
|
||||
using LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
using LCU.Library.Internal.Logging.Models;
|
||||
using LCU.Library.Internal.Logging;
|
||||
using LCU.Library.Internal.Logging.Middleware;
|
||||
using LCU.Library.Internal.LcuHttpClient;
|
||||
using LCU.Api.Internal.MaidBotApi.DataAccesses;
|
||||
namespace LCU.API.Internal.MaidBot;
|
||||
|
||||
public class Program
|
||||
{
|
||||
static string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = BuildBuilder(args);
|
||||
BuildServicesAndConfigurations(builder);
|
||||
BuildAppAndRun(builder);
|
||||
}
|
||||
|
||||
private static WebApplicationBuilder BuildBuilder(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Host.UseSerilog();
|
||||
builder.Configuration.AddJsonFile("/app/config/environment-appsettings/appsettings.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile("/app/config/workload-appsettings/appsettings.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile("/vault/secrets/maidbot-api-seq-api-key.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile("/vault/secrets/maidbot-api-connection-configuration-password.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile("/vault/secrets/maidbot-api-connection-configuration-username.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile("/vault/secrets/maidbot-database-connection.json", optional: true, reloadOnChange: true);
|
||||
return builder;
|
||||
}
|
||||
|
||||
static void BuildServicesAndConfigurations(WebApplicationBuilder builder)
|
||||
{
|
||||
var services = builder.Services;
|
||||
var configuration = builder.Configuration;
|
||||
services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
services.AddEndpointsApiExplorer();
|
||||
services.AddSwaggerGen();
|
||||
|
||||
services.AddDbContext<AppDbContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(configuration.GetSection("Connections").GetSection("SQL").GetSection("MaidBotDB").Value);
|
||||
});
|
||||
|
||||
services.AddHttpClient();
|
||||
services.AddLCUHttpServices();
|
||||
|
||||
//Configurations
|
||||
var apiConfiguration = new ApiConfiguration();
|
||||
configuration.GetSection("Primary").Bind(apiConfiguration);
|
||||
services.AddSingleton(apiConfiguration);
|
||||
//debugging
|
||||
Console.WriteLine(JsonConvert.SerializeObject(apiConfiguration));
|
||||
|
||||
var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration);
|
||||
//debugging
|
||||
Console.WriteLine(JsonConvert.SerializeObject(connectionConfiguration));
|
||||
|
||||
var receiveEndpointConfiguration = new ReceiveEndpointConfiguration();
|
||||
receiveEndpointConfiguration = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "ReceiveEndpoint");
|
||||
services.AddSingleton(receiveEndpointConfiguration);
|
||||
//debugging
|
||||
Console.WriteLine(JsonConvert.SerializeObject(receiveEndpointConfiguration));
|
||||
|
||||
//EnvironmentView
|
||||
services.AddScoped<IEnvironmentView, EnvironmentView>();
|
||||
|
||||
var loggerConfiguration = new LoggingConfiguration(apiConfiguration.AppName,
|
||||
apiConfiguration.SeqApiKey,
|
||||
apiConfiguration.SeqUrl);
|
||||
var lcuLogging = new LcuLogging(loggerConfiguration);
|
||||
lcuLogging.UseLcuLogging();
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(MyAllowSpecificOrigins,
|
||||
builder =>
|
||||
{
|
||||
builder.WithOrigins(
|
||||
apiConfiguration.CorsAllowedOrigins
|
||||
).AllowAnyHeader().AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
AddMaidBotServices(ref services);
|
||||
AddMaidBotLib(ref services);
|
||||
AddMaidBotDataAccesses(ref services);
|
||||
AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfiguration, apiConfiguration);
|
||||
|
||||
// The Hosted
|
||||
services.AddHostedService<SchedulerHostedService>();
|
||||
|
||||
// Register the Swagger generator, defining 1 or more Swagger documents
|
||||
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{apiConfiguration.AppName}", Version = "v1" }); });
|
||||
|
||||
services.Configure<ApiBehaviorOptions>(options =>
|
||||
{
|
||||
options.SuppressModelStateInvalidFilter = true;
|
||||
});
|
||||
}
|
||||
|
||||
static void AddMaidBotServices(ref IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ILogDistributionService, LogDistributionService>();
|
||||
services.AddScoped<IQueueEventLogService, QueueEventLogService>();
|
||||
services.AddScoped<INotificationsApiService, NotificationsApiService>();
|
||||
services.AddScoped<ILoggingMatrixService, LoggingMatrixService>();
|
||||
services.AddScoped<IEventLogService, EventLogService>();
|
||||
services.AddScoped<IDebugEventLogService, DebugEventLogService>();
|
||||
}
|
||||
|
||||
static void AddMaidBotLib(ref IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ILoggingMatrixRepository, LoggingMatrixRepository>();
|
||||
services.AddScoped<IEventLogRepository, EventLogRepository>();
|
||||
services.AddScoped<IDebugEventLogRepository, DebugEventLogRepository>();
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
}
|
||||
|
||||
static void AddMaidBotDataAccesses(ref IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<INotificationsApiDao, NotificationsApiDao>();
|
||||
}
|
||||
|
||||
static void AddMassTransitServices(ref IServiceCollection services,
|
||||
ConnectionConfiguration connectionConfiguration,
|
||||
ReceiveEndpointConfiguration receiveEndpointConfiguration,
|
||||
ApiConfiguration apiConfiguration)
|
||||
{
|
||||
services.AddMassTransit(x =>
|
||||
{
|
||||
x.AddConsumer<LoggingEventConsumer>();
|
||||
|
||||
x.UsingRabbitMq((context, cfg) =>
|
||||
{
|
||||
cfg.Host(connectionConfiguration.HostUrl, "/", host =>
|
||||
{
|
||||
host.Username(connectionConfiguration.UserName);
|
||||
host.Password(connectionConfiguration.Password);
|
||||
});
|
||||
cfg.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420));
|
||||
|
||||
if (apiConfiguration.FeatureFlags.LoggingEventConsumer)
|
||||
{
|
||||
cfg.ReceiveEndpoint(receiveEndpointConfiguration.ReceiveEndpoint, e =>
|
||||
{
|
||||
e.ConfigureConsumer<LoggingEventConsumer>(context);
|
||||
e.SetQuorumQueue();
|
||||
e.PrefetchCount = receiveEndpointConfiguration.PrefetchLimit;
|
||||
e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
services.AddMassTransitHostedService();
|
||||
}
|
||||
|
||||
static void BuildAppAndRun(WebApplicationBuilder builder)
|
||||
{
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "LCU.Api.Internal.MaidBotApi v1"));
|
||||
app.UseMiddleware<RequestResponseLoggingMiddleware>();
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest;
|
||||
});
|
||||
|
||||
app.UseRouting();
|
||||
app.UseCors(MyAllowSpecificOrigins);
|
||||
app.UseHttpsRedirection();
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace LCU.API.Internal.MaidBot.Security
|
||||
{
|
||||
public class AzureActiveDirectoryOptions
|
||||
{
|
||||
public string ClientId { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
public string Instance { get; set; }
|
||||
public string Domain { get; set; }
|
||||
public string TenantId { get; set; }
|
||||
public string ValidAudience { get; set; }
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace LCU.API.Internal.MaidBot.Security
|
||||
{
|
||||
public static class AzureAdServiceCollectionExtensions
|
||||
{
|
||||
public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder)
|
||||
=> builder.AddAzureAdBearer(_ => { });
|
||||
|
||||
public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder, Action<AzureActiveDirectoryOptions> configureOptions)
|
||||
{
|
||||
builder.Services.Configure(configureOptions);
|
||||
builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureAzureOptions>();
|
||||
builder.AddJwtBearer();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private class ConfigureAzureOptions : IConfigureNamedOptions<JwtBearerOptions>
|
||||
{
|
||||
private readonly AzureActiveDirectoryOptions _azureOptions;
|
||||
|
||||
public ConfigureAzureOptions(IOptions<AzureActiveDirectoryOptions> azureOptions)
|
||||
{
|
||||
_azureOptions = azureOptions.Value;
|
||||
}
|
||||
|
||||
public void Configure(string name, JwtBearerOptions options)
|
||||
{
|
||||
options.Audience = _azureOptions.ClientId;
|
||||
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidAudience = _azureOptions.ValidAudience
|
||||
};
|
||||
}
|
||||
|
||||
public void Configure(JwtBearerOptions options)
|
||||
{
|
||||
Configure(Options.DefaultName, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.Exceptions;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Api.Internal.MaidBotLib.Repositories;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class DebugEventLogService : IDebugEventLogService
|
||||
{
|
||||
private readonly IDebugEventLogRepository _debugEventLogRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public DebugEventLogService(IUnitOfWork unitOfWork,
|
||||
IDebugEventLogRepository debugEventLogRepository)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_debugEventLogRepository = debugEventLogRepository;
|
||||
}
|
||||
|
||||
public async Task DebugEventLog(EventLog eventLog)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(eventLog.DebugData))
|
||||
{
|
||||
var debugEventLog = new DebugEventLog(eventLog);
|
||||
await SaveAsync(debugEventLog);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DebugEventLog> SaveAsync(DebugEventLog debugEventLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _debugEventLogRepository.AddAsync(debugEventLog);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
return result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, $"Failure saving DebugEventLog.");
|
||||
throw new ServiceException($"Failure saving DebugEventLog.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using Newtonsoft.Json;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Api.Internal.MaidBotLib.Repositories;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class EventLogService : IEventLogService
|
||||
{
|
||||
private readonly IEventLogRepository _eventLogRepository;
|
||||
private readonly IDebugEventLogService _debugEventLogService;
|
||||
private readonly ILoggingMatrixService _loggingMatrixService;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public EventLogService(IEventLogRepository eventLogRepository,
|
||||
IDebugEventLogService debugEventLogService,
|
||||
ILoggingMatrixService loggingMatrixService,
|
||||
IUnitOfWork unitOfWork)
|
||||
{
|
||||
_eventLogRepository = eventLogRepository;
|
||||
_debugEventLogService = debugEventLogService;
|
||||
_loggingMatrixService = loggingMatrixService;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task HandleIncomingEventLog(EventLog eventLog)
|
||||
{
|
||||
var loggingMatrixes = await _loggingMatrixService.GetByApplicationLevelAndGroupAsync(eventLog.Application,
|
||||
eventLog.Level,
|
||||
eventLog.Group);
|
||||
if (loggingMatrixes.Any())
|
||||
{
|
||||
await SaveAsync(eventLog);
|
||||
}
|
||||
|
||||
await _debugEventLogService.DebugEventLog(eventLog);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<EventLog>> GetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _eventLogRepository.ListAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = "Failure retrieving EventLogs.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}. ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<EventLog> SaveAsync(EventLog eventLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _eventLogRepository.AddAsync(eventLog);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
return result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure saving EventLog. EventLog: { JsonConvert.SerializeObject(eventLog)}";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteMany(List<EventLog> eventLogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_eventLogRepository.RemoveRange(eventLogs);
|
||||
_unitOfWork.Complete();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure to delete EventLogs.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public interface IDebugEventLogService
|
||||
{
|
||||
Task DebugEventLog(EventLog eventLog);
|
||||
Task<DebugEventLog> SaveAsync(DebugEventLog debugEventLog);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public interface IEventLogService
|
||||
{
|
||||
Task HandleIncomingEventLog(EventLog eventLog);
|
||||
Task<IEnumerable<EventLog>> GetAsync();
|
||||
Task<EventLog> SaveAsync(EventLog eventLog);
|
||||
void DeleteMany(List<EventLog> eventLogs);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public interface ILogDistributionService
|
||||
{
|
||||
Task HandleLogDistribution();
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services
|
||||
{
|
||||
public interface ILoggingMatrixService
|
||||
{
|
||||
Task<IEnumerable<LoggingMatrix>> GetAsync();
|
||||
Task<IEnumerable<LoggingMatrix>> GetActiveAsync();
|
||||
Task<LoggingMatrix> GetByIdAsync(int id);
|
||||
Task<IEnumerable<LoggingMatrix>> GetByApplicationLevelAndGroupAsync(string application, string level, string group);
|
||||
Task<IEnumerable<LoggingMatrix>> GetByApplicationAndLevelAsync(string application, string level);
|
||||
Task<LoggingMatrix> SaveAsync(LoggingMatrix loggingMatrix);
|
||||
Task<LoggingMatrix> UpdateAsync(LoggingMatrix loggingMatrix);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public interface INotificationsApiService
|
||||
{
|
||||
Task SendNotificationAsync(LoggingMatrix loggingMatrix, List<EventLog> eventLogs);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public interface IQueueEventLogService
|
||||
{
|
||||
Task QueueEventLog(EventLog request);
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class LogDistributionService : ILogDistributionService
|
||||
{
|
||||
private readonly IEventLogService _eventLogService;
|
||||
private readonly ILoggingMatrixService _loggingMatrixService;
|
||||
private readonly INotificationsApiService _notificationsApiService;
|
||||
|
||||
public LogDistributionService(IEventLogService eventLogService,
|
||||
ILoggingMatrixService loggingMatrixService,
|
||||
INotificationsApiService notificationsApiService)
|
||||
{
|
||||
_eventLogService = eventLogService;
|
||||
_loggingMatrixService = loggingMatrixService;
|
||||
_notificationsApiService = notificationsApiService;
|
||||
}
|
||||
|
||||
public async Task HandleLogDistribution()
|
||||
{
|
||||
try
|
||||
{
|
||||
var eventLogs = await _eventLogService.GetAsync();
|
||||
if(eventLogs == null || !eventLogs.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var loggingMatrixs = await _loggingMatrixService.GetActiveAsync();
|
||||
if (loggingMatrixs == null || !loggingMatrixs.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var loggingMatrix in loggingMatrixs)
|
||||
{
|
||||
var matrixEventLogs = eventLogs.Where(eventLog => eventLog.Application == loggingMatrix.Application &&
|
||||
eventLog.Level == loggingMatrix.Level &&
|
||||
eventLog.Group == loggingMatrix.Group).ToList();
|
||||
|
||||
if (!matrixEventLogs.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await _notificationsApiService.SendNotificationAsync(loggingMatrix, matrixEventLogs);
|
||||
}
|
||||
|
||||
_eventLogService.DeleteMany(eventLogs.ToList());
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception, "Failure to Handle Log Distribution.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Api.Internal.MaidBotLib.Repositories;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class LoggingMatrixService : ILoggingMatrixService
|
||||
{
|
||||
private readonly ILoggingMatrixRepository _loggingMatrixRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public LoggingMatrixService(IUnitOfWork unitOfWork,
|
||||
ILoggingMatrixRepository loggingMatrixRepository)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_loggingMatrixRepository = loggingMatrixRepository;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LoggingMatrix>> GetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _loggingMatrixRepository.ListAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = "Failure retrieving Logging Matrixes.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}. ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LoggingMatrix>> GetActiveAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _loggingMatrixRepository.ListActiveAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = "Failure retrieving active Logging Matrixes.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}. ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LoggingMatrix>> GetByApplicationLevelAndGroupAsync(string application, string level, string group)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _loggingMatrixRepository.RetrieveByApplicationLevelAndGroup(application, level, group);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure retrieving Logging Matrixes with application {application} and level {level}.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}. ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LoggingMatrix>> GetByApplicationAndLevelAsync(string application, string level)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _loggingMatrixRepository.RetrieveByApplciationAndLevel(application, level);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure retrieving Logging Matrixes with application {application} and level {level}.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}. ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<LoggingMatrix> GetByIdAsync(int id)
|
||||
{
|
||||
LoggingMatrix loggingMatrix = null;
|
||||
try
|
||||
{
|
||||
loggingMatrix = await _loggingMatrixRepository.FindByIdAsync(id);
|
||||
return loggingMatrix;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure retrieving Logging Matrix with id {id}.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<LoggingMatrix> SaveAsync(LoggingMatrix loggingMatrix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _loggingMatrixRepository.AddAsync(loggingMatrix);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure saving Logging Matrix. LoggingMatrix: { JsonConvert.SerializeObject(loggingMatrix)}";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<LoggingMatrix> UpdateAsync(LoggingMatrix loggingMatrix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existing = await GetByIdAsync(loggingMatrix.LoggingMatrixId);
|
||||
if (existing == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
existing.Application = loggingMatrix.Application;
|
||||
existing.Level = loggingMatrix.Level;
|
||||
existing.Group = loggingMatrix.Group;
|
||||
existing.Email = loggingMatrix.Email;
|
||||
existing.Disabled = loggingMatrix.Disabled;
|
||||
|
||||
_loggingMatrixRepository.Update(existing);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
|
||||
return existing;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure updating Logging Matrix. LoggingMatrix: { JsonConvert.SerializeObject(loggingMatrix)}";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotApi.DataAccesses;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
using LCU.Shared.Common.NotificationApiLib.Models;
|
||||
using LCU.Api.Internal.MaidBotApi.Configurations;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class NotificationsApiService : INotificationsApiService
|
||||
{
|
||||
private readonly ApiConfiguration _apiConfiguration;
|
||||
private readonly INotificationsApiDao _notificationsApiDao;
|
||||
private const string TableRowsTag = "{TableRows}";
|
||||
private const string ApplicationTag = "{Application}";
|
||||
private const string LevelTag = "{Level}";
|
||||
|
||||
public NotificationsApiService(ApiConfiguration apiConfiguration,
|
||||
INotificationsApiDao notificationsApiDao)
|
||||
{
|
||||
_apiConfiguration = apiConfiguration;
|
||||
_notificationsApiDao = notificationsApiDao;
|
||||
}
|
||||
|
||||
public async Task SendNotificationAsync(LoggingMatrix loggingMatrix, List<EventLog> eventLogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var emailRequest = new EmailRequest
|
||||
{
|
||||
Recipient = new List<string> { loggingMatrix.Email },
|
||||
Subject = $"{loggingMatrix.Application} - {loggingMatrix.Level}",
|
||||
Body = CreateEmailBody(loggingMatrix, eventLogs)
|
||||
};
|
||||
await _notificationsApiDao.SendEmailRequest(emailRequest);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"Failure to Send Maidbot Email.";
|
||||
Log.Error(exception, message);
|
||||
throw new ServiceException($"{message} Exception Message: {exception.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public string CreateEmailBody(LoggingMatrix loggingMatrix, List<EventLog> eventLogs)
|
||||
{
|
||||
var errorRows = new StringBuilder();
|
||||
foreach(var eventLog in eventLogs)
|
||||
{
|
||||
errorRows.Append(CreateEventLogRow(eventLog));
|
||||
}
|
||||
|
||||
var logEmailTemplateReader = new StreamReader(_apiConfiguration.LogEmailTemplate);
|
||||
string logEmailTemplateOutput = logEmailTemplateReader.ReadToEnd();
|
||||
logEmailTemplateOutput = logEmailTemplateOutput.Replace(TableRowsTag, errorRows.ToString());
|
||||
logEmailTemplateOutput = logEmailTemplateOutput.Replace(ApplicationTag, loggingMatrix.Level);
|
||||
return logEmailTemplateOutput.Replace(LevelTag, loggingMatrix.Application);
|
||||
}
|
||||
|
||||
public string CreateEventLogRow(EventLog eventLog)
|
||||
{
|
||||
var errorRowReader = new StreamReader(_apiConfiguration.LogEmailRowTemplate);
|
||||
string errorRowOutput = errorRowReader.ReadToEnd();
|
||||
|
||||
foreach (var property in eventLog.GetType().GetProperties())
|
||||
{
|
||||
string value = property.GetValue(eventLog) == null ? string.Empty :
|
||||
property.GetValue(eventLog).ToString();
|
||||
errorRowOutput = errorRowOutput.Replace($"{{{property.Name}}}", value);
|
||||
}
|
||||
|
||||
return errorRowOutput;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MassTransit;
|
||||
using Serilog;
|
||||
using LCU.Api.Internal.MaidBotLib.Models;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
using LCU.Library.Internal.LcuMassTransitModels;
|
||||
namespace LCU.Api.Internal.MaidBotApi.Services;
|
||||
|
||||
public class QueueEventLogService : IQueueEventLogService
|
||||
{
|
||||
private readonly ISendEndpoint _sendEndpoint;
|
||||
|
||||
public QueueEventLogService(IBusControl busControl,
|
||||
ReceiveEndpointConfiguration receiveEndpointConfiguration)
|
||||
{
|
||||
_sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfiguration.ReceiveEndpointUri).Result;
|
||||
}
|
||||
|
||||
public async Task QueueEventLog(EventLog request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
await _sendEndpoint.Send(request, cancellationTokenSource.Token);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||
.Fatal(exception, "Failed to Queue EventLog Request.");
|
||||
throw new ServiceException("Failed to Queue EventLog Request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<tr id="errorRow" style="height: 22px;">
|
||||
<td style="max-width: 50px;">{LogTimeStamp}</td>
|
||||
<td style="max-width: 300px;">{Message}</td>
|
||||
</tr>
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<!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>MaidBot: {Application} Event Logs</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><span id="emailHeader">MaidBot: {Application} {Level} Event Logs</span></h1>
|
||||
<table id="errors">
|
||||
<thead id="errorTableHeader">
|
||||
<tr style="height: 17px;">
|
||||
<td style="max-width: 50px;font-weight: bolder;">Timestamp</td>
|
||||
<td style="max-width: 300px; font-weight: bolder;">Log Details</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="errorRows">
|
||||
{TableRows}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Primary": {
|
||||
"AppName": "MaidBot Api",
|
||||
"SeqUrl": "http://localhost:5080",
|
||||
"SeqApiKey": "VALUE-IN-KEYVAULT",
|
||||
"NotificationsApiEmail": "https://localhost:50353/api/Email",
|
||||
"FeatureFlags": {
|
||||
"LoggingEventConsumer": true,
|
||||
"RunScheduler": true
|
||||
},
|
||||
"BatchSchedulerCron": "*/5 * * * *",
|
||||
"CorsAllowedOrigins": [
|
||||
"http://localhost:3000",
|
||||
"http://lcu-internal.com",
|
||||
"https://lcu-internal.com",
|
||||
"https://qa-api.lcu-internal.com",
|
||||
"https://uat-api.lcu-internal.com",
|
||||
"https://api.lcu-internal.com"
|
||||
],
|
||||
"LogEmailTemplate": "./Templates/LogEmailTemplate.html",
|
||||
"LogEmailRowTemplate": "./Templates/LogEmailRowTemplate.html"
|
||||
},
|
||||
"ConnectionConfiguration": {
|
||||
"HostUrl": "localhost",
|
||||
"UserName": "guest",
|
||||
"Password": "guest"
|
||||
},
|
||||
"ReceiveEndpoint": {
|
||||
"ReceiveEndpoint": "LCU.MaidBot",
|
||||
"ReceiveEndpointType": "exchange",
|
||||
"PrefetchLimit": 1
|
||||
},
|
||||
"Connections": {
|
||||
"SQL": {
|
||||
"MaidBotDB": "data source=localhost;initial catalog=MaidBot;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user