From aeca0fbfe6718ca35648f3472ffc76d035ab24c0 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Sat, 10 Apr 2021 06:53:42 +0100 Subject: [PATCH 1/3] Swagger review --- .../RequestTests.cs | 4 +- .../ProcessLogonTransactionRequest.cs | 18 +- ...ansactionProcessorACL.BusinessLogic.csproj | 2 +- .../LogonTransactionRequestMessage.cs | 11 +- .../Requests/OperatorTotalRequest.cs | 5 + .../Requests/ReconciliationRequestMessage.cs | 4 + .../Requests/SaleTransactionRequestMessage.cs | 21 +- .../Requests/TransactionRequestMessage.cs | 6 + .../Responses/TransactionResponseMessage.cs | 7 + .../Reconciliation/Reconciliation.feature.cs | 560 +++++++++++++++++- .../SalesTransaction.feature.cs | 4 +- .../Shared/SharedSteps.cs | 1 - ...actionProcessorACL.IntegrationTests.csproj | 2 +- TransactionProcessorACL.Testing/TestData.cs | 11 +- .../Common/ConfigureSwaggerOptions.cs | 86 --- .../Common/Examples/ExampleData.cs | 85 +++ .../TransactionRequestMessageExample.cs | 81 +++ .../TransactionResponseMessageExample.cs | 68 +++ .../Common/SwaggerDefaultValues.cs | 5 - .../Controllers/TransactionController.cs | 12 +- TransactionProcessorACL/Startup.cs | 64 +- .../TransactionProcessorACL.csproj | 14 +- 22 files changed, 879 insertions(+), 192 deletions(-) delete mode 100644 TransactionProcessorACL/Common/ConfigureSwaggerOptions.cs create mode 100644 TransactionProcessorACL/Common/Examples/ExampleData.cs create mode 100644 TransactionProcessorACL/Common/Examples/TransactionRequestMessageExample.cs create mode 100644 TransactionProcessorACL/Common/Examples/TransactionResponseMessageExample.cs diff --git a/TransactionProcessorACL.BusinessLogic.Tests/RequestTests.cs b/TransactionProcessorACL.BusinessLogic.Tests/RequestTests.cs index 6b26e20..e9f371a 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/RequestTests.cs +++ b/TransactionProcessorACL.BusinessLogic.Tests/RequestTests.cs @@ -22,15 +22,13 @@ public void ProcessLogonTransactionRequest_CanBeCreated_IsCreated() TestData.MerchantId, TestData.TransactionDateTime, TestData.TransactionNumber, - TestData.DeviceIdentifier, - TestData.RequireConfigurationInResponseTrue); + TestData.DeviceIdentifier); request.EstateId.ShouldBe(TestData.EstateId); request.MerchantId.ShouldBe(TestData.MerchantId); request.TransactionDateTime.ShouldBe(TestData.TransactionDateTime); request.TransactionNumber.ShouldBe(TestData.TransactionNumber); request.DeviceIdentifier.ShouldBe(TestData.DeviceIdentifier); - request.RequireConfigurationInResponse.ShouldBe(TestData.RequireConfigurationInResponseTrue); } [Fact] diff --git a/TransactionProcessorACL.BusinessLogic/Requests/ProcessLogonTransactionRequest.cs b/TransactionProcessorACL.BusinessLogic/Requests/ProcessLogonTransactionRequest.cs index 76ef4ff..526410c 100644 --- a/TransactionProcessorACL.BusinessLogic/Requests/ProcessLogonTransactionRequest.cs +++ b/TransactionProcessorACL.BusinessLogic/Requests/ProcessLogonTransactionRequest.cs @@ -32,13 +32,11 @@ private ProcessLogonTransactionRequest(Guid estateId, Guid merchantId, DateTime transactionDateTime, String transactionNumber, - String deviceIdentifier, - Boolean requireConfigurationInResponse) + String deviceIdentifier) { this.EstateId = estateId; this.MerchantId = merchantId; this.DeviceIdentifier = deviceIdentifier; - this.RequireConfigurationInResponse = requireConfigurationInResponse; this.TransactionDateTime = transactionDateTime; this.TransactionNumber = transactionNumber; } @@ -71,14 +69,6 @@ private ProcessLogonTransactionRequest(Guid estateId, /// public Guid MerchantId { get; } - /// - /// Gets a value indicating whether [require configuration in response]. - /// - /// - /// true if [require configuration in response]; otherwise, false. - /// - public Boolean RequireConfigurationInResponse { get; } - /// /// Gets the transaction date time. /// @@ -107,16 +97,14 @@ private ProcessLogonTransactionRequest(Guid estateId, /// The transaction date time. /// The transaction number. /// The device identifier. - /// if set to true [require configuration in response]. /// public static ProcessLogonTransactionRequest Create(Guid estateId, Guid merchantId, DateTime transactionDateTime, String transactionNumber, - String deviceIdentifier, - Boolean requireConfigurationInResponse) + String deviceIdentifier) { - return new ProcessLogonTransactionRequest(estateId, merchantId, transactionDateTime, transactionNumber, deviceIdentifier, requireConfigurationInResponse); + return new ProcessLogonTransactionRequest(estateId, merchantId, transactionDateTime, transactionNumber, deviceIdentifier); } #endregion diff --git a/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj b/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj index 29c1d63..0ce1900 100644 --- a/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj +++ b/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj @@ -8,7 +8,7 @@ - + diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/LogonTransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/LogonTransactionRequestMessage.cs index 03672e8..5c4068e 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/LogonTransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/LogonTransactionRequestMessage.cs @@ -2,6 +2,7 @@ { using System; using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; /// /// @@ -10,16 +11,6 @@ [ExcludeFromCodeCoverage] public class LogonTransactionRequestMessage : TransactionRequestMessage { - #region Properties - /// - /// Gets or sets a value indicating whether [require configuration in response]. - /// - /// - /// true if [require configuration in response]; otherwise, false. - /// - public Boolean RequireConfigurationInResponse { get; set; } - - #endregion } } \ No newline at end of file diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs index 6011985..71bc19d 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/OperatorTotalRequest.cs @@ -2,6 +2,7 @@ { using System; using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; [ExcludeFromCodeCoverage] public class OperatorTotalRequest @@ -14,6 +15,7 @@ public class OperatorTotalRequest /// /// The contract identifier. /// + [JsonProperty("contract_id")] public Guid ContractId { get; set; } /// @@ -22,6 +24,7 @@ public class OperatorTotalRequest /// /// The operator identifier. /// + [JsonProperty("operator_identifier")] public String OperatorIdentifier { get; set; } /// @@ -30,6 +33,7 @@ public class OperatorTotalRequest /// /// The transaction count. /// + [JsonProperty("transaction_count")] public Int32 TransactionCount { get; set; } /// @@ -38,6 +42,7 @@ public class OperatorTotalRequest /// /// The transaction value. /// + [JsonProperty("transaction_value")] public Decimal TransactionValue { get; set; } #endregion diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs index 184e186..2c833fa 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/ReconciliationRequestMessage.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; [ExcludeFromCodeCoverage] public class ReconciliationRequestMessage : TransactionRequestMessage @@ -13,6 +14,7 @@ public class ReconciliationRequestMessage : TransactionRequestMessage /// /// The operator totals. /// + [JsonProperty("operator_totals")] public List OperatorTotals { get; set; } /// @@ -21,6 +23,7 @@ public class ReconciliationRequestMessage : TransactionRequestMessage /// /// The transaction count. /// + [JsonProperty("transaction_count")] public Int32 TransactionCount { get; set; } /// @@ -29,6 +32,7 @@ public class ReconciliationRequestMessage : TransactionRequestMessage /// /// The transaction value. /// + [JsonProperty("transaction_value")] public Decimal TransactionValue { get; set; } } } \ No newline at end of file diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs index f6047e8..300e8c5 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs @@ -2,6 +2,7 @@ { using System; using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; /// /// @@ -11,30 +12,33 @@ public class SaleTransactionRequestMessage : TransactionRequestMessage { #region Properties - + /// /// Gets or sets the contract identifier. /// /// /// The contract identifier. /// + [JsonProperty("contract_id")] public Guid ContractId { get; set; } - + /// - /// Gets or sets the customer email address. + /// Gets or sets the operator identifier. /// /// - /// The customer email address. + /// The operator identifier. /// - public String CustomerEmailAddress { get; set; } + [JsonProperty("operator_identifier")] + public String OperatorIdentifier { get; set; } /// - /// Gets or sets the operator identifier. + /// Gets or sets the customer email address. /// /// - /// The operator identifier. + /// The customer email address. /// - public String OperatorIdentifier { get; set; } + [JsonProperty("customer_email_address")] + public String CustomerEmailAddress { get; set; } /// /// Gets or sets the product identifier. @@ -42,6 +46,7 @@ public class SaleTransactionRequestMessage : TransactionRequestMessage /// /// The product identifier. /// + [JsonProperty("product_id")] public Guid ProductId { get; set; } #endregion diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs index 861701a..81af4b2 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/TransactionRequestMessage.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; /// /// @@ -18,6 +19,7 @@ public class TransactionRequestMessage /// /// The application version. /// + [JsonProperty("application_version")] public String ApplicationVersion { get; set; } /// @@ -26,6 +28,7 @@ public class TransactionRequestMessage /// /// The device identifier. /// + [JsonProperty("device_identifier")] public String DeviceIdentifier { get; set; } /// @@ -34,6 +37,7 @@ public class TransactionRequestMessage /// /// The transaction date time. /// + [JsonProperty("transaction_date_time")] public DateTime TransactionDateTime { get; set; } /// @@ -42,6 +46,7 @@ public class TransactionRequestMessage /// /// The transaction number. /// + [JsonProperty("transaction_number")] public String TransactionNumber { get; set; } /// @@ -50,6 +55,7 @@ public class TransactionRequestMessage /// /// The additional request meta data. /// + [JsonProperty("additional_request_metadata")] public Dictionary AdditionalRequestMetaData { get; set; } #endregion diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs index 9c45dc6..5521f45 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs @@ -5,6 +5,7 @@ namespace TransactionProcessorACL.DataTransferObjects.Responses { using System.Diagnostics.CodeAnalysis; + using Newtonsoft.Json; [ExcludeFromCodeCoverage] public class TransactionResponseMessage @@ -15,6 +16,7 @@ public class TransactionResponseMessage /// /// The response code. /// + [JsonProperty("response_code")] public String ResponseCode { get; set; } /// @@ -23,6 +25,7 @@ public class TransactionResponseMessage /// /// The response message. /// + [JsonProperty("response_message")] public String ResponseMessage { get; set; } /// @@ -31,6 +34,7 @@ public class TransactionResponseMessage /// /// The additional response meta data. /// + [JsonProperty("additional_response_metadata")] public Dictionary AdditionalResponseMetaData { get; set; } /// @@ -39,6 +43,7 @@ public class TransactionResponseMessage /// /// The estate identifier. /// + [JsonProperty("estate_id")] public Guid EstateId { get; set; } /// @@ -47,6 +52,7 @@ public class TransactionResponseMessage /// /// The merchant identifier. /// + [JsonProperty("merchant_id")] public Guid MerchantId { get; set; } /// @@ -55,6 +61,7 @@ public class TransactionResponseMessage /// /// true if [requires application update]; otherwise, false. /// + [JsonProperty("requires_application_update")] public Boolean RequiresApplicationUpdate { get; set; } } } diff --git a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs index 3f48dc0..27ebbbc 100644 --- a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs @@ -1,3 +1,557 @@ -#error Could not find a reference to SpecFlow in project 'TransactionProcessorACL.IntegrationTests'. -#error Please add the 'TechTalk.SpecFlow' package to the project and use MSBuild generation instead of using SpecFlowSingleFileGenerator. -#error For more information see https://specflow.org/documentation/Generate-Tests-from-MsBuild/ \ No newline at end of file +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (https://www.specflow.org/). +// SpecFlow Version:3.5.0.0 +// SpecFlow Generator Version:3.5.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +namespace TransactionProcessorACL.IntegrationTests.Reconciliation +{ + using TechTalk.SpecFlow; + using System; + using System.Linq; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.5.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [Xunit.TraitAttribute("Category", "base")] + [Xunit.TraitAttribute("Category", "shared")] + public partial class ReconciliationFeature : object, Xunit.IClassFixture, System.IDisposable + { + + private static TechTalk.SpecFlow.ITestRunner testRunner; + + private string[] _featureTags = new string[] { + "base", + "shared"}; + + private Xunit.Abstractions.ITestOutputHelper _testOutputHelper; + +#line 1 "Reconciliation.feature" +#line hidden + + public ReconciliationFeature(ReconciliationFeature.FixtureData fixtureData, TransactionProcessorACL_IntegrationTests_XUnitAssemblyFixture assemblyFixture, Xunit.Abstractions.ITestOutputHelper testOutputHelper) + { + this._testOutputHelper = testOutputHelper; + this.TestInitialize(); + } + + public static void FeatureSetup() + { + testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Reconciliation", "Reconciliation", null, ProgrammingLanguage.CSharp, new string[] { + "base", + "shared"}); + testRunner.OnFeatureStart(featureInfo); + } + + public static void FeatureTearDown() + { + testRunner.OnFeatureEnd(); + testRunner = null; + } + + public virtual void TestInitialize() + { + } + + public virtual void TestTearDown() + { + testRunner.OnScenarioEnd(); + } + + public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(_testOutputHelper); + } + + public virtual void ScenarioStart() + { + testRunner.OnScenarioStart(); + } + + public virtual void ScenarioCleanup() + { + testRunner.CollectScenarioErrors(); + } + + public virtual void FeatureBackground() + { +#line 4 +#line hidden + TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] { + "RoleName"}); + table15.AddRow(new string[] { + "Merchant"}); +#line 6 + testRunner.Given("the following security roles exist", ((string)(null)), table15, "Given "); +#line hidden + TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] { + "Name", + "DisplayName", + "Description"}); + table16.AddRow(new string[] { + "estateManagement", + "Estate Managememt REST Scope", + "A scope for Estate Managememt REST"}); + table16.AddRow(new string[] { + "transactionProcessor", + "Transaction Processor REST Scope", + "A scope for Transaction Processor REST"}); + table16.AddRow(new string[] { + "transactionProcessorACL", + "Transaction Processor ACL REST Scope", + "A scope for Transaction Processor ACL REST"}); +#line 10 + testRunner.Given("I create the following api scopes", ((string)(null)), table16, "Given "); +#line hidden + TechTalk.SpecFlow.Table table17 = new TechTalk.SpecFlow.Table(new string[] { + "ResourceName", + "DisplayName", + "Secret", + "Scopes", + "UserClaims"}); + table17.AddRow(new string[] { + "estateManagement", + "Estate Managememt REST", + "Secret1", + "estateManagement", + "merchantId, estateId, role"}); + table17.AddRow(new string[] { + "transactionProcessor", + "Transaction Processor REST", + "Secret1", + "transactionProcessor", + ""}); + table17.AddRow(new string[] { + "transactionProcessorACL", + "Transaction Processor ACL REST", + "Secret1", + "transactionProcessorACL", + "merchantId, estateId, role"}); +#line 16 + testRunner.Given("the following api resources exist", ((string)(null)), table17, "Given "); +#line hidden + TechTalk.SpecFlow.Table table18 = new TechTalk.SpecFlow.Table(new string[] { + "ClientId", + "ClientName", + "Secret", + "AllowedScopes", + "AllowedGrantTypes"}); + table18.AddRow(new string[] { + "serviceClient", + "Service Client", + "Secret1", + "estateManagement,transactionProcessor,transactionProcessorACL", + "client_credentials"}); + table18.AddRow(new string[] { + "merchantClient", + "Merchant Client", + "Secret1", + "transactionProcessorACL", + "password"}); +#line 22 + testRunner.Given("the following clients exist", ((string)(null)), table18, "Given "); +#line hidden + TechTalk.SpecFlow.Table table19 = new TechTalk.SpecFlow.Table(new string[] { + "ClientId"}); + table19.AddRow(new string[] { + "serviceClient"}); +#line 27 + testRunner.Given("I have a token to access the estate management and transaction processor acl reso" + + "urces", ((string)(null)), table19, "Given "); +#line hidden + TechTalk.SpecFlow.Table table20 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName"}); + table20.AddRow(new string[] { + "Test Estate 1"}); + table20.AddRow(new string[] { + "Test Estate 2"}); +#line 31 + testRunner.Given("I have created the following estates", ((string)(null)), table20, "Given "); +#line hidden + TechTalk.SpecFlow.Table table21 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "OperatorName", + "RequireCustomMerchantNumber", + "RequireCustomTerminalNumber"}); + table21.AddRow(new string[] { + "Test Estate 1", + "Safaricom", + "True", + "True"}); + table21.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "True", + "True"}); +#line 36 + testRunner.Given("I have created the following operators", ((string)(null)), table21, "Given "); +#line hidden + TechTalk.SpecFlow.Table table22 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "OperatorName", + "ContractDescription"}); + table22.AddRow(new string[] { + "Test Estate 1", + "Safaricom", + "Safaricom Contract"}); + table22.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "Safaricom Contract"}); +#line 41 + testRunner.Given("I create a contract with the following values", ((string)(null)), table22, "Given "); +#line hidden + TechTalk.SpecFlow.Table table23 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "OperatorName", + "ContractDescription", + "ProductName", + "DisplayText", + "Value"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Safaricom", + "Safaricom Contract", + "Variable Topup", + "Custom", + ""}); + table23.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "Safaricom Contract", + "Variable Topup", + "Custom", + ""}); +#line 46 + testRunner.When("I create the following Products", ((string)(null)), table23, "When "); +#line hidden + TechTalk.SpecFlow.Table table24 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "OperatorName", + "ContractDescription", + "ProductName", + "CalculationType", + "FeeDescription", + "Value"}); + table24.AddRow(new string[] { + "Test Estate 1", + "Safaricom", + "Safaricom Contract", + "Variable Topup", + "Fixed", + "Merchant Commission", + "2.50"}); + table24.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "Safaricom Contract", + "Variable Topup", + "Percentage", + "Merchant Commission", + "0.85"}); +#line 51 + testRunner.When("I add the following Transaction Fees", ((string)(null)), table24, "When "); +#line hidden + TechTalk.SpecFlow.Table table25 = new TechTalk.SpecFlow.Table(new string[] { + "MerchantName", + "AddressLine1", + "Town", + "Region", + "Country", + "ContactName", + "EmailAddress", + "EstateName"}); + table25.AddRow(new string[] { + "Test Merchant 1", + "Address Line 1", + "TestTown", + "Test Region", + "United Kingdom", + "Test Contact 1", + "testcontact1@merchant1.co.uk", + "Test Estate 1"}); + table25.AddRow(new string[] { + "Test Merchant 2", + "Address Line 1", + "TestTown", + "Test Region", + "United Kingdom", + "Test Contact 2", + "testcontact2@merchant2.co.uk", + "Test Estate 1"}); + table25.AddRow(new string[] { + "Test Merchant 3", + "Address Line 1", + "TestTown", + "Test Region", + "United Kingdom", + "Test Contact 3", + "testcontact3@merchant2.co.uk", + "Test Estate 2"}); +#line 56 + testRunner.Given("I create the following merchants", ((string)(null)), table25, "Given "); +#line hidden + TechTalk.SpecFlow.Table table26 = new TechTalk.SpecFlow.Table(new string[] { + "OperatorName", + "MerchantName", + "MerchantNumber", + "TerminalNumber", + "EstateName"}); + table26.AddRow(new string[] { + "Safaricom", + "Test Merchant 1", + "00000001", + "10000001", + "Test Estate 1"}); + table26.AddRow(new string[] { + "Safaricom", + "Test Merchant 2", + "00000002", + "10000002", + "Test Estate 1"}); + table26.AddRow(new string[] { + "Safaricom", + "Test Merchant 3", + "00000003", + "10000003", + "Test Estate 2"}); +#line 62 + testRunner.Given("I have assigned the following operator to the merchants", ((string)(null)), table26, "Given "); +#line hidden + TechTalk.SpecFlow.Table table27 = new TechTalk.SpecFlow.Table(new string[] { + "DeviceIdentifier", + "MerchantName", + "EstateName"}); + table27.AddRow(new string[] { + "123456780", + "Test Merchant 1", + "Test Estate 1"}); + table27.AddRow(new string[] { + "123456781", + "Test Merchant 2", + "Test Estate 1"}); + table27.AddRow(new string[] { + "123456782", + "Test Merchant 3", + "Test Estate 2"}); +#line 68 + testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table27, "Given "); +#line hidden + TechTalk.SpecFlow.Table table28 = new TechTalk.SpecFlow.Table(new string[] { + "Reference", + "Amount", + "DateTime", + "MerchantName", + "EstateName"}); + table28.AddRow(new string[] { + "Deposit1", + "200.00", + "Today", + "Test Merchant 1", + "Test Estate 1"}); + table28.AddRow(new string[] { + "Deposit1", + "100.00", + "Today", + "Test Merchant 2", + "Test Estate 1"}); + table28.AddRow(new string[] { + "Deposit1", + "100.00", + "Today", + "Test Merchant 3", + "Test Estate 2"}); +#line 74 + testRunner.Given("I make the following manual merchant deposits", ((string)(null)), table28, "Given "); +#line hidden + TechTalk.SpecFlow.Table table29 = new TechTalk.SpecFlow.Table(new string[] { + "EmailAddress", + "Password", + "GivenName", + "FamilyName", + "EstateName", + "MerchantName"}); + table29.AddRow(new string[] { + "merchantuser@testmerchant1.co.uk", + "123456", + "TestMerchant", + "User1", + "Test Estate 1", + "Test Merchant 1"}); + table29.AddRow(new string[] { + "merchantuser@testmerchant2.co.uk", + "123456", + "TestMerchant", + "User2", + "Test Estate 1", + "Test Merchant 2"}); + table29.AddRow(new string[] { + "merchantuser@testmerchant3.co.uk", + "123456", + "TestMerchant", + "User3", + "Test Estate 2", + "Test Merchant 3"}); +#line 80 + testRunner.Given("I have created the following security users", ((string)(null)), table29, "Given "); +#line hidden + } + + void System.IDisposable.Dispose() + { + this.TestTearDown(); + } + + [Xunit.SkippableFactAttribute(DisplayName="Reconciliation Transaction")] + [Xunit.TraitAttribute("FeatureTitle", "Reconciliation")] + [Xunit.TraitAttribute("Description", "Reconciliation Transaction")] + [Xunit.TraitAttribute("Category", "PRTest")] + public virtual void ReconciliationTransaction() + { + string[] tagsOfScenario = new string[] { + "PRTest"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Reconciliation Transaction", null, tagsOfScenario, argumentsOfScenario); +#line 87 +this.ScenarioInitialize(scenarioInfo); +#line hidden + bool isScenarioIgnored = default(bool); + bool isFeatureIgnored = default(bool); + if ((tagsOfScenario != null)) + { + isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); + } + if ((this._featureTags != null)) + { + isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); + } + if ((isScenarioIgnored || isFeatureIgnored)) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 4 +this.FeatureBackground(); +#line hidden +#line 88 + testRunner.Given("I am logged in as \"merchantuser@testmerchant1.co.uk\" with password \"123456\" for M" + + "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + + "\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table30 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "TransactionCount", + "TransactionValue"}); + table30.AddRow(new string[] { + "Today", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "1", + "100.00"}); +#line 89 + testRunner.When("I perform the following reconciliations", ((string)(null)), table30, "When "); +#line hidden +#line 93 + testRunner.Given("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)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table31 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "TransactionCount", + "TransactionValue"}); + table31.AddRow(new string[] { + "Today", + "Test Merchant 2", + "123456781", + "Test Estate 1", + "2", + "200.00"}); +#line 94 + testRunner.When("I perform the following reconciliations", ((string)(null)), table31, "When "); +#line hidden +#line 98 + testRunner.Given("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)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table32 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "TransactionCount", + "TransactionValue"}); + table32.AddRow(new string[] { + "Today", + "Test Merchant 3", + "123456782", + "Test Estate 2", + "3", + "300.00"}); +#line 99 + testRunner.When("I perform the following reconciliations", ((string)(null)), table32, "When "); +#line hidden + TechTalk.SpecFlow.Table table33 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "MerchantName", + "ResponseCode", + "ResponseMessage"}); + table33.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "0000", + "SUCCESS"}); + table33.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 2", + "0000", + "SUCCESS"}); + table33.AddRow(new string[] { + "Test Estate 2", + "Test Merchant 3", + "0000", + "SUCCESS"}); +#line 103 + testRunner.Then("reconciliation response should contain the following information", ((string)(null)), table33, "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.5.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FixtureData : System.IDisposable + { + + public FixtureData() + { + ReconciliationFeature.FeatureSetup(); + } + + void System.IDisposable.Dispose() + { + ReconciliationFeature.FeatureTearDown(); + } + } + } +} +#pragma warning restore +#endregion diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs index 1c79f9c..d75330e 100644 --- a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs @@ -126,7 +126,7 @@ public virtual void FeatureBackground() "Estate Managememt REST", "Secret1", "estateManagement", - "MerchantId, EstateId, role"}); + "merchantId, estateId, role"}); table36.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", @@ -138,7 +138,7 @@ public virtual void FeatureBackground() "Transaction Processor ACL REST", "Secret1", "transactionProcessorACL", - "MerchantId, EstateId, role"}); + "merchantId, estateId, role"}); table36.AddRow(new string[] { "voucherManagement", "Voucher Management REST", diff --git a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs index 0050494..6d498e2 100644 --- a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs @@ -955,7 +955,6 @@ private async Task PerformLogonTransaction(String merchantToken, DeviceIdentifier = deviceIdentifier, TransactionDateTime = transactionDateTime, TransactionNumber = transactionNumber, - RequireConfigurationInResponse = true, ApplicationVersion = SharedSteps.ApplicationVersion }; diff --git a/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj b/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj index b6a3749..a140f4e 100644 --- a/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj +++ b/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj @@ -22,7 +22,7 @@ - + all diff --git a/TransactionProcessorACL.Testing/TestData.cs b/TransactionProcessorACL.Testing/TestData.cs index 4a97c7d..783b8be 100644 --- a/TransactionProcessorACL.Testing/TestData.cs +++ b/TransactionProcessorACL.Testing/TestData.cs @@ -25,12 +25,7 @@ public class TestData /// The device identifier /// public static String DeviceIdentifier = "12345678"; - - /// - /// The require configuration in response true - /// - public static Boolean RequireConfigurationInResponseTrue = true; - + /// /// The transaction date time /// @@ -46,7 +41,6 @@ public class TestData /// public static LogonTransactionRequestMessage LogonTransactionRequestMessage = new LogonTransactionRequestMessage { - RequireConfigurationInResponse = TestData.RequireConfigurationInResponseTrue, DeviceIdentifier = TestData.DeviceIdentifier, TransactionDateTime = TestData.TransactionDateTime, TransactionNumber = TestData.TransactionNumber @@ -65,8 +59,7 @@ public class TestData TestData.MerchantId, TestData.TransactionDateTime, TestData.TransactionNumber, - TestData.DeviceIdentifier, - TestData.RequireConfigurationInResponseTrue); + TestData.DeviceIdentifier); public static String ResponseCode = "0000"; public static String InvalidOperationErrorResponseCode = "0001"; diff --git a/TransactionProcessorACL/Common/ConfigureSwaggerOptions.cs b/TransactionProcessorACL/Common/ConfigureSwaggerOptions.cs deleted file mode 100644 index ec3faca..0000000 --- a/TransactionProcessorACL/Common/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace TransactionProcessorACL.Common -{ - using System.Diagnostics.CodeAnalysis; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Options; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Configures the Swagger generation options. - /// - /// This allows API versioning to define a Swagger document per API version after the - /// service has been resolved from the service container. - [ExcludeFromCodeCoverage] - public class ConfigureSwaggerOptions : IConfigureOptions - { - #region Fields - - /// - /// The provider - /// - private readonly IApiVersionDescriptionProvider provider; - - #endregion - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - #endregion - - #region Methods - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (ApiVersionDescription description in this.provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, ConfigureSwaggerOptions.CreateInfoForApiVersion(description)); - } - } - - /// - /// Creates the information for API version. - /// - /// The description. - /// - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - OpenApiInfo info = new OpenApiInfo - { - Title = "Golf Handicapping API", - Version = description.ApiVersion.ToString(), - Description = "A REST Api to manage the golf club handicapping system.", - Contact = new OpenApiContact - { - Name = "Stuart Ferguson", - Email = "golfhandicapping@btinternet.com" - }, - License = new OpenApiLicense - { - Name = "TODO" - } - }; - - if (description.IsDeprecated) - { - info.Description += " This API version has been deprecated."; - } - - return info; - } - - #endregion - } -} diff --git a/TransactionProcessorACL/Common/Examples/ExampleData.cs b/TransactionProcessorACL/Common/Examples/ExampleData.cs new file mode 100644 index 0000000..d1baa72 --- /dev/null +++ b/TransactionProcessorACL/Common/Examples/ExampleData.cs @@ -0,0 +1,85 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace TransactionProcessorACL.Common.Examples +{ + using Microsoft.Extensions.Primitives; + using TransactionProcessor.DataTransferObjects; + + /// + /// + /// + internal static class ExampleData + { + /// + /// The transaction count + /// + internal static Int32 TransactionCount = 1; + /// + /// The transaction value + /// + internal static Decimal TransactionValue = 10.00m; + + /// + /// The application version + /// + internal static String ApplicationVersion = "1.0.0"; + + /// + /// The device identifier + /// + internal static String DeviceIdentifier = "exampledevice1"; + + /// + /// The transaction date time + /// + internal static DateTime TransactionDateTime = new DateTime(2021, 4, 9, 11, 3, 0); + + /// + /// The transaction number + /// + internal static String TransactionNumber = "0001"; + /// + /// The contract identifier + /// + internal static Guid ContractId = Guid.Parse("F969AAAB-A669-4EC2-A060-62082DD19096"); + + /// + /// The customer email address + /// + internal static String CustomerEmailAddress = "exampleemail@customerdomain.co.uk"; + + /// + /// The operator identifier + /// + internal static String OperatorIdentifier = "Safaricom"; + /// + /// The product identifier + /// + internal static Guid ProductId = Guid.Parse("743846D5-2FDC-47F7-8525-5D366BC5F67E"); + + /// + /// The estate identifier + /// + internal static Guid EstateId = Guid.Parse("D0BF0236-4709-4262-BD2C-3C8AF16189C9"); + /// + /// The merchant identifier + /// + internal static Guid MerchantId = Guid.Parse("D0BF0236-4709-4262-BD2C-3C8AF16189C9"); + + /// + /// The response code + /// + internal static String ResponseCode = "0000"; + /// + /// The response message + /// + internal static String ResponseMessage = "SUCCESS"; + + /// + /// The requires application update + /// + internal static Boolean RequiresApplicationUpdate = false; + } +} diff --git a/TransactionProcessorACL/Common/Examples/TransactionRequestMessageExample.cs b/TransactionProcessorACL/Common/Examples/TransactionRequestMessageExample.cs new file mode 100644 index 0000000..10c08cd --- /dev/null +++ b/TransactionProcessorACL/Common/Examples/TransactionRequestMessageExample.cs @@ -0,0 +1,81 @@ +namespace TransactionProcessorACL.Common.Examples +{ + using System; + using System.Collections.Generic; + using DataTransferObjects; + using Swashbuckle.AspNetCore.Filters; + + /// + /// + /// + /// + public class TransactionRequestMessageExample : IMultipleExamplesProvider + { + #region Methods + + /// + /// Gets the examples. + /// + /// + public IEnumerable> GetExamples() + { + return new List> + { + new SwaggerExample + { + Name = "Logon Transaction", + Value = new LogonTransactionRequestMessage + { + AdditionalRequestMetaData = new Dictionary(), + ApplicationVersion = ExampleData.ApplicationVersion, + DeviceIdentifier = ExampleData.DeviceIdentifier, + TransactionDateTime = ExampleData.TransactionDateTime, + TransactionNumber = ExampleData.TransactionNumber + } + }, + new SwaggerExample + { + Name = "Sale Transaction", + Value = new SaleTransactionRequestMessage + { + AdditionalRequestMetaData = new Dictionary(), + ApplicationVersion = ExampleData.ApplicationVersion, + DeviceIdentifier = ExampleData.DeviceIdentifier, + TransactionDateTime = ExampleData.TransactionDateTime, + TransactionNumber = ExampleData.TransactionNumber, + ContractId = ExampleData.ContractId, + CustomerEmailAddress = ExampleData.CustomerEmailAddress, + OperatorIdentifier = ExampleData.OperatorIdentifier, + ProductId = ExampleData.ProductId + } + }, + new SwaggerExample + { + Name = "Reconciliation Transaction", + Value = new ReconciliationRequestMessage + { + AdditionalRequestMetaData = new Dictionary(), + ApplicationVersion = ExampleData.ApplicationVersion, + DeviceIdentifier = ExampleData.DeviceIdentifier, + TransactionDateTime = ExampleData.TransactionDateTime, + TransactionNumber = ExampleData.TransactionNumber, + OperatorTotals = new List + { + new OperatorTotalRequest + { + ContractId = ExampleData.ContractId, + OperatorIdentifier = ExampleData.OperatorIdentifier, + TransactionCount = ExampleData.TransactionCount, + TransactionValue = ExampleData.TransactionValue + } + }, + TransactionCount = ExampleData.TransactionCount, + TransactionValue = ExampleData.TransactionValue + } + } + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/TransactionProcessorACL/Common/Examples/TransactionResponseMessageExample.cs b/TransactionProcessorACL/Common/Examples/TransactionResponseMessageExample.cs new file mode 100644 index 0000000..25c45c1 --- /dev/null +++ b/TransactionProcessorACL/Common/Examples/TransactionResponseMessageExample.cs @@ -0,0 +1,68 @@ +namespace TransactionProcessorACL.Common.Examples +{ + using System; + using System.Collections.Generic; + using DataTransferObjects.Responses; + using Swashbuckle.AspNetCore.Filters; + + /// + /// + /// + /// + public class TransactionResponseMessageExample : IMultipleExamplesProvider + { + #region Methods + + /// + /// Gets the examples. + /// + /// + public IEnumerable> GetExamples() + { + return new List> + { + new SwaggerExample + { + Name = "Logon Transaction", + Value = new LogonTransactionResponseMessage + { + AdditionalResponseMetaData = new Dictionary(), + EstateId = ExampleData.EstateId, + MerchantId = ExampleData.MerchantId, + RequiresApplicationUpdate = ExampleData.RequiresApplicationUpdate, + ResponseCode = ExampleData.ResponseCode, + ResponseMessage = ExampleData.ResponseMessage + } + }, + new SwaggerExample + { + Name = "Sale Transaction", + Value = new SaleTransactionResponseMessage + { + AdditionalResponseMetaData = new Dictionary(), + EstateId = ExampleData.EstateId, + MerchantId = ExampleData.MerchantId, + RequiresApplicationUpdate = ExampleData.RequiresApplicationUpdate, + ResponseCode = ExampleData.ResponseCode, + ResponseMessage = ExampleData.ResponseMessage + } + }, + new SwaggerExample + { + Name = "Reconciliation Transaction", + Value = new ReconciliationResponseMessage + { + AdditionalResponseMetaData = new Dictionary(), + EstateId = ExampleData.EstateId, + MerchantId = ExampleData.MerchantId, + RequiresApplicationUpdate = ExampleData.RequiresApplicationUpdate, + ResponseCode = ExampleData.ResponseCode, + ResponseMessage = ExampleData.ResponseMessage + } + }, + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/TransactionProcessorACL/Common/SwaggerDefaultValues.cs b/TransactionProcessorACL/Common/SwaggerDefaultValues.cs index 9c57981..d0ba2e4 100644 --- a/TransactionProcessorACL/Common/SwaggerDefaultValues.cs +++ b/TransactionProcessorACL/Common/SwaggerDefaultValues.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; @@ -26,10 +25,6 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) { ApiDescription apiDescription = context.ApiDescription; - ApiVersion apiVersion = apiDescription.GetApiVersion(); - ApiVersionModel model = apiDescription.ActionDescriptor.GetApiVersionModel(ApiVersionMapping.Explicit | ApiVersionMapping.Implicit); - - operation.Deprecated = model.DeprecatedApiVersions.Contains(apiVersion); if (operation.Parameters == null) { diff --git a/TransactionProcessorACL/Controllers/TransactionController.cs b/TransactionProcessorACL/Controllers/TransactionController.cs index 4e559b7..aafdd80 100644 --- a/TransactionProcessorACL/Controllers/TransactionController.cs +++ b/TransactionProcessorACL/Controllers/TransactionController.cs @@ -7,12 +7,16 @@ using BusinessLogic.RequestHandlers; using BusinessLogic.Requests; using Common; + using Common.Examples; using DataTransferObjects; + using DataTransferObjects.Responses; using Factories; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Shared.Logger; + using Swashbuckle.AspNetCore.Annotations; + using Swashbuckle.AspNetCore.Filters; /// /// @@ -21,7 +25,6 @@ [ExcludeFromCodeCoverage] [Route(TransactionController.ControllerRoute)] [ApiController] - [ApiVersion("1.0")] [Authorize] public class TransactionController : ControllerBase { @@ -65,6 +68,10 @@ public TransactionController(IMediator mediator, /// [HttpPost] [Route("")] + [SwaggerRequestExample(typeof(TransactionRequestMessage), typeof(TransactionRequestMessageExample))] + [SwaggerResponse(200,"OK", typeof(TransactionResponseMessage))] + [SwaggerResponseExample(200, typeof(TransactionResponseMessageExample))] + public async Task PerformTransaction([FromBody] TransactionRequestMessage transactionRequest, CancellationToken cancellationToken) { @@ -108,8 +115,7 @@ private ProcessLogonTransactionRequest CreateCommandFromRequest(LogonTransaction merchantId, logonTransactionRequestMessage.TransactionDateTime, logonTransactionRequestMessage.TransactionNumber, - logonTransactionRequestMessage.DeviceIdentifier, - logonTransactionRequestMessage.RequireConfigurationInResponse); + logonTransactionRequestMessage.DeviceIdentifier); return request; } diff --git a/TransactionProcessorACL/Startup.cs b/TransactionProcessorACL/Startup.cs index 3766ded..ecaa4eb 100644 --- a/TransactionProcessorACL/Startup.cs +++ b/TransactionProcessorACL/Startup.cs @@ -20,15 +20,17 @@ namespace TransactionProcessorACL using BusinessLogic.Requests; using BusinessLogic.Services; using Common; + using DataTransferObjects; + using DataTransferObjects.Responses; using Factories; using HealthChecks.UI.Client; using MediatR; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Diagnostics.HealthChecks; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.AspNetCore.Mvc.Versioning; + using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Options; + using Microsoft.OpenApi.Models; using Models; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -104,32 +106,29 @@ private void ConfigureMiddlewareServices(IServiceCollection services) failureStatus: HealthStatus.Unhealthy, tags: new string[] { "application", "transactionprocessing" }); - services.AddApiVersioning( - options => + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Transaction Processor ACL", + Version = "1.0", + Description = "A REST Api to provide and Anti Corruption Layer for the Transaction Mobile Application", + Contact = new OpenApiContact + { + Name = "Stuart Ferguson", + Email = "golfhandicapping@btinternet.com" + } + }); + c.UseAllOfForInheritance(); + c.SelectSubTypesUsing(baseType => { - // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions" - options.ReportApiVersions = true; - options.DefaultApiVersion = new ApiVersion(1, 0); - options.AssumeDefaultVersionWhenUnspecified = true; - options.ApiVersionReader = new HeaderApiVersionReader("api-version"); + return typeof(TransactionRequestMessage).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType)); }); - services.AddVersionedApiExplorer( - options => - { - // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service - // note: the specified format code will format the version as "'v'major[.minor][-status]" - options.GroupNameFormat = "'v'VVV"; - - // note: this option is only necessary when versioning by url segment. the SubstitutionFormat - // can also be used to control the format of the API version in route templates - options.SubstituteApiVersionInUrl = true; - }); - - services.AddTransient, ConfigureSwaggerOptions>(); - - services.AddSwaggerGen(c => - { + c.SelectSubTypesUsing(baseType => + { + return typeof(TransactionResponseMessage).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType)); + }); // add a custom operation filter which sets default values c.OperationFilter(); c.ExampleFilters(); @@ -162,7 +161,7 @@ private void ConfigureMiddlewareServices(IServiceCollection services) services.AddControllers().AddNewtonsoftJson(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - options.SerializerSettings.TypeNameHandling = TypeNameHandling.All; + options.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto; options.SerializerSettings.Formatting = Formatting.Indented; options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); @@ -173,8 +172,7 @@ private void ConfigureMiddlewareServices(IServiceCollection services) } // 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, - IApiVersionDescriptionProvider provider) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { String nlogConfigFilename = "nlog.config"; @@ -212,15 +210,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF app.UseSwagger(); - app.UseSwaggerUI( - options => - { - // build a swagger endpoint for each discovered API version - foreach (ApiVersionDescription description in provider.ApiVersionDescriptions) - { - options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); - } - }); + app.UseSwaggerUI(); } } } diff --git a/TransactionProcessorACL/TransactionProcessorACL.csproj b/TransactionProcessorACL/TransactionProcessorACL.csproj index 15b34ee..666c6ec 100644 --- a/TransactionProcessorACL/TransactionProcessorACL.csproj +++ b/TransactionProcessorACL/TransactionProcessorACL.csproj @@ -11,20 +11,18 @@ - - - - - - - - + + + + + + From 9d954329b4f3969e804a0a72fd7f05d21ded0e3f Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Sun, 11 Apr 2021 08:11:46 +0100 Subject: [PATCH 2/3] Fix failing unit tests --- ...ionProcessorACL.BusinessLogic.Tests.csproj | 2 +- ...tionProcessorACLApplicationServiceTests.cs | 6 +- TransactionProcessorACL.Testing/TestData.cs | 55 ++++++++++++++++++- .../TransactionProcessorACL.Testing.csproj | 4 ++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACL.BusinessLogic.Tests.csproj b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACL.BusinessLogic.Tests.csproj index 0cf1868..582c69e 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACL.BusinessLogic.Tests.csproj +++ b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACL.BusinessLogic.Tests.csproj @@ -2,7 +2,7 @@ net5.0 - None + Full false diff --git a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs index 0402a1b..f2bce8c 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs +++ b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs @@ -40,7 +40,7 @@ public async Task TransactionProcessorACLApplicationService_ProcessLogonTransact { Mock transactionProcessorClient = new Mock(); transactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponse); + .ReturnsAsync(TestData.SerialisedMessageResponseLogon); Mock securityServiceClient = new Mock(); securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); @@ -136,7 +136,7 @@ public async Task TransactionProcessorACLApplicationService_ProcessSaleTransacti { Mock transactionProcessorClient = new Mock(); transactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponse); + .ReturnsAsync(TestData.SerialisedMessageResponseSale); Mock securityServiceClient = new Mock(); securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); @@ -252,7 +252,7 @@ public async Task TransactionProcessorACLApplicationService_ProcessReconciliatio { Mock transactionProcessorClient = new Mock(); transactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponse); + .ReturnsAsync(TestData.SerialisedMessageResponseReconciliation); Mock securityServiceClient = new Mock(); securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); diff --git a/TransactionProcessorACL.Testing/TestData.cs b/TransactionProcessorACL.Testing/TestData.cs index 783b8be..d5f5d78 100644 --- a/TransactionProcessorACL.Testing/TestData.cs +++ b/TransactionProcessorACL.Testing/TestData.cs @@ -79,14 +79,23 @@ public class TestData MerchantId = TestData.MerchantId }; - public static SerialisedMessage SerialisedMessageResponse = new SerialisedMessage + public static LogonTransactionResponse ClientLogonTransactionResponse = new LogonTransactionResponse + { + ResponseMessage = TestData.ResponseMessage, + ResponseCode = TestData.ResponseCode, + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + + public static SerialisedMessage SerialisedMessageResponseLogon = new SerialisedMessage { Metadata = new Dictionary { {"EstateId", TestData.EstateId.ToString()}, {"MerchantId", TestData.MerchantId.ToString()}, }, - SerialisedData = JsonConvert.SerializeObject(TestData.ProcessLogonTransactionResponse, new JsonSerializerSettings + SerialisedData = JsonConvert.SerializeObject(TestData.ClientLogonTransactionResponse, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }) @@ -106,6 +115,27 @@ public class TestData MerchantId = TestData.MerchantId }; + public static SaleTransactionResponse ClientSaleTransactionResponse = new SaleTransactionResponse + { + ResponseMessage = TestData.ResponseMessage, + ResponseCode = TestData.ResponseCode, + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + public static SerialisedMessage SerialisedMessageResponseSale = new SerialisedMessage + { + Metadata = new Dictionary + { + {"EstateId", TestData.EstateId.ToString()}, + {"MerchantId", TestData.MerchantId.ToString()}, + }, + SerialisedData = JsonConvert.SerializeObject(TestData.ClientSaleTransactionResponse, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }) + }; + public static String OperatorIdentifier = "Operator1"; public static Decimal SaleAmount = 1000.00m; @@ -154,6 +184,27 @@ public class TestData MerchantId = TestData.MerchantId }; + public static TransactionProcessor.DataTransferObjects.ReconciliationResponse ClientReconciliationResponse = new ReconciliationResponse + { + ResponseMessage = TestData.ResponseMessage, + ResponseCode = TestData.ResponseCode, + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + public static SerialisedMessage SerialisedMessageResponseReconciliation = new SerialisedMessage + { + Metadata = new Dictionary + { + {"EstateId", TestData.EstateId.ToString()}, + {"MerchantId", TestData.MerchantId.ToString()}, + }, + SerialisedData = JsonConvert.SerializeObject(TestData.ClientReconciliationResponse, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }) + }; + public static IReadOnlyDictionary DefaultAppSettings => new Dictionary { diff --git a/TransactionProcessorACL.Testing/TransactionProcessorACL.Testing.csproj b/TransactionProcessorACL.Testing/TransactionProcessorACL.Testing.csproj index 123acec..6b72850 100644 --- a/TransactionProcessorACL.Testing/TransactionProcessorACL.Testing.csproj +++ b/TransactionProcessorACL.Testing/TransactionProcessorACL.Testing.csproj @@ -5,6 +5,10 @@ None + + + + From 4d3496d6175a0ad568df15a76f6e9e8ff61885d7 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 12 Apr 2021 15:22:57 +0100 Subject: [PATCH 3/3] Fix specflow issues --- .../LogonTransaction/LogonTransaction.feature | 2 +- .../LogonTransaction.feature.cs | 2 +- .../Reconciliation/Reconciliation.feature | 2 +- .../Reconciliation/Reconciliation.feature.cs | 2 +- .../SaleTransaction/SalesTransaction.feature | 2 +- .../SalesTransaction.feature.cs | 2 +- .../Shared/SharedSteps.cs | 56 +++++++++++-------- 7 files changed, 38 insertions(+), 30 deletions(-) diff --git a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature index 9f8280e..b05db1b 100644 --- a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature +++ b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature @@ -73,7 +73,7 @@ Scenario: Logon Transaction | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | | Today | 3 | Logon | Test Merchant 3 | 123456789 | Test Estate 2 | - Then transaction response should contain the following information + Then the logon transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | TransactionType | ResponseCode | ResponseMessage | | Test Estate 1 | Test Merchant 1 | 1 | Logon | 0000 | SUCCESS | | Test Estate 1 | Test Merchant 2 | 2 | Logon | 0000 | SUCCESS | diff --git a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs index 072ef77..132024e 100644 --- a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs @@ -426,7 +426,7 @@ public virtual void LogonTransaction() "0000", "SUCCESS"}); #line 76 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table14, "Then "); + testRunner.Then("the logon transaction response should contain the following information", ((string)(null)), table14, "Then "); #line hidden } this.ScenarioCleanup(); diff --git a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature index 25d6579..84d71d7 100644 --- a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature +++ b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature @@ -100,7 +100,7 @@ Scenario: Reconciliation Transaction | DateTime | MerchantName | DeviceIdentifier | EstateName | TransactionCount | TransactionValue | | Today | Test Merchant 3 | 123456782 | Test Estate 2 | 3 | 300.00 | - Then reconciliation response should contain the following information + Then the reconciliation response should contain the following information | EstateName | MerchantName | ResponseCode | ResponseMessage | | Test Estate 1 | Test Merchant 1 | 0000 | SUCCESS | | Test Estate 1 | Test Merchant 2 | 0000 | SUCCESS | diff --git a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs index 27ebbbc..b9a27e9 100644 --- a/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/Reconciliation/Reconciliation.feature.cs @@ -530,7 +530,7 @@ public virtual void ReconciliationTransaction() "0000", "SUCCESS"}); #line 103 - testRunner.Then("reconciliation response should contain the following information", ((string)(null)), table33, "Then "); + testRunner.Then("the reconciliation response should contain the following information", ((string)(null)), table33, "Then "); #line hidden } this.ScenarioCleanup(); diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature index 1f2d738..81fe8ae 100644 --- a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature @@ -115,7 +115,7 @@ Scenario: Sale Transaction | Today | 3 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | Today | 7 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | - Then transaction response should contain the following information + Then the sale transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | TransactionType | ResponseCode | ResponseMessage | | Test Estate 1 | Test Merchant 1 | 1 | Sale | 0000 | SUCCESS | | Test Estate 1 | Test Merchant 2 | 2 | Sale | 0000 | SUCCESS | diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs index d75330e..ca340fc 100644 --- a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs @@ -734,7 +734,7 @@ public virtual void SaleTransaction() "0000", "SUCCESS"}); #line 118 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table52, "Then "); + testRunner.Then("the sale transaction response should contain the following information", ((string)(null)), table52, "Then "); #line hidden } this.ScenarioCleanup(); diff --git a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs index 6d498e2..e9ed4c1 100644 --- a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs @@ -374,7 +374,7 @@ public async Task GivenTheFollowingSecurityRolesExist(Table table) /// Thens the reconciliation response should contain the following information. /// /// The table. - [Then(@"reconciliation response should contain the following information")] + [Then(@"the reconciliation response should contain the following information")] public void ThenReconciliationResponseShouldContainTheFollowingInformation(Table table) { foreach (TableRow tableRow in table.Rows) @@ -386,11 +386,7 @@ public void ThenReconciliationResponseShouldContainTheFollowingInformation(Table Guid merchantId = estateDetails.GetMerchantId(merchantName); String responseMessage = estateDetails.GetReconciliationResponse(merchantId); - Object transactionResponse = JsonConvert.DeserializeObject(responseMessage, - new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); + ReconciliationResponseMessage transactionResponse = JsonConvert.DeserializeObject(responseMessage); this.ValidateTransactionResponse((dynamic)transactionResponse, tableRow); } } @@ -399,30 +395,45 @@ public void ThenReconciliationResponseShouldContainTheFollowingInformation(Table /// Thens the transaction response should contain the following information. /// /// The table. - [Then(@"transaction response should contain the following information")] - public void ThenTransactionResponseShouldContainTheFollowingInformation(Table table) + [Then(@"the logon transaction response should contain the following information")] + public void ThenTheLogonTransactionResponseShouldContainTheFollowingInformation(Table table) { foreach (TableRow tableRow in table.Rows) { - // Get the merchant name - EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); + String responseMessage = this.GetResponseMessage(tableRow); - String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName"); - Guid merchantId = estateDetails.GetMerchantId(merchantName); + LogonTransactionResponseMessage transactionResponse = JsonConvert.DeserializeObject(responseMessage); + this.ValidateTransactionResponse((dynamic)transactionResponse, tableRow); + } + } - String transactionNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionNumber"); - String transactionType = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionType"); - String responseMessage = estateDetails.GetTransactionResponse(merchantId, transactionNumber, transactionType); + [Then(@"the sale transaction response should contain the following information")] + public void ThenTheSaleTransactionResponseShouldContainTheFollowingInformation(Table table) + { + foreach (TableRow tableRow in table.Rows) + { + String responseMessage = this.GetResponseMessage(tableRow); - Object transactionResponse = JsonConvert.DeserializeObject(responseMessage, - new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); + SaleTransactionResponseMessage transactionResponse = JsonConvert.DeserializeObject(responseMessage); this.ValidateTransactionResponse((dynamic)transactionResponse, tableRow); } } + private String GetResponseMessage(TableRow tableRow ) + { + // Get the merchant name + EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); + + String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName"); + Guid merchantId = estateDetails.GetMerchantId(merchantName); + + String transactionNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionNumber"); + String transactionType = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionType"); + String responseMessage = estateDetails.GetTransactionResponse(merchantId, transactionNumber, transactionType); + + return responseMessage; + } + /// /// Whens the i add the following transaction fees. /// @@ -552,10 +563,7 @@ public async Task WhenICreateTheFollowingEstates(Table table) this.TestingContext.AddEstateDetails(response.EstateId, estateName); this.TestingContext.Logger.LogInformation($"Estate {estateName} created with Id {response.EstateId}"); - } - - foreach (TableRow tableRow in table.Rows) - { + EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); EstateResponse estate = null;