Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Poc.TextProcessor.Business.Logic.Abstractions/ITextLogic.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using Poc.TextProcessor.ResourceAccess.Contracts;
using Poc.TextProcessor.ResourceAccess.Contracts.Collections;

namespace Poc.TextProcessor.Business.Logic.Abstractions
{
public interface ITextLogic
{
TextCollection Get();
Text Get(int id);
void Remove(int id);
Text GetRandom();
Statistics GetStatistics(string textContent);
}
Expand Down
18 changes: 18 additions & 0 deletions Poc.TextProcessor.Business.Logic/TextLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using Poc.TextProcessor.Business.Logic.Abstractions;
using Poc.TextProcessor.Business.Logic.Base;
using Poc.TextProcessor.CrossCutting.Utils.Constants;
using Poc.TextProcessor.CrossCutting.Utils.Helpers;
using Poc.TextProcessor.ResourceAccess.Contracts;
using Poc.TextProcessor.ResourceAccess.Contracts.Collections;
using Poc.TextProcessor.ResourceAccess.Mappers;
using Poc.TextProcessor.ResourceAccess.Repositories.Abstractions;

Expand All @@ -17,9 +19,25 @@ public class TextLogic(ITextRepository textRepository, ITextMapper textMapper) :
public Text Get(int id)
{
var textDomain = _textRepository.Get(id);
HttpStatusHelper.NotFoundException(textDomain, nameof(textDomain));

return _textMapper.Map(textDomain);
}

public void Remove(int id)
{
var textDomain = _textRepository.Get(id);
HttpStatusHelper.NotFoundException(textDomain, nameof(textDomain));

_textRepository.Remove(id);
}

public TextCollection Get()
{
var textDomains = _textRepository.Get();
return _textMapper.MapCollection(textDomains);
}

public Text GetRandom()
{
var random = new Random();
Expand Down
13 changes: 13 additions & 0 deletions Poc.TextProcessor.CrossCutting.Utils/Helpers/HttpStatusHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Poc.TextProcessor.CrossCutting.Utils.Helpers
{
public static class HttpStatusHelper
{
public static void NotFoundException(object entity, string entityName)
{
if (entity == null)
{
throw new KeyNotFoundException($"{entityName} not found.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Poc.TextProcessor.IntegrityAssurance.Core.Contracts.Collections
{
public record TextCollection(IEnumerable<Text> Items, int TotalCount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,31 @@ public static class Text
{
private static string BaseEndpoint => "Text";

#region Options Endpoint
#region Options Endpoints
public static string OptionsEndpoint => $"{BaseEndpoint}/Options";
public static string OptionsInvalidEndpoint => $"{BaseEndpoint}/Options/InvalidRequesttt";
#endregion

#region Ordered Text Endpoint
#region Ordered Text Endpoints
public static string OrderedTextEndpoint(string text, string orderOption) => $"{BaseEndpoint}/OrderedText?textToOrder={text}&orderOption={orderOption}";
public static string OrderedTextInvalidEndpoint => $"{BaseEndpoint}/OrderedText/1/2";
public static string OrderedTextBadRequestEndpoint => $"{BaseEndpoint}/OrderedText?textToOrder=&orderOption=";
#endregion

#region Statitics Endpoint
#region Get Text Endpoints
public static string GetEndpoint(int id) => $"{BaseEndpoint}/Get?id={id}";
public static string GetInvalidEndpointEndpoint => $"{BaseEndpoint}/Get////";
public static string GetIdTooLongEndpoint => $"{BaseEndpoint}/Get?id=4556566545656655644654656454566545645";
public static string GetAllEndpoint => $"{BaseEndpoint}/GetAll";
#endregion

#region Delete Text Endpoints
public static string DeleteEndpoint(int id) => $"{BaseEndpoint}/Delete?id={id}";
public static string DeleteInvalidEndpoint => $"{BaseEndpoint}/Delete////";
public static string DeleteIdTooLongParametersEndpoint => $"{BaseEndpoint}/Delete?id=4556566545656655644654656454566545645";
#endregion

#region Statitics Endpoints
public static string StatisticsEndpoint(string text) => $"{BaseEndpoint}/Statistics?textToAnalyze={text}";
public static string StatisticsInvalidEndpoint => $"{BaseEndpoint}/Statistics////";
public static string StatisticsNoParametersEndpoint => $"{BaseEndpoint}/Statistics";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Poc.TextProcessor.IntegrityAssurance.Tests.Endpoints.Base;
using System.Net;

namespace Poc.TextProcessor.IntegrityAssurance.Tests.Endpoints.Text
{
public class DeleteTests : TestsBase
{
[Test]
public async Task Delete_When_Id_Exists_Should_Return_NoContent()
{
var existingId = 10003;

var deleteRequest = new RestRequest(Core.Settings.Endpoints.Text.DeleteEndpoint(existingId), Method.Delete);
var deleteResponse = await _client.ExecuteAsync(deleteRequest);

var getRequest = new RestRequest(Core.Settings.Endpoints.Text.GetEndpoint(existingId), Method.Get);
var getResponse = await _client.ExecuteAsync<Core.Contracts.Text>(getRequest);

Assert.That(deleteResponse.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(getResponse.Data, Is.Null);
}

[Test]
public async Task Delete_When_Invalid_Input_Should_Return_BadRequest()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.DeleteIdTooLongParametersEndpoint, Method.Delete);
var response = await _client.ExecuteAsync(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}

[Test]
public async Task Delete_When_Invalid_Input_Should_Return_NotFound()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.DeleteInvalidEndpoint, Method.Delete);
var response = await _client.ExecuteAsync(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Poc.TextProcessor.IntegrityAssurance.Core.Contracts.Collections;
using Poc.TextProcessor.IntegrityAssurance.Core.Settings;
using Poc.TextProcessor.IntegrityAssurance.Tests.Endpoints.Base;
using Poc.TextProcessor.IntegrityAssurance.Tests.Helpers;
using System.Net;

namespace Poc.TextProcessor.IntegrityAssurance.Tests.Endpoints.Text
{
public class GetTests : TestsBase
{
private const int ExpectedGetId = 10002;

[Test]
public async Task GetAll_When_Called_Should_Return_Ok()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.GetAllEndpoint, Method.Get);
var response = await _client.ExecuteAsync<TextCollection>(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(response.ContentType, Is.EqualTo(Headers.ContentType.ApplicationJson));
}

[Test]
public async Task GetAll_When_Called_Should_Return_Inserted_Text()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.GetAllEndpoint, Method.Get);
var response = await _client.ExecuteAsync<TextCollection>(request);
var textCollectionResponse = response.Data;

var expectedTextContent = "Test Expected Result.";
var expectedTextId = 10001;


Assert.That(response.IsSuccessful);

Assert.That(textCollectionResponse.Items.Any());

var textItem = textCollectionResponse.Items.Single(x => x.Id == expectedTextId);

ValidationHelper.AssertIntegrityScriptNotNull(textItem, expectedTextId);

//Compare Expected Result
Assert.That(textItem.Content, Is.EqualTo(expectedTextContent));
}

[Test]
public async Task Get_When_Called_Should_Return_Ok()
{
var request = new RestRequest($"{Core.Settings.Endpoints.Text.GetEndpoint(ExpectedGetId)}", Method.Get);
var response = await _client.ExecuteAsync<TextCollection>(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(response.ContentType, Is.EqualTo(Headers.ContentType.ApplicationJson));
}

[Test]
public async Task Get_When_Called_Should_Return_Inserted_Text()
{
var expectedTextContent = "Test Result for Get by Id.";

var request = new RestRequest($"{Core.Settings.Endpoints.Text.GetEndpoint(ExpectedGetId)}", Method.Get);
var response = await _client.ExecuteAsync<Core.Contracts.Text>(request);
var textItem = response.Data;

Assert.That(response.IsSuccessful);

ValidationHelper.AssertIntegrityScriptNotNull(textItem, ExpectedGetId);

Assert.That(textItem.Content, Is.EqualTo(expectedTextContent));
}

[Test]
public async Task Get_When_Invalid_Input_Should_Return_BadRequest()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.GetIdTooLongEndpoint, Method.Get);
var response = await _client.ExecuteAsync(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}

[Test]
public async Task Get_When_Invalid_Input_Should_Return_NotFound()
{
var request = new RestRequest(Core.Settings.Endpoints.Text.GetInvalidEndpointEndpoint, Method.Get);
var response = await _client.ExecuteAsync(request);

Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Poc.TextProcessor.IntegrityAssurance.Tests.Helpers
{
public static class ValidationHelper
{
public static void AssertIntegrityScriptNotNull<T>(T textItem, int expectedId) where T : class
{
Assert.That(textItem, Is.Not.Null, $"Integrity Assurance Test Script with Id {expectedId} could not be found.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"BaseURL": "http://localhost:5162/"
"BaseURL": "https://localhost:6001/"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,40 @@ public class TextController(ITextService textService, ITextSortService textSortS
private readonly ITextService _textService = textService;
private readonly ITextSortService _textSortService = textSortService;

[HttpGet("GetAll")]
[Produces("application/json", "application/xml")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(TextCollection))]
public IActionResult GetAll()
{
var sortOptions = _textService.Get();
return Ok(sortOptions);
}

[HttpGet("Get")]
[Produces("application/json", "application/xml")]
[ResponseOnException(HttpStatusCode.NotFound, typeof(KeyNotFoundException))]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Text))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult Get(int id)
{
var sortOptions = _textService.Get(id);
return Ok(sortOptions);
}

[HttpDelete("Delete")]
[Produces("application/json", "application/xml")]
[ResponseOnException(HttpStatusCode.NotFound, typeof(KeyNotFoundException))]
[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(Text))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult Delete(int id)
{
_textService.Remove(id);
return NoContent();
}


[HttpGet("Options")]
[Produces("application/json", "application/xml")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SortCollection))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Microsoft.EntityFrameworkCore;
using Poc.TextProcessor.ResourceAccess.Database.Providers.EntityFramework;
using System.Diagnostics;

namespace Poc.TextProcessor.Presentation.RestApi.IntegrityAssurance
{
public static class IntegrityAssuranceInitializer
{
private const string EnvironmentVariableExecutionMode = "ExecutionMode";
private const string ExecutionModeIntegrityAssurance = "IntegrityAssurance";
private const string DockerComposeCommand = "docker-compose";
private const string DockerComposeUpArguments = "up -d";
private const string IntegrityAssuranceBaseDirectory = "IntegrityAssurance";
private const string ScriptsDirectory = "Scripts";
private const string SystemScriptsDirectory = "System";
private const string ScriptsExtensions = "*.sql";

/// <summary>
/// Initializes the application to run in Integrity Assurance mode.
/// This method starts a Docker container, waits for SQL Server to be ready,
/// executes a SQL script to drop the database, and applies any pending migrations.
/// </summary>
/// <param name="app">The WebApplication instance to configure.</param>
public static void InitializeIntegrityAssurance(WebApplication app)
{
if (Environment.GetEnvironmentVariable(EnvironmentVariableExecutionMode) == ExecutionModeIntegrityAssurance)
{
Console.WriteLine("Running in Integrity Assurance mode...");

var dockerUp = Process.Start(DockerComposeCommand, DockerComposeUpArguments);
dockerUp.WaitForExit();

// TODO: Temporary workaround to wait for SQL Server to be ready
Thread.Sleep(30000);

var systemScriptsDirectory = Path.Combine(Directory.GetCurrentDirectory(), IntegrityAssuranceBaseDirectory, ScriptsDirectory, SystemScriptsDirectory);
ExecuteScriptsDirectory(systemScriptsDirectory);

// TODO: Migrations will not apply in NHibernate replace this with the provider interface
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<PocContext>();
dbContext.Database.Migrate();
}

var testScriptsDirectory = Path.Combine(Directory.GetCurrentDirectory(), IntegrityAssuranceBaseDirectory, ScriptsDirectory);
ExecuteScriptsDirectory(testScriptsDirectory);
}
}

private static void ExecuteScriptsDirectory(string scriptsDirectory)
{
var scriptFiles = Directory
.GetFiles(scriptsDirectory, ScriptsExtensions, SearchOption.TopDirectoryOnly)
.OrderBy(f => f);

foreach (var scriptFile in scriptFiles)
{
try
{
ExecuteSqlScript(scriptFile);
}
catch (Exception ex)
{
//TODO: Add a custom exception and stop the execution
Console.WriteLine($"Error while loading script: {scriptFile}. Exception: {ex.Message}");
}
}
}

private static void ExecuteSqlScript(string scriptPath)
{
var sqlCmdProcess = Process.Start(new ProcessStartInfo
{
FileName = "sqlcmd",
Arguments = $"-S localhost -U sa -P YourStrong!Passw0rd -i \"{scriptPath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
});

sqlCmdProcess.WaitForExit();
Console.WriteLine($"SQL script {scriptPath} executed with exit code: {sqlCmdProcess.ExitCode}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
USE [PocTextProcessor];

SET IDENTITY_INSERT [dbo].[Texts] ON;

INSERT INTO [dbo].[Texts]([Id], [Content]) VALUES (10001, N'Test Expected Result.');
INSERT INTO [dbo].[Texts]([Id], [Content]) VALUES (10002, N'Test Result for Get by Id.');
INSERT INTO [dbo].[Texts]([Id], [Content]) VALUES (10003, N'Test Result for Delete.');

SET IDENTITY_INSERT [dbo].[Texts] OFF;
Loading