Skip to content

Commit fb60891

Browse files
committed
fix: Remove unused workspace routes in favor of list with filter
This consolidates the workspace routes into a single place. It allows users to fetch a workspace by their username and workspace name, which will be used by the frontend for routing.
1 parent ac6cb26 commit fb60891

File tree

12 files changed

+40
-239
lines changed

12 files changed

+40
-239
lines changed

cli/configssh.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ func configSSH() *cobra.Command {
4848
if err != nil {
4949
return err
5050
}
51-
organization, err := currentOrganization(cmd, client)
52-
if err != nil {
53-
return err
54-
}
5551
if strings.HasPrefix(sshConfigFile, "~/") {
5652
dirname, _ := os.UserHomeDir()
5753
sshConfigFile = filepath.Join(dirname, sshConfigFile[2:])
@@ -65,7 +61,9 @@ func configSSH() *cobra.Command {
6561
sshConfigContent = sshConfigContent[:startIndex-1] + sshConfigContent[endIndex+len(sshEndToken):]
6662
}
6763

68-
workspaces, err := client.WorkspacesByOwner(cmd.Context(), organization.ID, codersdk.Me)
64+
workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{
65+
Owner: codersdk.Me,
66+
})
6967
if err != nil {
7068
return err
7169
}

cli/server.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,9 @@ func server() *cobra.Command {
412412
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit"))
413413

414414
if dev {
415-
organizations, err := client.OrganizationsByUser(cmd.Context(), codersdk.Me)
416-
if err != nil {
417-
return xerrors.Errorf("get organizations: %w", err)
418-
}
419-
workspaces, err := client.WorkspacesByOwner(cmd.Context(), organizations[0].ID, codersdk.Me)
415+
workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{
416+
Owner: codersdk.Me,
417+
})
420418
if err != nil {
421419
return xerrors.Errorf("get workspaces: %w", err)
422420
}

cli/ssh.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, orgID uui
194194
err error
195195
)
196196
if shuffle {
197-
workspaces, err := client.WorkspacesByOwner(cmd.Context(), orgID, userID)
197+
workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{
198+
Owner: codersdk.Me,
199+
})
198200
if err != nil {
199201
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
200202
}

coderd/coderd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ func New(options *Options) *API {
154154
})
155155
r.Route("/workspaces", func(r chi.Router) {
156156
r.Post("/", api.postWorkspacesByOrganization)
157-
r.Get("/", api.workspacesByOrganization)
158157
r.Route("/{user}", func(r chi.Router) {
159158
r.Use(httpmw.ExtractUserParam(options.Database))
160159
r.Get("/{workspacename}", api.workspaceByOwnerAndName)
@@ -259,6 +258,7 @@ func New(options *Options) *API {
259258
r.Get("/", api.organizationsByUser)
260259
r.Get("/{organizationname}", api.organizationByUserAndName)
261260
})
261+
r.Get("/workspace/{workspacename}", api.workspaceByOwnerAndName)
262262
r.Get("/gitsshkey", api.gitSSHKey)
263263
r.Put("/gitsshkey", api.regenerateGitSSHKey)
264264
})

coderd/workspaces.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -100,35 +100,6 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
100100
httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, owner))
101101
}
102102

103-
func (api *API) workspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
104-
organization := httpmw.OrganizationParam(r)
105-
workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{
106-
OrganizationID: organization.ID,
107-
Deleted: false,
108-
})
109-
if errors.Is(err, sql.ErrNoRows) {
110-
err = nil
111-
}
112-
if err != nil {
113-
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
114-
Message: fmt.Sprintf("get workspaces: %s", err),
115-
})
116-
return
117-
}
118-
119-
// Rbac filter
120-
workspaces = AuthorizeFilter(api, r, rbac.ActionRead, workspaces)
121-
122-
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces)
123-
if err != nil {
124-
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
125-
Message: fmt.Sprintf("convert workspaces: %s", err),
126-
})
127-
return
128-
}
129-
httpapi.Write(rw, http.StatusOK, apiWorkspaces)
130-
}
131-
132103
// workspaces returns all workspaces a user can read.
133104
// Optional filters with query params
134105
func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
@@ -223,7 +194,6 @@ func (api *API) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
223194

224195
func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) {
225196
owner := httpmw.UserParam(r)
226-
organization := httpmw.OrganizationParam(r)
227197
workspaceName := chi.URLParam(r, "workspacename")
228198

229199
workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
@@ -241,14 +211,6 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
241211
})
242212
return
243213
}
244-
245-
if workspace.OrganizationID != organization.ID {
246-
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
247-
Message: fmt.Sprintf("workspace is not owned by organization %q", organization.Name),
248-
})
249-
return
250-
}
251-
252214
if !api.Authorize(rw, r, rbac.ActionRead, workspace) {
253215
return
254216
}

coderd/workspaces_test.go

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -210,73 +210,12 @@ func TestPostWorkspacesByOrganization(t *testing.T) {
210210
})
211211
}
212212

213-
func TestWorkspacesByOrganization(t *testing.T) {
214-
t.Parallel()
215-
t.Run("ListEmpty", func(t *testing.T) {
216-
t.Parallel()
217-
client := coderdtest.New(t, nil)
218-
user := coderdtest.CreateFirstUser(t, client)
219-
_, err := client.WorkspacesByOrganization(context.Background(), user.OrganizationID)
220-
require.NoError(t, err)
221-
})
222-
t.Run("List", func(t *testing.T) {
223-
t.Parallel()
224-
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
225-
user := coderdtest.CreateFirstUser(t, client)
226-
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
227-
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
228-
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
229-
ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
230-
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID)
231-
workspaces, err := client.WorkspacesByOrganization(context.Background(), user.OrganizationID)
232-
require.NoError(t, err)
233-
require.Len(t, workspaces, 1)
234-
})
235-
}
236-
237-
func TestWorkspacesByOwner(t *testing.T) {
238-
t.Parallel()
239-
t.Run("ListEmpty", func(t *testing.T) {
240-
t.Parallel()
241-
client := coderdtest.New(t, nil)
242-
user := coderdtest.CreateFirstUser(t, client)
243-
_, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, codersdk.Me)
244-
require.NoError(t, err)
245-
})
246-
247-
t.Run("ListMine", func(t *testing.T) {
248-
t.Parallel()
249-
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
250-
user := coderdtest.CreateFirstUser(t, client)
251-
me, err := client.User(context.Background(), codersdk.Me)
252-
require.NoError(t, err)
253-
254-
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
255-
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
256-
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
257-
_ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
258-
259-
// Create noise workspace that should be filtered out
260-
other := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
261-
_ = coderdtest.CreateWorkspace(t, other, user.OrganizationID, template.ID)
262-
263-
// Use a username
264-
workspaces, err := client.Workspaces(context.Background(), codersdk.WorkspaceFilter{
265-
OrganizationID: user.OrganizationID,
266-
Owner: me.Username,
267-
})
268-
require.NoError(t, err)
269-
require.Len(t, workspaces, 1)
270-
})
271-
}
272-
273213
func TestWorkspaceByOwnerAndName(t *testing.T) {
274214
t.Parallel()
275215
t.Run("NotFound", func(t *testing.T) {
276216
t.Parallel()
277217
client := coderdtest.New(t, nil)
278-
user := coderdtest.CreateFirstUser(t, client)
279-
_, err := client.WorkspaceByOwnerAndName(context.Background(), user.OrganizationID, codersdk.Me, "something")
218+
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something")
280219
var apiErr *codersdk.Error
281220
require.ErrorAs(t, err, &apiErr)
282221
require.Equal(t, http.StatusForbidden, apiErr.StatusCode())
@@ -289,7 +228,7 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
289228
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
290229
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
291230
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
292-
_, err := client.WorkspaceByOwnerAndName(context.Background(), user.OrganizationID, codersdk.Me, workspace.Name)
231+
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name)
293232
require.NoError(t, err)
294233
})
295234
}
@@ -409,7 +348,9 @@ func TestPostWorkspaceBuild(t *testing.T) {
409348
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
410349
coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
411350

412-
workspaces, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, user.UserID.String())
351+
workspaces, err := client.Workspaces(context.Background(), codersdk.WorkspaceFilter{
352+
Owner: user.UserID.String(),
353+
})
413354
require.NoError(t, err)
414355
require.Len(t, workspaces, 0)
415356
})

codersdk/organizations.go

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -200,51 +200,3 @@ func (c *Client) CreateWorkspace(ctx context.Context, organizationID uuid.UUID,
200200
var workspace Workspace
201201
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
202202
}
203-
204-
// WorkspacesByOrganization returns all workspaces in the specified organization.
205-
func (c *Client) WorkspacesByOrganization(ctx context.Context, organizationID uuid.UUID) ([]Workspace, error) {
206-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces", organizationID), nil)
207-
if err != nil {
208-
return nil, err
209-
}
210-
defer res.Body.Close()
211-
212-
if res.StatusCode != http.StatusOK {
213-
return nil, readBodyAsError(res)
214-
}
215-
216-
var workspaces []Workspace
217-
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
218-
}
219-
220-
// WorkspacesByOwner returns all workspaces contained in the organization owned by the user.
221-
func (c *Client) WorkspacesByOwner(ctx context.Context, organizationID uuid.UUID, user string) ([]Workspace, error) {
222-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces/%s", organizationID, user), nil)
223-
if err != nil {
224-
return nil, err
225-
}
226-
defer res.Body.Close()
227-
228-
if res.StatusCode != http.StatusOK {
229-
return nil, readBodyAsError(res)
230-
}
231-
232-
var workspaces []Workspace
233-
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
234-
}
235-
236-
// WorkspaceByOwnerAndName returns a workspace by the owner's UUID and the workspace's name.
237-
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, organization uuid.UUID, owner string, name string) (Workspace, error) {
238-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces/%s/%s", organization, owner, name), nil)
239-
if err != nil {
240-
return Workspace{}, err
241-
}
242-
defer res.Body.Close()
243-
244-
if res.StatusCode != http.StatusOK {
245-
return Workspace{}, readBodyAsError(res)
246-
}
247-
248-
var workspace Workspace
249-
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
250-
}

codersdk/workspaces.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,19 @@ func (c *Client) Workspaces(ctx context.Context, filter WorkspaceFilter) ([]Work
234234
var workspaces []Workspace
235235
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
236236
}
237+
238+
// WorkspaceByOwnerAndName returns a workspace by the owner's UUID and the workspace's name.
239+
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, owner string, name string) (Workspace, error) {
240+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s", owner, name), nil)
241+
if err != nil {
242+
return Workspace{}, err
243+
}
244+
defer res.Body.Close()
245+
246+
if res.StatusCode != http.StatusOK {
247+
return Workspace{}, readBodyAsError(res)
248+
}
249+
250+
var workspace Workspace
251+
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
252+
}

site/src/api/api.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,10 @@ export const getWorkspaces = async (filter?: TypesGen.WorkspaceFilter): Promise<
136136
}
137137

138138
export const getWorkspaceByOwnerAndName = async (
139-
organizationID: string,
140139
username = "me",
141140
workspaceName: string,
142141
): Promise<TypesGen.Workspace> => {
143-
const response = await axios.get<TypesGen.Workspace>(
144-
`/api/v2/organizations/${organizationID}/workspaces/${username}/${workspaceName}`,
145-
)
142+
const response = await axios.get<TypesGen.Workspace>(`/api/v2/users/${username}/workspace/${workspaceName}`)
146143
return response.data
147144
}
148145

site/src/pages/TerminalPage/TerminalPage.test.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,10 @@ describe("TerminalPage", () => {
5454
history.push(`/some-user/${MockWorkspace.name}/terminal`)
5555
})
5656

57-
it("shows an error if fetching organizations fails", async () => {
58-
// Given
59-
server.use(
60-
rest.get("/api/v2/users/me/organizations", async (req, res, ctx) => {
61-
return res(ctx.status(500), ctx.json({ message: "nope" }))
62-
}),
63-
)
64-
65-
// When
66-
const { container } = renderTerminal()
67-
68-
// Then
69-
await expectTerminalText(container, Language.organizationsErrorMessagePrefix)
70-
})
71-
7257
it("shows an error if fetching workspace fails", async () => {
7358
// Given
7459
server.use(
75-
rest.get("/api/v2/organizations/:organizationId/workspaces/:userName/:workspaceName", (req, res, ctx) => {
60+
rest.get("/api/v2/users/:userName/workspace/:workspaceName", (req, res, ctx) => {
7661
return res(ctx.status(500), ctx.json({ id: "workspace-id" }))
7762
}),
7863
)

site/src/pages/TerminalPage/TerminalPage.tsx

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
1111
import { terminalMachine } from "../../xServices/terminal/terminalXService"
1212

1313
export const Language = {
14-
organizationsErrorMessagePrefix: "Unable to fetch organizations: ",
1514
workspaceErrorMessagePrefix: "Unable to fetch workspace: ",
1615
workspaceAgentErrorMessagePrefix: "Unable to fetch workspace agent: ",
1716
websocketErrorMessagePrefix: "WebSocket failed: ",
@@ -58,8 +57,7 @@ const TerminalPage: FC<{
5857
})
5958
const isConnected = terminalState.matches("connected")
6059
const isDisconnected = terminalState.matches("disconnected")
61-
const { organizationsError, workspaceError, workspaceAgentError, workspaceAgent, websocketError } =
62-
terminalState.context
60+
const { workspaceError, workspaceAgentError, workspaceAgent, websocketError } = terminalState.context
6361

6462
// Create the terminal!
6563
useEffect(() => {
@@ -145,9 +143,6 @@ const TerminalPage: FC<{
145143
terminal.options = {
146144
disableStdin: true,
147145
}
148-
if (organizationsError instanceof Error) {
149-
terminal.writeln(Language.organizationsErrorMessagePrefix + organizationsError.message)
150-
}
151146
if (workspaceError instanceof Error) {
152147
terminal.writeln(Language.workspaceErrorMessagePrefix + workspaceError.message)
153148
}
@@ -180,17 +175,7 @@ const TerminalPage: FC<{
180175
width: terminal.cols,
181176
},
182177
})
183-
}, [
184-
workspaceError,
185-
organizationsError,
186-
workspaceAgentError,
187-
websocketError,
188-
workspaceAgent,
189-
terminal,
190-
fitAddon,
191-
isConnected,
192-
sendEvent,
193-
])
178+
}, [workspaceError, workspaceAgentError, websocketError, workspaceAgent, terminal, fitAddon, isConnected, sendEvent])
194179

195180
return (
196181
<>

0 commit comments

Comments
 (0)