diff --git a/EstateManagement.BusinessLogic.Tests/EstateManagement.BusinessLogic.Tests.csproj b/EstateManagement.BusinessLogic.Tests/EstateManagement.BusinessLogic.Tests.csproj
index 001f7b58..ea3e458a 100644
--- a/EstateManagement.BusinessLogic.Tests/EstateManagement.BusinessLogic.Tests.csproj
+++ b/EstateManagement.BusinessLogic.Tests/EstateManagement.BusinessLogic.Tests.csproj
@@ -11,7 +11,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/EstateManagement.BusinessLogic.Tests/RequestHandler/MerchantRequestHandlerTests.cs b/EstateManagement.BusinessLogic.Tests/RequestHandler/MerchantRequestHandlerTests.cs
index b23d5d6b..313a2c84 100644
--- a/EstateManagement.BusinessLogic.Tests/RequestHandler/MerchantRequestHandlerTests.cs
+++ b/EstateManagement.BusinessLogic.Tests/RequestHandler/MerchantRequestHandlerTests.cs
@@ -41,5 +41,20 @@ public void MerchantRequestHandler_AssignOperatorToMerchantRequest_IsHandled()
});
}
+
+ [Fact]
+ public void MerchantRequestHandler_CreateMerchantUserRequest_IsHandled()
+ {
+ Mock merchantDomainService = new Mock();
+ MerchantRequestHandler handler = new MerchantRequestHandler(merchantDomainService.Object);
+
+ CreateMerchantUserRequest request = TestData.CreateMerchantUserRequest;
+
+ Should.NotThrow(async () =>
+ {
+ await handler.Handle(request, CancellationToken.None);
+ });
+
+ }
}
}
\ No newline at end of file
diff --git a/EstateManagement.BusinessLogic.Tests/Requests/RequestsTests.cs b/EstateManagement.BusinessLogic.Tests/Requests/RequestsTests.cs
index e2d879e7..3cd84ea4 100644
--- a/EstateManagement.BusinessLogic.Tests/Requests/RequestsTests.cs
+++ b/EstateManagement.BusinessLogic.Tests/Requests/RequestsTests.cs
@@ -103,5 +103,27 @@ public void CreateEstateUserRequest_CanBeCreated_IsCreated()
createEstateUserRequest.FamilyName.ShouldBe(TestData.EstateUserFamilyName);
}
+
+ [Fact]
+ public void CreateMerchantUserRequest_CanBeCreated_IsCreated()
+ {
+ CreateMerchantUserRequest createMerchantUserRequest = CreateMerchantUserRequest.Create(TestData.EstateId,
+ TestData.MerchantId,
+ TestData.EstateUserEmailAddress,
+ TestData.EstateUserPassword,
+ TestData.EstateUserGivenName,
+ TestData.EstateUserMiddleName,
+ TestData.EstateUserFamilyName);
+
+ createMerchantUserRequest.ShouldNotBeNull();
+ createMerchantUserRequest.EstateId.ShouldBe(TestData.EstateId);
+ createMerchantUserRequest.MerchantId.ShouldBe(TestData.MerchantId);
+ createMerchantUserRequest.EmailAddress.ShouldBe(TestData.EstateUserEmailAddress);
+ createMerchantUserRequest.Password.ShouldBe(TestData.EstateUserPassword);
+ createMerchantUserRequest.GivenName.ShouldBe(TestData.EstateUserGivenName);
+ createMerchantUserRequest.MiddleName.ShouldBe(TestData.EstateUserMiddleName);
+ createMerchantUserRequest.FamilyName.ShouldBe(TestData.EstateUserFamilyName);
+
+ }
}
}
diff --git a/EstateManagement.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs b/EstateManagement.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs
index 8c01805c..bc8c045c 100644
--- a/EstateManagement.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs
+++ b/EstateManagement.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs
@@ -10,6 +10,8 @@ namespace EstateManagement.BusinessLogic.Tests.Services
using EstateAggregate;
using MerchantAggregate;
using Moq;
+ using SecurityService.Client;
+ using SecurityService.DataTransferObjects;
using Shared.DomainDrivenDesign.EventStore;
using Shared.EventStore.EventStore;
using Shouldly;
@@ -32,7 +34,9 @@ public async Task MerchantDomainService_CreateMerchant_MerchantIsCreated()
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.NotThrow( async () =>
{
@@ -70,7 +74,10 @@ public void MerchantDomainService_CreateMerchant_EstateNotFound_ErrorThrown()
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new
+ MerchantDomainService(aggregateRepositoryManager.Object,securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -108,7 +115,9 @@ public async Task MerchantDomainService_AssignOperatorToMerchant_OperatorAssigne
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
await domainService.AssignOperatorToMerchant(TestData.EstateId,
TestData.MerchantId,
@@ -132,7 +141,9 @@ public void MerchantDomainService_AssignOperatorToMerchant_MerchantNotCreated_Er
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -159,7 +170,9 @@ public void MerchantDomainService_AssignOperatorToMerchant_EstateNotCreated_Erro
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -186,7 +199,9 @@ public void MerchantDomainService_AssignOperatorToMerchant_OperatorNotFoundForEs
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -215,7 +230,9 @@ public void MerchantDomainService_AssignOperatorToMerchant_OperatorRequiresMerch
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -244,7 +261,9 @@ public void MerchantDomainService_AssignOperatorToMerchant_OperatorRequiresTermi
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(estateAggregateRepository.Object);
aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
- MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object);
+ Mock securityServiceClient = new Mock();
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
Should.Throw(async () =>
{
@@ -256,5 +275,36 @@ await domainService.AssignOperatorToMerchant(TestData.EstateId,
CancellationToken.None);
});
}
+
+ [Fact]
+ public async Task MerchantDomainService_CreateMerchantUser_MerchantUserIsCreated()
+ {
+ Mock> merchantAggregateRepository = new Mock>();
+ merchantAggregateRepository.Setup(m => m.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.CreatedMerchantAggregate);
+ merchantAggregateRepository.Setup(m => m.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask);
+
+ Mock aggregateRepositoryManager = new Mock();
+ aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(merchantAggregateRepository.Object);
+
+ Mock securityServiceClient = new Mock();
+ securityServiceClient.Setup(s => s.CreateUser(It.IsAny(), It.IsAny())).ReturnsAsync(new CreateUserResponse
+ {
+ UserId = Guid.NewGuid()
+ });
+
+ MerchantDomainService domainService = new MerchantDomainService(aggregateRepositoryManager.Object, securityServiceClient.Object);
+
+ Should.NotThrow(async () =>
+ {
+ await domainService.CreateMerchantUser(TestData.EstateId,
+ TestData.MerchantId,
+ TestData.MerchantUserEmailAddress,
+ TestData.MerchantUserPassword,
+ TestData.MerchantUserGivenName,
+ TestData.MerchantUserMiddleName,
+ TestData.MerchantUserFamilyName,
+ CancellationToken.None);
+ });
+ }
}
}
diff --git a/EstateManagement.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs b/EstateManagement.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs
index 8e1e7f9b..d9b278a1 100644
--- a/EstateManagement.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs
+++ b/EstateManagement.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs
@@ -12,7 +12,9 @@
///
///
///
- public class MerchantRequestHandler : IRequestHandler, IRequestHandler
+ public class MerchantRequestHandler : IRequestHandler,
+ IRequestHandler,
+ IRequestHandler
{
#region Fields
@@ -88,5 +90,20 @@ await this.MerchantDomainService.AssignOperatorToMerchant(request.EstateId,
}
#endregion
+
+ public async Task Handle(CreateMerchantUserRequest request,
+ CancellationToken cancellationToken)
+ {
+ Guid userId = await this.MerchantDomainService.CreateMerchantUser(request.EstateId,
+ request.MerchantId,
+ request.EmailAddress,
+ request.Password,
+ request.GivenName,
+ request.MiddleName,
+ request.FamilyName,
+ cancellationToken);
+
+ return userId;
+ }
}
}
\ No newline at end of file
diff --git a/EstateManagement.BusinessLogic/Requests/CreateMerchantUserRequest.cs b/EstateManagement.BusinessLogic/Requests/CreateMerchantUserRequest.cs
new file mode 100644
index 00000000..7e8a7aa3
--- /dev/null
+++ b/EstateManagement.BusinessLogic/Requests/CreateMerchantUserRequest.cs
@@ -0,0 +1,125 @@
+namespace EstateManagement.BusinessLogic.Requests
+{
+ using System;
+ using MediatR;
+
+ public class CreateMerchantUserRequest : IRequest
+ {
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The estate identifier.
+ /// The merchant identifier.
+ /// The email address.
+ /// The password.
+ /// Name of the given.
+ /// Name of the middle.
+ /// Name of the family.
+ private CreateMerchantUserRequest(Guid estateId,
+ Guid merchantId,
+ String emailAddress,
+ String password,
+ String givenName,
+ String middleName,
+ String familyName)
+ {
+ this.EstateId = estateId;
+ this.MerchantId = merchantId;
+ this.EmailAddress = emailAddress;
+ this.Password = password;
+ this.GivenName = givenName;
+ this.MiddleName = middleName;
+ this.FamilyName = familyName;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the estate identifier.
+ ///
+ ///
+ /// The estate identifier.
+ ///
+ public Guid EstateId { get; }
+
+ ///
+ /// Gets the merchant identifier.
+ ///
+ ///
+ /// The merchant identifier.
+ ///
+ public Guid MerchantId { get; }
+
+ ///
+ /// Gets the email address.
+ ///
+ ///
+ /// The email address.
+ ///
+ public String EmailAddress { get; }
+
+ ///
+ /// Gets or sets the name of the family.
+ ///
+ ///
+ /// The name of the family.
+ ///
+ public String FamilyName { get; }
+
+ ///
+ /// Gets or sets the name of the given.
+ ///
+ ///
+ /// The name of the given.
+ ///
+ public String GivenName { get; }
+
+ ///
+ /// Gets or sets the name of the middle.
+ ///
+ ///
+ /// The name of the middle.
+ ///
+ public String MiddleName { get; }
+
+ ///
+ /// Gets or sets the password.
+ ///
+ ///
+ /// The password.
+ ///
+ public String Password { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Creates the specified email address.
+ ///
+ /// The estate identifier.
+ /// The merchant identifier.
+ /// The email address.
+ /// The password.
+ /// Name of the given.
+ /// Name of the middle.
+ /// Name of the family.
+ ///
+ public static CreateMerchantUserRequest Create(Guid estateId,
+ Guid merchantId,
+ String emailAddress,
+ String password,
+ String givenName,
+ String middleName,
+ String familyName)
+ {
+ return new CreateMerchantUserRequest(estateId, merchantId, emailAddress, password, givenName, middleName, familyName);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/EstateManagement.BusinessLogic/Services/EstateDomainService.cs b/EstateManagement.BusinessLogic/Services/EstateDomainService.cs
index 8ea07fab..ed541c58 100644
--- a/EstateManagement.BusinessLogic/Services/EstateDomainService.cs
+++ b/EstateManagement.BusinessLogic/Services/EstateDomainService.cs
@@ -90,6 +90,17 @@ public async Task AddOperatorToEstate(Guid estateId,
await estateAggregateRepository.SaveChanges(estateAggregate, cancellationToken);
}
+ ///
+ /// Creates the estate user.
+ ///
+ /// The estate identifier.
+ /// The email address.
+ /// The password.
+ /// Name of the given.
+ /// Name of the middle.
+ /// Name of the family.
+ /// The cancellation token.
+ ///
public async Task CreateEstateUser(Guid estateId,
String emailAddress,
String password,
@@ -118,6 +129,11 @@ public async Task CreateEstateUser(Guid estateId,
CreateUserResponse createUserResponse = await this.SecurityServiceClient.CreateUser(createUserRequest, cancellationToken);
+ // Add the user to the aggregate
+ estateAggregate.AddSecurityUser(createUserResponse.UserId, emailAddress);
+
+ // TODO: add a delete user here in case the aggregate add fails...
+
await estateAggregateRepository.SaveChanges(estateAggregate, cancellationToken);
return createUserResponse.UserId;
diff --git a/EstateManagement.BusinessLogic/Services/IMerchantDomainService.cs b/EstateManagement.BusinessLogic/Services/IMerchantDomainService.cs
index 9c5db4fb..1e5d2263 100644
--- a/EstateManagement.BusinessLogic/Services/IMerchantDomainService.cs
+++ b/EstateManagement.BusinessLogic/Services/IMerchantDomainService.cs
@@ -68,6 +68,27 @@ Task AssignOperatorToMerchant(Guid estateId,
String terminalNumber,
CancellationToken cancellationToken);
+ ///
+ /// Creates the merchant user.
+ ///
+ /// The estate identifier.
+ /// The merchant identifier.
+ /// The email address.
+ /// The password.
+ /// Name of the given.
+ /// Name of the middle.
+ /// Name of the family.
+ /// The cancellation token.
+ ///
+ Task CreateMerchantUser(Guid estateId,
+ Guid merchantId,
+ String emailAddress,
+ String password,
+ String givenName,
+ String middleName,
+ String familyName,
+ CancellationToken cancellationToken);
+
#endregion
}
}
\ No newline at end of file
diff --git a/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs b/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs
index ff85816b..114187fd 100644
--- a/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs
+++ b/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs
@@ -1,12 +1,15 @@
namespace EstateManagement.BusinessLogic.Services
{
using System;
+ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EstateAggregate;
using MerchantAggregate;
using Models;
+ using SecurityService.Client;
+ using SecurityService.DataTransferObjects;
using Shared.DomainDrivenDesign.EventStore;
using Shared.EventStore.EventStore;
@@ -23,6 +26,11 @@ public class MerchantDomainService : IMerchantDomainService
///
private readonly IAggregateRepositoryManager AggregateRepositoryManager;
+ ///
+ /// The security service client
+ ///
+ private readonly ISecurityServiceClient SecurityServiceClient;
+
#endregion
#region Constructors
@@ -31,9 +39,11 @@ public class MerchantDomainService : IMerchantDomainService
/// Initializes a new instance of the class.
///
/// The aggregate repository manager.
- public MerchantDomainService(IAggregateRepositoryManager aggregateRepositoryManager)
+ public MerchantDomainService(IAggregateRepositoryManager aggregateRepositoryManager,
+ ISecurityServiceClient securityServiceClient)
{
this.AggregateRepositoryManager = aggregateRepositoryManager;
+ this.SecurityServiceClient = securityServiceClient;
}
#endregion
@@ -163,7 +173,45 @@ public async Task AssignOperatorToMerchant(Guid estateId,
await merchantAggregateRepository.SaveChanges(merchantAggregate, cancellationToken);
}
+ public async Task CreateMerchantUser(Guid estateId,
+ Guid merchantId,
+ String emailAddress,
+ String password,
+ String givenName,
+ String middleName,
+ String familyName,
+ CancellationToken cancellationToken)
+ {
+ IAggregateRepository merchantAggregateRepository = this.AggregateRepositoryManager.GetAggregateRepository(estateId);
+ MerchantAggregate merchantAggregate = await merchantAggregateRepository.GetLatestVersion(merchantId, cancellationToken);
+
+ CreateUserRequest createUserRequest = new CreateUserRequest
+ {
+ EmailAddress = emailAddress,
+ FamilyName = familyName,
+ GivenName = givenName,
+ MiddleName = middleName,
+ Password = password,
+ PhoneNumber = "123456", // Is this really needed :|
+ Roles = new List(),
+ Claims = new Dictionary()
+ };
+
+ //createUserRequest.Roles.Add("Estate");
+ createUserRequest.Claims.Add("EstateId", estateId.ToString());
+ createUserRequest.Claims.Add("MerchantId", merchantId.ToString());
+
+ CreateUserResponse createUserResponse = await this.SecurityServiceClient.CreateUser(createUserRequest, cancellationToken);
+
+ // Add the user to the aggregate
+ merchantAggregate.AddSecurityUser(createUserResponse.UserId, emailAddress);
+ // TODO: add a delete user here in case the aggregate add fails...
+
+ await merchantAggregateRepository.SaveChanges(merchantAggregate, cancellationToken);
+
+ return createUserResponse.UserId;
+ }
#endregion
}
}
\ No newline at end of file
diff --git a/EstateManagement.Client/EstateClient.cs b/EstateManagement.Client/EstateClient.cs
index 2740362a..a2cd0ac2 100644
--- a/EstateManagement.Client/EstateClient.cs
+++ b/EstateManagement.Client/EstateClient.cs
@@ -13,6 +13,7 @@
///
///
///
+ ///
///
///
public class EstateClient : ClientProxyBase, IEstateClient
@@ -47,25 +48,27 @@ public EstateClient(Func baseAddressResolver,
#region Methods
///
- /// Adds the operator.
+ /// Assigns the operator to merchant.
///
/// The access token.
/// The estate identifier.
- /// The create operator request.
+ /// The merchant identifier.
+ /// The assign operator request.
/// The cancellation token.
///
- public async Task CreateOperator(String accessToken,
- Guid estateId,
- CreateOperatorRequest createOperatorRequest,
- CancellationToken cancellationToken)
+ public async Task AssignOperatorToMerchant(String accessToken,
+ Guid estateId,
+ Guid merchantId,
+ AssignOperatorRequest assignOperatorRequest,
+ CancellationToken cancellationToken)
{
- CreateOperatorResponse response = null;
+ AssignOperatorResponse response = null;
- String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/operators";
+ String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/merchants/{merchantId}/operators";
try
{
- String requestSerialised = JsonConvert.SerializeObject(createOperatorRequest);
+ String requestSerialised = JsonConvert.SerializeObject(assignOperatorRequest);
StringContent httpContent = new StringContent(requestSerialised, Encoding.UTF8, "application/json");
@@ -79,12 +82,13 @@ public async Task CreateOperator(String accessToken,
String content = await this.HandleResponse(httpResponse, cancellationToken);
// call was successful so now deserialise the body to the response object
- response = JsonConvert.DeserializeObject(content);
+ response = JsonConvert.DeserializeObject(content);
}
- catch (Exception ex)
+ catch(Exception ex)
{
// An exception has occurred, add some additional information to the message
- Exception exception = new Exception($"Error creating new operator {createOperatorRequest.Name} for estate {estateId}.", ex);
+ Exception exception = new Exception($"Error assigning operator Id {assignOperatorRequest.OperatorId} to merchant Id {merchantId} for estate {estateId}.",
+ ex);
throw exception;
}
@@ -229,38 +233,47 @@ public async Task CreateMerchant(String accessToken,
}
///
- /// Gets the estate.
+ /// Creates the merchant user.
///
/// The access token.
/// The estate identifier.
+ /// The merchant identifier.
+ /// The create merchant user request.
/// The cancellation token.
///
- public async Task GetEstate(String accessToken,
- Guid estateId,
- CancellationToken cancellationToken)
+ public async Task CreateMerchantUser(String accessToken,
+ Guid estateId,
+ Guid merchantId,
+ CreateMerchantUserRequest createMerchantUserRequest,
+ CancellationToken cancellationToken)
{
- EstateResponse response = null;
+ CreateMerchantUserResponse response = null;
- String requestUri = $"{this.BaseAddress}/api/estates/{estateId}";
+ String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/merchants/{merchantId}/users";
try
{
+ String requestSerialised = JsonConvert.SerializeObject(createMerchantUserRequest);
+
+ StringContent httpContent = new StringContent(requestSerialised, Encoding.UTF8, "application/json");
+
// Add the access token to the client headers
//this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Make the Http Call here
- HttpResponseMessage httpResponse = await this.HttpClient.GetAsync(requestUri, cancellationToken);
+ HttpResponseMessage httpResponse = await this.HttpClient.PostAsync(requestUri, httpContent, cancellationToken);
// Process the response
String content = await this.HandleResponse(httpResponse, cancellationToken);
// call was successful so now deserialise the body to the response object
- response = JsonConvert.DeserializeObject(content);
+ response = JsonConvert.DeserializeObject(content);
}
catch(Exception ex)
{
// An exception has occurred, add some additional information to the message
- Exception exception = new Exception($"Error getting estate Id {estateId}.", ex);
+ Exception exception = new Exception($"Error creating new mercant user Merchant Id {estateId} Email Address {createMerchantUserRequest.EmailAddress}.",
+ ex);
throw exception;
}
@@ -269,21 +282,65 @@ public async Task GetEstate(String accessToken,
}
///
- /// Gets the merchant.
+ /// Adds the operator.
///
/// The access token.
/// The estate identifier.
- /// The merchant identifier.
+ /// The create operator request.
/// The cancellation token.
///
- public async Task GetMerchant(String accessToken,
+ public async Task CreateOperator(String accessToken,
+ Guid estateId,
+ CreateOperatorRequest createOperatorRequest,
+ CancellationToken cancellationToken)
+ {
+ CreateOperatorResponse response = null;
+
+ String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/operators";
+
+ try
+ {
+ String requestSerialised = JsonConvert.SerializeObject(createOperatorRequest);
+
+ StringContent httpContent = new StringContent(requestSerialised, Encoding.UTF8, "application/json");
+
+ // Add the access token to the client headers
+ //this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
+
+ // Make the Http Call here
+ HttpResponseMessage httpResponse = await this.HttpClient.PostAsync(requestUri, httpContent, cancellationToken);
+
+ // Process the response
+ String content = await this.HandleResponse(httpResponse, cancellationToken);
+
+ // call was successful so now deserialise the body to the response object
+ response = JsonConvert.DeserializeObject(content);
+ }
+ catch(Exception ex)
+ {
+ // An exception has occurred, add some additional information to the message
+ Exception exception = new Exception($"Error creating new operator {createOperatorRequest.Name} for estate {estateId}.", ex);
+
+ throw exception;
+ }
+
+ return response;
+ }
+
+ ///
+ /// Gets the estate.
+ ///
+ /// The access token.
+ /// The estate identifier.
+ /// The cancellation token.
+ ///
+ public async Task GetEstate(String accessToken,
Guid estateId,
- Guid merchantId,
CancellationToken cancellationToken)
{
- MerchantResponse response = null;
+ EstateResponse response = null;
- String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/merchants/{merchantId}";
+ String requestUri = $"{this.BaseAddress}/api/estates/{estateId}";
try
{
@@ -297,12 +354,12 @@ public async Task GetMerchant(String accessToken,
String content = await this.HandleResponse(httpResponse, cancellationToken);
// call was successful so now deserialise the body to the response object
- response = JsonConvert.DeserializeObject(content);
+ response = JsonConvert.DeserializeObject(content);
}
- catch (Exception ex)
+ catch(Exception ex)
{
// An exception has occurred, add some additional information to the message
- Exception exception = new Exception($"Error getting merchant Id {merchantId} in estate {estateId}.", ex);
+ Exception exception = new Exception($"Error getting estate Id {estateId}.", ex);
throw exception;
}
@@ -311,46 +368,40 @@ public async Task GetMerchant(String accessToken,
}
///
- /// Assigns the operator to merchant.
+ /// Gets the merchant.
///
/// The access token.
/// The estate identifier.
/// The merchant identifier.
- /// The assign operator request.
/// The cancellation token.
///
- public async Task AssignOperatorToMerchant(String accessToken,
- Guid estateId,
- Guid merchantId,
- AssignOperatorRequest assignOperatorRequest,
- CancellationToken cancellationToken)
+ public async Task GetMerchant(String accessToken,
+ Guid estateId,
+ Guid merchantId,
+ CancellationToken cancellationToken)
{
- AssignOperatorResponse response = null;
+ MerchantResponse response = null;
- String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/merchants/{merchantId}/operators";
+ String requestUri = $"{this.BaseAddress}/api/estates/{estateId}/merchants/{merchantId}";
try
{
- String requestSerialised = JsonConvert.SerializeObject(assignOperatorRequest);
-
- StringContent httpContent = new StringContent(requestSerialised, Encoding.UTF8, "application/json");
-
// Add the access token to the client headers
//this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Make the Http Call here
- HttpResponseMessage httpResponse = await this.HttpClient.PostAsync(requestUri, httpContent, cancellationToken);
+ HttpResponseMessage httpResponse = await this.HttpClient.GetAsync(requestUri, cancellationToken);
// Process the response
String content = await this.HandleResponse(httpResponse, cancellationToken);
// call was successful so now deserialise the body to the response object
- response = JsonConvert.DeserializeObject(content);
+ response = JsonConvert.DeserializeObject(content);
}
- catch (Exception ex)
+ catch(Exception ex)
{
// An exception has occurred, add some additional information to the message
- Exception exception = new Exception($"Error assigning operator Id {assignOperatorRequest.OperatorId} to merchant Id {merchantId} for estate {estateId}.", ex);
+ Exception exception = new Exception($"Error getting merchant Id {merchantId} in estate {estateId}.", ex);
throw exception;
}
diff --git a/EstateManagement.Client/IEstateClient.cs b/EstateManagement.Client/IEstateClient.cs
index 368f767c..45e6e62d 100644
--- a/EstateManagement.Client/IEstateClient.cs
+++ b/EstateManagement.Client/IEstateClient.cs
@@ -45,7 +45,7 @@ Task CreateEstate(String accessToken,
/// The access token.
/// The estate identifier.
/// The create estate user request.
- /// The none.
+ /// The cancellation token.
///
Task CreateEstateUser(String accessToken,
Guid estateId,
@@ -65,6 +65,21 @@ Task CreateMerchant(String accessToken,
CreateMerchantRequest createMerchantRequest,
CancellationToken cancellationToken);
+ ///
+ /// Creates the merchant user.
+ ///
+ /// The access token.
+ /// The estate identifier.
+ /// The merchant identifier.
+ /// The create merchant user request.
+ /// The cancellation token.
+ ///
+ Task CreateMerchantUser(String accessToken,
+ Guid estateId,
+ Guid merchantId,
+ CreateMerchantUserRequest createMerchantUserRequest,
+ CancellationToken cancellationToken);
+
///
/// Adds the operator.
///
diff --git a/EstateManagement.DataTransferObjects/Requests/CreateMerchantUserRequest.cs b/EstateManagement.DataTransferObjects/Requests/CreateMerchantUserRequest.cs
new file mode 100644
index 00000000..848e7991
--- /dev/null
+++ b/EstateManagement.DataTransferObjects/Requests/CreateMerchantUserRequest.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace EstateManagement.DataTransferObjects.Requests
+{
+ using System.ComponentModel.DataAnnotations;
+ using System.Diagnostics.CodeAnalysis;
+ using Newtonsoft.Json;
+
+ [ExcludeFromCodeCoverage]
+ public class CreateMerchantUserRequest
+ {
+ [Required]
+ [JsonProperty("email_address")]
+ public String EmailAddress { get; set; }
+
+ ///
+ /// Gets or sets the password.
+ ///
+ ///
+ /// The password.
+ ///
+ [Required]
+ [JsonProperty("password")]
+ public String Password { get; set; }
+
+ ///
+ /// Gets or sets the name of the given.
+ ///
+ ///
+ /// The name of the given.
+ ///
+ [Required]
+ [JsonProperty("given_name")]
+ public String GivenName { get; set; }
+
+ ///
+ /// Gets or sets the name of the middle.
+ ///
+ ///
+ /// The name of the middle.
+ ///
+ [JsonProperty("middle_name")]
+ public String MiddleName { get; set; }
+
+ ///
+ /// Gets or sets the name of the family.
+ ///
+ ///
+ /// The name of the family.
+ ///
+ [Required]
+ [JsonProperty("family_name")]
+ public String FamilyName { get; set; }
+ }
+}
diff --git a/EstateManagement.DataTransferObjects/Responses/CreateMerchantUserResponse.cs b/EstateManagement.DataTransferObjects/Responses/CreateMerchantUserResponse.cs
new file mode 100644
index 00000000..f2faa632
--- /dev/null
+++ b/EstateManagement.DataTransferObjects/Responses/CreateMerchantUserResponse.cs
@@ -0,0 +1,35 @@
+namespace EstateManagement.DataTransferObjects.Responses
+{
+ using System;
+ using Newtonsoft.Json;
+
+ public class CreateMerchantUserResponse
+ {
+ ///
+ /// Gets or sets the name of the estate.
+ ///
+ ///
+ /// The name of the estate.
+ ///
+ [JsonProperty("estate_id")]
+ public Guid EstateId { get; set; }
+
+ ///
+ /// Gets or sets the merchant identifier.
+ ///
+ ///
+ /// The merchant identifier.
+ ///
+ [JsonProperty("merchant_id")]
+ public Guid MerchantId { get; set; }
+
+ ///
+ /// Gets or sets the user identifier.
+ ///
+ ///
+ /// The user identifier.
+ ///
+ [JsonProperty("user_id")]
+ public Guid UserId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/EstateManagement.EstateAggregate.Tests/EstateManagement.EstateAggregate.Tests.csproj b/EstateManagement.EstateAggregate.Tests/EstateManagement.EstateAggregate.Tests.csproj
index 9b8f234c..5e491b79 100644
--- a/EstateManagement.EstateAggregate.Tests/EstateManagement.EstateAggregate.Tests.csproj
+++ b/EstateManagement.EstateAggregate.Tests/EstateManagement.EstateAggregate.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/EstateManagement.IntegrationTests/EstateManagement.IntegrationTests.csproj b/EstateManagement.IntegrationTests/EstateManagement.IntegrationTests.csproj
index a187c516..2b1b750e 100644
--- a/EstateManagement.IntegrationTests/EstateManagement.IntegrationTests.csproj
+++ b/EstateManagement.IntegrationTests/EstateManagement.IntegrationTests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/EstateManagement.IntegrationTests/Merchant/Merchant.feature b/EstateManagement.IntegrationTests/Merchant/Merchant.feature
index ea66f5ee..851963be 100644
--- a/EstateManagement.IntegrationTests/Merchant/Merchant.feature
+++ b/EstateManagement.IntegrationTests/Merchant/Merchant.feature
@@ -23,3 +23,12 @@ Scenario: Assign Operator To Merchant
| OperatorName | MerchantName | MerchantNumber | TerminalNumber |
| Test Operator 1 | Test Merchant 1 | 00000001 | 10000001 |
+@PRTest
+Scenario: Create Security User
+ Given I create the following merchants
+ | MerchantName | AddressLine1 | Town | Region | Country | ContactName | EmailAddress | EstateName |
+ | Test Merchant 1 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 1 | testcontact1@merchant1.co.uk | Test Estate 1 |
+ When I create the following security users
+ | EmailAddress | Password | GivenName | FamilyName | MerchantName |
+ | merchantuser1@testmerchant1.co.uk | 123456 | TestMerchant | User1 | Test Merchant 1 |
+
diff --git a/EstateManagement.IntegrationTests/Merchant/Merchant.feature.cs b/EstateManagement.IntegrationTests/Merchant/Merchant.feature.cs
index 7506feb9..bddc900f 100644
--- a/EstateManagement.IntegrationTests/Merchant/Merchant.feature.cs
+++ b/EstateManagement.IntegrationTests/Merchant/Merchant.feature.cs
@@ -238,6 +238,79 @@ public virtual void AssignOperatorToMerchant()
this.ScenarioCleanup();
}
+ [Xunit.SkippableFactAttribute(DisplayName="Create Security User")]
+ [Xunit.TraitAttribute("FeatureTitle", "Merchant")]
+ [Xunit.TraitAttribute("Description", "Create Security User")]
+ [Xunit.TraitAttribute("Category", "PRTest")]
+ public virtual void CreateSecurityUser()
+ {
+ string[] tagsOfScenario = new string[] {
+ "PRTest"};
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Create Security User", null, new string[] {
+ "PRTest"});
+#line 27
+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 table11 = new TechTalk.SpecFlow.Table(new string[] {
+ "MerchantName",
+ "AddressLine1",
+ "Town",
+ "Region",
+ "Country",
+ "ContactName",
+ "EmailAddress",
+ "EstateName"});
+ table11.AddRow(new string[] {
+ "Test Merchant 1",
+ "Address Line 1",
+ "TestTown",
+ "Test Region",
+ "United Kingdom",
+ "Test Contact 1",
+ "testcontact1@merchant1.co.uk",
+ "Test Estate 1"});
+#line 28
+ testRunner.Given("I create the following merchants", ((string)(null)), table11, "Given ");
+#line hidden
+ TechTalk.SpecFlow.Table table12 = new TechTalk.SpecFlow.Table(new string[] {
+ "EmailAddress",
+ "Password",
+ "GivenName",
+ "FamilyName",
+ "MerchantName"});
+ table12.AddRow(new string[] {
+ "merchantuser1@testmerchant1.co.uk",
+ "123456",
+ "TestMerchant",
+ "User1",
+ "Test Merchant 1"});
+#line 31
+ testRunner.When("I create the following security users", ((string)(null)), table12, "When ");
+#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/EstateManagement.IntegrationTests/Shared/SharedSteps.cs b/EstateManagement.IntegrationTests/Shared/SharedSteps.cs
index dab9d279..e6c6fde9 100644
--- a/EstateManagement.IntegrationTests/Shared/SharedSteps.cs
+++ b/EstateManagement.IntegrationTests/Shared/SharedSteps.cs
@@ -214,25 +214,60 @@ public async Task WhenICreateTheFollowingSecurityUsers(Table table)
{
foreach (TableRow tableRow in table.Rows)
{
- // lookup the estate id based on the name in the table
- String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
- Guid estateId = this.TestingContext.Estates.Single(e => e.Key == estateName).Value;
-
- CreateEstateUserRequest createEstateUserRequest = new CreateEstateUserRequest
- {
- EmailAddress = SpecflowTableHelper.GetStringRowValue(tableRow, "EmailAddress"),
- FamilyName = SpecflowTableHelper.GetStringRowValue(tableRow, "FamilyName"),
- GivenName = SpecflowTableHelper.GetStringRowValue(tableRow, "GivenName"),
- MiddleName = SpecflowTableHelper.GetStringRowValue(tableRow, "MiddleName"),
- Password = SpecflowTableHelper.GetStringRowValue(tableRow, "Password")
- };
+ if (tableRow.ContainsKey("EstateName"))
+ {
+ // Creating an Estate User
+
+ // lookup the estate id based on the name in the table
+ String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
+ Guid estateId = this.TestingContext.Estates.Single(e => e.Key == estateName).Value;
+
+ CreateEstateUserRequest createEstateUserRequest = new CreateEstateUserRequest
+ {
+ EmailAddress = SpecflowTableHelper.GetStringRowValue(tableRow, "EmailAddress"),
+ FamilyName = SpecflowTableHelper.GetStringRowValue(tableRow, "FamilyName"),
+ GivenName = SpecflowTableHelper.GetStringRowValue(tableRow, "GivenName"),
+ MiddleName = SpecflowTableHelper.GetStringRowValue(tableRow, "MiddleName"),
+ Password = SpecflowTableHelper.GetStringRowValue(tableRow, "Password")
+ };
+
+ CreateEstateUserResponse createEstateUserResponse =
+ await this.TestingContext.DockerHelper.EstateClient.CreateEstateUser(String.Empty, estateId, createEstateUserRequest, CancellationToken.None);
- CreateEstateUserResponse createEstateUserResponse = await this.TestingContext.DockerHelper.EstateClient.CreateEstateUser(String.Empty, estateId, createEstateUserRequest, CancellationToken.None);
+ createEstateUserResponse.EstateId.ShouldBe(estateId);
+ createEstateUserResponse.UserId.ShouldNotBe(Guid.Empty);
- createEstateUserResponse.EstateId.ShouldBe(estateId);
- createEstateUserResponse.UserId.ShouldNotBe(Guid.Empty);
+ this.TestingContext.Logger.LogInformation($"Security user {createEstateUserRequest.EmailAddress} assigned to Estate {estateName}");
+ }
+ else if (tableRow.ContainsKey("MerchantName"))
+ {
+ // Creating a merchant user
+
+ // lookup the merchant id based on the name in the table
+ String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
+ Guid merchantId = this.TestingContext.Merchants.Single(m => m.Key == merchantName).Value;
+
+ // Now find the estate Id
+ Guid estateId = this.TestingContext.EstateMerchants.Single(e => e.Value.Contains(merchantId)).Key;
- this.TestingContext.Logger.LogInformation($"Security user {createEstateUserRequest.EmailAddress} assigned to Estate {estateName}");
+ CreateMerchantUserRequest createMerchantUserRequest = new CreateMerchantUserRequest
+ {
+ EmailAddress = SpecflowTableHelper.GetStringRowValue(tableRow, "EmailAddress"),
+ FamilyName = SpecflowTableHelper.GetStringRowValue(tableRow, "FamilyName"),
+ GivenName = SpecflowTableHelper.GetStringRowValue(tableRow, "GivenName"),
+ MiddleName = SpecflowTableHelper.GetStringRowValue(tableRow, "MiddleName"),
+ Password = SpecflowTableHelper.GetStringRowValue(tableRow, "Password")
+ };
+
+ CreateMerchantUserResponse createMerchantUserResponse =
+ await this.TestingContext.DockerHelper.EstateClient.CreateMerchantUser(String.Empty, estateId, merchantId, createMerchantUserRequest, CancellationToken.None);
+
+ createMerchantUserResponse.EstateId.ShouldBe(estateId);
+ createMerchantUserResponse.MerchantId.ShouldBe(merchantId);
+ createMerchantUserResponse.UserId.ShouldNotBe(Guid.Empty);
+
+ this.TestingContext.Logger.LogInformation($"Security user {createMerchantUserRequest.EmailAddress} assigned to Merchant {merchantName}");
+ }
}
}
diff --git a/EstateManagement.Merchant.DomainEvents/SecurityUserAddedEvent.cs b/EstateManagement.Merchant.DomainEvents/SecurityUserAddedEvent.cs
new file mode 100644
index 00000000..b689002d
--- /dev/null
+++ b/EstateManagement.Merchant.DomainEvents/SecurityUserAddedEvent.cs
@@ -0,0 +1,108 @@
+namespace EstateManagement.Merchant.DomainEvents
+{
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using Newtonsoft.Json;
+ using Shared.DomainDrivenDesign.EventSourcing;
+
+ ///
+ ///
+ ///
+ ///
+ [JsonObject]
+ public class SecurityUserAddedEvent : DomainEvent
+ {
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [ExcludeFromCodeCoverage]
+ public SecurityUserAddedEvent()
+ {
+ //We need this for serialisation, so just embrace the DDD crime
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The aggregate identifier.
+ /// The event identifier.
+ /// The estate identifier.
+ /// The security user identifier.
+ /// The email address.
+ private SecurityUserAddedEvent(Guid aggregateId,
+ Guid eventId,
+ Guid estateId,
+ Guid securityUserId,
+ String emailAddress) : base(aggregateId, eventId)
+ {
+ this.EstateId = estateId;
+ this.MerchantId = aggregateId;
+ this.SecurityUserId = securityUserId;
+ this.EmailAddress = emailAddress;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the email address.
+ ///
+ ///
+ /// The email address.
+ ///
+ [JsonProperty]
+ public String EmailAddress { get; private set; }
+
+ ///
+ /// Gets the estate identifier.
+ ///
+ ///
+ /// The estate identifier.
+ ///
+ [JsonProperty]
+ public Guid EstateId { get; private set; }
+
+ ///
+ /// Gets the merchant identifier.
+ ///
+ ///
+ /// The merchant identifier.
+ ///
+ [JsonProperty]
+ public Guid MerchantId { get; private set; }
+
+ ///
+ /// Gets the security user identifier.
+ ///
+ ///
+ /// The security user identifier.
+ ///
+ [JsonProperty]
+ public Guid SecurityUserId { get; private set; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Creates the specified aggregate identifier.
+ ///
+ /// The aggregate identifier.
+ /// The estate identifier.
+ /// The security user identifier.
+ /// The email address.
+ ///
+ public static SecurityUserAddedEvent Create(Guid aggregateId,
+ Guid estateId,
+ Guid securityUserId,
+ String emailAddress)
+ {
+ return new SecurityUserAddedEvent(aggregateId, Guid.NewGuid(), estateId, securityUserId, emailAddress);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/EstateManagement.MerchantAggregate.Tests/DomainEventTests.cs b/EstateManagement.MerchantAggregate.Tests/DomainEventTests.cs
index 47a08e57..b53e03fc 100644
--- a/EstateManagement.MerchantAggregate.Tests/DomainEventTests.cs
+++ b/EstateManagement.MerchantAggregate.Tests/DomainEventTests.cs
@@ -93,7 +93,24 @@ public void OperatorAssignedToMerchantEvent_CanBeCreated_IsCreated()
operatorAssignedToMerchantEvent.Name.ShouldBe(TestData.OperatorName);
operatorAssignedToMerchantEvent.MerchantNumber.ShouldBe(TestData.OperatorMerchantNumber);
operatorAssignedToMerchantEvent.TerminalNumber.ShouldBe(TestData.OperatorTerminalNumber);
+ }
+
+ [Fact]
+ public void SecurityUserAddedEvent_CanBeCreated_IsCreated()
+ {
+ SecurityUserAddedEvent securityUserAddedEvent = SecurityUserAddedEvent.Create(TestData.MerchantId,
+ TestData.EstateId,
+ TestData.SecurityUserId,
+ TestData.EstateUserEmailAddress);
+ securityUserAddedEvent.ShouldNotBeNull();
+ securityUserAddedEvent.AggregateId.ShouldBe(TestData.MerchantId);
+ securityUserAddedEvent.MerchantId.ShouldBe(TestData.MerchantId);
+ securityUserAddedEvent.EventCreatedDateTime.ShouldNotBe(DateTime.MinValue);
+ securityUserAddedEvent.EventId.ShouldNotBe(Guid.Empty);
+ securityUserAddedEvent.EstateId.ShouldBe(TestData.EstateId);
+ securityUserAddedEvent.SecurityUserId.ShouldBe(TestData.SecurityUserId);
+ securityUserAddedEvent.EmailAddress.ShouldBe(TestData.EstateUserEmailAddress);
}
}
}
diff --git a/EstateManagement.MerchantAggregate.Tests/EstateManagement.MerchantAggregate.Tests.csproj b/EstateManagement.MerchantAggregate.Tests/EstateManagement.MerchantAggregate.Tests.csproj
index eb236f9c..3ac3bd77 100644
--- a/EstateManagement.MerchantAggregate.Tests/EstateManagement.MerchantAggregate.Tests.csproj
+++ b/EstateManagement.MerchantAggregate.Tests/EstateManagement.MerchantAggregate.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/EstateManagement.MerchantAggregate.Tests/MerchantAggregateTests.cs b/EstateManagement.MerchantAggregate.Tests/MerchantAggregateTests.cs
index 705275f4..98e40cd3 100644
--- a/EstateManagement.MerchantAggregate.Tests/MerchantAggregateTests.cs
+++ b/EstateManagement.MerchantAggregate.Tests/MerchantAggregateTests.cs
@@ -159,5 +159,32 @@ public void MerchantAggregate_AssignOperator_OperatorAlreadyAssigned_ErrorThrown
aggregate.AssignOperator(TestData.OperatorId, TestData.OperatorName, TestData.OperatorMerchantNumber, TestData.OperatorTerminalNumber);
});
}
+
+ [Fact]
+ public void MerchantAggregate_AddSecurityUserToMerchant_SecurityUserIsAdded()
+ {
+ MerchantAggregate aggregate = MerchantAggregate.Create(TestData.MerchantId);
+ aggregate.Create(TestData.EstateId, TestData.MerchantName, TestData.DateMerchantCreated);
+ aggregate.AddSecurityUser(TestData.SecurityUserId, TestData.MerchantUserEmailAddress);
+
+ Merchant merchantModel = aggregate.GetMerchant();
+ merchantModel.SecurityUsers.ShouldHaveSingleItem();
+ SecurityUser securityUser = merchantModel.SecurityUsers.Single();
+ securityUser.EmailAddress.ShouldBe(TestData.MerchantUserEmailAddress);
+ securityUser.SecurityUserId.ShouldBe(TestData.SecurityUserId);
+ }
+
+ [Fact]
+ public void MerchantAggregate_AddSecurityUserToMerchant_MerchantNotCreated_ErrorThrown()
+ {
+ MerchantAggregate aggregate = MerchantAggregate.Create(TestData.MerchantId);
+
+ InvalidOperationException exception = Should.Throw(() =>
+ {
+ aggregate.AddSecurityUser(TestData.SecurityUserId, TestData.EstateUserEmailAddress);
+ });
+
+ exception.Message.ShouldContain("Merchant has not been created");
+ }
}
}
diff --git a/EstateManagement.MerchantAggregate/MerchantAggregate.cs b/EstateManagement.MerchantAggregate/MerchantAggregate.cs
index 660f49fa..ef7fcc9c 100644
--- a/EstateManagement.MerchantAggregate/MerchantAggregate.cs
+++ b/EstateManagement.MerchantAggregate/MerchantAggregate.cs
@@ -33,6 +33,11 @@ public class MerchantAggregate : Aggregate
///
private readonly List Operators;
+ ///
+ /// The security users
+ ///
+ private readonly List SecurityUsers;
+
#endregion
#region Constructors
@@ -47,6 +52,7 @@ public MerchantAggregate()
this.Addresses = new List();
this.Contacts = new List();
this.Operators = new List();
+ this.SecurityUsers = new List();
}
///
@@ -61,6 +67,7 @@ private MerchantAggregate(Guid aggregateId)
this.Addresses = new List();
this.Contacts = new List();
this.Operators = new List();
+ this.SecurityUsers = new List();
}
#endregion
@@ -153,6 +160,15 @@ public Merchant GetMerchant()
}));
}
+ if (this.SecurityUsers.Any())
+ {
+ this.SecurityUsers.ForEach(s => merchantModel.SecurityUsers.Add(new Models.Merchant.SecurityUser
+ {
+ SecurityUserId = s.SecurityUserId,
+ EmailAddress = s.EmailAddress
+ }));
+ }
+
return merchantModel;
}
@@ -372,5 +388,27 @@ private void PlayEvent(OperatorAssignedToMerchantEvent operatorAssignedToMerchan
this.Operators.Add(@operator);
}
+
+ ///
+ /// Adds the security user.
+ ///
+ /// The security user identifier.
+ /// The email address.
+ public void AddSecurityUser(Guid securityUserId,
+ String emailAddress)
+ {
+ this.EnsureMerchantHasBeenCreated();
+
+ SecurityUserAddedEvent securityUserAddedEvent = SecurityUserAddedEvent.Create(this.AggregateId, this.EstateId, securityUserId, emailAddress);
+
+ this.ApplyAndPend(securityUserAddedEvent);
+ }
+
+ private void PlayEvent(SecurityUserAddedEvent domainEvent)
+ {
+ SecurityUser securityUser = SecurityUser.Create(domainEvent.SecurityUserId, domainEvent.EmailAddress);
+
+ this.SecurityUsers.Add(securityUser);
+ }
}
}
\ No newline at end of file
diff --git a/EstateManagement.MerchantAggregate/SecurityUser.cs b/EstateManagement.MerchantAggregate/SecurityUser.cs
new file mode 100644
index 00000000..ae56d13a
--- /dev/null
+++ b/EstateManagement.MerchantAggregate/SecurityUser.cs
@@ -0,0 +1,47 @@
+namespace EstateManagement.MerchantAggregate
+{
+ using System;
+
+ ///
+ ///
+ ///
+ internal class SecurityUser
+ {
+ ///
+ /// Gets the security user identifier.
+ ///
+ ///
+ /// The security user identifier.
+ ///
+ internal Guid SecurityUserId { get; }
+ ///
+ /// Gets the email address.
+ ///
+ ///
+ /// The email address.
+ ///
+ internal String EmailAddress { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The security user identifier.
+ /// The email address.
+ private SecurityUser(Guid securityUserId,String emailAddress)
+ {
+ this.SecurityUserId = securityUserId;
+ this.EmailAddress = emailAddress;
+ }
+
+ ///
+ /// Creates the specified security user identifier.
+ ///
+ /// The security user identifier.
+ /// The email address.
+ ///
+ internal static SecurityUser Create(Guid securityUserId, String emailAddress)
+ {
+ return new SecurityUser(securityUserId, emailAddress);
+ }
+ }
+}
diff --git a/EstateManagement.Models/Merchant/Merchant.cs b/EstateManagement.Models/Merchant/Merchant.cs
index 1ba8a0a0..3342ec9d 100644
--- a/EstateManagement.Models/Merchant/Merchant.cs
+++ b/EstateManagement.Models/Merchant/Merchant.cs
@@ -12,8 +12,12 @@ public class Merchant
public Merchant()
{
this.Addresses = new List();
+
this.Contacts = new List();
- this.Operators=new List();
+
+ this.Operators = new List();
+
+ this.SecurityUsers = new List();
}
#endregion
@@ -68,6 +72,14 @@ public Merchant()
///
public List Operators { get; set; }
+ ///
+ /// Gets or sets the security users.
+ ///
+ ///
+ /// The security users.
+ ///
+ public List SecurityUsers { get; set; }
+
#endregion
}
}
\ No newline at end of file
diff --git a/EstateManagement.Models/Merchant/SecurityUser.cs b/EstateManagement.Models/Merchant/SecurityUser.cs
new file mode 100644
index 00000000..3b176d23
--- /dev/null
+++ b/EstateManagement.Models/Merchant/SecurityUser.cs
@@ -0,0 +1,32 @@
+namespace EstateManagement.Models.Merchant
+{
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ ///
+ ///
+ ///
+ [ExcludeFromCodeCoverage]
+ public class SecurityUser
+ {
+ #region Properties
+
+ ///
+ /// Gets or sets the email address.
+ ///
+ ///
+ /// The email address.
+ ///
+ public String EmailAddress { get; set; }
+
+ ///
+ /// Gets or sets the security user identifier.
+ ///
+ ///
+ /// The security user identifier.
+ ///
+ public Guid SecurityUserId { get; set; }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/EstateManagement.Testing/TestData.cs b/EstateManagement.Testing/TestData.cs
index c2f6cb8c..cbaede93 100644
--- a/EstateManagement.Testing/TestData.cs
+++ b/EstateManagement.Testing/TestData.cs
@@ -254,13 +254,22 @@ public static MerchantAggregate MerchantAggregateWithOperator()
public static String EstateUserEmailAddress = "testestateuser@estate1.co.uk";
+ public static String MerchantUserEmailAddress = "testmerchantuser@merchant1.co.uk";
+
public static String EstateUserPassword="123456";
+ public static String MerchantUserPassword = "123456";
+
public static String EstateUserGivenName = "Test";
+ public static String MerchantUserGivenName = "Test";
+
public static String EstateUserMiddleName = "Middle";
+ public static String MerchantUserMiddleName = "Middle";
+
public static String EstateUserFamilyName = "Estate";
+ public static String MerchantUserFamilyName = "Merchant";
public static CreateEstateUserRequest CreateEstateUserRequest = CreateEstateUserRequest.Create(TestData.EstateId,
TestData.EstateUserEmailAddress,
@@ -270,5 +279,13 @@ public static MerchantAggregate MerchantAggregateWithOperator()
TestData.EstateUserFamilyName);
public static Guid SecurityUserId = Guid.Parse("45B74A2E-BF92-44E9-A300-08E5CDEACFE3");
+
+ public static CreateMerchantUserRequest CreateMerchantUserRequest = CreateMerchantUserRequest.Create(TestData.EstateId,
+ TestData.MerchantId,
+ TestData.MerchantUserEmailAddress,
+ TestData.MerchantUserPassword,
+ TestData.MerchantUserGivenName,
+ TestData.MerchantUserMiddleName,
+ TestData.MerchantUserFamilyName);
}
}
\ No newline at end of file
diff --git a/EstateManagement.Tests/EstateManagement.Tests.csproj b/EstateManagement.Tests/EstateManagement.Tests.csproj
index 95a791fd..b4bab1df 100644
--- a/EstateManagement.Tests/EstateManagement.Tests.csproj
+++ b/EstateManagement.Tests/EstateManagement.Tests.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/EstateManagement/Controllers/MerchantController.cs b/EstateManagement/Controllers/MerchantController.cs
index 7d46b63c..adb99d70 100644
--- a/EstateManagement/Controllers/MerchantController.cs
+++ b/EstateManagement/Controllers/MerchantController.cs
@@ -18,6 +18,8 @@
using CreateMerchantRequestDTO = DataTransferObjects.Requests.CreateMerchantRequest;
using AssignOperatorToMerchantRequest = BusinessLogic.Requests.AssignOperatorToMerchantRequest;
using AssignOperatorRequestDTO = DataTransferObjects.Requests.AssignOperatorRequest;
+ using CreateMerchantUserRequest = BusinessLogic.Requests.CreateMerchantUserRequest;
+ using CreateMerchantUserRequestDTO = DataTransferObjects.Requests.CreateMerchantUserRequest;
///
///
@@ -167,6 +169,33 @@ public async Task AssignOperator([FromRoute] Guid estateId,
});
}
+ [HttpPost]
+ [Route("{merchantId}/users")]
+ public async Task CreateMerchantUser([FromRoute] Guid estateId,
+ [FromRoute] Guid merchantId,
+ [FromBody] CreateMerchantUserRequestDTO createMerchantUserRequest,
+ CancellationToken cancellationToken)
+ {
+ // Create the command
+ CreateMerchantUserRequest request = CreateMerchantUserRequest.Create(estateId, merchantId, createMerchantUserRequest.EmailAddress,
+ createMerchantUserRequest.Password,
+ createMerchantUserRequest.GivenName,
+ createMerchantUserRequest.MiddleName,
+ createMerchantUserRequest.FamilyName);
+
+ // Route the command
+ Guid userId = await this.Mediator.Send(request, cancellationToken);
+
+ // return the result
+ return this.Created($"{MerchantController.ControllerRoute}/{merchantId}/users/{userId}",
+ new CreateMerchantUserResponse
+ {
+ EstateId = estateId,
+ MerchantId = merchantId,
+ UserId = userId
+ });
+ }
+
#endregion
#region Others
diff --git a/EstateManagement/Startup.cs b/EstateManagement/Startup.cs
index c1e9fa82..1241115d 100644
--- a/EstateManagement/Startup.cs
+++ b/EstateManagement/Startup.cs
@@ -158,6 +158,7 @@ public void ConfigureContainer(ContainerBuilder builder)
builder.RegisterType().As>().SingleInstance();
builder.RegisterType().As>().SingleInstance();
builder.RegisterType().As>().SingleInstance();
+ builder.RegisterType().As>().SingleInstance();
Func apiAddressResolver = (serviceName) => { return ConfigurationReader.GetBaseServerUri(serviceName).OriginalString; };