Skip to content

Commit 2ae8507

Browse files
committed
remove lookup by workspace name, refactor and simplify
1 parent 6f171b9 commit 2ae8507

File tree

2 files changed

+32
-86
lines changed

2 files changed

+32
-86
lines changed

coderd/httpmw/taskparam.go

Lines changed: 30 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package httpmw
33
import (
44
"context"
55
"database/sql"
6+
"errors"
67
"net/http"
78

89
"github.com/go-chi/chi/v5"
@@ -28,61 +29,10 @@ func TaskParam(r *http.Request) database.Task {
2829
return task
2930
}
3031

31-
func fetchTaskByUUID(ctx context.Context, db database.Store, taskParam string, _ uuid.UUID) (database.Task, error) {
32-
taskID, err := uuid.Parse(taskParam)
33-
if err != nil {
34-
// Not a valid UUID, skip this strategy
35-
return database.Task{}, sql.ErrNoRows
36-
}
37-
task, err := db.GetTaskByID(ctx, taskID)
38-
if err != nil {
39-
return database.Task{}, xerrors.Errorf("fetch task by uuid: %w", err)
40-
}
41-
return task, nil
42-
}
43-
44-
func fetchTaskByName(ctx context.Context, db database.Store, taskParam string, ownerID uuid.UUID) (database.Task, error) {
45-
task, err := db.GetTaskByOwnerIDAndName(ctx, database.GetTaskByOwnerIDAndNameParams{
46-
OwnerID: ownerID,
47-
Name: taskParam,
48-
})
49-
if err != nil {
50-
return database.Task{}, xerrors.Errorf("fetch task by name: %w", err)
51-
}
52-
return task, nil
53-
}
54-
55-
func fetchTaskByWorkspaceName(ctx context.Context, db database.Store, taskParam string, ownerID uuid.UUID) (database.Task, error) {
56-
workspace, err := db.GetWorkspaceByOwnerIDAndName(ctx, database.GetWorkspaceByOwnerIDAndNameParams{
57-
OwnerID: ownerID,
58-
Name: taskParam,
59-
})
60-
if err != nil {
61-
return database.Task{}, xerrors.Errorf("fetch workspace by name: %w", err)
62-
}
63-
// Check if workspace has an associated task before querying
64-
if !workspace.TaskID.Valid {
65-
return database.Task{}, sql.ErrNoRows
66-
}
67-
task, err := db.GetTaskByWorkspaceID(ctx, workspace.ID)
68-
if err != nil {
69-
return database.Task{}, xerrors.Errorf("fetch task by workspace id: %w", err)
70-
}
71-
return task, nil
72-
}
73-
74-
type taskFetchFunc func(ctx context.Context, db database.Store, taskParam string, ownerID uuid.UUID) (database.Task, error)
75-
76-
var (
77-
taskLookupStrategyNames = []string{"uuid", "name", "workspace"}
78-
taskLookupStrategyFuncs = []taskFetchFunc{fetchTaskByUUID, fetchTaskByName, fetchTaskByWorkspaceName}
79-
)
80-
8132
// ExtractTaskParam grabs a task from the "task" URL parameter.
82-
// It supports three lookup strategies with cascading fallback:
33+
// It supports two lookup strategies:
8334
// 1. Task UUID (primary)
8435
// 2. Task name scoped to owner (secondary)
85-
// 3. Workspace name scoped to owner (tertiary, for legacy links)
8636
//
8737
// This middleware depends on ExtractOrganizationMembersParam being in the chain
8838
// to provide the owner context for name-based lookups.
@@ -92,7 +42,7 @@ func ExtractTaskParam(db database.Store) func(http.Handler) http.Handler {
9242
ctx := r.Context()
9343

9444
// Get the task parameter value. We can't use ParseUUIDParam here because
95-
// we need to support non-UUID values (task names and workspace names) and
45+
// we need to support non-UUID values (task names) and
9646
// attempt all lookup strategies.
9747
taskParam := chi.URLParam(r, "task")
9848
if taskParam == "" {
@@ -106,29 +56,15 @@ func ExtractTaskParam(db database.Store) func(http.Handler) http.Handler {
10656
members := OrganizationMembersParam(r)
10757
ownerID := members.UserID()
10858

109-
// Try each strategy in order until one succeeds
110-
var task database.Task
111-
var foundBy string
112-
for i, fetch := range taskLookupStrategyFuncs {
113-
t, err := fetch(ctx, db, taskParam, ownerID)
114-
if err == nil {
115-
task = t
116-
foundBy = taskLookupStrategyNames[i]
117-
break
118-
}
59+
task, err := fetchTaskWithFallback(ctx, db, taskParam, ownerID)
60+
if err != nil {
11961
if !httpapi.Is404Error(err) {
120-
// Real error (not just "not found")
12162
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
12263
Message: "Internal error fetching task.",
12364
Detail: err.Error(),
12465
})
12566
return
12667
}
127-
// Continue to next strategy on 404
128-
}
129-
130-
// If no strategy succeeded, return 404
131-
if foundBy == "" {
13268
httpapi.ResourceNotFound(rw)
13369
return
13470
}
@@ -139,11 +75,35 @@ func ExtractTaskParam(db database.Store) func(http.Handler) http.Handler {
13975
rlogger.WithFields(
14076
slog.F("task_id", task.ID),
14177
slog.F("task_name", task.Name),
142-
slog.F("task_lookup_strategy", foundBy),
14378
)
14479
}
14580

14681
next.ServeHTTP(rw, r.WithContext(ctx))
14782
})
14883
}
14984
}
85+
86+
func fetchTaskWithFallback(ctx context.Context, db database.Store, taskParam string, ownerID uuid.UUID) (database.Task, error) {
87+
// Attempt to first lookup the task by UUID.
88+
taskID, err := uuid.Parse(taskParam)
89+
if err == nil {
90+
task, err := db.GetTaskByID(ctx, taskID)
91+
if err == nil {
92+
return task, nil
93+
}
94+
// There may be a task named with a valid UUID. Fall back to name lookup in this case.
95+
if !errors.Is(err, sql.ErrNoRows) {
96+
return database.Task{}, xerrors.Errorf("fetch task by uuid: %w", err)
97+
}
98+
}
99+
100+
// taskParam not a valid UUID, OR valid UUID but not found, so attempt lookup by name.
101+
task, err := db.GetTaskByOwnerIDAndName(ctx, database.GetTaskByOwnerIDAndNameParams{
102+
OwnerID: ownerID,
103+
Name: taskParam,
104+
})
105+
if err != nil {
106+
return database.Task{}, xerrors.Errorf("fetch task by name: %w", err)
107+
}
108+
return task, nil
109+
}

coderd/httpmw/taskparam_test.go

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ func TestTaskParam(t *testing.T) {
184184
require.Equal(t, http.StatusOK, res.StatusCode)
185185
})
186186

187-
t.Run("FoundByWorkspaceName", func(t *testing.T) {
187+
t.Run("NotFoundByWorkspaceName", func(t *testing.T) {
188188
t.Parallel()
189189
rtr := makeRouter()
190190
r := makeRequest(user.ID, token)
@@ -194,7 +194,7 @@ func TestTaskParam(t *testing.T) {
194194

195195
res := rw.Result()
196196
defer res.Body.Close()
197-
require.Equal(t, http.StatusOK, res.StatusCode)
197+
require.Equal(t, http.StatusNotFound, res.StatusCode)
198198
})
199199

200200
t.Run("CaseInsensitiveTaskName", func(t *testing.T) {
@@ -229,20 +229,6 @@ func TestTaskParam(t *testing.T) {
229229
require.Equal(t, "found-by-uuid", taskFoundByUUID.Name)
230230
})
231231

232-
t.Run("TaskNameNotFoundFallsBackToWorkspace", func(t *testing.T) {
233-
t.Parallel()
234-
rtr := makeRouter()
235-
r := makeRequest(user.ID, token)
236-
// Look up by workspace name (which is not a task name) - should fallback
237-
chi.RouteContext(r.Context()).URLParams.Add("task", "shared-name")
238-
rw := httptest.NewRecorder()
239-
rtr.ServeHTTP(rw, r)
240-
241-
res := rw.Result()
242-
defer res.Body.Close()
243-
require.Equal(t, http.StatusOK, res.StatusCode)
244-
})
245-
246232
t.Run("NotFoundWhenNoMatch", func(t *testing.T) {
247233
t.Parallel()
248234
rtr := makeRouter()

0 commit comments

Comments
 (0)