("EventStoreSettings:ConnectionString");
- applicationBuilder.ConfigureSubscriptionService(subscriptionWorkersRoot,
- connectionString,
- eventHandlerResolvers,
- Extensions.log,
- subscriptionRepositoryResolver).Wait(CancellationToken.None);
+ IApplicationBuilder applicationBuilder = new ApplicationBuilder(services);
+ await applicationBuilder.ConfigureSubscriptionService(subscriptionWorkersRoot,
+ connectionString,
+ eventHandlerResolvers,
+ Extensions.log,
+ subscriptionRepositoryResolver);
}
-}
\ No newline at end of file
+}
diff --git a/MessagingService/Common/MessagingRequestExamplesOperationTransformer.cs b/MessagingService/Common/MessagingRequestExamplesOperationTransformer.cs
new file mode 100644
index 0000000..2556081
--- /dev/null
+++ b/MessagingService/Common/MessagingRequestExamplesOperationTransformer.cs
@@ -0,0 +1,74 @@
+using System.Text.Json.Nodes;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.OpenApi;
+using Microsoft.OpenApi;
+
+namespace MessagingService.Common;
+
+///
+/// Adds representative JSON request examples to the messaging endpoints.
+///
+internal sealed class MessagingRequestExamplesOperationTransformer : IOpenApiOperationTransformer
+{
+ private const string SendEmailExample = """
+ {
+ "messageId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
+ "body": "Your appointment is confirmed for tomorrow at 10:00.",
+ "connectionIdentifier": "a8098c1a-f86e-11da-bd1a-00112444be1e",
+ "fromAddress": "notifications@example.test",
+ "isHtml": false,
+ "subject": "Appointment confirmed",
+ "toAddresses": ["recipient@example.test"],
+ "emailAttachments": []
+ }
+ """;
+
+ private const string ResendEmailExample = """
+ {
+ "messageId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
+ "connectionIdentifier": "a8098c1a-f86e-11da-bd1a-00112444be1e"
+ }
+ """;
+
+ private const string SendSmsExample = """
+ {
+ "connectionIdentifier": "a8098c1a-f86e-11da-bd1a-00112444be1e",
+ "destination": "+447700900123",
+ "message": "Your appointment is confirmed for tomorrow at 10:00.",
+ "messageId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
+ "sender": "Example Service"
+ }
+ """;
+
+ private const string ResendSmsExample = """
+ {
+ "messageId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
+ "connectionIdentifier": "a8098c1a-f86e-11da-bd1a-00112444be1e"
+ }
+ """;
+
+ public Task TransformAsync(OpenApiOperation operation,
+ OpenApiOperationTransformerContext context,
+ CancellationToken cancellationToken)
+ {
+ string path = context.Description.RelativePath?.Trim('/') ?? string.Empty;
+ string? example = (context.Description.HttpMethod, path) switch
+ {
+ ("POST", "api/email") => SendEmailExample,
+ ("POST", "api/email/resend") => ResendEmailExample,
+ ("POST", "api/sms") => SendSmsExample,
+ ("POST", "api/sms/resend") => ResendSmsExample,
+ _ => null
+ };
+
+ if (example is not null
+ && operation.RequestBody is OpenApiRequestBody requestBody
+ && requestBody.Content.TryGetValue("application/json", out OpenApiMediaType? mediaType))
+ {
+ mediaType.Example = JsonNode.Parse(example);
+ }
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/MessagingService/Common/RequestExamples/ExampleData.cs b/MessagingService/Common/RequestExamples/ExampleData.cs
deleted file mode 100644
index 2595c2f..0000000
--- a/MessagingService/Common/RequestExamples/ExampleData.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-namespace MessagingService.Common.RequestExamples
-{
- using System;
- using System.Diagnostics.CodeAnalysis;
-
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- internal static class ExampleData
- {
- #region Fields
-
- ///
- /// The connection identifier
- ///
- internal static Guid ConnectionIdentifier = Guid.Parse("A6080219-E243-48F8-A6C3-D79610A74A5B");
-
- ///
- /// The email message from address
- ///
- internal static String EmailMessageFromAddress = "fromaddress@exampleemail.com";
-
- ///
- /// The email message HTML body
- ///
- internal static String EmailMessageHtmlBody = "This is a test message body
";
-
- ///
- /// The email message identifier
- ///
- internal static Guid EmailMessageId = Guid.Parse("63BDE20F-28E0-4698-AF46-923A08198994");
-
- ///
- /// The email message is HTML
- ///
- internal static Boolean EmailMessageIsHtml = true;
-
- ///
- /// The email message plain text body
- ///
- internal static String EmailMessagePlainTextBody = "This is a test message body";
-
- ///
- /// The email message plain text is HTML
- ///
- internal static Boolean EmailMessagePlainTextIsHtml = false;
-
- ///
- /// The email message subject
- ///
- internal static String EmailMessageSubject = "Email Subject";
-
- ///
- /// The email message to address1
- ///
- internal static String EmailMessageToAddress1 = "toaddress1@exampleemail.com";
-
- ///
- /// The email message to address2
- ///
- internal static String EmailMessageToAddress2 = "toaddress2@exampleemail.com";
-
- ///
- /// The SMS message destination
- ///
- internal static String SMSMessageDestination = "07123456789";
-
- ///
- /// The SMS message identifier
- ///
- internal static Guid SMSMessageId = Guid.Parse("D38E20B1-64F1-4217-B192-24123862FE10");
-
- ///
- /// The SMS message message
- ///
- internal static String SMSMessageMessage = "Test SMS Message";
-
- ///
- /// The SMS message sender
- ///
- internal static String SMSMessageSender = "07000000001";
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/RequestExamples/SendEmailRequestExample.cs b/MessagingService/Common/RequestExamples/SendEmailRequestExample.cs
deleted file mode 100644
index 8d0685f..0000000
--- a/MessagingService/Common/RequestExamples/SendEmailRequestExample.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-namespace MessagingService.Common.RequestExamples
-{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using DataTransferObjects;
- using Swashbuckle.AspNetCore.Filters;
-
- ///
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class SendEmailRequestExample : IMultipleExamplesProvider
- {
- #region Methods
-
- ///
- /// Gets the examples.
- ///
- ///
- public IEnumerable> GetExamples()
- {
- SendEmailRequest htmlEmailRequest = new()
- {
- Body = ExampleData.EmailMessageHtmlBody,
- ConnectionIdentifier = ExampleData.ConnectionIdentifier,
- FromAddress = ExampleData.EmailMessageFromAddress,
- ToAddresses = new List
- {
- ExampleData.EmailMessageToAddress1,
- ExampleData.EmailMessageToAddress2
- },
- IsHtml = ExampleData.EmailMessageIsHtml,
- MessageId = ExampleData.EmailMessageId,
- Subject = ExampleData.EmailMessageSubject
- };
-
- SendEmailRequest plainTextEmailRequest = new()
- {
- Body = ExampleData.EmailMessagePlainTextBody,
- ConnectionIdentifier = ExampleData.ConnectionIdentifier,
- FromAddress = ExampleData.EmailMessageFromAddress,
- ToAddresses = new List
- {
- ExampleData.EmailMessageToAddress1,
- ExampleData.EmailMessageToAddress2
- },
- IsHtml = ExampleData.EmailMessagePlainTextIsHtml,
- MessageId = ExampleData.EmailMessageId,
- Subject = ExampleData.EmailMessageSubject
- };
- List> examples = new();
- examples.Add(new SwaggerExample
- {
- Name = "Html Email Request",
- Value = htmlEmailRequest
- });
- examples.Add(new SwaggerExample
- {
- Name = "Plan Text Email Request",
- Value = plainTextEmailRequest
- });
- return examples;
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/RequestExamples/SendEmailResponseExample.cs b/MessagingService/Common/RequestExamples/SendEmailResponseExample.cs
deleted file mode 100644
index 86f9a81..0000000
--- a/MessagingService/Common/RequestExamples/SendEmailResponseExample.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace MessagingService.Common.RequestExamples
-{
- using System.Diagnostics.CodeAnalysis;
- using DataTransferObjects;
- using Swashbuckle.AspNetCore.Filters;
-
- ///
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class SendEmailResponseExample : IExamplesProvider
- {
- #region Methods
-
- ///
- /// Gets the examples.
- ///
- ///
- public SendEmailResponse GetExamples()
- {
- return new SendEmailResponse
- {
- MessageId = ExampleData.EmailMessageId
- };
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/RequestExamples/SendSMSRequestExample.cs b/MessagingService/Common/RequestExamples/SendSMSRequestExample.cs
deleted file mode 100644
index 09623a6..0000000
--- a/MessagingService/Common/RequestExamples/SendSMSRequestExample.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace MessagingService.Common.RequestExamples
-{
- using System.Diagnostics.CodeAnalysis;
- using DataTransferObjects;
- using Swashbuckle.AspNetCore.Filters;
-
- ///
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class SendSMSRequestExample : IExamplesProvider
- {
- ///
- /// Gets the examples.
- ///
- ///
- public SendSMSRequest GetExamples()
- {
- return new SendSMSRequest
- {
- ConnectionIdentifier = ExampleData.ConnectionIdentifier,
- MessageId = ExampleData.SMSMessageId,
- Destination = ExampleData.SMSMessageDestination,
- Message = ExampleData.SMSMessageMessage,
- Sender = ExampleData.SMSMessageSender
- };
- }
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/RequestExamples/SendSMSResponseExample.cs b/MessagingService/Common/RequestExamples/SendSMSResponseExample.cs
deleted file mode 100644
index 3c5a635..0000000
--- a/MessagingService/Common/RequestExamples/SendSMSResponseExample.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace MessagingService.Common.RequestExamples
-{
- using System.Diagnostics.CodeAnalysis;
- using DataTransferObjects;
- using Swashbuckle.AspNetCore.Filters;
-
- ///
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class SendSMSResponseExample : IExamplesProvider
- {
- #region Methods
-
- ///
- /// Gets the examples.
- ///
- ///
- public SendSMSResponse GetExamples()
- {
- return new SendSMSResponse
- {
- MessageId = ExampleData.SMSMessageId
- };
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/SubscriptionPreWarmHostedService.cs b/MessagingService/Common/SubscriptionPreWarmHostedService.cs
new file mode 100644
index 0000000..f9099b8
--- /dev/null
+++ b/MessagingService/Common/SubscriptionPreWarmHostedService.cs
@@ -0,0 +1,35 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MessagingService.Common;
+
+public sealed class SubscriptionPreWarmHostedService : IHostedLifecycleService
+{
+ private readonly IConfiguration configuration;
+ private readonly IServiceProvider services;
+
+ public SubscriptionPreWarmHostedService(IConfiguration configuration, IServiceProvider services)
+ {
+ this.configuration = configuration;
+ this.services = services;
+ }
+
+ public async Task StartingAsync(CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ await Extensions.PreWarm(this.configuration, this.services, cancellationToken);
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+}
diff --git a/MessagingService/Common/SwaggerDefaultValues.cs b/MessagingService/Common/SwaggerDefaultValues.cs
deleted file mode 100644
index 6e86ce3..0000000
--- a/MessagingService/Common/SwaggerDefaultValues.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Microsoft.OpenApi;
-
-namespace MessagingService.Common
-{
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using Microsoft.AspNetCore.Mvc.ApiExplorer;
- using Swashbuckle.AspNetCore.SwaggerGen;
-
- ///
- /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
- ///
- /// This is only required due to bugs in the .
- /// Once they are fixed and published, this class can be removed.
- [ExcludeFromCodeCoverage]
- public class SwaggerDefaultValues : IOperationFilter
- {
- #region Methods
-
- ///
- /// Applies the filter to the specified operation using the given context.
- ///
- /// The operation to apply the filter to.
- /// The current operation filter context.
- public void Apply(OpenApiOperation operation,
- OperationFilterContext context)
- {
- ApiDescription apiDescription = context.ApiDescription;
-
- if (operation.Parameters == null)
- {
- return;
- }
-
- foreach (OpenApiParameter parameter in operation.Parameters)
- {
- ApiParameterDescription description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);
-
- if (parameter.Description == null)
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
-
- parameter.Required |= description.IsRequired;
- }
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Common/SwaggerJsonConverter.cs b/MessagingService/Common/SwaggerJsonConverter.cs
deleted file mode 100644
index 03c4fd8..0000000
--- a/MessagingService/Common/SwaggerJsonConverter.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-namespace MessagingService.Common
-{
- using System;
- using System.Diagnostics.CodeAnalysis;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
-
- ///
- ///
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class SwaggerJsonConverter : JsonConverter
- {
- #region Properties
-
- ///
- /// Gets a value indicating whether this can read JSON.
- ///
- ///
- /// true if this can read JSON; otherwise, false.
- ///
- public override Boolean CanRead => false;
-
- #endregion
-
- #region Methods
-
- ///
- /// Determines whether this instance can convert the specified object type.
- ///
- /// Type of the object.
- ///
- /// true if this instance can convert the specified object type; otherwise, false.
- ///
- public override Boolean CanConvert(Type objectType)
- {
- return true;
- }
-
- ///
- /// Reads the JSON representation of the object.
- ///
- /// The to read from.
- /// Type of the object.
- /// The existing value of object being read.
- /// The calling serializer.
- ///
- /// The object value.
- ///
- ///
- ///
- public override Object ReadJson(JsonReader reader,
- Type objectType,
- Object existingValue,
- JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Writes the JSON representation of the object.
- ///
- /// The to write to.
- /// The value.
- /// The calling serializer.
- public override void WriteJson(JsonWriter writer,
- Object value,
- JsonSerializer serializer)
- {
- // Disable sending the $type in the serialized json
- serializer.TypeNameHandling = TypeNameHandling.None;
-
- JToken t = JToken.FromObject(value);
- t.WriteTo(writer);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/MessagingService/Endpoints/EmailEndpoints.cs b/MessagingService/Endpoints/EmailEndpoints.cs
index 94271fa..80f3675 100644
--- a/MessagingService/Endpoints/EmailEndpoints.cs
+++ b/MessagingService/Endpoints/EmailEndpoints.cs
@@ -21,12 +21,14 @@ public static IEndpointRouteBuilder MapEmailEndpoints(this IEndpointRouteBuilder
.RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy)
.WithName("Send Email")
.WithSummary("Sends an email")
+ .WithDescription("Submits an email for delivery. If messageId is omitted or null, the service generates a message identifier. Attachments may be included in the request.")
.WithStandardProduces(StatusCodes.Status201Created);
emailGroup.MapPost("/resend", EmailHandlers.ResendEmail)
.RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy)
.WithName("Resend Email")
.WithSummary("Resends an email by id")
+ .WithDescription("Requests delivery of an existing email message, identified by messageId and connectionIdentifier.")
.WithStandardProduces< ErrorResponse>(StatusCodes.Status202Accepted);
return endpoints;
diff --git a/MessagingService/Endpoints/SMSEndpoints.cs b/MessagingService/Endpoints/SMSEndpoints.cs
index 8ee3527..95716a5 100644
--- a/MessagingService/Endpoints/SMSEndpoints.cs
+++ b/MessagingService/Endpoints/SMSEndpoints.cs
@@ -20,14 +20,16 @@ public static IEndpointRouteBuilder MapSMSEndpoints(this IEndpointRouteBuilder e
.RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy)
.WithName("Send SMS")
.WithSummary("Sends an SMS message")
+ .WithDescription("Submits an SMS message for delivery. If messageId is omitted or null, the service generates a message identifier.")
.WithStandardProduces(StatusCodes.Status201Created);
smsGroup.MapPost("/resend", SMSHandlers.ResendSMS)
.RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy)
.WithName("Resend SMS")
.WithSummary("Resends an SMS message by id")
+ .WithDescription("Requests delivery of an existing SMS message, identified by messageId and connectionIdentifier.")
.WithStandardProduces< ErrorResponse>(StatusCodes.Status202Accepted);
return endpoints;
}
-}
\ No newline at end of file
+}
diff --git a/MessagingService/MessagingService.csproj b/MessagingService/MessagingService.csproj
index 5e58f1b..70616be 100644
--- a/MessagingService/MessagingService.csproj
+++ b/MessagingService/MessagingService.csproj
@@ -4,10 +4,8 @@
net10.0
Linux
true
-
-
-
- D:\Projects\StuartFerguson\Messaging\MessagingService\MessagingService.xml
+ true
+ $(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated
@@ -30,15 +28,10 @@
-
-
-
-
-
-
+
diff --git a/MessagingService/Program.cs b/MessagingService/Program.cs
index 98c2210..a0da357 100644
--- a/MessagingService/Program.cs
+++ b/MessagingService/Program.cs
@@ -1,117 +1,201 @@
+using Lamar;
+using Lamar.Microsoft.DependencyInjection;
+using MessagingService.Bootstrapper;
+using MessagingService.Common;
+using MessagingService.Endpoints;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration.Json;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using NLog;
using NLog.Extensions.Logging;
using Sentry.Extensibility;
+using Shared.Extensions;
+using Shared.General;
using Shared.Logger;
using Shared.Middleware;
+using Shared.Serialisation;
using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
+using TransactionProcessing.ApiDefaults;
-namespace MessagingService
+namespace MessagingService;
+
+[ExcludeFromCodeCoverage]
+public static class Program
{
- using Lamar.Microsoft.DependencyInjection;
- using NLog;
- using Shared.General;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
-
- [ExcludeFromCodeCoverage]
- public class Program
+ public static void Main(string[] args)
{
- public static void Main(string[] args)
+ ConfigureLogging();
+
+ string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
+ ?? AppContext.BaseDirectory;
+ WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
- Program.CreateHostBuilder(args).Build() .Run();
- }
+ Args = args,
+ ContentRootPath = assemblyDirectory
+ });
+
+ builder.Host.UseWindowsService();
+ builder.Logging.AddConsole();
+ builder.Logging.AddNLog();
+
+ ConfigureConfiguration(builder.Configuration, builder.Environment, assemblyDirectory);
+ ConfigurationReader.Initialise(builder.Configuration);
+ ConfigureSentry(builder);
+
+ builder.Services.AddHostedService();
- public static IHostBuilder CreateHostBuilder(string[] args)
+ builder.Host.UseLamar((_, services) =>
{
- //At this stage, we only need our hosting file for ip and ports
- FileInfo fi = new(System.Reflection.Assembly.GetExecutingAssembly().Location);
-
- ConfigureLogging();
-
- IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
- hostBuilder.UseWindowsService();
- hostBuilder.UseLamar();
- hostBuilder.ConfigureLogging(logging =>
- {
- logging.AddConsole();
- logging.AddNLog();
-
- });
- hostBuilder.ConfigureWebHostDefaults(webBuilder => { ConfigureWebHost(webBuilder, fi.Directory.FullName); });
-
-
- return hostBuilder;
- }
+ IConfiguration configuration = builder.Configuration;
+ services.IncludeRegistry(new MediatorRegistry());
+ services.IncludeRegistry(new RepositoryRegistry(configuration));
+ services.IncludeRegistry(new MiddlewareRegistry(configuration));
+ services.IncludeRegistry(new DomainServiceRegistry());
+ services.IncludeRegistry(new DomainEventHandlerRegistry(configuration));
+ services.IncludeRegistry(new MessagingProxyRegistry(configuration));
+ services.IncludeRegistry(new SerialiserRegistry());
+ });
+
+ WebApplication app = builder.Build();
+ ConfigureApplication(app);
+ app.Run();
+ }
- private static void ConfigureLogging() {
- String contentRoot = Directory.GetCurrentDirectory();
- String nlogConfigPath = Path.Combine(contentRoot, "nlog.config");
+ private static void ConfigureLogging()
+ {
+ string nlogConfigPath = Path.Combine(Directory.GetCurrentDirectory(), "nlog.config");
- LogManager.Setup(b =>
+ LogManager.Setup(b =>
+ {
+ b.SetupLogFactory(setup =>
{
- b.SetupLogFactory(setup =>
- {
- setup.AddCallSiteHiddenAssembly(typeof(NlogLogger).Assembly);
- setup.AddCallSiteHiddenAssembly(typeof(Shared.Logger.Logger).Assembly);
- setup.AddCallSiteHiddenAssembly(typeof(TenantMiddleware).Assembly);
- });
- b.LoadConfigurationFromFile(nlogConfigPath);
+ setup.AddCallSiteHiddenAssembly(typeof(NlogLogger).Assembly);
+ setup.AddCallSiteHiddenAssembly(typeof(Shared.Logger.Logger).Assembly);
+ setup.AddCallSiteHiddenAssembly(typeof(TenantMiddleware).Assembly);
});
+ b.LoadConfigurationFromFile(nlogConfigPath);
+ });
+ }
+
+ private static void ConfigureConfiguration(IConfigurationManager configuration,
+ IHostEnvironment environment,
+ string basePath)
+ {
+ RemoveDefaultAppSettingsSources(configuration, environment.EnvironmentName);
+
+ configuration.SetBasePath(basePath)
+ .AddJsonFile("hosting.json", optional: true)
+ .AddJsonFile($"hosting.{environment.EnvironmentName}.json", optional: true)
+ .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true, reloadOnChange: true)
+ .AddJsonFile($"/home/txnproc/config/appsettings.{environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
+ .AddJsonFile("/home/txnproc/config/appsettings.local.json", optional: true, reloadOnChange: true)
+ .AddEnvironmentVariables()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["HealthMonitoring:Service:Version"] = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0"
+ });
+ }
+
+ private static void RemoveDefaultAppSettingsSources(IConfigurationManager configuration,
+ string environmentName)
+ {
+ string environmentSettingsPath = $"appsettings.{environmentName}.json";
+
+ for (int index = configuration.Sources.Count - 1; index >= 0; index--)
+ {
+ if (configuration.Sources[index] is JsonConfigurationSource jsonSource
+ && (jsonSource.Path == "appsettings.json" || jsonSource.Path == environmentSettingsPath))
+ {
+ configuration.Sources.RemoveAt(index);
+ }
}
+ }
+
+ private static void ConfigureSentry(WebApplicationBuilder builder)
+ {
+ IConfiguration configuration = builder.Configuration;
+ IHostEnvironment environment = builder.Environment;
+ IConfigurationSection sentrySection = configuration.GetSection("SentryConfiguration");
- private static void ConfigureWebHost(IWebHostBuilder webBuilder,
- String basePath) {
- webBuilder.ConfigureAppConfiguration((context, configBuilder) =>
+ if (sentrySection.Exists() && environment.IsDevelopment() == false)
+ {
+ builder.WebHost.UseSentry(options =>
{
- var env = context.HostingEnvironment;
-
- configBuilder.SetBasePath(basePath)
- .AddJsonFile("hosting.json", optional: true)
- .AddJsonFile($"hosting.{env.EnvironmentName}.json", optional: true)
- .AddJsonFile("/home/txnproc/config/appsettings.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"/home/txnproc/config/appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"/home/txnproc/config/appsettings.local.json", optional: true, reloadOnChange: true)
- .AddEnvironmentVariables().AddInMemoryCollection(new Dictionary
- {
- ["HealthMonitoring:Service:Version"] = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0"
- });
-
- // Build a snapshot of configuration so we can use it immediately (e.g. for Sentry)
- var builtConfig = configBuilder.Build();
-
- // Keep existing static usage (if you must), and initialise the ConfigurationReader now.
- Startup.Configuration = builtConfig;
- ConfigurationReader.Initialise(Startup.Configuration);
-
- // Configure Sentry on the webBuilder using the config snapshot.
- var sentrySection = builtConfig.GetSection("SentryConfiguration");
- if (sentrySection.Exists())
- {
- // Replace the condition below if you intended to only enable Sentry in certain environments.
- if (env.IsDevelopment() == false)
- {
- webBuilder.UseSentry(o =>
- {
- o.Dsn = builtConfig["SentryConfiguration:Dsn"];
- o.SendDefaultPii = true;
- o.MaxRequestBodySize = RequestSize.Always;
- o.CaptureBlockingCalls = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "CaptureBlockingCalls", false);
- o.IncludeActivityData = ConfigurationReader.GetValueOrDefault("SentryConfiguration", "IncludeActivityData", false);
- o.Release = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
- });
- }
- }
+ options.Dsn = configuration["SentryConfiguration:Dsn"];
+ options.SendDefaultPii = true;
+ options.MaxRequestBodySize = RequestSize.Always;
+ options.CaptureBlockingCalls = configuration.GetValue("SentryConfiguration:CaptureBlockingCalls", false);
+ options.IncludeActivityData = configuration.GetValue("SentryConfiguration:IncludeActivityData", false);
+ options.Release = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
});
+ }
+ }
+
+ private static void ConfigureApplication(WebApplication app)
+ {
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ Microsoft.Extensions.Logging.ILogger logger = app.Services.GetRequiredService().CreateLogger("MessagingService");
+ Shared.Logger.Logger.Initialise(logger);
+ ((IConfigurationRoot)app.Configuration).LogConfiguration(Shared.Logger.Logger.LogWarning);
- webBuilder.UseStartup();
- webBuilder.UseKestrel();
+ IStringSerialiser serialiser = app.Services.GetRequiredService();
+ StringSerialiser.Initialise(serialiser);
+
+ foreach (KeyValuePair type in TypeMap.Map)
+ {
+ Shared.Logger.Logger.LogInformation($"Type name {type.Value} mapped to {type.Key.Name}");
}
+
+ app.UseMiddleware();
+ app.AddRequestResponseLogging();
+ app.AddExceptionHandler();
+
+ app.UseRouting();
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ app.MapEmailEndpoints();
+ app.MapSMSEndpoints();
+ app.MapDomainEventEndpoint();
+
+ app.MapHealthChecks("health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
+ {
+ Predicate = _ => true,
+ ResponseWriter = Shared.HealthChecks.HealthCheckMiddleware.WriteResponse
+ })
+ .WithName("HealthCheck")
+ .WithMetadata(
+ new EndpointSummaryMetadata("Checks the health of the Messaging Service"),
+ new EndpointDescriptionMetadata("Returns the health status of the service and its configured dependency checks."));
+ app.MapHealthChecks("healthui", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
+ {
+ Predicate = _ => true,
+ ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse
+ })
+ .WithName("HealthCheckUi")
+ .WithMetadata(
+ new EndpointSummaryMetadata("Returns health details for the health-check UI"),
+ new EndpointDescriptionMetadata("Returns the health-check data formatted for the health monitoring UI."));
+
+ app.UseApiDefaults();
}
+
+ private sealed record EndpointSummaryMetadata(string Summary) : IEndpointSummaryMetadata;
+
+ private sealed record EndpointDescriptionMetadata(string Description) : IEndpointDescriptionMetadata;
}
diff --git a/MessagingService/Startup.cs b/MessagingService/Startup.cs
deleted file mode 100644
index 1f19dd0..0000000
--- a/MessagingService/Startup.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using MessagingService.Endpoints;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-
-namespace MessagingService
-{
- using Bootstrapper;
- using Common;
- using HealthChecks.UI.Client;
- using Lamar;
- using Microsoft.AspNetCore.Diagnostics.HealthChecks;
- using Shared.Extensions;
- using Shared.General;
- using Shared.Middleware;
- using Shared.Serialisation;
- using System.Diagnostics.CodeAnalysis;
-
- [ExcludeFromCodeCoverage]
- public class Startup
- {
- public Startup(IWebHostEnvironment webHostEnvironment)
- {
- Startup.WebHostEnvironment = webHostEnvironment;
- }
-
- public static IConfigurationRoot Configuration { get; set; }
-
- public static IWebHostEnvironment WebHostEnvironment { get; set; }
-
- public static Container Container;
-
- public void ConfigureContainer(ServiceRegistry services)
- {
- ConfigurationReader.Initialise(Startup.Configuration);
-
- services.IncludeRegistry();
- services.IncludeRegistry();
- services.IncludeRegistry();
- services.IncludeRegistry();
- services.IncludeRegistry();
- services.IncludeRegistry();
- services.IncludeRegistry();
-
- Startup.Container = new Container(services);
-
- var serialiser = Container.GetRequiredService();
- StringSerialiser.Initialise(serialiser);
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory,
- IHostApplicationLifetime lifetime, IHost host)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- Microsoft.Extensions.Logging.ILogger logger = loggerFactory.CreateLogger("MessagingService");
-
- Shared.Logger.Logger.Initialise(logger);
- Startup.Configuration.LogConfiguration(Shared.Logger.Logger.LogWarning);
-
- foreach (KeyValuePair type in TypeMap.Map)
- {
- Shared.Logger.Logger.LogInformation($"Type name {type.Value} mapped to {type.Key.Name}");
- }
-
- ConfigurationReader.Initialise(Startup.Configuration);
- app.UseMiddleware();
- app.AddRequestResponseLogging();
- app.AddExceptionHandler();
-
- app.UseRouting();
- app.UseAuthentication();
- app.UseAuthorization();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapEmailEndpoints();
- endpoints.MapSMSEndpoints();
- endpoints.MapDomainEventEndpoint();
-
- endpoints.MapHealthChecks("health", new HealthCheckOptions()
- {
- Predicate = _ => true,
- ResponseWriter = Shared.HealthChecks.HealthCheckMiddleware.WriteResponse
- });
- endpoints.MapHealthChecks("healthui", new HealthCheckOptions()
- {
- Predicate = _ => true,
- ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
- });
- });
- app.UseSwagger();
-
- app.UseSwaggerUI();
-
- app.PreWarm();
- }
- }
-}