Skip to content

Commit

Permalink
Split extension methods and move to hostbuilder where possible to fac…
Browse files Browse the repository at this point in the history
…iliate only starting what is required
  • Loading branch information
danielmarbach committed Mar 13, 2024
1 parent 5d7216d commit 3159462
Show file tree
Hide file tree
Showing 20 changed files with 68 additions and 83 deletions.
Expand Up @@ -11,15 +11,13 @@
using Infrastructure.DomainEvents;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NLog;
using NServiceBus;
using NServiceBus.AcceptanceTesting;
using NServiceBus.AcceptanceTesting.Support;
using NServiceBus.Configuration.AdvancedExtensibility;
using Particular.ServiceControl;
using Particular.ServiceControl.Hosting;
using RavenDB.Shared;
Expand Down Expand Up @@ -119,6 +117,7 @@ async Task InitializeServiceControl(ScenarioContext context)
EnvironmentName = Environments.Development
});
hostBuilder.AddServiceControl(settings, configuration, loggingSettings);
hostBuilder.AddServiceControlApi();

hostBuilder.AddServiceControlTesting(settings);

Expand Down
Expand Up @@ -23,11 +23,8 @@ public static void AddServiceControlTesting(this WebApplicationBuilder hostBuild
// This facilitates receiving the test server anywhere where DI is available
hostBuilder.Services.AddSingleton(provider => (TestServer)provider.GetRequiredService<IServer>());

// By default, ASP.NET Core uses the entry point assembly to discover controllers. When running
// inside a test runner the runner exe becomes the entry point which obviously has no controllers in it ;)
// so we are explicitly registering all necessary application parts.
// For acceptance testing purposes we are adding more controllers to the host
var addControllers = hostBuilder.Services.AddControllers();
addControllers.AddApplicationPart(typeof(Settings).Assembly);
addControllers.AddApplicationPart(typeof(AcceptanceTest).Assembly);

hostBuilder.Services.AddHttpClientDefaultsOverrides(settings);
Expand Down
Expand Up @@ -9,21 +9,16 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
using Auditing;
using Infrastructure;
using Infrastructure.Hosting;
using Infrastructure.Hosting.Commands;
using Infrastructure.Settings;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NServiceBus;
using NServiceBus.AcceptanceTesting;
using NServiceBus.AcceptanceTesting.Support;
using NServiceBus.Configuration.AdvancedExtensibility;

public class ServiceControlComponentRunner(
ITransportIntegration transportToUse,
Expand Down Expand Up @@ -130,6 +125,8 @@ async Task InitializeServiceControl(ScenarioContext context)
return criticalErrorContext.Stop(cancellationToken);
}, settings, configuration, loggingSettings);

hostBuilder.AddServiceControlAuditApi();

hostBuilder.AddServiceControlAuditTesting(settings);

hostBuilderCustomization(hostBuilder);
Expand Down
@@ -1,9 +1,7 @@
namespace ServiceControl.Audit.AcceptanceTests.TestSupport;

using System;
using Auditing;
using Infrastructure.Settings;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
Expand All @@ -21,11 +19,8 @@ public static void AddServiceControlAuditTesting(this WebApplicationBuilder host
// This facilitates receiving the test server anywhere where DI is available
hostBuilder.Services.AddSingleton(provider => (TestServer)provider.GetRequiredService<IServer>());

// By default, ASP.NET Core uses the entry point assembly to discover controllers. When running
// inside a test runner the runner exe becomes the entry point which obviously has no controllers in it ;)
// so we are explicitly registering all necessary application parts.
// For acceptance testing purposes we are adding more controllers to the host
var addControllers = hostBuilder.Services.AddControllers();
addControllers.AddApplicationPart(typeof(Settings).Assembly);
addControllers.AddApplicationPart(typeof(AcceptanceTest).Assembly);
}
}
Expand Up @@ -24,9 +24,9 @@ namespace ServiceControl.Audit;
using Persistence;
using Transports;

static class WebApplicationBuilderExtension
static class HostApplicationBuilderEtensions
{
public static void AddServiceControlAudit(this WebApplicationBuilder builder,
public static void AddServiceControlAudit(this IHostApplicationBuilder builder,
Func<ICriticalErrorContext, CancellationToken, Task> onCriticalError,
Settings settings,
EndpointConfiguration configuration,
Expand Down Expand Up @@ -92,8 +92,6 @@ static class WebApplicationBuilderExtension
services.AddHostedService<AuditIngestion>();
}

builder.AddWebApi(settings.RootUrl);

builder.Services.AddWindowsService();
}

Expand All @@ -110,7 +108,7 @@ static TransportSettings MapSettings(Settings settings)

static void RecordStartup(Settings settings, LoggingSettings loggingSettings, EndpointConfiguration endpointConfiguration, IPersistenceConfiguration persistenceConfiguration)
{
var version = FileVersionInfo.GetVersionInfo(typeof(WebApplicationBuilderExtension).Assembly.Location).ProductVersion;
var version = FileVersionInfo.GetVersionInfo(typeof(HostApplicationBuilderEtensions).Assembly.Location).ProductVersion;

var startupMessage = $@"
-------------------------------------------------------------
Expand All @@ -123,7 +121,7 @@ static void RecordStartup(Settings settings, LoggingSettings loggingSettings, En
Persistence: {persistenceConfiguration.Name}
-------------------------------------------------------------";

var logger = LogManager.GetLogger(typeof(WebApplicationBuilderExtension));
var logger = LogManager.GetLogger(typeof(HostApplicationBuilderEtensions));
logger.Info(startupMessage);
endpointConfiguration.GetSettings().AddStartupDiagnosticsSection("Startup", new
{
Expand Down
Expand Up @@ -4,8 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
using Auditing;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NLog;
using NServiceBus;
using Settings;
Expand All @@ -21,16 +21,14 @@ public override async Task Execute(HostArguments args, Settings settings)

using var tokenSource = new CancellationTokenSource();

// TODO: Ideally we would never want to actually bootstrap the web api. Figure out how
var hostBuilder = WebApplication.CreateBuilder();
var hostBuilder = Host.CreateApplicationBuilder();
hostBuilder.AddServiceControlAudit((_, __) =>
{
tokenSource.Cancel();
return Task.CompletedTask;
}, settings, endpointConfiguration, loggingSettings);

using var app = hostBuilder.Build();
app.UseServiceControlAudit();
await app.StartAsync(tokenSource.Token);

var importer = app.Services.GetRequiredService<ImportFailedAudits>();
Expand Down
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Builder;
using NServiceBus;
using Settings;
using WebApi;

class RunCommand : AbstractCommand
{
Expand All @@ -21,10 +22,11 @@ public override async Task Execute(HostArguments args, Settings settings)
//Do nothing. The transports in NSB 8 are designed to handle broker outages. Audit ingestion will be paused when broker is unavailable.
return Task.CompletedTask;
}, settings, endpointConfiguration, loggingSettings);
hostBuilder.AddServiceControlAuditApi();

var app = hostBuilder.Build();
app.UseServiceControlAudit();
await app.RunAsync();
await app.RunAsync(settings.RootUrl);
}
}
}
@@ -1,15 +1,14 @@
namespace ServiceControl.Audit.Infrastructure.WebApi
{
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

static class WebApplicationBuilderExtensions
static class HostApplicationBuilderExtensions
{
public static void AddWebApi(this WebApplicationBuilder builder, string rootUrl)
public static void AddServiceControlAuditApi(this IHostApplicationBuilder builder)
{
builder.WebHost.UseUrls(rootUrl);

builder.Services.AddCors(options => options.AddDefaultPolicy(Cors.GetDefaultPolicy()));

// We're not explicitly adding Gzip here because it's already in the default list of supported compressors
Expand All @@ -23,6 +22,7 @@ public static void AddWebApi(this WebApplicationBuilder builder, string rootUrl)
options.ModelBinderProviders.Insert(0, new PagingInfoModelBindingProvider());
options.ModelBinderProviders.Insert(0, new SortInfoModelBindingProvider());
});
controllers.AddApplicationPart(Assembly.GetExecutingAssembly());
controllers.AddJsonOptions(options => options.JsonSerializerOptions.CustomizeDefaults());
}
}
Expand Down
Expand Up @@ -8,16 +8,15 @@ namespace ServiceControl.Monitoring.AcceptanceTests.TestSupport
using System.Threading.Tasks;
using AcceptanceTesting;
using Infrastructure;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Monitoring;
using NServiceBus;
using NServiceBus.AcceptanceTesting;
using NServiceBus.AcceptanceTesting.Support;
using NServiceBus.Configuration.AdvancedExtensibility;
using NServiceBus.Logging;

class ServiceControlComponentRunner(
Expand Down Expand Up @@ -107,6 +106,7 @@ async Task InitializeServiceControl(ScenarioContext context)
context.Logs.Enqueue(logitem);
return criticalErrorContext.Stop(cancellationToken);
}, settings, configuration);
hostBuilder.AddServiceControlMonitoringApi();

hostBuilder.AddServiceControlMonitoringTesting(settings);

Expand Down
Expand Up @@ -18,11 +18,8 @@ public static void AddServiceControlMonitoringTesting(this WebApplicationBuilder
hostBuilder.Services.AddKeyedSingleton(settings.EndpointName,
(provider, _) => (TestServer)provider.GetRequiredService<IServer>());

// By default, ASP.NET Core uses the entry point assembly to discover controllers. When running
// inside a test runner the runner exe becomes the entry point which obviously has no controllers in it ;)
// so we are explicitly registering all necessary application parts.
// // For acceptance testing purposes we are adding more controllers to the host
var addControllers = hostBuilder.Services.AddControllers();
addControllers.AddApplicationPart(typeof(Settings).Assembly);
addControllers.AddApplicationPart(typeof(AcceptanceTest).Assembly);
}
}
Expand Up @@ -6,11 +6,8 @@ namespace ServiceControl.Monitoring;
using System.Threading.Tasks;
using Infrastructure;
using Infrastructure.Extensions;
using Infrastructure.WebApi;
using Licensing;
using Messaging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand All @@ -24,9 +21,9 @@ namespace ServiceControl.Monitoring;
using Timings;
using Transports;

public static class WebApplicationBuilderExtensions
public static class HostApplicationBuilderExtensions
{
public static void AddServiceControlMonitoring(this WebApplicationBuilder hostBuilder,
public static void AddServiceControlMonitoring(this IHostApplicationBuilder hostBuilder,
Func<ICriticalErrorContext, CancellationToken, Task> onCriticalError, Settings settings,
EndpointConfiguration endpointConfiguration)
{
Expand Down Expand Up @@ -69,16 +66,6 @@ public static class WebApplicationBuilderExtensions

ConfigureEndpoint(endpointConfiguration, onCriticalError, transportCustomization, settings);
hostBuilder.UseNServiceBus(endpointConfiguration);

// TODO Verify if that we need to check the expose API flag
// We also don't do this in the primary instance
hostBuilder.WebHost.UseUrls(settings.RootUrl);
var controllers = hostBuilder.Services.AddControllers(options =>
{
options.Filters.Add<XParticularVersionHttpHandler>();
options.Filters.Add<CachingHttpHandler>();
});
controllers.AddJsonOptions(options => options.JsonSerializerOptions.CustomizeDefaults());
}

internal static void ConfigureEndpoint(EndpointConfiguration config, Func<ICriticalErrorContext, CancellationToken, Task> onCriticalError, ITransportCustomization transportCustomization, Settings settings)
Expand Down
4 changes: 3 additions & 1 deletion src/ServiceControl.Monitoring/Hosting/Commands/RunCommand.cs
Expand Up @@ -2,6 +2,7 @@ namespace ServiceControl.Monitoring
{
using System.Threading.Tasks;
using Infrastructure;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using NServiceBus;

Expand All @@ -13,10 +14,11 @@ public override async Task Execute(Settings settings)

var hostBuilder = WebApplication.CreateBuilder();
hostBuilder.AddServiceControlMonitoring((_, __) => Task.CompletedTask, settings, endpointConfiguration);
hostBuilder.AddServiceControlMonitoringApi();

var app = hostBuilder.Build();
app.UseServiceControlMonitoring();
await app.RunAsync();
await app.RunAsync(settings.RootUrl);
}
}
}
@@ -0,0 +1,19 @@
namespace ServiceControl.Monitoring.Infrastructure.WebApi;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Infrastructure;

public static class HostApplicationBuilderExtensions
{
public static void AddServiceControlMonitoringApi(this IHostApplicationBuilder hostBuilder)
{
var controllers = hostBuilder.Services.AddControllers(options =>
{
options.Filters.Add<XParticularVersionHttpHandler>();
options.Filters.Add<CachingHttpHandler>();
});
controllers.AddApplicationPart(typeof(Settings).Assembly);
controllers.AddJsonOptions(options => options.JsonSerializerOptions.CustomizeDefaults());
}
}
Expand Up @@ -4,16 +4,16 @@
using System.Linq;
using NUnit.Framework;
using Particular.Approvals;
using Particular.ServiceControl;
using Raven.Client.Documents.Indexes;
using ServiceBus.Management.Infrastructure.Settings;
using ServiceControl.Persistence;

class RavenPersistedTypes
{
[Test]
public void Verify()
{
var allTypes = typeof(WebApplicationBuilderExtension).Assembly.GetTypes().Concat(typeof(RavenQueryExtensions).Assembly.GetTypes()).Concat(typeof(EndpointsView).Assembly.GetTypes());
var allTypes = typeof(Settings).Assembly.GetTypes().Concat(typeof(RavenQueryExtensions).Assembly.GetTypes()).Concat(typeof(EndpointsView).Assembly.GetTypes());

var documentTypes = allTypes
.Where(type => typeof(AbstractIndexCreationTask).IsAssignableFrom(type))
Expand Down
2 changes: 1 addition & 1 deletion src/ServiceControl.UnitTests/API/APIApprovals.cs
Expand Up @@ -178,7 +178,7 @@ static IEnumerable<ICustomCheck> GetCustomChecks()
{
var settings = (object)new Settings();

var serviceControlTypes = typeof(WebApplicationBuilderExtension).Assembly
var serviceControlTypes = typeof(Settings).Assembly
.GetTypes()
.Where(t => t.IsAbstract == false);

Expand Down
5 changes: 3 additions & 2 deletions src/ServiceControl.UnitTests/EventHierarchy.cs
Expand Up @@ -3,21 +3,22 @@
using System.Linq;
using NServiceBus;
using NUnit.Framework;
using Particular.ServiceControl;
using ServiceBus.Management.Infrastructure.Settings;

[TestFixture]
class EventHierarchy
{
[TestCase]
public void EnsureEventHierarchyIsFlat()
{
var serviceControlAssembly = typeof(WebApplicationBuilderExtension).Assembly;
var serviceControlAssembly = typeof(Settings).Assembly;
var eventTypes = serviceControlAssembly.GetTypes().Where(typeof(IEvent).IsAssignableFrom).Where(x => !x.IsAbstract).ToArray();

var flatEvents = eventTypes.Where(t => t.BaseType == typeof(object)).ToArray();

var nonFlatEvents = eventTypes.Except(flatEvents).ToArray();

Assert.IsNotEmpty(eventTypes);
Assert.IsEmpty(nonFlatEvents, "Complex Event Hierarchy causes duplicate event handling with Azure ServiceBus and SubscribeToOwnEvents");
}
}
Expand Down

0 comments on commit 3159462

Please sign in to comment.