Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/createrelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightlybuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<IAggregateRepositoryManager> aggregateRepositoryManager = new Mock<IAggregateRepositoryManager>();
Mock<IAggregateRepository<TransactionAggregate>> transactionAggregateRepository = new Mock<IAggregateRepository<TransactionAggregate>>();

Expand All @@ -32,15 +43,23 @@ public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIs
.ReturnsAsync(TestData.GetCompletedTransactionAggregate);
transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny<TransactionAggregate>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);

TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object);
Mock<IEstateClient> estateClient = new Mock<IEstateClient>();
Mock<ISecurityServiceClient> securityServiceClient = new Mock<ISecurityServiceClient>();

securityServiceClient.Setup(s => s.GetToken(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<CancellationToken>())).ReturnsAsync(TestData.TokenResponse);
estateClient.Setup(e => e.GetMerchant(It.IsAny<String>(), It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<DebugType>None</DebugType>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="2.7.0">
<PackageReference Include="coverlet.msbuild" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
Expand All @@ -14,13 +23,44 @@
/// <seealso cref="TransactionProcessor.BusinessLogic.Services.ITransactionDomainService" />
public class TransactionDomainService : ITransactionDomainService
{
#region Fields

/// <summary>
/// The aggregate repository manager
/// </summary>
private readonly IAggregateRepositoryManager AggregateRepositoryManager;

public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryManager)
/// <summary>
/// The estate client
/// </summary>
private readonly IEstateClient EstateClient;

/// <summary>
/// The security service client
/// </summary>
private readonly ISecurityServiceClient SecurityServiceClient;

#endregion

#region Constructors

/// <summary>
/// Initializes a new instance of the <see cref="TransactionDomainService" /> class.
/// </summary>
/// <param name="aggregateRepositoryManager">The aggregate repository manager.</param>
/// <param name="estateClient">The estate client.</param>
/// <param name="securityServiceClient">The security service client.</param>
public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryManager,
IEstateClient estateClient,
ISecurityServiceClient securityServiceClient)
{
this.AggregateRepositoryManager = aggregateRepositoryManager;
this.EstateClient = estateClient;
this.SecurityServiceClient = securityServiceClient;
}

#endregion

#region Methods

/// <summary>
Expand All @@ -34,18 +74,36 @@ public TransactionDomainService(IAggregateRepositoryManager aggregateRepositoryM
/// <param name="deviceIdentifier">The device identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public async Task<ProcessLogonTransactionResponse> ProcessLogonTransaction(Guid transactionId, Guid estateId, Guid merchantId, DateTime transactionDateTime,
String transactionNumber, String deviceIdentifier, CancellationToken cancellationToken)
public async Task<ProcessLogonTransactionResponse> ProcessLogonTransaction(Guid transactionId,
Guid estateId,
Guid merchantId,
DateTime transactionDateTime,
String transactionNumber,
String deviceIdentifier,
CancellationToken cancellationToken)
{
IAggregateRepository<TransactionAggregate> transactionAggregateRepository = this.AggregateRepositoryManager.GetAggregateRepository<TransactionAggregate>(estateId);

IAggregateRepository<TransactionAggregate> transactionAggregateRepository =
this.AggregateRepositoryManager.GetAggregateRepository<TransactionAggregate>(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();
Expand All @@ -60,7 +118,103 @@ public async Task<ProcessLogonTransactionResponse> ProcessLogonTransaction(Guid
};
}

/// <summary>
/// Validates the transaction.
/// </summary>
/// <param name="estateId">The estate identifier.</param>
/// <param name="merchantId">The merchant identifier.</param>
/// <param name="deviceIdentifier">The device identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="TransactionProcessor.BusinessLogic.Services.TransactionValidationException">Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName}</exception>
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<Guid, String> 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; }

/// <summary>
/// Initializes a new instance of the <see cref="TransactionValidationException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="responseCode">The response code.</param>
public TransactionValidationException(String message, TransactionResponseCode responseCode) : this(message, responseCode, null)
{

}

/// <summary>
/// Initializes a new instance of the <see cref="TransactionValidationException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="responseCode">The response code.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
public TransactionValidationException(String message, TransactionResponseCode responseCode, Exception innerException) : base(message, innerException)
{
this.ResponseCode = responseCode;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Shared" Version="0.0.5.1" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="0.0.5.1" />
<PackageReference Include="Shared.EventStore" Version="0.0.5.1" />
<PackageReference Include="MediatR" Version="7.0.0" />
<PackageReference Include="EstateManagement.Client" Version="0.0.8" />
<PackageReference Include="SecurityService.Client" Version="0.0.6.2" />
<PackageReference Include="Shared" Version="0.0.8.1" />
<PackageReference Include="Shared.DomainDrivenDesign" Version="0.0.8.1" />
<PackageReference Include="Shared.EventStore" Version="0.0.8.1" />
<PackageReference Include="MediatR" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ClientProxyBase" Version="0.0.7" />
<PackageReference Include="ClientProxyBase" Version="0.0.8.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading