Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split extension methods and move to hostbuilder where possible to faciliate only starting what is required #4011

Merged
merged 2 commits into from Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -118,6 +116,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 @@ -129,6 +124,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 @@ -9,8 +9,6 @@ namespace ServiceControl.Audit;
using Infrastructure;
using Infrastructure.Metrics;
using Infrastructure.Settings;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand All @@ -24,9 +22,9 @@ namespace ServiceControl.Audit;
using Persistence;
using Transports;

static class WebApplicationBuilderExtension
static class HostApplicationBuilderExtensions
{
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 @@ -90,8 +88,6 @@ static class WebApplicationBuilderExtension
services.AddHostedService<AuditIngestion>();
}

builder.AddWebApi(settings.RootUrl);

builder.Services.AddWindowsService();
}

Expand All @@ -108,7 +104,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(HostApplicationBuilderExtensions).Assembly.Location).ProductVersion;

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

var logger = LogManager.GetLogger(typeof(WebApplicationBuilderExtension));
var logger = LogManager.GetLogger(typeof(HostApplicationBuilderExtensions));
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 @@ -18,7 +18,7 @@ public override async Task Execute(HostArguments args, Settings.Settings setting
var hostBuilder = Host.CreateApplicationBuilder();
hostBuilder.Services.AddPersistence(persistenceSettings, persistenceConfiguration);

using var host = hostBuilder.Build();
var host = hostBuilder.Build();

if (WindowsServiceHelpers.IsWindowsService())
{
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 @@ -106,6 +105,7 @@ async Task InitializeServiceControl(ScenarioContext context)
context.Logs.Enqueue(logitem);
return criticalErrorContext.Stop(cancellationToken);
}, settings, configuration);
hostBuilder.AddServiceControlMonitoringApi();

hostBuilder.AddServiceControlMonitoringTesting(settings);

Expand Down
@@ -1,7 +1,6 @@
namespace ServiceControl.Monitoring.AcceptanceTests.TestSupport;

using System;
using Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
Expand All @@ -18,11 +17,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 @@ -56,7 +53,9 @@ public static class WebApplicationBuilderExtensions

services.AddHttpLogging(options =>
{
options.LoggingFields = HttpLoggingFields.RequestPath | HttpLoggingFields.RequestMethod | HttpLoggingFields.ResponseStatusCode | HttpLoggingFields.Duration;
// TODO Do we need to expose the host?
// we could also include the time it took to process the request
options.LoggingFields = HttpLoggingFields.RequestPath | HttpLoggingFields.RequestMethod | HttpLoggingFields.ResponseStatusCode;
});

// Core registers the message dispatcher to be resolved from the transport seam. The dispatcher
Expand All @@ -67,15 +66,6 @@ public static class WebApplicationBuilderExtensions

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

// 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,20 @@
namespace ServiceControl.Monitoring.Infrastructure.WebApi;

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

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(Assembly.GetExecutingAssembly());
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