diff --git a/cli/autostart.go b/cli/autostart.go index 778937cf3d236..8f31520784f2b 100644 --- a/cli/autostart.go +++ b/cli/autostart.go @@ -41,12 +41,8 @@ func autostartShow() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } @@ -93,10 +89,6 @@ func autostartEnable() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } spec := fmt.Sprintf("CRON_TZ=%s %s %s * * %s", autostartTimezone, autostartMinute, autostartHour, autostartDayOfWeek) validSchedule, err := schedule.Weekly(spec) @@ -104,7 +96,7 @@ func autostartEnable() *cobra.Command { return err } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } @@ -142,12 +134,8 @@ func autostartDisable() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/cli/bump.go b/cli/bump.go index 3fea50436fb95..54d1b4bf58d26 100644 --- a/cli/bump.go +++ b/cli/bump.go @@ -43,12 +43,7 @@ func bump() *cobra.Command { if err != nil { return xerrors.Errorf("create client: %w", err) } - organization, err := currentOrganization(cmd, client) - if err != nil { - return xerrors.Errorf("get current org: %w", err) - } - - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return xerrors.Errorf("get workspace: %w", err) } diff --git a/cli/configssh.go b/cli/configssh.go index f511dd07f47da..5ade88f70de6d 100644 --- a/cli/configssh.go +++ b/cli/configssh.go @@ -48,10 +48,6 @@ func configSSH() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } if strings.HasPrefix(sshConfigFile, "~/") { dirname, _ := os.UserHomeDir() sshConfigFile = filepath.Join(dirname, sshConfigFile[2:]) @@ -65,7 +61,9 @@ func configSSH() *cobra.Command { sshConfigContent = sshConfigContent[:startIndex-1] + sshConfigContent[endIndex+len(sshEndToken):] } - workspaces, err := client.WorkspacesByOwner(cmd.Context(), organization.ID, codersdk.Me) + workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{ + Owner: codersdk.Me, + }) if err != nil { return err } diff --git a/cli/create.go b/cli/create.go index aab94ed3a9f67..6852ec4f90449 100644 --- a/cli/create.go +++ b/cli/create.go @@ -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(), organization.ID, codersdk.Me, workspaceName) + _, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName) if err == nil { return xerrors.Errorf("A workspace already exists named %q!", workspaceName) } @@ -75,7 +75,7 @@ func create() *cobra.Command { return xerrors.Errorf("TTL must be at least 1 minute") } - _, err = client.WorkspaceByOwnerAndName(cmd.Context(), organization.ID, codersdk.Me, workspaceName) + _, err = client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, workspaceName) if err == nil { return xerrors.Errorf("A workspace already exists named %q!", workspaceName) } diff --git a/cli/delete.go b/cli/delete.go index d1f13ea08c540..bed17ee9ff483 100644 --- a/cli/delete.go +++ b/cli/delete.go @@ -30,11 +30,7 @@ func delete() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/cli/portforward.go b/cli/portforward.go index d0e1897802f95..de8f81ea2321b 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -70,12 +70,8 @@ func portForward() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, agent, err := getWorkspaceAndAgent(cmd, client, organization.ID, codersdk.Me, args[0], false) + workspace, agent, err := getWorkspaceAndAgent(cmd, client, codersdk.Me, args[0], false) if err != nil { return err } diff --git a/cli/root.go b/cli/root.go index ae65115aa12a0..d2fc267221594 100644 --- a/cli/root.go +++ b/cli/root.go @@ -9,7 +9,6 @@ import ( "golang.org/x/xerrors" - "github.com/google/uuid" "github.com/kirsle/configdir" "github.com/mattn/go-isatty" "github.com/spf13/cobra" @@ -180,7 +179,7 @@ func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (codersdk. // namedWorkspace fetches and returns a workspace by an identifier, which may be either // a bare name (for a workspace owned by the current user) or a "user/workspace" combination, // where user is either a username or UUID. -func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, orgID uuid.UUID, identifier string) (codersdk.Workspace, error) { +func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, identifier string) (codersdk.Workspace, error) { parts := strings.Split(identifier, "/") var owner, name string @@ -195,7 +194,7 @@ func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, orgID uuid.UUID return codersdk.Workspace{}, xerrors.Errorf("invalid workspace name: %q", identifier) } - return client.WorkspaceByOwnerAndName(cmd.Context(), orgID, owner, name) + return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name) } // createConfig consumes the global configuration flag to produce a config root. diff --git a/cli/server.go b/cli/server.go index 897653b01914c..02af6a822f112 100644 --- a/cli/server.go +++ b/cli/server.go @@ -412,11 +412,9 @@ func server() *cobra.Command { "Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit")) if dev { - organizations, err := client.OrganizationsByUser(cmd.Context(), codersdk.Me) - if err != nil { - return xerrors.Errorf("get organizations: %w", err) - } - workspaces, err := client.WorkspacesByOwner(cmd.Context(), organizations[0].ID, codersdk.Me) + workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{ + Owner: codersdk.Me, + }) if err != nil { return xerrors.Errorf("get workspaces: %w", err) } diff --git a/cli/show.go b/cli/show.go index e2245922821f0..2b1213adc1f21 100644 --- a/cli/show.go +++ b/cli/show.go @@ -18,11 +18,7 @@ func show() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return xerrors.Errorf("get workspace: %w", err) } diff --git a/cli/ssh.go b/cli/ssh.go index 48aaadd798f4e..10736901433d9 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -48,10 +48,6 @@ func ssh() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } if shuffle { err := cobra.ExactArgs(0)(cmd, args) @@ -65,7 +61,7 @@ func ssh() *cobra.Command { } } - workspace, agent, err := getWorkspaceAndAgent(cmd, client, organization.ID, codersdk.Me, args[0], shuffle) + workspace, agent, err := getWorkspaceAndAgent(cmd, client, codersdk.Me, args[0], shuffle) if err != nil { return err } @@ -185,7 +181,7 @@ func ssh() *cobra.Command { // getWorkspaceAgent returns the workspace and agent selected using either the // `[.]` syntax via `in` or picks a random workspace and agent // if `shuffle` is true. -func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, orgID uuid.UUID, userID string, in string, shuffle bool) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive +func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, userID string, in string, shuffle bool) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive ctx := cmd.Context() var ( @@ -194,7 +190,9 @@ func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, orgID uui err error ) if shuffle { - workspaces, err := client.WorkspacesByOwner(cmd.Context(), orgID, userID) + workspaces, err := client.Workspaces(cmd.Context(), codersdk.WorkspaceFilter{ + Owner: codersdk.Me, + }) if err != nil { return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err } @@ -207,7 +205,7 @@ func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, orgID uui return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err } } else { - workspace, err = namedWorkspace(cmd, client, orgID, workspaceParts[0]) + workspace, err = namedWorkspace(cmd, client, workspaceParts[0]) if err != nil { return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err } diff --git a/cli/start.go b/cli/start.go index 2fc142694986d..4a645cf08dbb7 100644 --- a/cli/start.go +++ b/cli/start.go @@ -28,11 +28,7 @@ func start() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/cli/state.go b/cli/state.go index 8cd7828532476..385df01d7754c 100644 --- a/cli/state.go +++ b/cli/state.go @@ -30,12 +30,7 @@ func statePull() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } @@ -76,12 +71,7 @@ func statePush() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/cli/stop.go b/cli/stop.go index 05738babebb76..2f2f6fc0f7a81 100644 --- a/cli/stop.go +++ b/cli/stop.go @@ -28,11 +28,7 @@ func stop() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/cli/ttl.go b/cli/ttl.go index 4a7e4306ab67b..cce6ef84a9345 100644 --- a/cli/ttl.go +++ b/cli/ttl.go @@ -39,12 +39,8 @@ func ttlShow() *cobra.Command { if err != nil { return xerrors.Errorf("create client: %w", err) } - organization, err := currentOrganization(cmd, client) - if err != nil { - return xerrors.Errorf("get current org: %w", err) - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return xerrors.Errorf("get workspace: %w", err) } @@ -72,12 +68,8 @@ func ttlset() *cobra.Command { if err != nil { return xerrors.Errorf("create client: %w", err) } - organization, err := currentOrganization(cmd, client) - if err != nil { - return xerrors.Errorf("get current org: %w", err) - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return xerrors.Errorf("get workspace: %w", err) } @@ -120,12 +112,8 @@ func ttlunset() *cobra.Command { if err != nil { return xerrors.Errorf("create client: %w", err) } - organization, err := currentOrganization(cmd, client) - if err != nil { - return xerrors.Errorf("get current org: %w", err) - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return xerrors.Errorf("get workspace: %w", err) } diff --git a/cli/update.go b/cli/update.go index 8b4a3e5d9a371..288d9dd570162 100644 --- a/cli/update.go +++ b/cli/update.go @@ -19,11 +19,7 @@ func update() *cobra.Command { if err != nil { return err } - organization, err := currentOrganization(cmd, client) - if err != nil { - return err - } - workspace, err := namedWorkspace(cmd, client, organization.ID, args[0]) + workspace, err := namedWorkspace(cmd, client, args[0]) if err != nil { return err } diff --git a/coderd/coderd.go b/coderd/coderd.go index d42e3e931a05b..74e41b828477c 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -152,15 +152,7 @@ func New(options *Options) *API { r.Get("/", api.templatesByOrganization) r.Get("/{templatename}", api.templateByOrganizationAndName) }) - r.Route("/workspaces", func(r chi.Router) { - r.Post("/", api.postWorkspacesByOrganization) - r.Get("/", api.workspacesByOrganization) - r.Route("/{user}", func(r chi.Router) { - r.Use(httpmw.ExtractUserParam(options.Database)) - r.Get("/{workspacename}", api.workspaceByOwnerAndName) - r.Get("/", api.workspacesByOwner) - }) - }) + r.Post("/workspaces", api.postWorkspacesByOrganization) r.Route("/members", func(r chi.Router) { r.Get("/roles", api.assignableOrgRoles) r.Route("/{user}", func(r chi.Router) { @@ -259,6 +251,7 @@ func New(options *Options) *API { r.Get("/", api.organizationsByUser) r.Get("/{organizationname}", api.organizationByUserAndName) }) + r.Get("/workspace/{workspacename}", api.workspaceByOwnerAndName) r.Get("/gitsshkey", api.gitSSHKey) r.Put("/gitsshkey", api.regenerateGitSSHKey) }) diff --git a/coderd/coderd_test.go b/coderd/coderd_test.go index a3135ebc9249d..6a2a9f6a9eed4 100644 --- a/coderd/coderd_test.go +++ b/coderd/coderd_test.go @@ -147,22 +147,16 @@ func TestAuthorizeAllEndpoints(t *testing.T) { "GET:/api/v2/workspaceagents/{workspaceagent}/turn": {NoAuthorize: true}, // These endpoints have more assertions. This is good, add more endpoints to assert if you can! - "GET:/api/v2/organizations/{organization}": {AssertObject: rbac.ResourceOrganization.InOrg(admin.OrganizationID)}, - "GET:/api/v2/users/{user}/organizations": {StatusCode: http.StatusOK, AssertObject: rbac.ResourceOrganization}, - "GET:/api/v2/users/{user}/workspaces": {StatusCode: http.StatusOK, AssertObject: rbac.ResourceWorkspace}, - "GET:/api/v2/organizations/{organization}/workspaces/{user}": {StatusCode: http.StatusOK, AssertObject: rbac.ResourceWorkspace}, - "GET:/api/v2/organizations/{organization}/workspaces/{user}/{workspace}": { - AssertObject: rbac.ResourceWorkspace.InOrg(organization.ID).WithID(workspace.ID.String()).WithOwner(workspace.OwnerID.String()), - }, - "GET:/api/v2/workspaces/{workspace}/builds/{workspacebuildname}": { + "GET:/api/v2/organizations/{organization}": {AssertObject: rbac.ResourceOrganization.InOrg(admin.OrganizationID)}, + "GET:/api/v2/users/{user}/organizations": {StatusCode: http.StatusOK, AssertObject: rbac.ResourceOrganization}, + "GET:/api/v2/users/{user}/workspace/{workspacename}": { + AssertObject: rbac.ResourceWorkspace, AssertAction: rbac.ActionRead, - AssertObject: workspaceRBACObj, }, - "GET:/api/v2/organizations/{organization}/workspaces/{user}/{workspacename}": { + "GET:/api/v2/workspaces/{workspace}/builds/{workspacebuildname}": { AssertAction: rbac.ActionRead, AssertObject: workspaceRBACObj, }, - "GET:/api/v2/organizations/{organization}/workspaces": {StatusCode: http.StatusOK, AssertObject: rbac.ResourceWorkspace}, "GET:/api/v2/workspacebuilds/{workspacebuild}": { AssertAction: rbac.ActionRead, AssertObject: workspaceRBACObj, diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 744dbd553df45..724aaa0a52be3 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -100,35 +100,6 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, owner)) } -func (api *API) workspacesByOrganization(rw http.ResponseWriter, r *http.Request) { - organization := httpmw.OrganizationParam(r) - workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{ - OrganizationID: organization.ID, - Deleted: false, - }) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get workspaces: %s", err), - }) - return - } - - // Rbac filter - workspaces = AuthorizeFilter(api, r, rbac.ActionRead, workspaces) - - apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("convert workspaces: %s", err), - }) - return - } - httpapi.Write(rw, http.StatusOK, apiWorkspaces) -} - // workspaces returns all workspaces a user can read. // Optional filters with query params func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { @@ -189,38 +160,8 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { httpapi.Write(rw, http.StatusOK, apiWorkspaces) } -func (api *API) workspacesByOwner(rw http.ResponseWriter, r *http.Request) { - owner := httpmw.UserParam(r) - workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{ - OwnerID: owner.ID, - Deleted: false, - }) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get workspaces: %s", err), - }) - return - } - - // Only return workspaces the user can read - workspaces = AuthorizeFilter(api, r, rbac.ActionRead, workspaces) - - apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("convert workspaces: %s", err), - }) - return - } - httpapi.Write(rw, http.StatusOK, apiWorkspaces) -} - func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) { owner := httpmw.UserParam(r) - organization := httpmw.OrganizationParam(r) workspaceName := chi.URLParam(r, "workspacename") workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{ @@ -238,14 +179,6 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) }) return } - - if workspace.OrganizationID != organization.ID { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ - Message: fmt.Sprintf("workspace is not owned by organization %q", organization.Name), - }) - return - } - if !api.Authorize(rw, r, rbac.ActionRead, workspace) { return } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 9ee42fd12ea81..377ef6ed1ab74 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -210,107 +210,15 @@ func TestPostWorkspacesByOrganization(t *testing.T) { }) } -func TestWorkspacesByOrganization(t *testing.T) { - t.Parallel() - t.Run("ListEmpty", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - _, err := client.WorkspacesByOrganization(context.Background(), user.OrganizationID) - require.NoError(t, err) - }) - t.Run("List", 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) - ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) - workspaces, err := client.WorkspacesByOrganization(context.Background(), user.OrganizationID) - require.NoError(t, err) - require.Len(t, workspaces, 1) - }) -} - -func TestWorkspacesByOwner(t *testing.T) { - t.Parallel() - t.Run("ListEmpty", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - _, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, codersdk.Me) - require.NoError(t, err) - }) - - t.Run("ListMine", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) - user := coderdtest.CreateFirstUser(t, client) - me, err := client.User(context.Background(), codersdk.Me) - require.NoError(t, err) - - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - _ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - - // Create noise workspace that should be filtered out - other := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - _ = coderdtest.CreateWorkspace(t, other, user.OrganizationID, template.ID) - - // Use a username - workspaces, err := client.Workspaces(context.Background(), codersdk.WorkspaceFilter{ - OrganizationID: user.OrganizationID, - Owner: me.Username, - }) - require.NoError(t, err) - require.Len(t, workspaces, 1) - }) - - t.Run("ListName", 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) - w := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - - // Create noise workspace that should be filtered out - _ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - - // Use name filter - workspaces, err := client.Workspaces(context.Background(), codersdk.WorkspaceFilter{ - Name: w.Name, - }) - require.NoError(t, err) - require.Len(t, workspaces, 1) - - // Create same name workspace that should be included - other := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - _ = coderdtest.CreateWorkspace(t, other, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) { cwr.Name = w.Name }) - - workspaces, err = client.Workspaces(context.Background(), codersdk.WorkspaceFilter{ - Name: w.Name, - }) - require.NoError(t, err) - require.Len(t, workspaces, 2) - }) -} - func TestWorkspaceByOwnerAndName(t *testing.T) { t.Parallel() t.Run("NotFound", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - _, err := client.WorkspaceByOwnerAndName(context.Background(), user.OrganizationID, codersdk.Me, "something") + _, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something") var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusForbidden, apiErr.StatusCode()) + require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) t.Run("Get", func(t *testing.T) { t.Parallel() @@ -320,7 +228,7 @@ 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(), user.OrganizationID, codersdk.Me, workspace.Name) + _, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, workspace.Name) require.NoError(t, err) }) } @@ -440,7 +348,9 @@ func TestPostWorkspaceBuild(t *testing.T) { require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber) coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID) - workspaces, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, user.UserID.String()) + workspaces, err := client.Workspaces(context.Background(), codersdk.WorkspaceFilter{ + Owner: user.UserID.String(), + }) require.NoError(t, err) require.Len(t, workspaces, 0) }) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index c252acc524eff..1e28760e1b95e 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -200,51 +200,3 @@ func (c *Client) CreateWorkspace(ctx context.Context, organizationID uuid.UUID, var workspace Workspace return workspace, json.NewDecoder(res.Body).Decode(&workspace) } - -// WorkspacesByOrganization returns all workspaces in the specified organization. -func (c *Client) WorkspacesByOrganization(ctx context.Context, organizationID uuid.UUID) ([]Workspace, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces", organizationID), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - - var workspaces []Workspace - return workspaces, json.NewDecoder(res.Body).Decode(&workspaces) -} - -// WorkspacesByOwner returns all workspaces contained in the organization owned by the user. -func (c *Client) WorkspacesByOwner(ctx context.Context, organizationID uuid.UUID, user string) ([]Workspace, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces/%s", organizationID, user), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - - var workspaces []Workspace - return workspaces, json.NewDecoder(res.Body).Decode(&workspaces) -} - -// WorkspaceByOwnerAndName returns a workspace by the owner's UUID and the workspace's name. -func (c *Client) WorkspaceByOwnerAndName(ctx context.Context, organization uuid.UUID, owner string, name string) (Workspace, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/workspaces/%s/%s", organization, owner, name), nil) - if err != nil { - return Workspace{}, err - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return Workspace{}, readBodyAsError(res) - } - - var workspace Workspace - return workspace, json.NewDecoder(res.Body).Decode(&workspace) -} diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index dbe3ba1b8574c..0d9aadccedca1 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -238,3 +238,19 @@ func (c *Client) Workspaces(ctx context.Context, filter WorkspaceFilter) ([]Work var workspaces []Workspace return workspaces, json.NewDecoder(res.Body).Decode(&workspaces) } + +// 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) + if err != nil { + return Workspace{}, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return Workspace{}, readBodyAsError(res) + } + + var workspace Workspace + return workspace, json.NewDecoder(res.Body).Decode(&workspace) +} diff --git a/site/src/api/api.ts b/site/src/api/api.ts index a02bcdcbf36b4..c86748b2b64cd 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -139,13 +139,10 @@ export const getWorkspaces = async (filter?: TypesGen.WorkspaceFilter): Promise< } export const getWorkspaceByOwnerAndName = async ( - organizationID: string, username = "me", workspaceName: string, ): Promise => { - const response = await axios.get( - `/api/v2/organizations/${organizationID}/workspaces/${username}/${workspaceName}`, - ) + const response = await axios.get(`/api/v2/users/${username}/workspace/${workspaceName}`) return response.data } diff --git a/site/src/pages/TerminalPage/TerminalPage.test.tsx b/site/src/pages/TerminalPage/TerminalPage.test.tsx index 0c28264bdca33..3a2176231ab37 100644 --- a/site/src/pages/TerminalPage/TerminalPage.test.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.test.tsx @@ -54,25 +54,10 @@ describe("TerminalPage", () => { history.push(`/some-user/${MockWorkspace.name}/terminal`) }) - it("shows an error if fetching organizations fails", async () => { - // Given - server.use( - rest.get("/api/v2/users/me/organizations", async (req, res, ctx) => { - return res(ctx.status(500), ctx.json({ message: "nope" })) - }), - ) - - // When - const { container } = renderTerminal() - - // Then - await expectTerminalText(container, Language.organizationsErrorMessagePrefix) - }) - it("shows an error if fetching workspace fails", async () => { // Given server.use( - rest.get("/api/v2/organizations/:organizationId/workspaces/:userName/:workspaceName", (req, res, ctx) => { + rest.get("/api/v2/users/:userId/workspace/:workspaceName", (req, res, ctx) => { return res(ctx.status(500), ctx.json({ id: "workspace-id" })) }), ) diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx index 9b1b4aa6432f9..55460ecb615b8 100644 --- a/site/src/pages/TerminalPage/TerminalPage.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.tsx @@ -11,7 +11,6 @@ import { MONOSPACE_FONT_FAMILY } from "../../theme/constants" import { terminalMachine } from "../../xServices/terminal/terminalXService" export const Language = { - organizationsErrorMessagePrefix: "Unable to fetch organizations: ", workspaceErrorMessagePrefix: "Unable to fetch workspace: ", workspaceAgentErrorMessagePrefix: "Unable to fetch workspace agent: ", websocketErrorMessagePrefix: "WebSocket failed: ", @@ -58,8 +57,7 @@ const TerminalPage: FC<{ }) const isConnected = terminalState.matches("connected") const isDisconnected = terminalState.matches("disconnected") - const { organizationsError, workspaceError, workspaceAgentError, workspaceAgent, websocketError } = - terminalState.context + const { workspaceError, workspaceAgentError, workspaceAgent, websocketError } = terminalState.context // Create the terminal! useEffect(() => { @@ -145,9 +143,6 @@ const TerminalPage: FC<{ terminal.options = { disableStdin: true, } - if (organizationsError instanceof Error) { - terminal.writeln(Language.organizationsErrorMessagePrefix + organizationsError.message) - } if (workspaceError instanceof Error) { terminal.writeln(Language.workspaceErrorMessagePrefix + workspaceError.message) } @@ -180,17 +175,7 @@ const TerminalPage: FC<{ width: terminal.cols, }, }) - }, [ - workspaceError, - organizationsError, - workspaceAgentError, - websocketError, - workspaceAgent, - terminal, - fitAddon, - isConnected, - sendEvent, - ]) + }, [workspaceError, workspaceAgentError, websocketError, workspaceAgent, terminal, fitAddon, isConnected, sendEvent]) return ( <> diff --git a/site/src/testHelpers/handlers.ts b/site/src/testHelpers/handlers.ts index 35419d8e333e5..b77dd09c11aca 100644 --- a/site/src/testHelpers/handlers.ts +++ b/site/src/testHelpers/handlers.ts @@ -42,9 +42,6 @@ export const handlers = [ rest.post("/api/v2/users", async (req, res, ctx) => { return res(ctx.status(200), ctx.json(M.MockUser)) }), - rest.post("/api/v2/users/me/workspaces", async (req, res, ctx) => { - return res(ctx.status(200), ctx.json(M.MockWorkspace)) - }), rest.get("/api/v2/users/me/organizations", (req, res, ctx) => { return res(ctx.status(200), ctx.json([M.MockOrganization])) }), @@ -83,23 +80,14 @@ export const handlers = [ rest.get("/api/v2/users/:userId/gitsshkey", async (req, res, ctx) => { return res(ctx.status(200), ctx.json(M.MockGitSSHKey)) }), + rest.get("/api/v2/users/:userId/workspace/:workspaceName", async (req, res, ctx) => { + return res(ctx.status(200), ctx.json(M.MockWorkspace)) + }), // workspaces rest.get("/api/v2/workspaces", async (req, res, ctx) => { return res(ctx.status(200), ctx.json([M.MockWorkspace])) }), - rest.get("/api/v2/organizations/:organizationId/workspaces/:userName/:workspaceName", (req, res, ctx) => { - if (req.params.workspaceName !== M.MockWorkspace.name) { - return res( - ctx.status(404), - ctx.json({ - message: "workspace not found", - }), - ) - } else { - return res(ctx.status(200), ctx.json(M.MockWorkspace)) - } - }), rest.get("/api/v2/workspaces/:workspaceId", async (req, res, ctx) => { return res(ctx.status(200), ctx.json(M.MockWorkspace)) }), diff --git a/site/src/xServices/terminal/terminalXService.ts b/site/src/xServices/terminal/terminalXService.ts index 056a7ddf7cafe..f3b6f6169c21f 100644 --- a/site/src/xServices/terminal/terminalXService.ts +++ b/site/src/xServices/terminal/terminalXService.ts @@ -4,8 +4,6 @@ import * as Types from "../../api/types" import * as TypesGen from "../../api/typesGenerated" export interface TerminalContext { - organizationsError?: Error | unknown - organizations?: TypesGen.Organization[] workspaceError?: Error | unknown workspace?: TypesGen.Workspace workspaceAgent?: TypesGen.WorkspaceAgent @@ -37,9 +35,6 @@ export const terminalMachine = context: {} as TerminalContext, events: {} as TerminalEvent, services: {} as { - getOrganizations: { - data: TypesGen.Organization[] - } getWorkspace: { data: TypesGen.Workspace } @@ -52,27 +47,8 @@ export const terminalMachine = }, }, id: "terminalState", - initial: "gettingOrganizations", + initial: "gettingWorkspace", states: { - gettingOrganizations: { - invoke: { - src: "getOrganizations", - id: "getOrganizations", - onDone: [ - { - actions: ["assignOrganizations", "clearOrganizationsError"], - target: "gettingWorkspace", - }, - ], - onError: [ - { - actions: "assignOrganizationsError", - target: "disconnected", - }, - ], - }, - tags: "loading", - }, gettingWorkspace: { invoke: { src: "getWorkspace", @@ -145,7 +121,7 @@ export const terminalMachine = on: { CONNECT: { actions: "assignConnection", - target: "gettingOrganizations", + target: "gettingWorkspace", }, }, }, @@ -153,12 +129,11 @@ export const terminalMachine = }, { services: { - getOrganizations: API.getOrganizations, getWorkspace: async (context) => { - if (!context.organizations || !context.workspaceName) { - throw new Error("organizations or workspace name not set") + if (!context.workspaceName) { + throw new Error("workspace name not set") } - return API.getWorkspaceByOwnerAndName(context.organizations[0].id, context.username, context.workspaceName) + return API.getWorkspaceByOwnerAndName(context.username, context.workspaceName) }, getWorkspaceAgent: async (context) => { if (!context.workspace || !context.workspaceName) { @@ -220,16 +195,6 @@ export const terminalMachine = reconnection: event.reconnection ?? context.reconnection, workspaceName: event.workspaceName ?? context.workspaceName, })), - assignOrganizations: assign({ - organizations: (_, event) => event.data, - }), - assignOrganizationsError: assign({ - organizationsError: (_, event) => event.data, - }), - clearOrganizationsError: assign((context) => ({ - ...context, - organizationsError: undefined, - })), assignWorkspace: assign({ workspace: (_, event) => event.data, }),