@@ -3,6 +3,7 @@ package httpmw
33import (
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+ }
0 commit comments