From 31c7ce85cd4228ddbb2e42ba97add264169e4922 Mon Sep 17 00:00:00 2001 From: Jezz Santos Date: Mon, 25 Dec 2023 18:05:07 +1300 Subject: [PATCH] Added HostRecorder (part 1) --- .../AncillaryApplication.UnitTests.csproj | 7 +- .../AncillaryApplicationSpec.cs | 3 +- .../AncillaryApplication.cs | 3 +- .../AncillaryApplication.csproj | 1 + .../IAncillaryApplication.cs | 2 +- .../IRecordingApplication.cs | 15 + .../RecordingApplication.cs | 43 +++ .../AncillaryDomain.UnitTests.csproj | 7 +- src/AncillaryDomain/Validations.cs | 11 + ...laryInfrastructure.IntegrationTests.csproj | 6 +- .../AuditsApiSpec.cs | 1 - .../RecordingApiSpec.cs | 89 ++++++ .../Stubs/StubRecorder.cs | 149 +++++++++ .../Stubs/StubUsageReportingService.cs | 2 +- .../UsagesApiSpec.cs | 1 - .../AncillaryInfrastructure.UnitTests.csproj | 8 +- .../Api/AdditionalValidatorSpec.cs | 83 +++++ .../DeliverAuditRequestValidatorSpec.cs | 2 +- .../RecordMeasureRequestValidatorSpec.cs | 48 +++ .../RecordUsageRequestValidatorSpec.cs | 48 +++ .../DeliverUsageRequestValidatorSpec.cs | 2 +- .../AncillaryInfrastructure.csproj | 1 + .../AncillaryModule.cs | 6 +- .../Api/AdditionalValidator.cs | 32 ++ .../Api/Audits/AuditsApi.cs | 9 +- .../Audits/DeliverAuditRequestValidator.cs | 2 +- .../RecordMeasureRequestValidator.cs | 18 ++ .../Recording/RecordUsageRequestValidator.cs | 18 ++ .../Api/Recording/RecordingApi.cs | 39 +++ .../Usages/DeliverUsageRequestValidator.cs | 2 +- .../Api/Usages/UsagesApi.cs | 7 +- .../NullUsageReportingService.cs | 2 +- .../Resources.Designer.cs | 31 +- src/AncillaryInfrastructure/Resources.resx | 11 +- src/ApiHost1/Program.cs | 2 +- .../Application.Common.UnitTests.csproj | 6 +- .../Application.Interfaces.UnitTests.csproj | 6 +- .../Application.Interfaces.csproj | 2 +- .../{IApiHostSetting.cs => IHostSetting.cs} | 2 +- src/Application.Interfaces/UsageConstants.cs | 6 + ...cation.Persistence.Common.UnitTests.csproj | 6 +- .../Application.Persistence.Shared.csproj | 5 + .../IAuditMessageQueueRepository.cs | 3 +- .../IUsageMessageQueueRepository.cs | 3 +- .../IUsageReportingService.cs | 2 +- .../UsageMessage.cs | 2 +- .../Application.Services.Shared.csproj | 1 - .../AzureFunctions.Api.WorkerHost.csproj | 2 +- .../HostExtensions.cs | 13 +- .../BookingsApplication.UnitTests.csproj | 9 +- .../BookingsDomain.UnitTests.csproj | 8 +- ...ingsInfrastructure.IntegrationTests.csproj | 7 +- .../BookingsInfrastructure.UnitTests.csproj | 6 +- src/BookingsInfrastructure/BookingsModule.cs | 2 +- .../CarsApplication.UnitTests.csproj | 7 +- .../CarsDomain.UnitTests.csproj | 7 +- ...CarsInfrastructure.IntegrationTests.csproj | 7 +- .../CarsInfrastructure.UnitTests.csproj | 8 +- .../CarsHttpServiceClient.cs | 4 +- src/CarsInfrastructure/CarsModule.cs | 2 +- src/Common.UnitTests/Common.UnitTests.csproj | 7 +- .../Domain.Common.UnitTests.csproj | 6 +- .../Domain.Interfaces.UnitTests.csproj | 8 +- .../Domain.Shared.UnitTests.csproj | 1 - .../Infrastructure.Common.UnitTests.csproj | 6 +- .../Infrastructure.Common.csproj | 4 + .../ApplicationInsightsCrashReporter.cs | 2 - .../ApplicationInsightsMetricReporter.cs | 33 ++ .../Recording/ForwardToAncillaryApiMetrics.cs | 48 +++ .../Recording/ForwardToAncillaryApiUsages.cs | 49 +++ .../Recording/QueuedAuditReporter.cs | 69 +++++ .../Recording/QueuedUsageReporter.cs | 73 +++++ .../Recording/RecorderOptions.cs | 53 +++- ...structure.Eventing.Common.UnitTests.csproj | 7 +- .../AspNetConfigurationSettingsSpec.cs | 3 +- .../Eventing/EventHandlerBaseSpec.cs | 4 +- ...ProcessSynchronousNotificationRelaySpec.cs | 4 +- .../Eventing/Notifications/TestConsumer.cs | 2 +- ...InProcessSynchronousProjectionRelaySpec.cs | 4 +- .../Eventing/Projections/TestProjection.cs | 2 +- .../ApplicationServices/Eventing/TestEvent.cs | 2 +- .../Eventing/TestEventingAggregateRoot.cs | 2 +- .../ApplicationServices/HostSettingsSpec.cs} | 16 +- .../ServiceCollectionExtensionsSpec.cs | 6 +- ...astructure.Hosting.Common.UnitTests.csproj | 18 ++ .../Recording/HostRecorderSpec.cs | 127 ++++++++ .../Recording/MockLogger.cs | 38 +++ .../Eventing/EventStreamHandlerBase.cs | 2 +- .../InProcessSynchronousNotificationRelay.cs | 2 +- .../InProcessSynchronousProjectionRelay.cs | 2 +- .../AspNetConfigurationSettings.cs | 2 +- .../Extensions/DependencyExtensions.cs | 5 +- .../Extensions/EventingExtensions.cs | 50 +-- .../Extensions/ServiceCollectionExtensions.cs | 63 ++-- .../Extensions/ServiceProviderExtensions.cs | 2 +- .../HostOptions.cs | 82 +++++ .../HostSettings.cs} | 12 +- .../Infrastructure.Hosting.Common.csproj | 43 +++ .../Recording/HostRecorder.cs | 289 ++++++++++++++++++ .../Recording}/TracingOnlyRecorder.cs | 2 +- .../Resources.Designer.cs | 125 ++++++++ .../Resources.resx | 47 +++ .../Infrastructure.Interfaces.csproj | 5 - ...ucture.Persistence.Common.UnitTests.csproj | 8 +- .../InProcessInMemStore.IBlobStore.cs | 2 +- .../InProcessInMemStore.IDataStore.cs | 2 +- .../InProcessInMemStore.IEventStore.cs | 3 +- .../InProcessInMemStore.IQueueStore.cs | 2 +- .../InProcessInMemStore.cs | 2 +- .../LocalMachineJsonFileStore.IBlobStore.cs | 12 +- .../LocalMachineJsonFileStore.IDataStore.cs | 2 +- .../LocalMachineJsonFileStore.IEventStore.cs | 3 +- .../LocalMachineJsonFileStore.IQueueStore.cs | 2 +- .../LocalMachineJsonFileStore.cs | 2 +- .../Resources.Designer.cs | 81 +++++ .../Resources.resx | 27 ++ ...re.Persistence.Interfaces.UnitTests.csproj | 7 +- .../InProcessInMemStoreSpec.cs | 2 +- ...Persistence.Shared.IntegrationTests.csproj | 3 +- .../LocalMachineJsonFileStoreSpec.cs | 2 +- .../StoreSpecSetupBase.cs | 2 +- .../AuditMessageQueueRepository.cs | 5 +- .../UsageMessageQueueRepository.cs | 5 +- .../Infrastructure.Persistence.Shared.csproj | 1 + .../Resources.Designer.cs | 63 ---- .../Resources.resx | 21 -- ...astructure.Web.Api.Common.UnitTests.csproj | 6 +- .../Extensions/RequestExtensions.cs | 27 ++ .../Extensions/WebApplicationExtensions.cs | 78 +++++ .../Infrastructure.Web.Api.Common.csproj | 3 +- ...astructure.Web.Api.IntegrationTests.csproj | 6 +- .../Ancillary/RecordMeasureRequest.cs | 11 + .../Ancillary/RecordUsageRequest.cs | 11 + .../ResponseProblemExtensionsSpec.cs | 7 +- ...Infrastructure.Web.Common.UnitTests.csproj | 17 ++ .../Clients/ApiClientRetryPolicies.cs | 2 +- .../Clients/InterHostServiceClient.cs | 4 +- .../Clients/JsonClient.cs | 7 +- .../Extensions/ResponseProblemExtensions.cs | 7 +- .../Infrastructure.Web.Common.csproj | 24 ++ ...ucture.Web.Hosting.Common.UnitTests.csproj | 12 +- .../StubQueueDrainingService.cs | 6 +- .../Extensions/HostExtensions.cs | 24 +- .../Infrastructure.Web.Hosting.Common.csproj | 6 +- .../Resources.Designer.cs | 63 ---- .../Resources.resx | 21 -- .../WebHostOptions.cs | 51 ++-- .../Clients/IHttpJsonClient.cs | 4 +- .../Clients/IServiceClient.cs | 3 +- .../Clients/JsonResponse.cs | 5 +- .../Clients/ResponseProblem.cs | 4 +- .../Infrastructure.Web.Interfaces.csproj | 18 ++ .../ApiWorkerSpec.cs | 2 +- .../AzureFunctions/AzureFunctionHostSetup.cs | 11 +- .../AzureFunctions/AzureStorageAccountBase.cs | 2 +- .../AzureFunctionsApiSpec.cs | 8 +- ...ucture.Worker.Api.IntegrationTests.csproj} | 1 - .../Stubs/StubServiceClient.cs | 4 +- .../appsettings.Testing.json | 0 .../CrashTraceOnlyRecorder.cs | 2 +- .../Infrastructure.Workers.Api.csproj | 3 +- .../ServiceClientExtensions.cs | 3 +- .../Workers/DeliverAuditRelayWorker.cs | 6 +- .../Workers/DeliverUsageRelayWorker.cs | 6 +- .../IntegrationTesting.WebApi.Common.csproj | 2 +- .../WebApiSpec.cs | 4 +- src/SaaStack.sln | 47 ++- .../TestingStubApiHost.csproj | 2 +- .../Tools.Analyzers.Platform.UnitTests.csproj | 12 +- ...Tools.Analyzers.Subdomain.UnitTests.csproj | 14 +- .../Tools.Generators.WebApi.UnitTests.csproj | 10 +- .../IntegrationTestProject/ProjectName.csproj | 6 +- .../UnitTestProject/ProjectName.csproj | 6 +- 173 files changed, 2428 insertions(+), 573 deletions(-) create mode 100644 src/AncillaryApplication/IRecordingApplication.cs create mode 100644 src/AncillaryApplication/RecordingApplication.cs create mode 100644 src/AncillaryDomain/Validations.cs create mode 100644 src/AncillaryInfrastructure.IntegrationTests/RecordingApiSpec.cs create mode 100644 src/AncillaryInfrastructure.IntegrationTests/Stubs/StubRecorder.cs create mode 100644 src/AncillaryInfrastructure.UnitTests/Api/AdditionalValidatorSpec.cs create mode 100644 src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordMeasureRequestValidatorSpec.cs create mode 100644 src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordUsageRequestValidatorSpec.cs create mode 100644 src/AncillaryInfrastructure/Api/AdditionalValidator.cs create mode 100644 src/AncillaryInfrastructure/Api/Recording/RecordMeasureRequestValidator.cs create mode 100644 src/AncillaryInfrastructure/Api/Recording/RecordUsageRequestValidator.cs create mode 100644 src/AncillaryInfrastructure/Api/Recording/RecordingApi.cs rename src/Application.Interfaces/Services/{IApiHostSetting.cs => IHostSetting.cs} (93%) rename src/{Application.Services.Shared => Application.Persistence.Shared}/IAuditMessageQueueRepository.cs (76%) rename src/{Application.Services.Shared => Application.Persistence.Shared}/IUsageMessageQueueRepository.cs (76%) rename src/{Application.Services.Shared => Application.Persistence.Shared}/IUsageReportingService.cs (91%) create mode 100644 src/Infrastructure.Common/Recording/ApplicationInsightsMetricReporter.cs create mode 100644 src/Infrastructure.Common/Recording/ForwardToAncillaryApiMetrics.cs create mode 100644 src/Infrastructure.Common/Recording/ForwardToAncillaryApiUsages.cs create mode 100644 src/Infrastructure.Common/Recording/QueuedAuditReporter.cs create mode 100644 src/Infrastructure.Common/Recording/QueuedUsageReporter.cs rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/AspNetConfigurationSettingsSpec.cs (98%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/EventHandlerBaseSpec.cs (97%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs (93%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/Notifications/TestConsumer.cs (84%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs (94%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/Projections/TestProjection.cs (86%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/TestEvent.cs (72%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/ApplicationServices/Eventing/TestEventingAggregateRoot.cs (96%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/ApiHostSettingsSpec.cs => Infrastructure.Hosting.Common.UnitTests/ApplicationServices/HostSettingsSpec.cs} (63%) rename src/{Infrastructure.Web.Hosting.Common.UnitTests => Infrastructure.Hosting.Common.UnitTests}/Extensions/ServiceCollectionExtensionsSpec.cs (97%) create mode 100644 src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj create mode 100644 src/Infrastructure.Hosting.Common.UnitTests/Recording/HostRecorderSpec.cs create mode 100644 src/Infrastructure.Hosting.Common.UnitTests/Recording/MockLogger.cs rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/ApplicationServices/Eventing/EventStreamHandlerBase.cs (98%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs (94%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs (94%) rename src/{Infrastructure.Web.Hosting.Common/ApplicationServices => Infrastructure.Hosting.Common}/AspNetConfigurationSettings.cs (98%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/Extensions/DependencyExtensions.cs (71%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/Extensions/EventingExtensions.cs (84%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/Extensions/ServiceCollectionExtensions.cs (81%) rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common}/Extensions/ServiceProviderExtensions.cs (97%) create mode 100644 src/Infrastructure.Hosting.Common/HostOptions.cs rename src/{Infrastructure.Web.Hosting.Common/ApiHostSettings.cs => Infrastructure.Hosting.Common/HostSettings.cs} (75%) create mode 100644 src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj create mode 100644 src/Infrastructure.Hosting.Common/Recording/HostRecorder.cs rename src/{Infrastructure.Web.Hosting.Common => Infrastructure.Hosting.Common/Recording}/TracingOnlyRecorder.cs (99%) create mode 100644 src/Infrastructure.Hosting.Common/Resources.Designer.cs create mode 100644 src/Infrastructure.Hosting.Common/Resources.resx rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/InProcessInMemStore.IBlobStore.cs (98%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/InProcessInMemStore.IDataStore.cs (98%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/InProcessInMemStore.IEventStore.cs (97%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/InProcessInMemStore.IQueueStore.cs (98%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/InProcessInMemStore.cs (94%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs (94%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs (99%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs (97%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs (98%) rename src/{Infrastructure.Persistence.Shared => Infrastructure.Persistence.Common}/ApplicationServices/LocalMachineJsonFileStore.cs (99%) rename src/{AncillaryInfrastructure/Persistence => Infrastructure.Persistence.Shared/ApplicationServices}/AuditMessageQueueRepository.cs (90%) rename src/{AncillaryInfrastructure/Persistence => Infrastructure.Persistence.Shared/ApplicationServices}/UsageMessageQueueRepository.cs (90%) create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordMeasureRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordUsageRequest.cs rename src/{Infrastructure.Web.Api.Common.UnitTests => Infrastructure.Web.Common.UnitTests}/Extensions/ResponseProblemExtensionsSpec.cs (97%) create mode 100644 src/Infrastructure.Web.Common.UnitTests/Infrastructure.Web.Common.UnitTests.csproj rename src/{Infrastructure.Web.Api.Common => Infrastructure.Web.Common}/Clients/ApiClientRetryPolicies.cs (96%) rename src/{Infrastructure.Web.Api.Common => Infrastructure.Web.Common}/Clients/InterHostServiceClient.cs (98%) rename src/{Infrastructure.Web.Api.Common => Infrastructure.Web.Common}/Clients/JsonClient.cs (97%) rename src/{Infrastructure.Web.Api.Common => Infrastructure.Web.Common}/Extensions/ResponseProblemExtensions.cs (94%) create mode 100644 src/Infrastructure.Web.Common/Infrastructure.Web.Common.csproj rename src/{Infrastructure.Web.Api.Interfaces => Infrastructure.Web.Interfaces}/Clients/IHttpJsonClient.cs (95%) rename src/{Infrastructure.Web.Api.Interfaces => Infrastructure.Web.Interfaces}/Clients/IServiceClient.cs (95%) rename src/{Infrastructure.Web.Api.Interfaces => Infrastructure.Web.Interfaces}/Clients/JsonResponse.cs (78%) rename src/{Infrastructure.Web.Api.Interfaces => Infrastructure.Web.Interfaces}/Clients/ResponseProblem.cs (81%) create mode 100644 src/Infrastructure.Web.Interfaces/Infrastructure.Web.Interfaces.csproj rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/ApiWorkerSpec.cs (95%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/AzureFunctions/AzureFunctionHostSetup.cs (93%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/AzureFunctions/AzureStorageAccountBase.cs (79%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/AzureFunctionsApiSpec.cs (95%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests/Infrastructure.Api.WorkerHost.IntegrationTests.csproj => Infrastructure.Worker.Api.IntegrationTests/Infrastructure.Worker.Api.IntegrationTests.csproj} (89%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/Stubs/StubServiceClient.cs (95%) rename src/{Infrastructure.Api.WorkerHost.IntegrationTests => Infrastructure.Worker.Api.IntegrationTests}/appsettings.Testing.json (100%) diff --git a/src/AncillaryApplication.UnitTests/AncillaryApplication.UnitTests.csproj b/src/AncillaryApplication.UnitTests/AncillaryApplication.UnitTests.csproj index 23e26c67..23b3bbf6 100644 --- a/src/AncillaryApplication.UnitTests/AncillaryApplication.UnitTests.csproj +++ b/src/AncillaryApplication.UnitTests/AncillaryApplication.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/AncillaryApplication.UnitTests/AncillaryApplicationSpec.cs b/src/AncillaryApplication.UnitTests/AncillaryApplicationSpec.cs index 747d5611..6a915bbd 100644 --- a/src/AncillaryApplication.UnitTests/AncillaryApplicationSpec.cs +++ b/src/AncillaryApplication.UnitTests/AncillaryApplicationSpec.cs @@ -2,7 +2,6 @@ using AncillaryDomain; using Application.Interfaces; using Application.Persistence.Shared; -using Application.Services.Shared; using Common; using Common.Extensions; using Domain.Common.Identity; @@ -98,7 +97,7 @@ public async Task WhenDeliverUsageAsync_ThenDelivers() { ForId = "aforid", EventName = "aneventname", - Context = new Dictionary + Additional = new Dictionary { { "aname", "avalue" } } diff --git a/src/AncillaryApplication/AncillaryApplication.cs b/src/AncillaryApplication/AncillaryApplication.cs index 32bbd0d3..8498ce05 100644 --- a/src/AncillaryApplication/AncillaryApplication.cs +++ b/src/AncillaryApplication/AncillaryApplication.cs @@ -5,7 +5,6 @@ using Application.Persistence.Interfaces; using Application.Persistence.Shared; using Application.Resources.Shared; -using Application.Services.Shared; using Common; using Common.Extensions; using Domain.Common.Identity; @@ -126,7 +125,7 @@ private async Task> DeliverUsageAsync(ICallerContext context return Error.RuleViolation(Resources.AncillaryApplication_MissingUsageEventName); } - await _usageReportingService.TrackAsync(context, message.ForId!, message.EventName!, message.Context, + await _usageReportingService.TrackAsync(context, message.ForId!, message.EventName!, message.Additional, cancellationToken); _recorder.TraceInformation(context.ToCall(), "Delivered usage for {For}", message.ForId!); diff --git a/src/AncillaryApplication/AncillaryApplication.csproj b/src/AncillaryApplication/AncillaryApplication.csproj index 91ad1caa..1aa1318d 100644 --- a/src/AncillaryApplication/AncillaryApplication.csproj +++ b/src/AncillaryApplication/AncillaryApplication.csproj @@ -8,6 +8,7 @@ + diff --git a/src/AncillaryApplication/IAncillaryApplication.cs b/src/AncillaryApplication/IAncillaryApplication.cs index 61d4ac85..53b2c635 100644 --- a/src/AncillaryApplication/IAncillaryApplication.cs +++ b/src/AncillaryApplication/IAncillaryApplication.cs @@ -1,6 +1,6 @@ using Application.Interfaces; -using Application.Resources.Shared; using Common; +using Audit = Application.Resources.Shared.Audit; namespace AncillaryApplication; diff --git a/src/AncillaryApplication/IRecordingApplication.cs b/src/AncillaryApplication/IRecordingApplication.cs new file mode 100644 index 00000000..11cff13c --- /dev/null +++ b/src/AncillaryApplication/IRecordingApplication.cs @@ -0,0 +1,15 @@ +using Application.Interfaces; +using Common; + +namespace AncillaryApplication; + +public interface IRecordingApplication +{ + Task> RecordMeasurementAsync(ICallerContext context, string eventName, + Dictionary? additional, + CancellationToken cancellationToken); + + Task> RecordUsageAsync(ICallerContext context, string eventName, + Dictionary? additional, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/AncillaryApplication/RecordingApplication.cs b/src/AncillaryApplication/RecordingApplication.cs new file mode 100644 index 00000000..0e67a8bc --- /dev/null +++ b/src/AncillaryApplication/RecordingApplication.cs @@ -0,0 +1,43 @@ +using Application.Common; +using Application.Interfaces; +using Common; +using Common.Extensions; +using Task = System.Threading.Tasks.Task; + +namespace AncillaryApplication; + +public class RecordingApplication : IRecordingApplication +{ + private readonly IRecorder _recorder; + + public RecordingApplication(IRecorder recorder) + { + _recorder = recorder; + } + + public Task> RecordMeasurementAsync(ICallerContext context, string eventName, + Dictionary? additional, + CancellationToken cancellationToken) + { + _recorder.Measure(context.ToCall(), eventName, (additional.Exists() + ? additional + .Where(pair => pair.Value.Exists()) + .ToDictionary(pair => pair.Key, pair => pair.Value) + : null)!); + + return Task.FromResult(Result.Ok); + } + + public Task> RecordUsageAsync(ICallerContext context, string eventName, + Dictionary? additional, + CancellationToken cancellationToken) + { + _recorder.TrackUsage(context.ToCall(), eventName, (additional.Exists() + ? additional + .Where(pair => pair.Value.Exists()) + .ToDictionary(pair => pair.Key, pair => pair.Value) + : null)!); + + return Task.FromResult(Result.Ok); + } +} \ No newline at end of file diff --git a/src/AncillaryDomain.UnitTests/AncillaryDomain.UnitTests.csproj b/src/AncillaryDomain.UnitTests/AncillaryDomain.UnitTests.csproj index 296165a7..0e5f6b2f 100644 --- a/src/AncillaryDomain.UnitTests/AncillaryDomain.UnitTests.csproj +++ b/src/AncillaryDomain.UnitTests/AncillaryDomain.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/AncillaryDomain/Validations.cs b/src/AncillaryDomain/Validations.cs new file mode 100644 index 00000000..6c38f0c7 --- /dev/null +++ b/src/AncillaryDomain/Validations.cs @@ -0,0 +1,11 @@ +using Domain.Interfaces.Validations; + +namespace AncillaryDomain; + +public static class Validations +{ + public static class Recording + { + public static readonly Validation AdditionalStringValue = CommonValidations.DescriptiveName(1, 300); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.IntegrationTests/AncillaryInfrastructure.IntegrationTests.csproj b/src/AncillaryInfrastructure.IntegrationTests/AncillaryInfrastructure.IntegrationTests.csproj index 8fefb12b..246ee85d 100644 --- a/src/AncillaryInfrastructure.IntegrationTests/AncillaryInfrastructure.IntegrationTests.csproj +++ b/src/AncillaryInfrastructure.IntegrationTests/AncillaryInfrastructure.IntegrationTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/AncillaryInfrastructure.IntegrationTests/AuditsApiSpec.cs b/src/AncillaryInfrastructure.IntegrationTests/AuditsApiSpec.cs index 9249fbb2..062277e3 100644 --- a/src/AncillaryInfrastructure.IntegrationTests/AuditsApiSpec.cs +++ b/src/AncillaryInfrastructure.IntegrationTests/AuditsApiSpec.cs @@ -1,7 +1,6 @@ using AncillaryInfrastructure.IntegrationTests.Stubs; using ApiHost1; using Application.Persistence.Shared; -using Application.Services.Shared; using Common; using Common.Extensions; using FluentAssertions; diff --git a/src/AncillaryInfrastructure.IntegrationTests/RecordingApiSpec.cs b/src/AncillaryInfrastructure.IntegrationTests/RecordingApiSpec.cs new file mode 100644 index 00000000..e97a48ef --- /dev/null +++ b/src/AncillaryInfrastructure.IntegrationTests/RecordingApiSpec.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using AncillaryInfrastructure.IntegrationTests.Stubs; +using ApiHost1; +using Common; +using FluentAssertions; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using IntegrationTesting.WebApi.Common; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Task = System.Threading.Tasks.Task; + +namespace AncillaryInfrastructure.IntegrationTests; + +[Trait("Category", "Integration.Web")] +public class RecordingApiSpec : WebApiSpec +{ + private readonly StubRecorder _recorder; + + public RecordingApiSpec(WebApiSetup setup) : base(setup, OverrideDependencies) + { + EmptyAllRepositories(setup); + _recorder = setup.GetRequiredService().As(); + _recorder.Reset(); + } + + [Fact] + public async Task WhenRecordUseWithNoAdditional_ThenRecords() + { + var request = new RecordUseRequest + { + EventName = "aneventname", + Additional = null + }; + await Api.PostAsync(request, req => req.SetHmacAuth(request, "asecret")); + + _recorder.LastUsageEventName.Should().Be("aneventname"); + _recorder.LastUsageAdditional.Should().BeNull(); + } + + [Fact] + public async Task WhenRecordUse_ThenRecords() + { + var request = new RecordUseRequest + { + EventName = "aneventname", + Additional = new Dictionary + { + { "aname1", "avalue" }, + { "aname2", 25 }, + { "aname3", true } + } + }; + await Api.PostAsync(request, req => req.SetHmacAuth(request, "asecret")); + + _recorder.LastUsageEventName.Should().Be("aneventname"); + _recorder.LastUsageAdditional!.Count.Should().Be(3); + _recorder.LastUsageAdditional!["aname1"].As().GetString().Should().Be("avalue"); + _recorder.LastUsageAdditional!["aname2"].As().GetInt32().Should().Be(25); + _recorder.LastUsageAdditional!["aname3"].As().GetBoolean().Should().Be(true); + } + + [Fact] + public async Task WhenRecordMeasure_ThenRecords() + { + var request = new RecordMeasureRequest + { + EventName = "aneventname", + Additional = new Dictionary + { + { "aname1", "avalue" }, + { "aname2", 25 }, + { "aname3", true } + } + }; + await Api.PostAsync(request, req => req.SetHmacAuth(request, "asecret")); + + _recorder.LastMeasureEventName.Should().Be("aneventname"); + _recorder.LastMeasureAdditional!.Count.Should().Be(3); + _recorder.LastMeasureAdditional!["aname1"].As().GetString().Should().Be("avalue"); + _recorder.LastMeasureAdditional!["aname2"].As().GetInt32().Should().Be(25); + _recorder.LastMeasureAdditional!["aname3"].As().GetBoolean().Should().Be(true); + } + + private static void OverrideDependencies(IServiceCollection services) + { + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubRecorder.cs b/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubRecorder.cs new file mode 100644 index 00000000..bbe33c75 --- /dev/null +++ b/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubRecorder.cs @@ -0,0 +1,149 @@ +using Common; +using Common.Recording; + +namespace AncillaryInfrastructure.IntegrationTests.Stubs; + +public class StubRecorder : IRecorder +{ + public string? LastAuditAuditCode { get; private set; } + + public object[]? LastCrashArguments { get; private set; } + + public CrashLevel? LastCrashLevel { get; private set; } + + public string? LastCrashMessageTemplate { get; private set; } + + public Dictionary? LastMeasureAdditional { get; private set; } + + public string? LastMeasureEventName { get; private set; } + + public object[]? LastTraceArguments { get; private set; } + + public RecorderTraceLevel? LastTraceLevel { get; private set; } + + public string? LastTraceMessageTemplate { get; private set; } + + public Dictionary? LastUsageAdditional { get; private set; } + + public string? LastUsageEventName { get; private set; } + + public void TraceDebug(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Debug; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceInformation(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Information; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceInformation(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Information; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceWarning(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Warning; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceWarning(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Warning; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceError(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Error; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void TraceError(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + LastTraceLevel = RecorderTraceLevel.Error; + LastTraceMessageTemplate = messageTemplate; + LastTraceArguments = templateArgs; + } + + public void Crash(ICallContext? context, CrashLevel level, Exception exception) + { + LastCrashLevel = level; + LastCrashMessageTemplate = null; + LastCrashArguments = null; + } + + public void Crash(ICallContext? context, CrashLevel level, Exception exception, string messageTemplate, + params object[] templateArgs) + { + LastCrashLevel = level; + LastCrashMessageTemplate = messageTemplate; + LastCrashArguments = templateArgs; + } + + public void Audit(ICallContext? context, string auditCode, string messageTemplate, params object[] templateArgs) + { + LastAuditAuditCode = auditCode; + } + + public void AuditAgainst(ICallContext? context, string againstId, string auditCode, string messageTemplate, + params object[] templateArgs) + { + LastAuditAuditCode = auditCode; + } + + public void TrackUsage(ICallContext? context, string eventName, Dictionary? additional = null) + { + LastUsageEventName = eventName; + LastUsageAdditional = additional; + } + + public void TrackUsageFor(ICallContext? context, string forId, string eventName, + Dictionary? additional = null) + { + LastUsageEventName = eventName; + LastUsageAdditional = additional; + } + + public void Measure(ICallContext? context, string eventName, Dictionary? additional = null) + { + LastMeasureEventName = eventName; + LastMeasureAdditional = additional; + } + + public void Reset() + { + LastTraceLevel = null; + LastTraceMessageTemplate = null; + LastTraceArguments = null; + LastCrashLevel = null; + LastCrashMessageTemplate = null; + LastCrashArguments = null; + LastMeasureEventName = null; + LastMeasureAdditional = null; + LastUsageEventName = null; + LastUsageAdditional = null; + LastAuditAuditCode = null; + } +} + +public enum RecorderTraceLevel +{ + Debug = 0, + Information = 1, + Warning = 2, + Error = 3 +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubUsageReportingService.cs b/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubUsageReportingService.cs index 430b0f30..9a393b0c 100644 --- a/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubUsageReportingService.cs +++ b/src/AncillaryInfrastructure.IntegrationTests/Stubs/StubUsageReportingService.cs @@ -1,5 +1,5 @@ using Application.Interfaces; -using Application.Services.Shared; +using Application.Persistence.Shared; using Common; namespace AncillaryInfrastructure.IntegrationTests.Stubs; diff --git a/src/AncillaryInfrastructure.IntegrationTests/UsagesApiSpec.cs b/src/AncillaryInfrastructure.IntegrationTests/UsagesApiSpec.cs index 0fff9639..b1f65506 100644 --- a/src/AncillaryInfrastructure.IntegrationTests/UsagesApiSpec.cs +++ b/src/AncillaryInfrastructure.IntegrationTests/UsagesApiSpec.cs @@ -1,7 +1,6 @@ using AncillaryInfrastructure.IntegrationTests.Stubs; using ApiHost1; using Application.Persistence.Shared; -using Application.Services.Shared; using Common; using Common.Extensions; using FluentAssertions; diff --git a/src/AncillaryInfrastructure.UnitTests/AncillaryInfrastructure.UnitTests.csproj b/src/AncillaryInfrastructure.UnitTests/AncillaryInfrastructure.UnitTests.csproj index 135a5f64..89f39a48 100644 --- a/src/AncillaryInfrastructure.UnitTests/AncillaryInfrastructure.UnitTests.csproj +++ b/src/AncillaryInfrastructure.UnitTests/AncillaryInfrastructure.UnitTests.csproj @@ -6,14 +6,12 @@ - + + - - - - + diff --git a/src/AncillaryInfrastructure.UnitTests/Api/AdditionalValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/AdditionalValidatorSpec.cs new file mode 100644 index 00000000..cce23f14 --- /dev/null +++ b/src/AncillaryInfrastructure.UnitTests/Api/AdditionalValidatorSpec.cs @@ -0,0 +1,83 @@ +using AncillaryDomain; +using AncillaryInfrastructure.Api; +using FluentAssertions; +using FluentValidation; +using UnitTesting.Common.Validation; +using Xunit; + +namespace AncillaryInfrastructure.UnitTests.Api; + +[Trait("Category", "Unit")] +public class AdditionalValidatorSpec +{ + private readonly AdditionalValidator _validator; + private Dictionary? _dto; + + public AdditionalValidatorSpec() + { + _validator = new AdditionalValidator(); + _dto = new Dictionary(); + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto!); + } + + [Fact] + public void WhenAnyNameIsEmpty_ThenThrows() + { + _dto = new Dictionary + { + { "aname1", "avalue1" }, + { string.Empty, "avalue2" }, + { "aname3", "avalue3" } + }; + + _validator.Invoking(x => x.ValidateAndThrow(_dto!)) + .Should().Throw() + .WithMessageLike(Resources.AdditionalValidator_InvalidName); + } + + [Fact] + public void WhenAnyValueIsNull_ThenSucceeds() + { + _dto = new Dictionary + { + { "aname1", "avalue1" }, + { "aname2", null! }, + { "aname3", "avalue3" } + }; + + _validator.ValidateAndThrow(_dto!); + } + + [Fact] + public void WhenAnyStringValueIsValid_ThenSucceeds() + { + _dto = new Dictionary + { + { "aname1", "avalue1" }, + { "aname2", new string('x', Validations.Recording.AdditionalStringValue.MaxLength!.Value) }, + { "aname3", "avalue3" } + }; + + _validator.ValidateAndThrow(_dto!); + } + + [Fact] + public void WhenAnyStringValueIsInvalid_ThenThrows() + { + _dto = new Dictionary + { + { "aname1", "avalue1" }, + { "aname2", new string('x', Validations.Recording.AdditionalStringValue.MaxLength!.Value + 1) }, + { "aname3", "avalue3" } + }; + + _validator.Invoking(x => x.ValidateAndThrow(_dto!)) + .Should().Throw() + .WithMessageLike(Resources.AdditionalValidator_InvalidStringValue); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/Audits/DeliverAuditRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/Audits/DeliverAuditRequestValidatorSpec.cs index 89426840..aadb02c8 100644 --- a/src/AncillaryInfrastructure.UnitTests/Api/Audits/DeliverAuditRequestValidatorSpec.cs +++ b/src/AncillaryInfrastructure.UnitTests/Api/Audits/DeliverAuditRequestValidatorSpec.cs @@ -35,6 +35,6 @@ public void WhenMessageIsNull_ThenThrows() _validator.Invoking(x => x.ValidateAndThrow(_dto)) .Should().Throw() - .WithMessageLike(Resources.AnyMessageValidator_InvalidMessage); + .WithMessageLike(Resources.AnyQueueMessageValidator_InvalidMessage); } } \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordMeasureRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordMeasureRequestValidatorSpec.cs new file mode 100644 index 00000000..bb9474f0 --- /dev/null +++ b/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordMeasureRequestValidatorSpec.cs @@ -0,0 +1,48 @@ +using AncillaryInfrastructure.Api.Recording; +using FluentAssertions; +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using UnitTesting.Common.Validation; +using Xunit; + +namespace AncillaryInfrastructure.UnitTests.Api.Recording; + +[Trait("Category", "Unit")] +public class RecordMeasureRequestValidatorSpec +{ + private readonly RecordMeasureRequest _dto; + private readonly RecordMeasureRequestValidator _validator; + + public RecordMeasureRequestValidatorSpec() + { + _validator = new RecordMeasureRequestValidator(); + _dto = new RecordMeasureRequest + { + EventName = "aneventname" + }; + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenEventNameIsNull_ThenThrows() + { + _dto.EventName = null!; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.AnyRecordingEventNameValidator_InvalidEventName); + } + + [Fact] + public void WhenAdditionalIsNull_ThenSucceeds() + { + _dto.Additional = null; + + _validator.ValidateAndThrow(_dto); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordUsageRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordUsageRequestValidatorSpec.cs new file mode 100644 index 00000000..5cd0b3a8 --- /dev/null +++ b/src/AncillaryInfrastructure.UnitTests/Api/Recording/RecordUsageRequestValidatorSpec.cs @@ -0,0 +1,48 @@ +using AncillaryInfrastructure.Api.Recording; +using FluentAssertions; +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using UnitTesting.Common.Validation; +using Xunit; + +namespace AncillaryInfrastructure.UnitTests.Api.Recording; + +[Trait("Category", "Unit")] +public class RecordUsageRequestValidatorSpec +{ + private readonly RecordUseRequest _dto; + private readonly RecordUseRequestValidator _validator; + + public RecordUsageRequestValidatorSpec() + { + _validator = new RecordUseRequestValidator(); + _dto = new RecordUseRequest + { + EventName = "aneventname" + }; + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenEventNameIsNull_ThenThrows() + { + _dto.EventName = null!; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.AnyRecordingEventNameValidator_InvalidEventName); + } + + [Fact] + public void WhenAdditionalIsNull_ThenSucceeds() + { + _dto.Additional = null; + + _validator.ValidateAndThrow(_dto); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/Usages/DeliverUsageRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/Usages/DeliverUsageRequestValidatorSpec.cs index 3d6fba15..b2b4bd91 100644 --- a/src/AncillaryInfrastructure.UnitTests/Api/Usages/DeliverUsageRequestValidatorSpec.cs +++ b/src/AncillaryInfrastructure.UnitTests/Api/Usages/DeliverUsageRequestValidatorSpec.cs @@ -35,6 +35,6 @@ public void WhenMessageIsNull_ThenThrows() _validator.Invoking(x => x.ValidateAndThrow(_dto)) .Should().Throw() - .WithMessageLike(Resources.AnyMessageValidator_InvalidMessage); + .WithMessageLike(Resources.AnyQueueMessageValidator_InvalidMessage); } } \ No newline at end of file diff --git a/src/AncillaryInfrastructure/AncillaryInfrastructure.csproj b/src/AncillaryInfrastructure/AncillaryInfrastructure.csproj index 13994184..f8f43fef 100644 --- a/src/AncillaryInfrastructure/AncillaryInfrastructure.csproj +++ b/src/AncillaryInfrastructure/AncillaryInfrastructure.csproj @@ -6,6 +6,7 @@ + diff --git a/src/AncillaryInfrastructure/AncillaryModule.cs b/src/AncillaryInfrastructure/AncillaryModule.cs index 006dde06..327eedb0 100644 --- a/src/AncillaryInfrastructure/AncillaryModule.cs +++ b/src/AncillaryInfrastructure/AncillaryModule.cs @@ -7,12 +7,13 @@ using AncillaryInfrastructure.Persistence; using AncillaryInfrastructure.Persistence.ReadModels; using Application.Persistence.Interfaces; -using Application.Services.Shared; +using Application.Persistence.Shared; using Common; using Domain.Interfaces; +using Infrastructure.Hosting.Common.Extensions; using Infrastructure.Persistence.Interfaces; +using Infrastructure.Persistence.Shared.ApplicationServices; using Infrastructure.Web.Hosting.Common; -using Infrastructure.Web.Hosting.Common.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -41,6 +42,7 @@ public Action RegisterServicesFunction { return (_, services) => { + services.RegisterUnshared(); services.RegisterUnshared(); services.RegisterUnshared(c => new UsageMessageQueueRepository(c.Resolve(), c.ResolveForPlatform())); diff --git a/src/AncillaryInfrastructure/Api/AdditionalValidator.cs b/src/AncillaryInfrastructure/Api/AdditionalValidator.cs new file mode 100644 index 00000000..0298bae5 --- /dev/null +++ b/src/AncillaryInfrastructure/Api/AdditionalValidator.cs @@ -0,0 +1,32 @@ +using AncillaryDomain; +using Common.Extensions; +using Domain.Interfaces.Validations; +using FluentValidation; + +namespace AncillaryInfrastructure.Api; + +public class AdditionalValidator : AbstractValidator?> +{ + public AdditionalValidator() + { + When(req => req.Exists(), () => + { + RuleForEach(item => item) + .SetValidator(new AdditionalItemValidator()); + }); + } +} + +public class AdditionalItemValidator : AbstractValidator> +{ + public AdditionalItemValidator() + { + RuleFor(item => item.Key) + .NotEmpty() + .WithMessage(Resources.AdditionalValidator_InvalidName); + RuleFor(item => item.Value) + .Must(val => Validations.Recording.AdditionalStringValue.Matches(val!.ToString())) + .When(item => item.Value.Exists()) + .WithMessage(Resources.AdditionalValidator_InvalidStringValue); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Audits/AuditsApi.cs b/src/AncillaryInfrastructure/Api/Audits/AuditsApi.cs index 3d55420d..5b1b903e 100644 --- a/src/AncillaryInfrastructure/Api/Audits/AuditsApi.cs +++ b/src/AncillaryInfrastructure/Api/Audits/AuditsApi.cs @@ -29,17 +29,18 @@ public async Task> Deliver(DeliverAu } #if TESTINGONLY - public async Task DrainAll(DrainAllAuditsRequest request, + public async Task DrainAll(DrainAllAuditsRequest request, CancellationToken cancellationToken) { - await _ancillaryApplication.DrainAllAuditsAsync(_context, cancellationToken); + var result = await _ancillaryApplication.DrainAllAuditsAsync(_context, cancellationToken); - return () => new Result(); + return () => result.Match(() => new Result(), + error => new Result(error)); } #endif #if TESTINGONLY - public async Task> SearchAll( + public async Task> SearchAll( SearchAllAuditsRequest request, CancellationToken cancellationToken) { var audits = await _ancillaryApplication.SearchAllAuditsAsync(_context, diff --git a/src/AncillaryInfrastructure/Api/Audits/DeliverAuditRequestValidator.cs b/src/AncillaryInfrastructure/Api/Audits/DeliverAuditRequestValidator.cs index 96217828..cebfb153 100644 --- a/src/AncillaryInfrastructure/Api/Audits/DeliverAuditRequestValidator.cs +++ b/src/AncillaryInfrastructure/Api/Audits/DeliverAuditRequestValidator.cs @@ -11,6 +11,6 @@ public DeliverAuditRequestValidator() { RuleFor(req => req.Message) .NotEmpty() - .WithMessage(Resources.AnyMessageValidator_InvalidMessage); + .WithMessage(Resources.AnyQueueMessageValidator_InvalidMessage); } } \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Recording/RecordMeasureRequestValidator.cs b/src/AncillaryInfrastructure/Api/Recording/RecordMeasureRequestValidator.cs new file mode 100644 index 00000000..b69c809b --- /dev/null +++ b/src/AncillaryInfrastructure/Api/Recording/RecordMeasureRequestValidator.cs @@ -0,0 +1,18 @@ +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using JetBrains.Annotations; + +namespace AncillaryInfrastructure.Api.Recording; + +[UsedImplicitly] +public class RecordMeasureRequestValidator : AbstractValidator +{ + public RecordMeasureRequestValidator() + { + RuleFor(req => req.EventName) + .NotEmpty() + .WithMessage(Resources.AnyRecordingEventNameValidator_InvalidEventName); + RuleFor(req => req.Additional) + .SetValidator(new AdditionalValidator()); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Recording/RecordUsageRequestValidator.cs b/src/AncillaryInfrastructure/Api/Recording/RecordUsageRequestValidator.cs new file mode 100644 index 00000000..bfea0292 --- /dev/null +++ b/src/AncillaryInfrastructure/Api/Recording/RecordUsageRequestValidator.cs @@ -0,0 +1,18 @@ +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using JetBrains.Annotations; + +namespace AncillaryInfrastructure.Api.Recording; + +[UsedImplicitly] +public class RecordUseRequestValidator : AbstractValidator +{ + public RecordUseRequestValidator() + { + RuleFor(req => req.EventName) + .NotEmpty() + .WithMessage(Resources.AnyRecordingEventNameValidator_InvalidEventName); + RuleFor(req => req.Additional) + .SetValidator(new AdditionalValidator()); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Recording/RecordingApi.cs b/src/AncillaryInfrastructure/Api/Recording/RecordingApi.cs new file mode 100644 index 00000000..30ce4e4a --- /dev/null +++ b/src/AncillaryInfrastructure/Api/Recording/RecordingApi.cs @@ -0,0 +1,39 @@ +using AncillaryApplication; +using Application.Interfaces; +using Common; +using Infrastructure.Web.Api.Interfaces; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; + +namespace AncillaryInfrastructure.Api.Recording; + +public class RecordingApi : IWebApiService +{ + private readonly ICallerContext _context; + private readonly IRecordingApplication _recordingApplication; + + public RecordingApi(ICallerContext context, IRecordingApplication recordingApplication) + { + _context = context; + _recordingApplication = recordingApplication; + } + + public async Task RecordMeasurement(RecordMeasureRequest request, + CancellationToken cancellationToken) + { + var result = await _recordingApplication.RecordMeasurementAsync(_context, request.EventName, request.Additional, + cancellationToken); + + return () => result.Match(() => new Result(), + error => new Result(error)); + } + + public async Task RecordUsage(RecordUseRequest request, + CancellationToken cancellationToken) + { + var result = await _recordingApplication.RecordUsageAsync(_context, request.EventName, request.Additional, + cancellationToken); + + return () => result.Match(() => new Result(), + error => new Result(error)); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Usages/DeliverUsageRequestValidator.cs b/src/AncillaryInfrastructure/Api/Usages/DeliverUsageRequestValidator.cs index 951c4038..cde205a1 100644 --- a/src/AncillaryInfrastructure/Api/Usages/DeliverUsageRequestValidator.cs +++ b/src/AncillaryInfrastructure/Api/Usages/DeliverUsageRequestValidator.cs @@ -11,6 +11,6 @@ public DeliverUsageRequestValidator() { RuleFor(req => req.Message) .NotEmpty() - .WithMessage(Resources.AnyMessageValidator_InvalidMessage); + .WithMessage(Resources.AnyQueueMessageValidator_InvalidMessage); } } \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/Usages/UsagesApi.cs b/src/AncillaryInfrastructure/Api/Usages/UsagesApi.cs index d3dfc637..8e34ffdc 100644 --- a/src/AncillaryInfrastructure/Api/Usages/UsagesApi.cs +++ b/src/AncillaryInfrastructure/Api/Usages/UsagesApi.cs @@ -28,12 +28,13 @@ public async Task> Deliver(DeliverUs } #if TESTINGONLY - public async Task DrainAll(DrainAllUsagesRequest request, + public async Task DrainAll(DrainAllUsagesRequest request, CancellationToken cancellationToken) { - await _ancillaryApplication.DrainAllUsagesAsync(_context, cancellationToken); + var result = await _ancillaryApplication.DrainAllUsagesAsync(_context, cancellationToken); - return () => new Result(); + return () => result.Match(() => new Result(), + error => new Result(error)); } #endif } \ No newline at end of file diff --git a/src/AncillaryInfrastructure/ApplicationServices/NullUsageReportingService.cs b/src/AncillaryInfrastructure/ApplicationServices/NullUsageReportingService.cs index 6cca8251..cf94223f 100644 --- a/src/AncillaryInfrastructure/ApplicationServices/NullUsageReportingService.cs +++ b/src/AncillaryInfrastructure/ApplicationServices/NullUsageReportingService.cs @@ -1,6 +1,6 @@ using Application.Common; using Application.Interfaces; -using Application.Services.Shared; +using Application.Persistence.Shared; using Common; using Common.Extensions; using Task = System.Threading.Tasks.Task; diff --git a/src/AncillaryInfrastructure/Resources.Designer.cs b/src/AncillaryInfrastructure/Resources.Designer.cs index 5c0af030..e1225bd2 100644 --- a/src/AncillaryInfrastructure/Resources.Designer.cs +++ b/src/AncillaryInfrastructure/Resources.Designer.cs @@ -59,12 +59,39 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to The 'Name' of an item in the 'Additional' is either missing or invalid. + /// + internal static string AdditionalValidator_InvalidName { + get { + return ResourceManager.GetString("AdditionalValidator_InvalidName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A string 'Value' of an item in the 'Additional' is invalid. + /// + internal static string AdditionalValidator_InvalidStringValue { + get { + return ResourceManager.GetString("AdditionalValidator_InvalidStringValue", resourceCulture); + } + } + /// /// Looks up a localized string similar to The 'Message' is either missing or invalid. /// - internal static string AnyMessageValidator_InvalidMessage { + internal static string AnyQueueMessageValidator_InvalidMessage { + get { + return ResourceManager.GetString("AnyQueueMessageValidator_InvalidMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'EventName' is either missing or invalid. + /// + internal static string AnyRecordingEventNameValidator_InvalidEventName { get { - return ResourceManager.GetString("AnyMessageValidator_InvalidMessage", resourceCulture); + return ResourceManager.GetString("AnyRecordingEventNameValidator_InvalidEventName", resourceCulture); } } } diff --git a/src/AncillaryInfrastructure/Resources.resx b/src/AncillaryInfrastructure/Resources.resx index f3001f47..ba4e2134 100644 --- a/src/AncillaryInfrastructure/Resources.resx +++ b/src/AncillaryInfrastructure/Resources.resx @@ -24,7 +24,16 @@ PublicKeyToken=b77a5c561934e089 - + The 'Message' is either missing or invalid + + The 'EventName' is either missing or invalid + + + The 'Name' of an item in the 'Additional' is either missing or invalid + + + A string 'Value' of an item in the 'Additional' is invalid + \ No newline at end of file diff --git a/src/ApiHost1/Program.cs b/src/ApiHost1/Program.cs index f9bfd669..24ea3a2e 100644 --- a/src/ApiHost1/Program.cs +++ b/src/ApiHost1/Program.cs @@ -6,7 +6,7 @@ var modules = HostedModules.Get(); var app = WebApplication.CreateBuilder(args) - .ConfigureApiHost(modules, WebHostOptions.BackEndApiHost); + .ConfigureApiHost(modules, WebHostOptions.BackEndAncillaryApiHost); app.Run(); namespace ApiHost1 diff --git a/src/Application.Common.UnitTests/Application.Common.UnitTests.csproj b/src/Application.Common.UnitTests/Application.Common.UnitTests.csproj index 40f90fb8..46a4ce22 100644 --- a/src/Application.Common.UnitTests/Application.Common.UnitTests.csproj +++ b/src/Application.Common.UnitTests/Application.Common.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Application.Interfaces.UnitTests/Application.Interfaces.UnitTests.csproj b/src/Application.Interfaces.UnitTests/Application.Interfaces.UnitTests.csproj index dfa41fde..9cd50ec8 100644 --- a/src/Application.Interfaces.UnitTests/Application.Interfaces.UnitTests.csproj +++ b/src/Application.Interfaces.UnitTests/Application.Interfaces.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Application.Interfaces/Application.Interfaces.csproj b/src/Application.Interfaces/Application.Interfaces.csproj index 63740b8d..8617f9ef 100644 --- a/src/Application.Interfaces/Application.Interfaces.csproj +++ b/src/Application.Interfaces/Application.Interfaces.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Application.Interfaces/Services/IApiHostSetting.cs b/src/Application.Interfaces/Services/IHostSetting.cs similarity index 93% rename from src/Application.Interfaces/Services/IApiHostSetting.cs rename to src/Application.Interfaces/Services/IHostSetting.cs index 5342ecc2..0bb86bcc 100644 --- a/src/Application.Interfaces/Services/IApiHostSetting.cs +++ b/src/Application.Interfaces/Services/IHostSetting.cs @@ -3,7 +3,7 @@ /// /// Defines settings for the current API host /// -public interface IApiHostSetting +public interface IHostSetting { /// /// Returns the URL for the Ancillary API host diff --git a/src/Application.Interfaces/UsageConstants.cs b/src/Application.Interfaces/UsageConstants.cs index fff51057..0889db8f 100644 --- a/src/Application.Interfaces/UsageConstants.cs +++ b/src/Application.Interfaces/UsageConstants.cs @@ -22,6 +22,7 @@ public static class Properties public const string Started = "Started"; public const string TenantId = "TenantId"; public const string UsedById = "UserId"; + public const string EndPoint = "EndPoint"; } public static class Events @@ -33,5 +34,10 @@ public static class UsageScenarios public const string BookingCreated = "Booking Created"; public const string Measurement = "Measured"; } + + public static class Api + { + public const string ApiEndpointRequested = "api.request"; + } } } \ No newline at end of file diff --git a/src/Application.Persistence.Common.UnitTests/Application.Persistence.Common.UnitTests.csproj b/src/Application.Persistence.Common.UnitTests/Application.Persistence.Common.UnitTests.csproj index d4ea2d55..c422a1be 100644 --- a/src/Application.Persistence.Common.UnitTests/Application.Persistence.Common.UnitTests.csproj +++ b/src/Application.Persistence.Common.UnitTests/Application.Persistence.Common.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Application.Persistence.Shared/Application.Persistence.Shared.csproj b/src/Application.Persistence.Shared/Application.Persistence.Shared.csproj index a005bd68..c64b5399 100644 --- a/src/Application.Persistence.Shared/Application.Persistence.Shared.csproj +++ b/src/Application.Persistence.Shared/Application.Persistence.Shared.csproj @@ -5,6 +5,7 @@ + @@ -14,4 +15,8 @@ + + + + diff --git a/src/Application.Services.Shared/IAuditMessageQueueRepository.cs b/src/Application.Persistence.Shared/IAuditMessageQueueRepository.cs similarity index 76% rename from src/Application.Services.Shared/IAuditMessageQueueRepository.cs rename to src/Application.Persistence.Shared/IAuditMessageQueueRepository.cs index f1c6482f..5637e353 100644 --- a/src/Application.Services.Shared/IAuditMessageQueueRepository.cs +++ b/src/Application.Persistence.Shared/IAuditMessageQueueRepository.cs @@ -1,8 +1,7 @@ using Application.Persistence.Interfaces; -using Application.Persistence.Shared; using Common; -namespace Application.Services.Shared; +namespace Application.Persistence.Shared; public interface IAuditMessageQueueRepository : IMessageQueueStore, IApplicationRepository { diff --git a/src/Application.Services.Shared/IUsageMessageQueueRepository.cs b/src/Application.Persistence.Shared/IUsageMessageQueueRepository.cs similarity index 76% rename from src/Application.Services.Shared/IUsageMessageQueueRepository.cs rename to src/Application.Persistence.Shared/IUsageMessageQueueRepository.cs index a00f806b..362166de 100644 --- a/src/Application.Services.Shared/IUsageMessageQueueRepository.cs +++ b/src/Application.Persistence.Shared/IUsageMessageQueueRepository.cs @@ -1,8 +1,7 @@ using Application.Persistence.Interfaces; -using Application.Persistence.Shared; using Common; -namespace Application.Services.Shared; +namespace Application.Persistence.Shared; public interface IUsageMessageQueueRepository : IMessageQueueStore, IApplicationRepository { diff --git a/src/Application.Services.Shared/IUsageReportingService.cs b/src/Application.Persistence.Shared/IUsageReportingService.cs similarity index 91% rename from src/Application.Services.Shared/IUsageReportingService.cs rename to src/Application.Persistence.Shared/IUsageReportingService.cs index 815550cb..6ec21026 100644 --- a/src/Application.Services.Shared/IUsageReportingService.cs +++ b/src/Application.Persistence.Shared/IUsageReportingService.cs @@ -1,7 +1,7 @@ using Application.Interfaces; using Common; -namespace Application.Services.Shared; +namespace Application.Persistence.Shared; /// /// Defines a service to which we can report usages events diff --git a/src/Application.Persistence.Shared/UsageMessage.cs b/src/Application.Persistence.Shared/UsageMessage.cs index 09a71142..c02e3eae 100644 --- a/src/Application.Persistence.Shared/UsageMessage.cs +++ b/src/Application.Persistence.Shared/UsageMessage.cs @@ -5,7 +5,7 @@ namespace Application.Persistence.Shared; [EntityName("usages")] public class UsageMessage : QueuedMessage { - public Dictionary? Context { get; set; } + public Dictionary? Additional { get; set; } public string? EventName { get; set; } diff --git a/src/Application.Services.Shared/Application.Services.Shared.csproj b/src/Application.Services.Shared/Application.Services.Shared.csproj index abd0d711..7ef72ef5 100644 --- a/src/Application.Services.Shared/Application.Services.Shared.csproj +++ b/src/Application.Services.Shared/Application.Services.Shared.csproj @@ -6,7 +6,6 @@ - diff --git a/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj b/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj index 2533bbc0..9a883f53 100644 --- a/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj +++ b/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs b/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs index 128e46d6..17fd43aa 100644 --- a/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs +++ b/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs @@ -4,11 +4,10 @@ using Common.Configuration; using Common.Recording; using Infrastructure.Common.Recording; -using Infrastructure.Web.Api.Common.Clients; -using Infrastructure.Web.Api.Interfaces.Clients; -using Infrastructure.Web.Hosting.Common; -using Infrastructure.Web.Hosting.Common.ApplicationServices; -using Infrastructure.Web.Hosting.Common.Extensions; +using Infrastructure.Hosting.Common; +using Infrastructure.Hosting.Common.Extensions; +using Infrastructure.Web.Common.Clients; +using Infrastructure.Web.Interfaces.Clients; using Infrastructure.Workers.Api; using Infrastructure.Workers.Api.Workers; using Microsoft.Extensions.DependencyInjection; @@ -26,7 +25,7 @@ public static void AddDependencies(this IServiceCollection services, HostBuilder { services.AddHttpClient(); services.AddSingleton(new AspNetConfigurationSettings(context.Configuration)); - services.AddSingleton(); + services.AddSingleton(); #if TESTINGONLY services.AddSingleton(new NullCrashReporter()); @@ -44,7 +43,7 @@ public static void AddDependencies(this IServiceCollection services, HostBuilder c.Resolve())); services.AddSingleton(c => new InterHostServiceClient(c.Resolve(), - c.Resolve().GetAncillaryApiHostBaseUrl())); + c.Resolve().GetAncillaryApiHostBaseUrl())); services.AddSingleton, DeliverUsageRelayWorker>(); services.AddSingleton, DeliverAuditRelayWorker>(); } diff --git a/src/BookingsApplication.UnitTests/BookingsApplication.UnitTests.csproj b/src/BookingsApplication.UnitTests/BookingsApplication.UnitTests.csproj index 18b47271..bb2842aa 100644 --- a/src/BookingsApplication.UnitTests/BookingsApplication.UnitTests.csproj +++ b/src/BookingsApplication.UnitTests/BookingsApplication.UnitTests.csproj @@ -6,15 +6,12 @@ - + + - - - - - + diff --git a/src/BookingsDomain.UnitTests/BookingsDomain.UnitTests.csproj b/src/BookingsDomain.UnitTests/BookingsDomain.UnitTests.csproj index 7dc77110..fc61b5dc 100644 --- a/src/BookingsDomain.UnitTests/BookingsDomain.UnitTests.csproj +++ b/src/BookingsDomain.UnitTests/BookingsDomain.UnitTests.csproj @@ -6,13 +6,13 @@ - + + + - - - + diff --git a/src/BookingsInfrastructure.IntegrationTests/BookingsInfrastructure.IntegrationTests.csproj b/src/BookingsInfrastructure.IntegrationTests/BookingsInfrastructure.IntegrationTests.csproj index afdc41bb..246ee85d 100644 --- a/src/BookingsInfrastructure.IntegrationTests/BookingsInfrastructure.IntegrationTests.csproj +++ b/src/BookingsInfrastructure.IntegrationTests/BookingsInfrastructure.IntegrationTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/BookingsInfrastructure.UnitTests/BookingsInfrastructure.UnitTests.csproj b/src/BookingsInfrastructure.UnitTests/BookingsInfrastructure.UnitTests.csproj index 58a1e7e5..093ee7e2 100644 --- a/src/BookingsInfrastructure.UnitTests/BookingsInfrastructure.UnitTests.csproj +++ b/src/BookingsInfrastructure.UnitTests/BookingsInfrastructure.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/BookingsInfrastructure/BookingsModule.cs b/src/BookingsInfrastructure/BookingsModule.cs index 38f800dc..df5cf0a0 100644 --- a/src/BookingsInfrastructure/BookingsModule.cs +++ b/src/BookingsInfrastructure/BookingsModule.cs @@ -4,8 +4,8 @@ using BookingsDomain; using BookingsInfrastructure.Api.Bookings; using BookingsInfrastructure.Persistence; +using Infrastructure.Hosting.Common.Extensions; using Infrastructure.Web.Hosting.Common; -using Infrastructure.Web.Hosting.Common.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/src/CarsApplication.UnitTests/CarsApplication.UnitTests.csproj b/src/CarsApplication.UnitTests/CarsApplication.UnitTests.csproj index 9efcad95..11a4f3fa 100644 --- a/src/CarsApplication.UnitTests/CarsApplication.UnitTests.csproj +++ b/src/CarsApplication.UnitTests/CarsApplication.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/CarsDomain.UnitTests/CarsDomain.UnitTests.csproj b/src/CarsDomain.UnitTests/CarsDomain.UnitTests.csproj index d352438e..c1940314 100644 --- a/src/CarsDomain.UnitTests/CarsDomain.UnitTests.csproj +++ b/src/CarsDomain.UnitTests/CarsDomain.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/CarsInfrastructure.IntegrationTests/CarsInfrastructure.IntegrationTests.csproj b/src/CarsInfrastructure.IntegrationTests/CarsInfrastructure.IntegrationTests.csproj index 3a68bd08..246ee85d 100644 --- a/src/CarsInfrastructure.IntegrationTests/CarsInfrastructure.IntegrationTests.csproj +++ b/src/CarsInfrastructure.IntegrationTests/CarsInfrastructure.IntegrationTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/CarsInfrastructure.UnitTests/CarsInfrastructure.UnitTests.csproj b/src/CarsInfrastructure.UnitTests/CarsInfrastructure.UnitTests.csproj index 5b39588c..e2389d1f 100644 --- a/src/CarsInfrastructure.UnitTests/CarsInfrastructure.UnitTests.csproj +++ b/src/CarsInfrastructure.UnitTests/CarsInfrastructure.UnitTests.csproj @@ -5,15 +5,13 @@ true - - - - - + + + diff --git a/src/CarsInfrastructure/ApplicationServices/CarsHttpServiceClient.cs b/src/CarsInfrastructure/ApplicationServices/CarsHttpServiceClient.cs index 45be17a1..808fc34a 100644 --- a/src/CarsInfrastructure/ApplicationServices/CarsHttpServiceClient.cs +++ b/src/CarsInfrastructure/ApplicationServices/CarsHttpServiceClient.cs @@ -2,9 +2,9 @@ using Application.Resources.Shared; using Application.Services.Shared; using Common; -using Infrastructure.Web.Api.Common.Extensions; -using Infrastructure.Web.Api.Interfaces.Clients; using Infrastructure.Web.Api.Operations.Shared.Cars; +using Infrastructure.Web.Common.Extensions; +using Infrastructure.Web.Interfaces.Clients; namespace CarsInfrastructure.ApplicationServices; diff --git a/src/CarsInfrastructure/CarsModule.cs b/src/CarsInfrastructure/CarsModule.cs index b10d9a9a..37349242 100644 --- a/src/CarsInfrastructure/CarsModule.cs +++ b/src/CarsInfrastructure/CarsModule.cs @@ -9,9 +9,9 @@ using CarsInfrastructure.Persistence.ReadModels; using Common; using Domain.Interfaces; +using Infrastructure.Hosting.Common.Extensions; using Infrastructure.Persistence.Interfaces; using Infrastructure.Web.Hosting.Common; -using Infrastructure.Web.Hosting.Common.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Common.UnitTests/Common.UnitTests.csproj b/src/Common.UnitTests/Common.UnitTests.csproj index d172e6bf..69f885c2 100644 --- a/src/Common.UnitTests/Common.UnitTests.csproj +++ b/src/Common.UnitTests/Common.UnitTests.csproj @@ -5,14 +5,13 @@ true - - - - + + + diff --git a/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj b/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj index a7dc37de..17f185a9 100644 --- a/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj +++ b/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Domain.Interfaces.UnitTests/Domain.Interfaces.UnitTests.csproj b/src/Domain.Interfaces.UnitTests/Domain.Interfaces.UnitTests.csproj index dd16e5a5..d7ff350a 100644 --- a/src/Domain.Interfaces.UnitTests/Domain.Interfaces.UnitTests.csproj +++ b/src/Domain.Interfaces.UnitTests/Domain.Interfaces.UnitTests.csproj @@ -5,14 +5,14 @@ true - - - - + + + + diff --git a/src/Domain.Shared.UnitTests/Domain.Shared.UnitTests.csproj b/src/Domain.Shared.UnitTests/Domain.Shared.UnitTests.csproj index eecab7ff..72e6f6f0 100644 --- a/src/Domain.Shared.UnitTests/Domain.Shared.UnitTests.csproj +++ b/src/Domain.Shared.UnitTests/Domain.Shared.UnitTests.csproj @@ -10,7 +10,6 @@ - diff --git a/src/Infrastructure.Common.UnitTests/Infrastructure.Common.UnitTests.csproj b/src/Infrastructure.Common.UnitTests/Infrastructure.Common.UnitTests.csproj index d0bfda09..9e248022 100644 --- a/src/Infrastructure.Common.UnitTests/Infrastructure.Common.UnitTests.csproj +++ b/src/Infrastructure.Common.UnitTests/Infrastructure.Common.UnitTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Infrastructure.Common/Infrastructure.Common.csproj b/src/Infrastructure.Common/Infrastructure.Common.csproj index ea16ca65..bce844f5 100644 --- a/src/Infrastructure.Common/Infrastructure.Common.csproj +++ b/src/Infrastructure.Common/Infrastructure.Common.csproj @@ -7,7 +7,11 @@ + + + + diff --git a/src/Infrastructure.Common/Recording/ApplicationInsightsCrashReporter.cs b/src/Infrastructure.Common/Recording/ApplicationInsightsCrashReporter.cs index 105df385..1c60fb80 100644 --- a/src/Infrastructure.Common/Recording/ApplicationInsightsCrashReporter.cs +++ b/src/Infrastructure.Common/Recording/ApplicationInsightsCrashReporter.cs @@ -14,12 +14,10 @@ public class ApplicationInsightsCrashReporter : ICrashReporter { private readonly TelemetryClient _telemetryClient; -#if !TESTINGONLY public ApplicationInsightsCrashReporter(TelemetryClient telemetryClient) { _telemetryClient = telemetryClient; } -#endif public ApplicationInsightsCrashReporter(IDependencyContainer container) { diff --git a/src/Infrastructure.Common/Recording/ApplicationInsightsMetricReporter.cs b/src/Infrastructure.Common/Recording/ApplicationInsightsMetricReporter.cs new file mode 100644 index 00000000..562de4b1 --- /dev/null +++ b/src/Infrastructure.Common/Recording/ApplicationInsightsMetricReporter.cs @@ -0,0 +1,33 @@ +#if HOSTEDONAZURE +using Common.Extensions; +using Common.Recording; +using Domain.Interfaces.Services; +using Microsoft.ApplicationInsights; + +namespace Infrastructure.Common.Recording; + +/// +/// Provides a that sends its reports to Application Insights +/// +public class ApplicationInsightsMetricReporter : IMetricReporter +{ + private readonly TelemetryClient _telemetryClient; + + public ApplicationInsightsMetricReporter(IDependencyContainer container) + { + _telemetryClient = container.Resolve(); + } + + public void Measure(string eventName, Dictionary? additional = null) + { + if (_telemetryClient.Exists()) + { + _telemetryClient.TrackEvent(eventName, + (additional ?? new Dictionary()) + .ToDictionary(pair => pair.Key, pair => pair.Value.Exists() + ? pair.Value.ToString() + : string.Empty)); + } + } +} +#endif \ No newline at end of file diff --git a/src/Infrastructure.Common/Recording/ForwardToAncillaryApiMetrics.cs b/src/Infrastructure.Common/Recording/ForwardToAncillaryApiMetrics.cs new file mode 100644 index 00000000..f3b3e13a --- /dev/null +++ b/src/Infrastructure.Common/Recording/ForwardToAncillaryApiMetrics.cs @@ -0,0 +1,48 @@ +using Application.Common; +using Application.Interfaces.Services; +using Common.Recording; +using Domain.Interfaces.Services; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Common.Clients; +using Infrastructure.Web.Interfaces.Clients; + +namespace Infrastructure.Common.Recording; + +/// +/// Provides a that forwards the measurement to the backend Ancillary API +/// +public class ForwardToAncillaryApiMetrics : IMetricReporter +{ + private readonly string _hmacSecret; + private readonly IServiceClient _serviceClient; + + public ForwardToAncillaryApiMetrics(IDependencyContainer container) : this( + new InterHostServiceClient(container.Resolve(), + container.Resolve().GetAncillaryApiHostBaseUrl()), + container.Resolve().GetAncillaryApiHostHmacAuthSecret()) + { + } + + private ForwardToAncillaryApiMetrics(IServiceClient serviceClient, string hmacSecret) + { + _serviceClient = serviceClient; + _hmacSecret = hmacSecret; + } + + public void Measure(string eventName, Dictionary? additional = null) + { + // TODO: If we are running on a BackEndForFrontEndWebHost we need to copy the bearer token from the cookie into the caller.Authorization + var caller = Caller.CreateAsAnonymous(); + var request = new RecordMeasureRequest + { + EventName = eventName, + Additional = additional! + }; + _serviceClient.PostAsync(caller, request, req => + { + req.SetHmacAuth(request, _hmacSecret); + req.SetRequestId(caller.ToCall()); + }).GetAwaiter().GetResult(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Common/Recording/ForwardToAncillaryApiUsages.cs b/src/Infrastructure.Common/Recording/ForwardToAncillaryApiUsages.cs new file mode 100644 index 00000000..c9ff11ee --- /dev/null +++ b/src/Infrastructure.Common/Recording/ForwardToAncillaryApiUsages.cs @@ -0,0 +1,49 @@ +using Application.Common; +using Application.Interfaces.Services; +using Common; +using Common.Recording; +using Domain.Interfaces.Services; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Common.Clients; +using Infrastructure.Web.Interfaces.Clients; + +namespace Infrastructure.Common.Recording; + +/// +/// Provides a that forwards the usage to the backend Ancillary API +/// +public class ForwardToAncillaryApiUsages : IUsageReporter +{ + private readonly string _hmacSecret; + private readonly IServiceClient _serviceClient; + + public ForwardToAncillaryApiUsages(IDependencyContainer container) : this( + new InterHostServiceClient(container.Resolve(), + container.Resolve().GetAncillaryApiHostBaseUrl()), + container.Resolve().GetAncillaryApiHostHmacAuthSecret()) + { + } + + private ForwardToAncillaryApiUsages(IServiceClient serviceClient, string hmacSecret) + { + _serviceClient = serviceClient; + _hmacSecret = hmacSecret; + } + + public void Track(ICallContext? call, string forId, string eventName, Dictionary? additional = null) + { + // TODO: If we are running on a BackEndForFrontEndWebHost we need to copy the bearer token from the cookie into the caller.Authorization + var caller = Caller.CreateAsCallerFromCall(call ?? CallContext.CreateUnknown()); + var request = new RecordUseRequest + { + EventName = eventName, + Additional = additional! + }; + _serviceClient.PostAsync(caller, request, req => + { + req.SetHmacAuth(request, _hmacSecret); + req.SetRequestId(caller.ToCall()); + }).GetAwaiter().GetResult(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Common/Recording/QueuedAuditReporter.cs b/src/Infrastructure.Common/Recording/QueuedAuditReporter.cs new file mode 100644 index 00000000..0d555956 --- /dev/null +++ b/src/Infrastructure.Common/Recording/QueuedAuditReporter.cs @@ -0,0 +1,69 @@ +using Application.Persistence.Shared; +using Common; +using Common.Configuration; +using Common.Extensions; +using Common.Recording; +using Domain.Interfaces.Services; +using Infrastructure.Persistence.Interfaces; +using Infrastructure.Persistence.Shared.ApplicationServices; +#if !TESTINGONLY +#if HOSTEDONAZURE +using Infrastructure.Storage.Azure; +#elif HOSTEDONAWS +using Infrastructure.Storage.Aws; +#endif + +#endif + +namespace Infrastructure.Common.Recording; + +/// +/// Provides an that asynchronously brokers the audit to a reliable queue for future +/// delivery +/// +public class QueuedAuditReporter : IAuditReporter +{ + private readonly IAuditMessageQueueRepository _repository; + + // ReSharper disable once UnusedParameter.Local + public QueuedAuditReporter(IDependencyContainer container, ISettings settings) + : this(new AuditMessageQueueRepository(NullRecorder.Instance, +#if !TESTINGONLY +#if HOSTEDONAZURE + AzureStorageAccountQueueStore.Create(NullRecorder.Instance, settings) +#elif HOSTEDONAWS + NullStore.Instance() +#endif +#else + container.Resolve() +#endif + )) + { + } + + internal QueuedAuditReporter(IAuditMessageQueueRepository repository) + { + _repository = repository; + } + + public void Audit(ICallContext? context, string againstId, string auditCode, string messageTemplate, + params object[] templateArgs) + { + ArgumentException.ThrowIfNullOrEmpty(againstId); + ArgumentException.ThrowIfNullOrEmpty(auditCode); + + var call = context ?? CallContext.CreateUnknown(); + var message = new AuditMessage + { + AuditCode = auditCode, + AgainstId = againstId, + MessageTemplate = messageTemplate, + Arguments = templateArgs.HasAny() + ? templateArgs.Select(arg => arg.ToString()!) + .ToList() + : new List() + }; + + _repository.PushAsync(call, message, CancellationToken.None).GetAwaiter().GetResult(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Common/Recording/QueuedUsageReporter.cs b/src/Infrastructure.Common/Recording/QueuedUsageReporter.cs new file mode 100644 index 00000000..860a266c --- /dev/null +++ b/src/Infrastructure.Common/Recording/QueuedUsageReporter.cs @@ -0,0 +1,73 @@ +using Application.Interfaces; +using Application.Persistence.Shared; +using Common; +using Common.Configuration; +using Common.Extensions; +using Common.Recording; +using Domain.Interfaces.Services; +using Infrastructure.Persistence.Interfaces; +using Infrastructure.Persistence.Shared.ApplicationServices; +#if !TESTINGONLY +#if HOSTEDONAZURE +using Infrastructure.Storage.Azure; +#elif HOSTEDONAWS +using Infrastructure.Storage.Aws; +#endif + +#endif + +namespace Infrastructure.Common.Recording; + +/// +/// Provides an that asynchronously brokers the audit to a reliable queue for future +/// delivery +/// +public class QueuedUsageReporter : IUsageReporter +{ + private readonly IUsageMessageQueueRepository _repository; + + // ReSharper disable once UnusedParameter.Local + public QueuedUsageReporter(IDependencyContainer container, ISettings settings) + : this(new UsageMessageQueueRepository(NullRecorder.Instance, +#if !TESTINGONLY +#if HOSTEDONAZURE + AzureStorageAccountQueueStore.Create(NullRecorder.Instance, settings) +#elif HOSTEDONAWS + NullStore.Instance() +#endif +#else + container.Resolve() +#endif + )) + { + } + + internal QueuedUsageReporter(IUsageMessageQueueRepository repository) + { + _repository = repository; + } + + public void Track(ICallContext? context, string forId, string eventName, + Dictionary? additional = null) + { + ArgumentException.ThrowIfNullOrEmpty(forId); + ArgumentException.ThrowIfNullOrEmpty(eventName); + + var call = context ?? CallContext.CreateUnknown(); + var properties = additional ?? new Dictionary(); + + properties[UsageConstants.Properties.CallId] = call.CallId; + properties[UsageConstants.Properties.TenantId] = call.TenantId!; + + var message = new UsageMessage + { + EventName = eventName, + ForId = forId, + Additional = properties.ToDictionary(pair => pair.Key, pair => pair.Value.Exists() + ? pair.Value.ToString() ?? string.Empty + : string.Empty) + }; + + _repository.PushAsync(call, message, CancellationToken.None).GetAwaiter().GetResult(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Common/Recording/RecorderOptions.cs b/src/Infrastructure.Common/Recording/RecorderOptions.cs index e64a83b8..79ccc2c4 100644 --- a/src/Infrastructure.Common/Recording/RecorderOptions.cs +++ b/src/Infrastructure.Common/Recording/RecorderOptions.cs @@ -2,14 +2,24 @@ namespace Infrastructure.Common.Recording; +/// +/// Defines the options for persistence in different environments +/// +public class PersistenceOptions +{ + public required bool UsesEventing { get; set; } + + public required bool UsesQueues { get; set; } +} + /// /// Defines the options for recording in the different environments /// public class RecorderOptions { - public static readonly RecorderOptions BackEndApiHost = new() + public static readonly RecorderOptions BackEndAncillaryApiHost = new() { - TrackUsageOfAllApis = true, + UsageComponentName = UsageConstants.Components.BackEndApiHost, Testing = new RecordingEnvironmentOptions { @@ -26,30 +36,47 @@ public class RecorderOptions UsageReporting = UsageReporterOption.ReliableQueue } }; + public static readonly RecorderOptions BackEndApiHost = new() + { + + UsageComponentName = UsageConstants.Components.BackEndApiHost, + Testing = new RecordingEnvironmentOptions + { + CrashReporting = CrashReporterOption.None, + AuditReporting = AuditReporterOption.ReliableQueue, + MetricReporting = MetricReporterOption.ForwardToAncillaryApi, + UsageReporting = UsageReporterOption.ForwardToAncillaryApi + }, + Production = new RecordingEnvironmentOptions + { + CrashReporting = CrashReporterOption.Cloud, + AuditReporting = AuditReporterOption.ReliableQueue, + MetricReporting = MetricReporterOption.ForwardToAncillaryApi, + UsageReporting = UsageReporterOption.ForwardToAncillaryApi + } + }; public static readonly RecorderOptions BackEndForFrontEndWebHost = new() { - TrackUsageOfAllApis = false, UsageComponentName = UsageConstants.Components.BackEndForFrontEndWebHost, Testing = new RecordingEnvironmentOptions { CrashReporting = CrashReporterOption.None, - AuditReporting = AuditReporterOption.None, - MetricReporting = MetricReporterOption.ForwardToBackEnd, - UsageReporting = UsageReporterOption.ForwardToBackEnd + AuditReporting = AuditReporterOption.ReliableQueue, + MetricReporting = MetricReporterOption.ForwardToAncillaryApi, + UsageReporting = UsageReporterOption.ForwardToAncillaryApi }, Production = new RecordingEnvironmentOptions { CrashReporting = CrashReporterOption.Cloud, - AuditReporting = AuditReporterOption.None, - MetricReporting = MetricReporterOption.ForwardToBackEnd, - UsageReporting = UsageReporterOption.ForwardToBackEnd + AuditReporting = AuditReporterOption.ReliableQueue, + MetricReporting = MetricReporterOption.ForwardToAncillaryApi, + UsageReporting = UsageReporterOption.ForwardToAncillaryApi } }; public static readonly RecorderOptions TestingStubsHost = new() { - TrackUsageOfAllApis = false, UsageComponentName = "TestingStubApiHost", Testing = new RecordingEnvironmentOptions { @@ -83,8 +110,6 @@ public RecordingEnvironmentOptions CurrentEnvironment public RecordingEnvironmentOptions Testing { get; private set; } = new(); - public bool TrackUsageOfAllApis { get; private set; } - public string UsageComponentName { get; private set; } = string.Empty; } @@ -127,7 +152,7 @@ public enum MetricReporterOption { None = 0, Cloud = 1, - ForwardToBackEnd = 3 + ForwardToAncillaryApi = 3 } /// @@ -137,5 +162,5 @@ public enum UsageReporterOption { None = 0, ReliableQueue = 1, - ForwardToBackEnd = 2 + ForwardToAncillaryApi = 2 } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj b/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj index f7f5440b..98b98056 100644 --- a/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj +++ b/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs similarity index 98% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs index 88e341cf..8ced7c77 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/AspNetConfigurationSettingsSpec.cs @@ -2,13 +2,12 @@ using Common.Extensions; using FluentAssertions; using Infrastructure.Interfaces; -using Infrastructure.Web.Hosting.Common.ApplicationServices; using JetBrains.Annotations; using Microsoft.Extensions.Configuration; using Moq; using Xunit; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices; [UsedImplicitly] public class AspNetConfigurationSettingsSpec diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs similarity index 97% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs index 3ed3d028..d1567722 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs @@ -2,13 +2,13 @@ using Common; using Common.Extensions; using FluentAssertions; -using Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing; +using Infrastructure.Hosting.Common.ApplicationServices.Eventing; using Moq; using UnitTesting.Common; using Xunit; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing; [Trait("Category", "Unit")] public sealed class EventHandlerBaseSpec : IDisposable diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs similarity index 93% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs index fc310355..bf53ebbc 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs @@ -5,13 +5,13 @@ using FluentAssertions; using Infrastructure.Eventing.Common.Notifications; using Infrastructure.Eventing.Interfaces.Notifications; +using Infrastructure.Hosting.Common.ApplicationServices.Eventing.Notifications; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Notifications; using Moq; using Xunit; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; [Trait("Category", "Unit")] public class InProcessSynchronousNotificationRelaySpec diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs similarity index 84% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs index 98999e46..e4abedf0 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs @@ -2,7 +2,7 @@ using Domain.Interfaces.Entities; using Infrastructure.Eventing.Interfaces.Notifications; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; internal class TestConsumer : IEventNotificationConsumer { diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs similarity index 94% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs index 064e58ae..b7ebb607 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelaySpec.cs @@ -5,13 +5,13 @@ using Domain.Interfaces.Entities; using FluentAssertions; using Infrastructure.Eventing.Interfaces.Projections; +using Infrastructure.Hosting.Common.ApplicationServices.Eventing.Projections; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Projections; using Moq; using Xunit; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing.Projections; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Projections; [Trait("Category", "Unit")] public class InProcessSynchronousProjectionRelaySpec diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs similarity index 86% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs index b0c402ef..116ac2ca 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Projections/TestProjection.cs @@ -2,7 +2,7 @@ using Common; using Domain.Interfaces.Entities; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing.Projections; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Projections; public class TestProjection : IReadModelProjection { diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs similarity index 72% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs index b9b23382..55c62639 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs @@ -1,6 +1,6 @@ using Domain.Interfaces.Entities; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing; public class TestEvent : IDomainEvent { diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs similarity index 96% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs index f0e71bee..27689cff 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEventingAggregateRoot.cs @@ -7,7 +7,7 @@ using Domain.Interfaces.ValueObjects; using QueryAny; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices.Eventing; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing; [EntityName("acontainername")] public class TestEventingAggregateRoot : IEventingAggregateRoot, IDehydratableAggregateRoot diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/ApiHostSettingsSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/HostSettingsSpec.cs similarity index 63% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/ApiHostSettingsSpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/HostSettingsSpec.cs index cd695a56..aca686e9 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/ApplicationServices/ApiHostSettingsSpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/HostSettingsSpec.cs @@ -3,24 +3,24 @@ using Moq; using Xunit; -namespace Infrastructure.Web.Hosting.Common.UnitTests.ApplicationServices; +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices; [Trait("Category", "Unit")] -public class ApiHostSettingsSpec +public class HostSettingsSpec { - private readonly ApiHostSettings _service; + private readonly HostSettings _service; private readonly Mock _settings; - public ApiHostSettingsSpec() + public HostSettingsSpec() { _settings = new Mock(); - _service = new ApiHostSettings(_settings.Object); + _service = new HostSettings(_settings.Object); } [Fact] public void WhenGetWebsiteHostBaseUrl_ThenReturnsBaseUrl() { - _settings.Setup(s => s.Platform.GetString(ApiHostSettings.WebsiteHostBaseUrlSettingName)) + _settings.Setup(s => s.Platform.GetString(HostSettings.WebsiteHostBaseUrlSettingName)) .Returns("http://localhost/api/"); var result = _service.GetWebsiteHostBaseUrl(); @@ -31,7 +31,7 @@ public void WhenGetWebsiteHostBaseUrl_ThenReturnsBaseUrl() [Fact] public void WhenGetAncillaryApiHostBaseUrl_ThenReturnsBaseUrl() { - _settings.Setup(s => s.Platform.GetString(ApiHostSettings.AncillaryApiHostBaseUrlSettingName)) + _settings.Setup(s => s.Platform.GetString(HostSettings.AncillaryApiHostBaseUrlSettingName)) .Returns("http://localhost/api/"); var result = _service.GetAncillaryApiHostBaseUrl(); @@ -42,7 +42,7 @@ public void WhenGetAncillaryApiHostBaseUrl_ThenReturnsBaseUrl() [Fact] public void WhenGetAncillaryApiHostHmacAuthSecret_ThenReturnsBaseUrl() { - _settings.Setup(s => s.Platform.GetString(ApiHostSettings.AncillaryApiHmacSecretSettingName)) + _settings.Setup(s => s.Platform.GetString(HostSettings.AncillaryApiHmacSecretSettingName)) .Returns("asecret"); var result = _service.GetAncillaryApiHostHmacAuthSecret(); diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs similarity index 97% rename from src/Infrastructure.Web.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs rename to src/Infrastructure.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs index 5f0c4599..8294bccd 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/Extensions/ServiceCollectionExtensionsSpec.cs @@ -1,9 +1,9 @@ using FluentAssertions; -using Infrastructure.Web.Hosting.Common.Extensions; +using Infrastructure.Hosting.Common.Extensions; using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Infrastructure.Web.Hosting.Common.UnitTests.Extensions; +namespace Infrastructure.Hosting.Common.UnitTests.Extensions; [Trait("Category", "Unit")] public class ServiceCollectionExtensionsSpec @@ -50,7 +50,7 @@ public void WhenRegisterUnsharedWithFourInterfaces_ThenRegistersFourWithTheSameI services .RegisterUnshared( - _ => new TestContainerClass()); + _ => new TestContainerClass()); using var provider = services.BuildServiceProvider(); var interface1 = provider.ResolveForUnshared(); diff --git a/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj b/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj new file mode 100644 index 00000000..6e5ecac2 --- /dev/null +++ b/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + true + + + + + + + + + + + + + diff --git a/src/Infrastructure.Hosting.Common.UnitTests/Recording/HostRecorderSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/Recording/HostRecorderSpec.cs new file mode 100644 index 00000000..d2c5d3d3 --- /dev/null +++ b/src/Infrastructure.Hosting.Common.UnitTests/Recording/HostRecorderSpec.cs @@ -0,0 +1,127 @@ +using Common; +using Common.Recording; +using FluentAssertions; +using Infrastructure.Common.Recording; +using Infrastructure.Hosting.Common.Recording; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Infrastructure.Hosting.Common.UnitTests.Recording; + +[Trait("Category", "Unit")] +public sealed class HostRecorderSpec : IDisposable +{ + private readonly MockLogger _logger; + private readonly HostRecorder _recorder; + + public HostRecorderSpec() + { + _logger = new MockLogger(); + var crasher = new Mock(); + var auditor = new Mock(); + var measurer = new Mock(); + var follower = new Mock(); + _recorder = new HostRecorder(_logger, new RecorderOptions(), crasher.Object, + auditor.Object, measurer.Object, follower.Object); + } + + ~HostRecorderSpec() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _recorder.Dispose(); + } + } + + [Fact] + public void WhenTraceInformationWithNoParameters_ThenLogs() + { + _recorder.TraceInformation(null, string.Empty); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Information); + item.Message.Should().Be($"Request: {CallConstants.UncorrelatedCallId} By: {CallConstants.UnknownCallerId}"); + } + + [Fact] + public void WhenTraceInformationWithTemplateAndNoArgs_ThenLogs() + { + _recorder.TraceInformation(null, "amessagetemplate"); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Information); + item.Message.Should() + .Be($"Request: {CallConstants.UncorrelatedCallId} By: {CallConstants.UnknownCallerId}: amessagetemplate"); + } + + [Fact] + public void WhenTraceInformationWithTemplateAndArgs_ThenLogs() + { + _recorder.TraceInformation(null, "{Value1} {Value2}", "avalue1", "avalue2"); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Information); + item.Message.Should() + .Be($"Request: {CallConstants.UncorrelatedCallId} By: {CallConstants.UnknownCallerId}: avalue1 avalue2"); + } + + [Fact] + public void WhenTraceInformationWithCallTemplateAndArgs_ThenLogs() + { + var call = Mock.Of(call => call.CallerId == "acallerid" && call.CallId == "acallid"); + _recorder.TraceInformation(call, "{Value1} {Value2}", "avalue1", "avalue2"); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Information); + item.Message.Should().Be("Request: acallid By: acallerid: avalue1 avalue2"); + } + + [Fact] + public void WhenTraceInformationWithCallAndNoTemplateNorArgs_ThenLogs() + { + var call = Mock.Of(call => call.CallerId == "acallerid" && call.CallId == "acallid"); + _recorder.TraceInformation(call, string.Empty); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Information); + item.Message.Should().Be("Request: acallid By: acallerid"); + } + + [Fact] + public void WhenTraceErrorWithCallAndNoTemplateNorArgs_ThenLogs() + { + var call = Mock.Of(call => call.CallerId == "acallerid" && call.CallId == "acallid"); + var exception = new Exception("amessage"); + _recorder.TraceError(call, exception, string.Empty); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Error); + item.Message.Should().Be("Request: acallid By: acallerid"); + item.Exception.Should().Be(exception); + } + + [Fact] + public void WhenTraceErrorWithCallAndTemplateAndArgs_ThenLogs() + { + var call = Mock.Of(call => call.CallerId == "acallerid" && call.CallId == "acallid"); + var exception = new Exception("amessage"); + _recorder.TraceError(call, exception, "{Value1} {Value2}", "avalue1", "avalue2"); + + var item = _logger.Items.Single(); + item.Level.Should().Be(LogLevel.Error); + item.Message.Should().Be("Request: acallid By: acallerid: avalue1 avalue2"); + item.Exception.Should().Be(exception); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/Recording/MockLogger.cs b/src/Infrastructure.Hosting.Common.UnitTests/Recording/MockLogger.cs new file mode 100644 index 00000000..398f7af4 --- /dev/null +++ b/src/Infrastructure.Hosting.Common.UnitTests/Recording/MockLogger.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Logging; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Infrastructure.Hosting.Common.UnitTests.Recording; + +public class MockLogger : ILogger +{ + private readonly List _items = new(); + + public IReadOnlyList Items => _items; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter) + { + _items.Add(new LogItem + { Level = logLevel, Exception = exception, Message = formatter(state, exception) }); + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public IDisposable? BeginScope(TState state) + where TState : notnull + { + return null; + } +} + +public class LogItem +{ + public Exception? Exception { get; set; } + + public LogLevel Level { get; set; } + + public string? Message { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs similarity index 98% rename from src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs rename to src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs index 0234198c..36338c48 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs +++ b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/EventStreamHandlerBase.cs @@ -2,7 +2,7 @@ using Common; using Common.Extensions; -namespace Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing; +namespace Infrastructure.Hosting.Common.ApplicationServices.Eventing; /// /// Provides a base handler that subscribes to one or more diff --git a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs similarity index 94% rename from src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs rename to src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs index d91a13a7..510276ed 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs +++ b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs @@ -4,7 +4,7 @@ using Infrastructure.Eventing.Common.Notifications; using Infrastructure.Eventing.Interfaces.Notifications; -namespace Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Notifications; +namespace Infrastructure.Hosting.Common.ApplicationServices.Eventing.Notifications; /// /// Defines an in-process service that subscribes to one or more diff --git a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs similarity index 94% rename from src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs rename to src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs index 9ddd9fac..82f1feb3 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs +++ b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Projections/InProcessSynchronousProjectionRelay.cs @@ -4,7 +4,7 @@ using Infrastructure.Eventing.Common.Projections; using Infrastructure.Eventing.Interfaces.Projections; -namespace Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Projections; +namespace Infrastructure.Hosting.Common.ApplicationServices.Eventing.Projections; /// /// Defines an in-process service that subscribes to one or more diff --git a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/AspNetConfigurationSettings.cs b/src/Infrastructure.Hosting.Common/AspNetConfigurationSettings.cs similarity index 98% rename from src/Infrastructure.Web.Hosting.Common/ApplicationServices/AspNetConfigurationSettings.cs rename to src/Infrastructure.Hosting.Common/AspNetConfigurationSettings.cs index 6a53ddf7..25170079 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/AspNetConfigurationSettings.cs +++ b/src/Infrastructure.Hosting.Common/AspNetConfigurationSettings.cs @@ -4,7 +4,7 @@ using Infrastructure.Interfaces; using Microsoft.Extensions.Configuration; -namespace Infrastructure.Web.Hosting.Common.ApplicationServices; +namespace Infrastructure.Hosting.Common; /// /// Provides settings read from .NET configuration diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/DependencyExtensions.cs b/src/Infrastructure.Hosting.Common/Extensions/DependencyExtensions.cs similarity index 71% rename from src/Infrastructure.Web.Hosting.Common/Extensions/DependencyExtensions.cs rename to src/Infrastructure.Hosting.Common/Extensions/DependencyExtensions.cs index 842cac2c..369ccdf6 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/DependencyExtensions.cs +++ b/src/Infrastructure.Hosting.Common/Extensions/DependencyExtensions.cs @@ -1,12 +1,13 @@ using Infrastructure.Interfaces; using Microsoft.Extensions.DependencyInjection; -namespace Infrastructure.Web.Hosting.Common.Extensions; +namespace Infrastructure.Hosting.Common.Extensions; public static class DependencyExtensions { /// - /// Converts the specified to a + /// Converts the specified to a + /// /// public static ServiceLifetime ToLifetime(this DependencyScope scope) { diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/EventingExtensions.cs b/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs similarity index 84% rename from src/Infrastructure.Web.Hosting.Common/Extensions/EventingExtensions.cs rename to src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs index 5b2f3590..09b1d5db 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/EventingExtensions.cs +++ b/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs @@ -6,14 +6,14 @@ using Infrastructure.Eventing.Common.Projections; using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Eventing.Interfaces.Projections; +using Infrastructure.Hosting.Common.ApplicationServices.Eventing.Notifications; +using Infrastructure.Hosting.Common.ApplicationServices.Eventing.Projections; using Infrastructure.Interfaces; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Notifications; -using Infrastructure.Web.Hosting.Common.ApplicationServices.Eventing.Projections; using Microsoft.Extensions.DependencyInjection; -namespace Infrastructure.Web.Hosting.Common.Extensions; +namespace Infrastructure.Hosting.Common.Extensions; public static class EventingExtensions { @@ -30,8 +30,9 @@ internal static void ClearEventConfiguration() /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// , and consumed by the specified + /// and + /// , and + /// consumed by the specified /// and /// public static IServiceCollection RegisterTenantedEventing( @@ -41,15 +42,16 @@ public static IServiceCollection RegisterTenantedEventing(DependencyScope.Tenanted, + return AddEventing(services, DependencyScope.Tenanted, projectionFactory, notificationFactory); } /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// , and consumed by the specified + /// and + /// , and + /// consumed by the specified /// /// public static IServiceCollection RegisterTenantedEventing( @@ -57,27 +59,28 @@ public static IServiceCollection RegisterTenantedEventing(DependencyScope.Tenanted, projectionFactory); + return AddEventing(services, DependencyScope.Tenanted, projectionFactory); } /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// + /// and + /// /// public static IServiceCollection RegisterTenantedEventing( this IServiceCollection services) where TAggregateRoot : IEventingAggregateRoot, IDehydratableAggregateRoot { - return services.AddEventing(DependencyScope.Tenanted); + return AddEventing(services, DependencyScope.Tenanted); } /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// , and consumed by the specified + /// and + /// , and + /// consumed by the specified /// and /// public static IServiceCollection RegisterUnTenantedEventing( where TProjection : class, IReadModelProjection where TNotificationRegistration : class, IEventNotificationRegistration { - return services.AddEventing(DependencyScope.UnTenanted, + return AddEventing(services, DependencyScope.UnTenanted, projectionFactory, notificationFactory); } /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// , and consumed by the specified + /// and + /// , and + /// consumed by the specified /// /// public static IServiceCollection RegisterUnTenantedEventing( @@ -104,21 +108,21 @@ public static IServiceCollection RegisterUnTenantedEventing(DependencyScope.UnTenanted, + return AddEventing(services, DependencyScope.UnTenanted, projectionFactory); } /// /// Configures event projection and event notification of events produced by the specified /// and raised by both - /// and - /// + /// and + /// /// public static IServiceCollection RegisterUnTenantedEventing( this IServiceCollection services) where TAggregateRoot : IEventingAggregateRoot, IDehydratableAggregateRoot { - return services.AddEventing(DependencyScope.UnTenanted); + return AddEventing(services, DependencyScope.UnTenanted); } private static IServiceCollection AddEventing( @@ -129,7 +133,7 @@ private static IServiceCollection AddEventing(scope, projectionFactory); + AddEventing(services, scope, projectionFactory); Eventing.AddNotificationFactory(); services.RegisterLifetime(scope, notificationFactory); return services; @@ -141,7 +145,7 @@ private static IServiceCollection AddEventing( where TAggregateRoot : IEventingAggregateRoot, IDehydratableAggregateRoot where TProjection : class, IReadModelProjection { - services.AddEventing(scope); + AddEventing(services, scope); Eventing.AddProjectionFactory(); services.RegisterLifetime(scope, projectionFactory); return services; diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/ServiceCollectionExtensions.cs b/src/Infrastructure.Hosting.Common/Extensions/ServiceCollectionExtensions.cs similarity index 81% rename from src/Infrastructure.Web.Hosting.Common/Extensions/ServiceCollectionExtensions.cs rename to src/Infrastructure.Hosting.Common/Extensions/ServiceCollectionExtensions.cs index 6c5853f4..f6ceea7c 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Infrastructure.Hosting.Common/Extensions/ServiceCollectionExtensions.cs @@ -3,7 +3,7 @@ using Infrastructure.Interfaces; using Microsoft.Extensions.DependencyInjection; -namespace Infrastructure.Web.Hosting.Common.Extensions; +namespace Infrastructure.Hosting.Common.Extensions; public static class ServiceCollectionExtensions { @@ -77,17 +77,18 @@ public static IServiceCollection RegisterLifetime c.Resolve()); - services.RegisterLifetime(scope, typeof(TService2), c => c.Resolve()); - services.RegisterLifetime(scope, typeof(TService3), c => c.Resolve()); - services.RegisterLifetime(scope, typeof(TService4), c => c.Resolve()); + RegisterLifetime(services, scope, implementationFactory); + RegisterLifetime(services, scope, typeof(TService1), c => c.Resolve()); + RegisterLifetime(services, scope, typeof(TService2), c => c.Resolve()); + RegisterLifetime(services, scope, typeof(TService3), c => c.Resolve()); + RegisterLifetime(services, scope, typeof(TService4), c => c.Resolve()); return services; } /// - /// Registers an instance of the as a + /// Registers an instance of the as a + /// /// to be resolved only by /// public static IServiceCollection RegisterPlatform(this IServiceCollection services, @@ -110,7 +111,7 @@ public static IServiceCollection RegisterPlatform), c => c.Resolve>()); services.AddSingleton(typeof(IPlatformDependency), @@ -173,11 +174,11 @@ public static IServiceCollection RegisterTenanted c.ResolveForTenant()); - services.RegisterTenanted(typeof(TService2), c => c.ResolveForTenant()); - services.RegisterTenanted(typeof(TService3), c => c.ResolveForTenant()); - services.RegisterTenanted(typeof(TService4), c => c.ResolveForTenant()); + RegisterTenanted(services, implementationFactory); + RegisterTenanted(services, typeof(TService1), c => c.ResolveForTenant()); + RegisterTenanted(services, typeof(TService2), c => c.ResolveForTenant()); + RegisterTenanted(services, typeof(TService3), c => c.ResolveForTenant()); + RegisterTenanted(services, typeof(TService4), c => c.ResolveForTenant()); return services; } @@ -231,9 +232,9 @@ public static IServiceCollection RegisterUnshared c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService2), c => c.ResolveForUnshared()); + RegisterUnshared(services, implementationFactory); + RegisterUnshared(services, typeof(TService1), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService2), c => c.ResolveForUnshared()); return services; } @@ -249,10 +250,10 @@ public static IServiceCollection RegisterUnshared c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService2), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService3), c => c.ResolveForUnshared()); + RegisterUnshared(services, implementationFactory); + RegisterUnshared(services, typeof(TService1), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService2), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService3), c => c.ResolveForUnshared()); return services; } @@ -268,11 +269,11 @@ public static IServiceCollection RegisterUnshared c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService2), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService3), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService4), c => c.ResolveForUnshared()); + RegisterUnshared(services, implementationFactory); + RegisterUnshared(services, typeof(TService1), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService2), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService3), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService4), c => c.ResolveForUnshared()); return services; } @@ -290,12 +291,12 @@ public static IServiceCollection RegisterUnshared c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService2), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService3), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService4), c => c.ResolveForUnshared()); - services.RegisterUnshared(typeof(TService5), c => c.ResolveForUnshared()); + RegisterUnshared(services, implementationFactory); + RegisterUnshared(services, typeof(TService1), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService2), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService3), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService4), c => c.ResolveForUnshared()); + RegisterUnshared(services, typeof(TService5), c => c.ResolveForUnshared()); return services; } diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/ServiceProviderExtensions.cs b/src/Infrastructure.Hosting.Common/Extensions/ServiceProviderExtensions.cs similarity index 97% rename from src/Infrastructure.Web.Hosting.Common/Extensions/ServiceProviderExtensions.cs rename to src/Infrastructure.Hosting.Common/Extensions/ServiceProviderExtensions.cs index 878305d0..3a91f28b 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/ServiceProviderExtensions.cs +++ b/src/Infrastructure.Hosting.Common/Extensions/ServiceProviderExtensions.cs @@ -1,7 +1,7 @@ using Infrastructure.Interfaces; using Microsoft.Extensions.DependencyInjection; -namespace Infrastructure.Web.Hosting.Common.Extensions; +namespace Infrastructure.Hosting.Common.Extensions; public static class ServiceProviderExtensions { diff --git a/src/Infrastructure.Hosting.Common/HostOptions.cs b/src/Infrastructure.Hosting.Common/HostOptions.cs new file mode 100644 index 00000000..af3f3657 --- /dev/null +++ b/src/Infrastructure.Hosting.Common/HostOptions.cs @@ -0,0 +1,82 @@ +using Infrastructure.Common.Recording; + +namespace Infrastructure.Hosting.Common; + +public class HostOptions +{ + public static readonly HostOptions BackEndAncillaryApiHost = new("BackendWithAncillaryAPI") + { + IsMultiTenanted = false, //TODO: change for multi-tenanted + Persistence = new PersistenceOptions + { + UsesQueues = true, + UsesEventing = true + }, + Recording = RecorderOptions.BackEndAncillaryApiHost + }; + public static readonly HostOptions BackEndApiHost = new("BackendAPI") + { + IsMultiTenanted = false, //TODO: change for multi-tenanted + Persistence = new PersistenceOptions + { + UsesQueues = true, + UsesEventing = true + }, + Recording = RecorderOptions.BackEndApiHost + }; + + public static readonly HostOptions BackEndForFrontEndWebHost = new("FrontendSite") + { + IsMultiTenanted = false, //TODO: change for multi-tenanted + Persistence = new PersistenceOptions + { + UsesQueues = true, + UsesEventing = false + }, + Recording = RecorderOptions.BackEndForFrontEndWebHost + }; + + public static readonly HostOptions TestingStubsHost = new("TestingStubs") + { + IsMultiTenanted = false, //TODO: change for multi-tenanted + Persistence = new PersistenceOptions + { + UsesQueues = false, + UsesEventing = false + }, + Recording = RecorderOptions.TestingStubsHost + }; + + protected HostOptions(HostOptions options) : this(options.HostName, options.IsMultiTenanted, options.Persistence, + options.Recording) + { + } + + private HostOptions(string hostName, bool isMultiTenanted, PersistenceOptions persistence, + RecorderOptions recording) : this(hostName) + { + IsMultiTenanted = isMultiTenanted; + Persistence = persistence; + Recording = recording; + } + + private HostOptions(string hostName) + { + HostName = hostName; + IsMultiTenanted = false; + Persistence = new PersistenceOptions + { + UsesQueues = false, + UsesEventing = false + }; + Recording = new RecorderOptions(); + } + + public string HostName { get; protected init; } + + public bool IsMultiTenanted { get; protected init; } + + public PersistenceOptions Persistence { get; protected init; } + + public RecorderOptions Recording { get; protected init; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Hosting.Common/ApiHostSettings.cs b/src/Infrastructure.Hosting.Common/HostSettings.cs similarity index 75% rename from src/Infrastructure.Web.Hosting.Common/ApiHostSettings.cs rename to src/Infrastructure.Hosting.Common/HostSettings.cs index bd180512..cc788b23 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApiHostSettings.cs +++ b/src/Infrastructure.Hosting.Common/HostSettings.cs @@ -2,12 +2,12 @@ using Common.Configuration; using Common.Extensions; -namespace Infrastructure.Web.Hosting.Common; +namespace Infrastructure.Hosting.Common; /// /// Provides settings for any API host /// -public class ApiHostSettings : IApiHostSetting +public class HostSettings : IHostSetting { internal const string AncillaryApiHmacSecretSettingName = "Hosts:AncillaryApi:HmacAuthNSecret"; internal const string AncillaryApiHostBaseUrlSettingName = "Hosts:AncillaryApi:BaseUrl"; @@ -15,7 +15,7 @@ public class ApiHostSettings : IApiHostSetting private readonly IConfigurationSettings _settings; - public ApiHostSettings(IConfigurationSettings settings) + public HostSettings(IConfigurationSettings settings) { _settings = settings; } @@ -29,7 +29,7 @@ public string GetAncillaryApiHostBaseUrl() } throw new InvalidOperationException( - Resources.WebApiHostSettings_MissingSetting.Format(AncillaryApiHostBaseUrlSettingName)); + Resources.HostSettings_MissingSetting.Format(AncillaryApiHostBaseUrlSettingName)); } public string GetWebsiteHostBaseUrl() @@ -41,7 +41,7 @@ public string GetWebsiteHostBaseUrl() } throw new InvalidOperationException( - Resources.WebApiHostSettings_MissingSetting.Format(WebsiteHostBaseUrlSettingName)); + Resources.HostSettings_MissingSetting.Format(WebsiteHostBaseUrlSettingName)); } public string GetAncillaryApiHostHmacAuthSecret() @@ -53,6 +53,6 @@ public string GetAncillaryApiHostHmacAuthSecret() } throw new InvalidOperationException( - Resources.WebApiHostSettings_MissingSetting.Format(AncillaryApiHmacSecretSettingName)); + Resources.HostSettings_MissingSetting.Format(AncillaryApiHmacSecretSettingName)); } } \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj b/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj new file mode 100644 index 00000000..cef14330 --- /dev/null +++ b/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj @@ -0,0 +1,43 @@ + + + + net7.0 + true + + + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + True + True + Resources.resx + + + + + + + + diff --git a/src/Infrastructure.Hosting.Common/Recording/HostRecorder.cs b/src/Infrastructure.Hosting.Common/Recording/HostRecorder.cs new file mode 100644 index 00000000..b30f0d94 --- /dev/null +++ b/src/Infrastructure.Hosting.Common/Recording/HostRecorder.cs @@ -0,0 +1,289 @@ +using System.Text; +using Application.Interfaces; +using Common; +using Common.Configuration; +using Common.Extensions; +using Common.Recording; +using Domain.Interfaces.Services; +using Infrastructure.Common.Recording; +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Hosting.Common.Recording; + +/// +/// Provides a configured for use in a multi-environment Host +/// +public sealed class HostRecorder : IRecorder, IDisposable +{ + private readonly IAuditReporter _auditReporter; + private readonly ICrashReporter _crashReporter; + private readonly ILogger _logger; + private readonly IMetricReporter _metricsReporter; + private readonly string _usageComponentName; + private readonly IUsageReporter _usageReporter; + + public HostRecorder(IDependencyContainer container, ILoggerFactory loggerFactory, HostOptions options) : this( + container, loggerFactory.CreateLogger(options.HostName), options.Recording) + { + } + + internal HostRecorder(ILogger logger, RecorderOptions options, ICrashReporter crashReporter, + IAuditReporter auditReporter, IMetricReporter metricsReporter, IUsageReporter usageReporter) + { + _logger = logger; + _usageComponentName = options.UsageComponentName; + _crashReporter = crashReporter; + _auditReporter = auditReporter; + _metricsReporter = metricsReporter; + _usageReporter = usageReporter; + } + + private HostRecorder(IDependencyContainer container, ILogger logger, RecorderOptions options) : this(logger, + options, GetCrashReporter(container, options.CurrentEnvironment), + GetAuditReporter(container, options.CurrentEnvironment), + GetMetricReporter(container, options.CurrentEnvironment), + GetUsageReporter(container, options.CurrentEnvironment)) + { + } + + ~HostRecorder() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + // ReSharper disable once SuspiciousTypeConversion.Global + (_auditReporter as IDisposable)?.Dispose(); + // ReSharper disable once SuspiciousTypeConversion.Global + (_crashReporter as IDisposable)?.Dispose(); + // ReSharper disable once SuspiciousTypeConversion.Global + (_metricsReporter as IDisposable)?.Dispose(); + } + } + + public void TraceDebug(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogDebug(augmentedMessageTemplate, augmentedArguments); + } + + public void TraceInformation(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogInformation(exception, augmentedMessageTemplate, augmentedArguments); + } + + public void TraceInformation(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogInformation(augmentedMessageTemplate, augmentedArguments); + } + + public void TraceWarning(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogWarning(exception, augmentedMessageTemplate, augmentedArguments); + } + + public void TraceWarning(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogWarning(augmentedMessageTemplate, augmentedArguments); + } + + public void TraceError(ICallContext? context, Exception exception, string messageTemplate, + params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogError(exception, augmentedMessageTemplate, augmentedArguments); + } + + public void TraceError(ICallContext? context, string messageTemplate, params object[] templateArgs) + { + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, messageTemplate, templateArgs); + _logger.LogError(augmentedMessageTemplate, augmentedArguments); + } + + public void Crash(ICallContext? context, CrashLevel level, Exception exception) + { + Crash(context, level, exception, exception.Message); + } + + public void Crash(ICallContext? context, CrashLevel level, Exception exception, string messageTemplate, + params object[] templateArgs) + { + var logLevel = level == CrashLevel.Critical + ? LogLevel.Critical + : LogLevel.Error; + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, $"Crash: {messageTemplate}", templateArgs); + _logger.Log(logLevel, exception, augmentedMessageTemplate, augmentedArguments); + + var errorSourceId = exception.GetBaseException().TargetSite?.ToString(); + if (errorSourceId.Exists()) + { + _metricsReporter.Measure($"Exceptions: {errorSourceId}"); + } + + _crashReporter.Crash(context ?? CallContext.CreateUnknown(), level, exception, messageTemplate, templateArgs); + } + + public void Audit(ICallContext? context, string auditCode, string messageTemplate, params object[] templateArgs) + { + var safeCall = context ?? CallContext.CreateUnknown(); + var againstId = safeCall.CallerId; + AuditAgainst(safeCall, againstId, auditCode, messageTemplate, templateArgs); + } + + public void AuditAgainst(ICallContext? context, string againstId, string auditCode, string messageTemplate, + params object[] templateArgs) + { + var safeCall = context ?? CallContext.CreateUnknown(); + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, $"Audit: {auditCode}, against {againstId}, {messageTemplate}", + templateArgs); + TraceInformation(safeCall, augmentedMessageTemplate, augmentedArguments); + TrackUsageFor(safeCall, againstId, UsageConstants.Events.UsageScenarios.Audit, + new Dictionary + { + { UsageConstants.Properties.UsedById, againstId }, + { UsageConstants.Properties.AuditCode, auditCode.ToLowerInvariant() } + }); + _auditReporter.Audit(safeCall, againstId, auditCode, messageTemplate, templateArgs); + } + + public void TrackUsage(ICallContext? context, string eventName, Dictionary? additional = null) + { + var safeCall = context ?? CallContext.CreateUnknown(); + var forId = safeCall.CallerId; + TrackUsageFor(safeCall, forId, eventName, additional); + } + + public void TrackUsageFor(ICallContext? context, string forId, string eventName, + Dictionary? additional = null) + { + ArgumentException.ThrowIfNullOrEmpty(eventName); + ArgumentException.ThrowIfNullOrEmpty(forId); + + var safeCall = context ?? CallContext.CreateUnknown(); + var (augmentedMessageTemplate, augmentedArguments) = + AugmentMessageTemplateAndArguments(context, $"Usage: {eventName}, for {forId}"); + TraceInformation(context, augmentedMessageTemplate, augmentedArguments); + + var properties = additional ?? new Dictionary(); + properties.TryAdd(UsageConstants.Properties.Component, _usageComponentName); + _usageReporter.Track(safeCall, forId, eventName, properties); + } + + public void Measure(ICallContext? context, string eventName, Dictionary? additional = null) + { + TraceInformation(context, $"Measure: {eventName}"); + var usageContext = additional ?? new Dictionary(); + usageContext.Add(UsageConstants.Properties.MetricEventName, eventName.ToLowerInvariant()); + TrackUsage(context, UsageConstants.Events.UsageScenarios.Measurement, usageContext); + _metricsReporter.Measure(eventName, additional ?? new Dictionary()); + } + + private static ICrashReporter GetCrashReporter(IDependencyContainer container, + RecordingEnvironmentOptions options) + { + return options.CrashReporting switch + { + CrashReporterOption.None => new NullCrashReporter(), + CrashReporterOption.Cloud => +#if HOSTEDONAZURE + new ApplicationInsightsCrashReporter(container), +#elif HOSTEDONAWS + new NullCrashReporter(), +#endif + _ => throw new ArgumentOutOfRangeException(nameof(options.MetricReporting)) + }; + } + + private static IAuditReporter GetAuditReporter(IDependencyContainer container, + RecordingEnvironmentOptions options) + { + return options.AuditReporting switch + { + AuditReporterOption.None => new NullAuditReporter(), + AuditReporterOption.ReliableQueue => new QueuedAuditReporter(container, + container.Resolve().Platform), + _ => throw new ArgumentOutOfRangeException(nameof(options.MetricReporting)) + }; + } + + private static IMetricReporter GetMetricReporter(IDependencyContainer container, + RecordingEnvironmentOptions options) + { + return options.MetricReporting switch + { + MetricReporterOption.None => new NullMetricReporter(), + MetricReporterOption.Cloud => +#if HOSTEDONAZURE + new ApplicationInsightsMetricReporter(container), +#elif HOSTEDONAWS + new NullMetricReporter(), +#endif + MetricReporterOption.ForwardToAncillaryApi => new ForwardToAncillaryApiMetrics(container), + _ => throw new ArgumentOutOfRangeException(nameof(options.MetricReporting)) + }; + } + + private static IUsageReporter GetUsageReporter(IDependencyContainer container, + RecordingEnvironmentOptions options) + { + return options.UsageReporting switch + { + UsageReporterOption.None => new NullUsageReporter(), + UsageReporterOption.ReliableQueue => new QueuedUsageReporter(container, + container.Resolve().Platform), + UsageReporterOption.ForwardToAncillaryApi => new ForwardToAncillaryApiUsages(container), + _ => throw new ArgumentOutOfRangeException(nameof(options.MetricReporting)) + }; + } + + private static (string messageTemplate, object[] arguments) AugmentMessageTemplateAndArguments( + ICallContext? context, string messageTemplate, params object[] templateArgs) + { + var arguments = new List(templateArgs); + var builder = new StringBuilder(); + + var unknownCall = CallContext.CreateUnknown(); + var currentCall = context ?? unknownCall; + + builder.Append("Request: {Request}"); + arguments.Insert(0, currentCall.CallId.HasValue() + ? currentCall.CallId + : unknownCall.CallId); + + builder.Append(" By: {Caller}"); + arguments.Insert(1, currentCall.CallerId.HasValue() + ? currentCall.CallerId + : unknownCall.CallerId); + + if (messageTemplate.HasValue()) + { + builder.Append($": {messageTemplate}"); + } + + return (builder.ToString(), arguments.ToArray()); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Hosting.Common/TracingOnlyRecorder.cs b/src/Infrastructure.Hosting.Common/Recording/TracingOnlyRecorder.cs similarity index 99% rename from src/Infrastructure.Web.Hosting.Common/TracingOnlyRecorder.cs rename to src/Infrastructure.Hosting.Common/Recording/TracingOnlyRecorder.cs index 0b0a9af4..3f086da8 100644 --- a/src/Infrastructure.Web.Hosting.Common/TracingOnlyRecorder.cs +++ b/src/Infrastructure.Hosting.Common/Recording/TracingOnlyRecorder.cs @@ -6,7 +6,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Logging; -namespace Infrastructure.Web.Hosting.Common; +namespace Infrastructure.Hosting.Common.Recording; /// /// Provides a that only supports tracing diff --git a/src/Infrastructure.Hosting.Common/Resources.Designer.cs b/src/Infrastructure.Hosting.Common/Resources.Designer.cs new file mode 100644 index 00000000..285ed188 --- /dev/null +++ b/src/Infrastructure.Hosting.Common/Resources.Designer.cs @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Infrastructure.Hosting.Common { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Infrastructure.Hosting.Common.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The setting '{0}' cannot be found in any configuration. + /// + internal static string AspNetConfigurationSettings_KeyNotFound { + get { + return ResourceManager.GetString("AspNetConfigurationSettings_KeyNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The setting '{0}' exists, but is not a recognizable boolean value. + /// + internal static string AspNetConfigurationSettings_ValueNotBoolean { + get { + return ResourceManager.GetString("AspNetConfigurationSettings_ValueNotBoolean", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The setting '{0}' exists, but is not a recognizable number value. + /// + internal static string AspNetConfigurationSettings_ValueNotNumber { + get { + return ResourceManager.GetString("AspNetConfigurationSettings_ValueNotNumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to relay new events to read model for: {0}. + /// + internal static string EventStreamHandlerBase_OnEventStreamStateChanged_FailedToProject { + get { + return ResourceManager.GetString("EventStreamHandlerBase_OnEventStreamStateChanged_FailedToProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The event stream {0} contains events with out of order versions.. + /// + internal static string EventStreamHandlerBase_OutOfOrderEvents { + get { + return ResourceManager.GetString("EventStreamHandlerBase_OutOfOrderEvents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' cannot be found in appsettings.json for this host. + /// + internal static string HostSettings_MissingSetting { + get { + return ResourceManager.GetString("HostSettings_MissingSetting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Recorder is ready to record. + /// + internal static string TracingOnlyRecorder_Started { + get { + return ResourceManager.GetString("TracingOnlyRecorder_Started", resourceCulture); + } + } + } +} diff --git a/src/Infrastructure.Hosting.Common/Resources.resx b/src/Infrastructure.Hosting.Common/Resources.resx new file mode 100644 index 00000000..268f0909 --- /dev/null +++ b/src/Infrastructure.Hosting.Common/Resources.resx @@ -0,0 +1,47 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + The setting '{0}' cannot be found in any configuration + + + The setting '{0}' exists, but is not a recognizable boolean value + + + The setting '{0}' exists, but is not a recognizable number value + + + Recorder is ready to record + + + '{0}' cannot be found in appsettings.json for this host + + + The event stream {0} contains events with out of order versions. + + + Failed to relay new events to read model for: {0} + + \ No newline at end of file diff --git a/src/Infrastructure.Interfaces/Infrastructure.Interfaces.csproj b/src/Infrastructure.Interfaces/Infrastructure.Interfaces.csproj index fbd55c3d..cb1f1d1c 100644 --- a/src/Infrastructure.Interfaces/Infrastructure.Interfaces.csproj +++ b/src/Infrastructure.Interfaces/Infrastructure.Interfaces.csproj @@ -5,9 +5,4 @@ true - - - - - diff --git a/src/Infrastructure.Persistence.Common.UnitTests/Infrastructure.Persistence.Common.UnitTests.csproj b/src/Infrastructure.Persistence.Common.UnitTests/Infrastructure.Persistence.Common.UnitTests.csproj index faee6503..2a3ba8d7 100644 --- a/src/Infrastructure.Persistence.Common.UnitTests/Infrastructure.Persistence.Common.UnitTests.csproj +++ b/src/Infrastructure.Persistence.Common.UnitTests/Infrastructure.Persistence.Common.UnitTests.csproj @@ -5,14 +5,14 @@ true - - - - + + + + diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IBlobStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IBlobStore.cs similarity index 98% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IBlobStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IBlobStore.cs index de1b8ad3..49812360 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IBlobStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IBlobStore.cs @@ -6,7 +6,7 @@ using Infrastructure.Persistence.Interfaces; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; public partial class InProcessInMemStore : IBlobStore { diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IDataStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IDataStore.cs similarity index 98% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IDataStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IDataStore.cs index 9cc8b81a..9b64438e 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IDataStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IDataStore.cs @@ -7,7 +7,7 @@ using QueryAny; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; public partial class InProcessInMemStore : IDataStore { diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IEventStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IEventStore.cs similarity index 97% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IEventStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IEventStore.cs index 6ebd936d..633d76ba 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IEventStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IEventStore.cs @@ -3,13 +3,12 @@ using Common.Extensions; using Domain.Interfaces; using Domain.Interfaces.Entities; -using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Common.Extensions; using Infrastructure.Persistence.Interfaces; using QueryAny; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; public partial class InProcessInMemStore : IEventStore { diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IQueueStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IQueueStore.cs similarity index 98% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IQueueStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IQueueStore.cs index 0f08af2d..aa177730 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.IQueueStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.IQueueStore.cs @@ -6,7 +6,7 @@ using Infrastructure.Persistence.Interfaces.ApplicationServices; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; public partial class InProcessInMemStore : IQueueStore, IQueueStoreTrigger { diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.cs similarity index 94% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.cs index 723cd9b6..812c5cf0 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/InProcessInMemStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/InProcessInMemStore.cs @@ -5,7 +5,7 @@ using Infrastructure.Persistence.Interfaces; using Infrastructure.Persistence.Interfaces.ApplicationServices; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; /// /// Provides a combined store that persists all data to memory, in the current process diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs similarity index 94% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs index c8eafde3..6fb9b11b 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IBlobStore.cs @@ -5,7 +5,7 @@ using Infrastructure.Persistence.Interfaces; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; public partial class LocalMachineJsonFileStore : IBlobStore { @@ -88,13 +88,13 @@ private static string GetBlobStoreContainerPath(string containerName, string? en return $"{BlobStoreContainerName}/{containerName}"; } -} -internal class BinaryFile -{ - public required string ContentType { get; set; } + private class BinaryFile + { + public required string ContentType { get; set; } - public required byte[] Data { get; set; } + public required byte[] Data { get; set; } + } } #endif \ No newline at end of file diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs similarity index 99% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs index ab02b22c..28d8c236 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IDataStore.cs @@ -7,7 +7,7 @@ using QueryAny; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; /// /// Defines a file repository on the local machine, that stores each entity as raw JSON. diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs similarity index 97% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs index 938c5c94..a1b4f708 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IEventStore.cs @@ -3,13 +3,12 @@ using Common.Extensions; using Domain.Interfaces; using Domain.Interfaces.Entities; -using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Common.Extensions; using Infrastructure.Persistence.Interfaces; using QueryAny; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; /// /// Defines a file repository on the local machine, that stores each entity as raw JSON. diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs similarity index 98% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs index be8899b7..b4f83df7 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.IQueueStore.cs @@ -5,7 +5,7 @@ using Infrastructure.Persistence.Interfaces.ApplicationServices; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; /// /// Defines a file repository on the local machine, that stores each entity as raw JSON. diff --git a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.cs b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.cs similarity index 99% rename from src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.cs rename to src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.cs index 111e9095..af985448 100644 --- a/src/Infrastructure.Persistence.Shared/ApplicationServices/LocalMachineJsonFileStore.cs +++ b/src/Infrastructure.Persistence.Common/ApplicationServices/LocalMachineJsonFileStore.cs @@ -9,7 +9,7 @@ using Infrastructure.Persistence.Interfaces.ApplicationServices; using Polly; -namespace Infrastructure.Persistence.Shared.ApplicationServices; +namespace Infrastructure.Persistence.Common.ApplicationServices; /// /// Provides a combined store that persists all data to individual files of JSON on the local hard drive. diff --git a/src/Infrastructure.Persistence.Common/Resources.Designer.cs b/src/Infrastructure.Persistence.Common/Resources.Designer.cs index 18eb8fe2..f4973051 100644 --- a/src/Infrastructure.Persistence.Common/Resources.Designer.cs +++ b/src/Infrastructure.Persistence.Common/Resources.Designer.cs @@ -68,6 +68,24 @@ internal static string AnyStore_EmptyContainerName { } } + /// + /// Looks up a localized string similar to ContainerName cannot be empty. + /// + internal static string AnyStore_MissingContainerName { + get { + return ResourceManager.GetString("AnyStore_MissingContainerName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Id cannot be empty. + /// + internal static string AnyStore_MissingId { + get { + return ResourceManager.GetString("AnyStore_MissingId", resourceCulture); + } + } + /// /// Looks up a localized string similar to Blob must have a content type. /// @@ -131,6 +149,69 @@ internal static string IEventSourcingDddCommandStore_StreamTombstoned { } } + /// + /// Looks up a localized string similar to BlobName cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingBlobName { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingBlobName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ContentType cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingContentType { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingContentType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to EntityId cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingEntityId { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingEntityId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to EntityName cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingEntityName { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingEntityName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingQueueMessage { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingQueueMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to QueueName cannot be empty. + /// + internal static string InProcessInMemDataStore_MissingQueueName { + get { + return ResourceManager.GetString("InProcessInMemDataStore_MissingQueueName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified root path: {0}, is not a valid path or for some reason cannot be created on this local machine.. + /// + internal static string LocalMachineJsonFileStore_VerifyRootPath_CreateFailed { + get { + return ResourceManager.GetString("LocalMachineJsonFileStore_VerifyRootPath_CreateFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Id cannot be empty. /// diff --git a/src/Infrastructure.Persistence.Common/Resources.resx b/src/Infrastructure.Persistence.Common/Resources.resx index 0896a078..f262e48a 100644 --- a/src/Infrastructure.Persistence.Common/Resources.resx +++ b/src/Infrastructure.Persistence.Common/Resources.resx @@ -69,4 +69,31 @@ Publishing of changed events for {0} failed. + + Id cannot be empty + + + ContainerName cannot be empty + + + BlobName cannot be empty + + + ContentType cannot be empty + + + QueueName cannot be empty + + + Message cannot be empty + + + EntityName cannot be empty + + + EntityId cannot be empty + + + The specified root path: {0}, is not a valid path or for some reason cannot be created on this local machine. + \ No newline at end of file diff --git a/src/Infrastructure.Persistence.Interfaces.UnitTests/Infrastructure.Persistence.Interfaces.UnitTests.csproj b/src/Infrastructure.Persistence.Interfaces.UnitTests/Infrastructure.Persistence.Interfaces.UnitTests.csproj index f2831f2b..9c565d83 100644 --- a/src/Infrastructure.Persistence.Interfaces.UnitTests/Infrastructure.Persistence.Interfaces.UnitTests.csproj +++ b/src/Infrastructure.Persistence.Interfaces.UnitTests/Infrastructure.Persistence.Interfaces.UnitTests.csproj @@ -6,13 +6,12 @@ - + + - - - + diff --git a/src/Infrastructure.Persistence.Shared.IntegrationTests/InProcessInMemStoreSpec.cs b/src/Infrastructure.Persistence.Shared.IntegrationTests/InProcessInMemStoreSpec.cs index a8dcea0a..b5fcd578 100644 --- a/src/Infrastructure.Persistence.Shared.IntegrationTests/InProcessInMemStoreSpec.cs +++ b/src/Infrastructure.Persistence.Shared.IntegrationTests/InProcessInMemStoreSpec.cs @@ -1,6 +1,6 @@ #if TESTINGONLY +using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Persistence.Shared.ApplicationServices; using JetBrains.Annotations; using Xunit; diff --git a/src/Infrastructure.Persistence.Shared.IntegrationTests/Infrastructure.Persistence.Shared.IntegrationTests.csproj b/src/Infrastructure.Persistence.Shared.IntegrationTests/Infrastructure.Persistence.Shared.IntegrationTests.csproj index 6ddbe72e..2b4a5d33 100644 --- a/src/Infrastructure.Persistence.Shared.IntegrationTests/Infrastructure.Persistence.Shared.IntegrationTests.csproj +++ b/src/Infrastructure.Persistence.Shared.IntegrationTests/Infrastructure.Persistence.Shared.IntegrationTests.csproj @@ -11,9 +11,8 @@ + - - diff --git a/src/Infrastructure.Persistence.Shared.IntegrationTests/LocalMachineJsonFileStoreSpec.cs b/src/Infrastructure.Persistence.Shared.IntegrationTests/LocalMachineJsonFileStoreSpec.cs index f8c52440..9f12ee28 100644 --- a/src/Infrastructure.Persistence.Shared.IntegrationTests/LocalMachineJsonFileStoreSpec.cs +++ b/src/Infrastructure.Persistence.Shared.IntegrationTests/LocalMachineJsonFileStoreSpec.cs @@ -1,6 +1,6 @@ #if TESTINGONLY +using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Persistence.Shared.ApplicationServices; using JetBrains.Annotations; using Xunit; diff --git a/src/Infrastructure.Persistence.Shared.IntegrationTests/StoreSpecSetupBase.cs b/src/Infrastructure.Persistence.Shared.IntegrationTests/StoreSpecSetupBase.cs index 347eefaa..8ead6e4d 100644 --- a/src/Infrastructure.Persistence.Shared.IntegrationTests/StoreSpecSetupBase.cs +++ b/src/Infrastructure.Persistence.Shared.IntegrationTests/StoreSpecSetupBase.cs @@ -1,5 +1,5 @@ using Common.Configuration; -using Infrastructure.Web.Hosting.Common.ApplicationServices; +using Infrastructure.Hosting.Common; using Microsoft.Extensions.Configuration; namespace Infrastructure.Persistence.Shared.IntegrationTests; diff --git a/src/AncillaryInfrastructure/Persistence/AuditMessageQueueRepository.cs b/src/Infrastructure.Persistence.Shared/ApplicationServices/AuditMessageQueueRepository.cs similarity index 90% rename from src/AncillaryInfrastructure/Persistence/AuditMessageQueueRepository.cs rename to src/Infrastructure.Persistence.Shared/ApplicationServices/AuditMessageQueueRepository.cs index d3416fb6..6984d214 100644 --- a/src/AncillaryInfrastructure/Persistence/AuditMessageQueueRepository.cs +++ b/src/Infrastructure.Persistence.Shared/ApplicationServices/AuditMessageQueueRepository.cs @@ -1,11 +1,10 @@ using Application.Persistence.Interfaces; using Application.Persistence.Shared; -using Application.Services.Shared; using Common; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; -namespace AncillaryInfrastructure.Persistence; +namespace Infrastructure.Persistence.Shared.ApplicationServices; public class AuditMessageQueueRepository : IAuditMessageQueueRepository { @@ -40,6 +39,6 @@ public Task> PushAsync(ICallContext call, AuditMessage message, Ca Task> IApplicationRepository.DestroyAllAsync(CancellationToken cancellationToken) { - return _messageQueue.DestroyAllAsync(cancellationToken); + return DestroyAllAsync(cancellationToken); } } \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Persistence/UsageMessageQueueRepository.cs b/src/Infrastructure.Persistence.Shared/ApplicationServices/UsageMessageQueueRepository.cs similarity index 90% rename from src/AncillaryInfrastructure/Persistence/UsageMessageQueueRepository.cs rename to src/Infrastructure.Persistence.Shared/ApplicationServices/UsageMessageQueueRepository.cs index 01e7f6bd..bb817f68 100644 --- a/src/AncillaryInfrastructure/Persistence/UsageMessageQueueRepository.cs +++ b/src/Infrastructure.Persistence.Shared/ApplicationServices/UsageMessageQueueRepository.cs @@ -1,11 +1,10 @@ using Application.Persistence.Interfaces; using Application.Persistence.Shared; -using Application.Services.Shared; using Common; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; -namespace AncillaryInfrastructure.Persistence; +namespace Infrastructure.Persistence.Shared.ApplicationServices; public class UsageMessageQueueRepository : IUsageMessageQueueRepository { @@ -40,6 +39,6 @@ public Task> PushAsync(ICallContext call, UsageMessage message, Ca Task> IApplicationRepository.DestroyAllAsync(CancellationToken cancellationToken) { - return _messageQueue.DestroyAllAsync(cancellationToken); + return DestroyAllAsync(cancellationToken); } } \ No newline at end of file diff --git a/src/Infrastructure.Persistence.Shared/Infrastructure.Persistence.Shared.csproj b/src/Infrastructure.Persistence.Shared/Infrastructure.Persistence.Shared.csproj index 31f16aa6..bf4130ea 100644 --- a/src/Infrastructure.Persistence.Shared/Infrastructure.Persistence.Shared.csproj +++ b/src/Infrastructure.Persistence.Shared/Infrastructure.Persistence.Shared.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Infrastructure.Persistence.Shared/Resources.Designer.cs b/src/Infrastructure.Persistence.Shared/Resources.Designer.cs index 6f9a0883..e76b47e4 100644 --- a/src/Infrastructure.Persistence.Shared/Resources.Designer.cs +++ b/src/Infrastructure.Persistence.Shared/Resources.Designer.cs @@ -76,68 +76,5 @@ internal static string AnyStore_MissingId { return ResourceManager.GetString("AnyStore_MissingId", resourceCulture); } } - - /// - /// Looks up a localized string similar to BlobName cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingBlobName { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingBlobName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ContentType cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingContentType { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingContentType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to EntityId cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingEntityId { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingEntityId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to EntityName cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingEntityName { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingEntityName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingQueueMessage { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingQueueMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QueueName cannot be empty. - /// - internal static string InProcessInMemDataStore_MissingQueueName { - get { - return ResourceManager.GetString("InProcessInMemDataStore_MissingQueueName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The specified root path: {0}, is not a valid path or for some reason cannot be created on this local machine.. - /// - internal static string LocalMachineJsonFileStore_VerifyRootPath_CreateFailed { - get { - return ResourceManager.GetString("LocalMachineJsonFileStore_VerifyRootPath_CreateFailed", resourceCulture); - } - } } } diff --git a/src/Infrastructure.Persistence.Shared/Resources.resx b/src/Infrastructure.Persistence.Shared/Resources.resx index 4728c41a..f312d737 100644 --- a/src/Infrastructure.Persistence.Shared/Resources.resx +++ b/src/Infrastructure.Persistence.Shared/Resources.resx @@ -30,25 +30,4 @@ ContainerName cannot be empty - - BlobName cannot be empty - - - ContentType cannot be empty - - - QueueName cannot be empty - - - Message cannot be empty - - - EntityName cannot be empty - - - EntityId cannot be empty - - - The specified root path: {0}, is not a valid path or for some reason cannot be created on this local machine. - \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Common.UnitTests/Infrastructure.Web.Api.Common.UnitTests.csproj b/src/Infrastructure.Web.Api.Common.UnitTests/Infrastructure.Web.Api.Common.UnitTests.csproj index 9fbe9c56..00f4b0b4 100644 --- a/src/Infrastructure.Web.Api.Common.UnitTests/Infrastructure.Web.Api.Common.UnitTests.csproj +++ b/src/Infrastructure.Web.Api.Common.UnitTests/Infrastructure.Web.Api.Common.UnitTests.csproj @@ -6,11 +6,11 @@ - + + - - + diff --git a/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs b/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs index fe13158b..f129c6cb 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs +++ b/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs @@ -59,6 +59,33 @@ public static string ToUrl(this IWebRequest request) return request.GetRequestInfo().Route; } + /// + /// returns the inbound request as a + /// + public static IWebRequest? ToWebRequest(this HttpContext httpContext) + { + //HACK: We need to find a way to get to the type of our IWebRequest in the EndPoint.RequestDelegate + //var request = httpContext.Request; + // var endpoint = httpContext.GetEndpoint(); + // if (endpoint is null || endpoint.RequestDelegate is null) + // { + // return null; + // } + + //which should all be following the predictable pattern like this: + // carsapiGroup.MapPatch("/cars/{id}/maintain", + // async (global::MediatR.IMediator mediator, global::Infrastructure.Web.Api.Operations.Shared.Cars.ScheduleMaintenanceCarRequest request) => + // await mediator.Send(request, global::System.Threading.CancellationToken.None)); + // carsapiGroup.MapGet("/cars", + // async (global::MediatR.IMediator mediator, [global::Microsoft.AspNetCore.Http.AsParameters] global::Infrastructure.Web.Api.Operations.Shared.Cars.SearchAllCarsRequest request) => + // await mediator.Send(request, global::System.Threading.CancellationToken.None)); + + //We want the type of the XXXRequest class (2nd parameter of the handler function), + // and then we want to return the actual instance of it + + return null; + } + private static RouteAttribute GetRouteFromAttribute(IWebRequest request) { var attribute = request.GetType().GetCustomAttribute(); diff --git a/src/Infrastructure.Web.Api.Common/Extensions/WebApplicationExtensions.cs b/src/Infrastructure.Web.Api.Common/Extensions/WebApplicationExtensions.cs index 6f6fa011..e7cd4cc9 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/WebApplicationExtensions.cs +++ b/src/Infrastructure.Web.Api.Common/Extensions/WebApplicationExtensions.cs @@ -1,7 +1,12 @@ using System.Net; +using Application.Common; +using Application.Interfaces; +using Application.Interfaces.Resources; +using Common; using Common.Extensions; using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Eventing.Interfaces.Projections; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Http; @@ -13,6 +18,23 @@ namespace Infrastructure.Web.Api.Common.Extensions; public static class WebApplicationExtensions { + public static readonly Type[] IgnoredTrackedRequestTypes = + { + // Exclude these as they are not API's called by users +#if TESTINGONLY + typeof(DrainAllAuditsRequest), + typeof(DrainAllUsagesRequest), + typeof(SearchAllAuditsRequest), +#endif + //typeof(GetHealthCheckRequest), + typeof(DeliverUsageRequest), + typeof(DeliverAuditRequest), + + // Exclude these or we will get a Stackoverflow! + typeof(RecordUseRequest), + typeof(RecordMeasureRequest) + }; + /// /// Starts the relays for eventing projections and notifications /// @@ -81,6 +103,29 @@ await Results.Problem(details) })); } + /// + /// Enables the tracking of all inbound API calls + /// + /// + public static WebApplication EnableApiUsageTracking(this WebApplication app, bool tracksUsage) + { + if (!tracksUsage) + { + return app; + } + + app.Use(async (context, next) => + { + var recorder = context.RequestServices.GetRequiredService(); + var caller = context.RequestServices.GetRequiredService(); + + TrackUsage(context, recorder, caller); + await next(); + }); + + return app; + } + /// /// Enables request buffering, so that request bodies can be read in filters /// @@ -92,4 +137,37 @@ public static void EnableRequestRewind(this WebApplication app) await next(); }); } + + private static void TrackUsage(HttpContext httpContext, IRecorder recorder, ICallerContext caller) + { + var request = httpContext.ToWebRequest(); + var requestType = request?.GetType(); + if (requestType.NotExists()) + { + return; + } + + if (IgnoredTrackedRequestTypes.Contains(requestType)) + { + return; + } + + var requestName = requestType.Name.ToLowerInvariant(); + var additional = new Dictionary + { + { UsageConstants.Properties.EndPoint, requestName } + }; + var requestAsProperties = request + .ToObjectDictionary() + .ToDictionary(pair => pair.Key, pair => pair.Value?.ToString()); + if (requestAsProperties.TryGetValue(nameof(IIdentifiableResource.Id), out var id)) + { + if (id.Exists()) + { + additional.Add(nameof(IIdentifiableResource.Id), id); + } + } + + recorder.TrackUsage(caller.ToCall(), UsageConstants.Events.Api.ApiEndpointRequested, additional); + } } \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Common/Infrastructure.Web.Api.Common.csproj b/src/Infrastructure.Web.Api.Common/Infrastructure.Web.Api.Common.csproj index 7330c597..c22112cb 100644 --- a/src/Infrastructure.Web.Api.Common/Infrastructure.Web.Api.Common.csproj +++ b/src/Infrastructure.Web.Api.Common/Infrastructure.Web.Api.Common.csproj @@ -6,9 +6,10 @@ - + + diff --git a/src/Infrastructure.Web.Api.IntegrationTests/Infrastructure.Web.Api.IntegrationTests.csproj b/src/Infrastructure.Web.Api.IntegrationTests/Infrastructure.Web.Api.IntegrationTests.csproj index 8fefb12b..246ee85d 100644 --- a/src/Infrastructure.Web.Api.IntegrationTests/Infrastructure.Web.Api.IntegrationTests.csproj +++ b/src/Infrastructure.Web.Api.IntegrationTests/Infrastructure.Web.Api.IntegrationTests.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordMeasureRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordMeasureRequest.cs new file mode 100644 index 00000000..56c91fa6 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordMeasureRequest.cs @@ -0,0 +1,11 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +[Route("/record/measure", ServiceOperation.Post)] +public class RecordMeasureRequest : UnTenantedEmptyRequest +{ + public Dictionary? Additional { get; set; } + + public required string EventName { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordUsageRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordUsageRequest.cs new file mode 100644 index 00000000..0d1fe0cc --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/RecordUsageRequest.cs @@ -0,0 +1,11 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +[Route("/record/use", ServiceOperation.Post)] +public class RecordUseRequest : UnTenantedEmptyRequest +{ + public Dictionary? Additional { get; set; } + + public required string EventName { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs b/src/Infrastructure.Web.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs similarity index 97% rename from src/Infrastructure.Web.Api.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs rename to src/Infrastructure.Web.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs index 2a6f014b..691132a7 100644 --- a/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs +++ b/src/Infrastructure.Web.Common.UnitTests/Extensions/ResponseProblemExtensionsSpec.cs @@ -2,14 +2,15 @@ using Common; using Common.Extensions; using FluentAssertions; -using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Common.Extensions; +using Infrastructure.Web.Interfaces.Clients; using Microsoft.AspNetCore.Mvc; using UnitTesting.Common; using Xunit; -namespace Infrastructure.Web.Api.Common.UnitTests.Extensions; +namespace Infrastructure.Web.Common.UnitTests.Extensions; [Trait("Category", "Unit")] public class ResponseProblemExtensionsSpec diff --git a/src/Infrastructure.Web.Common.UnitTests/Infrastructure.Web.Common.UnitTests.csproj b/src/Infrastructure.Web.Common.UnitTests/Infrastructure.Web.Common.UnitTests.csproj new file mode 100644 index 00000000..1555c582 --- /dev/null +++ b/src/Infrastructure.Web.Common.UnitTests/Infrastructure.Web.Common.UnitTests.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + true + + + + + + + + + + + + diff --git a/src/Infrastructure.Web.Api.Common/Clients/ApiClientRetryPolicies.cs b/src/Infrastructure.Web.Common/Clients/ApiClientRetryPolicies.cs similarity index 96% rename from src/Infrastructure.Web.Api.Common/Clients/ApiClientRetryPolicies.cs rename to src/Infrastructure.Web.Common/Clients/ApiClientRetryPolicies.cs index 194cd1c1..38187c2e 100644 --- a/src/Infrastructure.Web.Api.Common/Clients/ApiClientRetryPolicies.cs +++ b/src/Infrastructure.Web.Common/Clients/ApiClientRetryPolicies.cs @@ -5,7 +5,7 @@ using Polly.Retry; using Polly.Timeout; -namespace Infrastructure.Web.Api.Common.Clients; +namespace Infrastructure.Web.Common.Clients; public static class ApiClientRetryPolicies { diff --git a/src/Infrastructure.Web.Api.Common/Clients/InterHostServiceClient.cs b/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs similarity index 98% rename from src/Infrastructure.Web.Api.Common/Clients/InterHostServiceClient.cs rename to src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs index 24600f38..dcdda36e 100644 --- a/src/Infrastructure.Web.Api.Common/Clients/InterHostServiceClient.cs +++ b/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs @@ -5,11 +5,11 @@ using Common.Extensions; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Interfaces.Clients; using Polly.Retry; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Web.Api.Common.Clients; +namespace Infrastructure.Web.Common.Clients; /// /// A service client used to call between API hosts, with retries diff --git a/src/Infrastructure.Web.Api.Common/Clients/JsonClient.cs b/src/Infrastructure.Web.Common/Clients/JsonClient.cs similarity index 97% rename from src/Infrastructure.Web.Api.Common/Clients/JsonClient.cs rename to src/Infrastructure.Web.Common/Clients/JsonClient.cs index d5bd470d..e2c006a4 100644 --- a/src/Infrastructure.Web.Api.Common/Clients/JsonClient.cs +++ b/src/Infrastructure.Web.Common/Clients/JsonClient.cs @@ -3,13 +3,16 @@ using System.Net.Http.Json; using Common; using Common.Extensions; +using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Common.Extensions; +using Infrastructure.Web.Interfaces.Clients; using Microsoft.AspNetCore.Mvc; +using HttpHeaders = Infrastructure.Web.Api.Common.HttpHeaders; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Web.Api.Common.Clients; +namespace Infrastructure.Web.Common.Clients; /// /// Provides a convenient typed that accepts and returns JSON diff --git a/src/Infrastructure.Web.Api.Common/Extensions/ResponseProblemExtensions.cs b/src/Infrastructure.Web.Common/Extensions/ResponseProblemExtensions.cs similarity index 94% rename from src/Infrastructure.Web.Api.Common/Extensions/ResponseProblemExtensions.cs rename to src/Infrastructure.Web.Common/Extensions/ResponseProblemExtensions.cs index 2021314d..9f2393b7 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/ResponseProblemExtensions.cs +++ b/src/Infrastructure.Web.Common/Extensions/ResponseProblemExtensions.cs @@ -1,11 +1,12 @@ using System.Net; using Common; using Common.Extensions; +using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Interfaces.Clients; using Microsoft.AspNetCore.Mvc; -namespace Infrastructure.Web.Api.Common.Extensions; +namespace Infrastructure.Web.Common.Extensions; public static class ResponseProblemExtensions { @@ -49,7 +50,7 @@ public static Exception ToException(this ResponseProblem problem) } /// - /// Converts the given to a + /// Converts the given to a /// public static ResponseProblem ToResponseProblem(this ProblemDetails? details) { diff --git a/src/Infrastructure.Web.Common/Infrastructure.Web.Common.csproj b/src/Infrastructure.Web.Common/Infrastructure.Web.Common.csproj new file mode 100644 index 00000000..c10c0bfb --- /dev/null +++ b/src/Infrastructure.Web.Common/Infrastructure.Web.Common.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + true + + + + + + + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + + + diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/Infrastructure.Web.Hosting.Common.UnitTests.csproj b/src/Infrastructure.Web.Hosting.Common.UnitTests/Infrastructure.Web.Hosting.Common.UnitTests.csproj index 0413360c..d2f65a37 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/Infrastructure.Web.Hosting.Common.UnitTests.csproj +++ b/src/Infrastructure.Web.Hosting.Common.UnitTests/Infrastructure.Web.Hosting.Common.UnitTests.csproj @@ -5,16 +5,16 @@ true - - - - + + + + true @@ -28,8 +28,4 @@ - - - - diff --git a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/StubQueueDrainingService.cs b/src/Infrastructure.Web.Hosting.Common/ApplicationServices/StubQueueDrainingService.cs index f19e91e5..47fa22ca 100644 --- a/src/Infrastructure.Web.Hosting.Common/ApplicationServices/StubQueueDrainingService.cs +++ b/src/Infrastructure.Web.Hosting.Common/ApplicationServices/StubQueueDrainingService.cs @@ -2,9 +2,9 @@ using Application.Interfaces.Services; using Common.Extensions; using Infrastructure.Persistence.Interfaces.ApplicationServices; -using Infrastructure.Web.Api.Common.Clients; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Common.Clients; +using Infrastructure.Web.Interfaces.Clients; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Task = System.Threading.Tasks.Task; @@ -27,7 +27,7 @@ public class StubQueueDrainingService : BackgroundService private readonly IMonitoredMessageQueues _monitoredMessageQueues; private readonly Dictionary _monitorQueueMappings; - public StubQueueDrainingService(IHttpClientFactory httpClientFactory, IApiHostSetting settings, + public StubQueueDrainingService(IHttpClientFactory httpClientFactory, IHostSetting settings, ILogger logger, IMonitoredMessageQueues monitoredMessageQueues, Dictionary monitoredQueueApiMappings) : this(httpClientFactory, logger, monitoredMessageQueues, monitoredQueueApiMappings, settings.GetAncillaryApiHostBaseUrl()) diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs index 6dd5242c..a252c291 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs +++ b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs @@ -12,7 +12,11 @@ using Infrastructure.Common; using Infrastructure.Common.DomainServices; using Infrastructure.Eventing.Common.Projections.ReadModels; +using Infrastructure.Hosting.Common; +using Infrastructure.Hosting.Common.Extensions; +using Infrastructure.Hosting.Common.Recording; using Infrastructure.Interfaces; +using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Interfaces; using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Common.Extensions; @@ -25,7 +29,7 @@ using Microsoft.Extensions.Logging; #if TESTINGONLY using Infrastructure.Persistence.Interfaces.ApplicationServices; -using Infrastructure.Persistence.Shared.ApplicationServices; + #else using Infrastructure.Persistence.Common.ApplicationServices; #endif @@ -48,15 +52,17 @@ public static WebApplication ConfigureApiHost(this WebApplicationBuilder builder ConfigureWireFormats(); ConfigureApiRequests(); ConfigureApplicationServices(); - ConfigurePersistence(options.UsesQueues); + ConfigurePersistence(options.Persistence.UsesQueues); var app = builder.Build(); app.EnableRequestRewind(); app.AddExceptionShielding(); //TODO: app.AddMultiTenancyDetection(); we need a TenantDetective - app.AddEventingListeners(options.UsesEventing); - + app.AddEventingListeners(options.Persistence.UsesEventing); + app.EnableApiUsageTracking(options.TrackApiUsage); + //TODO: add the Healthcheck endpoint + modules.ConfigureHost(app); return app; @@ -69,8 +75,8 @@ void ConfigureSharedServices() void ConfigureRecording() { builder.Services.RegisterUnshared(c => - new TracingOnlyRecorder(options.HostName, - c.ResolveForUnshared())); // TODO: we need a more comprehensive HostRecorder using Azure or AWS or GC + new HostRecorder(c.ResolveForUnshared(), c.ResolveForUnshared(), + options)); } void ConfigureMultiTenancy(bool isMultiTenanted) @@ -100,8 +106,8 @@ void ConfigureConfiguration(bool isMultiTenanted) new AspNetConfigurationSettings(c.GetRequiredService())); } - builder.Services.RegisterUnshared(c => - new ApiHostSettings(new AspNetConfigurationSettings(c.GetRequiredService()))); + builder.Services.RegisterUnshared(c => + new HostSettings(new AspNetConfigurationSettings(c.GetRequiredService()))); } void ConfigureAuthenticationAuthorization() @@ -203,7 +209,7 @@ static void RegisterStubMessageQueueDrainingService(WebApplicationBuilder builde // }; builder.Services.AddHostedService(services => new StubQueueDrainingService(services.GetRequiredService(), - services.ResolveForUnshared(), + services.ResolveForUnshared(), services.GetRequiredService>(), services.ResolveForUnshared(), drainApiMappings)); } diff --git a/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj b/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj index 85f162d3..005fcca4 100644 --- a/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj +++ b/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj @@ -6,10 +6,10 @@ + - - - + + diff --git a/src/Infrastructure.Web.Hosting.Common/Resources.Designer.cs b/src/Infrastructure.Web.Hosting.Common/Resources.Designer.cs index f8f2a308..2ea49476 100644 --- a/src/Infrastructure.Web.Hosting.Common/Resources.Designer.cs +++ b/src/Infrastructure.Web.Hosting.Common/Resources.Designer.cs @@ -58,68 +58,5 @@ internal Resources() { resourceCulture = value; } } - - /// - /// Looks up a localized string similar to The setting '{0}' cannot be found in any configuration. - /// - internal static string AspNetConfigurationSettings_KeyNotFound { - get { - return ResourceManager.GetString("AspNetConfigurationSettings_KeyNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The setting '{0}' exists, but is not a recognizable boolean value. - /// - internal static string AspNetConfigurationSettings_ValueNotBoolean { - get { - return ResourceManager.GetString("AspNetConfigurationSettings_ValueNotBoolean", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The setting '{0}' exists, but is not a recognizable number value. - /// - internal static string AspNetConfigurationSettings_ValueNotNumber { - get { - return ResourceManager.GetString("AspNetConfigurationSettings_ValueNotNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to relay new events to read model for: {0}. - /// - internal static string EventStreamHandlerBase_OnEventStreamStateChanged_FailedToProject { - get { - return ResourceManager.GetString("EventStreamHandlerBase_OnEventStreamStateChanged_FailedToProject", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The event stream {0} contains events with out of order versions.. - /// - internal static string EventStreamHandlerBase_OutOfOrderEvents { - get { - return ResourceManager.GetString("EventStreamHandlerBase_OutOfOrderEvents", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Recorder is ready to record. - /// - internal static string TracingOnlyRecorder_Started { - get { - return ResourceManager.GetString("TracingOnlyRecorder_Started", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' cannot be found in appsettings.json for this host. - /// - internal static string WebApiHostSettings_MissingSetting { - get { - return ResourceManager.GetString("WebApiHostSettings_MissingSetting", resourceCulture); - } - } } } diff --git a/src/Infrastructure.Web.Hosting.Common/Resources.resx b/src/Infrastructure.Web.Hosting.Common/Resources.resx index 7ec6817c..755958fe 100644 --- a/src/Infrastructure.Web.Hosting.Common/Resources.resx +++ b/src/Infrastructure.Web.Hosting.Common/Resources.resx @@ -24,25 +24,4 @@ PublicKeyToken=b77a5c561934e089 - - The setting '{0}' cannot be found in any configuration - - - The setting '{0}' exists, but is not a recognizable boolean value - - - The setting '{0}' exists, but is not a recognizable number value - - - Recorder is ready to record - - - '{0}' cannot be found in appsettings.json for this host - - - The event stream {0} contains events with out of order versions. - - - Failed to relay new events to read model for: {0} - \ No newline at end of file diff --git a/src/Infrastructure.Web.Hosting.Common/WebHostOptions.cs b/src/Infrastructure.Web.Hosting.Common/WebHostOptions.cs index 6f3c6f4d..85644597 100644 --- a/src/Infrastructure.Web.Hosting.Common/WebHostOptions.cs +++ b/src/Infrastructure.Web.Hosting.Common/WebHostOptions.cs @@ -1,64 +1,49 @@ -using Infrastructure.Common.Recording; +using Infrastructure.Hosting.Common; namespace Infrastructure.Web.Hosting.Common; /// /// Defines options for different web hosts /// -public class WebHostOptions +public class WebHostOptions : HostOptions { - public static readonly WebHostOptions BackEndApiHost = new("BackendAPI") + public new static readonly WebHostOptions BackEndAncillaryApiHost = new(HostOptions.BackEndAncillaryApiHost) { DefaultApiPath = string.Empty, AllowCors = true, - IsMultiTenanted = false, //TODO: change for multi-tenanted - UsesQueues = true, - UsesEventing = true, - Recording = RecorderOptions.BackEndApiHost + TrackApiUsage = true, + }; + public new static readonly WebHostOptions BackEndApiHost = new(HostOptions.BackEndApiHost) + { + DefaultApiPath = string.Empty, + AllowCors = true, + TrackApiUsage = true }; - public static readonly WebHostOptions BackEndForFrontEndWebHost = new("FrontendSite") + public new static readonly WebHostOptions BackEndForFrontEndWebHost = new(HostOptions.BackEndForFrontEndWebHost) { DefaultApiPath = "api", AllowCors = true, - IsMultiTenanted = false, //TODO: change for multi-tenanted - UsesQueues = true, - UsesEventing = false, - Recording = RecorderOptions.BackEndForFrontEndWebHost + TrackApiUsage = false }; - public static readonly WebHostOptions TestingStubsHost = new("TestingStubs") + public new static readonly WebHostOptions TestingStubsHost = new(HostOptions.TestingStubsHost) { DefaultApiPath = string.Empty, AllowCors = true, - IsMultiTenanted = false, //TODO: change for multi-tenanted - UsesQueues = false, - UsesEventing = false, - Recording = RecorderOptions.TestingStubsHost + TrackApiUsage = false }; - private WebHostOptions(string hostName) + private WebHostOptions(HostOptions options) : base(options) { - HostName = hostName; DefaultApiPath = string.Empty; AllowCors = true; - IsMultiTenanted = false; - UsesQueues = false; - UsesEventing = false; - Recording = new RecorderOptions(); + TrackApiUsage = false; } + + public bool TrackApiUsage { get; private set; } public bool AllowCors { get; private init; } public string DefaultApiPath { get; private init; } - - public string HostName { get; private init; } - - public bool IsMultiTenanted { get; private init; } - - public RecorderOptions Recording { get; private init; } - - public bool UsesEventing { get; private init; } - - public bool UsesQueues { get; private init; } } \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Interfaces/Clients/IHttpJsonClient.cs b/src/Infrastructure.Web.Interfaces/Clients/IHttpJsonClient.cs similarity index 95% rename from src/Infrastructure.Web.Api.Interfaces/Clients/IHttpJsonClient.cs rename to src/Infrastructure.Web.Interfaces/Clients/IHttpJsonClient.cs index 5795fa25..4e620ab9 100644 --- a/src/Infrastructure.Web.Api.Interfaces/Clients/IHttpJsonClient.cs +++ b/src/Infrastructure.Web.Interfaces/Clients/IHttpJsonClient.cs @@ -1,4 +1,6 @@ -namespace Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Interfaces.Clients; /// /// Defines a JSON diff --git a/src/Infrastructure.Web.Api.Interfaces/Clients/IServiceClient.cs b/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs similarity index 95% rename from src/Infrastructure.Web.Api.Interfaces/Clients/IServiceClient.cs rename to src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs index b94263e8..780d12ac 100644 --- a/src/Infrastructure.Web.Api.Interfaces/Clients/IServiceClient.cs +++ b/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs @@ -1,7 +1,8 @@ using Application.Interfaces; using Common; +using Infrastructure.Web.Api.Interfaces; -namespace Infrastructure.Web.Api.Interfaces.Clients; +namespace Infrastructure.Web.Interfaces.Clients; /// /// Defines a service client for calling remote APIs diff --git a/src/Infrastructure.Web.Api.Interfaces/Clients/JsonResponse.cs b/src/Infrastructure.Web.Interfaces/Clients/JsonResponse.cs similarity index 78% rename from src/Infrastructure.Web.Api.Interfaces/Clients/JsonResponse.cs rename to src/Infrastructure.Web.Interfaces/Clients/JsonResponse.cs index 7484bbb0..b7bcc5da 100644 --- a/src/Infrastructure.Web.Api.Interfaces/Clients/JsonResponse.cs +++ b/src/Infrastructure.Web.Interfaces/Clients/JsonResponse.cs @@ -1,8 +1,9 @@ using System.Net; using System.Net.Http.Headers; using Common; +using Infrastructure.Web.Api.Interfaces; -namespace Infrastructure.Web.Api.Interfaces.Clients; +namespace Infrastructure.Web.Interfaces.Clients; /// /// Defines a JSON response @@ -19,7 +20,7 @@ public class JsonResponse } /// -/// Defines a JSON response of the specified +/// Defines a JSON response of the specified /// public class JsonResponse : JsonResponse where TResponse : IWebResponse diff --git a/src/Infrastructure.Web.Api.Interfaces/Clients/ResponseProblem.cs b/src/Infrastructure.Web.Interfaces/Clients/ResponseProblem.cs similarity index 81% rename from src/Infrastructure.Web.Api.Interfaces/Clients/ResponseProblem.cs rename to src/Infrastructure.Web.Interfaces/Clients/ResponseProblem.cs index 99c81a9f..91c8e919 100644 --- a/src/Infrastructure.Web.Api.Interfaces/Clients/ResponseProblem.cs +++ b/src/Infrastructure.Web.Interfaces/Clients/ResponseProblem.cs @@ -1,4 +1,6 @@ -namespace Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Interfaces.Clients; /// /// Defines a problem returned in a response diff --git a/src/Infrastructure.Web.Interfaces/Infrastructure.Web.Interfaces.csproj b/src/Infrastructure.Web.Interfaces/Infrastructure.Web.Interfaces.csproj new file mode 100644 index 00000000..10718c8e --- /dev/null +++ b/src/Infrastructure.Web.Interfaces/Infrastructure.Web.Interfaces.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + true + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + + + diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/ApiWorkerSpec.cs b/src/Infrastructure.Worker.Api.IntegrationTests/ApiWorkerSpec.cs similarity index 95% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/ApiWorkerSpec.cs rename to src/Infrastructure.Worker.Api.IntegrationTests/ApiWorkerSpec.cs index 8b5344bc..3a5f1a63 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/ApiWorkerSpec.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/ApiWorkerSpec.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Infrastructure.Api.WorkerHost.IntegrationTests; +namespace Infrastructure.Worker.Api.IntegrationTests; public interface IApiWorkerSpec { diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs similarity index 93% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs rename to src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs index 5c196621..8988a2dc 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureFunctionHostSetup.cs @@ -1,10 +1,10 @@ using AzureFunctions.Api.WorkerHost; using Common.Extensions; using Common.Recording; +using Infrastructure.Hosting.Common; +using Infrastructure.Hosting.Common.Extensions; using Infrastructure.Persistence.Azure.ApplicationServices; using Infrastructure.Persistence.Interfaces; -using Infrastructure.Web.Hosting.Common.ApplicationServices; -using Infrastructure.Web.Hosting.Common.Extensions; using JetBrains.Annotations; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Configuration; @@ -13,7 +13,7 @@ using Microsoft.Extensions.Hosting; using Xunit; -namespace Infrastructure.Api.WorkerHost.IntegrationTests.AzureFunctions; +namespace Infrastructure.Worker.Api.IntegrationTests.AzureFunctions; [CollectionDefinition("AzureFunctions", DisableParallelization = true)] public class AllAzureFunctionSpecs : ICollectionFixture @@ -65,7 +65,7 @@ public void Start() builder .AddJsonFile("appsettings.Testing.json", true); }) - .ConfigureAzureFunctionTesting() + .ConfigureAzureFunctionTesting() .ConfigureServices((_, services) => { if (_overridenTestingDependencies.Exists()) @@ -125,7 +125,8 @@ public static IHostBuilder ConfigureAzureFunctionTesting(this IHostBui /// Invokes auto-generated configuration methods for a given . /// This method searches for classes that implement the interface in the assembly /// of the specified type . - /// This mimics what the method does on + /// This mimics what the + /// method does on /// startup in an Azure project /// private static IHostBuilder InvokeAutoGeneratedConfigureMethods(this IHostBuilder builder) diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs similarity index 79% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs rename to src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs index 03997f56..a206c23d 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctions/AzureStorageAccountBase.cs @@ -1,6 +1,6 @@ using IntegrationTesting.Persistence.Common; -namespace Infrastructure.Api.WorkerHost.IntegrationTests.AzureFunctions; +namespace Infrastructure.Worker.Api.IntegrationTests.AzureFunctions; public static class AzureStorageAccountBase { diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctionsApiSpec.cs b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctionsApiSpec.cs similarity index 95% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctionsApiSpec.cs rename to src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctionsApiSpec.cs index d4aedaf0..3635eff0 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/AzureFunctionsApiSpec.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/AzureFunctionsApiSpec.cs @@ -1,10 +1,10 @@ using Application.Persistence.Shared; using Common.Extensions; using FluentAssertions; -using Infrastructure.Api.WorkerHost.IntegrationTests.AzureFunctions; -using Infrastructure.Api.WorkerHost.IntegrationTests.Stubs; -using Infrastructure.Web.Api.Interfaces.Clients; using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Interfaces.Clients; +using Infrastructure.Worker.Api.IntegrationTests.AzureFunctions; +using Infrastructure.Worker.Api.IntegrationTests.Stubs; using Infrastructure.Workers.Api.Workers; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -12,7 +12,7 @@ using Xunit; using Task = System.Threading.Tasks.Task; -namespace Infrastructure.Api.WorkerHost.IntegrationTests; +namespace Infrastructure.Worker.Api.IntegrationTests; [UsedImplicitly] public class AzureFunctionsApiSpec diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/Infrastructure.Api.WorkerHost.IntegrationTests.csproj b/src/Infrastructure.Worker.Api.IntegrationTests/Infrastructure.Worker.Api.IntegrationTests.csproj similarity index 89% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/Infrastructure.Api.WorkerHost.IntegrationTests.csproj rename to src/Infrastructure.Worker.Api.IntegrationTests/Infrastructure.Worker.Api.IntegrationTests.csproj index 28d61339..91609fd4 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/Infrastructure.Api.WorkerHost.IntegrationTests.csproj +++ b/src/Infrastructure.Worker.Api.IntegrationTests/Infrastructure.Worker.Api.IntegrationTests.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/Stubs/StubServiceClient.cs b/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs similarity index 95% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/Stubs/StubServiceClient.cs rename to src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs index deb359fd..96b0d480 100644 --- a/src/Infrastructure.Api.WorkerHost.IntegrationTests/Stubs/StubServiceClient.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs @@ -1,9 +1,9 @@ using Application.Interfaces; using Common; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Interfaces.Clients; -namespace Infrastructure.Api.WorkerHost.IntegrationTests.Stubs; +namespace Infrastructure.Worker.Api.IntegrationTests.Stubs; public class StubServiceClient : IServiceClient { diff --git a/src/Infrastructure.Api.WorkerHost.IntegrationTests/appsettings.Testing.json b/src/Infrastructure.Worker.Api.IntegrationTests/appsettings.Testing.json similarity index 100% rename from src/Infrastructure.Api.WorkerHost.IntegrationTests/appsettings.Testing.json rename to src/Infrastructure.Worker.Api.IntegrationTests/appsettings.Testing.json diff --git a/src/Infrastructure.Workers.Api/CrashTraceOnlyRecorder.cs b/src/Infrastructure.Workers.Api/CrashTraceOnlyRecorder.cs index ea31b93f..3e443e9b 100644 --- a/src/Infrastructure.Workers.Api/CrashTraceOnlyRecorder.cs +++ b/src/Infrastructure.Workers.Api/CrashTraceOnlyRecorder.cs @@ -1,6 +1,6 @@ using Common; using Common.Recording; -using Infrastructure.Web.Hosting.Common; +using Infrastructure.Hosting.Common.Recording; using Microsoft.Extensions.Logging; namespace Infrastructure.Workers.Api; diff --git a/src/Infrastructure.Workers.Api/Infrastructure.Workers.Api.csproj b/src/Infrastructure.Workers.Api/Infrastructure.Workers.Api.csproj index 2b02619d..cec109b6 100644 --- a/src/Infrastructure.Workers.Api/Infrastructure.Workers.Api.csproj +++ b/src/Infrastructure.Workers.Api/Infrastructure.Workers.Api.csproj @@ -8,9 +8,8 @@ - + - diff --git a/src/Infrastructure.Workers.Api/ServiceClientExtensions.cs b/src/Infrastructure.Workers.Api/ServiceClientExtensions.cs index 0a6e1761..5e5ee99a 100644 --- a/src/Infrastructure.Workers.Api/ServiceClientExtensions.cs +++ b/src/Infrastructure.Workers.Api/ServiceClientExtensions.cs @@ -4,7 +4,8 @@ using Common.Recording; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Interfaces; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Common.Extensions; +using Infrastructure.Web.Interfaces.Clients; namespace Infrastructure.Workers.Api; diff --git a/src/Infrastructure.Workers.Api/Workers/DeliverAuditRelayWorker.cs b/src/Infrastructure.Workers.Api/Workers/DeliverAuditRelayWorker.cs index c5d7e2e4..dc62718b 100644 --- a/src/Infrastructure.Workers.Api/Workers/DeliverAuditRelayWorker.cs +++ b/src/Infrastructure.Workers.Api/Workers/DeliverAuditRelayWorker.cs @@ -2,8 +2,8 @@ using Application.Persistence.Shared; using Common; using Common.Extensions; -using Infrastructure.Web.Api.Interfaces.Clients; using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Interfaces.Clients; using Task = System.Threading.Tasks.Task; namespace Infrastructure.Workers.Api.Workers; @@ -13,9 +13,9 @@ public sealed class DeliverAuditRelayWorker : IQueueMonitoringApiRelayWorker + - diff --git a/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs b/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs index 7f9d0f32..fa5c50ab 100644 --- a/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs +++ b/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs @@ -1,8 +1,8 @@ using Application.Persistence.Interfaces; using Common; using Common.Extensions; -using Infrastructure.Web.Api.Common.Clients; -using Infrastructure.Web.Api.Interfaces.Clients; +using Infrastructure.Web.Common.Clients; +using Infrastructure.Web.Interfaces.Clients; using JetBrains.Annotations; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; diff --git a/src/SaaStack.sln b/src/SaaStack.sln index c33294ad..6f2ddef5 100644 --- a/src/SaaStack.sln +++ b/src/SaaStack.sln @@ -216,7 +216,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AncillaryInfrastructure.Int EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{E458A872-783B-4F84-B0A6-0903EB9F3EF8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Api.WorkerHost.IntegrationTests", "Infrastructure.Api.WorkerHost.IntegrationTests\Infrastructure.Api.WorkerHost.IntegrationTests.csproj", "{EA58877D-3023-429C-A1A8-E8479441139E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Worker.Api.IntegrationTests", "Infrastructure.Worker.Api.IntegrationTests\Infrastructure.Worker.Api.IntegrationTests.csproj", "{EA58877D-3023-429C-A1A8-E8479441139E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTesting.Persistence.Common", "IntegrationTesting.Persistence.Common\IntegrationTesting.Persistence.Common.csproj", "{B610A0C7-B573-4B25-A9F6-B6E5C4593722}" EndProject @@ -236,6 +236,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Analyzers.Common", "T EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Analyzers.Subdomain.UnitTests", "Tools.Analyzers.Subdomain.UnitTests\Tools.Analyzers.Subdomain.UnitTests.csproj", "{3EF40C54-AACB-45B7-A013-BB46F3F8FC35}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Web.Interfaces", "Infrastructure.Web.Interfaces\Infrastructure.Web.Interfaces.csproj", "{11871155-F741-4AFD-BD9E-9AE7C7670B1C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Web.Common", "Infrastructure.Web.Common\Infrastructure.Web.Common.csproj", "{005FCB6F-860B-4DCC-A699-D1A68823D882}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Web.Common.UnitTests", "Infrastructure.Web.Common.UnitTests\Infrastructure.Web.Common.UnitTests.csproj", "{9BA6D2C4-7FB9-41F7-9396-40704B9878DC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Hosting.Common", "Infrastructure.Hosting.Common\Infrastructure.Hosting.Common.csproj", "{FB2419FA-457F-406A-AADC-E6E44813896B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Hosting.Common.UnitTests", "Infrastructure.Hosting.Common.UnitTests\Infrastructure.Hosting.Common.UnitTests.csproj", "{22E40299-87EF-437F-ACDA-A9A0C1199AD9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -693,6 +703,36 @@ Global {3EF40C54-AACB-45B7-A013-BB46F3F8FC35}.Release|Any CPU.Build.0 = Release|Any CPU {3EF40C54-AACB-45B7-A013-BB46F3F8FC35}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU {3EF40C54-AACB-45B7-A013-BB46F3F8FC35}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.Release|Any CPU.Build.0 = Release|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {11871155-F741-4AFD-BD9E-9AE7C7670B1C}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.Debug|Any CPU.Build.0 = Debug|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.Release|Any CPU.ActiveCfg = Release|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.Release|Any CPU.Build.0 = Release|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {005FCB6F-860B-4DCC-A699-D1A68823D882}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.Release|Any CPU.Build.0 = Release|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.Release|Any CPU.Build.0 = Release|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {FB2419FA-457F-406A-AADC-E6E44813896B}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.Release|Any CPU.Build.0 = Release|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {22E40299-87EF-437F-ACDA-A9A0C1199AD9}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5C77A86-38AF-40E4-82FC-617E624B2754} = {508E7DA4-4DF2-4201-955D-CCF70C41AD05} @@ -805,5 +845,10 @@ Global {8EE34354-4B11-41AD-BE92-845356A36D00} = {BAE0D6F2-6920-4B02-9F30-D71B04B7170D} {D09C110B-9639-43D7-8A85-7F2A102554E2} = {BAE0D6F2-6920-4B02-9F30-D71B04B7170D} {3EF40C54-AACB-45B7-A013-BB46F3F8FC35} = {A25A3BA8-5602-4825-9595-2CF96B166920} + {11871155-F741-4AFD-BD9E-9AE7C7670B1C} = {B68592DF-E8E8-452A-A46F-5C8ECB178FDF} + {005FCB6F-860B-4DCC-A699-D1A68823D882} = {B68592DF-E8E8-452A-A46F-5C8ECB178FDF} + {9BA6D2C4-7FB9-41F7-9396-40704B9878DC} = {19ADDB2F-B589-49EF-9BDA-BD9908057D60} + {FB2419FA-457F-406A-AADC-E6E44813896B} = {0358DED1-114C-4EFB-98C7-3D6B50A127DF} + {22E40299-87EF-437F-ACDA-A9A0C1199AD9} = {F561CAF6-2A8C-4440-B12E-7753F25D9879} EndGlobalSection EndGlobal diff --git a/src/TestingStubApiHost/TestingStubApiHost.csproj b/src/TestingStubApiHost/TestingStubApiHost.csproj index db240034..85e236ff 100644 --- a/src/TestingStubApiHost/TestingStubApiHost.csproj +++ b/src/TestingStubApiHost/TestingStubApiHost.csproj @@ -7,7 +7,6 @@ - @@ -22,6 +21,7 @@ + diff --git a/src/Tools.Analyzers.Platform.UnitTests/Tools.Analyzers.Platform.UnitTests.csproj b/src/Tools.Analyzers.Platform.UnitTests/Tools.Analyzers.Platform.UnitTests.csproj index e12a1e21..c1d5d8df 100644 --- a/src/Tools.Analyzers.Platform.UnitTests/Tools.Analyzers.Platform.UnitTests.csproj +++ b/src/Tools.Analyzers.Platform.UnitTests/Tools.Analyzers.Platform.UnitTests.csproj @@ -5,6 +5,12 @@ true + + + + + + @@ -13,10 +19,4 @@ - - - - - - diff --git a/src/Tools.Analyzers.Subdomain.UnitTests/Tools.Analyzers.Subdomain.UnitTests.csproj b/src/Tools.Analyzers.Subdomain.UnitTests/Tools.Analyzers.Subdomain.UnitTests.csproj index a0f359e3..df80bf20 100644 --- a/src/Tools.Analyzers.Subdomain.UnitTests/Tools.Analyzers.Subdomain.UnitTests.csproj +++ b/src/Tools.Analyzers.Subdomain.UnitTests/Tools.Analyzers.Subdomain.UnitTests.csproj @@ -5,6 +5,13 @@ true + + + + + + + @@ -13,11 +20,4 @@ - - - - - - - diff --git a/src/Tools.Generators.WebApi.UnitTests/Tools.Generators.WebApi.UnitTests.csproj b/src/Tools.Generators.WebApi.UnitTests/Tools.Generators.WebApi.UnitTests.csproj index 8800428f..7f619baa 100644 --- a/src/Tools.Generators.WebApi.UnitTests/Tools.Generators.WebApi.UnitTests.csproj +++ b/src/Tools.Generators.WebApi.UnitTests/Tools.Generators.WebApi.UnitTests.csproj @@ -5,6 +5,11 @@ true + + + + + @@ -12,9 +17,4 @@ - - - - - diff --git a/src/Tools.Templates/IntegrationTestProject/ProjectName.csproj b/src/Tools.Templates/IntegrationTestProject/ProjectName.csproj index 8fefb12b..246ee85d 100644 --- a/src/Tools.Templates/IntegrationTestProject/ProjectName.csproj +++ b/src/Tools.Templates/IntegrationTestProject/ProjectName.csproj @@ -6,12 +6,12 @@ - + + - - + diff --git a/src/Tools.Templates/UnitTestProject/ProjectName.csproj b/src/Tools.Templates/UnitTestProject/ProjectName.csproj index dfa41fde..9cd50ec8 100644 --- a/src/Tools.Templates/UnitTestProject/ProjectName.csproj +++ b/src/Tools.Templates/UnitTestProject/ProjectName.csproj @@ -6,12 +6,12 @@ - + + - - +