From e53651ecf57ed5c6bddc83598f3fe96ee109873e Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 17 Nov 2025 20:36:09 +0000 Subject: [PATCH] Moved and cleaned up project structure (removed `AStar.Web.Web`, migrated contents to `AStar.Web`). --- .github/copilot-instructions.md | 144 ++++++++++++------ .github/instructions/blazor.instructions.md | 30 ++-- .github/prompts/astar.prompt.md | 3 +- .github/prompts/xunit.prompt.md | 6 +- .github/workflows/main_astar-dev.yml | 2 +- AStar.Web.slnx | 64 ++++---- .../AStar.Web.AppHost.csproj | 4 +- src/aspire/AStar.Web.AppHost/AppHost.cs | 6 +- .../AStar.Web.ServiceDefaults/Extensions.cs | 6 +- .../AStar.Web.ApiService.csproj | 2 +- .../apis/AStar.Web.ApiService/Program.cs | 7 +- src/uis/AStar.Web.Web/Components/Routes.razor | 6 - .../AStar.Web.csproj} | 2 +- .../Components/App.razor | 2 +- .../Components/Layout/MainLayout.razor | 0 .../Components/Layout/MainLayout.razor.css | 36 ++--- .../Components/Layout/NavMenu.razor | 0 .../Components/Layout/NavMenu.razor.css | 34 ++--- .../Components/Pages/Counter.razor | 2 +- .../Components/Pages/Error.razor | 6 +- .../Components/Pages/Home.razor | 0 .../Components/Pages/Weather.razor | 2 +- src/uis/AStar.Web/Components/Routes.razor | 7 + .../Components/_Imports.razor | 4 +- .../{AStar.Web.Web => AStar.Web}/Program.cs | 8 +- .../Properties/launchSettings.json | 0 .../WeatherApiClient.cs | 7 +- .../appsettings.json | 0 .../wwwroot/app.css | 8 +- .../wwwroot/favicon.png | Bin .../AStar.Web.Tests/AStar.Web.Tests.csproj | 2 +- test/aspire/AStar.Web.Tests/WebTests.cs | 3 +- 32 files changed, 228 insertions(+), 175 deletions(-) delete mode 100644 src/uis/AStar.Web.Web/Components/Routes.razor rename src/uis/{AStar.Web.Web/AStar.Web.Web.csproj => AStar.Web/AStar.Web.csproj} (70%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/App.razor (87%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Layout/MainLayout.razor (100%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Layout/MainLayout.razor.css (72%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Layout/NavMenu.razor (100%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Layout/NavMenu.razor.css (86%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Pages/Counter.razor (90%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Pages/Error.razor (89%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Pages/Home.razor (100%) rename src/uis/{AStar.Web.Web => AStar.Web}/Components/Pages/Weather.razor (96%) create mode 100644 src/uis/AStar.Web/Components/Routes.razor rename src/uis/{AStar.Web.Web => AStar.Web}/Components/_Imports.razor (87%) rename src/uis/{AStar.Web.Web => AStar.Web}/Program.cs (84%) rename src/uis/{AStar.Web.Web => AStar.Web}/Properties/launchSettings.json (100%) rename src/uis/{AStar.Web.Web => AStar.Web}/WeatherApiClient.cs (86%) rename src/uis/{AStar.Web.Web => AStar.Web}/appsettings.json (100%) rename src/uis/{AStar.Web.Web => AStar.Web}/wwwroot/app.css (94%) rename src/uis/{AStar.Web.Web => AStar.Web}/wwwroot/favicon.png (100%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 6a3f323..62b1d25 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -6,106 +6,147 @@ Keep instructions short and concrete — point to exact files and examples the a ## High-level architecture (what to know fast) - Monorepo of .NET 9 projects grouped under `src/` and tests under `test/`. -- The repo follows the Aspire patterns: shared helpers live in the `_aspire/` folder and are consumed by APIs, services and the UI. +- The repo follows the Aspire patterns: shared helpers live in the `_aspire/` folder and are consumed by APIs, services + and the UI. - Important surface areas you should read before editing code: - - App host / orchestration: `src/_aspire/AStar.Dev.AppHost/AppHost.cs` — builds a DistributedApplication and wires application projects. - - Shared service helpers: `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs` — AddServiceDefaults, OpenTelemetry wiring, health checks. - - Constants (authoritative names): `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs` — DB and service name constants. - - Example API wiring: `src/apis/AStar.Dev.Files.Api/Program.cs` shows the typical minimal Program.cs and extension usage. - - Shared packages: `src/nuget-packages/*` contain code intended for reuse across services; prefer adding cross-cutting changes here. + - App host / orchestration: `src/_aspire/AStar.Dev.AppHost/AppHost.cs` — builds a DistributedApplication and wires + application projects. + - Shared service helpers: `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs` — AddServiceDefaults, OpenTelemetry + wiring, health checks. + - Constants (authoritative names): `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs` — DB and service name + constants. + - Example API wiring: `src/apis/AStar.Dev.Files.Api/Program.cs` shows the typical minimal Program.cs and extension + usage. + - Shared packages: `src/nuget-packages/*` contain code intended for reuse across services; prefer adding + cross-cutting changes here. ## What to read & re-use (concrete files) -- Use `AspireConstants` for DB/service names (avoid string literals): `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`. -- Reuse `AddServiceDefaults()` from `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs` for telemetry, logging, health checks. +- Use `AspireConstants` for DB/service names (avoid string literals): + `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`. +- Reuse `AddServiceDefaults()` from `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs` for telemetry, logging, health + checks. - Register EF contexts with `AddSqlServerDbContext(name)` — pass names from `AspireConstants.Sql.*`. - RabbitMQ clients use the constant `AspireConstants.Services.AstarMessaging` when registering/consuming. -- AppHost uses `applicationConfiguration:sqlServerMountDirectory` — check `src/_aspire/AStar.Dev.AppHost/appsettings.json` for local dev hints. +- AppHost uses `applicationConfiguration:sqlServerMountDirectory` — check + `src/_aspire/AStar.Dev.AppHost/appsettings.json` for local dev hints. ## Developer workflows & commands - Build repository (CI): from repo root run - - dotnet build --configuration Release + - dotnet build --configuration Release - Run unit tests (fast): - - dotnet test --filter 'FullyQualifiedName!~Tests.EndToEnd&FullyQualifiedName!~Tests.Integration' - - Tests primarily use xUnit V3 and Shouldly. NSubstitute is used for mocking when necessary. -- When adding shared runtime code, add to `src/nuget-packages/*` and reference the project instead of introducing new global packages. + - dotnet test --filter 'FullyQualifiedName!~Tests.EndToEnd&FullyQualifiedName!~Tests.Integration' + - Tests primarily use xUnit V3 and Shouldly. NSubstitute is used for mocking when necessary. +- When adding shared runtime code, add to `src/nuget-packages/*` and reference the project instead of introducing new + global packages. ## Project-specific conventions and patterns -- Tiny Program.cs: services and APIs are composed via extension methods. Prefer adding extension helpers in `_aspire` or `src/nuget-packages`. -- Telemetry & logging: Serilog + OpenTelemetry. Do not add duplicate OTLP exporters — use `AddOpenTelemetryExporters()` from service defaults. -- Configuration keys: use the existing `Parameters` entries (e.g., `Parameters:sql1-password`) and `applicationConfiguration` keys when present. -- Tests: put unit tests in `test//*.Tests.Unit` and follow existing patterns (Shouldly for assertions, test fixtures in Fixtures/ when needed). -- Whilst the AAA pattern is used, comments should not be added to a test unless the logic is complex. If the logic is complex, consider breaking it into multiple tests. Instead of comments, use blank lines to separate the Arrange, Act, and Assert sections. +- Tiny Program.cs: services and APIs are composed via extension methods. Prefer adding extension helpers in `_aspire` or + `src/nuget-packages`. +- Telemetry & logging: Serilog + OpenTelemetry. Do not add duplicate OTLP exporters — use `AddOpenTelemetryExporters()` + from service defaults. +- Configuration keys: use the existing `Parameters` entries (e.g., `Parameters:sql1-password`) and + `applicationConfiguration` keys when present. +- Tests: put unit tests in `test//*.Tests.Unit` and follow existing patterns (Shouldly for assertions, test + fixtures in Fixtures/ when needed). +- Whilst the AAA pattern is used, comments should not be added to a test unless the logic is complex. If the logic is + complex, consider breaking it into multiple tests. Instead of comments, use blank lines to separate the Arrange, Act, + and Assert sections. - Using statements for Shouldly and Xunit are not required as they are included globally via the relevant `csproj`. - Public methods in the production code should have XML doc comments. Test methods do not require XML doc comments. ## Integration & cross-component communication -- Database contexts: EF DbContexts registered with `AddSqlServerDbContext(name)` use the named DBs from `AspireConstants.Sql.*`. -- Message bus: RabbitMQ client names come from `AspireConstants.Services.*` — follow that naming for publishers/subscribers. -- AppHost mounts local SQL folders via `applicationConfiguration:sqlServerMountDirectory` — used for local integration runs. +- Database contexts: EF DbContexts registered with `AddSqlServerDbContext(name)` use the named DBs from + `AspireConstants.Sql.*`. +- Message bus: RabbitMQ client names come from `AspireConstants.Services.*` — follow that naming for + publishers/subscribers. +- AppHost mounts local SQL folders via `applicationConfiguration:sqlServerMountDirectory` — used for local integration + runs. ## Editing guidance (what NOT to change without approval) - Do not change values in `AspireConstants.cs` without coordinating infra/deployments — these are authoritative. -- Avoid adding new top-level telemetry exporters or duplicate service discovery wiring; reuse `AddServiceDefaults` and `AddOpenTelemetryExporters()`. +- Avoid adding new top-level telemetry exporters or duplicate service discovery wiring; reuse `AddServiceDefaults` and + `AddOpenTelemetryExporters()`. ## Quick examples (copy/paste patterns) - Register files DB context (use constants): - - builder.AddSqlServerDbContext(AspireConstants.Sql.FilesDb); + - builder.AddSqlServerDbContext(AspireConstants.Sql.FilesDb); - Add RabbitMQ client: - - builder.AddRabbitMQClient(AspireConstants.Services.AstarMessaging); + - builder.AddRabbitMQClient(AspireConstants.Services.AstarMessaging); ## Where to add shared code -- If the change is cross-cutting (telemetry, DI helpers, logging) add an extension in `_aspire/` or create/update a project under `src/nuget-packages/` so it can be consumed across services. +- If the change is cross-cutting (telemetry, DI helpers, logging) add an extension in `_aspire/` or create/update a + project under `src/nuget-packages/` so it can be consumed across services. ## Tests & CI expectations -- New production code should include unit tests in the relevant `test/*` project. Follow local test project references (xUnit + Shouldly) and CI filters. -- CI workflow: `.github/workflows/main_astar-dev.yml` runs build + coverage; ensure changes don't dramatically increase test runtime. +- New production code should include unit tests in the relevant `test/*` project. Follow local test project references ( + xUnit + Shouldly) and CI filters. +- CI workflow: `.github/workflows/main_astar-dev.yml` runs build + coverage; ensure changes don't dramatically increase + test runtime. ## If you need more context -- Look at these files first: `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs`, `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`, `src/_aspire/AStar.Dev.AppHost/AppHost.cs`, `src/apis/AStar.Dev.Files.Api/Program.cs`. -- If a service fails to start locally, check `appsettings.Development.json` in the service project and `applicationConfiguration:sqlServerMountDirectory` in AppHost appsettings. +- Look at these files first: `src/_aspire/AStar.Dev.ServiceDefaults/Extensions.cs`, + `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`, `src/_aspire/AStar.Dev.AppHost/AppHost.cs`, + `src/apis/AStar.Dev.Files.Api/Program.cs`. +- If a service fails to start locally, check `appsettings.Development.json` in the service project and + `applicationConfiguration:sqlServerMountDirectory` in AppHost appsettings. ## Naming & configuration conventions -- AspireConstants are authoritative for service and DB names: `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`. Use these constants when referencing DB names, API names, or service names (e.g., `AspireConstants.Sql.FilesDb`, `AspireConstants.Services.AstarMessaging`). +- AspireConstants are authoritative for service and DB names: `src/_aspire/AStar.Dev.Aspire.Common/AspireConstants.cs`. + Use these constants when referencing DB names, API names, or service names (e.g., `AspireConstants.Sql.FilesDb`, + `AspireConstants.Services.AstarMessaging`). - Connection strings and parameters are read via extension helpers. Example config keys: - - `applicationConfiguration:sqlServerMountDirectory` (used by AppHost) - - `Parameters:sql1-password`, `rabbitmq-username`, `rabbitmq-password` (optional parameter placeholders in appsettings.json) + - `applicationConfiguration:sqlServerMountDirectory` (used by AppHost) + - `Parameters:sql1-password`, `rabbitmq-username`, `rabbitmq-password` (optional parameter placeholders in + appsettings.json) ## Common extension helpers & patterns agents should reuse -- AddServiceDefaults (from `AStar.Dev.ServiceDefaults`) is the place for telemetry, health checks, and HTTP client defaults. New services should call `builder.AddServiceDefaults()` early in `Program.cs`. -- AddSqlServerDbContext(name) is used to register EF contexts against named DBs. Look for usages in `src/apis/*` and `src/services/*`. Use `AspireConstants.Sql.*` for the name parameter. -- OpenTelemetry exporters / Application Insights are enabled by environment variables or configuration keys (see `ConfigureOpenTelemetry` in `Extensions.cs`). Don't duplicate exporters; reuse `AddOpenTelemetryExporters()`. +- AddServiceDefaults (from `AStar.Dev.ServiceDefaults`) is the place for telemetry, health checks, and HTTP client + defaults. New services should call `builder.AddServiceDefaults()` early in `Program.cs`. +- AddSqlServerDbContext(name) is used to register EF contexts against named DBs. Look for usages in + `src/apis/*` and `src/services/*`. Use `AspireConstants.Sql.*` for the name parameter. +- OpenTelemetry exporters / Application Insights are enabled by environment variables or configuration keys (see + `ConfigureOpenTelemetry` in `Extensions.cs`). Don't duplicate exporters; reuse `AddOpenTelemetryExporters()`. - RabbitMQ client names are referenced via `AspireConstants.Services.AstarMessaging` — use the constant. ## Testing & CI signals -- CI uses the repository-level GitHub workflow at `.github/workflows/main_astar-dev.yml` which runs `dotnet build --configuration Release` and a dotnet coverage command. Follow the same `dotnet` commands locally: - - Build: `dotnet build --configuration Release` from repository root. - - Test (fast): `dotnet test --filter 'FullyQualifiedName!~Tests.EndToEnd&FullyQualifiedName!~Tests.Integration'` to run unit tests only (the CI wraps this with coverage collector). - - Tests: existing tests use xUnit. Shouldly is used for assertions. NSubstitute is used for mocking when necessary. - - When creating new production code, add unit tests in the corresponding `test/*` project. Follow existing test patterns. +- CI uses the repository-level GitHub workflow at `.github/workflows/main_astar-dev.yml` which runs + `dotnet build --configuration Release` and a dotnet coverage command. Follow the same `dotnet` commands locally: + - Build: `dotnet build --configuration Release` from repository root. + - Test (fast): `dotnet test --filter 'FullyQualifiedName!~Tests.EndToEnd&FullyQualifiedName!~Tests.Integration'` to + run unit tests only (the CI wraps this with coverage collector). + - Tests: existing tests use xUnit. Shouldly is used for assertions. NSubstitute is used for mocking when necessary. + - When creating new production code, add unit tests in the corresponding `test/*` project. Follow existing test + patterns. ## How services start (local dev hints) - Each service has a `Program.cs` that uses builder extension helpers. For local runs you usually need: - - Environment configuration (appsettings.Development.json) is present in many projects. Adjust `Parameters` or environment variables to provide secrets (sql password, rabbitmq password) if required. - - Many services call `builder.AddSqlServerDbContext<...>(AspireConstants.Sql.FilesDb)` — supplying a real SQL connection or a local SQL mount directory (see `applicationConfiguration:sqlServerMountDirectory` in `src/_aspire/AStar.Dev.AppHost/appsettings.json`) will make EF start. + - Environment configuration (appsettings.Development.json) is present in many projects. Adjust `Parameters` or + environment variables to provide secrets (sql password, rabbitmq password) if required. + - Many services call `builder.AddSqlServerDbContext<...>(AspireConstants.Sql.FilesDb)` — supplying a real SQL + connection or a local SQL mount directory (see `applicationConfiguration:sqlServerMountDirectory` in + `src/_aspire/AStar.Dev.AppHost/appsettings.json`) will make EF start. ## Project-specific patterns agents should follow -- Prefer extension methods and small Program.cs files. Most functionality is surfaced via extension packages in `src/_aspire` or `src/nuget-packages`. -- When adding dependencies, consider adding them to the appropriate `src/nuget-packages/*` project if they're intended to be shared across services. Those projects are built and packaged from source inside this repo. -- Logging and telemetry: services use Serilog and OpenTelemetry by default. Use the existing logging patterns (Log.Information / Log.Error) rather than adding new logging frameworks. +- Prefer extension methods and small Program.cs files. Most functionality is surfaced via extension packages in + `src/_aspire` or `src/nuget-packages`. +- When adding dependencies, consider adding them to the appropriate `src/nuget-packages/*` project if they're intended + to be shared across services. Those projects are built and packaged from source inside this repo. +- Logging and telemetry: services use Serilog and OpenTelemetry by default. Use the existing logging patterns ( + Log.Information / Log.Error) rather than adding new logging frameworks. ## Files and locations to reference when changing behavior @@ -117,18 +158,21 @@ Keep instructions short and concrete — point to exact files and examples the a ## Examples (concrete snippets agents can follow) - Register DB context for files DB (use constants): - - builder.AddSqlServerDbContext(AspireConstants.Sql.FilesDb); + - builder.AddSqlServerDbContext(AspireConstants.Sql.FilesDb); - Add RabbitMQ client with consistent service name: - - builder.AddRabbitMQClient(AspireConstants.Services.AstarMessaging); + - builder.AddRabbitMQClient(AspireConstants.Services.AstarMessaging); ## What not to change without checking humans -- Do not change the constants in `AspireConstants.cs` unless you're also updating deployment and orchestration configuration. -- Avoid enabling health endpoints in non-development environments without checking the security implications (see comment in `MapDefaultEndpoints`). +- Do not change the constants in `AspireConstants.cs` unless you're also updating deployment and orchestration + configuration. +- Avoid enabling health endpoints in non-development environments without checking the security implications (see + comment in `MapDefaultEndpoints`). ## Quick checklist for PRs -1. Follow existing extension patterns — prefer adding an extension in `_aspire` or `src/nuget-packages` for cross-cutting changes. +1. Follow existing extension patterns — prefer adding an extension in `_aspire` or `src/nuget-packages` for + cross-cutting changes. 2. Update/consume `AspireConstants` when adding new services or DBs. 3. Ensure telemetry and exporters aren't duplicated (reuse `AddOpenTelemetryExporters`). 4. Run unit tests (see Test section) and ensure CI-friendly filters are respected. diff --git a/.github/instructions/blazor.instructions.md b/.github/instructions/blazor.instructions.md index eea3440..9f1555c 100644 --- a/.github/instructions/blazor.instructions.md +++ b/.github/instructions/blazor.instructions.md @@ -29,7 +29,8 @@ applyTo: '**/*.razor, **/*.razor.cs, **/*.razor.css' ## Error Handling and Validation - Implement proper error handling for Blazor pages and API calls. -- Use logging for error tracking in the backend and consider capturing UI-level errors in Blazor with tools like ErrorBoundary. +- Use logging for error tracking in the backend and consider capturing UI-level errors in Blazor with tools like + ErrorBoundary. - Implement validation using FluentValidation or DataAnnotations in forms. ## Blazor API and Performance Optimization @@ -42,17 +43,23 @@ applyTo: '**/*.razor, **/*.razor.cs, **/*.razor.css' ## Caching Strategies -- Implement in-memory caching for frequently used data, especially for Blazor Server apps. Use IMemoryCache for lightweight caching solutions. +- Implement in-memory caching for frequently used data, especially for Blazor Server apps. Use IMemoryCache for + lightweight caching solutions. - For Blazor WebAssembly, utilize localStorage or sessionStorage to cache application state between user sessions. -- Consider Distributed Cache strategies (like Redis or SQL Server Cache) for larger applications that need shared state across multiple users or clients. -- Cache API calls by storing responses to avoid redundant calls when data is unlikely to change, thus improving the user experience. +- Consider Distributed Cache strategies (like Redis or SQL Server Cache) for larger applications that need shared state + across multiple users or clients. +- Cache API calls by storing responses to avoid redundant calls when data is unlikely to change, thus improving the user + experience. ## State Management Libraries - Use Blazor's built-in Cascading Parameters and EventCallbacks for basic state sharing across components. -- Implement advanced state management solutions using libraries like Fluxor or BlazorState when the application grows in complexity. -- For client-side state persistence in Blazor WebAssembly, consider using Blazored.LocalStorage or Blazored.SessionStorage to maintain state between page reloads. -- For server-side Blazor, use Scoped Services and the StateContainer pattern to manage state within user sessions while minimizing re-renders. +- Implement advanced state management solutions using libraries like Fluxor or BlazorState when the application grows in + complexity. +- For client-side state persistence in Blazor WebAssembly, consider using Blazored.LocalStorage or + Blazored.SessionStorage to maintain state between page reloads. +- For server-side Blazor, use Scoped Services and the StateContainer pattern to manage state within user sessions while + minimizing re-renders. ## API Design and Integration @@ -64,13 +71,16 @@ applyTo: '**/*.razor, **/*.razor.cs, **/*.razor.css' - All unit testing and integration testing should be done in Visual Studio Enterprise. - Test Blazor components and services using xUnit.V3 and bUnit for component testing. - Use NSubstitute for mocking dependencies during tests. -- Use Shouldly for fluent assertions in tests. Fallback to xUnit built-in assertions when Shouldly does not provide the needed functionality. -- Debug Blazor UI issues using browser developer tools and Visual Studio's debugging tools for backend and server-side issues. +- Use Shouldly for fluent assertions in tests. Fallback to xUnit built-in assertions when Shouldly does not provide the + needed functionality. +- Debug Blazor UI issues using browser developer tools and Visual Studio's debugging tools for backend and server-side + issues. - For performance profiling and optimization, rely on Visual Studio's diagnostics tools. ## Security and Authentication -- Implement Authentication and Authorization in the Blazor app where necessary using ASP.NET Identity or JWT tokens for API authentication. +- Implement Authentication and Authorization in the Blazor app where necessary using ASP.NET Identity or JWT tokens for + API authentication. - Use HTTPS for all web communication and ensure proper CORS policies are implemented. ## API Documentation and Scalar diff --git a/.github/prompts/astar.prompt.md b/.github/prompts/astar.prompt.md index f9dbb9d..928ae69 100644 --- a/.github/prompts/astar.prompt.md +++ b/.github/prompts/astar.prompt.md @@ -6,7 +6,8 @@ description: 'Create ASP.NET Minimal API endpoints with proper OpenAPI documenta # ASP.NET Minimal API with OpenAPI -Your goal is to help me create well-structured ASP.NET Minimal API endpoints with correct types and comprehensive OpenAPI/Swagger documentation. +Your goal is to help me create well-structured ASP.NET Minimal API endpoints with correct types and comprehensive +OpenAPI/Swagger documentation. ## API Organization diff --git a/.github/prompts/xunit.prompt.md b/.github/prompts/xunit.prompt.md index 9946f67..5a4aa51 100644 --- a/.github/prompts/xunit.prompt.md +++ b/.github/prompts/xunit.prompt.md @@ -6,7 +6,8 @@ description: 'Get best practices for XUnit unit testing, including data-driven t # XUnit Best Practices -Your goal is to help me write effective unit tests with XUnit.V3, covering both standard and data-driven testing approaches. +Your goal is to help me write effective unit tests with XUnit.V3, covering both standard and data-driven testing +approaches. ## Project Setup @@ -48,7 +49,8 @@ Your goal is to help me write effective unit tests with XUnit.V3, covering both ## Assertions -- Use the `Shouldly` NuGet package for more readable assertions. Only use the xUnit built-in assertions when Shouldly does not provide the needed functionality. +- Use the `Shouldly` NuGet package for more readable assertions. Only use the xUnit built-in assertions when Shouldly + does not provide the needed functionality. - Use `Assert.Equal` for value equality - Use `Assert.Same` for reference equality - Use `Assert.True`/`Assert.False` for boolean conditions diff --git a/.github/workflows/main_astar-dev.yml b/.github/workflows/main_astar-dev.yml index adbc901..d19221d 100644 --- a/.github/workflows/main_astar-dev.yml +++ b/.github/workflows/main_astar-dev.yml @@ -25,7 +25,7 @@ jobs: uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' - + - name: 🛠 Set up JDK 17 uses: actions/setup-java@v4 with: diff --git a/AStar.Web.slnx b/AStar.Web.slnx index 132481b..35f1939 100644 --- a/AStar.Web.slnx +++ b/AStar.Web.slnx @@ -1,34 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj b/src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj index db6f588..a381483 100644 --- a/src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj +++ b/src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/aspire/AStar.Web.AppHost/AppHost.cs b/src/aspire/AStar.Web.AppHost/AppHost.cs index 5fa5bd6..18a39b9 100644 --- a/src/aspire/AStar.Web.AppHost/AppHost.cs +++ b/src/aspire/AStar.Web.AppHost/AppHost.cs @@ -1,9 +1,11 @@ +using Projects; + var builder = DistributedApplication.CreateBuilder(args); -var apiService = builder.AddProject("apiservice") +var apiService = builder.AddProject("apiservice") .WithHttpHealthCheck("/health"); -builder.AddProject("webfrontend") +builder.AddProject("webfrontend") .WithExternalHttpEndpoints() .WithHttpHealthCheck("/health") .WithReference(apiService) diff --git a/src/aspire/AStar.Web.ServiceDefaults/Extensions.cs b/src/aspire/AStar.Web.ServiceDefaults/Extensions.cs index 18cc330..c2e04a3 100644 --- a/src/aspire/AStar.Web.ServiceDefaults/Extensions.cs +++ b/src/aspire/AStar.Web.ServiceDefaults/Extensions.cs @@ -3,7 +3,6 @@ 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; @@ -84,10 +83,7 @@ private static TBuilder AddOpenTelemetryExporters(this TBuilder builde { var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); - if (useOtlpExporter) - { - builder.Services.AddOpenTelemetry().UseOtlpExporter(); - } + 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"])) diff --git a/src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj b/src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj index b7dd5e0..98b9f47 100644 --- a/src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj +++ b/src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/modules/apis/AStar.Web.ApiService/Program.cs b/src/modules/apis/AStar.Web.ApiService/Program.cs index 2081ab8..f0c1c43 100644 --- a/src/modules/apis/AStar.Web.ApiService/Program.cs +++ b/src/modules/apis/AStar.Web.ApiService/Program.cs @@ -14,10 +14,7 @@ // Configure the HTTP request pipeline. app.UseExceptionHandler(); -if (app.Environment.IsDevelopment()) -{ - app.MapOpenApi(); -} +if (app.Environment.IsDevelopment()) app.MapOpenApi(); string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"]; @@ -42,7 +39,7 @@ app.Run(); -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } \ No newline at end of file diff --git a/src/uis/AStar.Web.Web/Components/Routes.razor b/src/uis/AStar.Web.Web/Components/Routes.razor deleted file mode 100644 index ae94e9e..0000000 --- a/src/uis/AStar.Web.Web/Components/Routes.razor +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/uis/AStar.Web.Web/AStar.Web.Web.csproj b/src/uis/AStar.Web/AStar.Web.csproj similarity index 70% rename from src/uis/AStar.Web.Web/AStar.Web.Web.csproj rename to src/uis/AStar.Web/AStar.Web.csproj index 3d93d66..da9a099 100644 --- a/src/uis/AStar.Web.Web/AStar.Web.Web.csproj +++ b/src/uis/AStar.Web/AStar.Web.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/uis/AStar.Web.Web/Components/App.razor b/src/uis/AStar.Web/Components/App.razor similarity index 87% rename from src/uis/AStar.Web.Web/Components/App.razor rename to src/uis/AStar.Web/Components/App.razor index def3138..ec9b502 100644 --- a/src/uis/AStar.Web.Web/Components/App.razor +++ b/src/uis/AStar.Web/Components/App.razor @@ -7,7 +7,7 @@ - + diff --git a/src/uis/AStar.Web.Web/Components/Layout/MainLayout.razor b/src/uis/AStar.Web/Components/Layout/MainLayout.razor similarity index 100% rename from src/uis/AStar.Web.Web/Components/Layout/MainLayout.razor rename to src/uis/AStar.Web/Components/Layout/MainLayout.razor diff --git a/src/uis/AStar.Web.Web/Components/Layout/MainLayout.razor.css b/src/uis/AStar.Web/Components/Layout/MainLayout.razor.css similarity index 72% rename from src/uis/AStar.Web.Web/Components/Layout/MainLayout.razor.css rename to src/uis/AStar.Web/Components/Layout/MainLayout.razor.css index 038baf1..d49e4b7 100644 --- a/src/uis/AStar.Web.Web/Components/Layout/MainLayout.razor.css +++ b/src/uis/AStar.Web/Components/Layout/MainLayout.razor.css @@ -21,20 +21,20 @@ main { align-items: center; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } +.top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; +} - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; +} - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } +.top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} @media (max-width: 640.98px) { .top-row { @@ -88,9 +88,9 @@ main { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/src/uis/AStar.Web.Web/Components/Layout/NavMenu.razor b/src/uis/AStar.Web/Components/Layout/NavMenu.razor similarity index 100% rename from src/uis/AStar.Web.Web/Components/Layout/NavMenu.razor rename to src/uis/AStar.Web/Components/Layout/NavMenu.razor diff --git a/src/uis/AStar.Web.Web/Components/Layout/NavMenu.razor.css b/src/uis/AStar.Web/Components/Layout/NavMenu.razor.css similarity index 86% rename from src/uis/AStar.Web.Web/Components/Layout/NavMenu.razor.css rename to src/uis/AStar.Web/Components/Layout/NavMenu.razor.css index 1338edb..03fb4b7 100644 --- a/src/uis/AStar.Web.Web/Components/Layout/NavMenu.razor.css +++ b/src/uis/AStar.Web/Components/Layout/NavMenu.razor.css @@ -17,7 +17,7 @@ .top-row { min-height: 3.5rem; - background-color: rgba(0,0,0,0.4); + background-color: rgba(0, 0, 0, 0.4); } .navbar-brand { @@ -51,30 +51,30 @@ padding-bottom: 0.5rem; } - .nav-item:first-of-type { - padding-top: 1rem; - } +.nav-item:first-of-type { + padding-top: 1rem; +} - .nav-item:last-of-type { - padding-bottom: 1rem; - } +.nav-item:last-of-type { + padding-bottom: 1rem; +} - .nav-item ::deep a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } +.nav-item ::deep a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; +} .nav-item ::deep a.active { - background-color: rgba(255,255,255,0.37); + background-color: rgba(255, 255, 255, 0.37); color: white; } .nav-item ::deep a:hover { - background-color: rgba(255,255,255,0.1); + background-color: rgba(255, 255, 255, 0.1); color: white; } diff --git a/src/uis/AStar.Web.Web/Components/Pages/Counter.razor b/src/uis/AStar.Web/Components/Pages/Counter.razor similarity index 90% rename from src/uis/AStar.Web.Web/Components/Pages/Counter.razor rename to src/uis/AStar.Web/Components/Pages/Counter.razor index 818def9..4f4fa2f 100644 --- a/src/uis/AStar.Web.Web/Components/Pages/Counter.razor +++ b/src/uis/AStar.Web/Components/Pages/Counter.razor @@ -10,7 +10,7 @@ @code { - private int currentCount = 0; + private int currentCount; private void IncrementCount() { diff --git a/src/uis/AStar.Web.Web/Components/Pages/Error.razor b/src/uis/AStar.Web/Components/Pages/Error.razor similarity index 89% rename from src/uis/AStar.Web.Web/Components/Pages/Error.razor rename to src/uis/AStar.Web/Components/Pages/Error.razor index 049dd5b..03e8c11 100644 --- a/src/uis/AStar.Web.Web/Components/Pages/Error.razor +++ b/src/uis/AStar.Web/Components/Pages/Error.razor @@ -15,12 +15,14 @@

Development Mode

- Swapping to Development environment will display more detailed information about the error that occurred. + Swapping to Development environment will display more detailed information about the error that + occurred.

The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT + environment variable to Development and restarting the app.

diff --git a/src/uis/AStar.Web.Web/Components/Pages/Home.razor b/src/uis/AStar.Web/Components/Pages/Home.razor similarity index 100% rename from src/uis/AStar.Web.Web/Components/Pages/Home.razor rename to src/uis/AStar.Web/Components/Pages/Home.razor diff --git a/src/uis/AStar.Web.Web/Components/Pages/Weather.razor b/src/uis/AStar.Web/Components/Pages/Weather.razor similarity index 96% rename from src/uis/AStar.Web.Web/Components/Pages/Weather.razor rename to src/uis/AStar.Web/Components/Pages/Weather.razor index b29a8ff..0260c81 100644 --- a/src/uis/AStar.Web.Web/Components/Pages/Weather.razor +++ b/src/uis/AStar.Web/Components/Pages/Weather.razor @@ -1,5 +1,5 @@ @page "/weather" -@attribute [StreamRendering(true)] +@attribute [StreamRendering] @attribute [OutputCache(Duration = 5)] @inject WeatherApiClient WeatherApi diff --git a/src/uis/AStar.Web/Components/Routes.razor b/src/uis/AStar.Web/Components/Routes.razor new file mode 100644 index 0000000..b7dad05 --- /dev/null +++ b/src/uis/AStar.Web/Components/Routes.razor @@ -0,0 +1,7 @@ +@using AStar.Web.Components.Layout + + + + + + \ No newline at end of file diff --git a/src/uis/AStar.Web.Web/Components/_Imports.razor b/src/uis/AStar.Web/Components/_Imports.razor similarity index 87% rename from src/uis/AStar.Web.Web/Components/_Imports.razor rename to src/uis/AStar.Web/Components/_Imports.razor index 4943de8..3740c80 100644 --- a/src/uis/AStar.Web.Web/Components/_Imports.razor +++ b/src/uis/AStar.Web/Components/_Imports.razor @@ -7,5 +7,5 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.OutputCaching @using Microsoft.JSInterop -@using AStar.Web.Web -@using AStar.Web.Web.Components \ No newline at end of file +@using AStar.Web +@using AStar.Web.Components \ No newline at end of file diff --git a/src/uis/AStar.Web.Web/Program.cs b/src/uis/AStar.Web/Program.cs similarity index 84% rename from src/uis/AStar.Web.Web/Program.cs rename to src/uis/AStar.Web/Program.cs index 626a3f3..1ac9c56 100644 --- a/src/uis/AStar.Web.Web/Program.cs +++ b/src/uis/AStar.Web/Program.cs @@ -1,5 +1,5 @@ -using AStar.Web.Web; -using AStar.Web.Web.Components; +using AStar.Web; +using AStar.Web.Components; var builder = WebApplication.CreateBuilder(args); @@ -16,14 +16,14 @@ { // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP. // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes. - client.BaseAddress = new("https+http://apiservice"); + client.BaseAddress = new Uri("https+http://apiservice"); }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { - app.UseExceptionHandler("/Error", createScopeForErrors: true); + app.UseExceptionHandler("/Error", true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } diff --git a/src/uis/AStar.Web.Web/Properties/launchSettings.json b/src/uis/AStar.Web/Properties/launchSettings.json similarity index 100% rename from src/uis/AStar.Web.Web/Properties/launchSettings.json rename to src/uis/AStar.Web/Properties/launchSettings.json diff --git a/src/uis/AStar.Web.Web/WeatherApiClient.cs b/src/uis/AStar.Web/WeatherApiClient.cs similarity index 86% rename from src/uis/AStar.Web.Web/WeatherApiClient.cs rename to src/uis/AStar.Web/WeatherApiClient.cs index 08ff50a..e2f4b0c 100644 --- a/src/uis/AStar.Web.Web/WeatherApiClient.cs +++ b/src/uis/AStar.Web/WeatherApiClient.cs @@ -1,4 +1,4 @@ -namespace AStar.Web.Web; +namespace AStar.Web; public class WeatherApiClient(HttpClient httpClient) { @@ -10,10 +10,7 @@ public async Task GetWeatherAsync(int maxItems = 10, await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable("/weatherforecast", cancellationToken)) { - if (forecasts?.Count >= maxItems) - { - break; - } + if (forecasts?.Count >= maxItems) break; if (forecast is not null) { diff --git a/src/uis/AStar.Web.Web/appsettings.json b/src/uis/AStar.Web/appsettings.json similarity index 100% rename from src/uis/AStar.Web.Web/appsettings.json rename to src/uis/AStar.Web/appsettings.json diff --git a/src/uis/AStar.Web.Web/wwwroot/app.css b/src/uis/AStar.Web/wwwroot/app.css similarity index 94% rename from src/uis/AStar.Web.Web/wwwroot/app.css rename to src/uis/AStar.Web/wwwroot/app.css index 6329f29..2424b5c 100644 --- a/src/uis/AStar.Web.Web/wwwroot/app.css +++ b/src/uis/AStar.Web/wwwroot/app.css @@ -13,7 +13,7 @@ a, .btn-link { } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } .content { @@ -42,9 +42,9 @@ h1:focus { color: white; } - .blazor-error-boundary::after { - content: "An error has occurred." - } +.blazor-error-boundary::after { + content: "An error has occurred." +} .form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--bs-secondary-color); diff --git a/src/uis/AStar.Web.Web/wwwroot/favicon.png b/src/uis/AStar.Web/wwwroot/favicon.png similarity index 100% rename from src/uis/AStar.Web.Web/wwwroot/favicon.png rename to src/uis/AStar.Web/wwwroot/favicon.png diff --git a/test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj b/test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj index ab10008..075fa69 100644 --- a/test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj +++ b/test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj @@ -26,7 +26,7 @@ - + diff --git a/test/aspire/AStar.Web.Tests/WebTests.cs b/test/aspire/AStar.Web.Tests/WebTests.cs index 9d3af24..322d684 100644 --- a/test/aspire/AStar.Web.Tests/WebTests.cs +++ b/test/aspire/AStar.Web.Tests/WebTests.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Projects; namespace AStar.Web.Tests; @@ -13,7 +14,7 @@ public async Task GetWebResourceRootReturnsOkStatusCode() var cancellationToken = new CancellationTokenSource(DefaultTimeout).Token; var appHost = - await DistributedApplicationTestingBuilder.CreateAsync(cancellationToken); + await DistributedApplicationTestingBuilder.CreateAsync(cancellationToken); appHost.Services.AddLogging(logging => { logging.SetMinimumLevel(LogLevel.Debug);