Skip to content

Commit 8d75e2d

Browse files
authored
Merge branch 'master' into dependabot/github_actions/actions/setup-dotnet-5.0.1
2 parents a23e95d + 8300420 commit 8d75e2d

File tree

3 files changed

+169
-61
lines changed

3 files changed

+169
-61
lines changed

.github/copilot-instructions.md

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,119 @@ return player; // Let caller handle null
250250
- Use route parameters for resource identification
251251
- Apply validation before processing requests
252252

253+
## 🛠️ Essential Commands & Workflows
254+
255+
### Build & Run
256+
```bash
257+
# Build the solution
258+
dotnet build
259+
260+
# Run the API (hot reload enabled)
261+
dotnet watch run --project src/Dotnet.Samples.AspNetCore.WebApi/Dotnet.Samples.AspNetCore.WebApi.csproj
262+
263+
# Access Swagger UI (Development only)
264+
# https://localhost:9000/swagger/index.html
265+
266+
# Health check endpoint
267+
# https://localhost:9000/health
268+
```
269+
270+
### Testing
271+
```bash
272+
# Run all tests
273+
dotnet test
274+
275+
# Run tests with coverage
276+
dotnet test --results-directory "coverage" --collect:"XPlat Code Coverage" --settings .runsettings
277+
278+
# Run specific test category
279+
dotnet test --filter "Category=Unit"
280+
```
281+
282+
### Database Migrations
283+
```bash
284+
# Create a new migration
285+
dotnet ef migrations add <MigrationName> --project src/Dotnet.Samples.AspNetCore.WebApi
286+
287+
# Apply migrations
288+
dotnet ef database update --project src/Dotnet.Samples.AspNetCore.WebApi
289+
290+
# Regenerate database with seed data
291+
./scripts/run-migrations-and-copy-database.sh
292+
```
293+
294+
**Important**: The `run-migrations-and-copy-database.sh` script:
295+
- Resets the placeholder database file
296+
- Runs all migrations
297+
- Copies the generated database from `bin/Debug/net8.0/Data/` to `Data/`
298+
- Requires `dotnet ef` CLI tool installed globally
299+
300+
### Docker Operations
301+
```bash
302+
# Build the image
303+
docker compose build
304+
305+
# Start the app (with persistent volume)
306+
docker compose up
307+
308+
# Stop the app (preserve data)
309+
docker compose down
310+
311+
# Reset database (removes volume)
312+
docker compose down -v
313+
```
314+
315+
**Important**: The SQLite database is stored in a Docker volume for persistence. First run copies a pre-seeded database from the image to the volume.
316+
317+
### Rate Limiting
318+
- Configured via `RateLimiter` section in `appsettings.json`
319+
- Default: 60 requests per 60 seconds (fixed window)
320+
- Queue limit: 0 (immediate rejection when limit reached)
321+
322+
## 🚨 Common Issues & Workarounds
323+
324+
### Database Path Issues
325+
- **SQLite database location**: `storage/players-sqlite3.db` relative to binary output
326+
- **Container storage**: `/storage/players-sqlite3.db` (mounted volume)
327+
- **Environment variable**: `STORAGE_PATH` can override the default path in containers
328+
329+
### Validation Patterns
330+
- **FluentValidation** runs in the validator class for input format/structure
331+
- **Business rule validation** (e.g., unique squad number check) happens in the service layer
332+
- This separation is intentional to keep validators focused on data structure, not business logic
333+
334+
### Locking & Caching
335+
- **DbContextPool** is used for performance - don't manually dispose DbContext
336+
- **IMemoryCache** is cleared on data modifications using `Remove(CacheKey_RetrieveAsync)`
337+
- Cache keys use `nameof()` for type safety
338+
339+
### Test Configuration
340+
- Test coverage excludes test projects via `.runsettings` configuration
341+
- Coverage reports merge multiple Cobertura files into one
342+
- `FluentAssertions` and `Moq` are standard testing libraries
343+
344+
## 📝 Commit Message Conventions
345+
346+
Follow **Conventional Commits** (<https://www.conventionalcommits.org/>):
347+
- `feat:` - New features
348+
- `fix:` - Bug fixes
349+
- `chore:` - Maintenance tasks
350+
- `docs:` - Documentation changes
351+
- `test:` - Test additions or modifications
352+
- `refactor:` - Code restructuring without behavior change
353+
354+
**Constraints**:
355+
- Header max length: 80 characters
356+
- Body max line length: 80 characters
357+
- Enforced via `commitlint.config.mjs` in CI/CD
358+
253359
## 🚀 Future Evolution Considerations
254360

255-
- **Database Migration**: SQLite → PostgreSQL transition path
256-
- **Authentication**: JWT Bearer token implementation ready
257-
- **API Versioning**: URL-based versioning strategy
258-
- **OpenAPI**: Comprehensive Swagger documentation
259-
- **Monitoring**: Health checks and metrics endpoints
260-
- **Containerization**: Docker multi-stage builds optimized
361+
See open issues on GitHub for planned enhancements:
362+
- **Clean Architecture Refactoring** ([#266](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/266)) - Migrate to Clean Architecture-inspired structure
363+
- **PostgreSQL Support** ([#249](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/249)) - Add PostgreSQL to Docker Compose setup
364+
- **.NET Aspire Integration** ([#256](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/256)) - Evaluate Aspire for dev-time orchestration and observability
365+
- **JWT Authentication** ([#105](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/105)) - Implement Client Credentials Flow for protected routes
366+
- **Global Exception Handling** ([#184](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/184)) - Add middleware with RFC 7807 Problem Details
367+
- **Optimistic Concurrency** ([#65](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/65)) - Handle conflicts with application-managed tokens
368+
- **Database Normalization** ([#125](https://github.com/nanotaboada/Dotnet.Samples.AspNetCore.WebApi/issues/125)) - Extract Position, Team, League into separate tables

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Dotnet.Samples.AspNetCore.WebApi.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup Label="Test dependencies">
12-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" PrivateAssets="all" />
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" PrivateAssets="all" />
1313
<PackageReference Include="Moq" Version="4.20.72" PrivateAssets="all" />
1414
<PackageReference Include="FluentAssertions" Version="8.8.0" PrivateAssets="all" />
1515
<PackageReference Include="xunit" Version="2.9.3" PrivateAssets="all" />

test/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
},
1717
"Microsoft.NET.Test.Sdk": {
1818
"type": "Direct",
19-
"requested": "[17.14.1, )",
20-
"resolved": "17.14.1",
21-
"contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==",
19+
"requested": "[18.0.1, )",
20+
"resolved": "18.0.1",
21+
"contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==",
2222
"dependencies": {
23-
"Microsoft.CodeCoverage": "17.14.1",
24-
"Microsoft.TestPlatform.TestHost": "17.14.1"
23+
"Microsoft.CodeCoverage": "18.0.1",
24+
"Microsoft.TestPlatform.TestHost": "18.0.1"
2525
}
2626
},
2727
"Moq": {
@@ -90,8 +90,8 @@
9090
},
9191
"Microsoft.CodeCoverage": {
9292
"type": "Transitive",
93-
"resolved": "17.14.1",
94-
"contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg=="
93+
"resolved": "18.0.1",
94+
"contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA=="
9595
},
9696
"Microsoft.Data.Sqlite.Core": {
9797
"type": "Transitive",
@@ -190,19 +190,19 @@
190190
},
191191
"Microsoft.Extensions.Configuration": {
192192
"type": "Transitive",
193-
"resolved": "9.0.10",
194-
"contentHash": "UAm3SLGAMlJdowbN+/xnh2UGJkdJoXVm4MsdhZ60dAMS8jteoyCx5WfIab5DKv0TCYpdhVecLJVUjEO3abs9UQ==",
193+
"resolved": "10.0.0",
194+
"contentHash": "H4SWETCh/cC5L1WtWchHR6LntGk3rDTTznZMssr4cL8IbDmMWBxY+MOGDc/ASnqNolLKPIWHWeuC1ddiL/iNPw==",
195195
"dependencies": {
196-
"Microsoft.Extensions.Configuration.Abstractions": "9.0.10",
197-
"Microsoft.Extensions.Primitives": "9.0.10"
196+
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0",
197+
"Microsoft.Extensions.Primitives": "10.0.0"
198198
}
199199
},
200200
"Microsoft.Extensions.Configuration.Abstractions": {
201201
"type": "Transitive",
202-
"resolved": "9.0.11",
203-
"contentHash": "g23//mPpMa33QdJkLujJICoCRbiLFpiQ4XbROG9JdeDI6/sM+qZPB2t5SmUWNM8GwY8dYW3NucxlZDFe8s3NAQ==",
202+
"resolved": "10.0.0",
203+
"contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==",
204204
"dependencies": {
205-
"Microsoft.Extensions.Primitives": "9.0.11"
205+
"Microsoft.Extensions.Primitives": "10.0.0"
206206
}
207207
},
208208
"Microsoft.Extensions.Configuration.Binder": {
@@ -215,26 +215,26 @@
215215
},
216216
"Microsoft.Extensions.Configuration.FileExtensions": {
217217
"type": "Transitive",
218-
"resolved": "9.0.10",
219-
"contentHash": "kYWY9VRoCKQJCLKAA4Wqn74FVnytqosF7vFq1chJ8st9mGZS6SQrkoZg7GmcpqrRRUWmWDOZI4nFdoFnxsI/Ug==",
218+
"resolved": "10.0.0",
219+
"contentHash": "LqCTyF0twrG4tyEN6PpSC5ewRBDwCBazRUfCOdRddwaQ3n2S57GDDeYOlTLcbV/V2dxSSZWg5Ofr48h6BsBmxw==",
220220
"dependencies": {
221-
"Microsoft.Extensions.Configuration": "9.0.10",
222-
"Microsoft.Extensions.Configuration.Abstractions": "9.0.10",
223-
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.10",
224-
"Microsoft.Extensions.FileProviders.Physical": "9.0.10",
225-
"Microsoft.Extensions.Primitives": "9.0.10"
221+
"Microsoft.Extensions.Configuration": "10.0.0",
222+
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0",
223+
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.0",
224+
"Microsoft.Extensions.FileProviders.Physical": "10.0.0",
225+
"Microsoft.Extensions.Primitives": "10.0.0"
226226
}
227227
},
228228
"Microsoft.Extensions.Configuration.Json": {
229229
"type": "Transitive",
230-
"resolved": "9.0.10",
231-
"contentHash": "bn+qnwuOaDelax8PUw30UTjLOuEd0lGWqUG4Z+oVr4D/gEWouCWOyvCVkyn+PWbftPlnmAmWxd4J+7ljwE8wVw==",
230+
"resolved": "10.0.0",
231+
"contentHash": "BIOPTEAZoeWbHlDT9Zudu+rpecZizFwhdIFRiyZKDml7JbayXmfTXKUt+ezifsSXfBkWDdJM10oDOxo8pufEng==",
232232
"dependencies": {
233-
"Microsoft.Extensions.Configuration": "9.0.10",
234-
"Microsoft.Extensions.Configuration.Abstractions": "9.0.10",
235-
"Microsoft.Extensions.Configuration.FileExtensions": "9.0.10",
236-
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.10",
237-
"System.Text.Json": "9.0.10"
233+
"Microsoft.Extensions.Configuration": "10.0.0",
234+
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0",
235+
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.0",
236+
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.0",
237+
"System.Text.Json": "10.0.0"
238238
}
239239
},
240240
"Microsoft.Extensions.DependencyInjection": {
@@ -271,26 +271,26 @@
271271
},
272272
"Microsoft.Extensions.FileProviders.Abstractions": {
273273
"type": "Transitive",
274-
"resolved": "9.0.10",
275-
"contentHash": "3+cLxZKUWBbpfIXLLuKcEok9C91PsV1h5xxfUsEnLSXXLNMiPDfrhpb1xajNFcejFPs9Ck/Fi3z71hYDqFBwYg==",
274+
"resolved": "10.0.0",
275+
"contentHash": "/ppSdehKk3fuXjlqCDgSOtjRK/pSHU8eWgzSHfHdwVm5BP4Dgejehkw+PtxKG2j98qTDEHDst2Y99aNsmJldmw==",
276276
"dependencies": {
277-
"Microsoft.Extensions.Primitives": "9.0.10"
277+
"Microsoft.Extensions.Primitives": "10.0.0"
278278
}
279279
},
280280
"Microsoft.Extensions.FileProviders.Physical": {
281281
"type": "Transitive",
282-
"resolved": "9.0.10",
283-
"contentHash": "Eg3YOEMpHWZzAgPD9YvGkQSv97AtG3II6maRQV/voDRORh4bRiyl0mVtT2PKnu1JoD9rJeYgjGCwRvVWMBaqgQ==",
282+
"resolved": "10.0.0",
283+
"contentHash": "UZUQ74lQMmvcprlG8w+XpxBbyRDQqfb7GAnccITw32hdkUBlmm9yNC4xl4aR9YjgV3ounZcub194sdmLSfBmPA==",
284284
"dependencies": {
285-
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.10",
286-
"Microsoft.Extensions.FileSystemGlobbing": "9.0.10",
287-
"Microsoft.Extensions.Primitives": "9.0.10"
285+
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.0",
286+
"Microsoft.Extensions.FileSystemGlobbing": "10.0.0",
287+
"Microsoft.Extensions.Primitives": "10.0.0"
288288
}
289289
},
290290
"Microsoft.Extensions.FileSystemGlobbing": {
291291
"type": "Transitive",
292-
"resolved": "9.0.10",
293-
"contentHash": "KdZAM2YMYBipVp/4tSEWPLnrocd17SL4iaXdgXjR5/nheBXbfR5QfPWYoTyh6C6IW3uKR7TRMwQr2qCvtaCTiA=="
292+
"resolved": "10.0.0",
293+
"contentHash": "5hfVl/e+bx1px2UkN+1xXhd3hu7Ui6ENItBzckFaRDQXfr+SHT/7qrCDrlQekCF/PBtEu2vtk87U2+gDEF8EhQ=="
294294
},
295295
"Microsoft.Extensions.Hosting.Abstractions": {
296296
"type": "Transitive",
@@ -334,8 +334,8 @@
334334
},
335335
"Microsoft.Extensions.Primitives": {
336336
"type": "Transitive",
337-
"resolved": "9.0.11",
338-
"contentHash": "rtUNSIhbQTv8iSBTFvtg2b/ZUkoqC9qAH9DdC2hr+xPpoZrxiCITci9UR/ELUGUGnGUrF8Xye+tGVRhCxE+4LA=="
337+
"resolved": "10.0.0",
338+
"contentHash": "inRnbpCS0nwO/RuoZIAqxQUuyjaknOOnCEZB55KSMMjRhl0RQDttSmLSGsUJN3RQ3ocf5NDLFd2mOQViHqMK5w=="
339339
},
340340
"Microsoft.OpenApi": {
341341
"type": "Transitive",
@@ -344,18 +344,18 @@
344344
},
345345
"Microsoft.TestPlatform.ObjectModel": {
346346
"type": "Transitive",
347-
"resolved": "17.14.1",
348-
"contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==",
347+
"resolved": "18.0.1",
348+
"contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==",
349349
"dependencies": {
350350
"System.Reflection.Metadata": "8.0.0"
351351
}
352352
},
353353
"Microsoft.TestPlatform.TestHost": {
354354
"type": "Transitive",
355-
"resolved": "17.14.1",
356-
"contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==",
355+
"resolved": "18.0.1",
356+
"contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==",
357357
"dependencies": {
358-
"Microsoft.TestPlatform.ObjectModel": "17.14.1",
358+
"Microsoft.TestPlatform.ObjectModel": "18.0.1",
359359
"Newtonsoft.Json": "13.0.3"
360360
}
361361
},
@@ -525,8 +525,8 @@
525525
},
526526
"System.IO.Pipelines": {
527527
"type": "Transitive",
528-
"resolved": "9.0.11",
529-
"contentHash": "NfGnevAV0r2gqtZWxa/7uCm3MNRYz1o4WRHhFahgBq46LuG2eaLwXIlPgtgaRUvf9CCrGFnuzN47MOzJUH1HKg=="
528+
"resolved": "10.0.0",
529+
"contentHash": "M1eb3nfXntaRJPrrMVM9EFS8I1bDTnt0uvUS6QP/SicZf/ZZjydMD5NiXxfmwW/uQwaMDP/yX2P+zQN1NBHChg=="
530530
},
531531
"System.Memory": {
532532
"type": "Transitive",
@@ -543,16 +543,16 @@
543543
},
544544
"System.Text.Encodings.Web": {
545545
"type": "Transitive",
546-
"resolved": "9.0.11",
547-
"contentHash": "l5L3Ov+pyD0dfK2bv6IMU2KPEyaaWnix6U0/YhgkNBGEOAgVTVlvh5ZyXWuuRlCtLnOziz+VtM5HFeqLlH2AbA=="
546+
"resolved": "10.0.0",
547+
"contentHash": "257hh1ep1Gqm1Lm0ulxf7vVBVMJuGN6EL4xSWjpi46DffXzm1058IiWsfSC06zSm7SniN+Tb5160UnXsSa8rRg=="
548548
},
549549
"System.Text.Json": {
550550
"type": "Transitive",
551-
"resolved": "9.0.11",
552-
"contentHash": "DGToqSFbBSU6pMSbZuJ+7jDvLa73rvpcYdGFqZIB3FKdCVlEAbrBJrl9PuCT6E0QbdhXjPwqalYc5lxjUqMQzw==",
551+
"resolved": "10.0.0",
552+
"contentHash": "1Dpjwq9peG/Wt5BNbrzIhTpclfOSqBWZsUO28vVr59yQlkvL5jLBWfpfzRmJ1OY+6DciaY0DUcltyzs4fuZHjw==",
553553
"dependencies": {
554-
"System.IO.Pipelines": "9.0.11",
555-
"System.Text.Encodings.Web": "9.0.11"
554+
"System.IO.Pipelines": "10.0.0",
555+
"System.Text.Encodings.Web": "10.0.0"
556556
}
557557
},
558558
"xunit.abstractions": {
@@ -603,7 +603,7 @@
603603
"FluentValidation.DependencyInjectionExtensions": "[12.1.0, )",
604604
"Microsoft.AspNetCore.OpenApi": "[8.0.22, )",
605605
"Microsoft.EntityFrameworkCore.Sqlite": "[9.0.11, )",
606-
"Microsoft.Extensions.Configuration.Json": "[9.0.10, )",
606+
"Microsoft.Extensions.Configuration.Json": "[10.0.0, )",
607607
"Serilog.AspNetCore": "[9.0.0, )",
608608
"Serilog.Settings.Configuration": "[9.0.0, )",
609609
"Serilog.Sinks.Console": "[6.1.1, )",

0 commit comments

Comments
 (0)