mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
bddb808b25
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example: ``` import ( "context" "time" "github.com/prometheus/client_golang/prometheus" "golang.org/x/xerrors" "gopkg.in/natefinch/lumberjack.v2" "cdr.dev/slog/v3" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/serpent" ) ``` 3 groups: standard library, 3rd partly libs, Coder libs. This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
109 lines
3.1 KiB
Go
109 lines
3.1 KiB
Go
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/v3"
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/httpapi"
|
|
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
type taskParamContextKey struct{}
|
|
|
|
// TaskParam returns the task from the ExtractTaskParam handler.
|
|
func TaskParam(r *http.Request) database.Task {
|
|
task, ok := r.Context().Value(taskParamContextKey{}).(database.Task)
|
|
if !ok {
|
|
panic("developer error: task param middleware not provided")
|
|
}
|
|
return task
|
|
}
|
|
|
|
// 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()
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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)
|
|
return
|
|
}
|
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
Message: "Internal error fetching task.",
|
|
Detail: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
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),
|
|
)
|
|
}
|
|
|
|
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
|
|
}
|