Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.agents/
.claude/
.codex/
.forge/
.kilo/
.serena/
.specify/
Expand Down
3 changes: 3 additions & 0 deletions specs/013-gen-endpoint-command/clarifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Clarifications

No critical ambiguities detected worth formal clarification. The specification and plan provide sufficient detail. No action needed at this stage, so you can proceed.
24 changes: 24 additions & 0 deletions specs/013-gen-endpoint-command/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Data Model: Gen Endpoint Command

## Data Entities

### 1. Endpoint Generation Arguments (`GenEndpointArgs`)

- **Fields**:
- `operation_type`: Enum string representing HTTP verb (GET, POST, PUT, DELETE)
- `name`: Target command or query name
- `feature`: Target feature folder in the Application layer
- `service`: Optional target service (if multiple exist in workspace)
- **Validation Rules**:
- MUST match existing standard HTTP verbs (Get, Post, Put, Delete).
- MUST find the file `Application/Features/{feature}/{name}.cs` that represents the request.

### 2. Generator Template Payload

- **Fields sent to Template Engine**:
- `operation_type`: (String) e.g. "Get"
- `name`: (String) e.g. "GetInventoryItem"
- `feature`: (String) e.g. "Inventory"
- `service_name`: (String) e.g. "Catalog"
- `request_type`: (String) Evaluated from the name (Command vs Query) to map HTTP return types intuitively if needed.
- `route_path`: (String) Evaluated from the feature and name (e.g. "/api/inventory/getinventoryitem").
61 changes: 61 additions & 0 deletions specs/013-gen-endpoint-command/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Implementation Plan: Gen Endpoint Command

**Branch**: `013-gen-endpoint-command` | **Date**: 2026-05-07 | **Spec**: `src/nfw/specs/013-gen-endpoint-command/spec.md`
**Input**: Feature specification from `specs/013-gen-endpoint-command/spec.md`

## Summary

Implement `nfw gen endpoint` command to generate an HTTP Minimal API endpoint for an existing MediatR command or query. The generated file should use attribute routing based on the HTTP operation type (GET/POST/PUT/DELETE) and include OpenAPI annotations, completing in under 3 seconds.

## Technical Context

**Language/Version**: Rust 1.85+ (2024 edition)
**Primary Dependencies**: clap (CLI), serde/serde_yaml (templates), NFramework templates store
**Storage**: File system (templates and generated C# code)
**Testing**: cargo test (unit, integration)
**Target Platform**: CLI binary (Linux/macOS/Windows)
**Project Type**: CLI Tool
**Performance Goals**: < 3 seconds execution time per endpoint generation
**Constraints**: Must validate C# Application layer structure before generation
**Scale/Scope**: Generates single C# file in Api layer per invocation

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

- **I. Library-First (N/A)**: Command implementation is an extension of existing CLI application codebase
- **II. CLI Interface (PASS)**: Built entirely as a CLI command via `clap`, reading args format mapping
- **III. Test-First (PASS)**: Integration tests planned covering failure validation and successful boilerplate generation
- **IV. Integration Testing (PASS)**: Tests will invoke CLI to analyze scaffolded file structures
- **V. Observability (PASS)**: Uses existing nfw CLI error handling structured tracing

## Project Structure

### Documentation (this feature)

```text
specs/013-gen-endpoint-command/
├── spec.md # Feature specification
├── plan.md # This file
├── research.md # Phase 0 output
├── data-model.md # Phase 1 output
└── quickstart.md # Phase 1 output
```

### Source Code (repository root)

```text
src/nfw/src/commands/gen/
├── mod.rs # (Update to register endpoint command)
└── endpoint.rs # (New command implementation)

src/nfw/tests/integration/
└── gen_endpoint_tests.rs # (New test file)

src/nfw-templates/src/dotnet-service/
├── template.yaml # (Update to register endpoint generator)
└── endpoint/
└── Endpoint.cs.tera # (New template)
```

**Structure Decision**: Extending existing CLI command logic. Adding `commands/gen/endpoint.rs` and the generator definition in `nfw-templates/src/dotnet-service/`.
39 changes: 39 additions & 0 deletions specs/013-gen-endpoint-command/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Quickstart: Gen Endpoint Command

## Usage

Generate an endpoint by referencing an existing feature and command/query name:

```bash
# General syntax
nfw gen endpoint <OPERATION_TYPE> <NAME> <FEATURE>

# Examples
nfw gen endpoint GET GetInventoryItem Inventory
nfw gen endpoint POST CreateProduct Catalog
nfw gen endpoint PUT UpdateOrder Orders
nfw gen endpoint DELETE DeleteUser Management
```

## Generated Artifacts

This will generate an endpoint file like `Api/Endpoints/Inventory/GetInventoryItemEndpoint.cs` containing:

```csharp
namespace Catalog.Presentation.WebApi.Endpoints.Inventory;

public static class GetInventoryItemEndpoint
{
public static void MapGetInventoryItemEndpoint(this IEndpointRouteBuilder app)
{
app.MapGet("/api/inventory/getinventoryitem", async ([AsParameters] GetInventoryItem request, ISender sender) =>
{
var result = await sender.Send(request);
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
})
.WithName("GetInventoryItem")
.WithTags("Inventory")
.Produces<object>(StatusCodes.Status200OK);
}
}
```
11 changes: 11 additions & 0 deletions specs/013-gen-endpoint-command/research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Research: Gen Endpoint Command

## Needs Clarification Resolution

All technical details are well known. No required clarification.

## Best Practices

- Standard Minimal API configurations using `Map{Verb}`.
- Include typical OpenAPI descriptors like `.WithName(...)`, `.WithTags(...)`, and `.Produces<T>(...)`.
- Ensure mapping between MediatR request and HTTP endpoint aligns cleanly.
80 changes: 80 additions & 0 deletions specs/013-gen-endpoint-command/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Feature Specification: Gen Endpoint Command

## User Scenarios & Testing

### User Story 1 - Generate Endpoint for Existing Command/Query (Priority: P1)

As a .NET developer, I want to generate a Minimal API HTTP endpoint for an existing MediatR command or query so I can easily expose application logic over HTTP without writing boilerplate routing and mapping code.

**Why this priority**: Exposing application features over HTTP is a core and frequent workflow.

**Independent Test**: Can be tested by running `nfw gen endpoint GET GetInventoryItem Inventory` in a workspace containing the specified MediatR query, and verifying that the endpoint file is created and the route is registered.

**Acceptance Scenarios**:

1. **Given** a service with an existing query `GetInventoryItem` in feature `Inventory`, **When** I run `nfw gen endpoint GET GetInventoryItem Inventory`, **Then** a GET endpoint file is created in the API layer.
2. **Given** a service with an existing command `CreateInventoryItem` in feature `Inventory`, **When** I run `nfw gen endpoint POST CreateInventoryItem Inventory`, **Then** a POST endpoint file is created in the API layer.

---

### User Story 2 - Validation of Referenced Command/Query (Priority: P1)

As a developer, I want the CLI to validate that the referenced command or query actually exists before generating the endpoint so I don't scaffold broken code that references missing application logic.

**Why this priority**: Generating code that doesn't compile due to missing references degrades the developer experience.

**Independent Test**: Can be tested by running the command for a nonexistent feature or command and asserting that it fails with a validation error.

**Acceptance Scenarios**:

1. **Given** a service without the feature `NonExistentFeature`, **When** I run `nfw gen endpoint GET GetItem NonExistentFeature`, **Then** the command fails with a "Feature not found" error.
2. **Given** a service with feature `Inventory` but missing the `UpdateItem` command, **When** I run `nfw gen endpoint PUT UpdateItem Inventory`, **Then** the command fails with a "Command/Query not found" error.

---

### Edge Cases

- **Operation Type Validation**: Specifying an invalid operation type (e.g., `PATCH` if not supported, or `FETCH`) should fail argument validation.
- **Service Selection**: If multiple services exist and no service is specified, it should prompt the user to select one.
- **Endpoint Already Exists**: If the endpoint file already exists, the command should fail or warn instead of overwriting custom code.
- **Missing API Layer**: If the target service does not have an API layer (WebAPI module), the command should fail with a helpful error suggesting `nfw add webapi`.

## Requirements

### Functional Requirements

- **FR-001**: The CLI MUST provide an `nfw gen endpoint <OPERATION_TYPE> <NAME> <FEATURE>` command to generate HTTP endpoint boilerplate.
- **FR-002**: The `<OPERATION_TYPE>` MUST support and validate standard HTTP verbs: GET, POST, PUT, DELETE.
- **FR-003**: The command MUST discover and validate that the `<NAME>` references an existing Command or Query in the target `<FEATURE>` folder.
- **FR-004**: The `<FEATURE>` MUST reference an existing feature folder in the application layer.
- **FR-005**: The generated endpoint MUST use Minimal API route definitions with proper attribute routing (e.g., `app.MapGet(...)`). The default route pattern MUST be `"/api/{feature.ToLower()}/{name.ToLower()}"`, avoiding assumptions about specific path parameters.
- **FR-006**: The generated endpoint MUST include OpenAPI/Swagger documentation annotations (e.g., `Produces`, `WithTags`, `WithName`).
- **FR-007**: The generated files MUST be placed in the API layer following project conventions (e.g., `Api/Endpoints/{Feature}/{Name}Endpoint.cs` or similar).
- **FR-008**: The command MUST complete in under 3 seconds.
- **FR-009**: The command MUST include integration tests covering the generation and validation behavior.

### Key Entities

- **Endpoint Artifacts**: Minimal API endpoint files, route registrations, and request/response mappers if necessary.
- **Application Command/Query**: The existing MediatR requests that the endpoints will map HTTP requests to.

## Success Criteria

### Measurable Outcomes

- **SC-001**: The command completes the entire generation process in under 3 seconds.
- **SC-002**: 100% of integration tests pass correctly.
- **SC-003**: A generated endpoint compiles immediately and clearly calls `ISender.Send(...)` with the mapped request.
- **SC-004**: Operation type correctly dictates the generated HTTP verb (GET -> `MapGet`, POST -> `MapPost`, etc.).

## Assumptions

- The target service has an API layer (WebAPI) using Minimal APIs (e.g., native endpoint groups).
- The Application layer uses MediatR for commands and queries, organized by Feature folders.
- Template rendering engine is available to handle endpoint file generation.

## Non-Goals

- Refactoring or altering the existing Command/Query.
- Implementing the actual mapping logic properties within the endpoint (only the boilerplate is generated).
- Supporting GraphQL or gRPC endpoints.
102 changes: 102 additions & 0 deletions specs/013-gen-endpoint-command/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Tasks: Gen Endpoint Command

**Input**: Design documents from `/specs/013-gen-endpoint-command/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, quickstart.md

**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.

## Format: `[ID] [P?] [Story] Description`

- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: Which user story this task belongs to (e.g., US1, US2)
- Include exact file paths in descriptions

## Path Conventions

- Paths shown below assume single project repository logic in `src/nfw/` and template directory `src/nfw-templates/`

---

## Phase 1: Setup & Foundational (Shared Infrastructure)

**Purpose**: Command registration, project setup

- [x] T001 Initialize endpoint generator configuration `src/nfw-templates/src/dotnet-service/endpoint/template.yaml`
- [x] T002 In `src/nfw-templates/src/dotnet-service/template.yaml`, register the new generator under `generators: endpoint: ./endpoint/`
- [x] T003 Create Tera template for the endpoint in `src/nfw-templates/src/dotnet-service/endpoint/Endpoint.cs.tera` (including Map{OperationType}, ISender, OpenAPI mapping)

**Checkpoint**: Foundation ready - template and config scaffolded natively.

---

## Phase 2: User Story 1 - Generate Endpoint for Existing Command/Query (Priority: P1) 🎯 MVP

**Goal**: As a .NET developer, I want to generate a Minimal API HTTP endpoint for an existing MediatR command or query so I can easily expose application logic over HTTP without writing boilerplate routing and mapping code.

**Independent Test**: Can be tested by running `nfw gen endpoint GET GetInventoryItem Inventory` in a workspace containing the specified MediatR query, and verifying that the endpoint file is created and the route is registered.

### Implementation for User Story 1

- [x] T004 [US1] Create command handler logic via clean architecture layout: Application layer command `GenEndpointCommand` and `GenEndpointCommandHandler`
- [x] T005 [US1] Create presentation endpoint in `src/n-framework-nfw/presentation/n-framework-nfw-cli/src/commands/gen/endpoint/` via `GenEndpointCliCommand` to capture operation bounds over GET/POST/PUT/DELETE
- [x] T006 [US1] Hook `endpoint` submodule to `src/n-framework-nfw/presentation/n-framework-nfw-cli/src/commands/gen/registration.rs` Command mapping
- [x] T007 [US1] Integration tests for happy path GET, POST, PUT, DELETE in test directories

**Checkpoint**: At this point, User Story 1 should be fully functional provided the application is structured normally.

---

## Phase 3: User Story 2 - Validation of Referenced Command/Query (Priority: P1)

**Goal**: As a developer, I want the CLI to validate that the referenced command or query actually exists before generating the endpoint so I don't scaffold broken code that references missing application logic.

**Independent Test**: Can be tested by running the command for a nonexistent feature or command and asserting that it fails with a validation error.

### Implementation for User Story 2

- [x] T008 [US2] In `GenEndpointCommandHandler`, implement validation checking that target application handles Minimal APIs
- [x] T009 [US2] In `GenEndpointCommandHandler`, implement validation checking that `Application/Features/{feature}` directory exists using Workspace utilities
- [x] T010 [US2] In `GenEndpointCommandHandler`, implement validation checking that specified command/query actually exists in the target feature directory
- [x] T011 [US2] Add integration test for nonexistent feature error case
- [x] T012 [US2] Add integration test for nonexistent command/query error case
- [x] T013 [US2] Add edge case validation to block overwriting an existing endpoint file
- [x] T014 [US2] Implement interactive service selection prompt if multiple services exist and no service is specified

**Checkpoint**: At this point, User Story 2 validation constraints will safeguard CLI users.

---

## Expected Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)**: No dependencies - can start immediately
- **Foundational (Phase 1)**: Depends on Setup completion - BLOCKS all user stories
- **User Stories (Phase 2+)**: All depend on Foundational phase completion
- Sequential in priority order (US1 → US2)

### Within Each User Story

- Arg structures before business logic
- Business logic implementation before testing

### Parallel Opportunities

- Within Phase 1: T002 and T003 can be built essentially decoupled from Rust code (T001 is a base)
- Within User Story 2: Validation features (T008, T009, T010) are sequentially independent logic chunks in the `handler` func.

---

## Implementation Strategy

### MVP First (User Story 1 Only)

1. Complete Phase 1: Setup
2. Complete Phase 2: User Story 1
3. **STOP and VALIDATE**: Test User Story 1 independently against existing templates

### Incremental Delivery

1. Verify User Story 1 MVP works
2. Add validations for User Story 2
3. Each validation block narrows down scaffold edge-case failures
Loading
Loading