Files
mxaccessgw/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs
T
Joseph Doherty dc9c0c950c rename: prefix gateway projects/namespaces with ZB.MOM.WW + sln→slnx
Apply the ZB.MOM.WW. prefix to all gateway-side projects, folders,
.csproj/.sln contents, C# namespaces, using directives, generated proto
C# (csharp_namespace + checked-in generated files), InternalsVisibleTo
attributes, project-name string literals (LoadProject, .sln lookups,
worker exe paths, staticwebassets manifest), and the install/script/doc
references that point at any of the above. Migrate the solution from
.sln to .slnx via `dotnet sln migrate` and delete the old file.

External-runtime identifiers are intentionally NOT prefixed so external
configuration keeps working:
- GatewayMetrics.cs MeterName ("MxGateway.Server")
- DashboardAuthenticationDefaults Scheme/Policy ("MxGateway.Dashboard")
- GatewayRequestLoggingMiddleware logger category ("MxGateway.Request")
- StaRuntime thread name ("MxGateway.Worker.STA")
- appsettings.json root section "MxGateway" + env-var prefix
  MxGateway__... and secret-name MxGateway:ApiKeyPepper
- C:\ProgramData\MxGateway\ data dir paths

Also fixes two tests that were not rename-related but became visible
while validating the rename:

- WorkerLiveMxAccessSmokeTests.ShutDownAsync: cancellation that the
  gateway service correctly maps to RpcException(Cancelled) per gRPC
  convention was being misclassified as a stream fault. Added a sibling
  catch on RpcException with StatusCode.Cancelled.

- IntegrationTestEnvironment.ResolveRepositoryRoot: extracted IsRepositoryRoot
  and made it accept either a .git marker OR a .sln/.slnx next to src/
  so the worker-exe walker works in non-git working copies.

clients/proto/proto-inputs.json's protoRoot updated to point at
src/ZB.MOM.WW.MxGateway.Contracts/Protos.

Verified by `dotnet build` and a full `dotnet test` of the .slnx with
MXGATEWAY_RUN_LIVE_{MXACCESS,LDAP,GALAXY}_TESTS=1:
  Tests: 472/472 pass
  Worker.Tests: 280/280 pass (4 dev-rig [Fact(Skip=...)] skipped)
  IntegrationTests: 18/18 pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 16:22:23 -04:00

163 lines
5.9 KiB
C#

using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using ZB.MOM.WW.MxGateway.Contracts;
using ZB.MOM.WW.MxGateway.Server.Alarms;
using ZB.MOM.WW.MxGateway.Server.Configuration;
using ZB.MOM.WW.MxGateway.Server.Dashboard;
using ZB.MOM.WW.MxGateway.Server.Diagnostics;
using ZB.MOM.WW.MxGateway.Server.Galaxy;
using ZB.MOM.WW.MxGateway.Server.Grpc;
using ZB.MOM.WW.MxGateway.Server.Metrics;
using ZB.MOM.WW.MxGateway.Server.Security.Authentication;
using ZB.MOM.WW.MxGateway.Server.Security.Authorization;
using ZB.MOM.WW.MxGateway.Server.Sessions;
using ZB.MOM.WW.MxGateway.Server.Workers;
namespace ZB.MOM.WW.MxGateway.Server;
/// <summary>
/// Configures and builds the gateway web application.
/// </summary>
public static class GatewayApplication
{
private const string StaticAssetsManifestFileName = "ZB.MOM.WW.MxGateway.Server.staticwebassets.endpoints.json";
/// <summary>
/// Builds a configured web application with all gateway services and middleware.
/// </summary>
/// <param name="args">Command-line arguments passed to the application.</param>
/// <returns>A configured web application ready to run.</returns>
public static WebApplication Build(string[] args)
{
WebApplicationBuilder builder = CreateBuilder(args);
WebApplication app = builder.Build();
app.UseGatewayRequestLoggingScope();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapGatewayEndpoints();
return app;
}
/// <summary>
/// Creates a web application builder configured with gateway services.
/// </summary>
/// <param name="args">Command-line arguments passed to the application.</param>
/// <returns>A configured web application builder.</returns>
public static WebApplicationBuilder CreateBuilder(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ContentRootPath = ResolveContentRootPath(),
});
StaticWebAssetsLoader.UseStaticWebAssets(builder.Environment, builder.Configuration);
builder.Services.AddGatewayConfiguration();
builder.Services.AddSqliteAuthStore();
builder.Services.AddGatewayGrpcAuthorization();
builder.Services.AddHealthChecks();
builder.Services.AddSingleton<GatewayMetrics>();
builder.Services.AddSingleton<MxAccessGrpcMapper>();
builder.Services.AddSingleton<MxAccessGrpcRequestValidator>();
builder.Services.AddSingleton<IEventStreamService, EventStreamService>();
builder.Services.AddWorkerProcessLauncher();
builder.Services.AddGatewaySessions();
builder.Services.AddGatewayAlarms();
builder.Services.AddGatewayDashboard();
builder.Services.AddGalaxyRepository();
return builder;
}
private static string ResolveContentRootPath()
{
string? configuredContentRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_CONTENTROOT");
if (!string.IsNullOrWhiteSpace(configuredContentRootPath)
&& IsServerContentRoot(configuredContentRootPath))
{
return configuredContentRootPath;
}
string currentDirectory = Environment.CurrentDirectory;
if (IsServerContentRoot(currentDirectory))
{
return currentDirectory;
}
string baseDirectory = AppContext.BaseDirectory;
if (IsServerContentRoot(baseDirectory))
{
return baseDirectory;
}
string? discoveredContentRootPath = DiscoverServerContentRoot(currentDirectory)
?? DiscoverServerContentRoot(baseDirectory);
return discoveredContentRootPath ?? baseDirectory;
}
private static string? DiscoverServerContentRoot(string startPath)
{
DirectoryInfo? directory = new(startPath);
while (directory is not null)
{
if (IsServerContentRoot(directory.FullName))
{
return directory.FullName;
}
string serverProjectPath = Path.Combine(directory.FullName, "src", "ZB.MOM.WW.MxGateway.Server");
if (IsServerContentRoot(serverProjectPath))
{
return serverProjectPath;
}
directory = directory.Parent;
}
return null;
}
private static bool IsServerContentRoot(string path)
{
return File.Exists(Path.Combine(path, "appsettings.json"))
&& Directory.Exists(Path.Combine(path, "wwwroot"));
}
/// <summary>
/// Maps gateway endpoints including gRPC services, health checks, and the dashboard.
/// </summary>
/// <param name="endpoints">Endpoint route builder to map endpoints to.</param>
/// <returns>The same endpoint route builder for chaining.</returns>
public static IEndpointRouteBuilder MapGatewayEndpoints(this IEndpointRouteBuilder endpoints)
{
endpoints.MapStaticAssets(ResolveStaticAssetsManifestPath());
endpoints.MapGet("/", () => Results.Redirect("/health/live"));
endpoints.MapGet(
"/health/live",
() => Results.Ok(new GatewayHealthReply(
Status: "Healthy",
DefaultBackend: GatewayContractInfo.DefaultBackendName,
WorkerProtocolVersion: GatewayContractInfo.WorkerProtocolVersion)))
.WithName("LiveHealth");
endpoints.MapGrpcService<MxAccessGatewayService>();
endpoints.MapGrpcService<GalaxyRepositoryGrpcService>();
endpoints.MapGatewayDashboard();
return endpoints;
}
private static string ResolveStaticAssetsManifestPath()
{
string manifestPath = Path.Combine(AppContext.BaseDirectory, StaticAssetsManifestFileName);
return File.Exists(manifestPath) ? manifestPath : StaticAssetsManifestFileName;
}
}