-
Notifications
You must be signed in to change notification settings - Fork 0
Add repository skills for common development workflows #1061
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| --- | ||
| name: add-api-endpoint | ||
| description: Step-by-step guide for adding a new Minimal API endpoint to MenuApi following the layered model pattern (EF Entity → DBModel → ViewModel, Mapperly, repository, service, MapGroup registration). | ||
| user-invokable: true | ||
| context: inline | ||
| --- | ||
|
|
||
| # Adding a New API Endpoint | ||
|
|
||
| ## Purpose | ||
|
|
||
| Use this skill when adding any new HTTP endpoint to `MenuApi`. It enforces the layered model pattern and prevents common mistakes such as layer-mixing, missing Mapperly attributes, or unregistered DI services. | ||
|
|
||
| ## Repository facts | ||
|
|
||
| - Backend solution root: `backend/` | ||
| - API project: `backend/MenuApi/` | ||
| - DB project: `backend/MenuDB/` | ||
|
|
||
| ## The three model layers | ||
|
|
||
| Never mix these layers. Each has a distinct location and purpose: | ||
|
|
||
| | Layer | Location | Purpose | | ||
| |---|---|---| | ||
| | EF Entities | `backend/MenuDB/Data/` | Database rows (e.g. `RecipeEntity`) | | ||
| | DB Models | `backend/MenuApi/DBModel/` | Intermediate records with Vogen value objects (e.g. `DBModel.Recipe`) | | ||
| | ViewModels | `backend/MenuApi/ViewModel/` | API request/response DTOs (e.g. `ViewModel.Recipe`, `NewRecipe`) | | ||
|
|
||
| ## Steps | ||
|
|
||
| ### 1. Add ViewModel DTOs | ||
|
|
||
| Create or update files in `backend/MenuApi/ViewModel/`. Each DTO is a C# `class` (not a `record`) to support DataAnnotations such as `[Required]`. Use Vogen value objects for all typed properties (see `add-value-object` skill); never use raw `int`, `string`, or `Guid` for domain identifiers or constrained values. | ||
|
|
||
| ### 2. Add DB model records (if needed) | ||
|
|
||
| If the endpoint requires a new data shape that differs from existing DB models, create the corresponding `record` in `backend/MenuApi/DBModel/`. | ||
|
|
||
| ### 3. Update Mapperly mappings | ||
|
|
||
| Open `backend/MenuApi/MappingProfiles/ViewModelMapper.cs` and add or update `[MapProperty]` attributes for any renamed or new properties. Mapperly is source-generated and zero-reflection; missing attributes cause compile errors that `TreatWarningsAsErrors` will surface immediately. | ||
|
|
||
| ### 4. Add the repository method | ||
|
|
||
| - Declare the method signature on the interface: `backend/MenuApi/Repositories/I<Name>Repository.cs` | ||
| - Implement it in: `backend/MenuApi/Repositories/<Name>Repository.cs` | ||
| - Use `.Value` to unwrap Vogen types when writing EF queries. | ||
| - Use `TypeName.From(x)` to wrap raw values returned from EF into Vogen types. | ||
| - Add `ConfigureAwait(false)` on every `await` call. | ||
|
|
||
| ### 5. Add the service method | ||
|
|
||
| - Declare the method on the interface: `backend/MenuApi/Services/I<Name>Service.cs` | ||
| - Implement it in: `backend/MenuApi/Services/<Name>Service.cs` | ||
| - Services call repositories and apply business rules. Add `ConfigureAwait(false)` on every `await` call. | ||
|
|
||
| ### 6. Register the endpoint | ||
|
|
||
| Add the endpoint in the relevant `backend/MenuApi/Recipes/*Api.cs` file using the `MapGroup` pattern. Use `.Produces<T>(statusCode)` and `.ProducesProblem(statusCode)` to document response types — the existing endpoints do not use `.WithName()` or `.WithOpenApi()`. Example: | ||
|
|
||
| ```csharp | ||
| group.MapGet("/{id}", GetByIdAsync) | ||
| .Produces<FullRecipe>(StatusCodes.Status200OK) | ||
| .ProducesProblem(StatusCodes.Status404NotFound); | ||
| ``` | ||
|
|
||
| ### 7. Register DI services | ||
|
|
||
| Add any new repository and service registrations to `backend/MenuApi/Program.cs`: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddScoped<IRecipeRepository, RecipeRepository>(); | ||
| builder.Services.AddScoped<IRecipeService, RecipeService>(); | ||
| ``` | ||
|
|
||
| ## Validation | ||
|
|
||
| After implementing, run from `backend/`: | ||
|
|
||
| ```bash | ||
| dotnet restore MenuApi.sln | ||
| dotnet build MenuApi.sln --configuration Release --no-restore | ||
| dotnet test --project MenuApi.Tests/MenuApi.Tests.csproj --configuration Release --no-build | ||
| ``` | ||
|
|
||
| Then regenerate the OpenAPI spec and validate the frontend (see `openapi-sync` skill): | ||
|
|
||
| ```bash | ||
| cd ../ui/menu-website | ||
| pnpm run generate-openapi | ||
| pnpm run lint | ||
| pnpm run build | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| --- | ||
| name: add-value-object | ||
| description: How to create a new Vogen value object in MenuApi — covering the attribute, assembly-wide defaults, EF Core value converter registration, Swagger mapping, and repository wrapping conventions. | ||
| user-invokable: true | ||
| context: inline | ||
| --- | ||
|
|
||
| # Adding a New Vogen Value Object | ||
|
|
||
| ## Purpose | ||
|
|
||
| Use this skill when wrapping a primitive type in a Vogen value object. All domain identifiers and constrained values in `MenuApi` must be Vogen types — never raw `int`, `string`, or `Guid`. | ||
|
|
||
| ## Repository facts | ||
|
|
||
| - Value objects live in: `backend/MenuApi/ValueObjects/` | ||
| - Assembly-wide Vogen defaults: `backend/MenuApi/VogenDefaults.cs` (note: at the project root, not inside the `ValueObjects/` folder) | ||
| - The assembly-level attribute in `VogenDefaults.cs` enables EF Core value converters and Swagger schema mapping automatically for all value objects in the assembly. | ||
|
|
||
| ## Steps | ||
|
|
||
| ### 1. Create the value object file | ||
|
|
||
| Add a new file in `backend/MenuApi/ValueObjects/`, e.g. `RecipeId.cs`: | ||
|
|
||
| ```csharp | ||
| [ValueObject<int>] | ||
| public readonly partial struct RecipeId { } | ||
| ``` | ||
|
|
||
| For a string-backed value object: | ||
|
|
||
| ```csharp | ||
| [ValueObject<string>] | ||
| public readonly partial struct RecipeName { } | ||
| ``` | ||
|
|
||
| Supported backing types include `int`, `long`, `string`, `Guid`, `decimal`, and any other primitive that Vogen supports. | ||
|
|
||
| ### 2. No additional EF or Swagger registration needed | ||
|
|
||
| `VogenDefaults.cs` contains an assembly-level attribute that instructs Vogen to generate EF Core value converters and Swagger schema mappings for every value object in the assembly. No manual registration is required. | ||
|
|
||
| ### 3. Use the value object in DB models and ViewModels | ||
|
|
||
| - **DB models** (`backend/MenuApi/DBModel/`): use the Vogen type directly as the property type. | ||
| - **ViewModels** (`backend/MenuApi/ViewModel/`): use the Vogen type for request and response DTOs. | ||
| - **EF entities** (`backend/MenuDB/Data/`): store the raw primitive; EF will use the generated value converter automatically. | ||
|
|
||
| ### 4. Wrap and unwrap in repositories | ||
|
|
||
| In repository implementations, convert between the raw EF primitive and the Vogen type: | ||
|
|
||
| - **Wrap** (raw → Vogen): `RecipeId.From(entity.Id)` | ||
| - **Unwrap** (Vogen → raw): `recipeId.Value` | ||
|
|
||
| ```csharp | ||
| // Wrapping when reading from EF | ||
| var recipeId = RecipeId.From(entity.Id); | ||
|
|
||
| // Unwrapping when writing an EF query predicate | ||
| var entity = await _context.Recipes | ||
| .FirstOrDefaultAsync(r => r.Id == recipeId.Value) | ||
| .ConfigureAwait(false); | ||
| ``` | ||
|
|
||
| ### 5. Update Mapperly if needed | ||
|
|
||
| If the new value object appears in a mapping profile, open `backend/MenuApi/MappingProfiles/ViewModelMapper.cs` and add the appropriate `[MapProperty]` attribute. Mapperly handles Vogen conversions automatically when both sides use the same Vogen type, but explicit attributes are needed for renamed properties. | ||
|
|
||
| ## Validation | ||
|
|
||
| ```bash | ||
| cd backend | ||
| dotnet restore MenuApi.sln | ||
| dotnet build MenuApi.sln --configuration Release --no-restore | ||
| ``` | ||
|
|
||
| `TreatWarningsAsErrors` is enabled, so any Mapperly mapping gap or Vogen configuration error will surface as a build failure. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| --- | ||
| name: code-review-checklist | ||
| description: Structured checklist for reviewing pull requests in the Menu repository — layer separation, Vogen coverage, Mapperly attributes, async conventions, and StyleCop compliance. | ||
| user-invokable: true | ||
| context: inline | ||
| --- | ||
|
|
||
| # Code Review Checklist | ||
|
|
||
| ## Purpose | ||
|
|
||
| Use this skill when reviewing a pull request against the Menu repository. Work through each section in order and flag any item that fails. | ||
|
|
||
| ## 1. Layer separation | ||
|
|
||
| The three model layers must never be mixed: | ||
|
|
||
| - [ ] EF entities (`backend/MenuDB/Data/`) are not referenced directly from `MenuApi` controllers or services — only from repository implementations. | ||
| - [ ] DB models (`backend/MenuApi/DBModel/`) are not returned from API endpoints — only from repository methods. | ||
| - [ ] ViewModels (`backend/MenuApi/ViewModel/`) are not passed into repository methods — only into and out of service methods. | ||
| - [ ] No raw EF `DbContext` usage outside of repository implementations. | ||
|
|
||
| ## 2. Vogen value objects | ||
|
|
||
| - [ ] New domain identifiers and constrained values use Vogen types — not raw `int`, `string`, or `Guid`. | ||
| - [ ] Repository code uses `.Value` to unwrap and `TypeName.From(x)` to wrap. | ||
| - [ ] New value objects are placed in `backend/MenuApi/ValueObjects/`. | ||
|
|
||
| ## 3. Mapperly mappings | ||
|
|
||
| - [ ] Any new or renamed property on a ViewModel or DB model has a corresponding `[MapProperty]` attribute update in `backend/MenuApi/MappingProfiles/ViewModelMapper.cs`. | ||
| - [ ] The solution builds without warnings (build with `--configuration Release` to confirm, since `TreatWarningsAsErrors` is enabled). | ||
|
|
||
| ## 4. Async conventions | ||
|
|
||
| - [ ] Every `await` call in service and repository implementations has `ConfigureAwait(false)`. | ||
| - [ ] No blocking calls (`.Result`, `.Wait()`) on `Task` in service or repository code. | ||
|
|
||
| ## 5. Dependency injection | ||
|
|
||
| - [ ] New repositories and services are registered in `backend/MenuApi/Program.cs`. | ||
| - [ ] Interfaces are used for DI registrations (e.g. `IRecipeService`, not `RecipeService`). | ||
|
|
||
| ## 6. API endpoints | ||
|
|
||
| - [ ] Each new endpoint is documented with `.Produces<T>(statusCode)`, `.ProducesProblem(statusCode)`, and/or `.ProducesValidationProblem()` — do not use `.WithName(...)` or `.WithOpenApi()`. | ||
| - [ ] The generated frontend types (`src/generated/open-api/menu-api.ts`) are updated in the same commit as the endpoint change (run `pnpm generate-openapi` in `ui/menu-website/`). Note: `open-api/menu-api.json` is gitignored and should not be committed. | ||
|
|
||
| ## 7. StyleCop and code style | ||
|
|
||
| - [ ] No StyleCop suppressions added without a justifying comment. | ||
| - [ ] `using` directives are ordered per StyleCop rules. | ||
| - [ ] `public partial class Program` remains in `Program.cs` for integration test `WebApplicationFactory` compatibility. | ||
|
|
||
| ## 8. Tests | ||
|
|
||
| - [ ] New service or repository logic has corresponding unit tests in `MenuApi.Tests`. | ||
| - [ ] Unit tests use `[CustomAutoData]` (not bare `[AutoData]`) to handle Vogen specimen construction. | ||
| - [ ] Assertions use AwesomeAssertions (`.Should()`) — not xUnit raw assertions or FluentAssertions. | ||
| - [ ] New integration tests carry `[Collection("API Host Collection")]`. | ||
| - [ ] Integration test string parameters use `[StringLength(N, MinimumLength = 1)]` to avoid `varchar` overflow and `[NoAutoProperties]` to prevent constraint violations from auto-populated collections. | ||
|
|
||
| ## 9. Frontend (if applicable) | ||
|
|
||
| - [ ] No hand-written types in `src/generated/` — types are always generated by `pnpm generate-openapi`. | ||
| - [ ] New components have a co-located Storybook story (`*.stories.ts`). | ||
| - [ ] Pages and components use the service layer — never the API layer (`recipe-api.ts`) directly. | ||
| - [ ] `import type` is used for type-only imports. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| name: ef-migration | ||
| description: Canonical commands and safety rules for EF Core migration management in the Menu repository — adding, removing, and applying migrations correctly. | ||
| user-invokable: true | ||
| context: inline | ||
| --- | ||
|
|
||
| # EF Core Migration Management | ||
|
|
||
| ## Purpose | ||
|
|
||
| Use this skill when adding, removing, or reasoning about EF Core migrations in the Menu repository. The project layout requires specific flags that differ from the EF CLI defaults. | ||
|
|
||
| ## Repository facts | ||
|
|
||
| - Migrations project: `backend/MenuDB` (contains `MenuDbContext` and the `Migrations/` folder) | ||
| - Startup project: `backend/MenuApi` (required for EF tool configuration) | ||
| - Always run EF CLI commands from the **backend solution root** (`backend/`), not from a project subfolder. | ||
|
|
||
| ## Commands | ||
|
|
||
| ### Add a migration | ||
|
|
||
| ```bash | ||
| cd backend | ||
| dotnet ef migrations add <MigrationName> --project MenuDB --startup-project MenuApi | ||
| ``` | ||
|
|
||
| `<MigrationName>` should be PascalCase and describe the schema change, e.g. `AddRecipeIngredientsTable`. | ||
|
|
||
| ### Remove the most recent migration (before it is applied) | ||
|
|
||
| ```bash | ||
| cd backend | ||
| dotnet ef migrations remove --project MenuDB --startup-project MenuApi | ||
| ``` | ||
|
|
||
| Only remove a migration that has **not** been applied to any environment. If the migration is already in the database, revert it first with a new migration. | ||
|
|
||
| ### List applied and pending migrations | ||
|
|
||
| ```bash | ||
| cd backend | ||
| dotnet ef migrations list --project MenuDB --startup-project MenuApi | ||
| ``` | ||
|
|
||
| ## How migrations are applied at runtime | ||
|
|
||
| `Menu.MigrationService` is a `BackgroundService` that applies all pending EF migrations on startup and then exits. `MenuApi` is configured with `WaitForCompletion` on `Menu.MigrationService`, so the API never starts before migrations complete. | ||
|
|
||
| **Do not run `dotnet ef database update` against a shared or production database.** Let `Menu.MigrationService` manage migration application at runtime. | ||
|
|
||
| ## EF entity configuration | ||
|
|
||
| Entity mappings live in `backend/MenuDB/Data/`. Column constraints (max length, required, etc.) are configured inside `MenuDbContext.OnModelCreating`. After adding a migration, always inspect the generated migration file to confirm the SQL it will execute matches your intent before committing. | ||
|
|
||
| ## Validation | ||
|
|
||
| After adding a migration, build the solution to confirm there are no EF model snapshot conflicts: | ||
|
|
||
| ```bash | ||
| cd backend | ||
| dotnet restore MenuApi.sln | ||
| dotnet build MenuApi.sln --configuration Release --no-restore | ||
| ``` |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.