diff --git a/Directory.Packages.props b/Directory.Packages.props index c8066eb..9361a83 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,18 +5,18 @@ - + - + - + - + @@ -29,7 +29,7 @@ - + @@ -38,20 +38,15 @@ - - - - - + + + + + - - - - - - - - + + + diff --git a/TransactionProcessorACL.BusinessLogic.Tests/MediatorTests.cs b/TransactionProcessorACL.BusinessLogic.Tests/MediatorTests.cs index f54de88..9576cbb 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/MediatorTests.cs +++ b/TransactionProcessorACL.BusinessLogic.Tests/MediatorTests.cs @@ -11,6 +11,8 @@ using System.Text; using System.Threading.Tasks; using SimpleResults; +using Shared.General; +using Shared.Serialisation; using TransactionProcessorACL.Testing; using Xunit; @@ -53,14 +55,21 @@ public async Task Mediator_Send_RequestHandled() hostingEnvironment.ApplicationName.Getter().Returns("Test Application"); ServiceRegistry services = new ServiceRegistry(); - Startup s = new Startup(hostingEnvironment.Instance()); - Startup.Configuration = this.SetupMemoryConfiguration(); - - this.AddTestRegistrations(services, hostingEnvironment.Instance()); - s.ConfigureContainer(services); - + IConfigurationRoot configuration = this.SetupMemoryConfiguration(); + ConfigurationReader.Initialise(configuration); + + this.AddTestRegistrations(services, hostingEnvironment.Instance(), configuration); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.MiddlewareRegistry(configuration)); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.ApplicationServiceRegistry()); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.ClientRegistry()); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.MediatorRegistry(configuration)); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.MiscRegistry()); + services.IncludeRegistry(new TransactionProcessorACL.Bootstrapper.SerialiserRegistry()); + + using Container container = new(services); + StringSerialiser.Initialise(container.GetRequiredService()); List errors = new List(); - IMediator mediator = Startup.Container.GetService(); + IMediator mediator = container.GetService(); foreach (IBaseRequest baseRequest in this.Requests) { try @@ -92,7 +101,8 @@ private IConfigurationRoot SetupMemoryConfiguration() } private void AddTestRegistrations(ServiceRegistry services, - IWebHostEnvironment hostingEnvironment) + IWebHostEnvironment hostingEnvironment, + IConfiguration configuration) { services.AddLogging(); DiagnosticListener diagnosticSource = new DiagnosticListener(hostingEnvironment.ApplicationName); @@ -100,7 +110,7 @@ private void AddTestRegistrations(ServiceRegistry services, services.AddSingleton(diagnosticSource); services.AddSingleton(hostingEnvironment); services.AddSingleton(hostingEnvironment); - services.AddSingleton(Startup.Configuration); + services.AddSingleton(configuration); services.OverrideServices(s => { s.AddSingleton(); }); } diff --git a/TransactionProcessorACL.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs b/TransactionProcessorACL.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs index 8ed657b..35f7926 100644 --- a/TransactionProcessorACL.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs +++ b/TransactionProcessorACL.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs @@ -4,8 +4,8 @@ using SimpleResults; using TransactionProcessorACL.BusinessLogic.Requests; using TransactionProcessorACL.BusinessLogic.Services; -using TransactionProcessorACL.Models; using TransactionProcessorACL.DataTransferObjects.Requests; +using TransactionProcessorACL.Models; namespace TransactionProcessorACL.BusinessLogic.RequestHandlers; diff --git a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs index b92449d..ab826f1 100644 --- a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs +++ b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs @@ -428,7 +428,7 @@ public async Task> GetMerchantDa if (apiResponse.IsFailed) return ResultHelpers.CreateFailure(apiResponse); - MerchantDailyPerformanceSummaryResponse response = new MerchantDailyPerformanceSummaryResponse { + MerchantDailyPerformanceSummaryResponse response = new() { Metrics = apiResponse.Data.Metrics.Select(m => new MetricItem { Category = m.Category, Description = m.Description, Title = m.Title, Value = m.Value }).ToList(), DrillDownTransactions = apiResponse.Data.DrillDownTransactions.Select(d => new DrillDownTransaction { Amount = d.Amount, @@ -466,7 +466,7 @@ public async Task> GetMerchantTran if (apiResponse.IsFailed) return ResultHelpers.CreateFailure(apiResponse); - MerchantTransactionMixSummaryResponse response = new MerchantTransactionMixSummaryResponse() { + MerchantTransactionMixSummaryResponse response = new() { Breakdown = Enum.Parse(apiResponse.Data.Breakdown.ToString()), Measure = Enum.Parse(apiResponse.Data.Measure.ToString()), FromDate = apiResponse.Data.FromDate, diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs index 95df5a0..46729ae 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace TransactionProcessorACL.DataTransferObjects.Requests; @@ -6,10 +7,15 @@ namespace TransactionProcessorACL.DataTransferObjects.Requests; [ExcludeFromCodeCoverage] public class MerchantDailyPerformanceSummaryRequest { + [Description("Reporting identifier of the merchant to summarize.")] public int MerchantReportingId { get; set; } + [Description("Inclusive start of the reporting date/time range.")] public DateTime StartDate { get; set; } + [Description("Inclusive end of the reporting date/time range.")] public DateTime EndDate { get; set; } + + [Description("Client application version used by the version check when supplied in the JSON request body.")] public String ApplicationVersion { get; set; } } diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs index 467a2e0..127746a 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace TransactionProcessorACL.DataTransferObjects.Requests; @@ -6,17 +7,25 @@ namespace TransactionProcessorACL.DataTransferObjects.Requests; [ExcludeFromCodeCoverage] public class MerchantTransactionMixSummaryRequest { + [Description("Reporting identifier of the merchant to summarize.")] public int MerchantReportingId { get; set; } + [Description("Inclusive start of the reporting date/time range.")] public DateTime StartDate { get; set; } + [Description("Inclusive end of the reporting date/time range.")] public DateTime EndDate { get; set; } + [Description("Numeric grouping dimension: 1=TransactionType, 2=Product, 3=Operator, 4=Status.")] public TransactionMixBreakdown Breakdown { get; set; } + [Description("Numeric measure: 1=Count, 2=Value.")] public TransactionMixMeasure Measure { get; set; } + + [Description("Client application version used by the version check when supplied in the JSON request body.")] public String ApplicationVersion { get; set; } + [Description("Maximum number of ranked groups to return. Defaults to 5.")] public int TopN { get; set; } = 5; } diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs index 3d4946a..9bf49da 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs @@ -2,20 +2,25 @@ { using System; using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class OperatorTotalRequest { #region Properties + [Description("Contract for this operator's totals.")] public Guid ContractId { get; set; } + [Description("Operator whose transactions are included in these totals.")] public Guid OperatorId { get; set; } + [Description("Transaction count for this operator and contract.")] public Int32 TransactionCount { get; set; } + [Description("Transaction value for this operator and contract.")] public Decimal TransactionValue { get; set; } #endregion } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/RecentActivityReceiptSearchRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/RecentActivityReceiptSearchRequest.cs index 56799e2..d57fba5 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/RecentActivityReceiptSearchRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/RecentActivityReceiptSearchRequest.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace TransactionProcessorACL.DataTransferObjects.Requests; @@ -6,15 +7,21 @@ namespace TransactionProcessorACL.DataTransferObjects.Requests; [ExcludeFromCodeCoverage] public class RecentActivityReceiptSearchRequest { + [Description("Client application version used by the version check when supplied in the JSON request body.")] public string ApplicationVersion { get; set; } + [Description("Reporting identifier of the merchant to search.")] public int MerchantReportingId { get; set; } + [Description("Date whose transaction activity should be searched.")] public DateTime ReportDate { get; set; } + [Description("Optional text used to filter matching transactions or receipts.")] public string? SearchText { get; set; } + [Description("One-based page number. Defaults to 1.")] public int PageNumber { get; set; } = 1; + [Description("Maximum number of results per page. Defaults to 5.")] public int PageSize { get; set; } = 5; } diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs index 9dde745..995a1df 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs @@ -3,14 +3,18 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class ReconciliationRequestMessage : TransactionRequestMessage { + [Description("Optional reconciliation totals broken down by operator and contract.")] public List OperatorTotals { get; set; } + [Description("Total number of transactions in the reconciliation period.")] public Int32 TransactionCount { get; set; } + [Description("Total monetary value of transactions in the reconciliation period.")] public Decimal TransactionValue { get; set; } } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/ResendReceiptRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/ResendReceiptRequestMessage.cs index aaab1d2..d547ac2 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/ResendReceiptRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/ResendReceiptRequestMessage.cs @@ -1,11 +1,14 @@ using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; namespace TransactionProcessorACL.DataTransferObjects.Requests; [ExcludeFromCodeCoverage] public class ResendReceiptRequestMessage { + [Description("Transaction reference or receipt reference for the receipt to resend. Required.")] public string Reference { get; set; } + [Description("Valid email address to receive the receipt. Required.")] public string RecipientEmailAddress { get; set; } } diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs index 8b9112c..5b257c8 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs @@ -2,20 +2,25 @@ { using System; using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class SaleTransactionRequestMessage : TransactionRequestMessage { #region Properties + [Description("Contract under which the sale is processed.")] public Guid ContractId { get; set; } + [Description("Operator who performed the sale.")] public Guid OperatorId { get; set; } + [Description("Optional email address for the customer receipt.")] public String CustomerEmailAddress { get; set; } + [Description("Product being sold under the selected contract.")] public Guid ProductId { get; set; } #endregion } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs index f5433f3..8446eac 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.ComponentModel; using System.Diagnostics.CodeAnalysis; [ExcludeFromCodeCoverage] @@ -9,19 +10,26 @@ public class TransactionRequestMessage { #region Properties + [Description("Estate identifier from the transaction context. The API derives the authoritative estate from the access token.")] public Guid EstateId { get; set; } + [Description("Merchant identifier from the transaction context. The API derives the authoritative merchant from the access token.")] public Guid MerchantId { get; set; } + [Description("Client application version. The API version check is performed using the applicationVersion query parameter or the application_version JSON field.")] public String ApplicationVersion { get; set; } + [Description("Identifier of the device that initiated the transaction.")] public String DeviceIdentifier { get; set; } + [Description("Date and time when the transaction occurred.")] public DateTime TransactionDateTime { get; set; } + [Description("Client supplied transaction reference or number.")] public String TransactionNumber { get; set; } + [Description("Optional string key/value metadata associated with the request.")] public Dictionary AdditionalRequestMetadata { get; set; } #endregion } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/ContractResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/ContractResponse.cs index 8b6727b..b3c9802 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/ContractResponse.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/ContractResponse.cs @@ -1,44 +1,66 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace TransactionProcessorACL.DataTransferObjects.Responses { public class ContractResponse { + [Description("Unique contract identifier.")] public Guid ContractId { get; set; } + [Description("Contract reporting identifier.")] public int ContractReportingId { get; set; } + [Description("Display description of the contract.")] public string Description { get; set; } + [Description("Estate that owns the contract.")] public Guid EstateId { get; set; } + [Description("Estate reporting identifier.")] public int EstateReportingId { get; set; } + [Description("Operator associated with the contract.")] public Guid OperatorId { get; set; } + [Description("Display name of the associated operator.")] public string OperatorName { get; set; } + [Description("Products and fees available under this contract.")] public List Products { get; set; } } public class ContractProduct { + [Description("Text intended for display to the client.")] public string DisplayText { get; set; } + [Description("Product name.")] public string Name { get; set; } + [Description("Unique product identifier.")] public Guid ProductId { get; set; } + [Description("Product reporting identifier.")] public int ProductReportingId { get; set; } + [Description("Transaction fees applicable to the product.")] public List TransactionFees { get; set; } + [Description("Product value, when the product has a fixed value.")] public Decimal? Value { get; set; } + [Description("Product category, such as MobileTopup, Voucher, or BillPayment.")] public ProductType ProductType { get; set; } } public class ContractProductTransactionFee { + [Description("How the fee is calculated: Fixed or Percentage.")] public CalculationType CalculationType { get; set; } + [Description("Party to whom the fee applies: Merchant or ServiceProvider.")] public FeeType FeeType { get; set; } + [Description("Description of the transaction fee.")] public string Description { get; set; } + [Description("Unique transaction fee identifier.")] public Guid TransactionFeeId { get; set; } + [Description("Transaction fee reporting identifier.")] public int TransactionFeeReportingId { get; set; } + [Description("Fee amount or percentage, interpreted according to CalculationType.")] public Decimal Value { get; set; } } @@ -61,4 +83,4 @@ public enum ProductType Voucher, BillPayment, } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/GetVoucherResponseMessage.cs b/TransactionProcessorACL.DataTransferObjects/Responses/GetVoucherResponseMessage.cs index 28087f1..4337cf2 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/GetVoucherResponseMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/GetVoucherResponseMessage.cs @@ -1,29 +1,39 @@ namespace TransactionProcessorACL.DataTransferObjects.Responses{ using System; using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class GetVoucherResponseMessage{ #region Properties + [Description("Application response code for the voucher lookup.")] public String ResponseCode{ get; set; } + [Description("Human-readable result message.")] public String ResponseMessage{ get; set; } + [Description("Contract that issued the voucher.")] public Guid ContractId{ get; set; } + [Description("Estate that owns the voucher.")] public Guid EstateId{ get; set; } + [Description("Voucher expiry date and time.")] public DateTime ExpiryDate{ get; set; } + [Description("Original value of the voucher.")] public Decimal Value{ get; set; } + [Description("Remaining redeemable balance.")] public Decimal Balance{ get; set; } + [Description("Voucher code returned by the lookup.")] public String VoucherCode{ get; set; } + [Description("Unique voucher identifier.")] public Guid VoucherId{ get; set; } #endregion } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/MerchantDailyPerformanceSummaryResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantDailyPerformanceSummaryResponse.cs index 3d17a3d..7605f85 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/MerchantDailyPerformanceSummaryResponse.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantDailyPerformanceSummaryResponse.cs @@ -1,37 +1,49 @@ using System; using System.Collections.Generic; using System.Text; +using System.ComponentModel; namespace TransactionProcessorACL.DataTransferObjects.Responses { public class MerchantDailyPerformanceSummaryResponse { + [Description("Summary metrics for the requested period.")] public List Metrics { get; set; } = []; + [Description("Transaction rows for drilling into the summary.")] public List DrillDownTransactions { get; set; } = []; } public class MetricItem { + [Description("Metric title.")] public string Title { get; set; } + [Description("Numeric value for the metric.")] public decimal Value { get; set; } + [Description("Human-readable explanation of the metric.")] public string Description { get; set; } + [Description("Category identifier used by the reporting client.")] public int Category { get; set; } } public class DrillDownTransaction { + [Description("Transaction reference.")] public string Reference { get; set; } + [Description("Product associated with the transaction.")] public string Product { get; set; } + [Description("Transaction status.")] public string Status { get; set; } + [Description("Transaction amount.")] public decimal Amount { get; set; } + [Description("Date and time when the transaction occurred.")] public DateTime TransactionDateTime { get; set; } } } diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/MerchantResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantResponse.cs index 625de4e..0e3f6ad 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/MerchantResponse.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantResponse.cs @@ -1,102 +1,140 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; namespace TransactionProcessorACL.DataTransferObjects.Responses { [ExcludeFromCodeCoverage] public class MerchantResponse { + [Description("Merchant postal addresses.")] public List Addresses { get; set; } + [Description("Merchant contacts.")] public List Contacts { get; set; } + [Description("Devices associated with the merchant, keyed by device identifier with a display name as the value.")] public Dictionary Devices { get; set; } + [Description("Estate that owns the merchant.")] public Guid EstateId { get; set; } + [Description("Estate reporting identifier.")] public int EstateReportingId { get; set; } + [Description("Unique merchant identifier.")] public Guid MerchantId { get; set; } + [Description("Merchant reporting identifier.")] public int MerchantReportingId { get; set; } + [Description("Merchant display name.")] public string MerchantName { get; set; } + [Description("Merchant reference used by clients or reporting.")] public string MerchantReference { get; set; } + [Description("Date of the merchant's next statement.")] public DateTime NextStatementDate { get; set; } + [Description("Operators registered for the merchant.")] public List Operators { get; set; } + [Description("Merchant settlement frequency.")] public SettlementSchedule SettlementSchedule { get; set; } + [Description("Contracts associated with the merchant and their product identifiers.")] public List Contracts { get; set; } + [Description("Opening hours keyed by day of the week.")] public Dictionary OpeningHours { get; set; } } public class OpeningHoursResponse { + [Description("Local opening time for the day, in the format supplied by the merchant data.")] public string Opening { get; set; } + [Description("Local closing time for the day, in the format supplied by the merchant data.")] public string Closing { get; set; } } [ExcludeFromCodeCoverage] public class AddressResponse { + [Description("Unique address identifier.")] public Guid AddressId { get; set; } + [Description("First address line.")] public string AddressLine1 { get; set; } + [Description("Second address line, when present.")] public string AddressLine2 { get; set; } + [Description("Third address line, when present.")] public string AddressLine3 { get; set; } + [Description("Fourth address line, when present.")] public string AddressLine4 { get; set; } + [Description("Country name or code.")] public string Country { get; set; } + [Description("Postal or ZIP code.")] public string PostalCode { get; set; } + [Description("Region, county, or state.")] public string Region { get; set; } + [Description("Town or city.")] public string Town { get; set; } } [ExcludeFromCodeCoverage] public class ContactResponse { + [Description("Contact email address.")] public string ContactEmailAddress { get; set; } + [Description("Unique contact identifier.")] public Guid ContactId { get; set; } + [Description("Contact's name.")] public string ContactName { get; set; } + [Description("Contact telephone number.")] public string ContactPhoneNumber { get; set; } } [ExcludeFromCodeCoverage] public class MerchantOperatorResponse { + [Description("Operator display name.")] public string Name { get; set; } + [Description("Unique operator identifier.")] public Guid OperatorId { get; set; } + [Description("Merchant number assigned to this operator.")] public string MerchantNumber { get; set; } + [Description("Terminal number assigned to this operator.")] public string TerminalNumber { get; set; } + [Description("Indicates whether the operator has been deleted.")] public bool IsDeleted { get; set; } } [ExcludeFromCodeCoverage] public class MerchantContractResponse { + [Description("Unique contract identifier.")] public Guid ContractId { get; set; } + [Description("Indicates whether the merchant's contract is deleted.")] public bool IsDeleted { get; set; } + [Description("Product identifiers available on this merchant contract.")] public List ContractProducts { get; set; } public MerchantContractResponse() => this.ContractProducts = new List(); @@ -109,4 +147,4 @@ public enum SettlementSchedule Weekly, Monthly, } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/MerchantTransactionMixSummaryResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantTransactionMixSummaryResponse.cs new file mode 100644 index 0000000..9f8a8ca --- /dev/null +++ b/TransactionProcessorACL.DataTransferObjects/Responses/MerchantTransactionMixSummaryResponse.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using TransactionProcessorACL.DataTransferObjects.Requests; + +namespace TransactionProcessorACL.DataTransferObjects.Responses; + +public class MerchantTransactionMixSummaryResponse +{ + [Description("Start of the reporting period represented by the result.")] + public DateTime FromDate { get; set; } + + [Description("End of the reporting period represented by the result.")] + public DateTime ToDate { get; set; } + + [Description("Dimension used to group the results.")] + public TransactionMixBreakdown Breakdown { get; set; } + + [Description("Measure returned for each group: Count or Value.")] + public TransactionMixMeasure Measure { get; set; } + + [Description("Total transaction count across the requested period.")] + public decimal TotalCount { get; set; } + + [Description("Total transaction value across the requested period.")] + public decimal TotalValue { get; set; } + + [Description("Ranked transaction groups, limited by the request's TopN value.")] + public List Items { get; set; } = []; + + [Description("Transaction rows contributing to the summary.")] + public List DrillDownTransactions { get; set; } = []; +} + +public class TransactionMixSummaryItem +{ + [Description("Stable grouping key for this item.")] + public string Key { get; set; } + + [Description("Display label for the group.")] + public string Label { get; set; } + + [Description("Transaction count for this group.")] + public decimal Count { get; set; } + + [Description("Transaction value for this group.")] + public decimal Value { get; set; } +} + +public class TransactionMixDrillDownTransaction +{ + [Description("Transaction reference.")] + public string Reference { get; set; } + + [Description("Transaction type.")] + public string TransactionType { get; set; } + + [Description("Product associated with the transaction.")] + public string Product { get; set; } + + [Description("Operator associated with the transaction.")] + public string Operator { get; set; } + + [Description("Transaction status.")] + public string Status { get; set; } + + [Description("Transaction amount.")] + public decimal Amount { get; set; } + + [Description("Date and time when the transaction occurred.")] + public DateTime TransactionDateTime { get; set; } +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/RecentActivityReceiptSearchResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/RecentActivityReceiptSearchResponse.cs new file mode 100644 index 0000000..4944b3d --- /dev/null +++ b/TransactionProcessorACL.DataTransferObjects/Responses/RecentActivityReceiptSearchResponse.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace TransactionProcessorACL.DataTransferObjects.Responses; + +public class RecentActivityReceiptSearchResponse +{ + [Description("Date searched for recent activity.")] + public DateTime ReportDate { get; set; } + + [Description("One-based page number returned.")] + public int PageNumber { get; set; } + + [Description("Maximum number of results per page used for this response.")] + public int PageSize { get; set; } + + [Description("Total number of matches across all pages.")] + public int TotalCount { get; set; } + + [Description("Transaction and receipt records on the requested page.")] + public List Items { get; set; } = []; +} + +public class RecentActivityReceiptSearchItem +{ + [Description("Transaction reference.")] + public string Reference { get; set; } + + [Description("Transaction type.")] + public string TransactionType { get; set; } + + [Description("Product associated with the transaction.")] + public string Product { get; set; } + + [Description("Operator associated with the transaction.")] + public string Operator { get; set; } + + [Description("Transaction status.")] + public string Status { get; set; } + + [Description("Transaction amount.")] + public decimal Amount { get; set; } + + [Description("Date and time when the transaction occurred.")] + public DateTime TransactionDateTime { get; set; } + + [Description("Receipt reference, when a receipt is available.")] + public string ReceiptReference { get; set; } +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/RedeemVoucherResponseMessage.cs b/TransactionProcessorACL.DataTransferObjects/Responses/RedeemVoucherResponseMessage.cs index f5565c7..4c8c1ca 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/RedeemVoucherResponseMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/RedeemVoucherResponseMessage.cs @@ -3,23 +3,31 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class RedeemVoucherResponseMessage{ #region Properties + [Description("Application response code for the redemption.")] public String ResponseCode{ get; set; } + [Description("Human-readable result message.")] public String ResponseMessage{ get; set; } + [Description("Contract that issued the voucher.")] public Guid ContractId{ get; set; } + [Description("Estate that owns the voucher.")] public Guid EstateId{ get; set; } + [Description("Voucher expiry date and time.")] public DateTime ExpiryDate{ get; set; } + [Description("Remaining balance after redemption.")] public Decimal Balance{ get; set; } + [Description("Redeemed voucher code.")] public String VoucherCode{ get; set; } #endregion @@ -28,16 +36,20 @@ public class RedeemVoucherResponseMessage{ [ExcludeFromCodeCoverage] public class MerchantScheduleResponse { + [Description("Calendar year covered by the schedule.")] public int Year { get; set; } + [Description("Schedule details grouped by calendar month.")] public List Months { get; set; } = new List(); } [ExcludeFromCodeCoverage] public class MerchantScheduleMonthResponse { + [Description("Calendar month number, from 1 (January) through 12 (December).")] public int Month { get; set; } + [Description("Day numbers in this month when the merchant is closed.")] public List ClosedDays { get; set; } = new List(); } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/ResendReceiptResponse.cs b/TransactionProcessorACL.DataTransferObjects/Responses/ResendReceiptResponse.cs new file mode 100644 index 0000000..73d139c --- /dev/null +++ b/TransactionProcessorACL.DataTransferObjects/Responses/ResendReceiptResponse.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; + +namespace TransactionProcessorACL.DataTransferObjects.Responses; + +public class ResendReceiptResponse +{ + [Description("True when the receipt resend request succeeded.")] + public bool Success { get; set; } + + [Description("Human-readable result or failure reason.")] + public string Message { get; set; } + + [Description("Transaction or receipt reference supplied with the request.")] + public string Reference { get; set; } + + [Description("Receipt reference associated with the resent receipt, when available.")] + public string ReceiptReference { get; set; } + + [Description("Transaction reference associated with the resent receipt, when available.")] + public string TransactionReference { get; set; } +} diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs index 733c8fa..4b6a527 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs @@ -5,22 +5,30 @@ namespace TransactionProcessorACL.DataTransferObjects.Responses { using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; [ExcludeFromCodeCoverage] public class TransactionResponseMessage { + [Description("Application response code for the transaction result.")] public String ResponseCode { get; set; } + [Description("Human-readable message describing the transaction result.")] public String ResponseMessage { get; set; } + [Description("Optional string key/value metadata returned by the transaction processor.")] public Dictionary AdditionalResponseMetadata { get; set; } + [Description("Estate that owns the transaction.")] public Guid EstateId { get; set; } + [Description("Merchant that owns the transaction.")] public Guid MerchantId { get; set; } + [Description("Indicates whether the client must update its application before continuing.")] public Boolean RequiresApplicationUpdate { get; set; } + [Description("Identifier assigned to the processed transaction.")] public Guid TransactionId { get; set; } } } diff --git a/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs index 99c928c..aad38a6 100644 --- a/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs +++ b/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs @@ -1,5 +1,6 @@ using DotNet.Testcontainers.Builders; using EventStore.Client; +using KurrentDB.Client; using Shared.IntegrationTesting; using System.Linq; @@ -47,7 +48,7 @@ public class DockerHelper : global::Shared.IntegrationTesting.TestContainers.Doc private readonly TestingContext TestingContext; - public EventStoreProjectionManagementClient ProjectionManagementClient; + public KurrentDBProjectionManagementClient ProjectionManagementClient; public IAgencyBankingClient AgencyBankingClient; @@ -61,7 +62,7 @@ public class DockerHelper : global::Shared.IntegrationTesting.TestContainers.Doc /// The logger. public DockerHelper() { - this.IsSecureEventStore = false; + this.IsSecureKurrentDb = false; this.TestingContext = new TestingContext(); StringSerialiser.Initialise((IStringSerialiser)new SystemTextJsonSerializer(SystemTextJsonSerializer.GetDefaultJsonSerializerOptions())); } @@ -70,9 +71,9 @@ public DockerHelper() #region Methods - public override ContainerBuilder SetupEventStoreContainer() + public override ContainerBuilder SetupKurrentDbContainer() { - this.Trace("About to Start Event Store Container"); + this.Trace("About to Start Kurrent Db Container"); Dictionary environmentVariables = new(){ {"EVENTSTORE_RUN_PROJECTIONS","all"}, @@ -82,9 +83,9 @@ public override ContainerBuilder SetupEventStoreContainer() {"KURRENTDB_PREPARE_TIMEOUT_MS","30000"}, {"KURRENTDB_COMMIT_TIMEOUT_MS","30000"}, }; - ContainerBuilder eventStoreContainer = new ContainerBuilder(); + ContainerBuilder eventStoreContainer = new(); - if (!this.IsSecureEventStore) + if (!this.IsSecureKurrentDb) { environmentVariables.Add("EVENTSTORE_INSECURE", "true"); } @@ -107,15 +108,15 @@ public override ContainerBuilder SetupEventStoreContainer() String logfilePath = "/var/log/kurrentdb"; eventStoreContainer = eventStoreContainer.MountHostFolder(this.HostTraceFolder, logfilePath); - (String imageName, Boolean useLatest) imageDetails = this.GetImageDetails(ContainerType.EventStore).Data; - this.Trace($"About to Start Event Store Container using image [{imageDetails.imageName}]"); + (String imageName, Boolean useLatest) imageDetails = this.GetImageDetails(ContainerType.KurrentDb).Data; + this.Trace($"About to Start Kurrent Db Container using image [{imageDetails.imageName}]"); - eventStoreContainer = eventStoreContainer.WithName(this.EventStoreContainerName) // similar to WithName() + eventStoreContainer = eventStoreContainer.WithName(this.KurrentDbContainerName) // similar to WithName() .WithImage(imageDetails.imageName) .WithEnvironment(environmentVariables) - .WithPortBinding(DockerPorts.EventStoreHttpDockerPort, true); + .WithPortBinding(DockerPorts.KurrentDbHttpDockerPort, true); return eventStoreContainer; } @@ -148,9 +149,9 @@ public override ContainerBuilder SetupTransactionProcessorContainer() return base.SetupTransactionProcessorContainer(); } - protected override String GenerateEventStoreConnectionString(String userName = "admin", String password = "changeit") + protected override String GenerateKurrentDbConnectionString(String userName = "admin", String password = "changeit") { - String eventStoreAddress = $"esdb://{userName}:{password}@{this.EventStoreContainerName}:{DockerPorts.EventStoreHttpDockerPort}?tls=false&defaultDeadline=240000"; + String eventStoreAddress = $"esdb://{userName}:{password}@{this.KurrentDbContainerName}:{DockerPorts.KurrentDbHttpDockerPort}?tls=false&defaultDeadline=240000"; return eventStoreAddress; } @@ -212,7 +213,7 @@ public override async Task StartContainersForScenarioRun(String scenarioName, Do this.HttpClient.BaseAddress = new Uri(TransactionProcessorAclBaseAddressResolver(string.Empty)); this.HttpClient.Timeout = TimeSpan.FromMinutes(5); - this.ProjectionManagementClient = new EventStoreProjectionManagementClient(ConfigureEventStoreSettings()); + this.ProjectionManagementClient = new KurrentDBProjectionManagementClient(this.ConfigureKurrentDbSettings()); SimpleResults.Result bootstrapToken = await this.SecurityServiceClient.GetToken("management-bootstrap", "management-bootstrap-secret", CancellationToken.None); if (bootstrapToken.IsFailed || String.IsNullOrWhiteSpace(bootstrapToken.Data?.AccessToken)) diff --git a/TransactionProcessorACL.IntegrationTests/Common/EstateDetails.cs b/TransactionProcessorACL.IntegrationTests/Common/EstateDetails.cs index 8cc87ac..b717055 100644 --- a/TransactionProcessorACL.IntegrationTests/Common/EstateDetails.cs +++ b/TransactionProcessorACL.IntegrationTests/Common/EstateDetails.cs @@ -1,6 +1,7 @@ using TransactionProcessor.IntegrationTesting.Helpers; using TransactionProcessorACL.DataTransferObjects.Responses; using TransactionProcessorACL.IntegrationTests.Shared; +using ReportingMixResponse = TransactionProcessorACL.IntegrationTests.Shared.MerchantTransactionMixSummaryResponse; namespace TransactionProcessor.IntegrationTests.Common { @@ -15,7 +16,7 @@ public class EstateDetails1 private readonly Dictionary> MerchantUsersTokens; private readonly Dictionary MerchantReportingIds; private readonly Dictionary MerchantDailyPerformanceSummaryResponses; - private readonly Dictionary MerchantTransactionMixSummaryResponses; + private readonly Dictionary MerchantTransactionMixSummaryResponses; private Dictionary> VoucherRedemptionUsersTokens; public EstateDetails1(EstateDetails estateDetails) @@ -24,7 +25,7 @@ public EstateDetails1(EstateDetails estateDetails) this.MerchantUsersTokens = new Dictionary>(); this.MerchantReportingIds = new Dictionary(); this.MerchantDailyPerformanceSummaryResponses = new Dictionary(); - this.MerchantTransactionMixSummaryResponses = new Dictionary(); + this.MerchantTransactionMixSummaryResponses = new Dictionary(); this.VoucherRedemptionUsersTokens = new Dictionary>(); } @@ -75,7 +76,7 @@ public void AddMerchantDailyPerformanceSummaryResponse(Guid merchantId, } public void AddMerchantTransactionMixSummaryResponse(Guid merchantId, - MerchantTransactionMixSummaryResponse response) + ReportingMixResponse response) { if (this.MerchantTransactionMixSummaryResponses.ContainsKey(merchantId)) { @@ -129,7 +130,7 @@ public MerchantDailyPerformanceSummaryResponse GetMerchantDailyPerformanceSummar return this.MerchantDailyPerformanceSummaryResponses.SingleOrDefault(x => x.Key == merchantId).Value; } - public MerchantTransactionMixSummaryResponse GetMerchantTransactionMixSummaryResponse(Guid merchantId) + public ReportingMixResponse GetMerchantTransactionMixSummaryResponse(Guid merchantId) { return this.MerchantTransactionMixSummaryResponses.SingleOrDefault(x => x.Key == merchantId).Value; } diff --git a/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs b/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs index 55669fa..b918b3d 100644 --- a/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs @@ -31,12 +31,12 @@ public GenericSteps(ScenarioContext scenarioContext, public async Task StartSystem() { // Initialise a logger - String scenarioName = this.ScenarioContext.ScenarioInfo.Title.Replace(" ", ""); + String scenarioName = this.ScenarioContext.ScenarioInfo.Title.Replace(" ", String.Empty); NlogLogger logger = new NlogLogger(); logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName); LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly); - DockerServices dockerServices = DockerServices.CallbackHandler | DockerServices.EventStore | + DockerServices dockerServices = DockerServices.CallbackHandler | DockerServices.KurrentDb | DockerServices.FileProcessor | DockerServices.MessagingService | DockerServices.SecurityService | DockerServices.SqlServer | DockerServices.TestHost | DockerServices.TransactionProcessor | DockerServices.TransactionProcessorAcl | DockerServices.EstateReporting; diff --git a/TransactionProcessorACL.IntegrationTests/Merchants/Merchants.feature.cs b/TransactionProcessorACL.IntegrationTests/Merchants/Merchants.feature.cs index 7621a53..c68ee94 100644 --- a/TransactionProcessorACL.IntegrationTests/Merchants/Merchants.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/Merchants/Merchants.feature.cs @@ -111,174 +111,174 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { "Role Name"}); - table1.AddRow(new string[] { + table16.AddRow(new string[] { "Merchant"}); #line 6 - await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table1, "Given "); + await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table16, "Given "); #line hidden - global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table2.AddRow(new string[] { + table17.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table2.AddRow(new string[] { + table17.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST Scope", "A scope for Transaction Processor ACL REST"}); #line 10 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table2, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table17, "Given "); #line hidden - global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table3.AddRow(new string[] { + table18.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "merchantId, estateId, role"}); - table3.AddRow(new string[] { + table18.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST", "Secret1", "transactionProcessorACL", "merchantId, estateId, role"}); #line 15 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table3, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table18, "Given "); #line hidden - global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table4.AddRow(new string[] { + table19.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,transactionProcessorACL", "client_credentials"}); - table4.AddRow(new string[] { + table19.AddRow(new string[] { "merchantClient", "Merchant Client", "Secret1", "transactionProcessorACL", "password"}); #line 20 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table4, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table19, "Given "); #line hidden - global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table5.AddRow(new string[] { + table20.AddRow(new string[] { "serviceClient"}); #line 25 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor acl reso" + - "urces", ((string)(null)), table5, "Given "); + "urces", ((string)(null)), table20, "Given "); #line hidden - global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { "AccountNumber", "AccountName", "DueDate", "Amount"}); - table6.AddRow(new string[] { + table21.AddRow(new string[] { "12345678", "Test Account 1", "Today", "100.00"}); #line 29 - await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table6, "Given "); + await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table21, "Given "); #line hidden - global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { "Username", "Password"}); - table7.AddRow(new string[] { + table22.AddRow(new string[] { "operatora", "1234567898"}); #line 33 - await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table7, "Given "); + await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table22, "Given "); #line hidden - global::Reqnroll.Table table8 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { "MeterNumber", "CustomerName"}); - table8.AddRow(new string[] { + table23.AddRow(new string[] { "00000001", "Customer 1"}); - table8.AddRow(new string[] { + table23.AddRow(new string[] { "00000002", "Customer 2"}); - table8.AddRow(new string[] { + table23.AddRow(new string[] { "00000003", "Customer 3"}); #line 37 - await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table8, "Given "); + await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table23, "Given "); #line hidden #line 43 await testRunner.GivenAsync("I initialise the Agency Banking Host", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table9 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table9.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 1"}); - table9.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 2"}); #line 45 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table9, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table24, "Given "); #line hidden - global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table10.AddRow(new string[] { + table25.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table10.AddRow(new string[] { + table25.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); - table10.AddRow(new string[] { + table25.AddRow(new string[] { "Test Estate 2", "Safaricom", "True", "True"}); - table10.AddRow(new string[] { + table25.AddRow(new string[] { "Test Estate 2", "Voucher", "True", "True"}); #line 50 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table10, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table25, "Given "); #line hidden - global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table11.AddRow(new string[] { + table26.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table11.AddRow(new string[] { + table26.AddRow(new string[] { "Test Estate 1", "Voucher"}); - table11.AddRow(new string[] { + table26.AddRow(new string[] { "Test Estate 2", "Safaricom"}); - table11.AddRow(new string[] { + table26.AddRow(new string[] { "Test Estate 2", "Voucher"}); #line 57 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table11, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table26, "And "); #line hidden - global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -288,7 +288,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table12.AddRow(new string[] { + table27.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -298,7 +298,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 1", "testcontact1@merchant1.co.uk", "Test Estate 1"}); - table12.AddRow(new string[] { + table27.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -308,7 +308,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 2", "testcontact2@merchant2.co.uk", "Test Estate 1"}); - table12.AddRow(new string[] { + table27.AddRow(new string[] { "Test Merchant 3", "Address Line 1", "TestTown", @@ -319,75 +319,75 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact3@merchant2.co.uk", "Test Estate 2"}); #line 64 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table12, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table27, "Given "); #line hidden - global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Safaricom", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); - table13.AddRow(new string[] { + table28.AddRow(new string[] { "Voucher", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); #line 70 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table13, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table28, "Given "); #line hidden - global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { "EmailAddress", "Password", "GivenName", "FamilyName", "EstateName", "MerchantName"}); - table14.AddRow(new string[] { + table29.AddRow(new string[] { "merchantuser@testmerchant1.co.uk", "123456", "TestMerchant", "User1", "Test Estate 1", "Test Merchant 1"}); - table14.AddRow(new string[] { + table29.AddRow(new string[] { "merchantuser@testmerchant2.co.uk", "123456", "TestMerchant", "User2", "Test Estate 1", "Test Merchant 2"}); - table14.AddRow(new string[] { + table29.AddRow(new string[] { "merchantuser@testmerchant3.co.uk", "123456", "TestMerchant", @@ -395,32 +395,32 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Estate 2", "Test Merchant 3"}); #line 79 - await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table14, "Given "); + await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table29, "Given "); #line hidden - global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table15.AddRow(new string[] { + table30.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table15.AddRow(new string[] { + table30.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); - table15.AddRow(new string[] { + table30.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract"}); - table15.AddRow(new string[] { + table30.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract"}); #line 85 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table15, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table30, "Given "); #line hidden - global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -428,7 +428,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table16.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -436,7 +436,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table16.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -444,7 +444,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table16.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -452,7 +452,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table16.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract", @@ -461,9 +461,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 92 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table16, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table31, "When "); #line hidden - global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -471,7 +471,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table17.AddRow(new string[] { + table32.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -479,7 +479,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Fixed", "Merchant Commission", "2.50"}); - table17.AddRow(new string[] { + table32.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -488,38 +488,38 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "0.85"}); #line 99 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table17, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table32, "When "); #line hidden - global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Safaricom Contract"}); - table18.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Hospital 1 Contract"}); #line 104 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table18, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table33, "When "); #line hidden } @@ -556,7 +556,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -564,7 +564,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Country", "ContactName", "EmailAddress"}); - table19.AddRow(new string[] { + table34.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -574,7 +574,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk"}); #line 116 await testRunner.WhenAsync("I get the merchant information for Merchant \"Test Merchant 1\" for Estate \"Test Es" + - "tate 1\" the response should contain the following information", ((string)(null)), table19, "When "); + "tate 1\" the response should contain the following information", ((string)(null)), table34, "When "); #line hidden } await this.ScenarioCleanupAsync(); @@ -608,18 +608,18 @@ await testRunner.GivenAsync("I have a token to access the estate management and "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { "MerchantName", "ContractDescription"}); - table20.AddRow(new string[] { + table35.AddRow(new string[] { "Test Merchant 1", "Safaricom Contract"}); - table20.AddRow(new string[] { + table35.AddRow(new string[] { "Test Merchant 1", "Hospital 1 Contract"}); #line 122 await testRunner.WhenAsync("I get the merchant contract information for Merchant \"Test Merchant 1\" for Estate" + - " \"Test Estate 1\" the response should contain the following information", ((string)(null)), table20, "When "); + " \"Test Estate 1\" the response should contain the following information", ((string)(null)), table35, "When "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature.cs b/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature.cs index 4842a99..efddd99 100644 --- a/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature.cs @@ -113,204 +113,204 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { "Role Name"}); - table21.AddRow(new string[] { + table76.AddRow(new string[] { "Merchant"}); #line 6 - await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table21, "Given "); + await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table76, "Given "); #line hidden - global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table22.AddRow(new string[] { + table77.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table22.AddRow(new string[] { + table77.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST Scope", "A scope for Transaction Processor ACL REST"}); - table22.AddRow(new string[] { + table77.AddRow(new string[] { "estateReporting", "Estate Reporting REST Scope", "Scope for Estate Reporting REST"}); #line 10 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table22, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table77, "Given "); #line hidden - global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table23.AddRow(new string[] { + table78.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "merchantId, estateId, role"}); - table23.AddRow(new string[] { + table78.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST", "Secret1", "transactionProcessorACL", "merchantId, estateId, role"}); - table23.AddRow(new string[] { + table78.AddRow(new string[] { "estateReporting", "Estate Reporting REST", "Secret1", "estateReporting", "merchantId,estateId,role"}); #line 16 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table23, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table78, "Given "); #line hidden - global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table24.AddRow(new string[] { + table79.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,transactionProcessorACL, estateReporting", "client_credentials"}); - table24.AddRow(new string[] { + table79.AddRow(new string[] { "merchantClient", "Merchant Client", "Secret1", "transactionProcessorACL", "password"}); #line 22 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table24, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table79, "Given "); #line hidden - global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table25.AddRow(new string[] { + table80.AddRow(new string[] { "serviceClient"}); #line 27 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor acl reso" + - "urces", ((string)(null)), table25, "Given "); + "urces", ((string)(null)), table80, "Given "); #line hidden - global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { "AccountNumber", "AccountName", "DueDate", "Amount"}); - table26.AddRow(new string[] { + table81.AddRow(new string[] { "12345678", "Test Account 1", "Today", "100.00"}); #line 31 - await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table26, "Given "); + await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table81, "Given "); #line hidden - global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { "Username", "Password"}); - table27.AddRow(new string[] { + table82.AddRow(new string[] { "operatora", "1234567898"}); #line 35 - await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table27, "Given "); + await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table82, "Given "); #line hidden - global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { "MeterNumber", "CustomerName"}); - table28.AddRow(new string[] { + table83.AddRow(new string[] { "00000001", "Customer 1"}); - table28.AddRow(new string[] { + table83.AddRow(new string[] { "00000002", "Customer 2"}); - table28.AddRow(new string[] { + table83.AddRow(new string[] { "00000003", "Customer 3"}); #line 39 - await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table28, "Given "); + await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table83, "Given "); #line hidden - global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table29.AddRow(new string[] { + table84.AddRow(new string[] { "Test Estate 1"}); - table29.AddRow(new string[] { + table84.AddRow(new string[] { "Test Estate 2"}); #line 45 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table29, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table84, "Given "); #line hidden - global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table85 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table30.AddRow(new string[] { + table85.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table30.AddRow(new string[] { + table85.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); - table30.AddRow(new string[] { + table85.AddRow(new string[] { "Test Estate 2", "Safaricom", "True", "True"}); - table30.AddRow(new string[] { + table85.AddRow(new string[] { "Test Estate 2", "Voucher", "True", "True"}); #line 50 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table30, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table85, "Given "); #line hidden - global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table86 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table31.AddRow(new string[] { + table86.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table31.AddRow(new string[] { + table86.AddRow(new string[] { "Test Estate 1", "Voucher"}); - table31.AddRow(new string[] { + table86.AddRow(new string[] { "Test Estate 2", "Safaricom"}); - table31.AddRow(new string[] { + table86.AddRow(new string[] { "Test Estate 2", "Voucher"}); #line 57 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table31, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table86, "And "); #line hidden - global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table87 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table32.AddRow(new string[] { + table87.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table32.AddRow(new string[] { + table87.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); - table32.AddRow(new string[] { + table87.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract"}); - table32.AddRow(new string[] { + table87.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract"}); #line 64 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table32, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table87, "Given "); #line hidden - global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table88 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -318,7 +318,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table33.AddRow(new string[] { + table88.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -326,7 +326,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table33.AddRow(new string[] { + table88.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -334,7 +334,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table33.AddRow(new string[] { + table88.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -342,7 +342,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table33.AddRow(new string[] { + table88.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract", @@ -351,9 +351,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 71 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table33, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table88, "When "); #line hidden - global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table89 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -361,7 +361,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table34.AddRow(new string[] { + table89.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -369,7 +369,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Fixed", "Merchant Commission", "2.50"}); - table34.AddRow(new string[] { + table89.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -378,9 +378,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "0.85"}); #line 78 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table34, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table89, "When "); #line hidden - global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table90 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -390,7 +390,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table35.AddRow(new string[] { + table90.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -400,7 +400,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 1", "testcontact1@merchant1.co.uk", "Test Estate 1"}); - table35.AddRow(new string[] { + table90.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -410,7 +410,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 2", "testcontact2@merchant2.co.uk", "Test Estate 1"}); - table35.AddRow(new string[] { + table90.AddRow(new string[] { "Test Merchant 3", "Address Line 1", "TestTown", @@ -421,152 +421,152 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact3@merchant2.co.uk", "Test Estate 2"}); #line 83 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table35, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table90, "Given "); #line hidden - global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table91 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Safaricom", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); - table36.AddRow(new string[] { + table91.AddRow(new string[] { "Voucher", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); #line 89 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table36, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table91, "Given "); #line hidden - global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table92 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table37.AddRow(new string[] { + table92.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table37.AddRow(new string[] { + table92.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); - table37.AddRow(new string[] { + table92.AddRow(new string[] { "123456782", "Test Merchant 3", "Test Estate 2"}); #line 98 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table37, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table92, "Given "); #line hidden - global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table93 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Safaricom Contract"}); - table38.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Hospital 1 Contract"}); #line 104 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table38, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table93, "When "); #line hidden - global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table94 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table39.AddRow(new string[] { + table94.AddRow(new string[] { "Deposit1", "210.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table39.AddRow(new string[] { + table94.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 2", "Test Estate 1"}); - table39.AddRow(new string[] { + table94.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 3", "Test Estate 2"}); #line 113 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table39, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table94, "Given "); #line hidden - global::Reqnroll.Table table40 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table95 = new global::Reqnroll.Table(new string[] { "EmailAddress", "Password", "GivenName", "FamilyName", "EstateName", "MerchantName"}); - table40.AddRow(new string[] { + table95.AddRow(new string[] { "merchantuser@testmerchant1.co.uk", "123456", "TestMerchant", "User1", "Test Estate 1", "Test Merchant 1"}); - table40.AddRow(new string[] { + table95.AddRow(new string[] { "merchantuser@testmerchant2.co.uk", "123456", "TestMerchant", "User2", "Test Estate 1", "Test Merchant 2"}); - table40.AddRow(new string[] { + table95.AddRow(new string[] { "merchantuser@testmerchant3.co.uk", "123456", "TestMerchant", @@ -574,7 +574,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Estate 2", "Test Merchant 3"}); #line 119 - await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table40, "Given "); + await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table95, "Given "); #line hidden } @@ -613,7 +613,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table41 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table96 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -628,7 +628,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ProductName", "RecipientEmail", "RecipientMobile"}); - table41.AddRow(new string[] { + table96.AddRow(new string[] { "Today", "8", "Sale", @@ -644,7 +644,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", ""}); #line 128 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table41, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table96, "When "); #line hidden #line 132 await testRunner.WhenAsync("I get the merchant daily performance summary for Merchant \"Test Merchant 1\" for E" + @@ -688,7 +688,7 @@ await testRunner.ThenAsync("the merchant daily performance summary response shou "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table42 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table97 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -703,7 +703,7 @@ await testRunner.ThenAsync("the merchant daily performance summary response shou "ProductName", "RecipientEmail", "RecipientMobile"}); - table42.AddRow(new string[] { + table97.AddRow(new string[] { "Today", "9", "Sale", @@ -719,7 +719,7 @@ await testRunner.ThenAsync("the merchant daily performance summary response shou "", ""}); #line 138 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table42, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table97, "When "); #line hidden #line 142 await testRunner.WhenAsync("I get the merchant transaction mix summary for Merchant \"Test Merchant 1\" for Est" + @@ -763,7 +763,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table43 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table98 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -778,7 +778,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "ProductName", "RecipientEmail", "RecipientMobile"}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1001", "Sale", @@ -793,7 +793,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1002", "Sale", @@ -808,7 +808,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1003", "Sale", @@ -823,7 +823,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1004", "Sale", @@ -838,7 +838,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1005", "Sale", @@ -853,7 +853,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1006", "Sale", @@ -868,7 +868,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1007", "Sale", @@ -883,7 +883,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1008", "Sale", @@ -898,7 +898,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1009", "Sale", @@ -913,7 +913,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "Variable Topup", "", ""}); - table43.AddRow(new string[] { + table98.AddRow(new string[] { "Today", "1010", "Sale", @@ -929,7 +929,7 @@ await testRunner.ThenAsync("the merchant transaction mix summary response should "", ""}); #line 148 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table43, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table98, "When "); #line hidden #line 161 await testRunner.WhenAsync("I get the recent activity receipt search for Merchant \"Test Merchant 1\" for Estat" + diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs index 2a30933..5874b5b 100644 --- a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs @@ -111,330 +111,330 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table96 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table99 = new global::Reqnroll.Table(new string[] { "Role Name"}); - table96.AddRow(new string[] { + table99.AddRow(new string[] { "Merchant"}); #line 6 - await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table96, "Given "); + await testRunner.GivenAsync("the following security roles exist", ((string)(null)), table99, "Given "); #line hidden - global::Reqnroll.Table table97 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table100 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table97.AddRow(new string[] { + table100.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table97.AddRow(new string[] { + table100.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST Scope", "A scope for Transaction Processor ACL REST"}); #line 10 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table97, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table100, "Given "); #line hidden - global::Reqnroll.Table table98 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table101 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table98.AddRow(new string[] { + table101.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "merchantId, estateId, role"}); - table98.AddRow(new string[] { + table101.AddRow(new string[] { "transactionProcessorACL", "Transaction Processor ACL REST", "Secret1", "transactionProcessorACL", "merchantId, estateId, role"}); #line 15 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table98, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table101, "Given "); #line hidden - global::Reqnroll.Table table99 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table102 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table99.AddRow(new string[] { + table102.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,transactionProcessorACL", "client_credentials"}); - table99.AddRow(new string[] { + table102.AddRow(new string[] { "merchantClient", "Merchant Client", "Secret1", "transactionProcessorACL", "password"}); #line 20 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table99, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table102, "Given "); #line hidden - global::Reqnroll.Table table100 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table103 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table100.AddRow(new string[] { + table103.AddRow(new string[] { "serviceClient"}); #line 25 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor acl reso" + - "urces", ((string)(null)), table100, "Given "); + "urces", ((string)(null)), table103, "Given "); #line hidden - global::Reqnroll.Table table101 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table104 = new global::Reqnroll.Table(new string[] { "AccountNumber", "AccountName", "DueDate", "Amount"}); - table101.AddRow(new string[] { + table104.AddRow(new string[] { "12345678", "Test Account 1", "Today", "100.00"}); #line 29 - await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table101, "Given "); + await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table104, "Given "); #line hidden - global::Reqnroll.Table table102 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table105 = new global::Reqnroll.Table(new string[] { "Username", "Password"}); - table102.AddRow(new string[] { + table105.AddRow(new string[] { "operatora", "1234567898"}); #line 33 - await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table102, "Given "); + await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table105, "Given "); #line hidden - global::Reqnroll.Table table103 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table106 = new global::Reqnroll.Table(new string[] { "MeterNumber", "CustomerName"}); - table103.AddRow(new string[] { + table106.AddRow(new string[] { "00000001", "Customer 1"}); - table103.AddRow(new string[] { + table106.AddRow(new string[] { "00000002", "Customer 2"}); - table103.AddRow(new string[] { + table106.AddRow(new string[] { "00000003", "Customer 3"}); #line 37 - await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table103, "Given "); + await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table106, "Given "); #line hidden #line 43 await testRunner.GivenAsync("I initialise the Agency Banking Host", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table104 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table107 = new global::Reqnroll.Table(new string[] { "Type", "Code", "Name", "Currency"}); - table104.AddRow(new string[] { + table107.AddRow(new string[] { "LIABILITY", "200100", "Agent Float GL", "KES"}); - table104.AddRow(new string[] { + table107.AddRow(new string[] { "LIABILITY", "200200", "Settlement Suspense GL", "KES"}); - table104.AddRow(new string[] { + table107.AddRow(new string[] { "EXPENSE", "400100", "Agent Commission GL", "KES"}); - table104.AddRow(new string[] { + table107.AddRow(new string[] { "LIABILITY", "200300", "Reversal GL", "KES"}); - table104.AddRow(new string[] { + table107.AddRow(new string[] { "INCOME", "400200", "Fee Income GL", "KES"}); #line 45 - await testRunner.GivenAsync("I create the following accounts", ((string)(null)), table104, "Given "); + await testRunner.GivenAsync("I create the following accounts", ((string)(null)), table107, "Given "); #line hidden - global::Reqnroll.Table table105 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table108 = new global::Reqnroll.Table(new string[] { "AccountNumber", "BankCode", "Currency", "AccountName"}); - table105.AddRow(new string[] { + table108.AddRow(new string[] { "999000001", "001", "KES", "Main Settlement Account"}); #line 53 - await testRunner.GivenAsync("I create the settlement account", ((string)(null)), table105, "Given "); + await testRunner.GivenAsync("I create the settlement account", ((string)(null)), table108, "Given "); #line hidden - global::Reqnroll.Table table106 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table109 = new global::Reqnroll.Table(new string[] { "CustomerId", "FullName", "PhoneNumber", "NationalId", "AccountNumber"}); - table106.AddRow(new string[] { + table109.AddRow(new string[] { "CUST001", "Test Customer 1", "0712345678", "12345678", "12345678"}); #line 57 - await testRunner.GivenAsync("I create the following customers", ((string)(null)), table106, "Given "); + await testRunner.GivenAsync("I create the following customers", ((string)(null)), table109, "Given "); #line hidden - global::Reqnroll.Table table107 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table110 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table107.AddRow(new string[] { + table110.AddRow(new string[] { "Test Estate 1"}); - table107.AddRow(new string[] { + table110.AddRow(new string[] { "Test Estate 2"}); #line 61 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table107, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table110, "Given "); #line hidden - global::Reqnroll.Table table108 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table111 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "False", "False"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "False", "False"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "False", "False"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 2", "Safaricom", "True", "True"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 2", "Voucher", "True", "True"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 2", "PataPawa PostPay", "False", "False"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 2", "PataPawa PrePay", "False", "False"}); - table108.AddRow(new string[] { + table111.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "False", "False"}); #line 66 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table108, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table111, "Given "); #line hidden - global::Reqnroll.Table table109 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table112 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 1", "Voucher"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 1", "AgencyBanking"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 2", "Safaricom"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 2", "Voucher"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 2", "PataPawa PostPay"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 2", "PataPawa PrePay"}); - table109.AddRow(new string[] { + table112.AddRow(new string[] { "Test Estate 2", "AgencyBanking"}); #line 79 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table109, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table112, "And "); #line hidden - global::Reqnroll.Table table110 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table113 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "AgencyBanking Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 2", "PataPawa PostPay", "PataPawa PostPay Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 2", "PataPawa PrePay", "PataPawa PrePay Contract"}); - table110.AddRow(new string[] { + table113.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "AgencyBanking Contract"}); #line 92 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table110, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table113, "Given "); #line hidden - global::Reqnroll.Table table111 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table114 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -442,7 +442,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -450,7 +450,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -458,7 +458,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -466,7 +466,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Post)", "", "BillPayment"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -474,7 +474,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Pre)", "", "BillPayment"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "AgencyBanking Contract", @@ -482,7 +482,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Balance Enquiry", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "AgencyBanking Contract", @@ -490,7 +490,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Deposit", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "AgencyBanking Contract", @@ -498,7 +498,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Withdrawal", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 1", "AgencyBanking", "AgencyBanking Contract", @@ -506,7 +506,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Mini Statement", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -514,7 +514,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "Voucher", "Hospital 1 Contract", @@ -522,7 +522,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -530,7 +530,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Post)", "", "BillPayment"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -538,7 +538,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Pre)", "", "BillPayment"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "AgencyBanking Contract", @@ -546,7 +546,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Balance Enquiry", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "AgencyBanking Contract", @@ -554,7 +554,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Deposit", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "AgencyBanking Contract", @@ -562,7 +562,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Withdrawal", "", "MobileTopup"}); - table111.AddRow(new string[] { + table114.AddRow(new string[] { "Test Estate 2", "AgencyBanking", "AgencyBanking Contract", @@ -571,9 +571,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "MobileTopup"}); #line 105 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table111, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table114, "When "); #line hidden - global::Reqnroll.Table table112 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table115 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -581,7 +581,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -589,7 +589,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Fixed", "Merchant Commission", "2.50"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -597,7 +597,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -605,7 +605,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 2", "Safaricom", "Safaricom Contract", @@ -613,7 +613,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.85"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 2", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -621,7 +621,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table112.AddRow(new string[] { + table115.AddRow(new string[] { "Test Estate 2", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -630,9 +630,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "0.50"}); #line 124 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table112, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table115, "When "); #line hidden - global::Reqnroll.Table table113 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table116 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -643,7 +643,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "EmailAddress", "EstateName", "EnableAgencyBanking"}); - table113.AddRow(new string[] { + table116.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -654,7 +654,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk", "Test Estate 1", "True"}); - table113.AddRow(new string[] { + table116.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -665,7 +665,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact2@merchant2.co.uk", "Test Estate 1", "False"}); - table113.AddRow(new string[] { + table116.AddRow(new string[] { "Test Merchant 3", "Address Line 1", "TestTown", @@ -677,242 +677,242 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Estate 2", "False"}); #line 133 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table113, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table116, "Given "); #line hidden - global::Reqnroll.Table table114 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table117 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "AgencyBanking", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 2", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 2", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "AgencyBanking", "Test Merchant 2", "00000001", "10000001", "Test Estate 1"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Safaricom", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "Voucher", "Test Merchant 3", "00000003", "10000003", "Test Estate 2"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 3", "00000001", "10000001", "Test Estate 2"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 3", "00000001", "10000001", "Test Estate 2"}); - table114.AddRow(new string[] { + table117.AddRow(new string[] { "AgencyBanking", "Test Merchant 3", "00000001", "10000001", "Test Estate 2"}); #line 139 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table114, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table117, "Given "); #line hidden - global::Reqnroll.Table table115 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table118 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table115.AddRow(new string[] { + table118.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table115.AddRow(new string[] { + table118.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); - table115.AddRow(new string[] { + table118.AddRow(new string[] { "123456782", "Test Merchant 3", "Test Estate 2"}); #line 157 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table115, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table118, "Given "); #line hidden - global::Reqnroll.Table table116 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table119 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PostPay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PrePay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "AgencyBanking Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PostPay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PrePay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "AgencyBanking Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Safaricom Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "Hospital 1 Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "PataPawa PostPay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "PataPawa PrePay Contract"}); - table116.AddRow(new string[] { + table119.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "AgencyBanking Contract"}); #line 163 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table116, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table119, "When "); #line hidden - global::Reqnroll.Table table117 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table120 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table117.AddRow(new string[] { + table120.AddRow(new string[] { "Deposit1", "365.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table117.AddRow(new string[] { + table120.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 2", "Test Estate 1"}); - table117.AddRow(new string[] { + table120.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 3", "Test Estate 2"}); #line 181 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table117, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table120, "Given "); #line hidden - global::Reqnroll.Table table118 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table121 = new global::Reqnroll.Table(new string[] { "EmailAddress", "Password", "GivenName", "FamilyName", "EstateName", "MerchantName"}); - table118.AddRow(new string[] { + table121.AddRow(new string[] { "merchantuser@testmerchant1.co.uk", "123456", "TestMerchant", "User1", "Test Estate 1", "Test Merchant 1"}); - table118.AddRow(new string[] { + table121.AddRow(new string[] { "merchantuser@testmerchant2.co.uk", "123456", "TestMerchant", "User2", "Test Estate 1", "Test Merchant 2"}); - table118.AddRow(new string[] { + table121.AddRow(new string[] { "merchantuser@testmerchant3.co.uk", "123456", "TestMerchant", @@ -920,7 +920,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Estate 2", "Test Merchant 3"}); #line 187 - await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table118, "Given "); + await testRunner.GivenAsync("I have created the following security users", ((string)(null)), table121, "Given "); #line hidden } @@ -959,7 +959,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table119 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table122 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -978,7 +978,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "AccountNumber", "CustomerName", "MeterNumber"}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "1", "Sale", @@ -997,7 +997,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "4", "Sale", @@ -1016,7 +1016,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "5", "Sale", @@ -1035,7 +1035,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "8", "Sale", @@ -1054,7 +1054,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "9", "Sale", @@ -1073,7 +1073,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "Mr Test Customer", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "10", "Sale", @@ -1092,7 +1092,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", "00000001"}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "11", "Sale", @@ -1111,7 +1111,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "00000001", "Customer 1", "00000001"}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "12", "Sale", @@ -1130,7 +1130,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "13", "Sale", @@ -1149,7 +1149,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "14", "Sale", @@ -1168,7 +1168,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table119.AddRow(new string[] { + table122.AddRow(new string[] { "Today", "15", "Sale", @@ -1188,14 +1188,14 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", ""}); #line 196 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table119, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table122, "When "); #line hidden #line 211 await testRunner.GivenAsync("I am logged in as \"merchantuser@testmerchant2.co.uk\" with password \"123456\" for M" + "erchant \"Test Merchant 2\" for Estate \"Test Estate 1\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table120 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table123 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1210,7 +1210,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ProductName", "RecipientEmail", "RecipientMobile"}); - table120.AddRow(new string[] { + table123.AddRow(new string[] { "Today", "2", "Sale", @@ -1225,7 +1225,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Variable Topup", "", ""}); - table120.AddRow(new string[] { + table123.AddRow(new string[] { "Today", "6", "Sale", @@ -1241,14 +1241,14 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "123456789"}); #line 212 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table120, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table123, "When "); #line hidden #line 217 await testRunner.GivenAsync("I am logged in as \"merchantuser@testmerchant3.co.uk\" with password \"123456\" for M" + "erchant \"Test Merchant 3\" for Estate \"Test Estate 2\" with client \"merchantClient" + "\"", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); #line hidden - global::Reqnroll.Table table121 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table124 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1263,7 +1263,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ProductName", "RecipientEmail", "RecipientMobile"}); - table121.AddRow(new string[] { + table124.AddRow(new string[] { "Today", "3", "Sale", @@ -1278,7 +1278,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Variable Topup", "", ""}); - table121.AddRow(new string[] { + table124.AddRow(new string[] { "Today", "7", "Sale", @@ -1294,58 +1294,58 @@ await testRunner.GivenAsync("I have a token to access the estate management and "test@recipient.co.uk", ""}); #line 218 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table121, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table124, "When "); #line hidden - global::Reqnroll.Table table122 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table125 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "TransactionType", "ResponseCode", "ResponseMessage"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "1", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "2", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "3", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "4", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "5", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "6", "Sale", "0000", "SUCCESS"}); - table122.AddRow(new string[] { + table125.AddRow(new string[] { "Test Estate 2", "Test Merchant 3", "7", @@ -1353,7 +1353,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0000", "SUCCESS"}); #line 223 - await testRunner.ThenAsync("the sale transaction response should contain the following information", ((string)(null)), table122, "Then "); + await testRunner.ThenAsync("the sale transaction response should contain the following information", ((string)(null)), table125, "Then "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/TransactionProcessorACL/Bootstrapper/MediatorRegistry.cs b/TransactionProcessorACL/Bootstrapper/MediatorRegistry.cs index b9d6fdc..847f9c3 100644 --- a/TransactionProcessorACL/Bootstrapper/MediatorRegistry.cs +++ b/TransactionProcessorACL/Bootstrapper/MediatorRegistry.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using SimpleResults; using System.Collections.Generic; @@ -25,10 +25,10 @@ public class MediatorRegistry : ServiceRegistry /// /// Initializes a new instance of the class. /// - public MediatorRegistry() + public MediatorRegistry(IConfiguration configuration) { - string eventStoreConnectionString = Startup.Configuration.GetValue("EventStoreSettings:ConnectionString"); - int requestAuditStreamLifetimeDays = Startup.Configuration.GetValue("AuditSettings:RequestAuditStreamLifetimeDays") ?? 7; + string eventStoreConnectionString = configuration.GetValue("EventStoreSettings:ConnectionString"); + int requestAuditStreamLifetimeDays = configuration.GetValue("AuditSettings:RequestAuditStreamLifetimeDays") ?? 7; this.AddKurrentDBClient(eventStoreConnectionString); this.AddSingleton(new RequestAuditStreamOptions diff --git a/TransactionProcessorACL/Bootstrapper/MiddlewareRegistry.cs b/TransactionProcessorACL/Bootstrapper/MiddlewareRegistry.cs index a3fcd08..632644d 100644 --- a/TransactionProcessorACL/Bootstrapper/MiddlewareRegistry.cs +++ b/TransactionProcessorACL/Bootstrapper/MiddlewareRegistry.cs @@ -1,5 +1,7 @@ -using HealthMonitoring.Client; +using HealthMonitoring.Client; +using Microsoft.Extensions.Configuration; using Shared.Authorisation; +using TransactionProcessing.ApiDefaults; namespace TransactionProcessorACL.Bootstrapper { @@ -10,10 +12,14 @@ namespace TransactionProcessorACL.Bootstrapper using Shared.General; using Shared.Serialisation; using System; + using System.ComponentModel; using System.Diagnostics.CodeAnalysis; + using System.Linq; using System.Net.Http; using System.Reflection; using System.Text.Json; + using System.Threading.Tasks; + using Microsoft.AspNetCore.OpenApi; /// /// @@ -25,14 +31,30 @@ public class MiddlewareRegistry : ServiceRegistry /// /// Initializes a new instance of the class. /// - public MiddlewareRegistry() + public MiddlewareRegistry(IConfiguration configuration) { this.ConfigureHealthChecks(); this.ConfigureAuthentication(); this.ConfigurePasswordTokenHandling(); this.ConfigureJsonOptions(); this.ConfigureControllers(); - this.AddHealthMonitoringRegistration(Startup.Configuration); + this.AddHealthMonitoringRegistration(configuration); + this.AddApiDefaults(options => { + options.Title = "Transaction Processor ACL"; + options.Description = "Anti Corruption Layer for Transaction Processing"; + }); + this.ConfigureAll(options => options.AddSchemaTransformer((schema, context, _) => + { + DescriptionAttribute? description = context.JsonPropertyInfo?.AttributeProvider? + .GetCustomAttributes(typeof(DescriptionAttribute), inherit: true) + .OfType() + .FirstOrDefault(); + + if (description is not null) + schema.Description = description.Description; + + return Task.CompletedTask; + })); } /// diff --git a/TransactionProcessorACL/Common/VersionCheckMiddleware.cs b/TransactionProcessorACL/Common/VersionCheckMiddleware.cs index 3536da8..1dd8d6f 100644 --- a/TransactionProcessorACL/Common/VersionCheckMiddleware.cs +++ b/TransactionProcessorACL/Common/VersionCheckMiddleware.cs @@ -21,7 +21,10 @@ public async Task InvokeAsync(HttpContext context, IMediator mediator) { var path = context.Request.Path; - if (path.StartsWithSegments("/health") || path.StartsWithSegments("/healthui")) { + if (path.StartsWithSegments("/health") || + path.StartsWithSegments("/healthui") || + path.StartsWithSegments("/scalar") || + path.StartsWithSegments("/openapi")) { await _next(context).ConfigureAwait(false); return; } @@ -68,4 +71,4 @@ public async Task InvokeAsync(HttpContext context, var qs = context.Request.Query["applicationVersion"]; return string.IsNullOrEmpty(qs) ? null : qs.ToString(); } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL/Endpoints/EndpointDocumentationConstants.cs b/TransactionProcessorACL/Endpoints/EndpointDocumentationConstants.cs new file mode 100644 index 0000000..256fafd --- /dev/null +++ b/TransactionProcessorACL/Endpoints/EndpointDocumentationConstants.cs @@ -0,0 +1,10 @@ +namespace TransactionProcessorACL.Endpoints; + +internal static class EndpointDocumentationConstants +{ + public const string TransactionsTag = "Transactions"; + public const string ApplicationVersionQueryParameter = "applicationVersion"; + public const string ApplicationVersionExample = "1.0.5"; + public const string ApplicationVersionQueryDescription = "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505."; + public const string JsonMediaType = "application/json"; +} diff --git a/TransactionProcessorACL/Endpoints/EndpointDocumentationExtensions.cs b/TransactionProcessorACL/Endpoints/EndpointDocumentationExtensions.cs new file mode 100644 index 0000000..93de947 --- /dev/null +++ b/TransactionProcessorACL/Endpoints/EndpointDocumentationExtensions.cs @@ -0,0 +1,74 @@ +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.OpenApi; + +namespace TransactionProcessorACL.Endpoints; + +internal static class EndpointDocumentationExtensions +{ + public static RouteHandlerBuilder DocumentJsonRequestExample( + this RouteHandlerBuilder endpoint, + string exampleJson) + { + return endpoint.AddOpenApiOperationTransformer((operation, _, _) => + { + if (operation.RequestBody?.Content.TryGetValue("application/json", out OpenApiMediaType? mediaType) == true) + mediaType.Example = JsonNode.Parse(exampleJson); + + return Task.CompletedTask; + }); + } + + public static RouteHandlerBuilder DocumentJsonResponseExample( + this RouteHandlerBuilder endpoint, + int statusCode, + string exampleJson) + { + return endpoint.AddOpenApiOperationTransformer((operation, _, _) => + { + if (operation.Responses?.TryGetValue(statusCode.ToString(), out IOpenApiResponse? response) == true && + response is OpenApiResponse openApiResponse && + openApiResponse.Content?.TryGetValue("application/json", out OpenApiMediaType? jsonMediaType) == true) + { + jsonMediaType.Example = JsonNode.Parse(exampleJson); + } + + return Task.CompletedTask; + }); + } + + public static RouteHandlerBuilder DocumentQueryParameter( + this RouteHandlerBuilder endpoint, + string name, + string description, + JsonSchemaType type = JsonSchemaType.String, + object? exampleValue = null) + { + return endpoint.AddOpenApiOperationTransformer((operation, _, _) => + { + operation.Parameters ??= []; + OpenApiParameter? parameter = operation.Parameters.FirstOrDefault(candidate => + candidate.In == ParameterLocation.Query && candidate.Name == name) as OpenApiParameter; + + if (parameter is null) + { + parameter = new OpenApiParameter + { + Name = name, + In = ParameterLocation.Query + }; + operation.Parameters.Add(parameter); + } + + parameter.Description = description; + parameter.Required = true; + parameter.Schema ??= new OpenApiSchema { Type = type }; + if (exampleValue is not null) + parameter.Example = JsonSerializer.SerializeToNode(exampleValue); + return Task.CompletedTask; + }); + } +} diff --git a/TransactionProcessorACL/Endpoints/MerchantEndpoints.cs b/TransactionProcessorACL/Endpoints/MerchantEndpoints.cs index 3671cc0..bafe1b2 100644 --- a/TransactionProcessorACL/Endpoints/MerchantEndpoints.cs +++ b/TransactionProcessorACL/Endpoints/MerchantEndpoints.cs @@ -1,12 +1,8 @@ -using System.Threading; -using System.Threading.Tasks; -using MediatR; +using System.Collections.Generic; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; -using Shared.Logger; using TransactionProcessorACL.Handlers; namespace TransactionProcessorACL.Endpoints @@ -16,16 +12,46 @@ public static class MerchantEndpoints private const string BaseRoute = "/api/merchants"; public static IEndpointRouteBuilder MapMerchantEndpoints(this IEndpointRouteBuilder app) { - var group = app.MapGroup(BaseRoute).RequireAuthorization() - .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); - - group.MapGet("", MerchantHandlers.GetMerchant).WithName("GetMerchant"); + var group = app.MapGroup(BaseRoute) + .WithTags("Merchants") + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); - group.MapGet("contracts",MerchantHandlers.GetMerchantContracts).WithName("GetMerchantContracts"); + group.MapGet("", MerchantHandlers.GetMerchant) + .WithName("GetMerchant") + .WithSummary("Get the authenticated merchant") + .WithDescription("Returns merchant details, including contacts, addresses, devices, operators, contracts, opening hours, and settlement information. The estate and merchant are resolved from the authenticated token. Supply the client applicationVersion as a query parameter; it is checked against the configured minimum supported version.") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"addresses":[{"addressId":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","addressLine1":"10 High Street","addressLine2":"Unit 2","addressLine3":"","addressLine4":"","country":"United Kingdom","postalCode":"AB1 2CD","region":"Example County","town":"Exampleton"}],"contacts":[{"contactEmailAddress":"ops@example.com","contactId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","contactName":"Operations","contactPhoneNumber":"+441234567890"}],"devices":{"cccccccc-cccc-cccc-cccc-cccccccccccc":"Main terminal"},"estateId":"11111111-1111-1111-1111-111111111111","estateReportingId":100,"merchantId":"22222222-2222-2222-2222-222222222222","merchantReportingId":1024,"merchantName":"Example Merchant","merchantReference":"MERCHANT-1024","nextStatementDate":"2026-11-01T00:00:00Z","operators":[{"name":"Operator One","operatorId":"44444444-4444-4444-4444-444444444444","merchantNumber":"M-1024","terminalNumber":"T-01","isDeleted":false}],"settlementSchedule":2,"contracts":[{"contractId":"33333333-3333-3333-3333-333333333333","isDeleted":false,"contractProducts":["55555555-5555-5555-5555-555555555555"]}],"openingHours":{"Monday":{"opening":"09:00","closing":"17:00"}}}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); - group.MapGet("schedules", MerchantHandlers.GetMerchantSchedule).WithName("GetMerchantSchedule"); + group.MapGet("contracts", MerchantHandlers.GetMerchantContracts) + .WithName("GetMerchantContracts") + .WithSummary("List contracts for the authenticated merchant") + .WithDescription("Returns the contracts available to the authenticated merchant, including products and transaction fees. The estate and merchant are resolved from token claims. Supply the client applicationVersion as a query parameter.") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Produces>(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """[{"contractId":"33333333-3333-3333-3333-333333333333","contractReportingId":3001,"description":"Standard retail contract","estateId":"11111111-1111-1111-1111-111111111111","estateReportingId":100,"operatorId":"44444444-4444-4444-4444-444444444444","operatorName":"Example Operator","products":[{"displayText":"Mobile top-up","name":"Mobile Top-up","productId":"55555555-5555-5555-5555-555555555555","productReportingId":5001,"transactionFees":[{"calculationType":0,"feeType":0,"description":"Merchant fee","transactionFeeId":"66666666-6666-6666-6666-666666666666","transactionFeeReportingId":6001,"value":0.25}],"value":10.0,"productType":1}]}]""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); + + group.MapGet("schedules", MerchantHandlers.GetMerchantSchedule) + .WithName("GetMerchantSchedule") + .WithSummary("Get a merchant's trading schedule") + .WithDescription("Returns the merchant's schedule for the requested calendar year, including the days on which the merchant is closed. The estate and merchant are resolved from token claims. Provide year and applicationVersion as query parameters.") + .DocumentQueryParameter("year", "Calendar year for which to return the merchant schedule.", Microsoft.OpenApi.JsonSchemaType.Integer, 2026) + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"year":2026,"months":[{"month":1,"closedDays":[1,2]},{"month":2,"closedDays":[15]}]}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); return app; } } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL/Endpoints/ReportingEndpoints.cs b/TransactionProcessorACL/Endpoints/ReportingEndpoints.cs index 2328096..1f27208 100644 --- a/TransactionProcessorACL/Endpoints/ReportingEndpoints.cs +++ b/TransactionProcessorACL/Endpoints/ReportingEndpoints.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; @@ -11,14 +12,50 @@ public static class ReportingEndpoints public static IEndpointRouteBuilder MapReportingEndpoints(this IEndpointRouteBuilder app) { - var group = app.MapGroup(BaseRoute).RequireAuthorization().RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); + var group = app.MapGroup(BaseRoute) + .WithTags("Reporting") + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); // POST /api/reporting/dailymerchantperformancesummary - group.MapPost("dailymerchantperformancesummary", ReportingHandlers.GetMerchantDailyPerformanceSummary); + group.MapPost("dailymerchantperformancesummary", ReportingHandlers.GetMerchantDailyPerformanceSummary) + .WithName("GetMerchantDailyPerformanceSummary") + .WithSummary("Get daily merchant performance") + .WithDescription("Returns performance metrics and transaction drill-down rows for the requested merchant and date range. The estate is taken from the authenticated token. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Accepts("application/json") + .DocumentJsonRequestExample("""{"merchantReportingId":1024,"startDate":"2026-10-01T00:00:00Z","endDate":"2026-10-21T23:59:59Z","applicationVersion":"1.0.5"}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"metrics":[{"title":"Transaction count","value":42,"description":"Total completed transactions","category":1},{"title":"Transaction value","value":1250.75,"description":"Total value of completed transactions","category":2}],"drillDownTransactions":[{"reference":"SALE-20261021-0001","product":"Mobile Top-up","status":"Completed","amount":25.0,"transactionDateTime":"2026-10-21T14:30:00Z"}]}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); // POST /api/reporting/transactionmixsummary - group.MapPost("transactionmixsummary", ReportingHandlers.GetMerchantTransactionMixSummary); + group.MapPost("transactionmixsummary", ReportingHandlers.GetMerchantTransactionMixSummary) + .WithName("GetMerchantTransactionMixSummary") + .WithSummary("Get a transaction mix summary") + .WithDescription("Summarizes transactions for a merchant and date range. Choose a breakdown (transaction type, product, operator, or status) and a measure (count or value); TopN limits the ranked items. The estate is taken from the authenticated token. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Accepts("application/json") + .DocumentJsonRequestExample("""{"merchantReportingId":1024,"startDate":"2026-10-01T00:00:00Z","endDate":"2026-10-21T23:59:59Z","breakdown":2,"measure":2,"applicationVersion":"1.0.5","topN":5}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"fromDate":"2026-10-01T00:00:00Z","toDate":"2026-10-21T23:59:59Z","breakdown":2,"measure":2,"totalCount":42,"totalValue":1250.75,"items":[{"key":"55555555-5555-5555-5555-555555555555","label":"Mobile Top-up","count":30,"value":900.0}],"drillDownTransactions":[{"reference":"SALE-20261021-0001","transactionType":"Sale","product":"Mobile Top-up","operator":"Operator One","status":"Completed","amount":25.0,"transactionDateTime":"2026-10-21T14:30:00Z"}]}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); // POST /api/reporting/recentactivityreceiptsearch - group.MapPost("recentactivityreceiptsearch", ReportingHandlers.GetRecentActivityReceiptSearch); + group.MapPost("recentactivityreceiptsearch", ReportingHandlers.GetRecentActivityReceiptSearch) + .WithName("GetRecentActivityReceiptSearch") + .WithSummary("Search recent merchant activity and receipts") + .WithDescription("Searches a merchant's transactions for a report date, with optional search text and pagination. The estate is taken from the authenticated token. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Accepts("application/json") + .DocumentJsonRequestExample("""{"applicationVersion":"1.0.5","merchantReportingId":1024,"reportDate":"2026-10-21T00:00:00Z","searchText":"coffee","pageNumber":1,"pageSize":10}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"reportDate":"2026-10-21T00:00:00Z","pageNumber":1,"pageSize":10,"totalCount":1,"items":[{"reference":"SALE-20261021-0001","transactionType":"Sale","product":"Coffee","operator":"Operator One","status":"Completed","amount":4.5,"transactionDateTime":"2026-10-21T14:30:00Z","receiptReference":"RCPT-20261021-0001"}]}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); return app; } diff --git a/TransactionProcessorACL/Endpoints/TransactionEndpoints.cs b/TransactionProcessorACL/Endpoints/TransactionEndpoints.cs index 858a97d..fa4d60f 100644 --- a/TransactionProcessorACL/Endpoints/TransactionEndpoints.cs +++ b/TransactionProcessorACL/Endpoints/TransactionEndpoints.cs @@ -1,13 +1,7 @@ -using System.Threading; -using System.Threading.Tasks; -using MediatR; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Http; -using TransactionProcessorACL.Factories; -using System.Security.Claims; +using Microsoft.AspNetCore.Routing; using Shared.Authorisation; -using TransactionProcessorACL.DataTransferObjects; using TransactionProcessorACL.Handlers; namespace TransactionProcessorACL.Endpoints @@ -21,25 +15,85 @@ public static class TransactionEndpoints public static IEndpointRouteBuilder MapTransactionEndpoints(this IEndpointRouteBuilder app) { - var saleGroup = app.MapGroup(SaleBaseRoute).RequireAuthorization().RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); + var saleGroup = app.MapGroup(SaleBaseRoute) + .WithTags(EndpointDocumentationConstants.TransactionsTag) + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); // POST /api/saletransactions - saleGroup.MapPost("", TransactionHandlers.PerformSaleTransaction); + saleGroup.MapPost("", TransactionHandlers.PerformSaleTransaction) + .WithName("ProcessSaleTransaction") + .WithSummary("Process a sale transaction") + .WithDescription("Submits a sale for the authenticated merchant. Estate and merchant identifiers are resolved from token claims. The request includes transaction details, contract, operator, product, and optional customer and metadata fields. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter(EndpointDocumentationConstants.ApplicationVersionQueryParameter, EndpointDocumentationConstants.ApplicationVersionQueryDescription, exampleValue: EndpointDocumentationConstants.ApplicationVersionExample) + .Accepts(EndpointDocumentationConstants.JsonMediaType) + .DocumentJsonRequestExample("""{"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","applicationVersion":"1.0.5","deviceIdentifier":"terminal-01","transactionDateTime":"2026-10-21T14:30:00Z","transactionNumber":"SALE-20261021-0001","additionalRequestMetadata":{"channel":"terminal"},"contractId":"33333333-3333-3333-3333-333333333333","operatorId":"44444444-4444-4444-4444-444444444444","customerEmailAddress":"customer@example.com","productId":"55555555-5555-5555-5555-555555555555"}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"responseCode":"00","responseMessage":"Approved","additionalResponseMetadata":{"authorizationCode":"AUTH-12345"},"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","requiresApplicationUpdate":false,"transactionId":"77777777-7777-7777-7777-777777777777"}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); - var logonGroup = app.MapGroup(LogonBaseRoute).RequireAuthorization().RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); + var logonGroup = app.MapGroup(LogonBaseRoute) + .WithTags(EndpointDocumentationConstants.TransactionsTag) + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); // POST /api/logontransactions - logonGroup.MapPost("", TransactionHandlers.PerformLogonTransaction); + logonGroup.MapPost("", TransactionHandlers.PerformLogonTransaction) + .WithName("ProcessLogonTransaction") + .WithSummary("Process a logon transaction") + .WithDescription("Submits a logon transaction for the authenticated merchant. Estate and merchant identifiers are resolved from token claims. The request includes transaction date, transaction number, device identifier, application version, and optional metadata. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter(EndpointDocumentationConstants.ApplicationVersionQueryParameter, EndpointDocumentationConstants.ApplicationVersionQueryDescription, exampleValue: EndpointDocumentationConstants.ApplicationVersionExample) + .Accepts(EndpointDocumentationConstants.JsonMediaType) + .DocumentJsonRequestExample("""{"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","applicationVersion":"1.0.5","deviceIdentifier":"terminal-01","transactionDateTime":"2026-10-21T08:00:00Z","transactionNumber":"LOGON-20261021-0001","additionalRequestMetadata":{"terminalModel":"countertop"}}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"responseCode":"00","responseMessage":"Logon accepted","additionalResponseMetadata":{},"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","requiresApplicationUpdate":false,"transactionId":"88888888-8888-8888-8888-888888888888"}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); - var reconciliationGroup = app.MapGroup(ReconciliationBaseRoute).RequireAuthorization().RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); + var reconciliationGroup = app.MapGroup(ReconciliationBaseRoute) + .WithTags(EndpointDocumentationConstants.TransactionsTag) + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); // POST /api/reconciliationtransactions - reconciliationGroup.MapPost("", TransactionHandlers.PerformReconciliationTransaction); + reconciliationGroup.MapPost("", TransactionHandlers.PerformReconciliationTransaction) + .WithName("ProcessReconciliation") + .WithSummary("Submit transaction reconciliation") + .WithDescription("Submits reconciliation totals for the authenticated merchant, including total transaction count, transaction value, and optional totals by operator and contract. Estate and merchant identifiers are resolved from token claims. Provide applicationVersion as a query parameter for minimum-version validation; the middleware also reads application_version from the JSON body.") + .DocumentQueryParameter(EndpointDocumentationConstants.ApplicationVersionQueryParameter, EndpointDocumentationConstants.ApplicationVersionQueryDescription, exampleValue: EndpointDocumentationConstants.ApplicationVersionExample) + .Accepts(EndpointDocumentationConstants.JsonMediaType) + .DocumentJsonRequestExample("""{"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","applicationVersion":"1.0.5","deviceIdentifier":"terminal-01","transactionDateTime":"2026-10-21T23:59:59Z","transactionNumber":"RECON-20261021-0001","additionalRequestMetadata":{"batch":"daily"},"operatorTotals":[{"contractId":"33333333-3333-3333-3333-333333333333","operatorId":"44444444-4444-4444-4444-444444444444","transactionCount":42,"transactionValue":1250.75}],"transactionCount":42,"transactionValue":1250.75}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"responseCode":"00","responseMessage":"Reconciliation accepted","additionalResponseMetadata":{},"estateId":"11111111-1111-1111-1111-111111111111","merchantId":"22222222-2222-2222-2222-222222222222","requiresApplicationUpdate":false,"transactionId":"99999999-9999-9999-9999-999999999999"}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); - var transactionGroup = app.MapGroup(TransactionsBaseRoute).RequireAuthorization().RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); + var transactionGroup = app.MapGroup(TransactionsBaseRoute) + .WithTags(EndpointDocumentationConstants.TransactionsTag) + .RequireAuthorization() + .RequireAuthorization(AuthorizationExtensions.PolicyNames.PasswordTokenOnlyPolicy); // POST /api/transactions/resendreceipt - transactionGroup.MapPost("resendreceipt", TransactionHandlers.ResendReceipt); + transactionGroup.MapPost("resendreceipt", TransactionHandlers.ResendReceipt) + .WithName("ResendReceipt") + .WithSummary("Resend a transaction receipt") + .WithDescription("Sends a receipt to the requested email address for a transaction or receipt reference belonging to the authenticated merchant. Estate and merchant identifiers are resolved from token claims. The reference and recipientEmailAddress fields are required; the email address must be valid. Provide applicationVersion as a query parameter for minimum-version validation.") + .DocumentQueryParameter(EndpointDocumentationConstants.ApplicationVersionQueryParameter, EndpointDocumentationConstants.ApplicationVersionQueryDescription, exampleValue: EndpointDocumentationConstants.ApplicationVersionExample) + .Accepts(EndpointDocumentationConstants.JsonMediaType) + .DocumentJsonRequestExample("""{"reference":"SALE-20261021-0001","recipientEmailAddress":"customer@example.com"}""") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"success":true,"message":"Receipt sent","reference":"SALE-20261021-0001","receiptReference":"RCPT-20261021-0001","transactionReference":"SALE-20261021-0001"}""") + .Produces(StatusCodes.Status400BadRequest) + .DocumentJsonResponseExample(StatusCodes.Status400BadRequest, """{"success":false,"message":"Recipient email address is not valid.","reference":"SALE-20261021-0001","receiptReference":"","transactionReference":""}""") + .Produces(StatusCodes.Status404NotFound) + .DocumentJsonResponseExample(StatusCodes.Status404NotFound, """{"success":false,"message":"The transaction or receipt reference was not found.","reference":"SALE-20261021-0001","receiptReference":"","transactionReference":""}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status403Forbidden) + .Produces(505); return app; } diff --git a/TransactionProcessorACL/Endpoints/VoucherEndpoints.cs b/TransactionProcessorACL/Endpoints/VoucherEndpoints.cs index 4f8ca16..c5b791e 100644 --- a/TransactionProcessorACL/Endpoints/VoucherEndpoints.cs +++ b/TransactionProcessorACL/Endpoints/VoucherEndpoints.cs @@ -3,8 +3,8 @@ using System.Security.Claims; using MediatR; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; using TransactionProcessorACL.Factories; using Microsoft.AspNetCore.Mvc; using TransactionProcessorACL.Handlers; @@ -18,13 +18,32 @@ public static class VoucherEndpoints public static IEndpointRouteBuilder MapVoucherEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup(BaseRoute) - .RequireAuthorization(); + .WithTags("Vouchers") + .RequireAuthorization(); - group.MapGet("", VoucherHandlers.GetVoucher); + group.MapGet("", VoucherHandlers.GetVoucher) + .WithName("GetVoucher") + .WithSummary("Get voucher details") + .WithDescription("Looks up a voucher by voucherCode for the estate and contract identified by the authenticated token. Provide voucherCode and applicationVersion as query parameters. The application version is checked against the configured minimum.") + .DocumentQueryParameter("voucherCode", "Voucher code to look up.", exampleValue: "VOUCHER-EXAMPLE-001") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"responseCode":"00","responseMessage":"Voucher found","contractId":"33333333-3333-3333-3333-333333333333","estateId":"11111111-1111-1111-1111-111111111111","expiryDate":"2027-10-21T23:59:59Z","value":50.0,"balance":35.0,"voucherCode":"VOUCHER-EXAMPLE-001","voucherId":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(505); - group.MapPut("", VoucherHandlers.RedeemVoucher); + group.MapPut("", VoucherHandlers.RedeemVoucher) + .WithName("RedeemVoucher") + .WithSummary("Redeem a voucher") + .WithDescription("Redeems the voucher identified by voucherCode for the estate and contract identified by the authenticated token. Provide voucherCode and applicationVersion as query parameters. The application version is checked against the configured minimum.") + .DocumentQueryParameter("voucherCode", "Voucher code to redeem.", exampleValue: "VOUCHER-EXAMPLE-001") + .DocumentQueryParameter("applicationVersion", "Required client version. Use a version meeting the configured minimum; requests below it return HTTP 505.", exampleValue: "1.0.5") + .Produces(StatusCodes.Status200OK) + .DocumentJsonResponseExample(StatusCodes.Status200OK, """{"responseCode":"00","responseMessage":"Voucher redeemed","contractId":"33333333-3333-3333-3333-333333333333","estateId":"11111111-1111-1111-1111-111111111111","expiryDate":"2027-10-21T23:59:59Z","balance":0.0,"voucherCode":"VOUCHER-EXAMPLE-001"}""") + .Produces(StatusCodes.Status401Unauthorized) + .Produces(505); return app; } } -} \ No newline at end of file +} diff --git a/TransactionProcessorACL/Handlers/ReportingHandlers.cs b/TransactionProcessorACL/Handlers/ReportingHandlers.cs index 32be1b6..1985712 100644 --- a/TransactionProcessorACL/Handlers/ReportingHandlers.cs +++ b/TransactionProcessorACL/Handlers/ReportingHandlers.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -8,6 +10,8 @@ using SimpleResults; using TransactionProcessorACL.BusinessLogic.Requests; using TransactionProcessorACL.DataTransferObjects.Requests; +using ApiResponses = TransactionProcessorACL.DataTransferObjects.Responses; +using TransactionProcessorACL.Models; namespace TransactionProcessorACL.Handlers; @@ -23,9 +27,26 @@ public static async Task GetMerchantDailyPerformanceSummary(ClaimsPrinc return ResponseFactory.FromResult(Result.Forbidden()); ReportingQueries.GetMerchantDailyPerformanceSummaryQuery query = new(estateIdResult.Data, request); - Result result = + Result result = await mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, response => response); + return ResponseFactory.FromResult(result, response => new ApiResponses.MerchantDailyPerformanceSummaryResponse + { + Metrics = response.Metrics.Select(metric => new ApiResponses.MetricItem + { + Category = metric.Category, + Description = metric.Description, + Title = metric.Title, + Value = metric.Value + }).ToList(), + DrillDownTransactions = response.DrillDownTransactions.Select(transaction => new ApiResponses.DrillDownTransaction + { + Amount = transaction.Amount, + Product = transaction.Product, + Reference = transaction.Reference, + Status = transaction.Status, + TransactionDateTime = transaction.TransactionDateTime + }).ToList() + }); } public static async Task GetMerchantTransactionMixSummary(ClaimsPrincipal user, @@ -38,8 +59,33 @@ public static async Task GetMerchantTransactionMixSummary(ClaimsPrincip return ResponseFactory.FromResult(Result.Forbidden()); ReportingQueries.GetMerchantTransactionMixSummaryQuery query = new(estateIdResult.Data, request); - Result result = await mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, response => response); + Result result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, response => new ApiResponses.MerchantTransactionMixSummaryResponse + { + Breakdown = Enum.Parse(response.Breakdown.ToString()), + Measure = Enum.Parse(response.Measure.ToString()), + FromDate = response.FromDate, + ToDate = response.ToDate, + TotalCount = response.TotalCount, + TotalValue = response.TotalValue, + Items = response.Items.Select(item => new ApiResponses.TransactionMixSummaryItem + { + Key = item.Key, + Label = item.Label, + Count = item.Count, + Value = item.Value + }).ToList(), + DrillDownTransactions = response.DrillDownTransactions.Select(transaction => new ApiResponses.TransactionMixDrillDownTransaction + { + Amount = transaction.Amount, + Operator = transaction.Operator, + Product = transaction.Product, + Reference = transaction.Reference, + Status = transaction.Status, + TransactionDateTime = transaction.TransactionDateTime, + TransactionType = transaction.TransactionType + }).ToList() + }); } public static async Task GetRecentActivityReceiptSearch(ClaimsPrincipal user, @@ -52,8 +98,25 @@ public static async Task GetRecentActivityReceiptSearch(ClaimsPrincipal return ResponseFactory.FromResult(Result.Forbidden()); ReportingQueries.GetRecentActivityReceiptSearchQuery query = new(estateIdResult.Data, request); - Result result = + Result result = await mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, response => response); + return ResponseFactory.FromResult(result, response => new ApiResponses.RecentActivityReceiptSearchResponse + { + ReportDate = response.ReportDate, + PageNumber = response.PageNumber, + PageSize = response.PageSize, + TotalCount = response.TotalCount, + Items = response.Items.Select(item => new ApiResponses.RecentActivityReceiptSearchItem + { + Amount = item.Amount, + Operator = item.Operator, + Product = item.Product, + ReceiptReference = item.ReceiptReference, + Reference = item.Reference, + Status = item.Status, + TransactionDateTime = item.TransactionDateTime, + TransactionType = item.TransactionType + }).ToList() + }); } } diff --git a/TransactionProcessorACL/Handlers/TransactionHandlers.cs b/TransactionProcessorACL/Handlers/TransactionHandlers.cs index cd1a8d5..1b6f0d3 100644 --- a/TransactionProcessorACL/Handlers/TransactionHandlers.cs +++ b/TransactionProcessorACL/Handlers/TransactionHandlers.cs @@ -12,6 +12,7 @@ using TransactionProcessorACL.BusinessLogic.Requests; using TransactionProcessorACL.DataTransferObjects; using TransactionProcessorACL.DataTransferObjects.Requests; +using ApiResponses = TransactionProcessorACL.DataTransferObjects.Responses; using TransactionProcessorACL.Factories; using TransactionProcessorACL.Models; @@ -79,18 +80,18 @@ public static async Task ResendReceipt(IMediator mediator, } if (string.IsNullOrWhiteSpace(transactionRequest.Reference)) { - return Results.BadRequest(new ResendReceiptResponse { Success = false, Message = "A transaction reference or receipt reference is required." }); + return Results.BadRequest(new ApiResponses.ResendReceiptResponse { Success = false, Message = "A transaction reference or receipt reference is required." }); } if (string.IsNullOrWhiteSpace(transactionRequest.RecipientEmailAddress)) { - return Results.BadRequest(new ResendReceiptResponse { Success = false, Message = "Recipient email address is required." }); + return Results.BadRequest(new ApiResponses.ResendReceiptResponse { Success = false, Message = "Recipient email address is required." }); } try { _ = new MailAddress(transactionRequest.RecipientEmailAddress); } catch (FormatException) { - return Results.BadRequest(new ResendReceiptResponse { Success = false, Message = "Recipient email address is not valid." }); + return Results.BadRequest(new ApiResponses.ResendReceiptResponse { Success = false, Message = "Recipient email address is not valid." }); } TransactionCommands.ResendReceiptCommand resendCommand = new(claimsResult.Data.estateId, @@ -100,10 +101,21 @@ public static async Task ResendReceipt(IMediator mediator, Result resendResponse = await mediator.Send(resendCommand, cancellationToken); if (resendResponse.IsFailed && resendResponse.Status == ResultStatus.NotFound) { - return Results.NotFound(new ResendReceiptResponse { Success = false, Message = resendResponse.Message }); + return Results.NotFound(new ApiResponses.ResendReceiptResponse + { + Success = false, + Message = resendResponse.Message + }); } - return ResponseFactory.FromResult(resendResponse, response => response); + return ResponseFactory.FromResult(resendResponse, response => new ApiResponses.ResendReceiptResponse + { + Success = response.Success, + Message = response.Message, + Reference = response.Reference, + ReceiptReference = response.ReceiptReference, + TransactionReference = response.TransactionReference + }); } private static TransactionCommands.ProcessLogonTransactionCommand CreateLogonCommand(Guid estateId, Guid merchantId, LogonTransactionRequestMessage msg) diff --git a/TransactionProcessorACL/Program.cs b/TransactionProcessorACL/Program.cs index d7dcb66..ca8981d 100644 --- a/TransactionProcessorACL/Program.cs +++ b/TransactionProcessorACL/Program.cs @@ -1,104 +1,186 @@ +using Lamar; +using Lamar.Microsoft.DependencyInjection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using NLog; +using NLog.Extensions.Logging; +using Shared.General; +using Shared.Extensions; using Shared.Logger; using Shared.Middleware; +using Shared.Serialisation; +using Sentry.Extensibility; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using TransactionProcessorACL.Bootstrapper; +using TransactionProcessorACL.Endpoints; +using TransactionProcessorACL.Middleware; +using HealthChecks.UI.Client; using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; +using TransactionProcessing.ApiDefaults; +using TransactionProcessorACL.Common; namespace TransactionProcessorACL; -using Lamar.Microsoft.DependencyInjection; -using NLog; -using NLog.Extensions.Logging; -using Sentry.Extensibility; -using Shared.General; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Reflection; - [ExcludeFromCodeCoverage] -public class Program { - public static void Main(string[] args) { - Program.CreateHostBuilder(args).Build().Run(); +public class Program +{ + public static void Main(string[] args) + { + WebApplication app = CreateWebApplication(args); + app.Run(); } - public static IHostBuilder CreateHostBuilder(string[] args) { - String contentRoot = Directory.GetCurrentDirectory(); - String nlogConfigPath = Path.Combine(contentRoot, "nlog.config"); + public static WebApplication CreateWebApplication(string[] args) + { + string contentRoot = Directory.GetCurrentDirectory(); + string nlogConfigPath = Path.Combine(contentRoot, "nlog.config"); - LogManager.Setup(b => { - b.SetupLogFactory(setup => { + LogManager.Setup(builder => + { + builder.SetupLogFactory(setup => + { setup.AddCallSiteHiddenAssembly(typeof(NlogLogger).Assembly); setup.AddCallSiteHiddenAssembly(typeof(Shared.Logger.Logger).Assembly); setup.AddCallSiteHiddenAssembly(typeof(TenantMiddleware).Assembly); }); - b.LoadConfigurationFromFile(nlogConfigPath); + builder.LoadConfigurationFromFile(nlogConfigPath); }); - IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args); - hostBuilder.UseWindowsService(); - hostBuilder.UseLamar(); - hostBuilder.ConfigureLogging(logging => { - logging.AddConsole(); - logging.AddNLog(); - }); - ConfigureWebHost(hostBuilder); + WebApplicationBuilder appBuilder = WebApplication.CreateBuilder(args); + appBuilder.Host.UseWindowsService(); + appBuilder.Host.UseLamar(); + appBuilder.Logging.AddConsole(); + appBuilder.Logging.AddNLog(); + appBuilder.WebHost.UseKestrel(); - return hostBuilder; + ConfigureApplicationConfiguration(appBuilder); + + IConfigurationRoot configuration = appBuilder.Configuration; + ConfigurationReader.Initialise(configuration); + ConfigureServices(appBuilder, configuration); + ConfigureSentry(appBuilder, configuration); + + WebApplication app = appBuilder.Build(); + StringSerialiser.Initialise(app.Services.GetRequiredService()); + ConfigurePipeline(app, configuration); + + return app; } - private static void ConfigureWebHost(IHostBuilder hostBuilder) { - - FileInfo fi = new(Assembly.GetExecutingAssembly().Location); - hostBuilder.ConfigureWebHostDefaults(webBuilder => { - webBuilder.ConfigureAppConfiguration((context, - configBuilder) => { - var env = context.HostingEnvironment; - - configBuilder.SetBasePath(fi.Directory.FullName) - .AddJsonFile("hosting.json", optional: true) - .AddJsonFile($"hosting.{env.EnvironmentName}.json", optional: true) - .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"/home/txnproc/config/appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile($"/home/txnproc/config/appsettings.local.json", optional: true, reloadOnChange: true) - .AddEnvironmentVariables() - .AddInMemoryCollection(new Dictionary - { - ["HealthMonitoring:Service:Version"] = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0" - }); - - // Build a snapshot of configuration so we can use it immediately (e.g. for Sentry) - var builtConfig = configBuilder.Build(); - - // Keep existing static usage (if you must), and initialise the ConfigurationReader now. - Startup.Configuration = builtConfig; - ConfigurationReader.Initialise(Startup.Configuration); - - // Configure Sentry on the webBuilder using the config snapshot. - var sentrySection = builtConfig.GetSection("SentryConfiguration"); - if (sentrySection.Exists()) { - // Replace the condition below if you intended to only enable Sentry in certain environments. - if (env.IsDevelopment() == false) { - webBuilder.UseSentry(o => { - o.Dsn = builtConfig["SentryConfiguration:Dsn"]; - o.SendDefaultPii = true; - o.MaxRequestBodySize = RequestSize.Always; - o.CaptureBlockingCalls = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "CaptureBlockingCalls", false); - o.IncludeActivityData = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "IncludeActivityData", false); - o.Release = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; - }); - } - } + private static void ConfigureApplicationConfiguration(WebApplicationBuilder appBuilder) + { + FileInfo assemblyFile = new(Assembly.GetExecutingAssembly().Location); + + appBuilder.Configuration + .SetBasePath(assemblyFile.Directory!.FullName) + .AddJsonFile("hosting.json", optional: true) + .AddJsonFile($"hosting.{appBuilder.Environment.EnvironmentName}.json", optional: true) + .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"/home/txnproc/config/appsettings.{appBuilder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{appBuilder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile("/home/txnproc/config/appsettings.local.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .AddInMemoryCollection(new Dictionary + { + ["HealthMonitoring:Service:Version"] = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0" }); + } - webBuilder.UseStartup(); - webBuilder.UseKestrel(); + private static void ConfigureServices(WebApplicationBuilder appBuilder, IConfiguration configuration) + { + appBuilder.Host.ConfigureContainer((_, services) => + { + services.IncludeRegistry(new MiddlewareRegistry(configuration)); + services.IncludeRegistry(); + services.IncludeRegistry(); + services.IncludeRegistry(new MediatorRegistry(configuration)); + services.IncludeRegistry(); + services.IncludeRegistry(); }); } -} \ No newline at end of file + + private static void ConfigureSentry(WebApplicationBuilder appBuilder, IConfiguration configuration) + { + IConfigurationSection sentrySection = configuration.GetSection("SentryConfiguration"); + if (sentrySection.Exists() && appBuilder.Environment.IsDevelopment() == false) + { + appBuilder.WebHost.UseSentry(options => + { + options.Dsn = configuration["SentryConfiguration:Dsn"]; + options.SendDefaultPii = true; + options.MaxRequestBodySize = RequestSize.Always; + options.CaptureBlockingCalls = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "CaptureBlockingCalls", false); + options.IncludeActivityData = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "IncludeActivityData", false); + options.Release = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; + }); + } + } + + private static void ConfigurePipeline(WebApplication app, IConfigurationRoot configuration) + { + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + Microsoft.Extensions.Logging.ILogger logger = app.Services.GetRequiredService().CreateLogger("TransactionProcessor"); + Shared.Logger.Logger.Initialise(logger); + configuration.LogConfiguration(Shared.Logger.Logger.LogWarning); + + app.UseMiddleware(); + app.AddRequestResponseLogging(); + app.AddExceptionHandler(); + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseMiddleware(); + app.UseMiddleware(); + + MapEndpoints(app); + app.UseApiDefaults(); + } + + private static void MapEndpoints(IEndpointRouteBuilder endpoints) + { + endpoints.MapMerchantEndpoints(); + endpoints.MapReportingEndpoints(); + endpoints.MapTransactionEndpoints(); + endpoints.MapVoucherEndpoints(); + endpoints.MapHealthChecks("health", CreateHealthCheckOptions(Shared.HealthChecks.HealthCheckMiddleware.WriteResponse)) + .WithName("Health") + .WithTags("Health") + .WithSummary("Check service health") + .WithDescription("Runs the configured service health checks and returns their aggregate status. This route does not require an applicationVersion.") + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(object), new[] { EndpointDocumentationConstants.JsonMediaType })) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status503ServiceUnavailable, typeof(object), new[] { EndpointDocumentationConstants.JsonMediaType })); + endpoints.MapHealthChecks("healthui", CreateHealthCheckOptions(UIResponseWriter.WriteHealthCheckUIResponse)) + .WithName("HealthUi") + .WithTags("Health") + .WithSummary("Get health check details") + .WithDescription("Runs all configured service health checks and returns the detailed Health Checks UI response. This route does not require an applicationVersion.") + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(object), new[] { EndpointDocumentationConstants.JsonMediaType })) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status503ServiceUnavailable, typeof(object), new[] { EndpointDocumentationConstants.JsonMediaType })); + } + + private static HealthCheckOptions CreateHealthCheckOptions(Func responseWriter) + { + return new HealthCheckOptions + { + Predicate = _ => true, + ResponseWriter = responseWriter + }; + } +} diff --git a/TransactionProcessorACL/Startup.cs b/TransactionProcessorACL/Startup.cs deleted file mode 100644 index 5809d54..0000000 --- a/TransactionProcessorACL/Startup.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using TransactionProcessorACL.Endpoints; - -namespace TransactionProcessorACL -{ - using Bootstrapper; - using HealthChecks.UI.Client; - using Lamar; - using Microsoft.AspNetCore.Diagnostics.HealthChecks; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Routing; - using Microsoft.Extensions.Diagnostics.HealthChecks; - using Shared.Extensions; - using Shared.General; - using Shared.Logger; - using Shared.Middleware; - using Shared.Serialisation; - using System; - using System.Diagnostics.CodeAnalysis; - using System.Threading.Tasks; - using TransactionProcessorACL.Common; - using TransactionProcessorACL.Middleware; - using ILogger = Microsoft.Extensions.Logging.ILogger; - - [ExcludeFromCodeCoverage] - public class Startup - { - public Startup(IWebHostEnvironment webHostEnvironment) - { - Startup.WebHostEnvironment = webHostEnvironment; - } - - public static IConfigurationRoot Configuration { get; set; } - - public static IWebHostEnvironment WebHostEnvironment { get; set; } - - // This method gets called by the runtime. Use this method to add services to the container. - public static Container Container; - - public void ConfigureContainer(ServiceRegistry services) - { - ConfigurationReader.Initialise(Startup.Configuration); - - services.IncludeRegistry(); - services.IncludeRegistry(); - services.IncludeRegistry(); - services.IncludeRegistry(); - services.IncludeRegistry(); - services.IncludeRegistry(); - - Startup.Container = new Container(services); - - var serialiser = Container.GetRequiredService(); - StringSerialiser.Initialise(serialiser); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, - IHostApplicationLifetime lifetime, IHost host) - { - UseDevelopmentExceptionPage(app, env); - InitializeLogger(loggerFactory); - ConfigureMiddleware(app); - ConfigureEndpoints(app); - } - - private static void UseDevelopmentExceptionPage(IApplicationBuilder app, - IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - } - - private static void InitializeLogger(ILoggerFactory loggerFactory) - { - ILogger logger = loggerFactory.CreateLogger("TransactionProcessor"); - - Logger.Initialise(logger); - Startup.Configuration.LogConfiguration(Logger.LogWarning); - } - - private static void ConfigureMiddleware(IApplicationBuilder app) - { - app.UseMiddleware(); - app.AddRequestResponseLogging(); - app.AddExceptionHandler(); - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseMiddleware(); - app.UseMiddleware(); - } - - private static void ConfigureEndpoints(IApplicationBuilder app) - { - app.UseEndpoints(MapEndpoints); - } - - private static void MapEndpoints(IEndpointRouteBuilder endpoints) - { - endpoints.MapMerchantEndpoints(); - endpoints.MapReportingEndpoints(); - endpoints.MapTransactionEndpoints(); - endpoints.MapVoucherEndpoints(); - endpoints.MapHealthChecks("health", CreateHealthCheckOptions(Shared.HealthChecks.HealthCheckMiddleware.WriteResponse)); - endpoints.MapHealthChecks("healthui", CreateHealthCheckOptions(UIResponseWriter.WriteHealthCheckUIResponse)); - } - - private static HealthCheckOptions CreateHealthCheckOptions(Func responseWriter) - { - return new HealthCheckOptions - { - Predicate = _ => true, - ResponseWriter = responseWriter - }; - } - } -} diff --git a/TransactionProcessorACL/TransactionProcessorACL.csproj b/TransactionProcessorACL/TransactionProcessorACL.csproj index 4b446e1..8e2ff6c 100644 --- a/TransactionProcessorACL/TransactionProcessorACL.csproj +++ b/TransactionProcessorACL/TransactionProcessorACL.csproj @@ -31,14 +31,9 @@ - - - - - - +