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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace TransactionProcessor.BusinessLogic.Requests;
[ExcludeFromCodeCoverage]
public record ContractCommands {
public record CreateContractCommand(Guid EstateId,
Guid ContractId,
CreateContractRequest RequestDTO) : IRequest<Result>;

public record AddProductToContractCommand(Guid EstateId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IAggregateService> aggregateService, IEventStoreContext context) {
public ContractDomainService(Func<IAggregateService> aggregateService) {
this.AggregateService = aggregateService();
this.Context = context;
}

#endregion

#region Methods

public async Task<Result> AddProductToContract(ContractCommands.AddProductToContractCommand command, CancellationToken cancellationToken)
{
Models.Contract.ProductType productType = (Models.Contract.ProductType)command.RequestDTO.ProductType;
Expand All @@ -66,7 +54,7 @@ public async Task<Result> 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)
Expand Down Expand Up @@ -157,23 +145,28 @@ public async Task<Result> 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);

Result<EstateAggregate> estateResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get<EstateAggregate>(command.EstateId, ct), command.EstateId, cancellationToken);
if (estateResult.IsFailed)
return ResultHelpers.CreateFailure(estateResult);

Result<ContractAggregate> contractResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest<ContractAggregate>(command.ContractId, ct), command.ContractId, cancellationToken, false);
Result<ContractAggregate> contractResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest<ContractAggregate>(contractId, ct), contractId, cancellationToken, false);
Comment thread
StuartFerguson marked this conversation as resolved.
Resolved
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)
Comment thread
StuartFerguson marked this conversation as resolved.
Resolved
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}]");

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)
Expand All @@ -190,33 +183,5 @@ public async Task<Result> CreateContract(ContractCommands.CreateContractCommand
return Result.Failure(ex.GetExceptionMessages());
}
}

private async Task<Result> 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<String> 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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@

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
{
Expand Down Expand Up @@ -70,5 +63,13 @@
merchantId,
year
});

public static Guid GenerateContractId(Guid estateId, Guid operatorId, String contractName) {

String normalisedContractName = contractName.ToLower().Replace(" ", String.Empty);

Check warning on line 69 in TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs#L69

Define the locale to be used in this string operation.
Comment thread
StuartFerguson marked this conversation as resolved.
Resolved

return IdGenerationService.GenerateUniqueId(new { estateId, operatorId, normalisedContractName });
}

}
}
2 changes: 1 addition & 1 deletion TransactionProcessor.Testing/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ public static IEnumerable<object[]> ContractCases()
SetupSuccess<ContractCommands.CreateContractCommand>(command =>
{
command.EstateId.ShouldBe(EstateId);
command.ContractId.ShouldNotBe(Guid.Empty);
command.RequestDTO.Description.ShouldBe("Contract");
}),
mediator => ContractHandlers.CreateContract(mediator,
Expand Down
4 changes: 1 addition & 3 deletions TransactionProcessor/Handlers/ContractHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ public static async Task<IResult> DisableTransactionFeeForProduct(IMediator medi

public static async Task<IResult> 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);
Expand Down
10 changes: 5 additions & 5 deletions TransactionProcessor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string?>
{
Expand Down
Loading