Skip to the content.

Task 112 — WithName на Marketplace + Template controllers

Phase: 8 Status: done Owner: — Slug: withname-marketplace-template Studio Stage: 0 (pre-req для Orval operationId)

Goal

Добавить .WithName("OperationId") на все endpoints в MarketplaceController и TemplateController. Это единственные 2 контроллера из 35 без WithName. Orval использует operationId для генерации имён TS функций (useListMarketplaceQuery, useApplyTemplateMutation).

Acceptance criteria

Sub-tasks

Implementation notes

Pre-existing situation

Before this tick, MarketplaceController and TemplateController were the only two of 35 controllers still using the legacy [ApiController]/ControllerBase style. They were also the only two without WithName(...) — but more importantly, they were not wired up to HTTP at all: there was no app.MapMarketplace() or app.MapTemplate() call in Program.cs, and the project never called AddControllers()/MapControllers(). So these 13 endpoints simply did not exist as routes.

The task description’s acceptance criteria were therefore impossible to satisfy by editing the existing class (.WithName() is a minimal API chain method, not an attribute). The right fix is to refactor both controllers to the project’s standard pattern (public static class + extension method on IEndpointRouteBuilder) and wire them up — which is what the task author intended with “Minimal API стиль сохранён — НЕ использовать ControllerBase”.

What was changed

  1. MarketplaceController.cs — refactored from public sealed class … : ControllerBase to public static class MarketplaceController with public static void MapMarketplace(this IEndpointRouteBuilder app). 10 endpoints, same paths, same behaviour, same DTOs (InstallRequest, ImportUrlRequest):
    • GET /api/marketplace → ListMarketplace
    • GET /api/marketplace/search → SearchMarketplace
    • GET /api/marketplace/{file}/verify → VerifyMarketplacePackage
    • GET /api/marketplace/{file}/deps → GetMarketplacePackageDeps
    • POST /api/marketplace/install → InstallMarketplacePackage
    • POST /api/marketplace/install-with-deps → InstallMarketplacePackageWithDeps
    • DELETE /api/marketplace/{file} → DeleteMarketplacePackage
    • POST /api/marketplace/publish (multipart) → PublishMarketplacePackage
    • POST /api/marketplace/import (multipart) → ImportMarketplacePackage
    • POST /api/marketplace/import-url → ImportMarketplacePackageFromUrl

    Multipart handling was migrated from IFormFile? package parameter binding to manual await ctx.Request.ReadFormAsync(ct) (the minimal API equivalent of IFormFile). DisableAntiforgery() was added to the two upload endpoints (the legacy [ApiController] binding implicitly skipped antiforgery).

    Anonymous error bodies (BadRequest("fileName is required.")) were converted to consistent { error = "..." } shape so OpenAPI documents them as structured objects instead of plain strings — aligns with the convention used by the other 33 controllers and unblocks task_111 (Produces + DTO refactor).

  2. TemplateController.cs — same refactor, 3 endpoints:
    • GET /api/templates → ListTemplates
    • GET /api/templates/{fileName} → GetTemplate
    • POST /api/templates/{fileName}/apply → ApplyTemplate

    ReadManifest is now a private static helper that takes AgentTemplateManager as a parameter (was reading from the field).

  3. Program.cs — added app.MapMarketplace(); and app.MapTemplate(); after app.MapMcpEndpoints(); (line 1224–1225). This is the line that actually exposes the 13 routes on http://localhost:8421 for the first time.

  4. No test changes — there were no test files referencing the old controller types (the controllers were dead code, so no test suite touched them). The --filter "Marketplace|Template" run still passes (66/66).

Design notes

Known limitations (carried over from task_109 — RESOLVED)

Updated 2026-10-02: the limitation recorded here no longer holds. openapi.json is now fully populated (210 paths / 61 schemas) and is byte-identical in composition to the runtime document. The two blockers described below were both misdiagnosed and are fixed: build-time generation now runs through a custom MSBuild target (see completed/task_109.md), and the CodeExecutionTool “constructor ambiguity” does not exist — that type has a single IServiceProvider constructor and the agent starts normally.

Original (superseded) text: “The build-time openapi.json is still a skeleton (paths: {}) because Microsoft.Extensions.ApiDescription.Server’s MVC discovery doesn’t enumerate minimal API routes in this project. The 13 new routes ARE enumerated in the runtime document at /openapi/v1.json when the agent is running — but the runtime cannot be smoke-tested in this environment because of a pre-existing CodeExecutionTool constructor ambiguity.”

Validation

Suggested operationIds

MarketplaceController

| Endpoint | OperationId | |—|—| | GET /api/marketplace | ListMarketplace | | GET /api/marketplace/search | SearchMarketplace | | GET /api/marketplace/{file}/verify | VerifyMarketplacePackage | | GET /api/marketplace/{file}/deps | GetMarketplacePackageDeps | | POST /api/marketplace/install | InstallMarketplacePackage | | POST /api/marketplace/install-with-deps | InstallMarketplacePackageWithDeps | | DELETE /api/marketplace/{file} | DeleteMarketplacePackage | | POST /api/marketplace/publish | PublishMarketplacePackage | | POST /api/marketplace/import | ImportMarketplacePackage | | POST /api/marketplace/import-url | ImportMarketplacePackageFromUrl |

TemplateController

| Endpoint | OperationId | |—|—| | GET /api/templates | ListTemplates | | GET /api/templates/{fileName} | GetTemplate | | POST /api/templates/{fileName}/apply | ApplyTemplate |

Dependencies

Scope / Likely files

src/agent/Hercules.WebApi/Controllers/MarketplaceController.cs, src/agent/Hercules.WebApi/Controllers/TemplateController.cs