From 7924bb2a56a670e053c3089aa896050d67363222 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Fri, 15 Dec 2023 13:38:47 -0500 Subject: [PATCH] feat!: move workspace renames behind flag, disable by default (#11189) --- cli/rename_test.go | 2 +- cli/server.go | 1 + cli/testdata/coder_list_--output_json.golden | 3 +- cli/testdata/coder_server_--help.golden | 5 +++ cli/testdata/server-config.yaml.golden | 4 +++ coderd/apidoc/docs.go | 6 ++++ coderd/apidoc/swagger.json | 6 ++++ coderd/coderd.go | 3 +- coderd/coderdtest/coderdtest.go | 2 ++ coderd/workspaces.go | 28 ++++++++++++---- coderd/workspaces_test.go | 33 +++++++++++++++++-- codersdk/deployment.go | 10 ++++++ codersdk/workspaces.go | 1 + docs/api/general.md | 1 + docs/api/schemas.md | 6 ++++ docs/api/workspaces.md | 5 +++ docs/cli/server.md | 11 +++++++ .../cli/testdata/coder_server_--help.golden | 5 +++ site/src/api/typesGenerated.ts | 2 ++ .../WorkspaceSettingsForm.tsx | 33 ++++++++++++++----- .../WorkspaceSettingsPage.test.tsx | 20 ++++++++++- .../WorkspaceSettingsPageView.stories.tsx | 6 ++++ site/src/testHelpers/entities.ts | 1 + 23 files changed, 173 insertions(+), 21 deletions(-) diff --git a/cli/rename_test.go b/cli/rename_test.go index 5a08d29c5a..adbe946c0c 100644 --- a/cli/rename_test.go +++ b/cli/rename_test.go @@ -15,7 +15,7 @@ import ( func TestRename(t *testing.T) { t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, AllowWorkspaceRenames: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) diff --git a/cli/server.go b/cli/server.go index 214c4d3a94..2caf9f8912 100644 --- a/cli/server.go +++ b/cli/server.go @@ -583,6 +583,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. HostnamePrefix: vals.SSHConfig.DeploymentName.String(), SSHConfigOptions: configSSHOptions, }, + AllowWorkspaceRenames: vals.AllowWorkspaceRenames.Value(), } if httpServers.TLSConfig != nil { options.TLSCertificates = httpServers.TLSConfig.Certificates diff --git a/cli/testdata/coder_list_--output_json.golden b/cli/testdata/coder_list_--output_json.golden index fb6bea96e8..55b6606948 100644 --- a/cli/testdata/coder_list_--output_json.golden +++ b/cli/testdata/coder_list_--output_json.golden @@ -59,6 +59,7 @@ "healthy": true, "failing_agents": [] }, - "automatic_updates": "never" + "automatic_updates": "never", + "allow_renames": false } ] diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index 80849f1904..5f8cd85a84 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -14,6 +14,11 @@ SUBCOMMANDS: PostgreSQL deployment. OPTIONS: + --allow-workspace-renames bool, $CODER_ALLOW_WORKSPACE_RENAMES (default: false) + DEPRECATED: Allow users to rename their workspaces. Use only for + temporary compatibility reasons, this will be removed in a future + release. + --cache-dir string, $CODER_CACHE_DIRECTORY (default: [cache dir]) The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index ef071c8b29..5abf74e675 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -462,3 +462,7 @@ userQuietHoursSchedule: # change their quiet hours schedule and the site default is always used. # (default: true, type: bool) allowCustomQuietHours: true +# DEPRECATED: Allow users to rename their workspaces. Use only for temporary +# compatibility reasons, this will be removed in a future release. +# (default: false, type: bool) +allowWorkspaceRenames: false diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 569ade9033..0bf86fc999 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8558,6 +8558,9 @@ const docTemplate = `{ "agent_stat_refresh_interval": { "type": "integer" }, + "allow_workspace_renames": { + "type": "boolean" + }, "autobuild_poll_interval": { "type": "integer" }, @@ -11443,6 +11446,9 @@ const docTemplate = `{ "codersdk.Workspace": { "type": "object", "properties": { + "allow_renames": { + "type": "boolean" + }, "automatic_updates": { "enum": [ "always", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 30d83cc50c..2187fd7a05 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7642,6 +7642,9 @@ "agent_stat_refresh_interval": { "type": "integer" }, + "allow_workspace_renames": { + "type": "boolean" + }, "autobuild_poll_interval": { "type": "integer" }, @@ -10372,6 +10375,9 @@ "codersdk.Workspace": { "type": "object", "properties": { + "allow_renames": { + "type": "boolean" + }, "automatic_updates": { "enum": ["always", "never"], "allOf": [ diff --git a/coderd/coderd.go b/coderd/coderd.go index 8dd8cf3693..0e7421aa2d 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -179,7 +179,8 @@ type Options struct { // This janky function is used in telemetry to parse fields out of the raw // JWT. It needs to be passed through like this because license parsing is // under the enterprise license, and can't be imported into AGPL. - ParseLicenseClaims func(rawJWT string) (email string, trial bool, err error) + ParseLicenseClaims func(rawJWT string) (email string, trial bool, err error) + AllowWorkspaceRenames bool } // @title Coder API diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 4d1f832ea9..8de475c784 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -144,6 +144,7 @@ type Options struct { StatsBatcher *batchstats.Batcher WorkspaceAppsStatsCollectorOptions workspaceapps.StatsCollectorOptions + AllowWorkspaceRenames bool } // New constructs a codersdk client connected to an in-memory API instance. @@ -449,6 +450,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can HealthcheckRefresh: options.HealthcheckRefresh, StatsBatcher: options.StatsBatcher, WorkspaceAppsStatsCollectorOptions: options.WorkspaceAppsStatsCollectorOptions, + AllowWorkspaceRenames: options.AllowWorkspaceRenames, } } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 238780b1ec..8d4cba7a40 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -106,6 +106,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { data.builds[0], data.templates[0], ownerName, + api.Options.AllowWorkspaceRenames, )) } @@ -277,6 +278,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) data.builds[0], data.templates[0], ownerName, + api.Options.AllowWorkspaceRenames, )) } @@ -585,6 +587,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req apiBuild, template, member.Username, + api.Options.AllowWorkspaceRenames, )) } @@ -628,6 +631,12 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) { // patched in the future, it's enough if one changes. name := workspace.Name if req.Name != "" || req.Name != workspace.Name { + if !api.Options.AllowWorkspaceRenames { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Workspace renames are not allowed.", + }) + return + } name = req.Name } @@ -917,6 +926,7 @@ func (api *API) putWorkspaceDormant(rw http.ResponseWriter, r *http.Request) { data.builds[0], data.templates[0], ownerName, + api.Options.AllowWorkspaceRenames, )) } @@ -1242,6 +1252,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { data.builds[0], data.templates[0], ownerName, + api.Options.AllowWorkspaceRenames, ), }) } @@ -1293,9 +1304,10 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { } type workspaceData struct { - templates []database.Template - builds []codersdk.WorkspaceBuild - users []database.User + templates []database.Template + builds []codersdk.WorkspaceBuild + users []database.User + allowRenames bool } // workspacesData only returns the data the caller can access. If the caller @@ -1347,9 +1359,10 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa } return workspaceData{ - templates: templates, - builds: apiBuilds, - users: data.users, + templates: templates, + builds: apiBuilds, + users: data.users, + allowRenames: api.Options.AllowWorkspaceRenames, }, nil } @@ -1392,6 +1405,7 @@ func convertWorkspaces(workspaces []database.Workspace, data workspaceData) ([]c build, template, owner.Username, + data.allowRenames, )) } return apiWorkspaces, nil @@ -1402,6 +1416,7 @@ func convertWorkspace( workspaceBuild codersdk.WorkspaceBuild, template database.Template, ownerName string, + allowRenames bool, ) codersdk.Workspace { var autostartSchedule *string if workspace.AutostartSchedule.Valid { @@ -1456,6 +1471,7 @@ func convertWorkspace( FailingAgents: failingAgents, }, AutomaticUpdates: codersdk.AutomaticUpdates(workspace.AutomaticUpdates), + AllowRenames: allowRenames, } } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 9c6129b537..69ab38d8ca 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -100,7 +100,10 @@ func TestWorkspace(t *testing.T) { t.Run("Rename", func(t *testing.T) { t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + AllowWorkspaceRenames: true, + }) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -134,6 +137,29 @@ func TestWorkspace(t *testing.T) { require.Error(t, err, "workspace rename should have failed") }) + t.Run("RenameDisabled", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + AllowWorkspaceRenames: false, + }) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + ws1 := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws1.LatestBuild.ID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancel() + + want := "new-name" + err := client.UpdateWorkspace(ctx, ws1.ID, codersdk.UpdateWorkspaceRequest{ + Name: want, + }) + require.ErrorContains(t, err, "Workspace renames are not allowed") + }) + t.Run("TemplateProperties", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) @@ -2152,7 +2178,10 @@ func TestUpdateWorkspaceAutomaticUpdates_NotFound(t *testing.T) { func TestWorkspaceWatcher(t *testing.T) { t.Parallel() - client, closeFunc := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + client, closeFunc := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + AllowWorkspaceRenames: true, + }) defer closeFunc.Close() user := coderdtest.CreateFirstUser(t, client) authToken := uuid.NewString() diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 1e8a4c93cc..54b67f1279 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -181,6 +181,7 @@ type DeploymentValues struct { EnableTerraformDebugMode clibase.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"` UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"` WebTerminalRenderer clibase.String `json:"web_terminal_renderer,omitempty" typescript:",notnull"` + AllowWorkspaceRenames clibase.Bool `json:"allow_workspace_renames,omitempty" typescript:",notnull"` Healthcheck HealthcheckConfig `json:"healthcheck,omitempty" typescript:",notnull"` Config clibase.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"` @@ -1842,6 +1843,15 @@ Write out the current server config as YAML to stdout.`, Group: &deploymentGroupClient, YAML: "webTerminalRenderer", }, + { + Name: "Allow Workspace Renames", + Description: "DEPRECATED: Allow users to rename their workspaces. Use only for temporary compatibility reasons, this will be removed in a future release.", + Flag: "allow-workspace-renames", + Env: "CODER_ALLOW_WORKSPACE_RENAMES", + Default: "false", + Value: &c.AllowWorkspaceRenames, + YAML: "allowWorkspaceRenames", + }, // Healthcheck Options { Name: "Health Check Refresh", diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index 307bbdb0d3..59dede325e 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -57,6 +57,7 @@ type Workspace struct { // what is causing an unhealthy status. Health WorkspaceHealth `json:"health"` AutomaticUpdates AutomaticUpdates `json:"automatic_updates" enums:"always,never"` + AllowRenames bool `json:"allow_renames"` } func (w Workspace) FullName() string { diff --git a/docs/api/general.md b/docs/api/general.md index 043913bbf2..65d9d4badd 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -153,6 +153,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "user": {} }, "agent_stat_refresh_interval": 0, + "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, "cache_directory": "string", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 21eb6dba26..991f57bccb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2083,6 +2083,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "user": {} }, "agent_stat_refresh_interval": 0, + "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, "cache_directory": "string", @@ -2460,6 +2461,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "user": {} }, "agent_stat_refresh_interval": 0, + "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, "cache_directory": "string", @@ -2732,6 +2734,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `address` | [clibase.HostPort](#clibasehostport) | false | | Address Use HTTPAddress or TLS.Address instead. | | `agent_fallback_troubleshooting_url` | [clibase.URL](#clibaseurl) | false | | | | `agent_stat_refresh_interval` | integer | false | | | +| `allow_workspace_renames` | boolean | false | | | | `autobuild_poll_interval` | integer | false | | | | `browser_only` | boolean | false | | | | `cache_directory` | string | false | | | @@ -5764,6 +5767,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| ```json { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", @@ -5943,6 +5947,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | Name | Type | Required | Restrictions | Description | | ------------------------------------------- | ------------------------------------------------------ | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `allow_renames` | boolean | false | | | | `automatic_updates` | [codersdk.AutomaticUpdates](#codersdkautomaticupdates) | false | | | | `autostart_schedule` | string | false | | | | `created_at` | string | false | | | @@ -7025,6 +7030,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "count": 0, "workspaces": [ { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 7d0f7973d4..f825ba5f7c 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -47,6 +47,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member ```json { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", @@ -257,6 +258,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam ```json { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", @@ -470,6 +472,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "count": 0, "workspaces": [ { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", @@ -677,6 +680,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ ```json { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", @@ -1003,6 +1007,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ ```json { + "allow_renames": true, "automatic_updates": "always", "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", diff --git a/docs/cli/server.md b/docs/cli/server.md index 34fe77b3a5..a0c4aad6e9 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -42,6 +42,17 @@ The URL that users will use to access the Coder deployment. Allow users to set their own quiet hours schedule for workspaces to stop in (depending on template autostop requirement settings). If false, users can't change their quiet hours schedule and the site default is always used. +### --allow-workspace-renames + +| | | +| ----------- | ------------------------------------------- | +| Type | bool | +| Environment | $CODER_ALLOW_WORKSPACE_RENAMES | +| YAML | allowWorkspaceRenames | +| Default | false | + +DEPRECATED: Allow users to rename their workspaces. Use only for temporary compatibility reasons, this will be removed in a future release. + ### --block-direct-connections | | | diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index ad404185f0..0df1bec5bb 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -15,6 +15,11 @@ SUBCOMMANDS: PostgreSQL deployment. OPTIONS: + --allow-workspace-renames bool, $CODER_ALLOW_WORKSPACE_RENAMES (default: false) + DEPRECATED: Allow users to rename their workspaces. Use only for + temporary compatibility reasons, this will be removed in a future + release. + --cache-dir string, $CODER_CACHE_DIRECTORY (default: [cache dir]) The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 934f2681fd..53b3eb9067 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -424,6 +424,7 @@ export interface DeploymentValues { readonly enable_terraform_debug_mode?: boolean; readonly user_quiet_hours_schedule?: UserQuietHoursScheduleConfig; readonly web_terminal_renderer?: string; + readonly allow_workspace_renames?: boolean; readonly healthcheck?: HealthcheckConfig; readonly config?: string; readonly write_config?: boolean; @@ -1429,6 +1430,7 @@ export interface Workspace { readonly dormant_at?: string; readonly health: WorkspaceHealth; readonly automatic_updates: AutomaticUpdates; + readonly allow_renames: boolean; } // From codersdk/workspaceagents.go diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index b94eabee07..824d47460a 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -18,9 +18,9 @@ import { AutomaticUpdateses, Workspace, } from "api/typesGenerated"; -import { Alert } from "components/Alert/Alert"; import MenuItem from "@mui/material/MenuItem"; import upperFirst from "lodash/upperFirst"; +import { type Theme } from "@emotion/react"; export type WorkspaceSettingsFormValues = { name: string; @@ -34,6 +34,10 @@ export const WorkspaceSettingsForm: FC<{ onCancel: () => void; onSubmit: (values: WorkspaceSettingsFormValues) => Promise; }> = ({ onCancel, onSubmit, workspace, error, templatePoliciesEnabled }) => { + const formEnabled = + (templatePoliciesEnabled && !workspace.template_require_active_version) || + workspace.allow_renames; + const form = useFormik({ onSubmit, initialValues: { @@ -59,18 +63,19 @@ export const WorkspaceSettingsForm: FC<{ - {form.values.name !== form.initialValues.name && ( - - Depending on the template, renaming your workspace may be - destructive - - )} {templatePoliciesEnabled && ( @@ -106,7 +111,17 @@ export const WorkspaceSettingsForm: FC<{ )} - + {formEnabled && ( + + )} ); }; + +const styles = { + nameWarning: (theme: Theme) => ({ + "& .MuiFormHelperText-root": { + color: theme.palette.warning.light, + }, + }), +}; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx index 0aaa510857..d6d8c9a11c 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx @@ -12,7 +12,7 @@ test("Submit the workspace settings page successfully", async () => { // Mock the API calls that loads data jest .spyOn(api, "getWorkspaceByOwnerAndName") - .mockResolvedValueOnce(MockWorkspace); + .mockResolvedValueOnce({ ...MockWorkspace }); // Mock the API calls that submit data const patchWorkspaceSpy = jest .spyOn(api, "patchWorkspace") @@ -39,3 +39,21 @@ test("Submit the workspace settings page successfully", async () => { }); }); }); + +test("Name field is disabled if renames are disabled", async () => { + // Mock the API calls that loads data + jest + .spyOn(api, "getWorkspaceByOwnerAndName") + .mockResolvedValueOnce({ ...MockWorkspace, allow_renames: false }); + renderWithWorkspaceSettingsLayout(, { + route: "/@test-user/test-workspace/settings", + path: "/:username/:workspace/settings", + // Need this because after submit the user is redirected + extraRoutes: [{ path: "/:username/:workspace", element:
}], + }); + await waitForLoaderToBeRemoved(); + // Fill the form and submit + const form = screen.getByTestId("form"); + const name = within(form).getByLabelText("Name"); + expect(name).toBeDisabled(); +}); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx index 1a316b4bb9..41c2633473 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -21,3 +21,9 @@ export const AutoUpdates: Story = { templatePoliciesEnabled: true, }, }; + +export const RenamesDisabled: Story = { + args: { + workspace: { ...MockWorkspace, allow_renames: false }, + }, +}; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 14b475a6d1..743dcd57b7 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1006,6 +1006,7 @@ export const MockWorkspace: TypesGen.Workspace = { failing_agents: [], }, automatic_updates: "never", + allow_renames: true, }; export const MockStoppedWorkspace: TypesGen.Workspace = {