Skip to content

Commit 7e8692b

Browse files
kylecarbspresleyp
andauthored
fix: Update routing for workspace schedule (coder#2113)
* fix: Update routing for workspace schedule This was broken as part of coder#2101. It was a silly mistake, but unfortunate our tests didn't catch it. This is a rare change so unlikely to occur again, so I won't make an issue adding tests. * Update site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx Co-authored-by: Presley Pizzo <1290996+presleyp@users.noreply.github.com> Co-authored-by: Presley Pizzo <1290996+presleyp@users.noreply.github.com>
1 parent a4e259e commit 7e8692b

File tree

11 files changed

+100
-46
lines changed

11 files changed

+100
-46
lines changed

coderd/workspacebuilds.go

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,16 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
3737
return
3838
}
3939

40-
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspace, workspaceBuild, job))
40+
owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID)
41+
if err != nil {
42+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
43+
Message: "Internal error fetching user",
44+
Detail: err.Error(),
45+
})
46+
return
47+
}
48+
49+
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job))
4150
}
4251

4352
func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
@@ -100,8 +109,8 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
100109
}
101110

102111
jobIDs := make([]uuid.UUID, 0, len(builds))
103-
for _, version := range builds {
104-
jobIDs = append(jobIDs, version.JobID)
112+
for _, build := range builds {
113+
jobIDs = append(jobIDs, build.JobID)
105114
}
106115
jobs, err := api.Database.GetProvisionerJobsByIDs(r.Context(), jobIDs)
107116
if errors.Is(err, sql.ErrNoRows) {
@@ -119,6 +128,15 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
119128
jobByID[job.ID.String()] = job
120129
}
121130

131+
owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID)
132+
if err != nil {
133+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
134+
Message: "Internal error fetching user",
135+
Detail: err.Error(),
136+
})
137+
return
138+
}
139+
122140
apiBuilds := make([]codersdk.WorkspaceBuild, 0)
123141
for _, build := range builds {
124142
job, exists := jobByID[build.JobID.String()]
@@ -128,7 +146,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
128146
})
129147
return
130148
}
131-
apiBuilds = append(apiBuilds, convertWorkspaceBuild(workspace, build, job))
149+
apiBuilds = append(apiBuilds, convertWorkspaceBuild(owner, workspace, build, job))
132150
}
133151

134152
httpapi.Write(rw, http.StatusOK, apiBuilds)
@@ -167,8 +185,16 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
167185
})
168186
return
169187
}
188+
owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID)
189+
if err != nil {
190+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
191+
Message: "Internal error getting user",
192+
Detail: err.Error(),
193+
})
194+
return
195+
}
170196

171-
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspace, workspaceBuild, job))
197+
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job))
172198
}
173199

174200
func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
@@ -342,8 +368,17 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
342368
return
343369
}
344370

371+
owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID)
372+
if err != nil {
373+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
374+
Message: "Internal error getting user",
375+
Detail: err.Error(),
376+
})
377+
return
378+
}
379+
345380
httpapi.Write(rw, http.StatusCreated,
346-
convertWorkspaceBuild(workspace, workspaceBuild, provisionerJob))
381+
convertWorkspaceBuild(owner, workspace, workspaceBuild, provisionerJob))
347382
}
348383

349384
func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) {
@@ -473,6 +508,7 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) {
473508
}
474509

475510
func convertWorkspaceBuild(
511+
workspaceOwner database.User,
476512
workspace database.Workspace,
477513
workspaceBuild database.WorkspaceBuild,
478514
job database.ProvisionerJob) codersdk.WorkspaceBuild {
@@ -481,18 +517,20 @@ func convertWorkspaceBuild(
481517
panic("workspace and build do not match")
482518
}
483519
return codersdk.WorkspaceBuild{
484-
ID: workspaceBuild.ID,
485-
CreatedAt: workspaceBuild.CreatedAt,
486-
UpdatedAt: workspaceBuild.UpdatedAt,
487-
WorkspaceID: workspaceBuild.WorkspaceID,
488-
WorkspaceName: workspace.Name,
489-
TemplateVersionID: workspaceBuild.TemplateVersionID,
490-
BuildNumber: workspaceBuild.BuildNumber,
491-
Name: workspaceBuild.Name,
492-
Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition),
493-
InitiatorID: workspaceBuild.InitiatorID,
494-
Job: convertProvisionerJob(job),
495-
Deadline: workspaceBuild.Deadline,
520+
ID: workspaceBuild.ID,
521+
CreatedAt: workspaceBuild.CreatedAt,
522+
UpdatedAt: workspaceBuild.UpdatedAt,
523+
WorkspaceOwnerID: workspace.OwnerID,
524+
WorkspaceOwnerName: workspaceOwner.Username,
525+
WorkspaceID: workspaceBuild.WorkspaceID,
526+
WorkspaceName: workspace.Name,
527+
TemplateVersionID: workspaceBuild.TemplateVersionID,
528+
BuildNumber: workspaceBuild.BuildNumber,
529+
Name: workspaceBuild.Name,
530+
Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition),
531+
InitiatorID: workspaceBuild.InitiatorID,
532+
Job: convertProvisionerJob(job),
533+
Deadline: workspaceBuild.Deadline,
496534
}
497535
}
498536

coderd/workspaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ func convertWorkspace(
807807
OwnerID: workspace.OwnerID,
808808
OwnerName: owner.Username,
809809
TemplateID: workspace.TemplateID,
810-
LatestBuild: convertWorkspaceBuild(workspace, workspaceBuild, job),
810+
LatestBuild: convertWorkspaceBuild(owner, workspace, workspaceBuild, job),
811811
TemplateName: template.Name,
812812
Outdated: workspaceBuild.TemplateVersionID.String() != template.ActiveVersionID.String(),
813813
Name: workspace.Name,

codersdk/workspacebuilds.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,20 @@ const (
2222
// WorkspaceBuild is an at-point representation of a workspace state.
2323
// BuildNumbers start at 1 and increase by 1 for each subsequent build
2424
type WorkspaceBuild struct {
25-
ID uuid.UUID `json:"id"`
26-
CreatedAt time.Time `json:"created_at"`
27-
UpdatedAt time.Time `json:"updated_at"`
28-
WorkspaceID uuid.UUID `json:"workspace_id"`
29-
WorkspaceName string `json:"workspace_name"`
30-
TemplateVersionID uuid.UUID `json:"template_version_id"`
31-
BuildNumber int32 `json:"build_number"`
32-
Name string `json:"name"`
33-
Transition WorkspaceTransition `json:"transition"`
34-
InitiatorID uuid.UUID `json:"initiator_id"`
35-
Job ProvisionerJob `json:"job"`
36-
Deadline time.Time `json:"deadline"`
25+
ID uuid.UUID `json:"id"`
26+
CreatedAt time.Time `json:"created_at"`
27+
UpdatedAt time.Time `json:"updated_at"`
28+
WorkspaceID uuid.UUID `json:"workspace_id"`
29+
WorkspaceName string `json:"workspace_name"`
30+
WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id"`
31+
WorkspaceOwnerName string `json:"workspace_owner_name"`
32+
TemplateVersionID uuid.UUID `json:"template_version_id"`
33+
BuildNumber int32 `json:"build_number"`
34+
Name string `json:"name"`
35+
Transition WorkspaceTransition `json:"transition"`
36+
InitiatorID uuid.UUID `json:"initiator_id"`
37+
Job ProvisionerJob `json:"job"`
38+
Deadline time.Time `json:"deadline"`
3739
}
3840

3941
// WorkspaceBuild returns a single workspace build for a workspace.

site/src/api/typesGenerated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ export interface WorkspaceBuild {
440440
readonly updated_at: string
441441
readonly workspace_id: string
442442
readonly workspace_name: string
443+
readonly workspace_owner_id: string
444+
readonly workspace_owner_name: string
443445
readonly template_version_id: string
444446
readonly build_number: number
445447
readonly name: string

site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const WorkspaceBuildStats: FC<WorkspaceBuildStatsProps> = ({ build }) =>
2222
<span className={styles.statsLabel}>Workspace Name</span>
2323
<Link
2424
component={RouterLink}
25-
to={`/workspaces/${build.workspace_id}`}
25+
to={`/@${build.workspace_owner_name}/${build.workspace_name}`}
2626
className={combineClasses([styles.statsValue, styles.link])}
2727
>
2828
{build.workspace_name}

site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export const WorkspaceSchedule: FC<WorkspaceScheduleProps> = ({ workspace }) =>
9292
<span className={styles.scheduleValue}>{Language.autoStopDisplay(workspace)}</span>
9393
</div>
9494
<div>
95-
<Link className={styles.scheduleAction} component={RouterLink} to={`/workspaces/${workspace.id}/schedule`}>
95+
<Link
96+
className={styles.scheduleAction}
97+
component={RouterLink}
98+
to={`/@${workspace.owner_name}/${workspace.name}/schedule`}
99+
>
96100
{Language.editScheduleLink}
97101
</Link>
98102
</div>

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const CreateWorkspacePage: FC = () => {
1717
context: { organizationId, preSelectedTemplateName },
1818
actions: {
1919
onCreateWorkspace: (_, event) => {
20-
navigate("/workspaces/" + event.data.id)
20+
navigate(`/@${event.data.owner_name}/${event.data.name}`)
2121
},
2222
},
2323
})

site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,33 +142,39 @@ export const workspaceToInitialValues = (
142142
}
143143

144144
export const WorkspaceSchedulePage: React.FC = () => {
145+
const { username: usernameQueryParam, workspace: workspaceQueryParam } = useParams()
145146
const navigate = useNavigate()
146-
const { workspace: workspaceQueryParam } = useParams()
147-
const workspaceId = firstOrItem(workspaceQueryParam, null)
147+
const username = firstOrItem(usernameQueryParam, null)
148+
const workspaceName = firstOrItem(workspaceQueryParam, null)
148149
const [scheduleState, scheduleSend] = useMachine(workspaceSchedule)
149150
const { formErrors, getWorkspaceError, workspace } = scheduleState.context
150151

151-
// Get workspace on mount and whenever workspaceId changes.
152+
// Get workspace on mount and whenever the args for getting a workspace change.
152153
// scheduleSend should not change.
153154
useEffect(() => {
154-
workspaceId && scheduleSend({ type: "GET_WORKSPACE", workspaceId })
155-
}, [workspaceId, scheduleSend])
155+
username && workspaceName && scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })
156+
}, [username, workspaceName, scheduleSend])
156157

157-
if (!workspaceId) {
158+
if (!username || !workspaceName) {
158159
navigate("/workspaces")
159160
return null
160161
} else if (scheduleState.matches("idle") || scheduleState.matches("gettingWorkspace") || !workspace) {
161162
return <FullScreenLoader />
162163
} else if (scheduleState.matches("error")) {
163-
return <ErrorSummary error={getWorkspaceError} retry={() => scheduleSend({ type: "GET_WORKSPACE", workspaceId })} />
164+
return (
165+
<ErrorSummary
166+
error={getWorkspaceError}
167+
retry={() => scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })}
168+
/>
169+
)
164170
} else if (scheduleState.matches("presentForm") || scheduleState.matches("submittingSchedule")) {
165171
return (
166172
<WorkspaceScheduleForm
167173
fieldErrors={formErrors}
168174
initialValues={workspaceToInitialValues(workspace, dayjs.tz.guess())}
169175
isLoading={scheduleState.tags.has("loading")}
170176
onCancel={() => {
171-
navigate(`/workspaces/${workspaceId}`)
177+
navigate(`/@${username}/${workspaceName}`)
172178
}}
173179
onSubmit={(values) => {
174180
scheduleSend({
@@ -180,7 +186,7 @@ export const WorkspaceSchedulePage: React.FC = () => {
180186
/>
181187
)
182188
} else if (scheduleState.matches("submitSuccess")) {
183-
navigate(`/workspaces/${workspaceId}`)
189+
navigate(`/@${username}/${workspaceName}`)
184190
return <FullScreenLoader />
185191
} else {
186192
// Theoretically impossible - log and bail

site/src/testHelpers/entities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ export const MockWorkspaceBuild: TypesGen.WorkspaceBuild = {
137137
transition: "start",
138138
updated_at: "2022-05-17T17:39:01.382927298Z",
139139
workspace_name: "test-workspace",
140+
workspace_owner_id: MockUser.id,
141+
workspace_owner_name: MockUser.username,
140142
workspace_id: "759f1d46-3174-453d-aa60-980a9c1442f3",
141143
deadline: "2022-05-17T23:39:00.00Z",
142144
}

site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface WorkspaceScheduleContext {
2626
}
2727

2828
export type WorkspaceScheduleEvent =
29-
| { type: "GET_WORKSPACE"; workspaceId: string }
29+
| { type: "GET_WORKSPACE"; username: string; workspaceName: string }
3030
| {
3131
type: "SUBMIT_SCHEDULE"
3232
autoStart: TypesGen.UpdateWorkspaceAutostartRequest
@@ -132,7 +132,7 @@ export const workspaceSchedule = createMachine(
132132

133133
services: {
134134
getWorkspace: async (_, event) => {
135-
return await API.getWorkspace(event.workspaceId)
135+
return await API.getWorkspaceByOwnerAndName(event.username, event.workspaceName)
136136
},
137137
submitSchedule: async (context, event) => {
138138
if (!context.workspace?.id) {

site/webpack.dev.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const config: Configuration = {
6363
port: process.env.PORT || 8080,
6464
proxy: {
6565
"/api": {
66-
target: "https://dev.coder.com",
66+
target: process.env.CODER_HOST || "http://localhost:3000",
6767
ws: true,
6868
secure: false,
6969
},

0 commit comments

Comments
 (0)