From 0605e4042710157348fb6b02a023358711d22242 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Thu, 29 Oct 2026 10:26:55 +0000 Subject: [PATCH 1/3] remove the call to a projection on create contract --- .../Services/ContractDomainServiceTests.cs | 74 +------------------ .../Requests/ContractCommands.cs | 1 - .../Services/ContractDomainService.cs | 59 +++------------ .../Services/IdGenerationService.cs | 15 ++-- TransactionProcessor.Testing/TestData.cs | 2 +- .../HandlerTests/WebHandlersTests.cs | 1 - .../Handlers/ContractHandlers.cs | 4 +- 7 files changed, 22 insertions(+), 134 deletions(-) diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs index b8c4a887..e49a91c2 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs @@ -21,12 +21,10 @@ namespace TransactionProcessor.BusinessLogic.Tests.Services public class ContractDomainServiceTests { private ContractDomainService DomainService; private IAggregateServiceImposter AggregateService; - private IEventStoreContextImposter EventStoreContext; public ContractDomainServiceTests() { this.AggregateService = new IAggregateServiceImposter(); - this.EventStoreContext = new IEventStoreContextImposter(); IAggregateService AggregateServiceResolver() => this.AggregateService.Instance(); - this.DomainService = new ContractDomainService(AggregateServiceResolver, this.EventStoreContext.Instance()); + this.DomainService = new ContractDomainService(AggregateServiceResolver); StringSerialiser.Initialise(new SystemTextJsonSerializer(SystemTextJsonSerializer.GetDefaultJsonSerializerOptions())); } @@ -39,9 +37,6 @@ public async Task ContractDomainService_CreateContract_ContractIsCreated() this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); result.IsSuccess.ShouldBeTrue(); @@ -53,11 +48,7 @@ public async Task ContractDomainService_CreateContract_DuplicateContractNameForO this.AggregateService.Get(Arg.Any(), Arg.Any()) .ReturnsAsync(TestData.Aggregates.EstateAggregateWithOperator()); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - String queryResult = - "{\r\n \"total\": 1,\r\n \"contract_Id\": \"3015e4d0-e9a9-49e5-bd55-a5492f193b62\"\r\n}"; - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync(queryResult); - + ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -71,9 +62,6 @@ public async Task ContractDomainService_CreateContract_ContractAlreadyCreated_Re this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -84,8 +72,6 @@ public async Task ContractDomainService_CreateContract_EstateNotCreated_ResultFa { this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyEstateAggregate)); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); @@ -97,8 +83,6 @@ public async Task ContractDomainService_CreateContract_NoOperatorCreatedForEstat { this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); @@ -110,9 +94,6 @@ public async Task ContractDomainService_CreateContract_OperatorNotFoundForEstate { this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); - - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); @@ -126,8 +107,6 @@ public async Task ContractDomainService_AddProductToContract_FixedValue_ProductA this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_FixedValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); @@ -140,9 +119,6 @@ public async Task ContractDomainService_AddProductToContract_FixedValue_Contract this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_FixedValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -156,9 +132,6 @@ public async Task ContractDomainService_AddProductToContract_VariableValue_Produ this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_VariableValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); result.IsSuccess.ShouldBeTrue(); @@ -170,9 +143,6 @@ public async Task ContractDomainService_AddProductToContract_VariableValue_Contr this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_VariableValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -184,9 +154,6 @@ public async Task ContractDomainService_AddProductToContract_VariableValue_Estat this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyEstateAggregate)); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_VariableValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -198,9 +165,6 @@ public async Task ContractDomainService_AddProductToContract_FixedValue_EstateNo this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyEstateAggregate)); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddProductToContractCommand command = TestData.Commands.AddProductToContractCommand_FixedValue; Result result = await this.DomainService.AddProductToContract(command, TestContext.Current.CancellationToken); result.IsFailed.ShouldBeTrue(); @@ -219,9 +183,6 @@ public async Task ContractDomainService_AddTransactionFeeForProductToContract_Tr .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregateWithAProduct())); this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddTransactionFeeForProductToContractCommand command = TestData.Commands.AddTransactionFeeForProductToContractCommand(calculationType, feeType); Result result = await this.DomainService.AddTransactionFeeForProductToContract(command, TestContext.Current.CancellationToken); @@ -239,9 +200,6 @@ public async Task ContractDomainService_AddTransactionFeeForProductToContract_Co this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddTransactionFeeForProductToContractCommand command = TestData.Commands.AddTransactionFeeForProductToContractCommand(calculationType,feeType); Result result = await this.DomainService.AddTransactionFeeForProductToContract(command, TestContext.Current.CancellationToken); @@ -260,9 +218,6 @@ public async Task ContractDomainService_AddTransactionFeeForProductToContract_Pr this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.AddTransactionFeeForProductToContractCommand command = TestData.Commands.AddTransactionFeeForProductToContractCommand(calculationType, feeType); Result result = await this.DomainService.AddTransactionFeeForProductToContract(command, TestContext.Current.CancellationToken); @@ -285,9 +240,6 @@ public async Task ContractDomainService_DisableTransactionFeeForProduct_Transact .ReturnsAsync(TestData.Aggregates.CreatedContractAggregateWithAProductAndTransactionFee(calculationType, feeType)); this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); - ContractCommands.DisableTransactionFeeForProductCommand command = TestData.Commands.DisableTransactionFeeForProductCommand; Result result = await this.DomainService.DisableTransactionFeeForProduct(command, TestContext.Current.CancellationToken); result.IsSuccess.ShouldBeTrue(); @@ -364,21 +316,7 @@ public async Task ContractDomainService_CreateContract_GetContractFailed_ResultI result.IsFailed.ShouldBeTrue(); } - [Fact] - public async Task ContractDomainService_CreateContract_RunTransientQueryFailed_ResultIsFailed() - { - this.AggregateService.Get(Arg.Any(), Arg.Any()) - .ReturnsAsync(TestData.Aggregates.EstateAggregateWithOperator()); - this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) - .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync(Result.Failure()); - - ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; - Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); - result.IsFailed.ShouldBeTrue(); - } - + [Fact] public async Task ContractDomainService_CreateContract_StateChangeFailed_ResultIsFailed() { @@ -386,8 +324,6 @@ public async Task ContractDomainService_CreateContract_StateChangeFailed_ResultI .ReturnsAsync(TestData.Aggregates.EstateAggregateWithOperator()); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; command = command with { @@ -407,8 +343,6 @@ public async Task ContractDomainService_CreateContract_SaveFailed_ResultIsFailed .ReturnsAsync(TestData.Aggregates.EstateAggregateWithOperator()); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); this.AggregateService.Save(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Failure()); @@ -424,8 +358,6 @@ public async Task ContractDomainService_CreateContract_ExceptionThrown_ResultIsF .ReturnsAsync(TestData.Aggregates.EstateAggregateWithOperator()); this.AggregateService.GetLatest(Arg.Any(), Arg.Any()) .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); - this.EventStoreContext.RunTransientQuery(Arg.Any(), Arg.Any()) - .ReturnsAsync("{\r\n \"total\": 0,\r\n \"contract_Id\": \"\"\r\n}"); this.AggregateService.Save(Arg.Any(), Arg.Any()).ThrowsAsync(new Exception()); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; diff --git a/TransactionProcessor.BusinessLogic/Requests/ContractCommands.cs b/TransactionProcessor.BusinessLogic/Requests/ContractCommands.cs index 04a39aa1..32280383 100644 --- a/TransactionProcessor.BusinessLogic/Requests/ContractCommands.cs +++ b/TransactionProcessor.BusinessLogic/Requests/ContractCommands.cs @@ -9,7 +9,6 @@ namespace TransactionProcessor.BusinessLogic.Requests; [ExcludeFromCodeCoverage] public record ContractCommands { public record CreateContractCommand(Guid EstateId, - Guid ContractId, CreateContractRequest RequestDTO) : IRequest; public record AddProductToContractCommand(Guid EstateId, diff --git a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs index 72662c6d..89d76a9a 100644 --- a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs @@ -32,25 +32,13 @@ public interface IContractDomainService } public class ContractDomainService : IContractDomainService { - - #region Fields - private readonly IAggregateService AggregateService; - private readonly IEventStoreContext Context; - - #endregion - - #region Constructors + - public ContractDomainService(Func aggregateService, IEventStoreContext context) { + public ContractDomainService(Func aggregateService) { this.AggregateService = aggregateService(); - this.Context = context; } - #endregion - - #region Methods - public async Task AddProductToContract(ContractCommands.AddProductToContractCommand command, CancellationToken cancellationToken) { Models.Contract.ProductType productType = (Models.Contract.ProductType)command.RequestDTO.ProductType; @@ -157,23 +145,22 @@ public async Task CreateContract(ContractCommands.CreateContractCommand { try { + // Generate the Contract Id + Guid contractId = IdGenerationService.GenerateContractId(command.EstateId, command.RequestDTO.OperatorId, command.RequestDTO.Description); + Result estateResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken); if (estateResult.IsFailed) return ResultHelpers.CreateFailure(estateResult); - Result contractResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.ContractId, ct), command.ContractId, cancellationToken, false); + Result contractResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(contractId, ct), contractId, cancellationToken, false); if (contractResult.IsFailed) return ResultHelpers.CreateFailure(contractResult); - + ContractAggregate contractAggregate = contractResult.Data; Models.Estate.Estate estate = estateResult.Data.GetEstate(); - - Result preConditionResult = await this.ValidateContractCreationPreConditions(command, estate, cancellationToken); - if (preConditionResult.IsFailed) - return preConditionResult; - + if (contractAggregate.IsCreated) - return Result.Forbidden($"Contract Id [{command.ContractId}] already created for estate [{estate.Name}]"); + return Result.Forbidden($"Contract Name [{command.RequestDTO.Description}] already created for estate [{estate.Name}] and Operator [{command.RequestDTO.OperatorId}]"); Result stateResult = contractAggregate.Create(command.EstateId, command.RequestDTO.OperatorId, command.RequestDTO.Description); if (stateResult.IsFailed) @@ -190,33 +177,5 @@ public async Task CreateContract(ContractCommands.CreateContractCommand return Result.Failure(ex.GetExceptionMessages()); } } - - private async Task ValidateContractCreationPreConditions(ContractCommands.CreateContractCommand command, Models.Estate.Estate estate, CancellationToken cancellationToken) - { - if (estate.Operators.Any(o => o.OperatorId == command.RequestDTO.OperatorId) == false) - return Result.NotFound($"Unable to create a contract for an operator that is not setup on estate [{estate.Name}]"); - - String projection = - $"fromCategory(\"ContractAggregate\")\n.when({{\n $init: function (s, e) {{\n return {{\n total: 0,\n contractId: 0\n }};\n }},\n 'ContractCreatedEvent': function(s,e){{\n // Check if it matches\n if (e.data.description === '{command.RequestDTO.Description}' \n && e.data.operatorId === '{command.RequestDTO.OperatorId}'){{\n s.total += 1;\n s.contract_id = e.data.contractId\n }}\n }}\n}})"; - - Result result = await this.Context.RunTransientQuery(projection, cancellationToken); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - String resultString = result.Data; - if (String.IsNullOrEmpty(resultString) == false) { - var resultObject = new { total = 0, contract_Id = String.Empty }; - - var queryResult = StringSerialiser.DeserialiseAnonymousType(resultString, resultObject, new SerialiserOptions(SerialiserPropertyFormat.SnakeCase)); - - if (Guid.TryParse(queryResult.contract_Id, out Guid contractIdResult) && contractIdResult != Guid.Empty){ - return Result.Conflict($"Contract Description {command.RequestDTO.Description} already in use for operator {command.RequestDTO.OperatorId}"); - } - } - - return Result.Success(); - } - - #endregion } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs b/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs index ab633172..f8d90863 100644 --- a/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs +++ b/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs @@ -31,13 +31,6 @@ public class IdGenerationService public static Guid GenerateEventId(Object o) => IdGenerationService.GenerateUniqueId(o); - public static Guid GenerateFloatAggregateId(Guid estateId, Guid contractId, Guid productId) => - IdGenerationService.GenerateUniqueId(new{ - estateId, - contractId, - productId - }); - public static Guid GenerateFloatActivityAggregateId(Guid estateId, Guid floatId, DateTime dateTime) => IdGenerationService.GenerateUniqueId(new { @@ -70,5 +63,13 @@ public static Guid GenerateMerchantScheduleAggregateId(Guid estateId, Guid merch merchantId, year }); + + public static Guid GenerateContractId(Guid estateId, Guid operatorId, String contractName) { + + String normalisedContractName = contractName.ToLower().Replace(" ", String.Empty); + + return IdGenerationService.GenerateUniqueId(new { estateId, operatorId, normalisedContractName }); + } + } } diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 7c43a643..933a1f6a 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -2193,7 +2193,7 @@ public static ContractCommands.AddTransactionFeeForProductToContractCommand new(EstateId, ContractId, VariableContractProductId, TestData.AddProductToContractRequest_VariableValue); public static ContractCommands.CreateContractCommand CreateContractCommand => - new ContractCommands.CreateContractCommand(EstateId, ContractId, CreateContractRequest); + new ContractCommands.CreateContractCommand(EstateId, CreateContractRequest); public static EstateCommands.CreateEstateUserCommand CreateEstateUserCommand => new EstateCommands.CreateEstateUserCommand(TestData.EstateId, TestData.CreateEstateUserRequest); public static EstateCommands.AddOperatorToEstateCommand AddOperatorToEstateCommand => new EstateCommands.AddOperatorToEstateCommand(TestData.EstateId, TestData.AssignOperatorRequestToEstate); public static EstateCommands.CreateEstateCommand CreateEstateCommand => new EstateCommands.CreateEstateCommand(TestData.CreateEstateRequest); diff --git a/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs b/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs index 2be903b1..e75310d7 100644 --- a/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs +++ b/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs @@ -232,7 +232,6 @@ public static IEnumerable ContractCases() SetupSuccess(command => { command.EstateId.ShouldBe(EstateId); - command.ContractId.ShouldNotBe(Guid.Empty); command.RequestDTO.Description.ShouldBe("Contract"); }), mediator => ContractHandlers.CreateContract(mediator, diff --git a/TransactionProcessor/Handlers/ContractHandlers.cs b/TransactionProcessor/Handlers/ContractHandlers.cs index bc24edce..9d72007d 100644 --- a/TransactionProcessor/Handlers/ContractHandlers.cs +++ b/TransactionProcessor/Handlers/ContractHandlers.cs @@ -63,9 +63,7 @@ public static async Task DisableTransactionFeeForProduct(IMediator medi public static async Task CreateContract(IMediator mediator, HttpContext ctx, Guid estateId, CreateContractRequest createContractRequest, CancellationToken cancellationToken) { - Guid contractId = Guid.NewGuid(); - - ContractCommands.CreateContractCommand command = new(estateId, contractId, createContractRequest); + ContractCommands.CreateContractCommand command = new(estateId, createContractRequest); Result result = await mediator.Send(command, cancellationToken); return ResponseFactory.FromResult(result); From e4c33cbe138fa37dd80637a8fdb8993bf096b8e7 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Thu, 29 Oct 2026 11:16:52 +0000 Subject: [PATCH 2/3] fix pr comments --- .../Services/ContractDomainServiceTests.cs | 22 +++++++++++++++++++ .../Services/ContractDomainService.cs | 10 +++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs index e49a91c2..96536965 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs @@ -42,6 +42,26 @@ public async Task ContractDomainService_CreateContract_ContractIsCreated() result.IsSuccess.ShouldBeTrue(); } + [Theory] + [InlineData(null)] + [InlineData("")] + public async Task ContractDomainService_CreateContract_DescriptionIsNullOrEmpty_ResultIsInvalid(String description) + { + ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand with + { + RequestDTO = new CreateContractRequest + { + OperatorId = TestData.OperatorId, + Description = description + } + }; + + Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); + + result.Status.ShouldBe(ResultStatus.Invalid); + result.Message.ShouldBe("Contract description must not be null or empty"); + } + [Fact] public async Task ContractDomainService_CreateContract_DuplicateContractNameForOperator_ResultFailed() { @@ -83,6 +103,7 @@ public async Task ContractDomainService_CreateContract_NoOperatorCreatedForEstat { this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); + this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); @@ -94,6 +115,7 @@ public async Task ContractDomainService_CreateContract_OperatorNotFoundForEstate { this.AggregateService.GetLatest(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.EmptyContractAggregate())); this.AggregateService.Get(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success(TestData.Aggregates.CreatedEstateAggregate())); + this.AggregateService.Save(Arg.Any(), Arg.Any()).ReturnsAsync(Result.Success()); ContractCommands.CreateContractCommand command = TestData.Commands.CreateContractCommand; Result result = await this.DomainService.CreateContract(command, TestContext.Current.CancellationToken); diff --git a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs index 89d76a9a..e3120096 100644 --- a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs @@ -54,7 +54,7 @@ public async Task AddProductToContract(ContractCommands.AddProductToCont if (contractAggregate.IsCreated == false) { return Result.Forbidden($"Contract Id [{command.ContractId}] must be created to add products"); } - + Result stateResult = command.RequestDTO.Value.HasValue switch { true => contractAggregate.AddFixedValueProduct(command.ProductId, command.RequestDTO.ProductName, command.RequestDTO.DisplayText, command.RequestDTO.Value.Value, productType), _ => contractAggregate.AddVariableValueProduct(command.ProductId, command.RequestDTO.ProductName, command.RequestDTO.DisplayText, productType) @@ -145,6 +145,9 @@ public async Task CreateContract(ContractCommands.CreateContractCommand { try { + if (String.IsNullOrEmpty(command.RequestDTO.Description)) + return Result.Invalid("Contract description must not be null or empty"); + // Generate the Contract Id Guid contractId = IdGenerationService.GenerateContractId(command.EstateId, command.RequestDTO.OperatorId, command.RequestDTO.Description); @@ -162,6 +165,9 @@ public async Task CreateContract(ContractCommands.CreateContractCommand if (contractAggregate.IsCreated) return Result.Forbidden($"Contract Name [{command.RequestDTO.Description}] already created for estate [{estate.Name}] and Operator [{command.RequestDTO.OperatorId}]"); + if (estate.Operators.Any(o => o.OperatorId == command.RequestDTO.OperatorId) == false) + return Result.NotFound($"Unable to create a contract for an operator that is not setup on estate [{estate.Name}]"); + Result stateResult = contractAggregate.Create(command.EstateId, command.RequestDTO.OperatorId, command.RequestDTO.Description); if (stateResult.IsFailed) return ResultHelpers.CreateFailure(stateResult); @@ -178,4 +184,4 @@ public async Task CreateContract(ContractCommands.CreateContractCommand } } } -} \ No newline at end of file +} From e11f9652170b30491d0d4d167ce89f254c2eb726 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Thu, 29 Oct 2026 11:42:05 +0000 Subject: [PATCH 3/3] fix inotify isssue --- TransactionProcessor/Program.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TransactionProcessor/Program.cs b/TransactionProcessor/Program.cs index e3fb6a34..d4daeb6f 100644 --- a/TransactionProcessor/Program.cs +++ b/TransactionProcessor/Program.cs @@ -126,11 +126,11 @@ private static void ConfigureConfiguration(WebApplicationBuilder builder) builder.Configuration.SetBasePath(assemblyFile.Directory!.FullName) .AddJsonFile("hosting.json", optional: true) .AddJsonFile($"hosting.{builder.Environment.EnvironmentName}.json", optional: true) - .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"/home/txnproc/config/appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile("/home/txnproc/config/appsettings.local.json", optional: true, reloadOnChange: true) + .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true) + .AddJsonFile($"/home/txnproc/config/appsettings.{builder.Environment.EnvironmentName}.json", optional: true) + .AddJsonFile("appsettings.json", optional: true) + .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true) + .AddJsonFile("/home/txnproc/config/appsettings.local.json", optional: true) .AddEnvironmentVariables() .AddInMemoryCollection(new Dictionary {