From 034f7490138ab32d82e1b40f95b7790ef8163b15 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Thu, 9 Oct 2025 09:25:13 +0100 Subject: [PATCH] fix some of the warning issues --- .../Common/PolicyFactory.cs | 145 ------------------ .../MerchantStatementDomainEventHandler.cs | 5 +- .../VoucherDomainEventHandler.cs | 5 +- .../MerchantStatementDomainService.cs | 2 +- .../Services/SettlementDomainService.cs | 4 +- .../Services/TransactionValidationService.cs | 22 +-- .../MetadataContants.cs | 4 +- .../Bootstrapper/MiddlewareRegistry.cs | 12 +- 8 files changed, 31 insertions(+), 168 deletions(-) delete mode 100644 TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs diff --git a/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs b/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs deleted file mode 100644 index 4dfd0e87..00000000 --- a/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs +++ /dev/null @@ -1,145 +0,0 @@ -using EventStore.Client; -using Grpc.Core; -using Polly; -using Polly.Fallback; -using Polly.Retry; -using Polly.Wrap; -using Shared.Logger; -using SimpleResults; -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; - -namespace TransactionProcessor.BusinessLogic.Common; - -[ExcludeFromCodeCoverage] -public static class PolicyFactory -{ - private enum LogType - { - Retry, - Final - } - - public static IAsyncPolicy CreatePolicy( - int retryCount = 5, - TimeSpan? retryDelay = null, - string policyTag = "", - bool withFallBack = false) - { - TimeSpan delay = retryDelay.GetValueOrDefault(TimeSpan.FromSeconds(5)); - return CreateRetryPolicy(retryCount, delay, policyTag); - } - - public static IAsyncPolicy> CreatePolicy( - int retryCount = 5, - TimeSpan? retryDelay = null, - string policyTag = "", - bool withFallBack = false) - { - TimeSpan delay = retryDelay.GetValueOrDefault(TimeSpan.FromSeconds(5)); - return CreateRetryPolicy(retryCount, delay, policyTag); - } - - public static async Task ExecuteWithPolicyAsync( - Func> action, - IAsyncPolicy policy, - string policyTag = "") - { - var context = new Context(); - Result result = await policy.ExecuteAsync(ctx => action(), context); - - int retryCount = context.TryGetValue("RetryCount", out var retryObj) && retryObj is int r ? r : 0; - LogResult(policyTag, result, retryCount, LogType.Final); - - return result; - } - - public static async Task> ExecuteWithPolicyAsync( - Func>> action, - IAsyncPolicy> policy, - string policyTag = "") - { - var context = new Context(); - Result result = await policy.ExecuteAsync(ctx => action(), context); - - int retryCount = context.TryGetValue("RetryCount", out var retryObj) && retryObj is int r ? r : 0; - LogResult(policyTag, result, retryCount, LogType.Final); - - return result; - } - - private static AsyncRetryPolicy CreateRetryPolicy( - int retryCount, - TimeSpan retryDelay, - string policyTag) - { - return Policy - .HandleResult(ShouldRetry) - .WaitAndRetryAsync( - retryCount, - _ => retryDelay, - (result, timeSpan, attempt, context) => - { - context["RetryCount"] = attempt; - LogResult(policyTag, result.Result, attempt, LogType.Retry); - }); - } - - private static AsyncRetryPolicy> CreateRetryPolicy( - int retryCount, - TimeSpan retryDelay, - string policyTag) - { - return Policy> - .HandleResult(ShouldRetry) - .WaitAndRetryAsync( - retryCount, - _ => retryDelay, - (result, timeSpan, attempt, context) => - { - context["RetryCount"] = attempt; - LogResult(policyTag, result.Result, attempt, LogType.Retry); - }); - } - - private static bool ShouldRetry(ResultBase result) - { - return !result.IsSuccess && result.Errors.Any(e => - e.Contains("WrongExpectedVersion", StringComparison.OrdinalIgnoreCase) || - e.Contains("DeadlineExceeded", StringComparison.OrdinalIgnoreCase) || - e.Contains("Cancelled")); - } - - private static string FormatResultMessage(ResultBase result) - { - return result switch - { - { IsSuccess: true } => "Success", - { IsSuccess: false, Message: not "" } => result.Message, - { IsSuccess: false, Errors: var errors } when errors?.Any() == true => string.Join(", ", errors), - _ => "Unknown Error" - }; - } - - private static void LogResult(string policyTag, ResultBase result, int retryCount, LogType type) - { - string message = FormatResultMessage(result); - - switch (type) - { - case LogType.Retry: - Logger.LogWarning($"{policyTag} - Retry {retryCount} due to error: {message}. Waiting before retrying..."); - break; - - case LogType.Final: - string retryMessage = retryCount > 0 ? $" after {retryCount} retries." : ""; - Logger.LogWarning($"{policyTag} - {message}{retryMessage}"); - break; - default: - throw new ArgumentOutOfRangeException(nameof(type), type, null); - break; - } - } -} diff --git a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs index aa8759dd..8dac4183 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs @@ -14,6 +14,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Shared.Results; using TransactionProcessor.BusinessLogic.Common; using TransactionProcessor.BusinessLogic.Requests; using TransactionProcessor.BusinessLogic.Services; @@ -109,7 +110,7 @@ private async Task HandleSpecificDomainEvent(MerchantStatementDomainEven private async Task HandleSpecificDomainEvent(TransactionDomainEvents.TransactionHasBeenCompletedEvent domainEvent, CancellationToken cancellationToken) { - IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(policyTag: "MerchantStatementDomainEventHandler - HandleSpecificDomainEvent"); + IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(policyTag: "MerchantStatementDomainEventHandler - HandleSpecificDomainEvent"); try { @@ -158,7 +159,7 @@ private async Task HandleSpecificDomainEvent(MerchantDomainEvents.Withdr private async Task HandleSpecificDomainEvent(SettlementDomainEvents.MerchantFeeSettledEvent domainEvent, CancellationToken cancellationToken) { - IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(policyTag: "MerchantStatementDomainEventHandler - HandleSpecificDomainEvent"); + IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(policyTag: "MerchantStatementDomainEventHandler - HandleSpecificDomainEvent"); try { diff --git a/TransactionProcessor.BusinessLogic/EventHandling/VoucherDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/VoucherDomainEventHandler.cs index e2aeabba..a75a9f59 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/VoucherDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/VoucherDomainEventHandler.cs @@ -19,6 +19,7 @@ namespace TransactionProcessor.BusinessLogic.EventHandling; using Shared.EntityFramework; using Shared.EventStore.Aggregate; using Shared.EventStore.EventHandling; +using Shared.General; using System; using System.Collections.Generic; using System.IO.Abstractions; @@ -160,8 +161,8 @@ private async Task HandleSpecificDomainEvent(VoucherDomainEvents.Voucher { Body = message, ConnectionIdentifier = domainEvent.EstateId, - FromAddress = "golfhandicapping@btinternet.com", // TODO: lookup from config - IsHtml = true, + FromAddress = ConfigurationReader.GetValueOrDefault("AppSettings", "FromEmailAddress", "golfhandicapping@btinternet.com"), + IsHtml = true, MessageId = domainEvent.EventId, Subject = "Voucher Issue", ToAddresses = new List diff --git a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs index bb591305..9edc2e7a 100644 --- a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs @@ -193,7 +193,7 @@ public async Task EmailStatement(MerchantStatementCommands.EmailMerchant { Body = "Please find attached this months statement.", ConnectionIdentifier = command.EstateId, - FromAddress = "golfhandicapping@btinternet.com", // TODO: lookup from config + FromAddress = ConfigurationReader.GetValueOrDefault("AppSettings", "FromEmailAddress", "golfhandicapping@btinternet.com"), IsHtml = true, Subject = $"Merchant Statement for {statement.StatementDate}", ToAddresses = emailAddresses, diff --git a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs index 5754b15f..16f5bffe 100644 --- a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs @@ -38,10 +38,10 @@ public class SettlementDomainService : ISettlementDomainService public async Task> ProcessSettlement(SettlementCommands.ProcessSettlementCommand command, CancellationToken cancellationToken) { - IAsyncPolicy> retryPolicy = PolicyFactory.CreatePolicy(policyTag: "SettlementDomainService - ProcessSettlement"); + IAsyncPolicy> retryPolicy = PolicyFactory.CreatePolicy>(policyTag: "SettlementDomainService - ProcessSettlement"); try { - return await PolicyFactory.ExecuteWithPolicyAsync(async () => { + return await PolicyFactory.ExecuteWithPolicyAsync(async () => { Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId); diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs index 4360b394..9a419055 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs @@ -109,9 +109,11 @@ public async Task> ValidateReconciliationTra if (deviceValidationResult.IsFailed) return deviceValidationResult; // Validate the merchant device - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } + private const String SuccessResponseMessage = "SUCCESS"; + #endregion public async Task> ValidateSaleTransaction(Guid estateId, @@ -156,7 +158,7 @@ public async Task> ValidateSaleTransaction(G if (transactionAmountValidationResult.IsFailed) return transactionAmountValidationResult; // If we get here everything is good - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } private async Task>> ValidateEstate(Guid estateId, CancellationToken cancellationToken) @@ -186,7 +188,7 @@ private Result ValidateEstateOperator(EstateAggrega Result result = estateOperatorRecord switch { null => CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.OperatorNotValidForEstate, $"Operator {operatorId} not configured for Estate [{estate.EstateName}]")), _ when estateOperatorRecord.IsDeleted => CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.OperatorNotEnabledForEstate, $"Operator {operatorId} not enabled for Estate [{estate.EstateName}]")), - _ => Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")) + _ => Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)) }; return result; } @@ -203,7 +205,7 @@ private async Task>> Valid }; return CreateFailedResult(new TransactionValidationResult(transactionValidationResult)); } - return Result.Success(new TransactionValidationResult(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS"), getMerchantResult.Data)); + return Result.Success(new TransactionValidationResult(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage), getMerchantResult.Data)); } @@ -220,14 +222,14 @@ private Result ValidateDevice(Models.Merchant.Merch return CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.InvalidDeviceIdentifier, $"Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName}")); } - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } private Result ValidateDeviceForLogon(Models.Merchant.Merchant merchant, String deviceIdentifier) { if (merchant.Devices == null || !merchant.Devices.Any()) { - return Result.Success(new TransactionValidationResult(TransactionResponseCode.SuccessNeedToAddDevice,"SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.SuccessNeedToAddDevice,SuccessResponseMessage)); } Device device = merchant.Devices.SingleOrDefault(d => d.DeviceIdentifier == deviceIdentifier); @@ -236,7 +238,7 @@ private Result ValidateDeviceForLogon(Models.Mercha return CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.InvalidDeviceIdentifier, $"Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName}")); } - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } private Result ValidateMerchantOperator(Models.Merchant.Merchant merchant, Guid operatorId) @@ -252,7 +254,7 @@ private Result ValidateMerchantOperator(Models.Merc { null => CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.OperatorNotValidForMerchant, $"Operator {operatorId} not configured for Merchant [{merchant.MerchantName}]")), _ when merchantOperatorRecord.IsDeleted => CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.OperatorNotEnabledForMerchant, $"Operator {operatorId} not enabled for Merchant [{merchant.MerchantName}]")), - _ => Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")) + _ => Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)) }; return result; } @@ -287,7 +289,7 @@ private Result ValidateContractAndProduct(Models.Me return CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.ProductNotValidForMerchant, $"Product Id [{productId}] not valid for Merchant [{merchant.MerchantName}]")); } - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } private async Task> ValidateTransactionAmount(Guid merchantId, String merchantName, Decimal? transactionAmount, CancellationToken cancellationToken) @@ -309,7 +311,7 @@ private async Task> ValidateTransactionAmoun return CreateFailedResult(new TransactionValidationResult(TransactionResponseCode.MerchantDoesNotHaveEnoughCredit, $"Merchant [{merchantName}] does not have enough credit available [{projectionState.merchant.balance:0.00}] to perform transaction amount [{transactionAmount}]")); } - return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, "SUCCESS")); + return Result.Success(new TransactionValidationResult(TransactionResponseCode.Success, SuccessResponseMessage)); } } diff --git a/TransactionProcessor.DataTransferObjects/MetadataContants.cs b/TransactionProcessor.DataTransferObjects/MetadataContants.cs index 26d397d1..910bfec2 100644 --- a/TransactionProcessor.DataTransferObjects/MetadataContants.cs +++ b/TransactionProcessor.DataTransferObjects/MetadataContants.cs @@ -14,12 +14,12 @@ public static class MetadataContants /// /// The key name estate identifier /// - public const String KeyNameEstateId = "estate_id"; + public static readonly String KeyNameEstateId = "estate_id"; /// /// The key name merchant identifier /// - public const String KeyNameMerchantId = "merchant_id"; + public static readonly String KeyNameMerchantId = "merchant_id"; #endregion } diff --git a/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs b/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs index 35240ae5..95da1cdb 100644 --- a/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs +++ b/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs @@ -95,16 +95,16 @@ public MiddlewareRegistry() chain, sslPolicyErrors) => true }; - options.Authority = ConfigurationReader.GetValue("SecurityConfiguration", "Authority"); - options.Audience = ConfigurationReader.GetValue("SecurityConfiguration", "ApiName"); + options.Authority = GetSecurityServiceConfigValue("Authority"); + options.Audience = GetSecurityServiceConfigValue("ApiName"); options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false, ValidAudience = - ConfigurationReader.GetValue("SecurityConfiguration", "ApiName"), + GetSecurityServiceConfigValue("ApiName"), ValidIssuer = - ConfigurationReader.GetValue("SecurityConfiguration", "Authority"), + GetSecurityServiceConfigValue("Authority"), }; options.IncludeErrorDetails = true; }); @@ -124,6 +124,10 @@ public MiddlewareRegistry() #endregion + private String GetSecurityServiceConfigValue(String keyName) { + return ConfigurationReader.GetValue("SecurityConfiguration", keyName); + } + #region Methods ///