mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
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>
This commit is contained in:
Vendored
+1
@@ -83,6 +83,7 @@
|
||||
"workspaceapp",
|
||||
"workspaceapps",
|
||||
"workspacebuilds",
|
||||
"workspacename",
|
||||
"wsconncache",
|
||||
"xerrors",
|
||||
"xstate",
|
||||
|
||||
+2
-2
@@ -49,7 +49,7 @@ func create() *cobra.Command {
|
||||
workspaceName, err = cliui.Prompt(cmd, cliui.PromptOptions{
|
||||
Text: "Specify a name for your workspace:",
|
||||
Validate: func(workspaceName string) error {
|
||||
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName)
|
||||
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName, codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
if err == nil {
|
||||
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func create() *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName)
|
||||
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName, codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
if err == nil {
|
||||
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
|
||||
}
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, identifier stri
|
||||
return codersdk.Workspace{}, xerrors.Errorf("invalid workspace name: %q", identifier)
|
||||
}
|
||||
|
||||
return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name)
|
||||
return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name, codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
}
|
||||
|
||||
// createConfig consumes the global configuration flag to produce a config root.
|
||||
|
||||
@@ -165,10 +165,32 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
owner := httpmw.UserParam(r)
|
||||
workspaceName := chi.URLParam(r, "workspacename")
|
||||
|
||||
includeDeleted := false
|
||||
if s := r.URL.Query().Get("include_deleted"); s != "" {
|
||||
var err error
|
||||
includeDeleted, err = strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", s),
|
||||
Validations: []httpapi.Error{
|
||||
{Field: "include_deleted", Detail: "Must be a valid boolean"},
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
|
||||
OwnerID: owner.ID,
|
||||
Name: workspaceName,
|
||||
})
|
||||
if includeDeleted && errors.Is(err, sql.ErrNoRows) {
|
||||
workspace, err = api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
|
||||
OwnerID: owner.ID,
|
||||
Name: workspaceName,
|
||||
Deleted: includeDeleted,
|
||||
})
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// Do not leak information if the workspace exists or not
|
||||
httpapi.Forbidden(rw)
|
||||
|
||||
@@ -258,7 +258,7 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something")
|
||||
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something", codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
|
||||
@@ -271,9 +271,38 @@ func TestWorkspaceByOwnerAndName(t *testing.T) {
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name)
|
||||
_, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
t.Run("Deleted", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
// Given:
|
||||
// We delete the workspace
|
||||
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||
Transition: codersdk.WorkspaceTransitionDelete,
|
||||
})
|
||||
require.NoError(t, err, "delete the workspace")
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
|
||||
|
||||
// Then:
|
||||
// When we call without includes_deleted, we don't expect to get the workspace back
|
||||
_, err = client.WorkspaceByOwnerAndName(context.Background(), workspace.OwnerName, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{})
|
||||
require.ErrorContains(t, err, "403")
|
||||
|
||||
// Then:
|
||||
// When we call with includes_deleted, we should get the workspace back
|
||||
workspaceNew, err := client.WorkspaceByOwnerAndName(context.Background(), workspace.OwnerName, workspace.Name, codersdk.WorkspaceByOwnerAndNameParams{IncludeDeleted: true})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, workspace.ID, workspaceNew.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceFilter(t *testing.T) {
|
||||
|
||||
+10
-2
@@ -258,9 +258,17 @@ func (c *Client) Workspaces(ctx context.Context, filter WorkspaceFilter) ([]Work
|
||||
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
|
||||
}
|
||||
|
||||
type WorkspaceByOwnerAndNameParams struct {
|
||||
IncludeDeleted bool `json:"include_deleted,omitempty"`
|
||||
}
|
||||
|
||||
// WorkspaceByOwnerAndName returns a workspace by the owner's UUID and the workspace's name.
|
||||
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, owner string, name string) (Workspace, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s", owner, name), nil)
|
||||
func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, owner string, name string, params WorkspaceByOwnerAndNameParams) (Workspace, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s", owner, name), nil, func(r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
q.Set("include_deleted", fmt.Sprintf("%t", params.IncludeDeleted))
|
||||
r.URL.RawQuery = q.Encode()
|
||||
})
|
||||
if err != nil {
|
||||
return Workspace{}, err
|
||||
}
|
||||
|
||||
@@ -463,6 +463,11 @@ export interface WorkspaceBuildsRequest extends Pagination {
|
||||
readonly WorkspaceID: string
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go:261:6
|
||||
export interface WorkspaceByOwnerAndNameParams {
|
||||
readonly include_deleted?: boolean
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go:219:6
|
||||
export interface WorkspaceFilter {
|
||||
readonly organization_id?: string
|
||||
|
||||
Reference in New Issue
Block a user