mirror of
https://github.com/coder/coder.git
synced 2026-09-22 13:10:21 +08:00
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.
121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package coderd
|
|
|
|
import (
|
|
"net/http"
|
|
"sort"
|
|
|
|
"cdr.dev/slog/v3"
|
|
"github.com/coder/coder/v2/coderd/httpapi"
|
|
"github.com/coder/coder/v2/coderd/rbac"
|
|
"github.com/coder/coder/v2/coderd/rbac/policy"
|
|
"github.com/coder/coder/v2/coderd/templatebuilder"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/examples"
|
|
)
|
|
|
|
// @Summary List template builder base templates
|
|
// @ID list-template-builder-base-templates
|
|
// @Security CoderSessionToken
|
|
// @Produce json
|
|
// @Tags TemplateBuilder
|
|
// @Success 200 {object} codersdk.TemplateBuilderBasesResponse
|
|
// @Router /api/v2/templatebuilder/bases [get]
|
|
func (api *API) templateBuilderBases(rw http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
|
|
if !api.Authorize(r, policy.ActionRead, rbac.ResourceTemplate.AnyOrganization()) {
|
|
httpapi.ResourceNotFound(rw)
|
|
return
|
|
}
|
|
|
|
exampleList, err := examples.List()
|
|
if err != nil {
|
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
Message: "Internal error listing examples.",
|
|
Detail: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
examplesByID := make(map[string]codersdk.TemplateExample, len(exampleList))
|
|
for _, ex := range exampleList {
|
|
examplesByID[ex.ID] = ex
|
|
}
|
|
|
|
bases := make([]codersdk.TemplateBuilderBase, 0, len(templatebuilder.BaseTemplateIDs()))
|
|
for _, id := range templatebuilder.BaseTemplateIDs() {
|
|
ex, ok := examplesByID[id]
|
|
if !ok {
|
|
api.Logger.Warn(ctx, "base template has no matching example",
|
|
slog.F("base_template_id", id))
|
|
continue
|
|
}
|
|
bases = append(bases, codersdk.TemplateBuilderBase{
|
|
ID: ex.ID,
|
|
Name: ex.Name,
|
|
Description: ex.Description,
|
|
Icon: ex.Icon,
|
|
OS: string(templatebuilder.BaseTemplateOS(id)),
|
|
})
|
|
}
|
|
|
|
sort.Slice(bases, func(i, j int) bool {
|
|
return bases[i].Name < bases[j].Name
|
|
})
|
|
|
|
httpapi.Write(ctx, rw, http.StatusOK, codersdk.TemplateBuilderBasesResponse{
|
|
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,
|
|
})
|
|
}
|