mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(coderd): add lookup task by name in httpmw.TaskParam (#20647)
* Adds a `GetTaskByOwnerIDAndName` query * Updates `httpmw.TaskParam` to fall back to task name if no task by UUID found. * Updates the `TaskByIdentifier` used in `cli/` to use direct lookup instead of searching.
This commit is contained in:
@@ -2,8 +2,14 @@ package httpmw
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
@@ -23,16 +29,34 @@ func TaskParam(r *http.Request) database.Task {
|
||||
return task
|
||||
}
|
||||
|
||||
// ExtractTaskParam grabs a task from the "task" URL parameter by UUID.
|
||||
// ExtractTaskParam grabs a task from the "task" URL parameter.
|
||||
// It supports two lookup strategies:
|
||||
// 1. Task UUID (primary)
|
||||
// 2. Task name scoped to owner (secondary)
|
||||
//
|
||||
// This middleware depends on ExtractOrganizationMembersParam being in the chain
|
||||
// to provide the owner context for name-based lookups.
|
||||
func ExtractTaskParam(db database.Store) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
taskID, parsed := ParseUUIDParam(rw, r, "task")
|
||||
if !parsed {
|
||||
|
||||
// Get the task parameter value. We can't use ParseUUIDParam here because
|
||||
// we need to support non-UUID values (task names) and
|
||||
// attempt all lookup strategies.
|
||||
taskParam := chi.URLParam(r, "task")
|
||||
if taskParam == "" {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "\"task\" must be provided.",
|
||||
})
|
||||
return
|
||||
}
|
||||
task, err := db.GetTaskByID(ctx, taskID)
|
||||
|
||||
// Get owner from OrganizationMembersParam middleware for name-based lookups
|
||||
members := OrganizationMembersParam(r)
|
||||
ownerID := members.UserID()
|
||||
|
||||
task, err := fetchTaskWithFallback(ctx, db, taskParam, ownerID)
|
||||
if err != nil {
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
@@ -48,10 +72,38 @@ func ExtractTaskParam(db database.Store) func(http.Handler) http.Handler {
|
||||
ctx = context.WithValue(ctx, taskParamContextKey{}, task)
|
||||
|
||||
if rlogger := loggermw.RequestLoggerFromContext(ctx); rlogger != nil {
|
||||
rlogger.WithFields(slog.F("task_id", task.ID), slog.F("task_name", task.Name))
|
||||
rlogger.WithFields(
|
||||
slog.F("task_id", task.ID),
|
||||
slog.F("task_name", task.Name),
|
||||
)
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fetchTaskWithFallback(ctx context.Context, db database.Store, taskParam string, ownerID uuid.UUID) (database.Task, error) {
|
||||
// Attempt to first lookup the task by UUID.
|
||||
taskID, err := uuid.Parse(taskParam)
|
||||
if err == nil {
|
||||
task, err := db.GetTaskByID(ctx, taskID)
|
||||
if err == nil {
|
||||
return task, nil
|
||||
}
|
||||
// There may be a task named with a valid UUID. Fall back to name lookup in this case.
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return database.Task{}, xerrors.Errorf("fetch task by uuid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// taskParam not a valid UUID, OR valid UUID but not found, so attempt lookup by name.
|
||||
task, err := db.GetTaskByOwnerIDAndName(ctx, database.GetTaskByOwnerIDAndNameParams{
|
||||
OwnerID: ownerID,
|
||||
Name: taskParam,
|
||||
})
|
||||
if err != nil {
|
||||
return database.Task{}, xerrors.Errorf("fetch task by name: %w", err)
|
||||
}
|
||||
return task, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user