diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9e0400c5..3b66fd40 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,6 +17,33 @@ permissions: # Based from https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-net jobs: + changes: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + backend: ${{ steps.filter.outputs.backend || 'true' }} + frontend: ${{ steps.filter.outputs.frontend || 'true' }} + steps: + - name: Checkout + if: github.event_name == 'pull_request' + uses: actions/checkout@v6 + + - name: Check for file changes + if: github.event_name == 'pull_request' + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v3.0.2 + id: filter + with: + filters: | + backend: + - 'backend/**' + - '.github/workflows/main.yml' + frontend: + - 'ui/**' + - 'open-api/**' + - '.github/workflows/main.yml' + dependency-review: if: github.event_name == 'pull_request' runs-on: ubuntu-latest @@ -32,6 +59,10 @@ jobs: backend-build: name: Create OpenAPI spec + needs: changes + if: | + github.event_name != 'pull_request' || + needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest permissions: contents: read @@ -51,9 +82,11 @@ jobs: - name: Restore run: dotnet restore MenuApi.sln + working-directory: ./backend - name: Build solution (generate OpenAPI) run: dotnet build MenuApi.sln --configuration Release --no-restore + working-directory: ./backend - name: Upload OpenAPI document uses: actions/upload-artifact@v4 @@ -64,6 +97,10 @@ jobs: backend-tests: name: Backend tests + needs: changes + if: | + github.event_name != 'pull_request' || + needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest permissions: contents: read @@ -83,16 +120,23 @@ jobs: - name: Restore run: dotnet restore MenuApi.sln + working-directory: ./backend - name: Build solution (generate OpenAPI) run: dotnet build MenuApi.sln --configuration Release --no-restore + working-directory: ./backend - name: Test with the dotnet CLI run: dotnet test --project MenuApi.Tests/MenuApi.Tests.csproj --configuration Release --no-build + working-directory: ./backend backend-integration-tests: name: Backend integration tests + needs: changes + if: | + github.event_name != 'pull_request' || + needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest permissions: contents: read @@ -112,9 +156,11 @@ jobs: - name: Restore run: dotnet restore MenuApi.sln + working-directory: ./backend - name: Build solution (generate OpenAPI) run: dotnet build MenuApi.sln --configuration Release --no-restore + working-directory: ./backend - name: clean ssl cert run: dotnet dev-certs https --clean @@ -126,6 +172,7 @@ jobs: - name: Test with the dotnet CLI run: dotnet test --project MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj --configuration Release --no-build + working-directory: ./backend env: parameters__Auth0TestClientId: ${{ vars.AUTH0_CLIENT_ID }} parameters__Auth0TestClientSecret: ${{ secrets.AUTH0_CLIENT_SECRET }} @@ -135,7 +182,11 @@ jobs: frontend: name: Frontend validation runs-on: ubuntu-latest - needs: backend-build + needs: [changes, backend-build] + if: | + !cancelled() && + (github.event_name != 'pull_request' || needs.changes.outputs.frontend == 'true') && + (needs.backend-build.result == 'success' || needs.backend-build.result == 'skipped') permissions: contents: read actions: read @@ -145,6 +196,7 @@ jobs: uses: actions/checkout@v6 - name: Download OpenAPI document + if: needs.backend-build.result == 'success' uses: actions/download-artifact@v4 with: name: openapi-spec diff --git a/.vscode/settings.json b/.vscode/settings.json index a4c4bee6..bad6e73f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { - "dotnet.defaultSolution": "MenuApi.sln", + "dotnet.defaultSolution": "backend/MenuApi.sln", "sonarlint.connectedMode.project": { "connectionId": "dgee2-github", "projectKey": "dgee2_Menu" } -} \ No newline at end of file +} diff --git a/AGENTS.md b/AGENTS.md index d6f3fcb6..78a9812b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,10 +2,10 @@ ## Architecture -.NET Aspire distributed app (net10.0) for recipe/menu management. `Menu.AppHost` orchestrates all services: +.NET Aspire distributed app (net10.0) for recipe/menu management. Backend projects live under `backend/`, and `backend/Menu.AppHost` orchestrates all services: -- **MenuApi** – Minimal API (Auth0 JWT-secured). Endpoints defined via `MapGroup` extensions in `MenuApi/Recipes/`. -- **MenuDB** – EF Core `MenuDbContext` + entity definitions (`MenuDB/Data/`) + migrations (`MenuDB/Migrations/`). +- **MenuApi** – Minimal API (Auth0 JWT-secured). Endpoints defined via `MapGroup` extensions in `backend/MenuApi/Recipes/`. +- **MenuDB** – EF Core `MenuDbContext` + entity definitions (`backend/MenuDB/Data/`) + migrations (`backend/MenuDB/Migrations/`). - **Menu.MigrationService** – BackgroundService that applies EF migrations on startup, then exits. The API (`MenuApi`) waits for this to complete before starting (`WaitForCompletion`). - **Redis** – `AddRedis("cache")` resource for caching. - **Menu.ServiceDefaults / Menu.ApiServiceDefaults** – Shared Aspire service defaults (OpenTelemetry, health checks, Swagger). @@ -17,15 +17,15 @@ Three distinct model layers — never mix them: | Layer | Namespace / Location | Purpose | |---|---|---| -| **EF Entities** | `MenuDB/Data/` (e.g. `RecipeEntity`) | Database rows; configured in `MenuDbContext.OnModelCreating` | -| **DB Models** | `MenuApi/DBModel/` (e.g. `DBModel.Recipe`) | Intermediate records using Vogen value objects; returned by repositories | -| **ViewModels** | `MenuApi/ViewModel/` (e.g. `ViewModel.Recipe`, `NewRecipe`, `FullRecipe`) | API request/response DTOs | +| **EF Entities** | `backend/MenuDB/Data/` (e.g. `RecipeEntity`) | Database rows; configured in `MenuDbContext.OnModelCreating` | +| **DB Models** | `backend/MenuApi/DBModel/` (e.g. `DBModel.Recipe`) | Intermediate records using Vogen value objects; returned by repositories | +| **ViewModels** | `backend/MenuApi/ViewModel/` (e.g. `ViewModel.Recipe`, `NewRecipe`, `FullRecipe`) | API request/response DTOs | -Mapping between layers uses **Riok.Mapperly** (source-generated, zero-reflection) in `MenuApi/MappingProfiles/ViewModelMapper.cs`. When adding properties, update the `[MapProperty]` attributes there. +Mapping between layers uses **Riok.Mapperly** (source-generated, zero-reflection) in `backend/MenuApi/MappingProfiles/ViewModelMapper.cs`. When adding properties, update the `[MapProperty]` attributes there. ## Vogen Value Objects -Primitive types are wrapped with [Vogen](https://github.com/SteveDunn/Vogen) (`MenuApi/ValueObjects/`). Example: `RecipeId`, `RecipeName`, `IngredientAmount`. Assembly-wide defaults in `VogenDefaults.cs` enable EF Core value converters and Swagger mapping. When creating a new value object: +Primitive types are wrapped with [Vogen](https://github.com/SteveDunn/Vogen) (`backend/MenuApi/ValueObjects/`). Example: `RecipeId`, `RecipeName`, `IngredientAmount`. Assembly-wide defaults in `VogenDefaults.cs` enable EF Core value converters and Swagger mapping. When creating a new value object: ```csharp [ValueObject] @@ -38,9 +38,10 @@ Repositories must use `.Value` to unwrap and `TypeName.From(x)` to wrap. ```bash # Run the full stack (API + SQL container + migrations + UI) +cd backend dotnet run --project Menu.AppHost -# EF migrations (always from solution root) +# EF migrations (always from the backend solution root) dotnet ef migrations add --project MenuDB --startup-project MenuApi dotnet ef migrations remove --project MenuDB --startup-project MenuApi @@ -60,7 +61,7 @@ dotnet test MenuApi.Integration.Tests ## Code Style - `TreatWarningsAsErrors` is enabled in Debug and Release for all projects. -- StyleCop is configured via `MenuApi/stylecop.json`. +- StyleCop is configured via `backend/MenuApi/stylecop.json`. - `ConfigureAwait(false)` is used on all async calls in service/repository layers. - `Program.cs` exposes a `public partial class Program` for integration test `WebApplicationFactory` compatibility. @@ -138,10 +139,10 @@ pnpm storybook # Storybook dev server (port 6006) ## Adding a New API Endpoint -1. Add ViewModel DTOs in `MenuApi/ViewModel/`. -2. Add DB model records in `MenuApi/DBModel/` (if new data shapes are needed). -3. Add/update Mapperly mappings in `MenuApi/MappingProfiles/ViewModelMapper.cs`. -4. Add repository method (interface in `MenuApi/Repositories/I*Repository.cs`, impl in `*Repository.cs`). -5. Add service method (interface + impl in `MenuApi/Services/`). -6. Add the endpoint in the relevant `MenuApi/Recipes/*Api.cs` file using the `MapGroup` pattern. -7. Register new DI services in `MenuApi/Program.cs`. +1. Add ViewModel DTOs in `backend/MenuApi/ViewModel/`. +2. Add DB model records in `backend/MenuApi/DBModel/` (if new data shapes are needed). +3. Add/update Mapperly mappings in `backend/MenuApi/MappingProfiles/ViewModelMapper.cs`. +4. Add repository method (interface in `backend/MenuApi/Repositories/I*Repository.cs`, impl in `*Repository.cs`). +5. Add service method (interface + impl in `backend/MenuApi/Services/`). +6. Add the endpoint in the relevant `backend/MenuApi/Recipes/*Api.cs` file using the `MapGroup` pattern. +7. Register new DI services in `backend/MenuApi/Program.cs`. diff --git a/.editorconfig b/backend/.editorconfig similarity index 100% rename from .editorconfig rename to backend/.editorconfig diff --git a/Menu.ApiServiceDefaults/Extensions.cs b/backend/Menu.ApiServiceDefaults/Extensions.cs similarity index 100% rename from Menu.ApiServiceDefaults/Extensions.cs rename to backend/Menu.ApiServiceDefaults/Extensions.cs diff --git a/Menu.ApiServiceDefaults/Menu.ApiServiceDefaults.csproj b/backend/Menu.ApiServiceDefaults/Menu.ApiServiceDefaults.csproj similarity index 100% rename from Menu.ApiServiceDefaults/Menu.ApiServiceDefaults.csproj rename to backend/Menu.ApiServiceDefaults/Menu.ApiServiceDefaults.csproj diff --git a/Menu.AppHost/Menu.AppHost.csproj b/backend/Menu.AppHost/Menu.AppHost.csproj similarity index 100% rename from Menu.AppHost/Menu.AppHost.csproj rename to backend/Menu.AppHost/Menu.AppHost.csproj diff --git a/Menu.AppHost/Program.cs b/backend/Menu.AppHost/Program.cs similarity index 100% rename from Menu.AppHost/Program.cs rename to backend/Menu.AppHost/Program.cs diff --git a/Menu.AppHost/Properties/launchSettings.json b/backend/Menu.AppHost/Properties/launchSettings.json similarity index 100% rename from Menu.AppHost/Properties/launchSettings.json rename to backend/Menu.AppHost/Properties/launchSettings.json diff --git a/Menu.AppHost/appsettings.Development.json b/backend/Menu.AppHost/appsettings.Development.json similarity index 100% rename from Menu.AppHost/appsettings.Development.json rename to backend/Menu.AppHost/appsettings.Development.json diff --git a/Menu.AppHost/appsettings.json b/backend/Menu.AppHost/appsettings.json similarity index 100% rename from Menu.AppHost/appsettings.json rename to backend/Menu.AppHost/appsettings.json diff --git a/Menu.MigrationService/Menu.MigrationService.csproj b/backend/Menu.MigrationService/Menu.MigrationService.csproj similarity index 100% rename from Menu.MigrationService/Menu.MigrationService.csproj rename to backend/Menu.MigrationService/Menu.MigrationService.csproj diff --git a/Menu.MigrationService/Program.cs b/backend/Menu.MigrationService/Program.cs similarity index 100% rename from Menu.MigrationService/Program.cs rename to backend/Menu.MigrationService/Program.cs diff --git a/Menu.MigrationService/Worker.cs b/backend/Menu.MigrationService/Worker.cs similarity index 100% rename from Menu.MigrationService/Worker.cs rename to backend/Menu.MigrationService/Worker.cs diff --git a/Menu.ServiceDefaults/Extensions.cs b/backend/Menu.ServiceDefaults/Extensions.cs similarity index 100% rename from Menu.ServiceDefaults/Extensions.cs rename to backend/Menu.ServiceDefaults/Extensions.cs diff --git a/Menu.ServiceDefaults/Menu.ServiceDefaults.csproj b/backend/Menu.ServiceDefaults/Menu.ServiceDefaults.csproj similarity index 100% rename from Menu.ServiceDefaults/Menu.ServiceDefaults.csproj rename to backend/Menu.ServiceDefaults/Menu.ServiceDefaults.csproj diff --git a/MenuApi.Integration.Tests/Factory/ApiAuthentication.cs b/backend/MenuApi.Integration.Tests/Factory/ApiAuthentication.cs similarity index 100% rename from MenuApi.Integration.Tests/Factory/ApiAuthentication.cs rename to backend/MenuApi.Integration.Tests/Factory/ApiAuthentication.cs diff --git a/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs b/backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs similarity index 100% rename from MenuApi.Integration.Tests/Factory/ApiTestFixture.cs rename to backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs diff --git a/MenuApi.Integration.Tests/Factory/ShortStringAutoDataAttribute.cs b/backend/MenuApi.Integration.Tests/Factory/ShortStringAutoDataAttribute.cs similarity index 100% rename from MenuApi.Integration.Tests/Factory/ShortStringAutoDataAttribute.cs rename to backend/MenuApi.Integration.Tests/Factory/ShortStringAutoDataAttribute.cs diff --git a/MenuApi.Integration.Tests/Factory/TestParameters.cs b/backend/MenuApi.Integration.Tests/Factory/TestParameters.cs similarity index 100% rename from MenuApi.Integration.Tests/Factory/TestParameters.cs rename to backend/MenuApi.Integration.Tests/Factory/TestParameters.cs diff --git a/MenuApi.Integration.Tests/IngredientIntegrationTests.cs b/backend/MenuApi.Integration.Tests/IngredientIntegrationTests.cs similarity index 100% rename from MenuApi.Integration.Tests/IngredientIntegrationTests.cs rename to backend/MenuApi.Integration.Tests/IngredientIntegrationTests.cs diff --git a/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj b/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj similarity index 100% rename from MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj rename to backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj diff --git a/MenuApi.Integration.Tests/MenuApi.Integration.Tests.v3.ncrunchproject b/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.v3.ncrunchproject similarity index 100% rename from MenuApi.Integration.Tests/MenuApi.Integration.Tests.v3.ncrunchproject rename to backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.v3.ncrunchproject diff --git a/MenuApi.Integration.Tests/RecipeIntegrationTests.cs b/backend/MenuApi.Integration.Tests/RecipeIntegrationTests.cs similarity index 100% rename from MenuApi.Integration.Tests/RecipeIntegrationTests.cs rename to backend/MenuApi.Integration.Tests/RecipeIntegrationTests.cs diff --git a/MenuApi.Integration.Tests/RecipeWithIngredientsIntegrationTests.cs b/backend/MenuApi.Integration.Tests/RecipeWithIngredientsIntegrationTests.cs similarity index 100% rename from MenuApi.Integration.Tests/RecipeWithIngredientsIntegrationTests.cs rename to backend/MenuApi.Integration.Tests/RecipeWithIngredientsIntegrationTests.cs diff --git a/MenuApi.Integration.Tests/ValidationIntegrationTests.cs b/backend/MenuApi.Integration.Tests/ValidationIntegrationTests.cs similarity index 100% rename from MenuApi.Integration.Tests/ValidationIntegrationTests.cs rename to backend/MenuApi.Integration.Tests/ValidationIntegrationTests.cs diff --git a/MenuApi.Tests/Controllers/RecipeApiTests.cs b/backend/MenuApi.Tests/Controllers/RecipeApiTests.cs similarity index 100% rename from MenuApi.Tests/Controllers/RecipeApiTests.cs rename to backend/MenuApi.Tests/Controllers/RecipeApiTests.cs diff --git a/MenuApi.Tests/CustomAutoDataAttribute.cs b/backend/MenuApi.Tests/CustomAutoDataAttribute.cs similarity index 100% rename from MenuApi.Tests/CustomAutoDataAttribute.cs rename to backend/MenuApi.Tests/CustomAutoDataAttribute.cs diff --git a/MenuApi.Tests/CustomGenerator.cs b/backend/MenuApi.Tests/CustomGenerator.cs similarity index 100% rename from MenuApi.Tests/CustomGenerator.cs rename to backend/MenuApi.Tests/CustomGenerator.cs diff --git a/MenuApi.Tests/MenuApi.Tests.csproj b/backend/MenuApi.Tests/MenuApi.Tests.csproj similarity index 100% rename from MenuApi.Tests/MenuApi.Tests.csproj rename to backend/MenuApi.Tests/MenuApi.Tests.csproj diff --git a/MenuApi.Tests/Services/RecipeServiceTests.cs b/backend/MenuApi.Tests/Services/RecipeServiceTests.cs similarity index 100% rename from MenuApi.Tests/Services/RecipeServiceTests.cs rename to backend/MenuApi.Tests/Services/RecipeServiceTests.cs diff --git a/MenuApi.Tests/Validation/NewIngredientValidatorTests.cs b/backend/MenuApi.Tests/Validation/NewIngredientValidatorTests.cs similarity index 100% rename from MenuApi.Tests/Validation/NewIngredientValidatorTests.cs rename to backend/MenuApi.Tests/Validation/NewIngredientValidatorTests.cs diff --git a/MenuApi.Tests/Validation/NewRecipeValidatorTests.cs b/backend/MenuApi.Tests/Validation/NewRecipeValidatorTests.cs similarity index 100% rename from MenuApi.Tests/Validation/NewRecipeValidatorTests.cs rename to backend/MenuApi.Tests/Validation/NewRecipeValidatorTests.cs diff --git a/MenuApi.Tests/Validation/RecipeIngredientValidatorTests.cs b/backend/MenuApi.Tests/Validation/RecipeIngredientValidatorTests.cs similarity index 100% rename from MenuApi.Tests/Validation/RecipeIngredientValidatorTests.cs rename to backend/MenuApi.Tests/Validation/RecipeIngredientValidatorTests.cs diff --git a/MenuApi.Tests/Validation/ValidationFilterTests.cs b/backend/MenuApi.Tests/Validation/ValidationFilterTests.cs similarity index 100% rename from MenuApi.Tests/Validation/ValidationFilterTests.cs rename to backend/MenuApi.Tests/Validation/ValidationFilterTests.cs diff --git a/MenuApi.lutconfig b/backend/MenuApi.lutconfig similarity index 100% rename from MenuApi.lutconfig rename to backend/MenuApi.lutconfig diff --git a/MenuApi.sln b/backend/MenuApi.sln similarity index 100% rename from MenuApi.sln rename to backend/MenuApi.sln diff --git a/MenuApi.v3.ncrunchsolution b/backend/MenuApi.v3.ncrunchsolution similarity index 100% rename from MenuApi.v3.ncrunchsolution rename to backend/MenuApi.v3.ncrunchsolution diff --git a/MenuApi/DBModel/GetRecipeIngredient.cs b/backend/MenuApi/DBModel/GetRecipeIngredient.cs similarity index 100% rename from MenuApi/DBModel/GetRecipeIngredient.cs rename to backend/MenuApi/DBModel/GetRecipeIngredient.cs diff --git a/MenuApi/DBModel/Ingredient.cs b/backend/MenuApi/DBModel/Ingredient.cs similarity index 100% rename from MenuApi/DBModel/Ingredient.cs rename to backend/MenuApi/DBModel/Ingredient.cs diff --git a/MenuApi/DBModel/IngredientUnit.cs b/backend/MenuApi/DBModel/IngredientUnit.cs similarity index 100% rename from MenuApi/DBModel/IngredientUnit.cs rename to backend/MenuApi/DBModel/IngredientUnit.cs diff --git a/MenuApi/DBModel/Recipe.cs b/backend/MenuApi/DBModel/Recipe.cs similarity index 100% rename from MenuApi/DBModel/Recipe.cs rename to backend/MenuApi/DBModel/Recipe.cs diff --git a/MenuApi/DBModel/RecipeIngredient.cs b/backend/MenuApi/DBModel/RecipeIngredient.cs similarity index 100% rename from MenuApi/DBModel/RecipeIngredient.cs rename to backend/MenuApi/DBModel/RecipeIngredient.cs diff --git a/MenuApi/Exceptions/BusinessValidationException.cs b/backend/MenuApi/Exceptions/BusinessValidationException.cs similarity index 100% rename from MenuApi/Exceptions/BusinessValidationException.cs rename to backend/MenuApi/Exceptions/BusinessValidationException.cs diff --git a/MenuApi/Exceptions/BusinessValidationExceptionHandler.cs b/backend/MenuApi/Exceptions/BusinessValidationExceptionHandler.cs similarity index 100% rename from MenuApi/Exceptions/BusinessValidationExceptionHandler.cs rename to backend/MenuApi/Exceptions/BusinessValidationExceptionHandler.cs diff --git a/MenuApi/GlobalSuppressions.cs b/backend/MenuApi/GlobalSuppressions.cs similarity index 100% rename from MenuApi/GlobalSuppressions.cs rename to backend/MenuApi/GlobalSuppressions.cs diff --git a/MenuApi/MappingProfiles/ViewModelMapper.cs b/backend/MenuApi/MappingProfiles/ViewModelMapper.cs similarity index 100% rename from MenuApi/MappingProfiles/ViewModelMapper.cs rename to backend/MenuApi/MappingProfiles/ViewModelMapper.cs diff --git a/MenuApi/MenuApi.csproj b/backend/MenuApi/MenuApi.csproj similarity index 97% rename from MenuApi/MenuApi.csproj rename to backend/MenuApi/MenuApi.csproj index d34781c6..b3a43f89 100644 --- a/MenuApi/MenuApi.csproj +++ b/backend/MenuApi/MenuApi.csproj @@ -58,7 +58,7 @@ - ../open-api + ../../open-api --file-name menu-api diff --git a/MenuApi/Program.cs b/backend/MenuApi/Program.cs similarity index 100% rename from MenuApi/Program.cs rename to backend/MenuApi/Program.cs diff --git a/MenuApi/Properties/ServiceDependencies/local/appInsights1.arm.json b/backend/MenuApi/Properties/ServiceDependencies/local/appInsights1.arm.json similarity index 100% rename from MenuApi/Properties/ServiceDependencies/local/appInsights1.arm.json rename to backend/MenuApi/Properties/ServiceDependencies/local/appInsights1.arm.json diff --git a/MenuApi/Properties/launchSettings.json b/backend/MenuApi/Properties/launchSettings.json similarity index 100% rename from MenuApi/Properties/launchSettings.json rename to backend/MenuApi/Properties/launchSettings.json diff --git a/MenuApi/Properties/serviceDependencies.json b/backend/MenuApi/Properties/serviceDependencies.json similarity index 100% rename from MenuApi/Properties/serviceDependencies.json rename to backend/MenuApi/Properties/serviceDependencies.json diff --git a/MenuApi/Properties/serviceDependencies.local.json b/backend/MenuApi/Properties/serviceDependencies.local.json similarity index 100% rename from MenuApi/Properties/serviceDependencies.local.json rename to backend/MenuApi/Properties/serviceDependencies.local.json diff --git a/MenuApi/Recipes/IngredientApi.cs b/backend/MenuApi/Recipes/IngredientApi.cs similarity index 100% rename from MenuApi/Recipes/IngredientApi.cs rename to backend/MenuApi/Recipes/IngredientApi.cs diff --git a/MenuApi/Recipes/RecipeApi.cs b/backend/MenuApi/Recipes/RecipeApi.cs similarity index 100% rename from MenuApi/Recipes/RecipeApi.cs rename to backend/MenuApi/Recipes/RecipeApi.cs diff --git a/MenuApi/Repositories/IIngredientRepository.cs b/backend/MenuApi/Repositories/IIngredientRepository.cs similarity index 100% rename from MenuApi/Repositories/IIngredientRepository.cs rename to backend/MenuApi/Repositories/IIngredientRepository.cs diff --git a/MenuApi/Repositories/IRecipeRepository.cs b/backend/MenuApi/Repositories/IRecipeRepository.cs similarity index 100% rename from MenuApi/Repositories/IRecipeRepository.cs rename to backend/MenuApi/Repositories/IRecipeRepository.cs diff --git a/MenuApi/Repositories/IUnitRepository.cs b/backend/MenuApi/Repositories/IUnitRepository.cs similarity index 100% rename from MenuApi/Repositories/IUnitRepository.cs rename to backend/MenuApi/Repositories/IUnitRepository.cs diff --git a/MenuApi/Repositories/IngredientRepository.cs b/backend/MenuApi/Repositories/IngredientRepository.cs similarity index 100% rename from MenuApi/Repositories/IngredientRepository.cs rename to backend/MenuApi/Repositories/IngredientRepository.cs diff --git a/MenuApi/Repositories/RecipeRepository.cs b/backend/MenuApi/Repositories/RecipeRepository.cs similarity index 100% rename from MenuApi/Repositories/RecipeRepository.cs rename to backend/MenuApi/Repositories/RecipeRepository.cs diff --git a/MenuApi/Repositories/UnitRepository.cs b/backend/MenuApi/Repositories/UnitRepository.cs similarity index 100% rename from MenuApi/Repositories/UnitRepository.cs rename to backend/MenuApi/Repositories/UnitRepository.cs diff --git a/MenuApi/Services/IIngredientService.cs b/backend/MenuApi/Services/IIngredientService.cs similarity index 100% rename from MenuApi/Services/IIngredientService.cs rename to backend/MenuApi/Services/IIngredientService.cs diff --git a/MenuApi/Services/IRecipeService.cs b/backend/MenuApi/Services/IRecipeService.cs similarity index 100% rename from MenuApi/Services/IRecipeService.cs rename to backend/MenuApi/Services/IRecipeService.cs diff --git a/MenuApi/Services/IngredientService.cs b/backend/MenuApi/Services/IngredientService.cs similarity index 100% rename from MenuApi/Services/IngredientService.cs rename to backend/MenuApi/Services/IngredientService.cs diff --git a/MenuApi/Services/RecipeService.cs b/backend/MenuApi/Services/RecipeService.cs similarity index 100% rename from MenuApi/Services/RecipeService.cs rename to backend/MenuApi/Services/RecipeService.cs diff --git a/MenuApi/Validation/NewIngredientValidator.cs b/backend/MenuApi/Validation/NewIngredientValidator.cs similarity index 100% rename from MenuApi/Validation/NewIngredientValidator.cs rename to backend/MenuApi/Validation/NewIngredientValidator.cs diff --git a/MenuApi/Validation/NewRecipeValidator.cs b/backend/MenuApi/Validation/NewRecipeValidator.cs similarity index 100% rename from MenuApi/Validation/NewRecipeValidator.cs rename to backend/MenuApi/Validation/NewRecipeValidator.cs diff --git a/MenuApi/Validation/RecipeIngredientValidator.cs b/backend/MenuApi/Validation/RecipeIngredientValidator.cs similarity index 100% rename from MenuApi/Validation/RecipeIngredientValidator.cs rename to backend/MenuApi/Validation/RecipeIngredientValidator.cs diff --git a/MenuApi/Validation/ValidationFilter.cs b/backend/MenuApi/Validation/ValidationFilter.cs similarity index 100% rename from MenuApi/Validation/ValidationFilter.cs rename to backend/MenuApi/Validation/ValidationFilter.cs diff --git a/MenuApi/Validation/VogenValidationRules.cs b/backend/MenuApi/Validation/VogenValidationRules.cs similarity index 100% rename from MenuApi/Validation/VogenValidationRules.cs rename to backend/MenuApi/Validation/VogenValidationRules.cs diff --git a/MenuApi/ValueObjects/Ingredient.cs b/backend/MenuApi/ValueObjects/Ingredient.cs similarity index 100% rename from MenuApi/ValueObjects/Ingredient.cs rename to backend/MenuApi/ValueObjects/Ingredient.cs diff --git a/MenuApi/ValueObjects/IngredientUnit.cs b/backend/MenuApi/ValueObjects/IngredientUnit.cs similarity index 100% rename from MenuApi/ValueObjects/IngredientUnit.cs rename to backend/MenuApi/ValueObjects/IngredientUnit.cs diff --git a/MenuApi/ValueObjects/Recipe.cs b/backend/MenuApi/ValueObjects/Recipe.cs similarity index 100% rename from MenuApi/ValueObjects/Recipe.cs rename to backend/MenuApi/ValueObjects/Recipe.cs diff --git a/MenuApi/ValueObjects/ValueObject.cs b/backend/MenuApi/ValueObjects/ValueObject.cs similarity index 100% rename from MenuApi/ValueObjects/ValueObject.cs rename to backend/MenuApi/ValueObjects/ValueObject.cs diff --git a/MenuApi/ViewModel/FullRecipe.cs b/backend/MenuApi/ViewModel/FullRecipe.cs similarity index 100% rename from MenuApi/ViewModel/FullRecipe.cs rename to backend/MenuApi/ViewModel/FullRecipe.cs diff --git a/MenuApi/ViewModel/Ingredient.cs b/backend/MenuApi/ViewModel/Ingredient.cs similarity index 100% rename from MenuApi/ViewModel/Ingredient.cs rename to backend/MenuApi/ViewModel/Ingredient.cs diff --git a/MenuApi/ViewModel/IngredientUnit.cs b/backend/MenuApi/ViewModel/IngredientUnit.cs similarity index 100% rename from MenuApi/ViewModel/IngredientUnit.cs rename to backend/MenuApi/ViewModel/IngredientUnit.cs diff --git a/MenuApi/ViewModel/NewIngredient.cs b/backend/MenuApi/ViewModel/NewIngredient.cs similarity index 100% rename from MenuApi/ViewModel/NewIngredient.cs rename to backend/MenuApi/ViewModel/NewIngredient.cs diff --git a/MenuApi/ViewModel/NewRecipe.cs b/backend/MenuApi/ViewModel/NewRecipe.cs similarity index 100% rename from MenuApi/ViewModel/NewRecipe.cs rename to backend/MenuApi/ViewModel/NewRecipe.cs diff --git a/MenuApi/ViewModel/Recipe.cs b/backend/MenuApi/ViewModel/Recipe.cs similarity index 100% rename from MenuApi/ViewModel/Recipe.cs rename to backend/MenuApi/ViewModel/Recipe.cs diff --git a/MenuApi/ViewModel/RecipeIngredient.cs b/backend/MenuApi/ViewModel/RecipeIngredient.cs similarity index 100% rename from MenuApi/ViewModel/RecipeIngredient.cs rename to backend/MenuApi/ViewModel/RecipeIngredient.cs diff --git a/MenuApi/VogenDefaults.cs b/backend/MenuApi/VogenDefaults.cs similarity index 100% rename from MenuApi/VogenDefaults.cs rename to backend/MenuApi/VogenDefaults.cs diff --git a/MenuApi/appsettings.Development.json b/backend/MenuApi/appsettings.Development.json similarity index 100% rename from MenuApi/appsettings.Development.json rename to backend/MenuApi/appsettings.Development.json diff --git a/MenuApi/appsettings.json b/backend/MenuApi/appsettings.json similarity index 100% rename from MenuApi/appsettings.json rename to backend/MenuApi/appsettings.json diff --git a/MenuApi/libman.json b/backend/MenuApi/libman.json similarity index 100% rename from MenuApi/libman.json rename to backend/MenuApi/libman.json diff --git a/MenuApi/stylecop.json b/backend/MenuApi/stylecop.json similarity index 100% rename from MenuApi/stylecop.json rename to backend/MenuApi/stylecop.json diff --git a/MenuDB/Data/IngredientEntity.cs b/backend/MenuDB/Data/IngredientEntity.cs similarity index 100% rename from MenuDB/Data/IngredientEntity.cs rename to backend/MenuDB/Data/IngredientEntity.cs diff --git a/MenuDB/Data/IngredientUnitEntity.cs b/backend/MenuDB/Data/IngredientUnitEntity.cs similarity index 100% rename from MenuDB/Data/IngredientUnitEntity.cs rename to backend/MenuDB/Data/IngredientUnitEntity.cs diff --git a/MenuDB/Data/RecipeEntity.cs b/backend/MenuDB/Data/RecipeEntity.cs similarity index 100% rename from MenuDB/Data/RecipeEntity.cs rename to backend/MenuDB/Data/RecipeEntity.cs diff --git a/MenuDB/Data/RecipeIngredientEntity.cs b/backend/MenuDB/Data/RecipeIngredientEntity.cs similarity index 100% rename from MenuDB/Data/RecipeIngredientEntity.cs rename to backend/MenuDB/Data/RecipeIngredientEntity.cs diff --git a/MenuDB/Data/UnitEntity.cs b/backend/MenuDB/Data/UnitEntity.cs similarity index 100% rename from MenuDB/Data/UnitEntity.cs rename to backend/MenuDB/Data/UnitEntity.cs diff --git a/MenuDB/Data/UnitTypeEntity.cs b/backend/MenuDB/Data/UnitTypeEntity.cs similarity index 100% rename from MenuDB/Data/UnitTypeEntity.cs rename to backend/MenuDB/Data/UnitTypeEntity.cs diff --git a/MenuDB/MenuDB.csproj b/backend/MenuDB/MenuDB.csproj similarity index 100% rename from MenuDB/MenuDB.csproj rename to backend/MenuDB/MenuDB.csproj diff --git a/MenuDB/MenuDbContext.cs b/backend/MenuDB/MenuDbContext.cs similarity index 100% rename from MenuDB/MenuDbContext.cs rename to backend/MenuDB/MenuDbContext.cs diff --git a/MenuDB/MenuDbContextFactory.cs b/backend/MenuDB/MenuDbContextFactory.cs similarity index 100% rename from MenuDB/MenuDbContextFactory.cs rename to backend/MenuDB/MenuDbContextFactory.cs diff --git a/MenuDB/Migrations/20260307231933_InitialCreate.Designer.cs b/backend/MenuDB/Migrations/20260307231933_InitialCreate.Designer.cs similarity index 100% rename from MenuDB/Migrations/20260307231933_InitialCreate.Designer.cs rename to backend/MenuDB/Migrations/20260307231933_InitialCreate.Designer.cs diff --git a/MenuDB/Migrations/20260307231933_InitialCreate.cs b/backend/MenuDB/Migrations/20260307231933_InitialCreate.cs similarity index 100% rename from MenuDB/Migrations/20260307231933_InitialCreate.cs rename to backend/MenuDB/Migrations/20260307231933_InitialCreate.cs diff --git a/MenuDB/Migrations/MenuDbContextModelSnapshot.cs b/backend/MenuDB/Migrations/MenuDbContextModelSnapshot.cs similarity index 100% rename from MenuDB/Migrations/MenuDbContextModelSnapshot.cs rename to backend/MenuDB/Migrations/MenuDbContextModelSnapshot.cs diff --git a/aspire.config.json b/backend/aspire.config.json similarity index 100% rename from aspire.config.json rename to backend/aspire.config.json diff --git a/global.json b/backend/global.json similarity index 100% rename from global.json rename to backend/global.json diff --git a/docs/ci-path-filters.md b/docs/ci-path-filters.md new file mode 100644 index 00000000..51d48550 --- /dev/null +++ b/docs/ci-path-filters.md @@ -0,0 +1,163 @@ +# CI Path Filters + +## Overview + +The CI workflow uses conservative path filters to skip unaffected work while ensuring no required validation is missed for cross-stack changes. + +## Implementation + +The workflow uses the [dorny/paths-filter](https://github.com/dorny/paths-filter) action to detect which files have changed in pull requests and conditionally runs jobs based on those changes. + +### Path Filter Configuration + +#### Backend Jobs + +Backend jobs (`backend-build`, `backend-tests`, `backend-integration-tests`) run when any of these paths change: + +- `backend/**` - All backend projects, solution files, and backend-specific configuration grouped under the `backend/` directory +- `.github/workflows/main.yml` - The workflow file itself + +#### Frontend Job + +The frontend job (`frontend`) runs when any of these files change: + +- `ui/**` - Any file in the frontend directory +- `open-api/**` - OpenAPI specification files (triggers type regeneration) +- `.github/workflows/main.yml` - The workflow file itself + +## Behavior by Event Type + +### Pull Requests + +- **Changes detected**: Only jobs matching the changed files run +- **No changes detected**: Backend/frontend build and test jobs are skipped when no relevant files change +- **Workflow file changes**: Both frontend and backend jobs run (conservative approach) + +### Push Events (main/master branches) + +- **All jobs always run**: Path filters are only applied to pull requests +- This ensures complete validation on the main branches + +### Workflow Dispatch + +- **All jobs always run**: Manual triggers run complete validation + +## Cross-Stack Scenarios + +### Scenario: Backend-only changes (e.g., only files under `backend/`) + +- ✅ Backend jobs run +- ❌ Frontend job is skipped +- The checked-in OpenAPI spec in the repository remains unchanged + +### Scenario: Frontend-only changes (e.g., only `ui/` files) + +- ❌ Backend jobs are skipped +- ✅ Frontend job runs +- The frontend uses the existing OpenAPI spec from the repository + +### Scenario: OpenAPI contract changes + +- ❌ Backend jobs are skipped (OpenAPI is generated by backend build) +- ✅ Frontend job runs to regenerate types +- **Note**: If the OpenAPI spec is out of sync with the backend, both backend and frontend files should be changed together + +### Scenario: Both backend and frontend changes + +- ✅ Backend jobs run +- ✅ Frontend job runs +- Normal full validation + +### Scenario: Workflow file changes + +- ✅ Backend jobs run +- ✅ Frontend job runs +- Conservative approach to ensure workflow changes don't break validation + +## Frontend Job Dependencies + +The frontend job has special dependency handling: + +```yaml +needs: [changes, backend-build] +if: | + !cancelled() && + (github.event_name != 'pull_request' || needs.changes.outputs.frontend == 'true') && + (needs.backend-build.result == 'success' || needs.backend-build.result == 'skipped') +``` + +This allows the frontend to run even when backend-build is skipped (frontend-only changes), while ensuring it waits for backend-build when it does run. + +### OpenAPI Artifact Handling + +The "Download OpenAPI document" step in the frontend job is conditional: + +```yaml +- name: Download OpenAPI document + if: needs.backend-build.result == 'success' + uses: actions/download-artifact@v4 +``` + +- If backend-build runs and succeeds, the new OpenAPI spec is downloaded +- If backend-build is skipped, the existing OpenAPI spec from the repository is used + +## Performance Benefits + +Path filters improve CI throughput by: + +1. **Reducing build time**: Backend builds (~2-3 minutes) are skipped for frontend-only PRs +2. **Reducing test time**: Backend tests (~1-2 minutes) are skipped for frontend-only PRs +3. **Reducing runner usage**: Skipped jobs don't consume GitHub Actions runner minutes + +### Example Savings + +- **Frontend-only PR**: Saves ~5-7 minutes (skips 3 backend jobs) +- **Backend-only PR**: Saves ~2-3 minutes (skips 1 frontend job) +- **Documentation-only PR**: Saves ~7-10 minutes (skips all build/test jobs, runs only `changes` and `dependency-review`) + +## Guardrails + +The implementation includes several guardrails to prevent missing validation: + +1. **Conservative structure**: Backend projects and backend-only config files live under `backend/`, so one path captures solution, project, and analyzer changes together +2. **Workflow file triggers both**: Changes to the workflow file trigger all jobs +3. **Always run on push**: All jobs run on pushes to main/master branches +4. **Dependency review still runs**: The dependency-review job always runs on PRs regardless of changed files + +## Testing Path Filters + +To test the path filters work correctly: + +1. Create a PR with only backend changes (e.g., modify a `.cs` file) + - Verify backend jobs run and frontend job is skipped + +2. Create a PR with only frontend changes (e.g., modify a `.vue` file) + - Verify frontend job runs and backend jobs are skipped + +3. Create a PR with both backend and frontend changes + - Verify all jobs run + +4. Create a PR with only documentation changes (e.g., modify a `.md` file) + - Verify the `changes` job runs, `dependency-review` runs, and backend/frontend jobs are skipped + +## Troubleshooting + +### Jobs unexpectedly skipped + +- Check if the changed files match the path patterns +- Verify the PR is against the correct base branch +- Check if this is a push event (push events always run all jobs) + +### Jobs not being skipped + +- Ensure the event is a pull_request (not push) +- Check if the workflow file was changed (triggers all jobs) +- Verify the paths-filter action is running correctly in the changes job + +### Frontend fails because OpenAPI spec is missing + +This can happen if: +- The backend changed but wasn't included in the PR +- The OpenAPI spec in the repo is out of sync + +**Solution**: Include both backend and frontend changes in the same PR when the OpenAPI contract changes. diff --git a/docs/database-migrations.md b/docs/database-migrations.md index 41b91b33..5ce5e6e4 100644 --- a/docs/database-migrations.md +++ b/docs/database-migrations.md @@ -1,6 +1,6 @@ # Database Migrations -This project uses **Entity Framework Core** with SQL Server to manage the database schema for `MenuDbContext`. Migrations are stored under `MenuDB/Migrations/` and are applied automatically at startup when running inside .NET Aspire. +This project uses **Entity Framework Core** with SQL Server to manage the database schema for `MenuDbContext`. Migrations are stored under `backend/MenuDB/Migrations/` and are applied automatically at startup when running inside .NET Aspire. --- @@ -30,9 +30,9 @@ dotnet ef --version ## Creating a New Migration -All `dotnet ef` commands must be run from the **solution root** (`C:\git\Menu\`) and target the `MenuDB` project, which contains `MenuDbContext` and `MenuDbContextFactory`. `MenuApi` is used as the startup project to provide the runtime host. +All `dotnet ef` commands must be run from the **backend solution root** (`C:\git\Menu\backend\`) and target the `MenuDB` project, which contains `MenuDbContext` and `MenuDbContextFactory`. `MenuApi` is used as the startup project to provide the runtime host. -After modifying any entity in `MenuDB/Data/` or the model configuration in `MenuDB/MenuDbContext.cs`, create a new migration: +After modifying any entity in `backend/MenuDB/Data/` or the model configuration in `backend/MenuDB/MenuDbContext.cs`, create a new migration: ```bash dotnet ef migrations add --project MenuDB --startup-project MenuApi @@ -83,7 +83,7 @@ Migrations are applied by the dedicated **`Menu.MigrationService`** worker, not 2. `Menu.MigrationService` runs — applies all pending migrations, then exits 3. `MenuApi` starts — guaranteed to find the schema already up to date -This is enforced in `Menu.AppHost/Program.cs`: +This is enforced in `backend/Menu.AppHost/Program.cs`: ```csharp var menuDb = sql.AddDatabase("menu"); diff --git a/docs/integration-tests.md b/docs/integration-tests.md index 6b1c111e..5d4ddeca 100644 --- a/docs/integration-tests.md +++ b/docs/integration-tests.md @@ -100,7 +100,7 @@ The Aspire host starts a SQL Server container. Docker Desktop (or a compatible c The test project requires Auth0 credentials to obtain access tokens. Set them via user secrets: ```bash -cd MenuApi.Integration.Tests +cd backend/MenuApi.Integration.Tests dotnet user-secrets set "Parameters:Auth0TestClientId" "" dotnet user-secrets set "Parameters:Auth0TestClientSecret" "" dotnet user-secrets set "Parameters:Auth0Audience" "" @@ -109,15 +109,15 @@ dotnet user-secrets set "Parameters:Auth0Domain" "" The user secrets ID for the integration test project is `ea3aa4c7-9b32-4485-a5b2-fb1cb0def863`. -### 3. .NET 9 SDK +### 3. .NET 10 SDK -The solution targets `net9.0`. Ensure the .NET 9 SDK is installed. +The solution targets `net10.0`. Ensure the .NET 10 SDK is installed. --- ## How to Run the Tests -From the solution root: +From the backend solution root (`C:\git\Menu\backend\`): ```bash dotnet test MenuApi.Integration.Tests @@ -139,7 +139,7 @@ Follow these steps when adding integration tests for a new or existing endpoint. ### 1. Create the Test Class -Add a new `.cs` file in the `MenuApi.Integration.Tests` project root. Apply the collection attribute and inject the fixture: +Add a new `.cs` file in `backend/MenuApi.Integration.Tests`. Apply the collection attribute and inject the fixture: ```csharp using AwesomeAssertions; diff --git a/docs/specs/api-validation.md b/docs/specs/api-validation.md index 0b534b5d..c28bec88 100644 --- a/docs/specs/api-validation.md +++ b/docs/specs/api-validation.md @@ -301,7 +301,7 @@ group.MapPost("/", CreateRecipeAsync) New/modified files: ``` -MenuApi/ +backend/MenuApi/ ├── Validation/ │ ├── ValidationFilter.cs # Generic endpoint filter │ ├── NewRecipeValidator.cs # FluentValidation rules for NewRecipe