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
Original file line number Diff line number Diff line change
Expand Up @@ -479,5 +479,23 @@ public async Task TransactionProcessorManager_GetMerchants_RepoCallFails_Excepti
Result<List<Merchant>> getMerchantsResult = await this.TransactionProcessorManager.GetMerchants(TestData.EstateId, CancellationToken.None);
getMerchantsResult.IsFailed.ShouldBeTrue();
}

[Fact]
public async Task TransactionProcessorManager_GetMerchantLiveBalance_ResultSuccess()
{
this.AggregateService.Setup(a => a.GetLatest<MerchantBalanceAggregate>(It.IsAny<Guid>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Success(TestData.Aggregates.MerchantBalanceAggregateWithCredit()));

Result<Decimal> getLiveMerchantBalanceResult = await this.TransactionProcessorManager.GetMerchantLiveBalance(TestData.EstateId, TestData.MerchantId, CancellationToken.None);
getLiveMerchantBalanceResult.IsSuccess.ShouldBeTrue();
}

[Fact]
public async Task TransactionProcessorManager_GetMerchantLiveBalance_GetFailed_ResultFailed()
{
this.AggregateService.Setup(a => a.GetLatest<MerchantBalanceAggregate>(It.IsAny<Guid>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Failure());

Result<Decimal> getLiveMerchantBalanceResult = await this.TransactionProcessorManager.GetMerchantLiveBalance(TestData.EstateId, TestData.MerchantId, CancellationToken.None);
getLiveMerchantBalanceResult.IsFailed.ShouldBeTrue();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
namespace TransactionProcessor.BusinessLogic.Tests.Mediator;

public class DummyTransactionProcessorManager : ITransactionProcessorManager {
public async Task<Result<Decimal>> GetMerchantLiveBalance(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken) {
return Result.Success(1000.00m); // Dummy balance
}

public async Task<Result<List<Contract>>> GetMerchantContracts(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,9 @@ public async Task MerchantDomainService_MakeMerchantWithdrawal_WithdrawalIsMade(
.Setup(s => s.GetToken(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Success(TestData.TokenResponse()));

this.EventStoreContext.Setup(e => e.GetPartitionStateFromProjection(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Success<String>(JsonConvert.SerializeObject(TestData.MerchantBalanceProjectionState)));
this.AggregateService
.Setup(m => m.GetLatest<MerchantBalanceAggregate>(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Success(TestData.Aggregates.MerchantBalanceAggregateWithCredit()));

var result = await this.DomainService.MakeMerchantWithdrawal(TestData.Commands.MakeMerchantWithdrawalCommand, CancellationToken.None);
result.IsSuccess.ShouldBeTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public interface ITransactionProcessorManager
{
#region Methods

Task<Result<Decimal>> GetMerchantLiveBalance(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken);

Task<Result<List<Contract>>> GetMerchantContracts(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,24 @@ public async Task<Result<Merchant>> GetMerchant(Guid estateId,
return Result.Success(merchantModel);
}

public async Task<Result<Decimal>> GetMerchantLiveBalance(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken) {
Result<MerchantBalanceAggregate> getMerchantBalanceResult = await this.AggregateService.GetLatest<MerchantBalanceAggregate>(merchantId, cancellationToken);

if (getMerchantBalanceResult.Status == ResultStatus.NotFound)
return Result.Success(0.0m);

if (getMerchantBalanceResult.IsFailed)
return ResultHelpers.CreateFailure(getMerchantBalanceResult);

MerchantBalanceAggregate aggregate = getMerchantBalanceResult.Data;
return Result.Success(aggregate.Balance);
}

public async Task<Result<List<Contract>>> GetMerchantContracts(Guid estateId,
Guid merchantId,
CancellationToken cancellationToken)
Guid merchantId,
CancellationToken cancellationToken)
{
Result<List<Contract>> getMerchantContractsResult = await this.TransactionProcessorReadModelRepository.GetMerchantContracts(estateId, merchantId, cancellationToken);
if (getMerchantContractsResult.IsFailed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace TransactionProcessor.BusinessLogic.RequestHandlers;

public class MerchantRequestHandler :
IRequestHandler<MerchantQueries.GetMerchantBalanceQuery, Result<MerchantBalanceState>>,
IRequestHandler<MerchantQueries.GetMerchantLiveBalanceQuery, Result<MerchantBalanceProjectionState1>>,
IRequestHandler<MerchantQueries.GetMerchantLiveBalanceQuery, Result<Decimal>>,
IRequestHandler<MerchantQueries.GetMerchantBalanceHistoryQuery, Result<List<MerchantBalanceChangedEntry>>>,
IRequestHandler<MerchantCommands.SwapMerchantDeviceCommand, Result>,
IRequestHandler<MerchantCommands.AddMerchantContractCommand, Result>,
Expand Down Expand Up @@ -64,17 +64,9 @@ public async Task<Result<MerchantBalanceState>> Handle(MerchantQueries.GetMercha
return await this.MerchantBalanceStateRepository.Load(query.EstateId, query.MerchantId, cancellationToken);
}

public async Task<Result<MerchantBalanceProjectionState1>> Handle(MerchantQueries.GetMerchantLiveBalanceQuery query,
public async Task<Result<Decimal>> Handle(MerchantQueries.GetMerchantLiveBalanceQuery query,
CancellationToken cancellationToken) {

Result<String> result = await this.EventStoreContext.GetPartitionStateFromProjection("MerchantBalanceProjection", $"MerchantBalance-{query.MerchantId:N}", cancellationToken);
if (result.IsFailed)
return Result.NotFound(
$"Merchant Balance not found for Merchant {query.MerchantId} on MerchantBalanceProjection");

MerchantBalanceProjectionState1 projectionState = JsonConvert.DeserializeObject<MerchantBalanceProjectionState1>(result.Data);

return Result.Success(projectionState);
return await this.TransactionProcessorManager.GetMerchantLiveBalance(query.EstateId, query.MerchantId, cancellationToken);
}

public async Task<Result<List<MerchantBalanceChangedEntry>>> Handle(MerchantQueries.GetMerchantBalanceHistoryQuery query,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace TransactionProcessor.BusinessLogic.Requests;
public record MerchantQueries {
public record GetMerchantBalanceQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<MerchantBalanceState>>;

public record GetMerchantLiveBalanceQuery(Guid MerchantId) : IRequest<Result<MerchantBalanceProjectionState1>>;
public record GetMerchantLiveBalanceQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<Decimal>>;

public record GetMerchantBalanceHistoryQuery(Guid EstateId, Guid MerchantId, DateTime StartDate, DateTime EndDate)
: IRequest<Result<List<MerchantBalanceChangedEntry>>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,19 +398,18 @@ public async Task<Result> MakeMerchantWithdrawal(MerchantCommands.MakeMerchantWi
{
return Result.Invalid($"Merchant [{command.MerchantId}] has not made any deposits yet");
}

Result<MerchantBalanceAggregate> getMerchantBalanceResult = await this.AggregateService.GetLatest<MerchantBalanceAggregate>(command.MerchantId, cancellationToken);
Result<MerchantBalanceAggregate> merchantBalanceAggregateResult =
DomainServiceHelper.HandleGetAggregateResult(getMerchantBalanceResult, command.MerchantId, false);
if (merchantBalanceAggregateResult.IsFailed)
return ResultHelpers.CreateFailure(merchantBalanceAggregateResult);

// Now we need to check the merchants balance to ensure they have funds to withdraw
Result<String> getBalanceResult = await this.EventStoreContext.GetPartitionStateFromProjection("MerchantBalanceProjection", $"MerchantBalance-{command.MerchantId:N}", cancellationToken);
if (getBalanceResult.IsFailed)
{
Result.Invalid($"Failed to get Merchant Balance.");
}

MerchantBalanceProjectionState1 projectionState = JsonConvert.DeserializeObject<MerchantBalanceProjectionState1>(getBalanceResult.Data);
MerchantBalanceAggregate merchantBalanceAggregate = merchantBalanceAggregateResult.Data;

if (command.RequestDto.Amount > projectionState.merchant.balance)
if (command.RequestDto.Amount > merchantBalanceAggregate.Balance)
{
return Result.Invalid($"Not enough credit available for withdrawal of [{command.RequestDto.Amount}]. Balance is {projectionState.merchant.balance}");
return Result.Invalid($"Not enough credit available for withdrawal of [{command.RequestDto.Amount}]. Balance is {merchantBalanceAggregate.Balance}");
}

// If we are here we have enough credit to withdraw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,21 +279,21 @@
MerchantResponse merchant = getMerchantResult.Data;
responses.Add(merchant);

string projectionName = "MerchantBalanceProjection";
String partitionId = $"MerchantBalance-{m.Item2:N}";
//string projectionName = "MerchantBalanceProjection";

Check notice on line 282 in TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs#L282

Remove this commented out code.
//String partitionId = $"MerchantBalance-{m.Item2:N}";

dynamic gg = await this.ProjectionManagementClient.GetStateAsync<dynamic>(
projectionName, partitionId);
JsonElement x = (JsonElement)gg;
//dynamic gg = await this.ProjectionManagementClient.GetStateAsync<dynamic>(
// projectionName, partitionId);
//JsonElement x = (JsonElement)gg;

Result<MerchantBalanceResponse>? getMerchantBalanceResult = await this.TransactionProcessorClient.GetMerchantBalance(accessToken, m.Item1, m.Item2, CancellationToken.None);
getMerchantBalanceResult.IsSuccess.ShouldBeTrue();
getMerchantBalanceResult.Data.ShouldNotBeNull();
//Result<MerchantBalanceResponse>? getMerchantBalanceResult = await this.TransactionProcessorClient.GetMerchantBalance(accessToken, m.Item1, m.Item2, CancellationToken.None);
//getMerchantBalanceResult.IsSuccess.ShouldBeTrue();
//getMerchantBalanceResult.Data.ShouldNotBeNull();

// Force a read model database hit
Result<MerchantBalanceResponse>? getMerchantBalanceResult2 = await this.TransactionProcessorClient.GetMerchantBalance(accessToken, m.Item1, m.Item2, CancellationToken.None, liveBalance: false);
getMerchantBalanceResult2.IsSuccess.ShouldBeTrue();
getMerchantBalanceResult2.Data.ShouldNotBeNull();
//// Force a read model database hit
//Result<MerchantBalanceResponse>? getMerchantBalanceResult2 = await this.TransactionProcessorClient.GetMerchantBalance(accessToken, m.Item1, m.Item2, CancellationToken.None, liveBalance: false);
//getMerchantBalanceResult2.IsSuccess.ShouldBeTrue();
//getMerchantBalanceResult2.Data.ShouldNotBeNull();
});
}

Expand Down
9 changes: 4 additions & 5 deletions TransactionProcessor.IntegrationTests/Common/DockerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,10 @@
{
List<String> requiredProjections = new List<String>();

requiredProjections.Add("CallbackHandlerEnricher.js");
requiredProjections.Add("EstateAggregator.js");
requiredProjections.Add("MerchantAggregator.js");
requiredProjections.Add("MerchantBalanceCalculator.js");
requiredProjections.Add("MerchantBalanceProjection.js");
//requiredProjections.Add("EstateAggregator.js");

Check notice on line 227 in TransactionProcessor.IntegrationTests/Common/DockerHelper.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.IntegrationTests/Common/DockerHelper.cs#L227

Remove this commented out code.
//requiredProjections.Add("MerchantAggregator.js");
//requiredProjections.Add("MerchantBalanceCalculator.js");
//requiredProjections.Add("MerchantBalanceProjection.js");

return requiredProjections;
}
Expand Down
14 changes: 7 additions & 7 deletions TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,12 @@

foreach ((EstateDetails, Guid, MakeMerchantDepositRequest) request in requests)
{
Decimal previousMerchantBalance = await this.GetMerchantBalance(request.Item2);
Decimal previousMerchantBalance = await this.GetMerchantBalance(request.Item1.EstateId, request.Item2);

await this.TransactionProcessorSteps.GivenIMakeTheFollowingManualMerchantDeposits(this.TestingContext.AccessToken, request);

await Retry.For(async () => {
Decimal currentMerchantBalance = await this.GetMerchantBalance(request.Item2);
Decimal currentMerchantBalance = await this.GetMerchantBalance(request.Item1.EstateId, request.Item2);

currentMerchantBalance.ShouldBe(previousMerchantBalance + request.Item3.Amount);

Expand Down Expand Up @@ -374,12 +374,12 @@
await this.TransactionProcessorSteps.WhenIAddTheFollowingContractsToTheFollowingMerchants(this.TestingContext.AccessToken, requests);
}

private async Task<Decimal> GetMerchantBalance(Guid merchantId)
private async Task<Decimal> GetMerchantBalance(Guid estateId, Guid merchantId)
{
JsonElement jsonElement = (JsonElement)await this.TestingContext.DockerHelper.ProjectionManagementClient.GetStateAsync<dynamic>("MerchantBalanceProjection", $"MerchantBalance-{merchantId:N}");
JObject jsonObject = JObject.Parse(jsonElement.GetRawText());
decimal balanceValue = jsonObject.SelectToken("merchant.balance").Value<decimal>();
return balanceValue;
Result<MerchantBalanceResponse> result = await this.TestingContext.DockerHelper.TransactionProcessorClient.GetMerchantBalance(this.TestingContext.AccessToken, estateId, merchantId, CancellationToken.None, liveBalance: true);
if (result.IsFailed)
throw new Exception($"Error gettin merchant balance for merchant id {merchantId}");

Check warning on line 381 in TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs#L381

'System.Exception' should not be thrown by user code.
return result.Data.Balance;
}


Expand Down
2 changes: 1 addition & 1 deletion TransactionProcessor.Testing/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2264,7 +2264,7 @@ public static class Queries {

public static MerchantQueries.GetMerchantBalanceQuery GetMerchantBalanceQuery => new(EstateId, MerchantId);

public static MerchantQueries.GetMerchantLiveBalanceQuery GetMerchantLiveBalanceQuery => new(MerchantId);
public static MerchantQueries.GetMerchantLiveBalanceQuery GetMerchantLiveBalanceQuery => new(EstateId, MerchantId);

public static MerchantQueries.GetMerchantBalanceHistoryQuery GetMerchantBalanceHistoryQuery => new MerchantQueries.GetMerchantBalanceHistoryQuery(EstateId, MerchantId, DateTime.MinValue, DateTime.MaxValue);

Expand Down
2 changes: 1 addition & 1 deletion TransactionProcessor/Bootstrapper/MediatorRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private void RegisterMerchantRequestHandler() {
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantContractsQuery, Result<List<Models.Contract.Contract>>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantsQuery, Result<List<Models.Merchant.Merchant>>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetTransactionFeesForProductQuery, Result<List<Models.Contract.ContractProductTransactionFee>>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantLiveBalanceQuery, Result<MerchantBalanceProjectionState1>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantLiveBalanceQuery, Result<Decimal>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantBalanceQuery, Result<MerchantBalanceState>>, MerchantRequestHandler>();
this.AddSingleton<IRequestHandler<MerchantQueries.GetMerchantBalanceHistoryQuery, Result<List<MerchantBalanceChangedEntry>>>, MerchantRequestHandler>();
}
Expand Down
15 changes: 4 additions & 11 deletions TransactionProcessor/Controllers/MerchantController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,22 +161,15 @@ public async Task<IActionResult> GetMerchantBalanceLive([FromRoute] Guid estateI
return this.Forbid();
}

MerchantQueries.GetMerchantLiveBalanceQuery query = new MerchantQueries.GetMerchantLiveBalanceQuery(merchantId);
MerchantQueries.GetMerchantLiveBalanceQuery query = new MerchantQueries.GetMerchantLiveBalanceQuery(estateId, merchantId);

Result<MerchantBalanceProjectionState1> getLiveMerchantBalanceResult = await this.Mediator.Send(query, cancellationToken);
Result<Decimal> getLiveMerchantBalanceResult = await this.Mediator.Send(query, cancellationToken);

if (getLiveMerchantBalanceResult.IsFailed)
{
if (getLiveMerchantBalanceResult.IsFailed) {
return getLiveMerchantBalanceResult.ToActionResultX();
}

Result<MerchantBalanceResponse> result = Result.Success(new MerchantBalanceResponse
{
Balance = getLiveMerchantBalanceResult.Data.merchant.balance,
MerchantId = merchantId,
AvailableBalance = getLiveMerchantBalanceResult.Data.merchant.balance,
EstateId = estateId
});
Result<MerchantBalanceResponse> result = Result.Success(new MerchantBalanceResponse { Balance = getLiveMerchantBalanceResult.Data, MerchantId = merchantId, AvailableBalance = getLiveMerchantBalanceResult.Data, EstateId = estateId });

return result.ToActionResultX();
}
Expand Down
Loading