# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Stadium Release Names This project uses famous football stadiums (A-Z) that hosted FIFA World Cup matches (with notable fallbacks where necessary): | Letter | Stadium Name | Location | Tag Name | | ------ | ----------- | -------- | -------- | | A | Azteca | Mexico (1970, 1986, 2026) | `azteca` | | B | Bernabéu | Spain (1982) | `bernabeu` | | C | Centenario | Uruguay (1930) | `centenario` | | D | Düsseldorf (Merkur Spiel-Arena) | Germany (2006) | `dusseldorf` | | E | Ekaterinburg Arena | Russia (2018) | `ekaterinburg` | | F | Frankfurt Waldstadion | Germany (1974, 2006) | `frankfurt` | | G | Gelsenkirchen | Germany (2006) | `gelsenkirchen` | | H | Hard Rock Stadium | USA (2026) | `hardrock` | | I | Ibn Batouta Stadium | Morocco (2030) | `ibnbatouta` | | J | Johannesburg Soccer City | South Africa (2010) | `johannesburg` | | K | Kazan Arena | Russia (2018) | `kazan` | | L | Lusail | Qatar (2022) | `lusail` | | M | Maracanã | Brazil (1950, 2014) | `maracana` | | N | Nantes Beaujoire | France (1998) | `nantes` | | O | Olympiastadion Berlin | Germany (1974, 2006) | `olympiastadion` | | P | Parc des Princes | France (1938, 1998) | `parcdesprinces` | | Q | Qatar 974 | Qatar (2022) | `qatar974` | | R | Rose Bowl | USA (1994) | `rosebowl` | | S | San Siro | Italy (1934, 1990) | `sansiro` | | T | Toronto BMO Field | Canada (2026) | `toronto` | | U | Ullevi | Sweden (1958) | `ullevi` | | V | Volgograd Arena | Russia (2018) | `volgograd` | | W | Wembley | England (1966) | `wembley` | | X | Xiamen Egret Stadium | (famous fallback) | `xiamen` | | Y | Yokohama International Stadium | Japan (2002) | `yokohama` | | Z | Zentralstadion Leipzig | Germany (1974, 2006) | `zentralstadion` | --- ## [Unreleased] ### Added - `CLAUDE.md` as the single source of truth for project guidance and AI-agent instructions (issue #505); consolidates content previously split between `CLAUDE.md` and `.github/copilot-instructions.md`. - `Invariants` section to `CLAUDE.md` documenting port, API contract, commit format, and CHANGELOG update requirements. - `Architecture Decision Records` section to `CLAUDE.md` referencing `docs/adr/` with guidance on when to load and when to create new ADRs. - Pre-commit step 6 to `CLAUDE.md`: update CLAUDE.md and create/amend the relevant ADR when a commit introduces or changes an architectural decision. - `docs/adr/` path instruction to `.coderabbit.yaml` covering `**/*.csproj` (DATABASE_PROVIDER runtime selection) and `.github/workflows/**` (explicit provider requirement in CI jobs that run migrations or integration tests). - `DATABASE_PROVIDER` environment variable (`sqlite` default, `postgres` opt-in) to select the database engine at startup (issue #249). - PostgreSQL 17 support via `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.1; migrations in `Migrations/Npgsql/` use proper PostgreSQL column types (`uuid`, `boolean`, `timestamp with time zone`). - `ProviderSpecificMigrationsAssembly` that filters the EF Core migration set to the active provider's namespace, ensuring `MigrateAsync()` applies the correct migrations for both SQLite and PostgreSQL. - `postgres` Docker Compose profile and service (`postgres:17-alpine`), started only when `DATABASE_PROVIDER=postgres` is set; the API service uses `depends_on` with `required: false` so SQLite mode incurs no dependency on the postgres service. - `.env.example` documenting `DATABASE_PROVIDER`, `DATABASE_URL`, and `POSTGRES_PASSWORD`. - `.env` added to `.gitignore`. - ADR-0014 (`docs/adr/0014-configurable-database-provider.md`) documenting the decision; supersedes ADR-0003. - ADR-0015: Use Full-Replace PUT as the Partial Update Strategy - ADR-0016: Adopt AI-Assisted Development Workflow - ADR-0017: Adopt Spec-Driven Development (SDD) ### Changed - Improved `CLAUDE.md` project guidance via the [CLAUDE.md Management plugin](https://claude.com/plugins/claude-md-management): added `Migrations/`, `Utilities/` (src) and `Integration/` (test) to the structure tree; documented `DATABASE_URL` Npgsql format, `STORAGE_PATH` SQLite path override, and exact `dotnet ef migrations add` commands for both providers; collapsed issue template bullets for conciseness; clarified `MigrateAsync()` runs automatically at startup. - `adr/` directory moved to `docs/adr/`; all references in `README.md`, `CONTRIBUTING.md`, and `.coderabbit.yaml` updated to the new path (issue #505). - `.coderabbit.yaml` controller path instruction updated: status code list corrected from `(200, 201, 400, 404, 409, 500)` to `(200, 201, 404, 409, 500)` with explicit note that FluentValidation failures must return 422 via `TypedResults.Problem(new HttpValidationProblemDetails(...))` (issue #505). - `.coderabbit.yaml` knowledge base guidelines updated from `.github/copilot-instructions.md` to `CLAUDE.md` and `docs/adr/README.md` (issue #505). - `.coderabbit.yaml` caching description in Tech Stack corrected to reflect both 10-minute sliding and 1-hour absolute expiration (issue #505). - `AddDbContextPoolWithSqlite` renamed to `AddDbContextPool` in `ServiceCollectionExtensions`; now reads `DATABASE_PROVIDER` and wires either `UseSqlite` or `UseNpgsql` accordingly. - `compose.yaml`: `api` service receives `DATABASE_PROVIDER` and `DATABASE_URL` environment variables; `postgres-data` named volume added. - `scripts/entrypoint.sh`: SQLite file-presence check is skipped when `DATABASE_PROVIDER=postgres`. - ADR-0003 status updated to "Superseded by ADR-0014". - `README.md`: added Database section documenting SQLite and PostgreSQL modes. ### Fixed - Populate `BuildTargetModel` in Npgsql seed migration designer files so Npgsql's SQL generator can resolve column types when applying `InsertData` operations. - Suppress `PendingModelChangesWarning` for the postgres provider path — hand-crafted designer files cannot replicate Npgsql-injected runtime annotations (`Relational:MaxIdentifierLength`, `UseIdentityByDefaultColumn`), causing a false-positive that aborted `MigrateAsync()` at startup. - Normalize `DATABASE_PROVIDER` to lowercase in `entrypoint.sh` via `tr` so `POSTGRES`, `Postgres`, etc. are handled consistently with `AddDbContextPool`. - Trim and normalize `DATABASE_PROVIDER` in `AddDbContextPool` before the provider switch; add explicit `sqlite`/empty case; throw `InvalidOperationException` for unrecognized values so typos no longer silently fall through to SQLite. - Move `Npgsql.EntityFrameworkCore.PostgreSQL` package from the "Development dependencies" `ItemGroup` to "Runtime dependencies". ### Removed --- ## [2.1.2 - Frankfurt] - 2026-04-26 ### Changed - Field validation failures now return `422 Unprocessable Entity` (RFC 4918) instead of `400 Bad Request`; `400 Bad Request` is now reserved for malformed requests (unparseable JSON, route/body mismatch); unsupported media types return `415 Unsupported Media Type` (RFC 9110 §15.5.16) via the `[Consumes]` attribute. Error responses follow the Problem Details format (RFC 9457). ### Removed - Remove `## How to Release` section from `CHANGELOG.md`; capitalize release codenames in `##` version headers and reference links (#468) --- ## [2.1.1 - Ekaterinburg] - 2026-04-12 ### Added - Extract `test` job from `release` in CD pipeline so tests run in isolation before any publish step; add `linux/arm64` to build platforms; add `id-token: write` and `attestations: write` permissions to `release`; set `provenance: mode=max` and attest the image digest with `actions/attest-build-provenance@v4.1.0` (`push-to-registry: true`); add `--no-merges` to the changelog `git log` command; add empty changelog guard; normalize first-release message to `"No changes (first release)"` (#465) - Add `adr/0013-testing-strategy.md` documenting the decision to implement the full test pyramid as a deliberate educational choice (#421) - Add `test/.../Integration/PlayerWebApplicationTests.cs` with 14 HTTP-layer integration tests covering all player endpoints and `/health` via `WebApplicationFactory` backed by in-memory SQLite; includes `Utilities/TestAuthHandler.cs` to bypass `[Authorize]` on `GET /players/{id:Guid}`; expose `Program` to the test project via `public partial class Program {}` in `Program.cs`; add `Microsoft.AspNetCore.Mvc.Testing` to the test project (#421) - Add `test/.../Integration/PlayerRepositoryTests.cs` with 9 integration tests covering `Repository` (`GetAllAsync`, `FindByIdAsync`, `RemoveAsync`) and `PlayerRepository` (`FindBySquadNumberAsync`, `SquadNumberExistsAsync`); all tests use `DatabaseFakes.MigrateAsync()` on in-memory SQLite and are tagged `[Trait("Category", "Integration")]` (#461) - Add `ValidateAsync_SquadNumberNegative_ReturnsValidationError` test to exercise the `GreaterThan(0)` rule with a negative value, which passes `NotEmpty()` but fails the greater-than rule (#427) - Add `ValidateAsync_FirstNameEmptyInUpdateRuleSet_ReturnsValidationError` test to verify the `"Update"` rule set enforces structural field validation (#427) - Add `adr/` directory with 12 Architecture Decision Records documenting architectural choices, technology decisions, and design trade-offs (#372) - Add ADR index and template at `adr/README.md` (#372) - Add Architecture Decisions section to `README.md` referencing the ADR index (#372) - Add ADR guidance section to `CONTRIBUTING.md` (#372) - Add ADR context loading instructions to `.github/copilot-instructions.md` (#372) ### Changed - Call `.DisableRateLimiting()` on `MapHealthChecks("/health")` to enforce the exemption from the global rate limiter at the endpoint level, ensuring health check probes are never throttled (#451) - Move `UseCors()` before `MapControllers()` in `Program.cs` to follow the standard ASP.NET Core middleware pipeline order; add an `Infrastructure` service registration section separating cross-cutting concerns (health checks, CORS, rate limiting, Swagger) from the `Controllers` section; add descriptive phrases to top-level section banners; add inline comments explaining the purpose and ordering rationale of each middleware; document the dev-only CORS policy intent in both `Program.cs` and `ServiceCollectionExtensions.AddCorsDefaultPolicy` (#451) - Replace pre-seeded `storage/players-sqlite3.db` binary blob with EF Core `MigrateAsync()` at startup: schema and seed data are now applied automatically before the first request is served; `STORAGE_PATH` env var controls the database file path (Docker volume path in production, `AppContext.BaseDirectory/storage/` locally); the committed database file, `Dockerfile` db copy step, and `scripts/run-migrations-and-copy-database.sh` have been removed (#459) - Recreate EF Core migrations using `HasData()` in `OnModelCreating`: three self-contained migrations (`InitialCreate` DDL, `SeedStarting11` DML, `SeedSubstitutes` DML) generated by EF Core with literal `InsertData` values — no migration calls application methods; `NormalizePlayerDataset` patch migration eliminated by folding corrections into seed data from the start (#459) - Replace `DatabaseFakes.CreateTable()` (placeholder schema) and `DatabaseFakes.Seed()` (manual insert bypassing migrations) with `DatabaseFakes.MigrateAsync()`, which applies the full EF Core migration chain on in-memory SQLite (#459) - Switch runtime base image from `mcr.microsoft.com/dotnet/aspnet:10.0` (Debian) to `mcr.microsoft.com/dotnet/aspnet:10.0-alpine` (before: 113.4 MB → after: 73.9 MB compressed; measured via `docker manifest inspect` and `docker save | wc -c`); replace `apt-get` with `apk` and `useradd`/`groupadd` with `adduser`/`addgroup` accordingly (#456) - Refactor `scripts/entrypoint.sh`: add `log()` helper with timestamp prefix, replace raw `echo` calls, and print API base URL on startup (#456) - Rename `ValidateAsync_SquadNumber_BelongsToPlayerBeingUpdated_ReturnsNoErrors` to `ValidateAsync_SquadNumberBelongsToPlayerBeingUpdated_ReturnsNoErrors` to align with the 3-segment naming convention for service/validator tests (#427) - Make CSharpier step in `/pre-commit` conditional (skip with a note if not installed), consistent with the Docker and CodeRabbit steps (#427) - Add "Verify tag commit is reachable from master" step to CD workflow using `git merge-base --is-ancestor` before any build or publish steps (#439) - Rename five controller test methods to normalize data-state vocabulary: `NonExisting` → `Nonexistent` for the POST 201 scenario, `NonExisting` → `Unknown` for the four 404-by-lookup scenarios (#452) - Add XML doc `` block to `PlayerFakes` documenting the three-term data-state vocabulary (`existing`, `nonexistent`, `unknown`) (#452) ### Fixed - `GET /players` now returns `200 OK` with an empty list `[]` when no players exist, instead of `404 Not Found` (#425) - AutoMapper `Player → PlayerResponseModel` profile now explicitly ignores the `Id` source member via `ForSourceMember`, making the exclusion intentional rather than implicit (#425) ### Removed --- ## [2.1.0 - Dusseldorf] - 2026-03-29 ### Added - Add SonarCloud configuration via `.sonarcloud.properties` with explicit CPD exclusions for migrations, generated code, test files, and structurally repetitive production files (#426, #435) - Add bug report issue template (`.github/ISSUE_TEMPLATE/bug_report.md`) (#426) ### Changed - Normalize player dataset: add Lo Celso (squad 27), correct Fernández/Mac Allister/Messi team data, replace random UUIDs with deterministic UUID v5 values (#435) - Align CRUD test fixtures: Lo Celso (squad 27) for Create and Delete, Messi (squad 10) for Retrieve, Damián Martínez (squad 23) for Update (#435) - Bump `codecov/codecov-action` from 5.5.2 to 5.5.3 (#423) ### Fixed - Scope `BeUniqueSquadNumber` validator to `"Create"` rule set to prevent false rejection of valid `PUT` requests (#424) --- ## [2.0.0 - Centenario] - 2026-03-18 ### Changed - Refactor error responses to use RFC 7807 Problem Details (#418) - Add `/squadNumber/` path segment to PUT and DELETE routes — **breaking change** (#418) - Extract `NotFoundTitle` constant and remove redundant null-conditional operator (#418) - Rename test methods to follow Microsoft .NET naming standard (#396) - Upgrade AutoMapper from 14.x to 16.1.1 (#414) — see Security section - Bump FluentAssertions from 8.8.0 to 8.9.0 (#417) - Bump coverlet.collector from 8.0.0 to 8.0.1 (#419) - Bump softprops/action-gh-release from 2.6.0 to 2.6.1 (#416) ### Fixed - Add squad number mismatch guard in `PutAsync` and update README (#418) - Strengthen test assertions for Problem Details responses (#418) - Fix broken 201 assertion in controller test for `Post_Players_NonExisting` (#396) - Add missing edge case tests for `UpdateAsync`, `DeleteAsync`, and `DateOfBirth` boundary validation (#396) ### Security - Sanitize player data before logging to prevent log forging - Upgrade AutoMapper from 14.x to 16.1.1 to resolve high-severity security vulnerability GHSA-rvv3-g6hj-g44x (#414) --- ## [1.1.0 - Bernabeu] - 2026-02-09 ### Changed - Upgrade to .NET 10 LTS from .NET 8 (#368) - Update Microsoft.AspNetCore.OpenApi to 10.0.0 - Update Microsoft.EntityFrameworkCore.Sqlite to 10.0.0 - Update Microsoft.EntityFrameworkCore.Design to 10.0.0 - Update Microsoft.VisualStudio.Web.CodeGeneration.Design to 10.0.0 - Update Docker images to .NET 10 SDK and runtime (now based on Ubuntu 24.04 LTS instead of Debian 12) - Update Dockerfile user creation commands for Ubuntu compatibility (`groupadd`/`useradd` instead of `adduser`) - Update CI/CD pipelines to use .NET 10 SDK - Token efficiency strategy for Copilot/AI agents with optimized instruction loading and improved token counting script (#364) - Bump Swashbuckle.AspNetCore from 10.1.0 to 10.1.2 - Bump docker/login-action from 3.6.0 to 3.7.0 - Bump softprops/action-gh-release from 2.2.0 to 2.5.0 - Bump actions/checkout from 6.0.1 to 6.0.2 --- ## [1.0.0 - Azteca] - 2026-01-22 Initial release. See [README.md](README.md) for complete feature list and documentation. --- --- [unreleased]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v2.1.2-frankfurt...HEAD [2.1.2 - Frankfurt]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v2.1.1-ekaterinburg...v2.1.2-frankfurt [2.1.1 - Ekaterinburg]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v2.1.0-dusseldorf...v2.1.1-ekaterinburg [2.1.0 - Dusseldorf]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v2.0.0-centenario...v2.1.0-dusseldorf [2.0.0 - Centenario]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v1.1.0-bernabeu...v2.0.0-centenario [1.1.0 - Bernabeu]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/compare/v1.0.0-azteca...v1.1.0-bernabeu [1.0.0 - Azteca]: https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/releases/tag/v1.0.0-azteca