Skip to content
Merged
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
298 changes: 107 additions & 191 deletions FileProcessor.BusinessLogic.Tests/FileProcessingManagerTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<DebugType>None</DebugType>
<DebugType>Full</DebugType>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
3 changes: 1 addition & 2 deletions FileProcessor.BusinessLogic/Common/ModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ public List<FileImportLog> ConvertFrom(Guid estateId,
/// <returns></returns>
public FileImportLog ConvertFrom(Guid estateId,
TransactionProcessor.Database.Entities.FileImportLog importLog,
List<(TransactionProcessor.Database.Entities.FileImportLogFile, TransactionProcessor.Database.Entities.File, Merchant)> importLogFilesList)
{
List<(TransactionProcessor.Database.Entities.FileImportLogFile, TransactionProcessor.Database.Entities.File, Merchant)> importLogFilesList) {
FileImportLog model = new FileImportLog();

model.FileImportLogId = importLog.FileImportLogId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
<PackageReference Include="SecurityService.Client" Version="2025.7.1" />
<PackageReference Include="Shared" Version="2025.7.10" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.10" />
<PackageReference Include="Shared" Version="2025.7.12" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.12" />
<PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Shared.EventStore" Version="2025.7.10" />
<PackageReference Include="Shared.EventStore" Version="2025.7.12" />
<PackageReference Include="System.IO.Abstractions" Version="22.0.14" />
<PackageReference Include="TransactionProcessor.Client" Version="2025.7.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.5" />
Expand Down
43 changes: 24 additions & 19 deletions FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@

namespace FileProcessor.BusinessLogic.Managers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Common;
using FileAggregate;
using FileProcessor.Models;
using Microsoft.EntityFrameworkCore;
using Shared.DomainDrivenDesign.EventSourcing;
using Shared.EntityFramework;
using Shared.EventStore.Aggregate;
using Shared.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using FileImportLog = FileProcessor.Models.FileImportLog;

/// <summary>
Expand All @@ -28,19 +29,15 @@
public class FileProcessorManager : IFileProcessorManager
{
#region Fields

/// <summary>
/// The file profiles
/// </summary>
private readonly List<FileProfile> FileProfiles;

private readonly Shared.EntityFramework.IDbContextFactory<EstateManagementContext> DbContextFactory;
private readonly IDbContextResolver<EstateManagementContext> Resolver;
private static readonly String EstateManagementDatabaseName = "TransactionProcessorReadModel";

private readonly IModelFactory ModelFactory;

private readonly IAggregateRepository<FileAggregate, DomainEvent> FileAggregateRepository;

private const String ConnectionStringIdentifier = "EstateReportingReadModel";

#endregion

Expand All @@ -53,12 +50,12 @@
/// <param name="dbContextFactory">The database context factory.</param>
/// <param name="modelFactory">The model factory.</param>
public FileProcessorManager(List<FileProfile> fileProfiles,
Shared.EntityFramework.IDbContextFactory<EstateManagementContext> dbContextFactory,
IDbContextResolver<EstateManagementContext> resolver,
IModelFactory modelFactory,
IAggregateRepository<FileAggregate, DomainEvent> fileAggregateRepository)
{
this.FileProfiles = fileProfiles;
this.DbContextFactory = dbContextFactory;
this.Resolver = resolver;
this.ModelFactory = modelFactory;
this.FileAggregateRepository = fileAggregateRepository;
}
Expand All @@ -67,12 +64,12 @@

#region Methods

public async Task<Result<List<FileProfile>>> GetAllFileProfiles(CancellationToken cancellationToken)

Check warning on line 67 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 67 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
return Result.Success(this.FileProfiles);
}

public async Task<Result<FileProfile>> GetFileProfile(Guid fileProfileId,

Check warning on line 72 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 72 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
CancellationToken cancellationToken)
{
FileProfile fileProfile = this.FileProfiles.SingleOrDefault(f => f.FileProfileId == fileProfileId);
Expand All @@ -83,13 +80,20 @@

}

private async Task<EstateManagementContext> GetContext(Guid estateId)

Check warning on line 83 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 83 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString());

Check warning on line 85 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 85 in FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
return resolvedContext.Context;
}


public async Task<Result<List<FileImportLog>>> GetFileImportLogs(Guid estateId,
DateTime startDateTime,
DateTime endDateTime,
Guid? merchantId,
CancellationToken cancellationToken)
{
EstateManagementContext context = await this.DbContextFactory.GetContext(estateId, ConnectionStringIdentifier, cancellationToken);
EstateManagementContext context = await this.GetContext(estateId);

List<TransactionProcessor.Database.Entities.FileImportLog> importLogQuery =
await context.FileImportLogs.Where(f => f.ImportLogDateTime >= startDateTime).ToListAsync(cancellationToken);
Expand Down Expand Up @@ -130,8 +134,8 @@
Guid? merchantId,
CancellationToken cancellationToken)
{
EstateManagementContext context = await this.DbContextFactory.GetContext(estateId, ConnectionStringIdentifier, cancellationToken);
EstateManagementContext context = await this.GetContext(estateId);

// Fetch the import log entry
TransactionProcessor.Database.Entities.FileImportLog importLogQuery = await context.FileImportLogs
.SingleOrDefaultAsync(f => f.FileImportLogId == fileImportLogId, cancellationToken);
Expand All @@ -158,7 +162,8 @@
entityData.Add((file.fileImportLogFile, file.file, file.merchant));
}

return this.ModelFactory.ConvertFrom(estateId, importLogQuery, entityData);
var x = this.ModelFactory.ConvertFrom(estateId, importLogQuery, entityData);
return Result.Success(x);
}

public async Task<Result<FileDetails>> GetFile(Guid fileId,
Expand All @@ -179,7 +184,7 @@

FileDetails fileDetails = fileAggregate.GetFile();

EstateManagementContext context = await this.DbContextFactory.GetContext(estateId, ConnectionStringIdentifier, cancellationToken);
EstateManagementContext context = await this.GetContext(estateId);

Merchant merchant = await context.Merchants
.SingleOrDefaultAsync(m => m.MerchantId == fileDetails.MerchantId, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@
return Result.NotFound($"No contracts found for Merchant Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId}");
}

ContractResponse? contract = null;

Check warning on line 363 in FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 363 in FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
if (fileProfile.OperatorName == "Voucher")
{
contract = contracts.SingleOrDefault(c => c.Description.Contains(operatorName));
Expand All @@ -375,7 +375,7 @@
return Result.NotFound($"No merchant contract for operator Id {operatorName} found for Merchant Id {merchant.MerchantId}");
}

ContractProduct? product = contract.Products.SingleOrDefault(p => p.Value == null); // TODO: Is this enough or should the name be used and stored in file profile??

Check warning on line 378 in FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 378 in FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs

View workflow job for this annotation

GitHub Actions / Build and Test Pull Requests

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

if (product == null)
{
Expand Down Expand Up @@ -519,6 +519,7 @@
inProgressFile.MoveTo($"{fileProfile.ProcessedDirectory}/{inProgressFile.Name}", true);
}
else {
Logger.LogWarning($"About to move file {inProgressFile.Name} to [{fileProfile.FailedDirectory}]. Reason(s) [{String.Join(",", result.Errors)}]");
inProgressFile.MoveTo($"{fileProfile.FailedDirectory}/{inProgressFile.Name}", true);
}

Expand Down
4 changes: 2 additions & 2 deletions FileProcessor.Client/FileProcessor.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ClientProxyBase" Version="2025.7.10" />
<PackageReference Include="Shared.Results" Version="2025.7.10" />
<PackageReference Include="ClientProxyBase" Version="2025.7.12" />
<PackageReference Include="Shared.Results" Version="2025.7.12" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.10" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.12" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<ItemGroup>
<PackageReference Include="Grpc.Net.Client" Version="2.71.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.10" />
<PackageReference Include="Shared.EventStore" Version="2025.7.10" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.12" />
<PackageReference Include="Shared.EventStore" Version="2025.7.12" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.10" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.7.12" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Grpc.Net.Client" Version="2.71.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
<PackageReference Include="Shared.EventStore" Version="2025.7.10" />
<PackageReference Include="Shared.EventStore" Version="2025.7.12" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Shared.IntegrationTesting" Version="2025.7.10" />
<PackageReference Include="Shared.IntegrationTesting" Version="2025.7.12" />
<PackageReference Include="TransactionProcessor.IntegrationTesting.Helpers" Version="2025.7.1" />
</ItemGroup>

Expand Down
10 changes: 10 additions & 0 deletions FileProcessor.IntegrationTests/Common/DockerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,17 @@ public override async Task CreateSubscriptions(){
}
}

protected override EventStoreClientSettings ConfigureEventStoreSettings()
{
string str = $"esdb://127.0.0.1:{this.EventStoreHttpPort}?tls=false&tlsVerifyCert=false&defaultDeadline=30000";
if (this.IsSecureEventStore) {
str = $"esdb://admin:changeit@127.0.0.1:{this.EventStoreHttpPort}?tls=true&tlsVerifyCert=false&defaultDeadline=30000";
}
return EventStoreClientSettings.Create(str);
}

public override async Task StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices){

await base.StartContainersForScenarioRun(scenarioName, dockerServices);

// Setup the base address resolvers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
<PackageReference Include="Reqnroll.NUnit" Version="2.4.1" />
<PackageReference Include="SecurityService.Client" Version="2025.7.1" />
<PackageReference Include="SecurityService.IntegrationTesting.Helpers" Version="2025.7.1" />
<PackageReference Include="Shared" Version="2025.7.10" />
<PackageReference Include="Shared.IntegrationTesting" Version="2025.7.10" />
<PackageReference Include="Shared" Version="2025.7.12" />
<PackageReference Include="Shared.IntegrationTesting" Version="2025.7.12" />
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="TransactionProcessor.Client" Version="2025.7.1" />
<PackageReference Include="TransactionProcessor.IntegrationTesting.Helpers" Version="2025.7.1" />
Expand Down
5 changes: 3 additions & 2 deletions FileProcessor.Testing/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ public static FileProfile GetFileProfile(String operatorName)

public static List<FileProfile> FileProfiles => new List<FileProfile>
{
FileProfileSafaricom
FileProfileSafaricom,
TestData.FileProfile
};
public static Guid SafaricomFileProfileId = Guid.Parse("079F1FF5-F51E-4BE0-AF4F-2D4862E6D34F");
public static String SafaricomProfileName = "Safaricom Profile";
Expand Down Expand Up @@ -321,7 +322,7 @@ public static FileAggregate GetFileAggregateWithLinesAlreadyProcessed()
["AppSettings:TemporaryFileLocation"] = "C:\\Temp",
["AppSettings:FileProfilePollingWindowInSeconds"] = "30",
["ConnectionStrings:HealthCheck"] = "HeathCheckConnString",
["ConnectionStrings:EstateReportingReadModel"] = "EstateReportingReadModel",
["ConnectionStrings:TransactionProcessorReadModel"] = "EstateReportingReadModel",
["SecurityConfiguration:Authority"] = "https://127.0.0.1",
["EventStoreSettings:ConnectionString"] = "esdb://127.0.0.1:2113",
["EventStoreSettings:ConnectionName"] = "UnitTestConnection",
Expand Down
36 changes: 15 additions & 21 deletions FileProcessor/Bootstrapper/RepositoryRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

namespace FileProcessor.Bootstrapper;

using System;
using System.Diagnostics.CodeAnalysis;
using BusinessLogic.Common;
using BusinessLogic.Managers;
using FileAggregate;
using FileImportLogAggregate;
using Lamar;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Shared.DomainDrivenDesign.EventSourcing;
using Shared.EntityFramework;
using Shared.EntityFramework.ConnectionStringConfiguration;
Expand All @@ -19,6 +19,8 @@ namespace FileProcessor.Bootstrapper;
using Shared.EventStore.SubscriptionWorker;
using Shared.General;
using Shared.Repositories;
using System;
using System.Diagnostics.CodeAnalysis;

[ExcludeFromCodeCoverage]
public class RepositoryRegistry : ServiceRegistry
Expand All @@ -30,38 +32,30 @@ public class RepositoryRegistry : ServiceRegistry
/// </summary>
public RepositoryRegistry()
{
Boolean useConnectionStringConfig = bool.Parse(ConfigurationReader.GetValue("AppSettings", "UseConnectionStringConfig"));

if (useConnectionStringConfig)
this.AddSingleton(typeof(IDbContextResolver<>), typeof(DbContextResolver<>));
if (Startup.WebHostEnvironment.IsEnvironment("IntegrationTest") || Startup.Configuration.GetValue<Boolean>("ServiceOptions:UseInMemoryDatabase") == true)
{
String connectionStringConfigurationConnString = ConfigurationReader.GetConnectionString("ConnectionStringConfiguration");
this.AddSingleton<IConnectionStringConfigurationRepository, ConnectionStringConfigurationRepository>();
this.AddTransient(c => { return new ConnectionStringConfigurationContext(connectionStringConfigurationConnString); });

// TODO: Read this from a the database and set
this.AddDbContext<EstateManagementContext>(builder => builder.UseInMemoryDatabase("TransactionProcessorReadModel"));
}
else
{
String connectionString = Startup.Configuration.GetValue<String>("EventStoreSettings:ConnectionString");
this.AddDbContext<EstateManagementContext>(options =>
options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel")));
}

this.AddEventStoreProjectionManagementClient(connectionString);
this.AddEventStorePersistentSubscriptionsClient(connectionString);
String connectionString = Startup.Configuration.GetValue<String>("EventStoreSettings:ConnectionString");

this.AddEventStoreClient(connectionString);
this.AddSingleton<IConnectionStringConfigurationRepository, ConfigurationReaderConnectionStringRepository>();
}
this.AddEventStoreProjectionManagementClient(connectionString);
this.AddEventStorePersistentSubscriptionsClient(connectionString);

this.AddEventStoreClient(connectionString);

this.AddSingleton<IEventStoreContext, EventStoreContext>();

this.AddSingleton<IAggregateRepository<FileAggregate, DomainEvent>, AggregateRepository<FileAggregate, DomainEvent>>();
this.AddSingleton<IAggregateRepository<FileImportLogAggregate, DomainEvent>,
AggregateRepository<FileImportLogAggregate, DomainEvent>>();

this.AddSingleton<IDbContextFactory<EstateManagementContext>, DbContextFactory<EstateManagementContext>>();
this.AddSingleton<Func<String, EstateManagementContext>>(cont => connectionString =>
{
return new EstateManagementContext(connectionString);
});
this.AddSingleton<IFileProcessorManager, FileProcessorManager>();

this.AddSingleton<Func<String, Int32, ISubscriptionRepository>>(cont => (esConnString, cacheDuration) => {
Expand Down
6 changes: 3 additions & 3 deletions FileProcessor/FileProcessor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ClientProxyBase" Version="2025.7.10" />
<PackageReference Include="ClientProxyBase" Version="2025.7.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageReference Include="Lamar" Version="15.0.0" />
<PackageReference Include="Lamar.Microsoft.DependencyInjection" Version="15.0.0" />
Expand All @@ -19,12 +19,12 @@
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Shared" Version="2025.7.10" />
<PackageReference Include="Shared" Version="2025.7.12" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
<PackageReference Include="Shared.Results.Web" Version="2025.7.10" />
<PackageReference Include="Shared.Results.Web" Version="2025.7.12" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="8.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.3" />
Expand Down
Loading