diff --git a/.github/workflows/createrelease.yml b/.github/workflows/createrelease.yml
index a66501a..79c9f4b 100644
--- a/.github/workflows/createrelease.yml
+++ b/.github/workflows/createrelease.yml
@@ -37,14 +37,14 @@ jobs:
dotnet test "MessagingService.EmailAggregate.Tests\MessagingService.EmailAggregate.Tests.csproj"
dotnet test "MessagingService.Tests\MessagingService.Tests.csproj"
- #- name: Build Docker Image
- # run: docker build . --file MessagingService/Dockerfile --tag stuartferguson/messagingservice:latest --tag stuartferguson/messagingservice:${{ steps.get_version.outputs.VERSION }}
+ - name: Build Docker Image
+ run: docker build . --file MessagingService/Dockerfile --tag stuartferguson/messagingservice:latest --tag stuartferguson/messagingservice:${{ steps.get_version.outputs.VERSION }}
- #- name: Publish Images to Docker Hub
- # run: |
- # docker login --username=${{ secrets.DOCKER_USERNAME }} --password=${{ secrets.DOCKER_PASSWORD }}
- # docker push stuartferguson/messagingservice:latest
- # docker push stuartferguson/messagingservice:${{ steps.get_version.outputs.VERSION }}
+ - name: Publish Images to Docker Hub
+ run: |
+ docker login --username=${{ secrets.DOCKER_USERNAME }} --password=${{ secrets.DOCKER_PASSWORD }}
+ docker push stuartferguson/messagingservice:latest
+ docker push stuartferguson/messagingservice:${{ steps.get_version.outputs.VERSION }}
- name: Publish API
run: dotnet publish "MessagingService\MessagingService.csproj" --configuration Release --output publishOutput
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index c79d84d..87a0966 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -34,8 +34,8 @@ jobs:
dotnet test "MessagingService.EmailAggregate.Tests\MessagingService.EmailAggregate.Tests.csproj"
dotnet test "MessagingService.Tests\MessagingService.Tests.csproj"
- #- name: Build Docker Image
- # run: docker build . --file MessagingService/Dockerfile --tag messagingservice:latest
+ - name: Build Docker Image
+ run: docker build . --file MessagingService/Dockerfile --tag messagingservice:latest
- #- name: Run Integration Tests
- # run: dotnet test "MessagingService.IntegrationTests\MessagingService.IntegrationTests.csproj" --filter Category=PRTest
+ - name: Run Integration Tests
+ run: dotnet test "MessagingService.IntegrationTests\MessagingService.IntegrationTests.csproj" --filter Category=PRTest
diff --git a/MessagingService.IntegrationTests/Common/DockerHelper.cs b/MessagingService.IntegrationTests/Common/DockerHelper.cs
new file mode 100644
index 0000000..15723aa
--- /dev/null
+++ b/MessagingService.IntegrationTests/Common/DockerHelper.cs
@@ -0,0 +1,244 @@
+namespace MessagingService.IntegrationTests.Common
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Threading.Tasks;
+ using Ductus.FluentDocker.Builders;
+ using Ductus.FluentDocker.Common;
+ using Ductus.FluentDocker.Model.Builders;
+ using Ductus.FluentDocker.Services;
+ using Ductus.FluentDocker.Services.Extensions;
+ using global::Shared.Logger;
+ using SecurityService.Client;
+
+ ///
+ ///
+ ///
+ ///
+ public class DockerHelper : global::Shared.IntegrationTesting.DockerHelper
+ {
+ #region Fields
+
+ ///
+ /// The security service client
+ ///
+ public ISecurityServiceClient SecurityServiceClient;
+
+ public HttpClient MessagingServiceClient;
+
+ ///
+ /// The test identifier
+ ///
+ public Guid TestId;
+
+ ///
+ /// The containers
+ ///
+ protected List Containers;
+
+ ///
+ /// The event store container name
+ ///
+ protected String EventStoreContainerName;
+
+ ///
+ /// The event store HTTP port
+ ///
+ protected Int32 EventStoreHttpPort;
+
+ ///
+ /// The security service container name
+ ///
+ protected String SecurityServiceContainerName;
+
+ ///
+ /// The messaging service container name
+ ///
+ protected String MessagingServiceContainerName;
+
+ ///
+ /// The security service port
+ ///
+ protected Int32 SecurityServicePort;
+
+ ///
+ /// The messaging service port
+ ///
+ protected Int32 MessagingServicePort;
+
+ ///
+ /// The test networks
+ ///
+ protected List TestNetworks;
+
+ ///
+ /// The logger
+ ///
+ private readonly NlogLogger Logger;
+
+ ///
+ /// The testing context
+ ///
+ private readonly TestingContext TestingContext;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger.
+ /// The testing context.
+ public DockerHelper(NlogLogger logger,
+ TestingContext testingContext)
+ {
+ this.Logger = logger;
+ this.TestingContext = testingContext;
+ this.Containers = new List();
+ this.TestNetworks = new List();
+ }
+
+ #endregion
+
+ #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}";
+
+ Logging.Enabled();
+
+ Guid testGuid = Guid.NewGuid();
+ this.TestId = testGuid;
+
+ this.Logger.LogInformation($"Test Id is {testGuid}");
+
+ // Setup the container names
+ this.SecurityServiceContainerName = $"securityservice{testGuid:N}";
+ this.EventStoreContainerName = $"eventstore{testGuid:N}";
+ this.MessagingServiceContainerName = $"messagingservice{testGuid:N}";
+
+ (String, String, String) dockerCredentials = ("https://www.docker.com", "stuartferguson", "Sc0tland");
+
+ INetworkService testNetwork = DockerHelper.SetupTestNetwork();
+ this.TestNetworks.Add(testNetwork);
+ IContainerService eventStoreContainer =
+ DockerHelper.SetupEventStoreContainer(this.EventStoreContainerName, this.Logger, "eventstore/eventstore:release-5.0.2", testNetwork, traceFolder);
+
+ IContainerService securityServiceContainer = DockerHelper.SetupSecurityServiceContainer(this.SecurityServiceContainerName,
+ this.Logger,
+ "stuartferguson/securityservice",
+ testNetwork,
+ traceFolder,
+ dockerCredentials,
+ true);
+
+ IContainerService messagingServiceContainer = DockerHelper.SetupMessagingServiceContainer(this.MessagingServiceContainerName,
+ this.Logger,
+ "messagingservice",
+ new List{
+ testNetwork
+ },
+ traceFolder,
+ dockerCredentials,
+ this.SecurityServiceContainerName,
+ this.EventStoreContainerName,
+ ("serviceClient", "Secret1"));
+
+ this.Containers.AddRange(new List
+ {
+ eventStoreContainer,
+ securityServiceContainer,
+ messagingServiceContainer
+ });
+
+ // Cache the ports
+ this.SecurityServicePort = securityServiceContainer.ToHostExposedEndpoint("5001/tcp").Port;
+ this.EventStoreHttpPort = eventStoreContainer.ToHostExposedEndpoint("2113/tcp").Port;
+ this.MessagingServicePort = messagingServiceContainer.ToHostExposedEndpoint("5006/tcp").Port;
+
+ // Setup the base address resolvers
+ String SecurityServiceBaseAddressResolver(String api) => $"http://127.0.0.1:{this.SecurityServicePort}";
+
+ HttpClient httpClient = new HttpClient();
+ this.SecurityServiceClient = new SecurityServiceClient(SecurityServiceBaseAddressResolver, httpClient);
+
+ this.MessagingServiceClient = new HttpClient();
+ this.MessagingServiceClient.BaseAddress = new Uri($"http://127.0.0.1:{this.MessagingServicePort}");
+ }
+
+ ///
+ /// Stops the containers for scenario run.
+ ///
+ public override async Task StopContainersForScenarioRun()
+ {
+ if (this.Containers.Any())
+ {
+ foreach (IContainerService containerService in this.Containers)
+ {
+ containerService.StopOnDispose = true;
+ containerService.RemoveOnDispose = true;
+ containerService.Dispose();
+ }
+ }
+
+ if (this.TestNetworks.Any())
+ {
+ foreach (INetworkService networkService in this.TestNetworks)
+ {
+ networkService.Stop();
+ networkService.Remove(true);
+ }
+ }
+ }
+
+ public const int MessagingServiceDockerPort = 5006;
+ public static IContainerService SetupMessagingServiceContainer(String containerName,
+ ILogger logger,
+ String imageName,
+ List networkServices,
+ String hostFolder,
+ (String URL, String UserName, String Password)? dockerCredentials,
+ String securityServiceContainerName,
+ String eventStoreContainerName,
+ (String clientId, String clientSecret) clientDetails,
+ Boolean forceLatestImage = false,
+ Int32 securityServicePort = DockerHelper.SecurityServiceDockerPort)
+ {
+ logger.LogInformation("About to Start Messaging Service Container");
+
+ List environmentVariables = new List();
+ environmentVariables
+ .Add($"EventStoreSettings:ConnectionString=ConnectTo=tcp://admin:changeit@{eventStoreContainerName}:{DockerHelper.EventStoreTcpDockerPort};VerboseLogging=true;");
+ environmentVariables.Add($"AppSettings:SecurityService=http://{securityServiceContainerName}:{securityServicePort}");
+ environmentVariables.Add($"SecurityConfiguration:Authority=http://{securityServiceContainerName}:{securityServicePort}");
+ environmentVariables.Add($"urls=http://*:{DockerHelper.MessagingServiceDockerPort}");
+ environmentVariables.Add("AppSettings:EmailProxy=Integration");
+
+ ContainerBuilder messagingServiceContainer = new Builder().UseContainer().WithName(containerName).WithEnvironment(environmentVariables.ToArray())
+ .UseImage(imageName, forceLatestImage).ExposePort(DockerHelper.MessagingServiceDockerPort)
+ .UseNetwork(networkServices.ToArray()).Mount(hostFolder, "/home", MountType.ReadWrite);
+
+ if (dockerCredentials.HasValue)
+ {
+ messagingServiceContainer.WithCredential(dockerCredentials.Value.URL, dockerCredentials.Value.UserName, dockerCredentials.Value.Password);
+ }
+
+ // Now build and return the container
+ IContainerService builtContainer = messagingServiceContainer.Build().Start().WaitForPort($"{DockerHelper.MessagingServiceDockerPort}/tcp", 30000);
+
+ logger.LogInformation("Messaging Service Container Started");
+
+ return builtContainer;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MessagingService.IntegrationTests/Common/GenericSteps.cs b/MessagingService.IntegrationTests/Common/GenericSteps.cs
new file mode 100644
index 0000000..2278b22
--- /dev/null
+++ b/MessagingService.IntegrationTests/Common/GenericSteps.cs
@@ -0,0 +1,50 @@
+using NLog;
+using Shared.Logger;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using TechTalk.SpecFlow;
+
+namespace MessagingService.IntegrationTests.Common
+{
+ [Binding]
+ [Scope(Tag = "base")]
+ public class GenericSteps
+ {
+ private readonly ScenarioContext ScenarioContext;
+
+ private readonly TestingContext TestingContext;
+
+ public GenericSteps(ScenarioContext scenarioContext,
+ TestingContext testingContext)
+ {
+ this.ScenarioContext = scenarioContext;
+ this.TestingContext = testingContext;
+ }
+
+ [BeforeScenario]
+ public async Task StartSystem()
+ {
+ // Initialise a logger
+ String scenarioName = this.ScenarioContext.ScenarioInfo.Title.Replace(" ", "");
+ NlogLogger logger = new NlogLogger();
+ logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName);
+ LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly);
+
+ this.TestingContext.DockerHelper = new DockerHelper(logger, this.TestingContext);
+ this.TestingContext.Logger = logger;
+ this.TestingContext.Logger.LogInformation("About to Start Containers for Scenario Run");
+ await this.TestingContext.DockerHelper.StartContainersForScenarioRun(scenarioName).ConfigureAwait(false);
+ this.TestingContext.Logger.LogInformation("Containers for Scenario Run Started");
+
+ await Task.Delay(TimeSpan.FromSeconds(20));
+ }
+
+ [AfterScenario]
+ public async Task StopSystem()
+ {
+ await this.TestingContext.DockerHelper.StopContainersForScenarioRun().ConfigureAwait(false);
+ }
+ }
+}
diff --git a/MessagingService.IntegrationTests/Common/SpecflowTableHelper.cs b/MessagingService.IntegrationTests/Common/SpecflowTableHelper.cs
new file mode 100644
index 0000000..8b0ae2c
--- /dev/null
+++ b/MessagingService.IntegrationTests/Common/SpecflowTableHelper.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using TechTalk.SpecFlow;
+
+namespace MessagingService.IntegrationTests.Common
+{
+ public static class SpecflowTableHelper
+ {
+ #region Methods
+
+ public static Decimal GetDecimalValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return Decimal.TryParse(field, out Decimal value) ? value : -1;
+ }
+
+ public static Boolean GetBooleanValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return Boolean.TryParse(field, out Boolean value) && value;
+ }
+
+ public static Int32 GetIntValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return Int32.TryParse(field, out Int32 value) ? value : -1;
+ }
+
+ public static Int16 GetShortValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ if (Int16.TryParse(field, out Int16 value))
+ {
+ return value;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ public static String GetStringRowValue(TableRow row,
+ String key)
+ {
+ return row.TryGetValue(key, out String value) ? value : "";
+ }
+
+ ///
+ /// Gets the date for date string.
+ ///
+ /// The date string.
+ /// The today.
+ ///
+ public static DateTime GetDateForDateString(String dateString,
+ DateTime today)
+ {
+ switch (dateString.ToUpper())
+ {
+ case "TODAY":
+ return today.Date;
+ case "YESTERDAY":
+ return today.AddDays(-1).Date;
+ case "LASTWEEK":
+ return today.AddDays(-7).Date;
+ case "LASTMONTH":
+ return today.AddMonths(-1).Date;
+ case "LASTYEAR":
+ return today.AddYears(-1).Date;
+ case "TOMORROW":
+ return today.AddDays(1).Date;
+ default:
+ return DateTime.Parse(dateString);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/MessagingService.IntegrationTests/Common/TestingContext.cs b/MessagingService.IntegrationTests/Common/TestingContext.cs
new file mode 100644
index 0000000..7e233c7
--- /dev/null
+++ b/MessagingService.IntegrationTests/Common/TestingContext.cs
@@ -0,0 +1,67 @@
+using Shared.Logger;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MessagingService.IntegrationTests.Common
+{
+ using System.Linq;
+ using DataTransferObjects;
+ using SecurityService.DataTransferObjects.Responses;
+ using Shouldly;
+
+ public class TestingContext
+ {
+ public TestingContext()
+ {
+ this.Clients = new List();
+ this.EmailResponses=new Dictionary();
+ }
+
+ public NlogLogger Logger { get; set; }
+ public String AccessToken { get; set; }
+ public DockerHelper DockerHelper { get; set; }
+
+ private List Clients;
+ public void AddClientDetails(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ this.Clients.Add(ClientDetails.Create(clientId, clientSecret, grantType));
+ }
+
+ public ClientDetails GetClientDetails(String clientId)
+ {
+ ClientDetails clientDetails = this.Clients.SingleOrDefault(c => c.ClientId == clientId);
+
+ clientDetails.ShouldNotBeNull();
+
+ return clientDetails;
+ }
+
+ public Dictionary EmailResponses;
+ }
+
+ public class ClientDetails
+ {
+ public String ClientId { get; private set; }
+ public String ClientSecret { get; private set; }
+ public String GrantType { get; private set; }
+
+ private ClientDetails(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ this.ClientId = clientId;
+ this.ClientSecret = clientSecret;
+ this.GrantType = grantType;
+ }
+
+ public static ClientDetails Create(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ return new ClientDetails(clientId, clientSecret, grantType);
+ }
+ }
+}
diff --git a/MessagingService.IntegrationTests/Email/SendEmail.feature b/MessagingService.IntegrationTests/Email/SendEmail.feature
new file mode 100644
index 0000000..72d30bd
--- /dev/null
+++ b/MessagingService.IntegrationTests/Email/SendEmail.feature
@@ -0,0 +1,22 @@
+@base @shared @email
+Feature: SendEmail
+
+Background:
+ Given the following api resources exist
+ | ResourceName | DisplayName | Secret | Scopes | UserClaims |
+ | messagingService | Messaging REST | Secret1 | messagingService | |
+
+ Given the following clients exist
+ | ClientId | ClientName | Secret | AllowedScopes | AllowedGrantTypes |
+ | serviceClient | Service Client | Secret1 | messagingService | client_credentials |
+
+ Given I have a token to access the messaging service resource
+ | ClientId |
+ | serviceClient |
+
+@PRTest
+Scenario: Send Email
+ Given I send the following Email Messages
+ | FromAddress | ToAddresses | Subject | Body | IsHtml |
+ | fromaddress@testemail.com | toaddress1@testemail.com | Test Email 1 | Test Body | true |
+ | fromaddress@testemail.com | toaddress1@testemail.com,toaddress2@testemail.com | Test Email 1 | Test Body | true |
diff --git a/MessagingService.IntegrationTests/Email/SendEmail.feature.cs b/MessagingService.IntegrationTests/Email/SendEmail.feature.cs
new file mode 100644
index 0000000..84b3cb7
--- /dev/null
+++ b/MessagingService.IntegrationTests/Email/SendEmail.feature.cs
@@ -0,0 +1,211 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by SpecFlow (http://www.specflow.org/).
+// SpecFlow Version:3.1.0.0
+// SpecFlow Generator Version:3.1.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+#region Designer generated code
+#pragma warning disable
+namespace MessagingService.IntegrationTests.Email
+{
+ using TechTalk.SpecFlow;
+ using System;
+ using System.Linq;
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [Xunit.TraitAttribute("Category", "base")]
+ [Xunit.TraitAttribute("Category", "shared")]
+ [Xunit.TraitAttribute("Category", "email")]
+ public partial class SendEmailFeature : object, Xunit.IClassFixture, System.IDisposable
+ {
+
+ private static TechTalk.SpecFlow.ITestRunner testRunner;
+
+ private string[] _featureTags = new string[] {
+ "base",
+ "shared",
+ "email"};
+
+ private Xunit.Abstractions.ITestOutputHelper _testOutputHelper;
+
+#line 1 "SendEmail.feature"
+#line hidden
+
+ public SendEmailFeature(SendEmailFeature.FixtureData fixtureData, MessagingService_IntegrationTests_XUnitAssemblyFixture assemblyFixture, Xunit.Abstractions.ITestOutputHelper testOutputHelper)
+ {
+ this._testOutputHelper = testOutputHelper;
+ this.TestInitialize();
+ }
+
+ public static void FeatureSetup()
+ {
+ testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
+ TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "SendEmail", null, ProgrammingLanguage.CSharp, new string[] {
+ "base",
+ "shared",
+ "email"});
+ testRunner.OnFeatureStart(featureInfo);
+ }
+
+ public static void FeatureTearDown()
+ {
+ testRunner.OnFeatureEnd();
+ testRunner = null;
+ }
+
+ public virtual void TestInitialize()
+ {
+ }
+
+ public virtual void TestTearDown()
+ {
+ testRunner.OnScenarioEnd();
+ }
+
+ public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
+ {
+ testRunner.OnScenarioInitialize(scenarioInfo);
+ testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(_testOutputHelper);
+ }
+
+ public virtual void ScenarioStart()
+ {
+ testRunner.OnScenarioStart();
+ }
+
+ public virtual void ScenarioCleanup()
+ {
+ testRunner.CollectScenarioErrors();
+ }
+
+ public virtual void FeatureBackground()
+ {
+#line 4
+#line hidden
+ TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
+ "ResourceName",
+ "DisplayName",
+ "Secret",
+ "Scopes",
+ "UserClaims"});
+ table1.AddRow(new string[] {
+ "messagingService",
+ "Messaging REST",
+ "Secret1",
+ "messagingService",
+ ""});
+#line 5
+ testRunner.Given("the following api resources exist", ((string)(null)), table1, "Given ");
+#line hidden
+ TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
+ "ClientId",
+ "ClientName",
+ "Secret",
+ "AllowedScopes",
+ "AllowedGrantTypes"});
+ table2.AddRow(new string[] {
+ "serviceClient",
+ "Service Client",
+ "Secret1",
+ "messagingService",
+ "client_credentials"});
+#line 9
+ testRunner.Given("the following clients exist", ((string)(null)), table2, "Given ");
+#line hidden
+ TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] {
+ "ClientId"});
+ table3.AddRow(new string[] {
+ "serviceClient"});
+#line 13
+ testRunner.Given("I have a token to access the messaging service resource", ((string)(null)), table3, "Given ");
+#line hidden
+ }
+
+ void System.IDisposable.Dispose()
+ {
+ this.TestTearDown();
+ }
+
+ [Xunit.SkippableFactAttribute(DisplayName="Send Email")]
+ [Xunit.TraitAttribute("FeatureTitle", "SendEmail")]
+ [Xunit.TraitAttribute("Description", "Send Email")]
+ [Xunit.TraitAttribute("Category", "PRTest")]
+ public virtual void SendEmail()
+ {
+ string[] tagsOfScenario = new string[] {
+ "PRTest"};
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Send Email", null, new string[] {
+ "PRTest"});
+#line 18
+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 table4 = new TechTalk.SpecFlow.Table(new string[] {
+ "FromAddress",
+ "ToAddresses",
+ "Subject",
+ "Body",
+ "IsHtml"});
+ table4.AddRow(new string[] {
+ "fromaddress@testemail.com",
+ "toaddress1@testemail.com",
+ "Test Email 1",
+ "Test Body",
+ "true"});
+ table4.AddRow(new string[] {
+ "fromaddress@testemail.com",
+ "toaddress1@testemail.com,toaddress2@testemail.com",
+ "Test Email 1",
+ "Test Body",
+ "true"});
+#line 19
+ testRunner.Given("I send the following Email Messages", ((string)(null)), table4, "Given ");
+#line hidden
+ }
+ this.ScenarioCleanup();
+ }
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class FixtureData : System.IDisposable
+ {
+
+ public FixtureData()
+ {
+ SendEmailFeature.FeatureSetup();
+ }
+
+ void System.IDisposable.Dispose()
+ {
+ SendEmailFeature.FeatureTearDown();
+ }
+ }
+ }
+}
+#pragma warning restore
+#endregion
diff --git a/MessagingService.IntegrationTests/Email/SendEmailSteps.cs b/MessagingService.IntegrationTests/Email/SendEmailSteps.cs
new file mode 100644
index 0000000..b1677a7
--- /dev/null
+++ b/MessagingService.IntegrationTests/Email/SendEmailSteps.cs
@@ -0,0 +1,73 @@
+using System;
+using TechTalk.SpecFlow;
+
+namespace MessagingService.IntegrationTests.Email
+{
+ using System.Linq;
+ using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Common;
+ using DataTransferObjects;
+ using Newtonsoft.Json;
+ using Shouldly;
+
+ [Binding]
+ [Scope(Tag = "email")]
+ public class SendEmailSteps
+ {
+ private readonly ScenarioContext ScenarioContext;
+
+ private readonly TestingContext TestingContext;
+
+ public SendEmailSteps(ScenarioContext scenarioContext,
+ TestingContext testingContext)
+ {
+ this.ScenarioContext = scenarioContext;
+ this.TestingContext = testingContext;
+ }
+
+ [Given(@"I send the following Email Messages")]
+ public async Task GivenISendTheFollowingEmailMessages(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ await this.SendEmail(tableRow);
+ }
+ }
+
+ private async Task SendEmail(TableRow tableRow)
+ {
+ String fromAddress = SpecflowTableHelper.GetStringRowValue(tableRow, "FromAddress");
+ String toAddresses = SpecflowTableHelper.GetStringRowValue(tableRow, "ToAddresses");
+ String subject = SpecflowTableHelper.GetStringRowValue(tableRow, "Subject");
+ String body = SpecflowTableHelper.GetStringRowValue(tableRow, "Body");
+ Boolean isHtml = SpecflowTableHelper.GetBooleanValue(tableRow, "IsHtml");
+
+ SendEmailRequest request = new SendEmailRequest
+ {
+ Body = body,
+ ConnectionIdentifier = Guid.NewGuid(),
+ FromAddress = fromAddress,
+ IsHtml = isHtml,
+ Subject = subject,
+ ToAddresses = toAddresses.Split(",").ToList()
+ };
+
+ StringContent requestContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
+ this.TestingContext.DockerHelper.MessagingServiceClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.TestingContext.AccessToken);
+
+ HttpResponseMessage httpResponse = await this.TestingContext.DockerHelper.MessagingServiceClient.PostAsync("api/Email", requestContent, CancellationToken.None).ConfigureAwait(false);
+
+ httpResponse.StatusCode.ShouldBe(HttpStatusCode.Created);
+ String responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
+
+ responseContent.ShouldNotBeNullOrEmpty();
+ SendEmailResponse response = JsonConvert.DeserializeObject(responseContent);
+ response.MessageId.ShouldNotBe(Guid.Empty);
+ }
+ }
+}
diff --git a/MessagingService.IntegrationTests/MessagingService.IntegrationTests.csproj b/MessagingService.IntegrationTests/MessagingService.IntegrationTests.csproj
new file mode 100644
index 0000000..4979514
--- /dev/null
+++ b/MessagingService.IntegrationTests/MessagingService.IntegrationTests.csproj
@@ -0,0 +1,47 @@
+
+
+
+ netcoreapp3.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+ SpecFlowSingleFileGenerator
+ SendEmail.feature.cs
+
+
+
+
+
+
+ True
+
+
+
+
diff --git a/MessagingService.IntegrationTests/Shared/SharedSteps.cs b/MessagingService.IntegrationTests/Shared/SharedSteps.cs
new file mode 100644
index 0000000..21c2b17
--- /dev/null
+++ b/MessagingService.IntegrationTests/Shared/SharedSteps.cs
@@ -0,0 +1,127 @@
+using MessagingService.IntegrationTests.Common;
+using SecurityService.DataTransferObjects.Requests;
+using SecurityService.DataTransferObjects.Responses;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TechTalk.SpecFlow;
+
+namespace MessagingService.IntegrationTests.Shared
+{
+ using SecurityService.DataTransferObjects;
+ using ClientDetails = Common.ClientDetails;
+
+ [Binding]
+ [Scope(Tag = "shared")]
+ public class SharedSteps
+ {
+ private readonly ScenarioContext ScenarioContext;
+
+ private readonly TestingContext TestingContext;
+
+ public SharedSteps(ScenarioContext scenarioContext,
+ TestingContext testingContext)
+ {
+ this.ScenarioContext = scenarioContext;
+ this.TestingContext = testingContext;
+ }
+
+ [Given(@"the following api resources exist")]
+ public async Task GivenTheFollowingApiResourcesExist(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String resourceName = SpecflowTableHelper.GetStringRowValue(tableRow, "ResourceName");
+ String displayName = SpecflowTableHelper.GetStringRowValue(tableRow, "DisplayName");
+ String secret = SpecflowTableHelper.GetStringRowValue(tableRow, "Secret");
+ String scopes = SpecflowTableHelper.GetStringRowValue(tableRow, "Scopes");
+ String userClaims = SpecflowTableHelper.GetStringRowValue(tableRow, "UserClaims");
+
+ List splitScopes = scopes.Split(",").ToList();
+ List splitUserClaims = userClaims.Split(",").ToList();
+
+ CreateApiResourceRequest createApiResourceRequest = new CreateApiResourceRequest
+ {
+ Description = String.Empty,
+ DisplayName = displayName,
+ Name = resourceName,
+ Scopes = new List(),
+ Secret = secret,
+ UserClaims = new List()
+ };
+ splitScopes.ForEach(a =>
+ {
+ createApiResourceRequest.Scopes.Add(a.Trim());
+ });
+ splitUserClaims.ForEach(a =>
+ {
+ createApiResourceRequest.UserClaims.Add(a.Trim());
+ });
+
+ CreateApiResourceResponse createApiResourceResponse = await this.TestingContext.DockerHelper.SecurityServiceClient.CreateApiResource(createApiResourceRequest, CancellationToken.None).ConfigureAwait(false);
+
+ createApiResourceResponse.ApiResourceName.ShouldBe(resourceName);
+ }
+ }
+
+ [Given(@"the following clients exist")]
+ public async Task GivenTheFollowingClientsExist(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String clientId = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientId");
+ String clientName = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientName");
+ String secret = SpecflowTableHelper.GetStringRowValue(tableRow, "Secret");
+ String allowedScopes = SpecflowTableHelper.GetStringRowValue(tableRow, "AllowedScopes");
+ String allowedGrantTypes = SpecflowTableHelper.GetStringRowValue(tableRow, "AllowedGrantTypes");
+
+ List splitAllowedScopes = allowedScopes.Split(",").ToList();
+ List splitAllowedGrantTypes = allowedGrantTypes.Split(",").ToList();
+
+ CreateClientRequest createClientRequest = new CreateClientRequest
+ {
+ Secret = secret,
+ AllowedGrantTypes = new List(),
+ AllowedScopes = new List(),
+ ClientDescription = String.Empty,
+ ClientId = clientId,
+ ClientName = clientName
+ };
+
+ splitAllowedScopes.ForEach(a => { createClientRequest.AllowedScopes.Add(a.Trim()); });
+ splitAllowedGrantTypes.ForEach(a => { createClientRequest.AllowedGrantTypes.Add(a.Trim()); });
+
+ CreateClientResponse createClientResponse = await this.TestingContext.DockerHelper.SecurityServiceClient
+ .CreateClient(createClientRequest, CancellationToken.None).ConfigureAwait(false);
+
+ createClientResponse.ClientId.ShouldBe(clientId);
+
+ this.TestingContext.AddClientDetails(clientId, secret, allowedGrantTypes);
+ }
+ }
+
+ [Given(@"I have a token to access the messaging service resource")]
+ public async Task GivenIHaveATokenToAccessTheMessagingServiceResource(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String clientId = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientId");
+
+ ClientDetails clientDetails = this.TestingContext.GetClientDetails(clientId);
+
+ if (clientDetails.GrantType == "client_credentials")
+ {
+ TokenResponse tokenResponse = await this.TestingContext.DockerHelper.SecurityServiceClient
+ .GetToken(clientId, clientDetails.ClientSecret, CancellationToken.None).ConfigureAwait(false);
+
+ this.TestingContext.AccessToken = tokenResponse.AccessToken;
+ }
+ }
+ }
+
+ }
+}
diff --git a/MessagingService.Tests/General/BootstrapperTests.cs b/MessagingService.Tests/General/BootstrapperTests.cs
index 56cfb30..fb08480 100644
--- a/MessagingService.Tests/General/BootstrapperTests.cs
+++ b/MessagingService.Tests/General/BootstrapperTests.cs
@@ -52,6 +52,7 @@ private IConfigurationRoot SetupMemoryConfiguration()
configuration.Add("AppSettings:ClientSecret", "clientSecret");
configuration.Add("AppSettings:EstateManagementApi", "http://localhost");
configuration.Add("AppSettings:SecurityService", "http://localhost");
+ configuration.Add("AppSettings:EmailProxy", "UnitTest");
builder.AddInMemoryCollection(configuration);
diff --git a/MessagingService.sln b/MessagingService.sln
index 95a8a12..caa08cd 100644
--- a/MessagingService.sln
+++ b/MessagingService.sln
@@ -17,13 +17,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.EmailMessa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.EmailMessage.DomainEvents", "MessagingService.EmailMessage.DomainEvents\MessagingService.EmailMessage.DomainEvents.csproj", "{689A531D-86EB-4656-81B3-4C6E569A7E4B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagingService.Tests", "MessagingService.Tests\MessagingService.Tests.csproj", "{1617DC31-CE88-49ED-A865-CE896E8D861D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.Tests", "MessagingService.Tests\MessagingService.Tests.csproj", "{1617DC31-CE88-49ED-A865-CE896E8D861D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagingService.Testing", "MessagingService.Testing\MessagingService.Testing.csproj", "{5CEAAA6F-2B2C-4C1F-B812-65E5ACC9002C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.Testing", "MessagingService.Testing\MessagingService.Testing.csproj", "{5CEAAA6F-2B2C-4C1F-B812-65E5ACC9002C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagingService.BusinessLogic.Tests", "MessagingService.BusinessLogic.Tests\MessagingService.BusinessLogic.Tests.csproj", "{17A755D6-96EE-46EB-8850-9641B8F1A5E1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.BusinessLogic.Tests", "MessagingService.BusinessLogic.Tests\MessagingService.BusinessLogic.Tests.csproj", "{17A755D6-96EE-46EB-8850-9641B8F1A5E1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagingService.EmailAggregate.Tests", "MessagingService.EmailAggregate.Tests\MessagingService.EmailAggregate.Tests.csproj", "{2FBED4B6-3096-4AD1-8436-247A59E0CDC2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagingService.EmailAggregate.Tests", "MessagingService.EmailAggregate.Tests\MessagingService.EmailAggregate.Tests.csproj", "{2FBED4B6-3096-4AD1-8436-247A59E0CDC2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagingService.IntegrationTests", "MessagingService.IntegrationTests\MessagingService.IntegrationTests.csproj", "{FEBD44D2-5B93-40D8-B59C-E5221570055D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -67,6 +69,10 @@ Global
{2FBED4B6-3096-4AD1-8436-247A59E0CDC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FBED4B6-3096-4AD1-8436-247A59E0CDC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FBED4B6-3096-4AD1-8436-247A59E0CDC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FEBD44D2-5B93-40D8-B59C-E5221570055D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FEBD44D2-5B93-40D8-B59C-E5221570055D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FEBD44D2-5B93-40D8-B59C-E5221570055D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FEBD44D2-5B93-40D8-B59C-E5221570055D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -81,6 +87,7 @@ Global
{5CEAAA6F-2B2C-4C1F-B812-65E5ACC9002C} = {9AEE6ADE-DD45-4605-A933-E06CF0BA4203}
{17A755D6-96EE-46EB-8850-9641B8F1A5E1} = {9AEE6ADE-DD45-4605-A933-E06CF0BA4203}
{2FBED4B6-3096-4AD1-8436-247A59E0CDC2} = {9AEE6ADE-DD45-4605-A933-E06CF0BA4203}
+ {FEBD44D2-5B93-40D8-B59C-E5221570055D} = {9AEE6ADE-DD45-4605-A933-E06CF0BA4203}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1929C0FE-8CEB-4D0E-BD22-9E5E16E2B49F}
diff --git a/MessagingService/Dockerfile b/MessagingService/Dockerfile
index fe4b032..8e9c518 100644
--- a/MessagingService/Dockerfile
+++ b/MessagingService/Dockerfile
@@ -2,11 +2,15 @@
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.1-buster AS build
WORKDIR /src
+COPY ["MessagingService/NuGet.Config", "."]
COPY ["MessagingService/MessagingService.csproj", "MessagingService/"]
+COPY ["MessagingService.BusinessLogic/MessagingService.BusinessLogic.csproj", "MessagingService.BusinessLogic/"]
+COPY ["MessagingService.EmailMessageAggregate/MessagingService.EmailMessageAggregate.csproj", "MessagingService.EmailMessageAggregate/"]
+COPY ["MessagingService.EmailMessage.DomainEvents/MessagingService.EmailMessage.DomainEvents.csproj", "MessagingService.EmailMessage.DomainEvents/"]
+COPY ["MessagingService.DataTransferObjects/MessagingService.DataTransferObjects.csproj", "MessagingService.DataTransferObjects/"]
RUN dotnet restore "MessagingService/MessagingService.csproj"
COPY . .
WORKDIR "/src/MessagingService"
diff --git a/MessagingService/NuGet.Config b/MessagingService/NuGet.Config
new file mode 100644
index 0000000..c6bc4ff
--- /dev/null
+++ b/MessagingService/NuGet.Config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MessagingService/Startup.cs b/MessagingService/Startup.cs
index b2cbd66..bf978dc 100644
--- a/MessagingService/Startup.cs
+++ b/MessagingService/Startup.cs
@@ -33,6 +33,7 @@ namespace MessagingService
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NLog.Extensions.Logging;
+ using Service.Services.Email.IntegrationTest;
using Shared.DomainDrivenDesign.EventStore;
using Shared.EntityFramework.ConnectionStringConfiguration;
using Shared.EventStore.EventStore;
@@ -129,7 +130,9 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton();
services.AddSingleton, AggregateRepository>();
services.AddSingleton();
- services.AddSingleton();
+
+ this.RegisterEmailProxy(services);
+
//services.AddSingleton();
//services.AddSingleton();
//services.AddSingleton();
@@ -149,6 +152,25 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton();
}
+ ///
+ /// Registers the email proxy.
+ ///
+ /// The services.
+ private void RegisterEmailProxy(IServiceCollection services)
+ {
+ // read the config setting
+ String emailProxy = ConfigurationReader.GetValue("AppSettings", "EmailProxy");
+
+ if (emailProxy == "Smtp2Go")
+ {
+ services.AddSingleton();
+ }
+ else
+ {
+ services.AddSingleton();
+ }
+ }
+
private void ConfigureMiddlewareServices(IServiceCollection services)
{
services.AddApiVersioning(
diff --git a/MessagingService/appsettings.json b/MessagingService/appsettings.json
index 0935e8a..746da91 100644
--- a/MessagingService/appsettings.json
+++ b/MessagingService/appsettings.json
@@ -20,6 +20,7 @@
"AppSettings": {
"UseConnectionStringConfig": false,
"SecurityService": "http://192.168.1.133:5001",
+ "EmailProxy": "Smtp2Go",
"SMTP2GoBaseAddress": "https://api.smtp2go.com/v3/",
"SMTP2GoAPIKey": "api-4CE2C6BC80D111EAB45BF23C91C88F4E"
},