feat: add GET /api/v2/templatebuilder/modules endpoint (#26117)

Implement `GET /api/v2/templatebuilder/modules`, which returns the
filtered list of modules available for a given base template. Reads from
the bundled catalog via `LoadModules()` and applies OS-compatibility
filtering based on the `base` query param.

Computed variables (e.g. `agent_id`) are excluded from the API response
at the `ToSDK()` conversion boundary since they are wired automatically
by the builder. The `Computed` field is removed from the SDK type. Adds
`CompatibleWithOS()` to `ModuleManifest` for OS filtering.

Returns 400 for unknown base IDs and 404 when the template builder is
disabled.

Depends on #26116

> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
This commit is contained in:
Jeremy Ruppel
2026-06-12 17:53:48 -04:00
committed by GitHub
parent 776fbfa748
commit 9a6e348f5d
11 changed files with 664 additions and 17 deletions
+51
View File
@@ -67,3 +67,54 @@ func (api *API) templateBuilderBases(rw http.ResponseWriter, r *http.Request) {
Bases: bases,
})
}
// @Summary List template builder modules
// @ID list-template-builder-modules
// @Security CoderSessionToken
// @Produce json
// @Tags TemplateBuilder
// @Param base query string false "Base template example ID for OS-compatibility filtering"
// @Success 200 {object} codersdk.TemplateBuilderModulesResponse
// @Router /api/v2/templatebuilder/modules [get]
func (api *API) templateBuilderModules(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if !api.Authorize(r, policy.ActionRead, rbac.ResourceTemplate.AnyOrganization()) {
httpapi.ResourceNotFound(rw)
return
}
manifests, err := templatebuilder.LoadModules()
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error loading module catalog.",
Detail: err.Error(),
})
return
}
// Resolve OS filter from the base query param.
var filterOS templatebuilder.BaseOS
if base := r.URL.Query().Get("base"); base != "" {
filterOS = templatebuilder.BaseTemplateOS(base)
if filterOS == "" {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Unknown base template.",
Detail: "The \"base\" query parameter must be a valid base template ID.",
})
return
}
}
modules := make([]codersdk.TemplateBuilderModule, 0, len(manifests))
for _, m := range manifests {
if filterOS != "" && !m.CompatibleWithOS(string(filterOS)) {
continue
}
modules = append(modules, m.ToSDK())
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.TemplateBuilderModulesResponse{
Modules: modules,
})
}