diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 8877af73ac631..1d829409dabc2 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -37,7 +37,16 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspace, workspaceBuild, job)) + owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Internal error fetching user", + Detail: err.Error(), + }) + return + } + + httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job)) } func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { @@ -100,8 +109,8 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { } jobIDs := make([]uuid.UUID, 0, len(builds)) - for _, version := range builds { - jobIDs = append(jobIDs, version.JobID) + for _, build := range builds { + jobIDs = append(jobIDs, build.JobID) } jobs, err := api.Database.GetProvisionerJobsByIDs(r.Context(), jobIDs) if errors.Is(err, sql.ErrNoRows) { @@ -119,6 +128,15 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { jobByID[job.ID.String()] = job } + owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Internal error fetching user", + Detail: err.Error(), + }) + return + } + apiBuilds := make([]codersdk.WorkspaceBuild, 0) for _, build := range builds { job, exists := jobByID[build.JobID.String()] @@ -128,7 +146,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { }) return } - apiBuilds = append(apiBuilds, convertWorkspaceBuild(workspace, build, job)) + apiBuilds = append(apiBuilds, convertWorkspaceBuild(owner, workspace, build, job)) } httpapi.Write(rw, http.StatusOK, apiBuilds) @@ -167,8 +185,16 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) { }) return } + owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Internal error getting user", + Detail: err.Error(), + }) + return + } - httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspace, workspaceBuild, job)) + httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job)) } func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { @@ -342,8 +368,17 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return } + owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Internal error getting user", + Detail: err.Error(), + }) + return + } + httpapi.Write(rw, http.StatusCreated, - convertWorkspaceBuild(workspace, workspaceBuild, provisionerJob)) + convertWorkspaceBuild(owner, workspace, workspaceBuild, provisionerJob)) } func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) { @@ -473,6 +508,7 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { } func convertWorkspaceBuild( + workspaceOwner database.User, workspace database.Workspace, workspaceBuild database.WorkspaceBuild, job database.ProvisionerJob) codersdk.WorkspaceBuild { @@ -481,18 +517,20 @@ func convertWorkspaceBuild( panic("workspace and build do not match") } return codersdk.WorkspaceBuild{ - ID: workspaceBuild.ID, - CreatedAt: workspaceBuild.CreatedAt, - UpdatedAt: workspaceBuild.UpdatedAt, - WorkspaceID: workspaceBuild.WorkspaceID, - WorkspaceName: workspace.Name, - TemplateVersionID: workspaceBuild.TemplateVersionID, - BuildNumber: workspaceBuild.BuildNumber, - Name: workspaceBuild.Name, - Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition), - InitiatorID: workspaceBuild.InitiatorID, - Job: convertProvisionerJob(job), - Deadline: workspaceBuild.Deadline, + ID: workspaceBuild.ID, + CreatedAt: workspaceBuild.CreatedAt, + UpdatedAt: workspaceBuild.UpdatedAt, + WorkspaceOwnerID: workspace.OwnerID, + WorkspaceOwnerName: workspaceOwner.Username, + WorkspaceID: workspaceBuild.WorkspaceID, + WorkspaceName: workspace.Name, + TemplateVersionID: workspaceBuild.TemplateVersionID, + BuildNumber: workspaceBuild.BuildNumber, + Name: workspaceBuild.Name, + Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition), + InitiatorID: workspaceBuild.InitiatorID, + Job: convertProvisionerJob(job), + Deadline: workspaceBuild.Deadline, } } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 23c142bcdf873..115146598afb5 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -796,7 +796,7 @@ func convertWorkspace( OwnerID: workspace.OwnerID, OwnerName: owner.Username, TemplateID: workspace.TemplateID, - LatestBuild: convertWorkspaceBuild(workspace, workspaceBuild, job), + LatestBuild: convertWorkspaceBuild(owner, workspace, workspaceBuild, job), TemplateName: template.Name, Outdated: workspaceBuild.TemplateVersionID.String() != template.ActiveVersionID.String(), Name: workspace.Name, diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index 3216c4a62a439..ccf3e917f5d63 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -22,18 +22,20 @@ const ( // WorkspaceBuild is an at-point representation of a workspace state. // BuildNumbers start at 1 and increase by 1 for each subsequent build type WorkspaceBuild struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - WorkspaceID uuid.UUID `json:"workspace_id"` - WorkspaceName string `json:"workspace_name"` - TemplateVersionID uuid.UUID `json:"template_version_id"` - BuildNumber int32 `json:"build_number"` - Name string `json:"name"` - Transition WorkspaceTransition `json:"transition"` - InitiatorID uuid.UUID `json:"initiator_id"` - Job ProvisionerJob `json:"job"` - Deadline time.Time `json:"deadline"` + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + WorkspaceID uuid.UUID `json:"workspace_id"` + WorkspaceName string `json:"workspace_name"` + WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id"` + WorkspaceOwnerName string `json:"workspace_owner_name"` + TemplateVersionID uuid.UUID `json:"template_version_id"` + BuildNumber int32 `json:"build_number"` + Name string `json:"name"` + Transition WorkspaceTransition `json:"transition"` + InitiatorID uuid.UUID `json:"initiator_id"` + Job ProvisionerJob `json:"job"` + Deadline time.Time `json:"deadline"` } // WorkspaceBuild returns a single workspace build for a workspace. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 8e71d52a3906c..e71337dc786d6 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -436,6 +436,8 @@ export interface WorkspaceBuild { readonly updated_at: string readonly workspace_id: string readonly workspace_name: string + readonly workspace_owner_id: string + readonly workspace_owner_name: string readonly template_version_id: string readonly build_number: number readonly name: string diff --git a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx b/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx index 5087e4a6cb476..92152cd283209 100644 --- a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx +++ b/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx @@ -22,7 +22,7 @@ export const WorkspaceBuildStats: FC = ({ build }) => Workspace Name {build.workspace_name} diff --git a/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx b/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx index ccb103f15f85e..8ea1cc01c67c9 100644 --- a/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx +++ b/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx @@ -92,7 +92,11 @@ export const WorkspaceSchedule: FC = ({ workspace }) => {Language.autoStopDisplay(workspace)}
- + {Language.editScheduleLink}
diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 9749a75413adf..fec4684d4706e 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -17,7 +17,7 @@ const CreateWorkspacePage: FC = () => { context: { organizationId, preSelectedTemplateName }, actions: { onCreateWorkspace: (_, event) => { - navigate("/workspaces/" + event.data.id) + navigate(`/@${event.data.owner_name}/${event.data.name}`) }, }, }) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 0aef9da61c823..00af835a415a8 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -142,25 +142,31 @@ export const workspaceToInitialValues = ( } export const WorkspaceSchedulePage: React.FC = () => { + const { username: usernameQueryParam, workspace: workspaceQueryParam } = useParams() const navigate = useNavigate() - const { workspace: workspaceQueryParam } = useParams() - const workspaceId = firstOrItem(workspaceQueryParam, null) + const username = firstOrItem(usernameQueryParam, null) + const workspaceName = firstOrItem(workspaceQueryParam, null) const [scheduleState, scheduleSend] = useMachine(workspaceSchedule) const { formErrors, getWorkspaceError, workspace } = scheduleState.context - // Get workspace on mount and whenever workspaceId changes. + // Get workspace on mount and whenever the args for getting a workspace change. // scheduleSend should not change. useEffect(() => { - workspaceId && scheduleSend({ type: "GET_WORKSPACE", workspaceId }) - }, [workspaceId, scheduleSend]) + username && workspaceName && scheduleSend({ type: "GET_WORKSPACE", username, workspaceName }) + }, [username, workspaceName, scheduleSend]) - if (!workspaceId) { + if (!username || !workspaceName) { navigate("/workspaces") return null } else if (scheduleState.matches("idle") || scheduleState.matches("gettingWorkspace") || !workspace) { return } else if (scheduleState.matches("error")) { - return scheduleSend({ type: "GET_WORKSPACE", workspaceId })} /> + return ( + scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })} + /> + ) } else if (scheduleState.matches("presentForm") || scheduleState.matches("submittingSchedule")) { return ( { initialValues={workspaceToInitialValues(workspace, dayjs.tz.guess())} isLoading={scheduleState.tags.has("loading")} onCancel={() => { - navigate(`/workspaces/${workspaceId}`) + navigate(`/@${username}/${workspaceName}`) }} onSubmit={(values) => { scheduleSend({ @@ -180,7 +186,7 @@ export const WorkspaceSchedulePage: React.FC = () => { /> ) } else if (scheduleState.matches("submitSuccess")) { - navigate(`/workspaces/${workspaceId}`) + navigate(`/@${username}/${workspaceName}`) return } else { // Theoretically impossible - log and bail diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 2c06cb214020d..b277278929e35 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -135,6 +135,8 @@ export const MockWorkspaceBuild: TypesGen.WorkspaceBuild = { transition: "start", updated_at: "2022-05-17T17:39:01.382927298Z", workspace_name: "test-workspace", + workspace_owner_id: MockUser.id, + workspace_owner_name: MockUser.username, workspace_id: "759f1d46-3174-453d-aa60-980a9c1442f3", deadline: "2022-05-17T23:39:00.00Z", } diff --git a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts b/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts index a1ddb58254366..64f6ca3af37b0 100644 --- a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts +++ b/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts @@ -26,7 +26,7 @@ export interface WorkspaceScheduleContext { } export type WorkspaceScheduleEvent = - | { type: "GET_WORKSPACE"; workspaceId: string } + | { type: "GET_WORKSPACE"; username: string; workspaceName: string } | { type: "SUBMIT_SCHEDULE" autoStart: TypesGen.UpdateWorkspaceAutostartRequest @@ -132,7 +132,7 @@ export const workspaceSchedule = createMachine( services: { getWorkspace: async (_, event) => { - return await API.getWorkspace(event.workspaceId) + return await API.getWorkspaceByOwnerAndName(event.username, event.workspaceName) }, submitSchedule: async (context, event) => { if (!context.workspace?.id) { diff --git a/site/webpack.dev.ts b/site/webpack.dev.ts index aa66120ed516c..c6225f9afe795 100644 --- a/site/webpack.dev.ts +++ b/site/webpack.dev.ts @@ -63,7 +63,7 @@ const config: Configuration = { port: process.env.PORT || 8080, proxy: { "/api": { - target: "https://dev.coder.com", + target: process.env.CODER_HOST || "http://localhost:3000", ws: true, secure: false, }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy