From bb93f39662af80a6ef91e3ad62287ac4c147ffc8 Mon Sep 17 00:00:00 2001 From: ubik Date: Mon, 18 Nov 2024 13:12:44 +0100 Subject: [PATCH 1/2] Aspire 9 first version --- .../Program.cs | 30 +++-- .../Ubik.Accounting.SalesOrVatTax.Api.csproj | 3 +- src/Ubik.Accounting.Structure.Api/Program.cs | 30 +++-- .../Ubik.Accounting.Structure.Api.csproj | 2 +- .../Program.cs | 29 +++-- .../Ubik.Accounting.Transaction.Api.csproj | 2 +- src/Ubik.Accounting.WebApp/Program.cs | 6 +- .../Properties/launchSettings.json | 4 +- .../Styles/app.output.css | 37 +----- .../Ubik.Accounting.WebApp.csproj | 1 + src/Ubik.AppHost/Program.cs | 85 +++++++++++++ .../Properties/launchSettings.json | 29 +++++ src/Ubik.AppHost/Ubik.AppHost.csproj | 31 +++++ src/Ubik.AppHost/appsettings.Development.json | 8 ++ src/Ubik.AppHost/appsettings.json | 19 +++ src/Ubik.Security.Api/Program.cs | 31 ++--- .../Ubik.Security.Api.csproj | 3 +- src/Ubik.ServiceDefaults/Extensions.cs | 119 ++++++++++++++++++ .../Ubik.ServiceDefaults.csproj | 22 ++++ src/Ubik.YarpProxy/Program.cs | 14 ++- .../Properties/launchSettings.json | 4 +- src/Ubik.YarpProxy/Ubik.YarpProxy.csproj | 2 +- src/Ubik.sln | 12 ++ 23 files changed, 422 insertions(+), 101 deletions(-) create mode 100644 src/Ubik.AppHost/Program.cs create mode 100644 src/Ubik.AppHost/Properties/launchSettings.json create mode 100644 src/Ubik.AppHost/Ubik.AppHost.csproj create mode 100644 src/Ubik.AppHost/appsettings.Development.json create mode 100644 src/Ubik.AppHost/appsettings.json create mode 100644 src/Ubik.ServiceDefaults/Extensions.cs create mode 100644 src/Ubik.ServiceDefaults/Ubik.ServiceDefaults.csproj diff --git a/src/Ubik.Accounting.SalesOrVatTax.Api/Program.cs b/src/Ubik.Accounting.SalesOrVatTax.Api/Program.cs index 29f47a98..bfe23ead 100644 --- a/src/Ubik.Accounting.SalesOrVatTax.Api/Program.cs +++ b/src/Ubik.Accounting.SalesOrVatTax.Api/Program.cs @@ -19,6 +19,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + //Options var authOptions = new AuthServerOptions(); builder.Configuration.GetSection(AuthServerOptions.Position).Bind(authOptions); @@ -27,14 +29,15 @@ var swaggerUIOptions = new SwaggerUIOptions(); builder.Configuration.GetSection(SwaggerUIOptions.Position).Bind(swaggerUIOptions); -//Default httpclient -builder.Services.ConfigureHttpClientDefaults(http => -{ - http.AddStandardResilienceHandler(); -}); +//Default httpclient - Aspire now +//builder.Services.ConfigureHttpClientDefaults(http => +//{ +// http.AddStandardResilienceHandler(); +//}); builder.Services.AddDbContextFactory( options => options.UseNpgsql(builder.Configuration.GetConnectionString("AccountingSalesTaxDbContext")), ServiceLifetime.Scoped); +builder.EnrichNpgsqlDbContext(); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; @@ -79,14 +82,14 @@ //TODO: Cors builder.Services.AddCustomCors(); -//Tracing and metrics -builder.Logging.AddOpenTelemetry(logging => -{ - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; -}); +//Tracing and metrics +//builder.Logging.AddOpenTelemetry(logging => +//{ +// logging.IncludeFormattedMessage = true; +// logging.IncludeScopes = true; +//}); -builder.Services.AddTracingAndMetrics(); +//builder.Services.AddTracingAndMetrics(); //Swagger config var xmlPath = Path.Combine(AppContext.BaseDirectory, @@ -113,7 +116,6 @@ options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); builder.Services.AddHttpContextAccessor(); -builder.Services.AddEndpointsApiExplorer(); //Route config builder.Services.Configure(options => @@ -128,6 +130,8 @@ var app = builder.Build(); +app.MapDefaultEndpoints(); + app.UseExceptionHandler(app.Logger, app.Environment); // Configure the HTTP request pipeline. diff --git a/src/Ubik.Accounting.SalesOrVatTax.Api/Ubik.Accounting.SalesOrVatTax.Api.csproj b/src/Ubik.Accounting.SalesOrVatTax.Api/Ubik.Accounting.SalesOrVatTax.Api.csproj index 2f531ded..09761860 100644 --- a/src/Ubik.Accounting.SalesOrVatTax.Api/Ubik.Accounting.SalesOrVatTax.Api.csproj +++ b/src/Ubik.Accounting.SalesOrVatTax.Api/Ubik.Accounting.SalesOrVatTax.Api.csproj @@ -16,10 +16,10 @@ + - @@ -33,6 +33,7 @@ + diff --git a/src/Ubik.Accounting.Structure.Api/Program.cs b/src/Ubik.Accounting.Structure.Api/Program.cs index bf4d7d0c..57157c3c 100644 --- a/src/Ubik.Accounting.Structure.Api/Program.cs +++ b/src/Ubik.Accounting.Structure.Api/Program.cs @@ -26,6 +26,7 @@ public class Program public static async Task Main(string[] args) { var builder = WebApplication.CreateBuilder(args); + builder.AddServiceDefaults(); //Log //TODO: Begin to log usefull things @@ -42,11 +43,11 @@ public static async Task Main(string[] args) //Auth server and JWT (no need, no auth) //builder.Services.AddAuthServerAndJwt(authOptions); - //Default httpclient - builder.Services.ConfigureHttpClientDefaults(http => - { - http.AddStandardResilienceHandler(); - }); + ////Default httpclient - Aspire now + //builder.Services.ConfigureHttpClientDefaults(http => + //{ + // http.AddStandardResilienceHandler(); + //}); //DB builder.Services.AddDbContextFactory( @@ -102,14 +103,14 @@ public static async Task Main(string[] args) //TODO: Cors builder.Services.AddCustomCors(); - //Tracing and metrics - builder.Logging.AddOpenTelemetry(logging => - { - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; - }); + ////Tracing and metrics - Aspire now + //builder.Logging.AddOpenTelemetry(logging => + //{ + // logging.IncludeFormattedMessage = true; + // logging.IncludeScopes = true; + //}); - builder.Services.AddTracingAndMetrics(); + //builder.Services.AddTracingAndMetrics(); //Swagger config var xmlPath = Path.Combine(AppContext.BaseDirectory, @@ -149,7 +150,10 @@ public static async Task Main(string[] args) //Build the app var app = builder.Build(); - app.MapPrometheusScrapingEndpoint(); + //Build the app + app.MapDefaultEndpoints(); + + //app.MapPrometheusScrapingEndpoint(); //app.UseSerilogRequestLogging(); app.UseExceptionHandler(app.Logger, app.Environment); diff --git a/src/Ubik.Accounting.Structure.Api/Ubik.Accounting.Structure.Api.csproj b/src/Ubik.Accounting.Structure.Api/Ubik.Accounting.Structure.Api.csproj index fcb64e45..3f4f70b7 100644 --- a/src/Ubik.Accounting.Structure.Api/Ubik.Accounting.Structure.Api.csproj +++ b/src/Ubik.Accounting.Structure.Api/Ubik.Accounting.Structure.Api.csproj @@ -36,7 +36,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -46,6 +45,7 @@ + diff --git a/src/Ubik.Accounting.Transaction.Api/Program.cs b/src/Ubik.Accounting.Transaction.Api/Program.cs index 8a6db219..aa8b4859 100644 --- a/src/Ubik.Accounting.Transaction.Api/Program.cs +++ b/src/Ubik.Accounting.Transaction.Api/Program.cs @@ -19,6 +19,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + //Options var authOptions = new AuthServerOptions(); builder.Configuration.GetSection(AuthServerOptions.Position).Bind(authOptions); @@ -27,11 +29,11 @@ var swaggerUIOptions = new SwaggerUIOptions(); builder.Configuration.GetSection(SwaggerUIOptions.Position).Bind(swaggerUIOptions); -//Default httpclient -builder.Services.ConfigureHttpClientDefaults(http => -{ - http.AddStandardResilienceHandler(); -}); +//Default httpclient - Aspire now +//builder.Services.ConfigureHttpClientDefaults(http => +//{ +// http.AddStandardResilienceHandler(); +//}); builder.Services.AddDbContextFactory( options => options.UseNpgsql(builder.Configuration.GetConnectionString("AccountingTxContext")), ServiceLifetime.Scoped); @@ -82,12 +84,12 @@ //TODO: Cors builder.Services.AddCustomCors(); -//Tracing and metrics -builder.Logging.AddOpenTelemetry(logging => -{ - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; -}); +//Tracing and metrics - Aspire now +//builder.Logging.AddOpenTelemetry(logging => +//{ +// logging.IncludeFormattedMessage = true; +// logging.IncludeScopes = true; +//}); //Services builder.Services.AddScoped(); @@ -97,7 +99,7 @@ builder.Services.AddScoped(); builder.Services.AddTransient(); -builder.Services.AddTracingAndMetrics(); +//builder.Services.AddTracingAndMetrics(); builder.Services.AddControllers(o => { @@ -114,7 +116,6 @@ options.LowercaseUrls = true; }); -builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); @@ -126,6 +127,8 @@ var app = builder.Build(); +app.MapDefaultEndpoints(); + app.UseExceptionHandler(app.Logger, app.Environment); // Configure the HTTP request pipeline. diff --git a/src/Ubik.Accounting.Transaction.Api/Ubik.Accounting.Transaction.Api.csproj b/src/Ubik.Accounting.Transaction.Api/Ubik.Accounting.Transaction.Api.csproj index 91a50973..e35624f8 100644 --- a/src/Ubik.Accounting.Transaction.Api/Ubik.Accounting.Transaction.Api.csproj +++ b/src/Ubik.Accounting.Transaction.Api/Ubik.Accounting.Transaction.Api.csproj @@ -18,7 +18,6 @@ - @@ -30,6 +29,7 @@ + diff --git a/src/Ubik.Accounting.WebApp/Program.cs b/src/Ubik.Accounting.WebApp/Program.cs index 795f3d44..3512253b 100644 --- a/src/Ubik.Accounting.WebApp/Program.cs +++ b/src/Ubik.Accounting.WebApp/Program.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; @@ -22,6 +22,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents() @@ -190,6 +192,8 @@ }); var app = builder.Build(); + +app.MapDefaultEndpoints(); app.UseForwardedHeaders(); app.UseHttpsRedirection(); //app.UseHsts(); diff --git a/src/Ubik.Accounting.WebApp/Properties/launchSettings.json b/src/Ubik.Accounting.WebApp/Properties/launchSettings.json index fc9ddcc1..bedc8875 100644 --- a/src/Ubik.Accounting.WebApp/Properties/launchSettings.json +++ b/src/Ubik.Accounting.WebApp/Properties/launchSettings.json @@ -12,7 +12,7 @@ }, "https": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, @@ -49,4 +49,4 @@ "sslPort": 44339 } } -} \ No newline at end of file +} diff --git a/src/Ubik.Accounting.WebApp/Styles/app.output.css b/src/Ubik.Accounting.WebApp/Styles/app.output.css index 330a307a..5eaa98d2 100644 --- a/src/Ubik.Accounting.WebApp/Styles/app.output.css +++ b/src/Ubik.Accounting.WebApp/Styles/app.output.css @@ -1120,10 +1120,6 @@ p:is(.dark *) { width: 1.25rem; } -.w-52 { - width: 13rem; -} - .w-6 { width: 1.5rem; } @@ -2402,36 +2398,20 @@ p:is(.dark *) { display: block; } - .md\:flex { - display: flex; - } - .md\:hidden { display: none; } - .md\:w-auto { - width: auto; - } - - .md\:w-\[520px\] { - width: 520px; - } - .md\:w-28 { width: 7rem; } - .md\:items-center { - align-items: center; - } - - .md\:justify-between { - justify-content: space-between; + .md\:w-\[520px\] { + width: 520px; } - .md\:p-0 { - padding: 0px; + .md\:w-auto { + width: auto; } .md\:p-3 { @@ -2442,11 +2422,6 @@ p:is(.dark *) { padding: 1.25rem; } - .md\:px-4 { - padding-left: 1rem; - padding-right: 1rem; - } - .md\:px-5 { padding-left: 1.25rem; padding-right: 1.25rem; @@ -2487,10 +2462,6 @@ p:is(.dark *) { width: 1024px; } - .lg\:w-\[520px\] { - width: 520px; - } - .lg\:table-fixed { table-layout: fixed; } diff --git a/src/Ubik.Accounting.WebApp/Ubik.Accounting.WebApp.csproj b/src/Ubik.Accounting.WebApp/Ubik.Accounting.WebApp.csproj index d6583d52..3d131bfa 100644 --- a/src/Ubik.Accounting.WebApp/Ubik.Accounting.WebApp.csproj +++ b/src/Ubik.Accounting.WebApp/Ubik.Accounting.WebApp.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Ubik.AppHost/Program.cs b/src/Ubik.AppHost/Program.cs new file mode 100644 index 00000000..8906be48 --- /dev/null +++ b/src/Ubik.AppHost/Program.cs @@ -0,0 +1,85 @@ +using Aspire.Hosting; + +var builder = DistributedApplication.CreateBuilder(args); + +//Postgres +var postgresUsername = builder.AddParameter("postgres-username", secret: true); +var postgresPassword = builder.AddParameter("postgres-password", secret: true); + +var db = builder.AddPostgres("ubik-postgres-aspire", postgresUsername, postgresPassword) + .WithDataVolume(isReadOnly: false) + .WithPgAdmin() + .WithLifetime(ContainerLifetime.Persistent); + +//Keycloak +var keycloakUsername = builder.AddParameter("keycloak-username", secret: true); +var keycloakPassword = builder.AddParameter("keycloak-password", secret: true); + +var keycloak = builder.AddKeycloak("ubik-keycloak-aspire", 8080, keycloakUsername, keycloakPassword) + .WithRealmImport("../../tests/Ubik.Api.Tests.Integration/import") + .WithLifetime(ContainerLifetime.Persistent); + +//RabbitMQ +var rabbitUsername = builder.AddParameter("rabbit-username", secret: true); +var rabbitPassword = builder.AddParameter("rabbit-password", secret: true); +var rabbitmq = builder.AddRabbitMQ("ubik-rabbitmq-aspire",rabbitPassword,rabbitPassword) + .WithLifetime(ContainerLifetime.Persistent); + +//Redis +var redis = builder.AddRedis("ubik-redis-aspire") + .WithLifetime(ContainerLifetime.Persistent); + +//Accounting SalesOrVatTax API +var accountingSalesVatTaxDb = db.AddDatabase("ubik-db-accounting-salesorvattax-aspire"); +var accountingSalesVatTaxApi = builder.AddProject("ubik-api-accounting-salesorvattax-aspire") + .WaitFor(keycloak) + .WaitFor(rabbitmq) + .WithEnvironment("ConnectionStrings__AccountingSalesTaxDbContext", accountingSalesVatTaxDb.Resource.ConnectionStringExpression) + .WithEnvironment("MessageBroker__Host", rabbitmq.Resource.ConnectionStringExpression); + +//Accounting Structure +var accountingStructureDb = db.AddDatabase("ubik-db-accounting-structure-aspire"); +var accountingStructureApi = builder.AddProject("ubik-api-accounting-structure-aspire") + .WaitFor(keycloak) + .WaitFor(rabbitmq) + .WaitFor(redis) + .WithEnvironment("ConnectionStrings__AccountingContext", accountingStructureDb.Resource.ConnectionStringExpression) + .WithEnvironment("MessageBroker__Host", rabbitmq.Resource.ConnectionStringExpression); + +//Accounting Transaction +var accountingTxDb = db.AddDatabase("ubik-db-accounting-tx-aspire"); +var accountingTxApi = builder.AddProject("ubik-api-accounting-tx-aspire") + .WaitFor(keycloak) + .WaitFor(rabbitmq) + .WaitFor(redis) + .WithEnvironment("ConnectionStrings__AccountingTxContext", accountingTxDb.Resource.ConnectionStringExpression) + .WithEnvironment("MessageBroker__Host", rabbitmq.Resource.ConnectionStringExpression); + +//Security API +var securityDb = db.AddDatabase("ubik-db-security-aspire"); +var securityApi = builder.AddProject("ubik-api-security-aspire") + .WaitFor(securityDb) + .WaitFor(keycloak) + .WaitFor(rabbitmq) + .WaitFor(redis) + .WithEnvironment("ConnectionStrings__SecurityDbContext", securityDb.Resource.ConnectionStringExpression) + .WithEnvironment("MessageBroker__Host", rabbitmq.Resource.ConnectionStringExpression); + +//Yarp proxy +var yarp = builder.AddProject("ubik-proxy-yarp-aspire") + .WaitFor(securityDb) + .WaitFor(keycloak) + .WaitFor(rabbitmq) + .WaitFor(redis) + .WaitFor(securityApi) + .WaitFor(accountingSalesVatTaxApi) + .WaitFor(accountingStructureApi) + .WaitFor(accountingTxApi) + .WithEnvironment("RedisCache__ConnectionString", redis.Resource.ConnectionStringExpression); + +//Webapp +builder.AddProject("ubik-webapp-aspire") + .WaitFor(yarp) + .WithEnvironment("RedisCache__ConnectionString", redis.Resource.ConnectionStringExpression); + +builder.Build().Run(); diff --git a/src/Ubik.AppHost/Properties/launchSettings.json b/src/Ubik.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..db73835a --- /dev/null +++ b/src/Ubik.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17268;http://localhost:15275", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21217", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22191" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15275", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19186", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20059" + } + } + } +} diff --git a/src/Ubik.AppHost/Ubik.AppHost.csproj b/src/Ubik.AppHost/Ubik.AppHost.csproj new file mode 100644 index 00000000..ac48ea5c --- /dev/null +++ b/src/Ubik.AppHost/Ubik.AppHost.csproj @@ -0,0 +1,31 @@ + + + + + + Exe + net8.0 + enable + enable + true + eb996d54-08df-48fa-9f66-75afd9403045 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ubik.AppHost/appsettings.Development.json b/src/Ubik.AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/src/Ubik.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Ubik.AppHost/appsettings.json b/src/Ubik.AppHost/appsettings.json new file mode 100644 index 00000000..05117715 --- /dev/null +++ b/src/Ubik.AppHost/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + }, + "Parameters": { + "postgres-username": "postgres", + "postgres-password": "test01", + "keycloak-username": "admin", + "keycloak-password": "admin", + "rabbit-username": "guest", + "rabbit-password": "guest" + } +} + + diff --git a/src/Ubik.Security.Api/Program.cs b/src/Ubik.Security.Api/Program.cs index a2cf137e..bf1d9cde 100644 --- a/src/Ubik.Security.Api/Program.cs +++ b/src/Ubik.Security.Api/Program.cs @@ -21,7 +21,7 @@ var builder = WebApplication.CreateBuilder(args); - +builder.AddServiceDefaults(); //Options used in Program.cs var msgBrokerOptions = new MessageBrokerOptions(); @@ -39,14 +39,15 @@ //DB builder.Services.AddDbContextFactory( options => options.UseNpgsql(builder.Configuration.GetConnectionString("SecurityDbContext")), ServiceLifetime.Scoped); +builder.EnrichNpgsqlDbContext(); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; -//Default httpclient -builder.Services.ConfigureHttpClientDefaults(http => -{ - http.AddStandardResilienceHandler(); -}); +//Default httpclient -- Aspire now +//builder.Services.ConfigureHttpClientDefaults(http => +//{ +// http.AddStandardResilienceHandler(); +//}); //Auth Provider builder.Services.AddHttpClient(client => @@ -95,14 +96,14 @@ //TODO: Cors builder.Services.AddCustomCors(); -//Logs tracing and metrics -builder.Logging.AddOpenTelemetry(logging => -{ - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; -}); +////Logs tracing and metrics -- Aspire now +//builder.Logging.AddOpenTelemetry(logging => +//{ +// logging.IncludeFormattedMessage = true; +// logging.IncludeScopes = true; +//}); -builder.Services.AddTracingAndMetrics(); +//builder.Services.AddTracingAndMetrics(); //Swagger config var xmlPath = Path.Combine(AppContext.BaseDirectory, @@ -151,7 +152,9 @@ //Build the app var app = builder.Build(); -app.MapPrometheusScrapingEndpoint(); +app.MapDefaultEndpoints(); + +//app.MapPrometheusScrapingEndpoint(); //app.UseSerilogRequestLogging(); app.UseExceptionHandler(app.Logger, app.Environment); diff --git a/src/Ubik.Security.Api/Ubik.Security.Api.csproj b/src/Ubik.Security.Api/Ubik.Security.Api.csproj index 3a4d762e..6d50bd92 100644 --- a/src/Ubik.Security.Api/Ubik.Security.Api.csproj +++ b/src/Ubik.Security.Api/Ubik.Security.Api.csproj @@ -36,10 +36,10 @@ + - @@ -48,6 +48,7 @@ + diff --git a/src/Ubik.ServiceDefaults/Extensions.cs b/src/Ubik.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000..13151bf4 --- /dev/null +++ b/src/Ubik.ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/src/Ubik.ServiceDefaults/Ubik.ServiceDefaults.csproj b/src/Ubik.ServiceDefaults/Ubik.ServiceDefaults.csproj new file mode 100644 index 00000000..6c036a13 --- /dev/null +++ b/src/Ubik.ServiceDefaults/Ubik.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/src/Ubik.YarpProxy/Program.cs b/src/Ubik.YarpProxy/Program.cs index 8eaea8cb..83d70f95 100644 --- a/src/Ubik.YarpProxy/Program.cs +++ b/src/Ubik.YarpProxy/Program.cs @@ -15,6 +15,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + //Auth from auth provider, for Admin access to usermgt service var authOptions = new AuthServerOptions(); builder.Configuration.GetSection(AuthServerOptions.Position).Bind(authOptions); @@ -52,12 +54,12 @@ builder.Services.AddSwaggerGen(); -//Httpclient for userService (called to retrive auth/authorize the request sent) +//Httpclient for userService (called to retrive auth/authorize the request sent) //Aspire NOW //Internal ip or domain not exposed to public accesses -builder.Services.ConfigureHttpClientDefaults(http => -{ - http.AddStandardResilienceHandler(); -}); +//builder.Services.ConfigureHttpClientDefaults(http => +//{ +// http.AddStandardResilienceHandler(); +//}); builder.Services.AddHttpClient(client => { @@ -146,6 +148,8 @@ //Build var app = builder.Build(); +app.MapDefaultEndpoints(); + //Swagger if (app.Environment.IsDevelopment()) { diff --git a/src/Ubik.YarpProxy/Properties/launchSettings.json b/src/Ubik.YarpProxy/Properties/launchSettings.json index 5352e580..257f9af4 100644 --- a/src/Ubik.YarpProxy/Properties/launchSettings.json +++ b/src/Ubik.YarpProxy/Properties/launchSettings.json @@ -11,7 +11,7 @@ }, "https": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -47,4 +47,4 @@ "sslPort": 44355 } } -} \ No newline at end of file +} diff --git a/src/Ubik.YarpProxy/Ubik.YarpProxy.csproj b/src/Ubik.YarpProxy/Ubik.YarpProxy.csproj index c6584fcb..ac400895 100644 --- a/src/Ubik.YarpProxy/Ubik.YarpProxy.csproj +++ b/src/Ubik.YarpProxy/Ubik.YarpProxy.csproj @@ -14,7 +14,6 @@ - @@ -23,6 +22,7 @@ + diff --git a/src/Ubik.sln b/src/Ubik.sln index a439ee9c..57549435 100644 --- a/src/Ubik.sln +++ b/src/Ubik.sln @@ -37,6 +37,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ubik.Accounting.Transaction EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ubik.Accounting.Transaction.Contracts", "Ubik.Accounting.Transaction.Contracts\Ubik.Accounting.Transaction.Contracts.csproj", "{B4032FBF-3A2E-45E6-AB38-E148103B3A62}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ubik.AppHost", "Ubik.AppHost\Ubik.AppHost.csproj", "{5498C8DA-EEDC-49E3-A96B-93B0336CAAA5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ubik.ServiceDefaults", "Ubik.ServiceDefaults\Ubik.ServiceDefaults.csproj", "{E2311EFC-495A-4DAF-AB87-F47D18CED26A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -111,6 +115,14 @@ Global {B4032FBF-3A2E-45E6-AB38-E148103B3A62}.Debug|Any CPU.Build.0 = Debug|Any CPU {B4032FBF-3A2E-45E6-AB38-E148103B3A62}.Release|Any CPU.ActiveCfg = Release|Any CPU {B4032FBF-3A2E-45E6-AB38-E148103B3A62}.Release|Any CPU.Build.0 = Release|Any CPU + {5498C8DA-EEDC-49E3-A96B-93B0336CAAA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5498C8DA-EEDC-49E3-A96B-93B0336CAAA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5498C8DA-EEDC-49E3-A96B-93B0336CAAA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5498C8DA-EEDC-49E3-A96B-93B0336CAAA5}.Release|Any CPU.Build.0 = Release|Any CPU + {E2311EFC-495A-4DAF-AB87-F47D18CED26A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2311EFC-495A-4DAF-AB87-F47D18CED26A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2311EFC-495A-4DAF-AB87-F47D18CED26A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2311EFC-495A-4DAF-AB87-F47D18CED26A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 71c63c07e941a58e166ed2a9bb4b61d55e8c5269 Mon Sep 17 00:00:00 2001 From: ubik Date: Mon, 18 Nov 2024 13:36:16 +0100 Subject: [PATCH 2/2] Change readme --- README.md | 73 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 2fe65d2d..61ef01c2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ -LAST BIG PR implemented: +# News -- double-entry preparation -- 2 new backend apis (tx and sales-or-vat-tax) -- Blazor better token management +LAST PR implemented: -Next steps between: +- Aspire 9.0 (very minimal implementation without service discovery or test project) +- Now, you can run the apphost project and have fun -- vat-sales tax module implementation +Next steps or in progress: + +- Vat-sales tax module implementation - .Net 9 (Blazor adaptations) - SingalR hub to trace tx status - Blazor double-entry ui -- Aspire (re-test the thing) +- Aspire, enhance service defaults and see for discovery and maybe tests project - will see... # Ubik - Accounting @@ -21,7 +22,7 @@ A .net8 project to manage double entry accounting. (it's the very beginning of a Microservices arch and supports multi-tenants. -But for now, it's an experimental project that references a lot of things about .net 8 - Backend and Frontend sides of things -. +But for now, it's an experimental project that references a lot of things about .net 8 (9 soon) - Backend and Frontend sides of things -. ## Not ready for production @@ -30,11 +31,23 @@ At this stage, **DO NOT USE THIS SYSTEM ON A PRODUCTION** environnement. - **Don't forget to change all the "secrets" exposed here, and in the configuration files.** - **Never re-use the Keycloak realm configuration file.** -## For the Kubernetes/Minikube guys +# Run - Debug + +Choose between section 1), 2) or 3) + +## 1) For the Aspire guys + +You can run all the stuff with the host project: + +`dotnet run --project .\src\Ubik.AppHost\Ubik.AppHost.csproj` -For detailed instructions on deploying locally with Minikube (full experience), please refer to the [local deployment guide](./deploy/deploy-local-readme.md). +Or, in Visual Studio, you can set the Aspire Host project as project startup and you will be able to play/debug with all the dependencies. -## Build and Run +## 2) For the Kubernetes/Minikube guys + +For detailed instructions on deploying locally with Minikube, please refer to the [local deployment guide](./deploy/deploy-local-readme.md). + +## 3) For the Docker guys **At the root of the repository**, "Mount" the dependencies with Docker by running this command in your terminal: @@ -49,9 +62,7 @@ For detailed instructions on deploying locally with Minikube (full experience), > - Pgadmin: to admin your dbs if needed > - Apis: backend apis (security/accounting) for integration testing -### Ready to play and debug - -#### Run backend Apis or define a multiple projects startup +#### Run backend Apis or define a multiple projects startup to debug `dotnet run --launch-profile https --project ./src/Ubik.Accounting.SalesOrVatTax.Api/Ubik.Accounting.SalesOrVatTax.Api.csproj` @@ -69,9 +80,13 @@ For detailed instructions on deploying locally with Minikube (full experience), `dotnet run --launch-profile https --project ./src/Ubik.Accounting.WebApp/Ubik.Accounting.WebApp.csproj` -And now (when all the stuff are up and running), you can access the very first version of a the Blazor 8 webapp here +## For All + +After choosing your prefered way 1), 2) 3) you can access the Blazor app here: -### All the things are up + + +# All the things are up If you try to access the Blazor app, you will be redirected on a Keycloak login page @@ -89,6 +104,12 @@ Try to log with different access rights and play with the only available "Accoun Now you can run your preferred code editor and start to deep dive... (see below) +# Integration tests + +If you want to contribute, see section "3) For the Docker guys" to be able to mount the dependencies and run the integration tests project. No time to update to Aspire Test and change all the related stuff (github actions etc.). + +At the end, simply run `docker compose -f .\docker-compose.yml -f .\docker-integration-tests.yml up -d`, and you will be able to run the tests project. + ## External libs | Package | For what | @@ -100,6 +121,8 @@ Now you can run your preferred code editor and start to deep dive... (see below) | [LanguageExt.Core](https://github.com/louthy/language-ext) | use Either pattern | | [Masstransit](https://github.com/MassTransit/MassTransit) | message bus abstraction + inbox/outbox pattern | +Send some love on github to this projects... + ## Yarp Proxy -- Ubik.YarpProxy -- @@ -150,15 +173,11 @@ First you can maybe uncomment this lines in Ubik.Accounting.WebApp.csproj, if yo ``` -I don't know why, but my build fails in Github actions if I let this Tailwind instructions (related to the forms plugin). If someone has an idea... -`EXEC : error : Cannot find module '@tailwindcss/forms'` - **To use Tailwind in DEV**, you need to have node installed, Tailwind and Tailwind forms... I let you go on their site for installation instructions. Maybe, `npm install` command can be sufficient in the WebApp (server) project, cannot be sure because it seems to trigger a weird error in the dotnet build github actions. | Package | For what | |----------- | -------- | | [BalzorPageScript](https://github.com/MackinnonBuck/blazor-page-script) | small tool that allows Tailwind to apply its Dark or Light theme on each page and component | -| [IdentityModel](https://github.com/IdentityModel) | Small extension to refresh token in OpenIdC | Send some love on github to this projects... @@ -167,29 +186,19 @@ Send some love on github to this projects... (server side) - Static components and pages -- Typed HttpClient to access the backend api -- Tailwind config - Tailwind Flowbite design layout etc - A very minimal reverse proxy controller that allows components (automode) to call the backend api when they are WASM. - Some stuff about security (Token cache service) -In program.cs, you can access the config of: - -- Redis for token caching -- CascadingAuthenticationState -- Cookie auth with OpenIdC (connection + refresh token in OnValidatePrincipal) -- ... - => next, implement new .Net 9 Blazor stuff related to authentication and render modes. ### Ubik.Accounting.WebApp.Client (client-auto side) -- All components are able to run in auto mode (InteractiveServer or InteractiveWasm) +- **All components are able to run in auto mode (InteractiveServer or InteractiveWasm)** - Authorization components (depending on authorized state) - Minimal common components (Alerts, Buttons, Form Inputs, Grid *(Microsoft inspired/copied)*, Modal, Spinners) - Tailwind Flowbite design for components -- The implementation of the facade that call the reverse proxy controller that call the Backend api for automode - Error components that manage problemdetails returns from backend api (try to add a booking account with an existing code as an example) ### Ubik.Accounting.WebApp.Shared @@ -203,7 +212,7 @@ In program.cs, you can access the config of: -- Ubik.Accounting.Api.Tests.Integration -- -- In integration tests => test all proxy endpoints +- In integration tests => test all proxy endpoints ## Others