diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs index 4a0821fb..f3dadbc5 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs @@ -11,10 +11,13 @@ using EstateManagement.Client; using EstateManagement.DataTransferObjects; using EstateManagement.DataTransferObjects.Responses; + using EstateManagement.DataTransferObjects.Responses.Contract; + using EstateManagement.DataTransferObjects.Responses.Merchant; using EventHandling; using FloatAggregate; using MessagingService.Client; using MessagingService.DataTransferObjects; + using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Models; using Moq; @@ -48,6 +51,8 @@ public class TransactionDomainEventHandlerTests private Mock> FloatAggregateRepository; + private Mock MemoryCache; + private TransactionDomainEventHandler TransactionDomainEventHandler; public TransactionDomainEventHandlerTests() @@ -60,6 +65,7 @@ public TransactionDomainEventHandlerTests() this.SecurityServiceClient = new Mock(); this.TransactionReceiptBuilder = new Mock(); this.MessagingServiceClient = new Mock(); + this.MemoryCache = new Mock(); IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); ConfigurationReader.Initialise(configurationRoot); @@ -72,7 +78,8 @@ public TransactionDomainEventHandlerTests() this.TransactionReceiptBuilder.Object, this.MessagingServiceClient.Object, this.SettlementAggregateRepository.Object, - this.FloatAggregateRepository.Object); + this.FloatAggregateRepository.Object, + this.MemoryCache.Object); } [Theory] @@ -107,6 +114,8 @@ public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenComplet this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + this.MemoryCache.Setup(m => m.Set(It.IsAny(), It.IsAny(), It.IsAny())); + await this.TransactionDomainEventHandler.Handle(TestData.TransactionHasBeenCompletedEvent, CancellationToken.None); CalculatedFee merchantFee = transactionAggregate.GetFees().SingleOrDefault(f => f.FeeId == TestData.TransactionFeeId); @@ -118,7 +127,7 @@ public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenComplet merchantFee.IsSettled.ShouldBeFalse(); } - var expectedSettlementDate = settlementSchedule switch{ + DateTime expectedSettlementDate = settlementSchedule switch{ EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Monthly => transactionAggregate.TransactionDateTime.Date.AddMonths(1), EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Weekly => transactionAggregate.TransactionDateTime.Date.AddDays(7), _ => transactionAggregate.TransactionDateTime.Date @@ -129,6 +138,66 @@ public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenComplet nonMerchantFee.ShouldNotBeNull(); } + [Theory] + [InlineData(EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Immediate)] + [InlineData(EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Weekly)] + [InlineData(EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Monthly)] + public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenCompletedEvent_SuccessfulSale_FeesAlreadyCached_EventIsHandled(EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule settlementSchedule) + { + this.FloatAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFloatAggregateWithCostValues); + + TransactionAggregate transactionAggregate = TestData.GetCompletedAuthorisedSaleTransactionAggregate(); + this.TransactionAggregateRepository.Setup(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(transactionAggregate); + + this.FeeCalculationManager.Setup(f => f.CalculateFees(It.IsAny>(), It.IsAny(), It.IsAny())).Returns(new List + { + TestData.CalculatedFeeMerchantFee(TestData.TransactionFeeId), + TestData.CalculatedFeeServiceProviderFee(TestData.TransactionFeeId2) + }); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new EstateManagement.DataTransferObjects.Responses.Merchant.MerchantResponse + { + SettlementSchedule = settlementSchedule, + }); + + this.MemoryCache.Setup(m => m.TryGetValue(It.IsAny(), out It.Ref>.IsAny)) + .Returns((Object key, out List value) => + { + value = TestData.ContractProductTransactionFees; // Set the out parameter + return true; // Return value indicating success + }); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + + this.MemoryCache.Setup(m => m.Set(It.IsAny(), It.IsAny(), It.IsAny())); + + await this.TransactionDomainEventHandler.Handle(TestData.TransactionHasBeenCompletedEvent, CancellationToken.None); + + CalculatedFee merchantFee = transactionAggregate.GetFees().SingleOrDefault(f => f.FeeId == TestData.TransactionFeeId); + merchantFee.ShouldNotBeNull(); + if (settlementSchedule == EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Immediate) + { + merchantFee.IsSettled.ShouldBeTrue(); + } + else + { + merchantFee.IsSettled.ShouldBeFalse(); + } + + DateTime expectedSettlementDate = settlementSchedule switch + { + EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Monthly => transactionAggregate.TransactionDateTime.Date.AddMonths(1), + EstateManagement.DataTransferObjects.Responses.Merchant.SettlementSchedule.Weekly => transactionAggregate.TransactionDateTime.Date.AddDays(7), + _ => transactionAggregate.TransactionDateTime.Date + }; + merchantFee.SettlementDueDate.ShouldBe(expectedSettlementDate); + + CalculatedFee nonMerchantFee = transactionAggregate.GetFees().SingleOrDefault(f => f.FeeId == TestData.TransactionFeeId2); + nonMerchantFee.ShouldNotBeNull(); + } + [Fact] public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenCompletedEvent_SuccessfulSale_MerchantWithNotSetSettlementSchedule_ErrorThrown() { @@ -158,6 +227,8 @@ public async Task TransactionDomainEventHandler_Handle_TransactionHasBeenComplet this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + this.MemoryCache.Setup(m => m.Set(It.IsAny(), It.IsAny(), It.IsAny())); + Should.Throw(async () => { await this.TransactionDomainEventHandler.Handle(TestData.TransactionHasBeenCompletedEvent, CancellationToken.None); @@ -282,5 +353,114 @@ public async Task TransactionDomainEventHandler_Handle_TransactionCostInformatio //{ // await this.TransactionDomainEventHandler.Handle(TestData.SettledMerchantFeeAddedToTransactionEvent(DateTime.MinValue), CancellationToken.None); //} + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_IsNotAuthorised_ReturnsFalse(){ + + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Sale, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + TestData.TransactionAmount); + transactionAggregate.DeclineTransaction(TestData.OperatorId, "111", "SUCCESS", "0000", "SUCCESS"); + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeFalse(); + } + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_IsNotCompelted_ReturnsFalse() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Sale, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + TestData.TransactionAmount); + transactionAggregate.AuthoriseTransaction(TestData.OperatorId, "111", "111", "SUCCESS", "1234", "0000", "SUCCESS"); + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeFalse(); + } + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_IsALogon_ReturnsFalse() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Logon, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + TestData.TransactionAmount); + transactionAggregate.AuthoriseTransactionLocally("111", "0001", "SUCCESS"); + transactionAggregate.CompleteTransaction(); + + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeFalse(); + } + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_NoContractId_ReturnsFalse() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Sale, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + TestData.TransactionAmount); + transactionAggregate.AuthoriseTransaction(TestData.OperatorId, "111", "111", "SUCCESS", "1234", "0000", "SUCCESS"); + transactionAggregate.CompleteTransaction(); + + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeFalse(); + } + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_NullAmount_ReturnsFalse() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Sale, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + null); + transactionAggregate.AddProductDetails(TestData.ContractId, TestData.ProductId); + transactionAggregate.AuthoriseTransaction(TestData.OperatorId, "111", "111", "SUCCESS", "1234", "0000", "SUCCESS"); + transactionAggregate.CompleteTransaction(); + + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeFalse(); + } + + [Fact] + public async Task TransactionDomainEventHandler_RequireFeeCalculation_ReturnsTrue() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, + TransactionType.Sale, TestData.TransactionReference, + TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, + TestData.TransactionAmount); + transactionAggregate.AddProductDetails(TestData.ContractId, TestData.ProductId); + transactionAggregate.AuthoriseTransaction(TestData.OperatorId, "111", "111", "SUCCESS", "1234", "0000", "SUCCESS"); + transactionAggregate.CompleteTransaction(); + + + var result = TransactionDomainEventHandler.RequireFeeCalculation(transactionAggregate); + result.ShouldBeTrue(); + } + + [Theory] + [InlineData(SettlementSchedule.Immediate, "2024-05-01", "2024-05-01")] + [InlineData(SettlementSchedule.NotSet, "2024-05-01", "2024-05-01")] + [InlineData(SettlementSchedule.Weekly, "2024-05-01", "2024-05-08")] + [InlineData(SettlementSchedule.Monthly, "2024-05-01", "2024-06-01")] + public async Task TransactionDomainEventHandler_CalculateSettlementDate_CorrectDateReturned(SettlementSchedule settlementSchedule, String completedDateString, String expectedDateString){ + + DateTime completedDate = DateTime.ParseExact(completedDateString, "yyyy-MM-dd", null); + DateTime expectedDate = DateTime.ParseExact(expectedDateString, "yyyy-MM-dd", null); + DateTime result = TransactionDomainEventHandler.CalculateSettlementDate(settlementSchedule, completedDate); + result.Date.ShouldBe(expectedDate.Date); + } } } + + diff --git a/TransactionProcessor.BusinessLogic/Common/MemoryCacheWrapper.cs b/TransactionProcessor.BusinessLogic/Common/MemoryCacheWrapper.cs new file mode 100644 index 00000000..93a21dc3 --- /dev/null +++ b/TransactionProcessor.BusinessLogic/Common/MemoryCacheWrapper.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TransactionProcessor.BusinessLogic.Common +{ + public interface IMemoryCacheWrapper + { + Boolean TryGetValue(Object Key, out T cache); + void Set(Object key, T cache, MemoryCacheEntryOptions entryOptions); + } + + public class MemoryCacheWrapper : IMemoryCacheWrapper + { + private readonly IMemoryCache MemoryCache; + + public MemoryCacheWrapper(IMemoryCache memoryCache) + { + MemoryCache = memoryCache; + } + + public void Set(Object key, T cache, MemoryCacheEntryOptions entryOptions) + { + MemoryCache.Set(key, cache, entryOptions); + } + + public Boolean TryGetValue(Object Key, out T cache) + { + if (MemoryCache.TryGetValue(Key, out T cachedItem)) + { + cache = cachedItem; + return true; + } + cache = default(T); + return false; + } + } +} diff --git a/TransactionProcessor.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs index 4ddd5ca2..be1a4fe4 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs @@ -16,6 +16,7 @@ using Manager; using MessagingService.Client; using MessagingService.DataTransferObjects; + using Microsoft.Extensions.Caching.Memory; using Models; using SecurityService.Client; using SecurityService.DataTransferObjects.Responses; @@ -68,6 +69,8 @@ public class TransactionDomainEventHandler : IDomainEventHandler private readonly IAggregateRepository FloatAggregateRepository; + private readonly IMemoryCacheWrapper MemoryCache; + private readonly IAggregateRepository TransactionAggregateRepository; /// @@ -91,7 +94,8 @@ public TransactionDomainEventHandler(IAggregateRepository settlementAggregateRepository, - IAggregateRepository floatAggregateRepository) { + IAggregateRepository floatAggregateRepository, + IMemoryCacheWrapper memoryCache) { this.TransactionAggregateRepository = transactionAggregateRepository; this.FeeCalculationManager = feeCalculationManager; this.EstateClient = estateClient; @@ -100,6 +104,7 @@ public TransactionDomainEventHandler(IAggregateRepository false, _ when transactionAggregate.IsAuthorised == false => false, _ when transactionAggregate.IsCompleted == false => false, - _ when transactionAggregate.TransactionType == TransactionType.Logon => false, _ when transactionAggregate.ContractId == Guid.Empty => false, - _ when transactionAggregate.ProductId == Guid.Empty => false, _ when transactionAggregate.TransactionAmount == null => false, _ => true }; @@ -156,17 +160,6 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do TransactionAggregate transactionAggregate = await this.TransactionAggregateRepository.GetLatestVersion(domainEvent.TransactionId, cancellationToken); - - if (transactionAggregate.HasCostsCalculated == false){ - // Calculate the costs - Guid floatAggregateId = IdGenerationService.GenerateFloatAggregateId(transactionAggregate.EstateId, transactionAggregate.ContractId, transactionAggregate.ProductId); - FloatAggregate floatAggregate = await this.FloatAggregateRepository.GetLatestVersion(floatAggregateId, cancellationToken); - - Decimal unitCost = floatAggregate.GetUnitCostPrice(); - Decimal totalCost = transactionAggregate.TransactionAmount.GetValueOrDefault() * unitCost; - - transactionAggregate.RecordCostPrice(unitCost,totalCost); - } if (RequireFeeCalculation(transactionAggregate) == false) return; @@ -200,7 +193,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do foreach (CalculatedFee calculatedFee in merchantFees){ // Determine when the fee should be applied - DateTime settlementDate = this.CalculateSettlementDate(merchant.SettlementSchedule, domainEvent.CompletedDateTime); + DateTime settlementDate = TransactionDomainEventHandler.CalculateSettlementDate(merchant.SettlementSchedule, domainEvent.CompletedDateTime); transactionAggregate.AddFeePendingSettlement(calculatedFee, settlementDate); @@ -288,16 +281,42 @@ private async Task HandleSpecificDomainEvent(MerchantFeeSettledEvent domainEvent await this.TransactionAggregateRepository.SaveChanges(aggregate, cancellationToken); } - private async Task> GetTransactionFeesForCalculation(TransactionAggregate transactionAggregate, CancellationToken cancellationToken) - { + private async Task> GetTransactionFeesForCalculation(TransactionAggregate transactionAggregate, CancellationToken cancellationToken){ + // Ok we should have filtered out the not applicable transactions - // Get the fees to be calculated - List feesForProduct = await this.EstateClient.GetTransactionFeesForProduct(this.TokenResponse.AccessToken, - transactionAggregate.EstateId, - transactionAggregate.MerchantId, - transactionAggregate.ContractId, - transactionAggregate.ProductId, - cancellationToken); + // Check if we have fees for this product in the cache + Boolean feesInCache = this.MemoryCache.TryGetValue((transactionAggregate.EstateId, transactionAggregate.ContractId, transactionAggregate.ProductId), + out List feesForProduct); + + if (feesInCache == false){ + Logger.LogInformation($"Fees for Key: Estate Id {transactionAggregate.EstateId} Contract Id {transactionAggregate.ContractId} ProductId {transactionAggregate.ProductId} not found in the cache"); + + // Nothing in cache so we need to make a remote call + // Get the fees to be calculated + feesForProduct = await this.EstateClient.GetTransactionFeesForProduct(this.TokenResponse.AccessToken, + transactionAggregate.EstateId, + transactionAggregate.MerchantId, + transactionAggregate.ContractId, + transactionAggregate.ProductId, + cancellationToken); + // Now add this the result to the cache + String contractProductFeeCacheExpiryInHours = ConfigurationReader.GetValue("ContractProductFeeCacheExpiryInHours"); + if (String.IsNullOrEmpty(contractProductFeeCacheExpiryInHours)){ + contractProductFeeCacheExpiryInHours = "168"; // 7 Days default + } + this.MemoryCache.Set((transactionAggregate.EstateId, transactionAggregate.ContractId, transactionAggregate.ProductId), + feesForProduct, + new MemoryCacheEntryOptions(){ + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(Int32.Parse(contractProductFeeCacheExpiryInHours)) + }); + Logger.LogInformation($"Fees for Key: Estate Id {transactionAggregate.EstateId} Contract Id {transactionAggregate.ContractId} ProductId {transactionAggregate.ProductId} added to cache"); + + } + else + { + Logger.LogInformation($"Fees for Key: Estate Id {transactionAggregate.EstateId} Contract Id {transactionAggregate.ContractId} ProductId {transactionAggregate.ProductId} found in the cache"); + } + List feesForCalculation = new List(); foreach (ContractProductTransactionFee contractProductTransactionFee in feesForProduct) diff --git a/TransactionProcessor.Models/TransactionProcessor.Models.csproj b/TransactionProcessor.Models/TransactionProcessor.Models.csproj index 86c2dbfa..2c10ecbc 100644 --- a/TransactionProcessor.Models/TransactionProcessor.Models.csproj +++ b/TransactionProcessor.Models/TransactionProcessor.Models.csproj @@ -2,6 +2,7 @@ net8.0 + None diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 56b1553d..f900412e 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -326,7 +326,8 @@ public static Dictionary AdditionalTransactionMetaDataForPataPaw ["EventStoreSettings:ConnectionString"] = "esdb://127.0.0.1:2113", ["SecurityConfiguration:Authority"] = "https://127.0.0.1", ["AppSettings:EstateManagementApi"] = "http://127.0.0.1", - ["AppSettings:SecurityService"] = "http://127.0.0.1" + ["AppSettings:SecurityService"] = "http://127.0.0.1", + ["AppSettings:ContractProductFeeCacheExpiryInHours"] = "" }; public static EstateResponse GetEmptyEstateResponse => diff --git a/TransactionProcessor/Bootstrapper/MiscRegistry.cs b/TransactionProcessor/Bootstrapper/MiscRegistry.cs index 7a3c9042..dc9397df 100644 --- a/TransactionProcessor/Bootstrapper/MiscRegistry.cs +++ b/TransactionProcessor/Bootstrapper/MiscRegistry.cs @@ -2,6 +2,7 @@ { using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; + using BusinessLogic.Common; using BusinessLogic.Manager; using BusinessLogic.Services; using Factories; @@ -27,6 +28,7 @@ public MiscRegistry() this.AddSingleton(); this.AddSingleton(); this.AddSingleton(); + this.AddSingleton(); } #endregion diff --git a/TransactionProcessor/Extensions.cs b/TransactionProcessor/Extensions.cs index 0556c538..c3356417 100644 --- a/TransactionProcessor/Extensions.cs +++ b/TransactionProcessor/Extensions.cs @@ -8,6 +8,8 @@ namespace TransactionProcessor using System.Net.Http; using System.Threading; using System.Threading.Tasks; + using EstateManagement.Client; + using EstateManagement.DataTransferObjects.Responses.Contract; using EventStore.Client; using Microsoft.AspNetCore.Builder; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,8 +19,10 @@ namespace TransactionProcessor using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; + using SecurityService.Client; using Shared.EventStore.Aggregate; using Shared.EventStore.EventHandling; + using Shared.EventStore.EventStore; using Shared.EventStore.Extensions; using Shared.EventStore.SubscriptionWorker; using Shared.General; @@ -84,28 +88,58 @@ public static void PreWarm(this IApplicationBuilder applicationBuilder) { subscriptionRepositoryResolver, CancellationToken.None).Wait(CancellationToken.None); - //if (Startup.AutoApiLogonOperators.Any()) { - // foreach (String autoApiLogonOperator in Startup.AutoApiLogonOperators) { - // OperatorLogon(autoApiLogonOperator); - // } - //} + //LoadContractData(CancellationToken.None).Wait(CancellationToken.None); + + } + + private static async Task LoadContractData(CancellationToken cancellationToken){ + IEstateClient estateClient = Startup.Container.GetRequiredService(); + ISecurityServiceClient securityServiceClient = Startup.Container.GetRequiredService(); + IEventStoreContext eventStoreContext = Startup.Container.GetRequiredService(); + + var clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); + var clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); + var token = await securityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + + List contractResponses = new List(); + + Stopwatch sw = Stopwatch.StartNew(); + // get a list of the contracts from ES projection + String state = await eventStoreContext.GetStateFromProjection("ContractList",cancellationToken); + ContractList contractList = JsonConvert.DeserializeObject(state); + contractList.Contracts.AddRange(contractList.Contracts); + contractList.Contracts.AddRange(contractList.Contracts); + contractList.Contracts.AddRange(contractList.Contracts); + contractList.Contracts.AddRange(contractList.Contracts); + contractList.Contracts.AddRange(contractList.Contracts); + contractList.Contracts.AddRange(contractList.Contracts); + + List> tasks = new (); + foreach (var contract in contractList.Contracts){ + //ContractResponse contractResponse = await estateClient.GetContract(token.AccessToken, contract.EstateId, contract.ContractId, cancellationToken); + //contractResponses.Add(contractResponse); + tasks.Add(estateClient.GetContract(token.AccessToken, contract.EstateId, contract.ContractId, cancellationToken)); + } + + ContractResponse[] t = await Task.WhenAll(tasks); + + contractResponses = t.ToList(); + if (contractResponses.Any()){ + IMemoryCache memoryCache = Startup.Container.GetRequiredService(); + // // Build up the cache + Dictionary<(Guid, Guid), List> productfees = contractResponses + .SelectMany(contractResponse => contractResponse.Products.Select(contractResponseProduct => + new{ + //Key = (contractResponse.EstateId, contractResponseProduct.ProductId), + Key = (Guid.NewGuid(),Guid.NewGuid()), + Value = contractResponseProduct.TransactionFees + })) + .ToDictionary(x => x.Key, x => x.Value); + memoryCache.Set("TransactionFeeCache", productfees); + } + sw.Stop(); + Logger.LogWarning($"Contract Data loaded an cached [{sw.ElapsedMilliseconds} ms"); } - - //private static void OperatorLogon(String operatorId) - //{ - // try { - // Logger.LogInformation($"About to do auto logon for operator Id [{operatorId}]"); - // Func resolver = Startup.ServiceProvider.GetService>(); - // IOperatorProxy proxy = resolver(operatorId); - - // OperatorResponse logonResult = proxy.ProcessLogonMessage(null, CancellationToken.None).Result; - // Logger.LogInformation($"Auto logon for operator Id [{operatorId}] status [{logonResult.IsSuccessful}]"); - // } - // catch(Exception ex) { - // Logger.LogWarning($"Auto logon for operator Id [{operatorId}] failed."); - // Logger.LogWarning(ex.ToString()); - // } - //} } public class AutoLogonWorkerService : BackgroundService{ @@ -119,10 +153,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken){ } } - //String fileProfilePollingWindowInSeconds = ConfigurationReader.GetValue("AppSettings", "FileProfilePollingWindowInSeconds"); - //if (string.IsNullOrEmpty(fileProfilePollingWindowInSeconds)){ - // fileProfilePollingWindowInSeconds = "5"; - //} String fileProfilePollingWindowInSeconds ="5"; // Delay for configured seconds before polling for files again await Task.Delay(TimeSpan.FromSeconds(int.Parse(fileProfilePollingWindowInSeconds)), stoppingToken); @@ -147,4 +177,15 @@ private static void OperatorLogon(String operatorId) } } } + + public class Contract + { + public Guid EstateId { get; set; } + public Guid ContractId { get; set; } + } + + public class ContractList + { + public List Contracts { get; set; } + } } \ No newline at end of file diff --git a/TransactionProcessor/Startup.cs b/TransactionProcessor/Startup.cs index 3dc3b524..8bcd6088 100644 --- a/TransactionProcessor/Startup.cs +++ b/TransactionProcessor/Startup.cs @@ -115,15 +115,22 @@ public Startup(IWebHostEnvironment webHostEnvironment) { public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { + ConfigurationReader.Initialise(Startup.Configuration); + String nlogConfigFilename = "nlog.config"; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + LogManager.AddHiddenAssembly(Assembly.LoadFrom(Path.Combine(directoryPath, "Shared.dll"))); + } + else{ + LogManager.AddHiddenAssembly(Assembly.LoadFrom(Path.Combine(env.ContentRootPath, "Shared.dll"))); } - //string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - LogManager.AddHiddenAssembly(Assembly.LoadFrom(Path.Combine(env.ContentRootPath, "Shared.dll"))); + + loggerFactory.ConfigureNLog(Path.Combine(env.ContentRootPath, nlogConfigFilename)); loggerFactory.AddNLog(); diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index ba1bb11d..542e950f 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -2,6 +2,7 @@ "AppSettings": { "ClientId": "serviceClient", "ClientSecret": "d192cbc46d834d0da90e8a9d50ded543", + "ContractProductFeeCacheExpiryInHours": "", //"SecurityService": "https://127.0.0.1:5001", "ProjectionTraceThresholdInSeconds": 1, "EventHandlerConfiguration": {