diff --git a/.github/workflows/createrelease.yml b/.github/workflows/createrelease.yml index 8335d125..cf3449f1 100644 --- a/.github/workflows/createrelease.yml +++ b/.github/workflows/createrelease.yml @@ -19,10 +19,10 @@ jobs: id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - name: Setup .NET Core 3.0 + - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.0.100 + dotnet-version: 3.1.100 - name: Restore Nuget Packages run: dotnet restore TransactionProcessor.sln --source https://api.nuget.org/v3/index.json --source https://www.myget.org/F/transactionprocessing/api/v3/index.json diff --git a/.github/workflows/nightlybuild.yml b/.github/workflows/nightlybuild.yml index 125f1da2..3d45db3e 100644 --- a/.github/workflows/nightlybuild.yml +++ b/.github/workflows/nightlybuild.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.0.100 + dotnet-version: 3.1.100 - name: Restore Nuget Packages run: dotnet restore TransactionProcessor.sln --source https://api.nuget.org/v3/index.json --source https://www.myget.org/F/transactionprocessing/api/v3/index.json diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 667c7626..c334550d 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.0.100 + dotnet-version: 3.1.100 - name: Restore Nuget Packages run: dotnet restore TransactionProcessor.sln --source https://api.nuget.org/v3/index.json --source https://www.myget.org/F/transactionprocessing/api/v3/index.json diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index b668c3fe..6444896b 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -7,10 +7,16 @@ namespace TransactionProcessor.BusinessLogic.Tests.Services using System.Threading; using System.Threading.Tasks; using BusinessLogic.Services; + using EstateManagement.Client; + using Microsoft.Extensions.Configuration; using Models; using Moq; + using SecurityService.Client; + using SecurityService.DataTransferObjects.Responses; using Shared.DomainDrivenDesign.EventStore; using Shared.EventStore.EventStore; + using Shared.General; + using Shared.Logger; using Shouldly; using Testing; using TransactionAggregate; @@ -21,6 +27,11 @@ public class TransactionDomainServiceTests [Fact] public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIsProcessed() { + var configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + Mock aggregateRepositoryManager = new Mock(); Mock> transactionAggregateRepository = new Mock>(); @@ -32,15 +43,23 @@ public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIs .ReturnsAsync(TestData.GetCompletedTransactionAggregate); transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object); + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponse); + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object); + ProcessLogonTransactionResponse response = await transactionDomainService.ProcessLogonTransaction(TestData.TransactionId, - TestData.EstateId, - TestData.MerchantId, - TestData.TransactionDateTime, - TestData.TransactionNumber, - TestData.DeviceIdentifier, - CancellationToken.None); + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + CancellationToken.None); response.ShouldNotBeNull(); response.ResponseCode.ShouldBe(TestData.ResponseCode); diff --git a/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj b/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj index 004bf540..d88ee50a 100644 --- a/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj +++ b/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj @@ -1,13 +1,13 @@  - netcoreapp3.0 + netcoreapp3.1 None false - + @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index 45b84af1..a661a375 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -1,11 +1,20 @@ namespace TransactionProcessor.BusinessLogic.Services { using System; + using System.Collections.Generic; + using System.Linq; using System.Threading; using System.Threading.Tasks; + using EstateManagement.Client; + using EstateManagement.DataTransferObjects.Requests; + using EstateManagement.DataTransferObjects.Responses; using Models; + using SecurityService.Client; + using SecurityService.DataTransferObjects.Responses; using Shared.DomainDrivenDesign.EventStore; using Shared.EventStore.EventStore; + using Shared.General; + using Shared.Logger; using TransactionAggregate; /// @@ -14,13 +23,44 @@ /// public class TransactionDomainService : ITransactionDomainService { + #region Fields + + /// + /// The aggregate repository manager + /// private readonly IAggregateRepositoryManager AggregateRepositoryManager; - public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryManager) + /// + /// The estate client + /// + private readonly IEstateClient EstateClient; + + /// + /// The security service client + /// + private readonly ISecurityServiceClient SecurityServiceClient; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The aggregate repository manager. + /// The estate client. + /// The security service client. + public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryManager, + IEstateClient estateClient, + ISecurityServiceClient securityServiceClient) { this.AggregateRepositoryManager = aggregateRepositoryManager; + this.EstateClient = estateClient; + this.SecurityServiceClient = securityServiceClient; } + #endregion + #region Methods /// @@ -34,18 +74,36 @@ public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryM /// The device identifier. /// The cancellation token. /// - public async Task ProcessLogonTransaction(Guid transactionId, Guid estateId, Guid merchantId, DateTime transactionDateTime, - String transactionNumber, String deviceIdentifier, CancellationToken cancellationToken) + public async Task ProcessLogonTransaction(Guid transactionId, + Guid estateId, + Guid merchantId, + DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + CancellationToken cancellationToken) { - IAggregateRepository transactionAggregateRepository = this.AggregateRepositoryManager.GetAggregateRepository(estateId); - + IAggregateRepository transactionAggregateRepository = + this.AggregateRepositoryManager.GetAggregateRepository(estateId); + TransactionAggregate transactionAggregate = await transactionAggregateRepository.GetLatestVersion(transactionId, cancellationToken); transactionAggregate.StartTransaction(transactionDateTime, transactionNumber, "Logon", estateId, merchantId, deviceIdentifier); await transactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken); - transactionAggregate = await transactionAggregateRepository.GetLatestVersion(transactionId, cancellationToken); - transactionAggregate.AuthoriseTransactionLocally("ABCD1234", "0000", "SUCCESS"); - await transactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken); + (String responseMessage, TransactionResponseCode responseCode) validationResult = await this.ValidateTransaction(estateId, merchantId, deviceIdentifier, cancellationToken); + + if (validationResult.responseCode == TransactionResponseCode.Success) + { + // Record the successful validation + transactionAggregate = await transactionAggregateRepository.GetLatestVersion(transactionId, cancellationToken); + // TODO: Generate local authcode + transactionAggregate.AuthoriseTransactionLocally("ABCD1234", ((Int32)validationResult.responseCode).ToString().PadLeft(4,'0'), validationResult.responseMessage); + await transactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken); + } + else + { + // Record the failure + throw new NotImplementedException(); + } transactionAggregate = await transactionAggregateRepository.GetLatestVersion(transactionId, cancellationToken); transactionAggregate.CompleteTransaction(); @@ -60,7 +118,103 @@ public async Task ProcessLogonTransaction(Guid }; } + /// + /// Validates the transaction. + /// + /// The estate identifier. + /// The merchant identifier. + /// The device identifier. + /// The cancellation token. + /// + /// Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName} + private async Task<(String responseMessage, TransactionResponseCode responseCode)> ValidateTransaction(Guid estateId, + Guid merchantId, + String deviceIdentifier, + CancellationToken cancellationToken) + { + try + { + + // Get a token to talk to the estate service + String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); + String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); + + Logger.LogInformation($"Client Id is {clientId}"); + Logger.LogInformation($"Client Secret is {clientSecret}"); + + TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + Logger.LogInformation($"Token is {token.AccessToken}"); + + // get the merchant record and validate the device + // TODO: Token + MerchantResponse merchant = await this.EstateClient.GetMerchant(token.AccessToken, estateId, merchantId, cancellationToken); + + if (merchant.Devices == null || merchant.Devices.Any() == false) + { + // Add the device to the merchant + await this.EstateClient.AddDeviceToMerchant(token.AccessToken, + estateId, + merchantId, + new AddMerchantDeviceRequest + { + DeviceIdentifier = deviceIdentifier + }, + cancellationToken); + } + else + { + // Validate the device + KeyValuePair device = merchant.Devices.SingleOrDefault(d => d.Value == deviceIdentifier); + + if (device.Key == Guid.Empty) + { + // Device not found,throw error + throw new TransactionValidationException($"Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName}", TransactionResponseCode.InvalidDeviceIdentifier); + } + } + + // If we get here everything is good + return ("SUCCESS", TransactionResponseCode.Success); + } + catch (TransactionValidationException tvex) + { + return (tvex.Message, tvex.ResponseCode); + } + + } #endregion } + + public enum TransactionResponseCode + { + Success = 0, + InvalidDeviceIdentifier = 1000 + } + + public class TransactionValidationException : Exception + { + public TransactionResponseCode ResponseCode { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + /// The response code. + public TransactionValidationException(String message, TransactionResponseCode responseCode) : this(message, responseCode, null) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The response code. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public TransactionValidationException(String message, TransactionResponseCode responseCode, Exception innerException) : base(message, innerException) + { + this.ResponseCode = responseCode; + } + } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj index 75cfea3d..b309e542 100644 --- a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj +++ b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj @@ -1,14 +1,16 @@  - netcoreapp3.0 + netcoreapp3.1 - - - - + + + + + + diff --git a/TransactionProcessor.Client/TransactionProcessor.Client.csproj b/TransactionProcessor.Client/TransactionProcessor.Client.csproj index 67d991b8..08cd2d8f 100644 --- a/TransactionProcessor.Client/TransactionProcessor.Client.csproj +++ b/TransactionProcessor.Client/TransactionProcessor.Client.csproj @@ -6,7 +6,7 @@ - + diff --git a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs index af1c6ee3..09bb2efc 100644 --- a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs +++ b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs @@ -4,24 +4,86 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; - using System.Threading; using System.Threading.Tasks; using Client; - using Ductus.FluentDocker.Builders; using Ductus.FluentDocker.Common; - using Ductus.FluentDocker.Executors; - using Ductus.FluentDocker.Extensions; - using Ductus.FluentDocker.Model.Builders; using Ductus.FluentDocker.Services; using Ductus.FluentDocker.Services.Extensions; using EstateManagement.Client; using global::Shared.Logger; using SecurityService.Client; + /// + /// + /// + /// public class DockerHelper : global::Shared.IntegrationTesting.DockerHelper { + #region Fields + + /// + /// The estate client + /// + public IEstateClient EstateClient; + + /// + /// The security service client + /// + public ISecurityServiceClient SecurityServiceClient; + + /// + /// The test identifier + /// + public Guid TestId; + + /// + /// The transaction processor client + /// + public ITransactionProcessorClient TransactionProcessorClient; + + /// + /// The containers + /// + protected List Containers; + + /// + /// The estate management API port + /// + protected Int32 EstateManagementApiPort; + + /// + /// The event store HTTP port + /// + protected Int32 EventStoreHttpPort; + + /// + /// The security service port + /// + protected Int32 SecurityServicePort; + + /// + /// The test networks + /// + protected List TestNetworks; + + /// + /// The transaction processor port + /// + protected Int32 TransactionProcessorPort; + + /// + /// The logger + /// private readonly NlogLogger Logger; + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The logger. public DockerHelper(NlogLogger logger) { this.Logger = logger; @@ -29,12 +91,14 @@ public DockerHelper(NlogLogger logger) this.TestNetworks = new List(); } - public Guid TestId; - - protected List Containers; + #endregion - protected List TestNetworks; + #region Methods + /// + /// Starts the containers for scenario run. + /// + /// Name of the scenario. public override async Task StartContainersForScenarioRun(String scenarioName) { String traceFolder = FdOs.IsWindows() ? $"D:\\home\\txnproc\\trace\\{scenarioName}" : $"//home//txnproc//trace//{scenarioName}"; @@ -59,7 +123,6 @@ public override async Task StartContainersForScenarioRun(String scenarioName) IContainerService eventStoreContainer = DockerHelper.SetupEventStoreContainer(eventStoreContainerName, this.Logger, "eventstore/eventstore:release-5.0.2", testNetwork, traceFolder); - IContainerService estateManagementContainer = DockerHelper.SetupEstateManagementContainer(estateManagementApiContainerName, this.Logger, "stuartferguson/estatemanagement", @@ -70,14 +133,17 @@ public override async Task StartContainersForScenarioRun(String scenarioName) traceFolder, dockerCredentials, securityServiceContainerName, - eventStoreContainerName); + eventStoreContainerName, + (null, null), + true); IContainerService securityServiceContainer = DockerHelper.SetupSecurityServiceContainer(securityServiceContainerName, this.Logger, "stuartferguson/securityservice", testNetwork, traceFolder, - dockerCredentials); + dockerCredentials, + true); IContainerService transactionProcessorContainer = DockerHelper.SetupTransactionProcessorContainer(transactionProcessorContainerName, this.Logger, @@ -89,8 +155,9 @@ public override async Task StartContainersForScenarioRun(String scenarioName) traceFolder, dockerCredentials, securityServiceContainerName, - eventStoreContainerName); - + estateManagementApiContainerName, + eventStoreContainerName, + ("serviceClient", "Secret1")); this.Containers.AddRange(new List { @@ -117,20 +184,9 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.TransactionProcessorClient = new TransactionProcessorClient(TransactionProcessorBaseAddressResolver, httpClient); } - public IEstateClient EstateClient; - - public ITransactionProcessorClient TransactionProcessorClient; - - public ISecurityServiceClient SecurityServiceClient; - - protected Int32 EstateManagementApiPort; - - protected Int32 SecurityServicePort; - - protected Int32 EventStoreHttpPort; - - protected Int32 TransactionProcessorPort; - + /// + /// Stops the containers for scenario run. + /// public override async Task StopContainersForScenarioRun() { if (this.Containers.Any()) @@ -152,5 +208,7 @@ public override async Task StopContainersForScenarioRun() } } } + + #endregion } -} +} \ No newline at end of file diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature index b245bc76..a2110800 100644 --- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature +++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature @@ -37,14 +37,44 @@ Background: @PRTest Scenario: Logon Transaction + When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | MerchantName | IMEINumber | EstateName | - | Today | 1 | Logon | Test Merchant 1 | 123456789 | Test Estate 1 | - | Today | 2 | Logon | Test Merchant 2 | 123456789 | Test Estate 1 | - | Today | 3 | Logon | Test Merchant 3 | 123456789 | Test Estate 2 | + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | + | Today | 1 | Logon | Test Merchant 1 | 123456780 | Test Estate 1 | + | Today | 2 | Logon | Test Merchant 2 | 123456781 | Test Estate 1 | + | Today | 3 | Logon | Test Merchant 3 | 123456782 | Test Estate 2 | Then transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS | | Test Estate 1 | Test Merchant 2 | 2 | 0000 | SUCCESS | | Test Estate 2 | Test Merchant 3 | 3 | 0000 | SUCCESS | + +Scenario: Logon Transaction with Existing Device + + Given I have assigned the following devices to the merchants + | DeviceIdentifier | MerchantName | MerchantNumber | EstateName | + | 123456780 | Test Merchant 1 | 00000001 | Test Estate 1 | + + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | + | Today | 1 | Logon | Test Merchant 1 | 123456780 | Test Estate 1 | + + Then transaction response should contain the following information + | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | + | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS | + +@ignore +Scenario: Logon Transaction with Invalid Device + + Given I have assigned the following devices to the merchants + | DeviceIdentifier | MerchantName | MerchantNumber | EstateName | + | 123456780 | Test Merchant 1 | 00000001 | Test Estate 1 | + + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | + | Today | 1 | Logon | Test Merchant 1 | 123456781 | Test Estate 1 | + + Then transaction response should contain the following information + | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | + | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS | diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs index b98b05f0..a35c2133 100644 --- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs +++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs @@ -250,30 +250,30 @@ public virtual void LogonTransaction() "TransactionNumber", "TransactionType", "MerchantName", - "IMEINumber", + "DeviceIdentifier", "EstateName"}); table8.AddRow(new string[] { "Today", "1", "Logon", "Test Merchant 1", - "123456789", + "123456780", "Test Estate 1"}); table8.AddRow(new string[] { "Today", "2", "Logon", "Test Merchant 2", - "123456789", + "123456781", "Test Estate 1"}); table8.AddRow(new string[] { "Today", "3", "Logon", "Test Merchant 3", - "123456789", + "123456782", "Test Estate 2"}); -#line 40 +#line 41 testRunner.When("I perform the following transactions", ((string)(null)), table8, "When "); #line hidden TechTalk.SpecFlow.Table table9 = new TechTalk.SpecFlow.Table(new string[] { @@ -300,13 +300,171 @@ public virtual void LogonTransaction() "3", "0000", "SUCCESS"}); -#line 46 +#line 47 testRunner.Then("transaction response should contain the following information", ((string)(null)), table9, "Then "); #line hidden } this.ScenarioCleanup(); } + [Xunit.SkippableFactAttribute(DisplayName="Logon Transaction with Existing Device")] + [Xunit.TraitAttribute("FeatureTitle", "LogonTransaction")] + [Xunit.TraitAttribute("Description", "Logon Transaction with Existing Device")] + public virtual void LogonTransactionWithExistingDevice() + { + string[] tagsOfScenario = ((string[])(null)); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Logon Transaction with Existing Device", null, ((string[])(null))); +#line 53 +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 + TechTalk.SpecFlow.Table table10 = new TechTalk.SpecFlow.Table(new string[] { + "DeviceIdentifier", + "MerchantName", + "MerchantNumber", + "EstateName"}); + table10.AddRow(new string[] { + "123456780", + "Test Merchant 1", + "00000001", + "Test Estate 1"}); +#line 55 + testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table10, "Given "); +#line hidden + TechTalk.SpecFlow.Table table11 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName"}); + table11.AddRow(new string[] { + "Today", + "1", + "Logon", + "Test Merchant 1", + "123456780", + "Test Estate 1"}); +#line 59 + testRunner.When("I perform the following transactions", ((string)(null)), table11, "When "); +#line hidden + TechTalk.SpecFlow.Table table12 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "MerchantName", + "TransactionNumber", + "ResponseCode", + "ResponseMessage"}); + table12.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "1", + "0000", + "SUCCESS"}); +#line 63 + testRunner.Then("transaction response should contain the following information", ((string)(null)), table12, "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + + [Xunit.SkippableFactAttribute(DisplayName="Logon Transaction with Invalid Device")] + [Xunit.TraitAttribute("FeatureTitle", "LogonTransaction")] + [Xunit.TraitAttribute("Description", "Logon Transaction with Invalid Device")] + public virtual void LogonTransactionWithInvalidDevice() + { + string[] tagsOfScenario = ((string[])(null)); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Logon Transaction with Invalid Device", null, ((string[])(null))); +#line 67 +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 + TechTalk.SpecFlow.Table table13 = new TechTalk.SpecFlow.Table(new string[] { + "DeviceIdentifier", + "MerchantName", + "MerchantNumber", + "EstateName"}); + table13.AddRow(new string[] { + "123456780", + "Test Merchant 1", + "00000001", + "Test Estate 1"}); +#line 69 + testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table13, "Given "); +#line hidden + TechTalk.SpecFlow.Table table14 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName"}); + table14.AddRow(new string[] { + "Today", + "1", + "Logon", + "Test Merchant 1", + "123456781", + "Test Estate 1"}); +#line 73 + testRunner.When("I perform the following transactions", ((string)(null)), table14, "When "); +#line hidden + TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "MerchantName", + "TransactionNumber", + "ResponseCode", + "ResponseMessage"}); + table15.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "1", + "0000", + "SUCCESS"}); +#line 77 + testRunner.Then("transaction response should contain the following information", ((string)(null)), table15, "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class FixtureData : System.IDisposable diff --git a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs index 91dd9b72..946c6361 100644 --- a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs @@ -221,7 +221,7 @@ public async Task WhenIPerformTheFollowingTransactions(Table table) DateTime transactionDateTime = SpecflowTableHelper.GetDateForDateString(dateString, DateTime.Today); String transactionNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionNumber"); String transactionType = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionType"); - String imeiNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "IMEINumber"); + String deviceIdentifier = SpecflowTableHelper.GetStringRowValue(tableRow, "DeviceIdentifier"); EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); @@ -236,7 +236,7 @@ public async Task WhenIPerformTheFollowingTransactions(Table table) transactionDateTime, transactionType, transactionNumber, - imeiNumber, + deviceIdentifier, CancellationToken.None); break; @@ -403,5 +403,39 @@ public async Task GivenIHaveATokenToAccessTheEstateManagementAndTransactionProce } } + [Given(@"I have assigned the following devices to the merchants")] + public async Task GivenIHaveAssignedTheFollowingDevicesToTheMerchants(Table table) + { + foreach (TableRow tableRow in table.Rows) + { + EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); + + String token = this.TestingContext.AccessToken; + if (String.IsNullOrEmpty(estateDetails.AccessToken) == false) + { + token = estateDetails.AccessToken; + } + + // Lookup the merchant id + String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName"); + Guid merchantId = estateDetails.GetMerchantId(merchantName); + + // Lookup the operator id + String deviceIdentifier = SpecflowTableHelper.GetStringRowValue(tableRow, "DeviceIdentifier"); + + AddMerchantDeviceRequest addMerchantDeviceRequest = new AddMerchantDeviceRequest + { + DeviceIdentifier = deviceIdentifier + }; + + AddMerchantDeviceResponse addMerchantDeviceResponse = await this.TestingContext.DockerHelper.EstateClient.AddDeviceToMerchant(token, estateDetails.EstateId, merchantId, addMerchantDeviceRequest, CancellationToken.None).ConfigureAwait(false); + + addMerchantDeviceResponse.EstateId.ShouldBe(estateDetails.EstateId); + addMerchantDeviceResponse.MerchantId.ShouldBe(merchantId); + addMerchantDeviceResponse.DeviceId.ShouldNotBe(Guid.Empty); + + this.TestingContext.Logger.LogInformation($"Device {deviceIdentifier} assigned to Merchant {merchantName} Estate {estateDetails.EstateName}"); + } + } } } diff --git a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj index 9489c20d..be1733a7 100644 --- a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj +++ b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj @@ -1,30 +1,30 @@  - netcoreapp3.0 + netcoreapp3.1 false - + - - - - - - + + + + + + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/TransactionProcessor.Models/TransactionProcessor.Models.csproj b/TransactionProcessor.Models/TransactionProcessor.Models.csproj index ea83d296..cb631906 100644 --- a/TransactionProcessor.Models/TransactionProcessor.Models.csproj +++ b/TransactionProcessor.Models/TransactionProcessor.Models.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 5b2e14d0..6ff6f0e9 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -5,7 +5,9 @@ namespace TransactionProcessor.Testing { using BusinessLogic.Requests; + using EstateManagement.DataTransferObjects.Responses; using Models; + using SecurityService.DataTransferObjects.Responses; using TransactionAggregate; public class TestData @@ -81,5 +83,28 @@ public static TransactionAggregate GetCompletedTransactionAggregate() return transactionAggregate; } + + public static IReadOnlyDictionary DefaultAppSettings { get; } = new Dictionary + { + ["AppSettings:ClientId"] = "clientId", + ["AppSettings:ClientSecret"] = "clientSecret" + }; + + public static TokenResponse TokenResponse() + { + return SecurityService.DataTransferObjects.Responses.TokenResponse.Create("AccessToken", String.Empty, 100); + } + + public static Guid DeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); + + public static MerchantResponse GetMerchantResponse = new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + Devices = new Dictionary + { + {TestData.DeviceId, TestData.DeviceIdentifier} + } + }; } } diff --git a/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj b/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj index 248202d0..b2e3a2e1 100644 --- a/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj +++ b/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 None diff --git a/TransactionProcessor.Tests/General/BootstrapperTests.cs b/TransactionProcessor.Tests/General/BootstrapperTests.cs index bd4cb7f1..a96b1fe4 100644 --- a/TransactionProcessor.Tests/General/BootstrapperTests.cs +++ b/TransactionProcessor.Tests/General/BootstrapperTests.cs @@ -59,6 +59,10 @@ private IConfigurationRoot SetupMemoryConfiguration() configuration.Add("EventStoreSettings:ConnectionName", "UnitTestConnection"); configuration.Add("EventStoreSettings:HttpPort", "2113"); configuration.Add("AppSettings:UseConnectionStringConfig", "false"); + configuration.Add("AppSettings:ClientId", "clientId"); + configuration.Add("AppSettings:ClientSecret", "clientSecret"); + configuration.Add("AppSettings:EstateManagementApi", "http://localhost"); + configuration.Add("AppSettings:SecurityService", "http://localhost"); builder.AddInMemoryCollection(configuration); diff --git a/TransactionProcessor.Tests/TransactionProcessor.Tests.csproj b/TransactionProcessor.Tests/TransactionProcessor.Tests.csproj index e6af1398..a9e45135 100644 --- a/TransactionProcessor.Tests/TransactionProcessor.Tests.csproj +++ b/TransactionProcessor.Tests/TransactionProcessor.Tests.csproj @@ -1,14 +1,14 @@  - netcoreapp3.0 + netcoreapp3.1 None false - + @@ -16,7 +16,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj b/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj index f2c5f8a2..a35ece09 100644 --- a/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj +++ b/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj @@ -5,7 +5,7 @@ - + diff --git a/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj b/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj index 2a87ed14..6e63abc0 100644 --- a/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj +++ b/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 None false @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj b/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj index c0def16c..905a57a9 100644 --- a/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj +++ b/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj @@ -1,11 +1,11 @@  - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/TransactionProcessor/Dockerfile b/TransactionProcessor/Dockerfile index 344b150d..2ff22ed4 100644 --- a/TransactionProcessor/Dockerfile +++ b/TransactionProcessor/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src COPY ["TransactionProcessor/NuGet.Config", "."] COPY ["TransactionProcessor/TransactionProcessor.csproj", "TransactionProcessor/"] diff --git a/TransactionProcessor/Startup.cs b/TransactionProcessor/Startup.cs index 73d67bab..b0b25024 100644 --- a/TransactionProcessor/Startup.cs +++ b/TransactionProcessor/Startup.cs @@ -14,12 +14,14 @@ namespace TransactionProcessor { using System.Diagnostics.CodeAnalysis; using System.IO; + using System.Net.Http; using System.Reflection; using Autofac; using BusinessLogic.RequestHandlers; using BusinessLogic.Requests; using BusinessLogic.Services; using Common; + using EstateManagement.Client; using EventStore.ClientAPI; using MediatR; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -30,6 +32,7 @@ namespace TransactionProcessor using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using NLog.Extensions.Logging; + using SecurityService.Client; using Shared.DomainDrivenDesign.CommandHandling; using Shared.DomainDrivenDesign.EventStore; using Shared.EntityFramework.ConnectionStringConfiguration; @@ -115,7 +118,14 @@ public void ConfigureContainer(ContainerBuilder builder) builder.RegisterType>().As>().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType(typeof(HttpClient)).SingleInstance(); + builder.Register>(c => (api) => + { + Uri uri = ConfigurationReader.GetBaseServerUri(api); + return uri.AbsoluteUri.Substring(0, uri.AbsoluteUri.Length - 1); + }); // request & notification handlers builder.Register(context => { diff --git a/TransactionProcessor/TransactionProcessor.csproj b/TransactionProcessor/TransactionProcessor.csproj index 551f68c5..f3ab431c 100644 --- a/TransactionProcessor/TransactionProcessor.csproj +++ b/TransactionProcessor/TransactionProcessor.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 Linux @@ -10,23 +10,23 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + - - - - - - - + + + + + + + diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index 257cac75..8543a25c 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -7,20 +7,23 @@ } }, "EventStoreSettings": { - "ConnectionString": "ConnectTo=tcp://admin:changeit@192.168.1.132:1113;VerboseLogging=true;", + "ConnectionString": "ConnectTo=tcp://admin:changeit@192.168.1.133:1113;VerboseLogging=true;", "ConnectionName": "Estate Management", "HttpPort": 2113, "START_PROJECTIONS": false, "ContinuousProjectionsFolder": "" }, "ConnectionStrings": { - "ConnectionStringConfiguration": "server=localhost;database=ConnectionStringConfiguration;user id=sa;password=sp1ttal" + //"ConnectionStringConfiguration": "server=192.168.1.133;database=ConnectionStringConfiguration;user id=sa;password=Sc0tland" }, "AppSettings": { "HandlerEventTypesToSilentlyHandle": { }, "UseConnectionStringConfig": false, - "SecurityService": "http://192.168.1.133:5001" + "SecurityService": "http://192.168.1.133:5001", + "EstateManagementApi": "http://192.168.1.133:5000", + "ClientId": "serviceClient", + "ClientSecret": "d192cbc46d834d0da90e8a9d50ded543" }, "SecurityConfiguration": { "ApiName": "transactionProcessor",