Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d7451d0

Browse files
committed
update build url to @username/workspace/builds/buildnumber
1 parent 953e8c8 commit d7451d0

File tree

16 files changed

+200
-49
lines changed

16 files changed

+200
-49
lines changed

coderd/coderd.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,10 @@ func New(options *Options) *API {
270270
r.Get("/", api.organizationsByUser)
271271
r.Get("/{organizationname}", api.organizationByUserAndName)
272272
})
273-
r.Get("/workspace/{workspacename}", api.workspaceByOwnerAndName)
273+
r.Route("/workspace/{workspacename}", func(r chi.Router) {
274+
r.Get("/", api.workspaceByOwnerAndName)
275+
r.Get("/builds/{buildnumber}", api.workspaceBuildByBuildNumber)
276+
})
274277
r.Get("/gitsshkey", api.gitSSHKey)
275278
r.Put("/gitsshkey", api.regenerateGitSSHKey)
276279
})

coderd/coderd_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"io"
66
"net/http"
7+
"strconv"
78
"strings"
89
"testing"
910
"time"
@@ -163,6 +164,10 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
163164
AssertObject: rbac.ResourceWorkspace,
164165
AssertAction: rbac.ActionRead,
165166
},
167+
"GET:/api/v2/users/me/workspace/{workspacename}/builds/{buildnumber}": {
168+
AssertObject: rbac.ResourceWorkspace,
169+
AssertAction: rbac.ActionRead,
170+
},
166171
"GET:/api/v2/workspaces/{workspace}/builds/{workspacebuildname}": {
167172
AssertAction: rbac.ActionRead,
168173
AssertObject: workspaceRBACObj,
@@ -388,6 +393,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
388393
route = strings.ReplaceAll(route, "{workspacename}", workspace.Name)
389394
route = strings.ReplaceAll(route, "{workspacebuildname}", workspace.LatestBuild.Name)
390395
route = strings.ReplaceAll(route, "{workspaceagent}", workspaceResources[0].Agents[0].ID.String())
396+
route = strings.ReplaceAll(route, "{buildnumber}", strconv.FormatInt(int64(workspace.LatestBuild.BuildNumber), 10))
391397
route = strings.ReplaceAll(route, "{template}", template.ID.String())
392398
route = strings.ReplaceAll(route, "{hash}", file.Hash)
393399
route = strings.ReplaceAll(route, "{workspaceresource}", workspaceResources[0].ID.String())

coderd/database/databasefake/databasefake.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,22 @@ func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndName(_ context.Context, a
616616
return database.WorkspaceBuild{}, sql.ErrNoRows
617617
}
618618

619+
func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) {
620+
q.mutex.RLock()
621+
defer q.mutex.RUnlock()
622+
623+
for _, workspaceBuild := range q.workspaceBuilds {
624+
if workspaceBuild.WorkspaceID.String() != arg.WorkspaceID.String() {
625+
continue
626+
}
627+
if workspaceBuild.BuildNumber != arg.BuildNumber {
628+
continue
629+
}
630+
return workspaceBuild, nil
631+
}
632+
return database.WorkspaceBuild{}, sql.ErrNoRows
633+
}
634+
619635
func (q *fakeQuerier) GetWorkspacesByOrganizationIDs(_ context.Context, req database.GetWorkspacesByOrganizationIDsParams) ([]database.Workspace, error) {
620636
q.mutex.RLock()
621637
defer q.mutex.RUnlock()

coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/workspacebuilds.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ WHERE
2727
workspace_id = $1
2828
AND "name" = $2;
2929

30+
-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one
31+
SELECT
32+
*
33+
FROM
34+
workspace_builds
35+
WHERE
36+
workspace_id = $1
37+
AND build_number = $2;
38+
3039
-- name: GetWorkspaceBuildByWorkspaceID :many
3140
SELECT
3241
*

coderd/workspacebuilds.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"net/http"
9+
"strconv"
910

1011
"github.com/go-chi/chi/v5"
1112
"github.com/google/uuid"
@@ -160,6 +161,70 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
160161
httpapi.Write(rw, http.StatusOK, apiBuilds)
161162
}
162163

164+
func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Request) {
165+
owner := httpmw.UserParam(r)
166+
workspaceName := chi.URLParam(r, "workspacename")
167+
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "buildnumber"), 10, 32)
168+
if err != nil {
169+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
170+
Message: "Internal error parsing build number as integer.",
171+
Detail: err.Error(),
172+
})
173+
return
174+
}
175+
176+
workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
177+
OwnerID: owner.ID,
178+
Name: workspaceName,
179+
})
180+
181+
if err != nil {
182+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
183+
Message: "Internal error fetching workspace by name.",
184+
Detail: err.Error(),
185+
})
186+
return
187+
}
188+
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceWorkspace.
189+
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
190+
return
191+
}
192+
193+
workspaceBuild, err := api.Database.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(r.Context(), database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{
194+
WorkspaceID: workspace.ID,
195+
BuildNumber: int32(buildNumber),
196+
})
197+
if err != nil {
198+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
199+
Message: "Internal error fetching workspace build.",
200+
Detail: err.Error(),
201+
})
202+
return
203+
}
204+
205+
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
206+
if err != nil {
207+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
208+
Message: "Internal error fetching provisioner job.",
209+
Detail: err.Error(),
210+
})
211+
return
212+
}
213+
214+
users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID})
215+
if err != nil {
216+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
217+
Message: "Internal error fetching user.",
218+
Detail: err.Error(),
219+
})
220+
return
221+
}
222+
223+
httpapi.Write(rw, http.StatusOK,
224+
convertWorkspaceBuild(findUser(workspace.OwnerID, users), findUser(workspaceBuild.InitiatorID, users),
225+
workspace, workspaceBuild, job))
226+
}
227+
163228
func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
164229
workspace := httpmw.WorkspaceParam(r)
165230
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceWorkspace.

site/src/AppRouter.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,6 @@ export const AppRouter: FC = () => (
113113
<Route path="ssh-keys" element={<SSHKeysPage />} />
114114
</Route>
115115

116-
<Route
117-
path="builds/:buildId"
118-
element={
119-
<AuthAndFrame>
120-
<WorkspaceBuildPage />
121-
</AuthAndFrame>
122-
}
123-
/>
124-
125116
<Route path="/@:username">
126117
<Route path=":workspace">
127118
<Route
@@ -160,6 +151,15 @@ export const AppRouter: FC = () => (
160151
}
161152
/>
162153
</Route>
154+
155+
<Route
156+
path="builds/:buildNumber"
157+
element={
158+
<AuthAndFrame>
159+
<WorkspaceBuildPage />
160+
</AuthAndFrame>
161+
}
162+
/>
163163
</Route>
164164
</Route>
165165

site/src/api/api.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,14 @@ export const getWorkspaceBuilds = async (workspaceId: string): Promise<TypesGen.
268268
return response.data
269269
}
270270

271-
export const getWorkspaceBuild = async (workspaceId: string): Promise<TypesGen.WorkspaceBuild> => {
272-
const response = await axios.get<TypesGen.WorkspaceBuild>(`/api/v2/workspacebuilds/${workspaceId}`)
271+
export const getWorkspaceBuildByNumber = async (
272+
username = "me",
273+
workspaceName: string,
274+
buildNumber: string,
275+
): Promise<TypesGen.WorkspaceBuild> => {
276+
const response = await axios.get<TypesGen.WorkspaceBuild>(
277+
`/api/v2/users/${username}/workspace/${workspaceName}/builds/${buildNumber}`,
278+
)
273279
return response.data
274280
}
275281

site/src/components/BuildsTable/BuildsTable.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ export const Language = {
2626
export interface BuildsTableProps {
2727
builds?: TypesGen.WorkspaceBuild[]
2828
className?: string
29+
username: string
30+
workspaceName: string
2931
}
3032

31-
export const BuildsTable: FC<BuildsTableProps> = ({ builds, className }) => {
33+
export const BuildsTable: FC<BuildsTableProps> = ({ builds, className, username, workspaceName }) => {
3234
const isLoading = !builds
3335
const theme: Theme = useTheme()
3436
const navigate = useNavigate()
@@ -52,7 +54,7 @@ export const BuildsTable: FC<BuildsTableProps> = ({ builds, className }) => {
5254
const status = getDisplayWorkspaceBuildStatus(theme, build)
5355

5456
const navigateToBuildPage = () => {
55-
navigate(`/builds/${build.id}`)
57+
navigate(`/@${username}/${workspaceName}/builds/${build.build_number}`)
5658
}
5759

5860
return (

site/src/components/Workspace/Workspace.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface WorkspaceProps {
2929
getResourcesError?: Error
3030
builds?: TypesGen.WorkspaceBuild[]
3131
canUpdateWorkspace: boolean
32+
username: string
3233
}
3334

3435
/**
@@ -46,6 +47,7 @@ export const Workspace: FC<WorkspaceProps> = ({
4647
getResourcesError,
4748
builds,
4849
canUpdateWorkspace,
50+
username,
4951
}) => {
5052
const styles = useStyles()
5153
const navigate = useNavigate()
@@ -91,7 +93,12 @@ export const Workspace: FC<WorkspaceProps> = ({
9193
)}
9294

9395
<WorkspaceSection title="Timeline" contentsProps={{ className: styles.timelineContents }}>
94-
<BuildsTable builds={builds} className={styles.timelineTable} />
96+
<BuildsTable
97+
builds={builds}
98+
className={styles.timelineTable}
99+
username={username}
100+
workspaceName={workspace.name}
101+
/>
95102
</WorkspaceSection>
96103
</Stack>
97104

site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { screen } from "@testing-library/react"
22
import * as API from "../../api/api"
3-
import { MockWorkspaceBuild, MockWorkspaceBuildLogs, renderWithAuth } from "../../testHelpers/renderHelpers"
3+
import {
4+
MockWorkspace,
5+
MockWorkspaceBuild,
6+
MockWorkspaceBuildLogs,
7+
renderWithAuth,
8+
} from "../../testHelpers/renderHelpers"
49
import { WorkspaceBuildPage } from "./WorkspaceBuildPage"
510

611
describe("WorkspaceBuildPage", () => {
@@ -16,7 +21,10 @@ describe("WorkspaceBuildPage", () => {
1621
closed: Promise.resolve(undefined),
1722
cancel: jest.fn(),
1823
})
19-
renderWithAuth(<WorkspaceBuildPage />, { route: `/builds/${MockWorkspaceBuild.id}`, path: "/builds/:buildId" })
24+
renderWithAuth(<WorkspaceBuildPage />, {
25+
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}/builds/${MockWorkspace.latest_build.build_number}`,
26+
path: "/@:username/:workspace/builds/:buildNumber",
27+
})
2028

2129
await screen.findByText(MockWorkspaceBuild.workspace_name)
2230
await screen.findByText(MockWorkspaceBuildLogs[0].stage)

site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,9 @@ import { pageTitle } from "../../util/page"
66
import { workspaceBuildMachine } from "../../xServices/workspaceBuild/workspaceBuildXService"
77
import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView"
88

9-
const useBuildId = () => {
10-
const { buildId } = useParams()
11-
12-
if (!buildId) {
13-
throw new Error("buildId param is required.")
14-
}
15-
16-
return buildId
17-
}
18-
199
export const WorkspaceBuildPage: FC = () => {
20-
const buildId = useBuildId()
21-
const [buildState] = useMachine(workspaceBuildMachine, { context: { buildId } })
10+
const { username, workspace: workspaceName, buildNumber } = useParams()
11+
const [buildState] = useMachine(workspaceBuildMachine, { context: { username, workspaceName, buildNumber } })
2212
const { logs, build } = buildState.context
2313
const isWaitingForLogs = !buildState.matches("logs.loaded")
2414

site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const WorkspacePage: React.FC = () => {
6868
getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined}
6969
builds={builds}
7070
canUpdateWorkspace={canUpdateWorkspace}
71+
username={username || "me"}
7172
/>
7273
<DeleteWorkspaceDialog
7374
isOpen={workspaceState.matches({ ready: { build: "askingDelete" } })}

site/src/testHelpers/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const handlers = [
115115
rest.get("/api/v2/workspaces/:workspaceId/builds", async (req, res, ctx) => {
116116
return res(ctx.status(200), ctx.json(M.MockBuilds))
117117
}),
118-
rest.get("/api/v2/workspacebuilds/:workspaceBuildId", (req, res, ctx) => {
118+
rest.get("/api/v2/users/:username/workspace/:workspaceName/builds/:buildNumber", (req, res, ctx) => {
119119
return res(ctx.status(200), ctx.json(M.MockWorkspaceBuild))
120120
}),
121121
rest.get("/api/v2/workspacebuilds/:workspaceBuildId/resources", (req, res, ctx) => {

0 commit comments

Comments
 (0)