Skip to content

Commit 9f974c8

Browse files
Kira-Pilotmafredrijohnstcn
authored andcommitted
feat: added include_deleted to getWorkspaceByOwnerAndName (#2164)
* feat: added include_deleted relates to #1955 * Update coderd/workspaces.go defining vars in the scope of conditional Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> * Update coderd/workspaces.go avoid newline Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> * Update coderd/workspaces.go Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> * PR feedback * wrote test, added type * Update coderd/workspaces_test.go shortening test name Co-authored-by: Cian Johnston <cian@coder.com> * taking out api.ts change for now * casing Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> Co-authored-by: Cian Johnston <cian@coder.com>
1 parent 6caffb1 commit 9f974c8

File tree

7 files changed

+72
-7
lines changed

7 files changed

+72
-7
lines changed

.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"workspaceapp",
8484
"workspaceapps",
8585
"workspacebuilds",
86+
"workspacename",
8687
"wsconncache",
8788
"xerrors",
8889
"xstate",

cli/create.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func create() *cobra.Command {
4949
workspaceName, err = cliui.Prompt(cmd, cliui.PromptOptions{
5050
Text: "Specify a name for your workspace:",
5151
Validate: func(workspaceName string) error {
52-
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName)
52+
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName, codersdk.WorkspaceByOwnerAndNameParams{})
5353
if err == nil {
5454
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
5555
}
@@ -61,7 +61,7 @@ func create() *cobra.Command {
6161
}
6262
}
6363

64-
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName)
64+
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName, codersdk.WorkspaceByOwnerAndNameParams{})
6565
if err == nil {
6666
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
6767
}

cli/root.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, identifier stri
214214
return codersdk.Workspace{}, xerrors.Errorf("invalid workspace name: %q", identifier)
215215
}
216216

217-
return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name)
217+
return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name, codersdk.WorkspaceByOwnerAndNameParams{})
218218
}
219219

220220
// createConfig consumes the global configuration flag to produce a config root.

coderd/workspaces.go

+22
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,32 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
165165
owner := httpmw.UserParam(r)
166166
workspaceName := chi.URLParam(r, "workspacename")
167167

168+
includeDeleted := false
169+
if s := r.URL.Query().Get("include_deleted"); s != "" {
170+
var err error
171+
includeDeleted, err = strconv.ParseBool(s)
172+
if err != nil {
173+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
174+
Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", s),
175+
Validations: []httpapi.Error{
176+
{Field: "include_deleted", Detail: "Must be a valid boolean"},
177+
},
178+
})
179+
return
180+
}
181+
}
182+
168183
workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
169184
OwnerID: owner.ID,
170185
Name: workspaceName,
171186
})
187+
if includeDeleted && errors.Is(err, sql.ErrNoRows) {
188+
workspace, err = api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
189+
OwnerID: owner.ID,
190+
Name: workspaceName,
191+
Deleted: includeDeleted,
192+
})
193+
}
172194
if errors.Is(err, sql.ErrNoRows) {
173195
// Do not leak information if the workspace exists or not
174196
httpapi.Forbidden(rw)

coderd/workspaces_test.go

+31-2
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
258258
t.Run("NotFound", func(t *testing.T) {
259259
t.Parallel()
260260
client := coderdtest.New(t, nil)
261-
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something")
261+
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something", codersdk.WorkspaceByOwnerAndNameParams{})
262262
var apiErr *codersdk.Error
263263
require.ErrorAs(t, err, &apiErr)
264264
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
@@ -271,9 +271,38 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
271271
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
272272
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
273273
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
274-
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name)
274+
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{})
275275
require.NoError(t, err)
276276
})
277+
t.Run("Deleted", func(t *testing.T) {
278+
t.Parallel()
279+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
280+
user := coderdtest.CreateFirstUser(t, client)
281+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
282+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
283+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
284+
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
285+
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
286+
287+
// Given:
288+
// We delete the workspace
289+
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
290+
Transition: codersdk.WorkspaceTransitionDelete,
291+
})
292+
require.NoError(t, err, "delete the workspace")
293+
coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
294+
295+
// Then:
296+
// When we call without includes_deleted, we don't expect to get the workspace back
297+
_, err = client.WorkspaceByOwnerAndName(context.Background(), workspace.OwnerName, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{})
298+
require.ErrorContains(t, err, "403")
299+
300+
// Then:
301+
// When we call with includes_deleted, we should get the workspace back
302+
workspaceNew, err := client.WorkspaceByOwnerAndName(context.Background(), workspace.OwnerName, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{IncludeDeleted: true})
303+
require.NoError(t, err)
304+
require.Equal(t, workspace.ID, workspaceNew.ID)
305+
})
277306
}
278307

279308
func TestWorkspaceFilter(t *testing.T) {

codersdk/workspaces.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,17 @@ func (c *Client) Workspaces(ctx context.Context, filter WorkspaceFilter) ([]Work
258258
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
259259
}
260260

261+
type WorkspaceByOwnerAndNameParams struct {
262+
IncludeDeleted bool `json:"include_deleted,omitempty"`
263+
}
264+
261265
// WorkspaceByOwnerAndName returns a workspace by the owner's UUID and the workspace's name.
262-
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, owner string, name string) (Workspace, error) {
263-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s", owner, name), nil)
266+
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, owner string, name string, params WorkspaceByOwnerAndNameParams) (Workspace, error) {
267+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s", owner, name), nil, func(r *http.Request) {
268+
q := r.URL.Query()
269+
q.Set("include_deleted", fmt.Sprintf("%t", params.IncludeDeleted))
270+
r.URL.RawQuery = q.Encode()
271+
})
264272
if err != nil {
265273
return Workspace{}, err
266274
}

site/src/api/typesGenerated.ts

+5
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,11 @@ export interface WorkspaceBuildsRequest extends Pagination {
463463
readonly WorkspaceID: string
464464
}
465465

466+
// From codersdk/workspaces.go:261:6
467+
export interface WorkspaceByOwnerAndNameParams {
468+
readonly include_deleted?: boolean
469+
}
470+
466471
// From codersdk/workspaces.go:219:6
467472
export interface WorkspaceFilter {
468473
readonly organization_id?: string

0 commit comments

Comments
 (0)