From a66709a99da90c1941b6ff0546459f998523bcde Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Fri, 20 Oct 2023 14:51:47 +0000 Subject: [PATCH 01/10] feat: add frontend support for enabling automatic workspace updates --- scripts/develop.sh | 2 +- site/src/api/api.ts | 15 +++++ .../WorkspaceSettingsForm.tsx | 59 ++++++++++++++++++- .../WorkspaceSettingsPage.tsx | 11 +++- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/scripts/develop.sh b/scripts/develop.sh index 39f81c2951bc4..d8981fe8de7d2 100755 --- a/scripts/develop.sh +++ b/scripts/develop.sh @@ -136,7 +136,7 @@ fatal() { trap 'fatal "Script encountered an error"' ERR cdroot - start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "${CODER_DEV_ACCESS_URL}" --dangerous-allow-cors-requests=true "$@" + start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "${CODER_DEV_ACCESS_URL}" --experiments="template_update_policies" --dangerous-allow-cors-requests=true "$@" echo '== Waiting for Coder to become ready' # Start the timeout in the background so interrupting this script diff --git a/site/src/api/api.ts b/site/src/api/api.ts index c602a727e389e..80d43b4c9dade 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -576,6 +576,21 @@ export const updateWorkspaceDormancy = async ( return response.data; }; +export const updateWorkspaceAutomaticUpdates = async ( + workspaceId: string, + automaticUpdates: TypesGen.AutomaticUpdates, +): Promise => { + const data: TypesGen.UpdateWorkspaceAutomaticUpdatesRequest = { + automatic_updates: automaticUpdates, + }; + + const response = await axios.put( + `/api/v2/workspaces/${workspaceId}/autoupdates`, + data, + ); + return response.data; +}; + export const restartWorkspace = async ({ workspace, buildParameters, diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 7626e12585797..e7af44f3a7e28 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -6,18 +6,30 @@ import { HorizontalForm, } from "components/Form/Form"; import { useFormik } from "formik"; -import { type FC } from "react"; +import { useState, type FC } from "react"; import * as Yup from "yup"; import { nameValidator, getFormHelpers, onChangeTrimmed, } from "utils/formUtils"; -import { Workspace } from "api/typesGenerated"; +import { + AutomaticUpdates, + AutomaticUpdateses, + Workspace, +} from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; +import { + FormControl, + InputLabel, + MenuItem, + Select, + SelectChangeEvent, +} from "@mui/material"; export type WorkspaceSettingsFormValues = { name: string; + automatic_updates: AutomaticUpdates; }; export const WorkspaceSettingsForm: FC<{ @@ -31,6 +43,7 @@ export const WorkspaceSettingsForm: FC<{ onSubmit, initialValues: { name: workspace.name, + automatic_updates: workspace.automatic_updates, }, validationSchema: Yup.object({ name: nameValidator("Name"), @@ -41,9 +54,25 @@ export const WorkspaceSettingsForm: FC<{ error, ); + const capitalizeFirstLetter = (inputString: string): string => { + return inputString.charAt(0).toUpperCase() + inputString.slice(1); + }; + + const [automaticUpdates, setAutomaticUpdates] = useState( + workspace.automatic_updates, + ); + + const handleChange = (event: SelectChangeEvent) => { + setAutomaticUpdates(event.target.value as AutomaticUpdates); + form.setFieldValue("automatic_updates", automaticUpdates); + }; + return ( - + + + + + Update Policy + + + + ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index 38b65eeacf6ec..fcc8cd8497564 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -5,7 +5,7 @@ import { useWorkspaceSettings } from "./WorkspaceSettingsLayout"; import { WorkspaceSettingsPageView } from "./WorkspaceSettingsPageView"; import { useMutation } from "react-query"; import { displaySuccess } from "components/GlobalSnackbar/utils"; -import { patchWorkspace } from "api/api"; +import { patchWorkspace, updateWorkspaceAutomaticUpdates } from "api/api"; import { WorkspaceSettingsFormValues } from "./WorkspaceSettingsForm"; const WorkspaceSettingsPage = () => { @@ -18,8 +18,13 @@ const WorkspaceSettingsPage = () => { const workspace = useWorkspaceSettings(); const navigate = useNavigate(); const mutation = useMutation({ - mutationFn: (formValues: WorkspaceSettingsFormValues) => - patchWorkspace(workspace.id, { name: formValues.name }), + mutationFn: async (formValues: WorkspaceSettingsFormValues) => { + await patchWorkspace(workspace.id, { name: formValues.name }); + await updateWorkspaceAutomaticUpdates( + workspace.id, + formValues.automatic_updates, + ); + }, onSuccess: (_, formValues) => { displaySuccess("Workspace updated successfully"); navigate(`/@${username}/${formValues.name}/settings`); From 262050a0dab8c5924aed76e08e6c45860f9e3a33 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 20 Oct 2023 19:55:54 +0000 Subject: [PATCH 02/10] cleanup --- .../WorkspaceSettingsForm.tsx | 60 +++++++------------ .../WorkspaceSettingsPage.tsx | 2 + 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index e7af44f3a7e28..cf8ed1fb83f4e 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -6,26 +6,16 @@ import { HorizontalForm, } from "components/Form/Form"; import { useFormik } from "formik"; -import { useState, type FC } from "react"; +import { type FC } from "react"; import * as Yup from "yup"; import { nameValidator, getFormHelpers, onChangeTrimmed, } from "utils/formUtils"; -import { - AutomaticUpdates, - AutomaticUpdateses, - Workspace, -} from "api/typesGenerated"; +import { AutomaticUpdates, Workspace } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; -import { - FormControl, - InputLabel, - MenuItem, - Select, - SelectChangeEvent, -} from "@mui/material"; +import MenuItem from "@mui/material/MenuItem"; export type WorkspaceSettingsFormValues = { name: string; @@ -58,14 +48,7 @@ export const WorkspaceSettingsForm: FC<{ return inputString.charAt(0).toUpperCase() + inputString.slice(1); }; - const [automaticUpdates, setAutomaticUpdates] = useState( - workspace.automatic_updates, - ); - - const handleChange = (event: SelectChangeEvent) => { - setAutomaticUpdates(event.target.value as AutomaticUpdates); - form.setFieldValue("automatic_updates", automaticUpdates); - }; + const autoUpdatesSet = ["never", "always"]; return ( @@ -94,25 +77,22 @@ export const WorkspaceSettingsForm: FC<{ title="Automatic Updates" description="Configure your workspace to automatically update to the active template version when started." > - - - Update Policy - - - + + + {autoUpdatesSet.map((value) => ( + + {capitalizeFirstLetter(value)} + + ))} + + diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index fcc8cd8497564..4f2150d7da087 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -17,8 +17,10 @@ const WorkspaceSettingsPage = () => { const username = params.username.replace("@", ""); const workspace = useWorkspaceSettings(); const navigate = useNavigate(); + const mutation = useMutation({ mutationFn: async (formValues: WorkspaceSettingsFormValues) => { + console.log("in here", formValues.automatic_updates); await patchWorkspace(workspace.id, { name: formValues.name }); await updateWorkspaceAutomaticUpdates( workspace.id, From 86b0cb70dc8c9e29f050ba3706179c5b13d441c4 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sat, 21 Oct 2023 22:06:25 +0000 Subject: [PATCH 03/10] add update policy to workspace stats --- .../Dashboard/DashboardProvider.tsx | 8 +++++ .../TemplateSettingsPage.tsx | 7 ++-- site/src/pages/WorkspacePage/Workspace.tsx | 1 + .../WorkspaceActions/constants.ts | 4 +-- .../pages/WorkspacePage/WorkspaceStats.tsx | 35 +++++++++++++++++++ .../WorkspaceSettingsPage.tsx | 1 - 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/site/src/components/Dashboard/DashboardProvider.tsx b/site/src/components/Dashboard/DashboardProvider.tsx index 7e06b4a656620..d90b0d3f1b6b6 100644 --- a/site/src/components/Dashboard/DashboardProvider.tsx +++ b/site/src/components/Dashboard/DashboardProvider.tsx @@ -122,3 +122,11 @@ export const useIsWorkspaceActionsEnabled = (): boolean => { const allowWorkspaceActions = experiments.includes("workspace_actions"); return allowWorkspaceActions && allowAdvancedScheduling; }; + +export const useTemplatePoliciesEnabled = (): boolean => { + const { entitlements, experiments } = useDashboard(); + return ( + entitlements.features.access_control.enabled && + experiments.includes("template_update_policies") + ); +}; diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx index d483fb7ad053f..3a3fa11a425f7 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx @@ -10,7 +10,7 @@ import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplateSettingsPageView } from "./TemplateSettingsPageView"; import { templateByNameKey } from "api/queries/templates"; import { useOrganizationId } from "hooks"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; export const TemplateSettingsPage: FC = () => { const { template: templateName } = useParams() as { template: string }; @@ -18,10 +18,7 @@ export const TemplateSettingsPage: FC = () => { const orgId = useOrganizationId(); const { template } = useTemplateSettings(); const queryClient = useQueryClient(); - const { entitlements, experiments } = useDashboard(); - const accessControlEnabled = - entitlements.features["advanced_template_scheduling"].enabled && - experiments.includes("template_update_policies"); + const accessControlEnabled = useTemplatePoliciesEnabled(); const { mutate: updateTemplate, diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 7dbcd6cc0527a..a6ed168ec87ed 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -191,6 +191,7 @@ export const Workspace: FC> = ({ quotaBudget={quotaBudget} handleUpdate={handleUpdate} canUpdateWorkspace={canUpdateWorkspace} + canChangeVersions={canChangeVersions} maxDeadlineDecrease={scheduleProps.maxDeadlineDecrease} maxDeadlineIncrease={scheduleProps.maxDeadlineIncrease} onDeadlineMinus={scheduleProps.onDeadlineMinus} diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts index dc57d0e4fbd0e..baf0a6477ab1b 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts +++ b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts @@ -43,9 +43,9 @@ export const actionsByWorkspaceStatus = ( }; } if ( - workspace.template_require_active_version && workspace.outdated && - !canChangeVersions + ((workspace.template_require_active_version && !canChangeVersions) || + workspace.automatic_updates === "always") ) { if (status === "running") { return { diff --git a/site/src/pages/WorkspacePage/WorkspaceStats.tsx b/site/src/pages/WorkspacePage/WorkspaceStats.tsx index 773be0baa59f6..b73bb3c7e81f0 100644 --- a/site/src/pages/WorkspacePage/WorkspaceStats.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceStats.tsx @@ -26,6 +26,12 @@ import { PopoverTrigger, usePopover, } from "components/Popover/Popover"; +import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; +import { + HelpTooltip, + HelpTooltipText, +} from "components/HelpTooltip/HelpTooltip"; +import { Stack } from "components/Stack/Stack"; const Language = { workspaceDetails: "Workspace Details", @@ -37,6 +43,7 @@ const Language = { upToDate: "Up to date", byLabel: "Last built by", costLabel: "Daily cost", + updatePolicy: "Update policy", }; export interface WorkspaceStatsProps { @@ -44,6 +51,7 @@ export interface WorkspaceStatsProps { maxDeadlineIncrease: number; maxDeadlineDecrease: number; canUpdateWorkspace: boolean; + canChangeVersions: boolean; quotaBudget?: number; onDeadlinePlus: (hours: number) => void; onDeadlineMinus: (hours: number) => void; @@ -56,6 +64,7 @@ export const WorkspaceStats: FC = ({ maxDeadlineDecrease, maxDeadlineIncrease, canUpdateWorkspace, + canChangeVersions, handleUpdate, onDeadlineMinus, onDeadlinePlus, @@ -67,6 +76,13 @@ export const WorkspaceStats: FC = ({ const styles = useStyles(); const deadlinePlusEnabled = maxDeadlineIncrease >= 1; const deadlineMinusEnabled = maxDeadlineDecrease >= 1; + const templatePoliciesEnabled = useTemplatePoliciesEnabled(); + const workspaceUpdatePolicy = (): string => { + if (workspace.template_require_active_version && !canChangeVersions) { + return "Always"; + } + return upperFirst(workspace.automatic_updates); + }; return ( <> @@ -198,6 +214,25 @@ export const WorkspaceStats: FC = ({ }`} /> )} + {templatePoliciesEnabled && ( + + + {workspace.automatic_updates === "never" && + workspace.template_require_active_version && + !canChangeVersions && ( + + + Your workspace has not opted in to automatic updates but + your template requires updating to the active version. + + + )} + + )} ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index 4f2150d7da087..4bd481437e339 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -20,7 +20,6 @@ const WorkspaceSettingsPage = () => { const mutation = useMutation({ mutationFn: async (formValues: WorkspaceSettingsFormValues) => { - console.log("in here", formValues.automatic_updates); await patchWorkspace(workspace.id, { name: formValues.name }); await updateWorkspaceAutomaticUpdates( workspace.id, From ce25bba16e2282ca149512c7db1599527a70ffec Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sat, 21 Oct 2023 22:35:55 +0000 Subject: [PATCH 04/10] revert restrictive behavior --- scripts/develop.sh | 2 +- .../WorkspacePage/WorkspaceActions/constants.ts | 5 +++-- site/src/pages/WorkspacePage/WorkspaceStats.tsx | 11 ++++------- site/src/utils/workspace.tsx | 13 +++++++++++++ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/scripts/develop.sh b/scripts/develop.sh index d8981fe8de7d2..39f81c2951bc4 100755 --- a/scripts/develop.sh +++ b/scripts/develop.sh @@ -136,7 +136,7 @@ fatal() { trap 'fatal "Script encountered an error"' ERR cdroot - start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "${CODER_DEV_ACCESS_URL}" --experiments="template_update_policies" --dangerous-allow-cors-requests=true "$@" + start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "${CODER_DEV_ACCESS_URL}" --dangerous-allow-cors-requests=true "$@" echo '== Waiting for Coder to become ready' # Start the timeout in the background so interrupting this script diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts index baf0a6477ab1b..4ebcf9e21c2a5 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts +++ b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts @@ -1,5 +1,6 @@ import { Workspace, WorkspaceStatus } from "api/typesGenerated"; import { ReactNode } from "react"; +import { workspaceUpdatePolicy } from "utils/workspace"; // the button types we have export enum ButtonTypesEnum { @@ -44,8 +45,8 @@ export const actionsByWorkspaceStatus = ( } if ( workspace.outdated && - ((workspace.template_require_active_version && !canChangeVersions) || - workspace.automatic_updates === "always") + workspace.template_require_active_version && + !canChangeVersions ) { if (status === "running") { return { diff --git a/site/src/pages/WorkspacePage/WorkspaceStats.tsx b/site/src/pages/WorkspacePage/WorkspaceStats.tsx index b73bb3c7e81f0..bf4c37d3ecf52 100644 --- a/site/src/pages/WorkspacePage/WorkspaceStats.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceStats.tsx @@ -7,6 +7,7 @@ import { getDisplayWorkspaceBuildInitiatedBy, getDisplayWorkspaceTemplateName, isWorkspaceOn, + workspaceUpdatePolicy, } from "utils/workspace"; import { Workspace } from "api/typesGenerated"; import { Stats, StatsItem } from "components/Stats/Stats"; @@ -77,12 +78,6 @@ export const WorkspaceStats: FC = ({ const deadlinePlusEnabled = maxDeadlineIncrease >= 1; const deadlineMinusEnabled = maxDeadlineDecrease >= 1; const templatePoliciesEnabled = useTemplatePoliciesEnabled(); - const workspaceUpdatePolicy = (): string => { - if (workspace.template_require_active_version && !canChangeVersions) { - return "Always"; - } - return upperFirst(workspace.automatic_updates); - }; return ( <> @@ -219,7 +214,9 @@ export const WorkspaceStats: FC = ({ {workspace.automatic_updates === "never" && workspace.template_require_active_version && diff --git a/site/src/utils/workspace.tsx b/site/src/utils/workspace.tsx index 9365e5d615cac..abe40832462a2 100644 --- a/site/src/utils/workspace.tsx +++ b/site/src/utils/workspace.tsx @@ -303,3 +303,16 @@ export const getMatchingAgentOrFirst = ( }) .filter((a) => a)[0]; }; + +export const workspaceUpdatePolicy = ( + workspace: TypesGen.Workspace, + canChangeVersions: boolean, +): TypesGen.AutomaticUpdates => { + // If a template requires the active version and you cannot change versions + // (restricted to template admins), then your policy must be "Always". + if (workspace.template_require_active_version && !canChangeVersions) { + return "always"; + } + // Else prefer the workspace-level setting. + return workspace.automatic_updates; +}; From 3602dd914f02c1ad3cd7892c676683c08c25462a Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sat, 21 Oct 2023 22:42:56 +0000 Subject: [PATCH 05/10] move workspace setting behind experimental --- .../WorkspaceSettingsForm.tsx | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index cf8ed1fb83f4e..05355c8aa5ac2 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -16,6 +16,7 @@ import { import { AutomaticUpdates, Workspace } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; import MenuItem from "@mui/material/MenuItem"; +import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; export type WorkspaceSettingsFormValues = { name: string; @@ -50,6 +51,8 @@ export const WorkspaceSettingsForm: FC<{ const autoUpdatesSet = ["never", "always"]; + const templatePoliciesEnabled = useTemplatePoliciesEnabled(); + return ( - - - - {autoUpdatesSet.map((value) => ( - - {capitalizeFirstLetter(value)} - - ))} - - - + {templatePoliciesEnabled && ( + + + + {autoUpdatesSet.map((value) => ( + + {capitalizeFirstLetter(value)} + + ))} + + + + )} ); From 2f0ef946b733497757726a5ffcc818b5d517e8ce Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sat, 21 Oct 2023 22:53:48 +0000 Subject: [PATCH 06/10] remove unused import --- site/src/pages/WorkspacePage/WorkspaceActions/constants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts index 4ebcf9e21c2a5..7d7c3a88975d9 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts +++ b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts @@ -1,6 +1,5 @@ import { Workspace, WorkspaceStatus } from "api/typesGenerated"; import { ReactNode } from "react"; -import { workspaceUpdatePolicy } from "utils/workspace"; // the button types we have export enum ButtonTypesEnum { From e7515522cf31ccb7c9db1b31e9b20a7e4fd8de0e Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Mon, 30 Oct 2023 23:43:54 +0000 Subject: [PATCH 07/10] pr comments --- site/src/api/api.ts | 4 ++-- .../WorkspaceSettingsForm.tsx | 20 +++++++++---------- .../WorkspaceSettingsPage.tsx | 12 ++++++----- site/src/utils/workspace.tsx | 1 - 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 80d43b4c9dade..8461c758bf692 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -580,13 +580,13 @@ export const updateWorkspaceAutomaticUpdates = async ( workspaceId: string, automaticUpdates: TypesGen.AutomaticUpdates, ): Promise => { - const data: TypesGen.UpdateWorkspaceAutomaticUpdatesRequest = { + const req: TypesGen.UpdateWorkspaceAutomaticUpdatesRequest = { automatic_updates: automaticUpdates, }; const response = await axios.put( `/api/v2/workspaces/${workspaceId}/autoupdates`, - data, + req, ); return response.data; }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 05355c8aa5ac2..678c817d08d7d 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -13,10 +13,15 @@ import { getFormHelpers, onChangeTrimmed, } from "utils/formUtils"; -import { AutomaticUpdates, Workspace } from "api/typesGenerated"; +import { + AutomaticUpdates, + AutomaticUpdateses, + Workspace, +} from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; import MenuItem from "@mui/material/MenuItem"; import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; +import upperFirst from "lodash/upperFirst"; export type WorkspaceSettingsFormValues = { name: string; @@ -38,6 +43,7 @@ export const WorkspaceSettingsForm: FC<{ }, validationSchema: Yup.object({ name: nameValidator("Name"), + automatic_updates: Yup.string().oneOf(AutomaticUpdateses), }), }); const getFieldHelpers = getFormHelpers( @@ -45,12 +51,6 @@ export const WorkspaceSettingsForm: FC<{ error, ); - const capitalizeFirstLetter = (inputString: string): string => { - return inputString.charAt(0).toUpperCase() + inputString.slice(1); - }; - - const autoUpdatesSet = ["never", "always"]; - const templatePoliciesEnabled = useTemplatePoliciesEnabled(); return ( @@ -79,7 +79,7 @@ export const WorkspaceSettingsForm: FC<{ {templatePoliciesEnabled && ( - {autoUpdatesSet.map((value) => ( + {AutomaticUpdateses.map((value) => ( - {capitalizeFirstLetter(value)} + {upperFirst(value)} ))} diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index 4bd481437e339..c9ce9e0447a3d 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -20,11 +20,13 @@ const WorkspaceSettingsPage = () => { const mutation = useMutation({ mutationFn: async (formValues: WorkspaceSettingsFormValues) => { - await patchWorkspace(workspace.id, { name: formValues.name }); - await updateWorkspaceAutomaticUpdates( - workspace.id, - formValues.automatic_updates, - ); + await Promise.all([ + patchWorkspace(workspace.id, { name: formValues.name }), + updateWorkspaceAutomaticUpdates( + workspace.id, + formValues.automatic_updates, + ), + ]); }, onSuccess: (_, formValues) => { displaySuccess("Workspace updated successfully"); diff --git a/site/src/utils/workspace.tsx b/site/src/utils/workspace.tsx index abe40832462a2..bfa06dde290ef 100644 --- a/site/src/utils/workspace.tsx +++ b/site/src/utils/workspace.tsx @@ -313,6 +313,5 @@ export const workspaceUpdatePolicy = ( if (workspace.template_require_active_version && !canChangeVersions) { return "always"; } - // Else prefer the workspace-level setting. return workspace.automatic_updates; }; From 2a67fe3430f3317f44bfb4de0b40c05eb337e5b2 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 31 Oct 2023 19:06:18 +0000 Subject: [PATCH 08/10] pr comments --- site/src/pages/WorkspacePage/WorkspaceActions/constants.ts | 4 ++-- .../pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx | 5 ++--- .../pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx | 3 +++ .../WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx | 3 +++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts index 7d7c3a88975d9..4d49df9d0b0a1 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts +++ b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts @@ -1,5 +1,6 @@ import { Workspace, WorkspaceStatus } from "api/typesGenerated"; import { ReactNode } from "react"; +import { workspaceUpdatePolicy } from "utils/workspace"; // the button types we have export enum ButtonTypesEnum { @@ -44,8 +45,7 @@ export const actionsByWorkspaceStatus = ( } if ( workspace.outdated && - workspace.template_require_active_version && - !canChangeVersions + workspaceUpdatePolicy(workspace, canChangeVersions) ) { if (status === "running") { return { diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 678c817d08d7d..3807d79130682 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -20,7 +20,6 @@ import { } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; import MenuItem from "@mui/material/MenuItem"; -import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; import upperFirst from "lodash/upperFirst"; export type WorkspaceSettingsFormValues = { @@ -32,9 +31,10 @@ export const WorkspaceSettingsForm: FC<{ isSubmitting: boolean; workspace: Workspace; error: unknown; + templatePoliciesEnabled: boolean, onCancel: () => void; onSubmit: (values: WorkspaceSettingsFormValues) => void; -}> = ({ onCancel, onSubmit, workspace, error, isSubmitting }) => { +}> = ({ onCancel, onSubmit, workspace, error, isSubmitting, templatePoliciesEnabled }) => { const form = useFormik({ onSubmit, initialValues: { @@ -51,7 +51,6 @@ export const WorkspaceSettingsForm: FC<{ error, ); - const templatePoliciesEnabled = useTemplatePoliciesEnabled(); return ( diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index c9ce9e0447a3d..1f3239905becb 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -7,6 +7,7 @@ import { useMutation } from "react-query"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { patchWorkspace, updateWorkspaceAutomaticUpdates } from "api/api"; import { WorkspaceSettingsFormValues } from "./WorkspaceSettingsForm"; +import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider"; const WorkspaceSettingsPage = () => { const params = useParams() as { @@ -17,6 +18,7 @@ const WorkspaceSettingsPage = () => { const username = params.username.replace("@", ""); const workspace = useWorkspaceSettings(); const navigate = useNavigate(); + const templatePoliciesEnabled = useTemplatePoliciesEnabled(); const mutation = useMutation({ mutationFn: async (formValues: WorkspaceSettingsFormValues) => { @@ -46,6 +48,7 @@ const WorkspaceSettingsPage = () => { workspace={workspace} onCancel={() => navigate(`/@${username}/${workspaceName}`)} onSubmit={mutation.mutate} + templatePoliciesEnabled={templatePoliciesEnabled} /> ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx index eae60876b07d3..6e23e1e34443d 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx @@ -9,6 +9,7 @@ export type WorkspaceSettingsPageViewProps = { workspace: Workspace; onCancel: () => void; onSubmit: ComponentProps["onSubmit"]; + templatePoliciesEnabled: boolean; }; export const WorkspaceSettingsPageView: FC = ({ @@ -17,6 +18,7 @@ export const WorkspaceSettingsPageView: FC = ({ isSubmitting, error, workspace, + templatePoliciesEnabled, }) => { return ( <> @@ -34,6 +36,7 @@ export const WorkspaceSettingsPageView: FC = ({ workspace={workspace} onCancel={onCancel} onSubmit={onSubmit} + templatePoliciesEnabled={templatePoliciesEnabled} /> ); From 2018b0b525e2314af8149c56bdd4101de15c0c59 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 31 Oct 2023 19:26:03 +0000 Subject: [PATCH 09/10] remove unused prop --- .../WorkspaceSettingsPage/WorkspaceSettingsForm.tsx | 10 ++++------ .../WorkspaceSettingsPage/WorkspaceSettingsPage.tsx | 3 +-- .../WorkspaceSettingsPageView.stories.tsx | 1 - .../WorkspaceSettingsPageView.tsx | 3 --- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 3807d79130682..605fbd406c2d7 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -28,13 +28,12 @@ export type WorkspaceSettingsFormValues = { }; export const WorkspaceSettingsForm: FC<{ - isSubmitting: boolean; workspace: Workspace; error: unknown; - templatePoliciesEnabled: boolean, + templatePoliciesEnabled: boolean; onCancel: () => void; - onSubmit: (values: WorkspaceSettingsFormValues) => void; -}> = ({ onCancel, onSubmit, workspace, error, isSubmitting, templatePoliciesEnabled }) => { + onSubmit: (values: WorkspaceSettingsFormValues) => Promise; +}> = ({ onCancel, onSubmit, workspace, error, templatePoliciesEnabled }) => { const form = useFormik({ onSubmit, initialValues: { @@ -51,7 +50,6 @@ export const WorkspaceSettingsForm: FC<{ error, ); - return ( )} - + ); }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index 1f3239905becb..4ba770eb65735 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -44,10 +44,9 @@ const WorkspaceSettingsPage = () => { navigate(`/@${username}/${workspaceName}`)} - onSubmit={mutation.mutate} + onSubmit={mutation.mutateAsync} templatePoliciesEnabled={templatePoliciesEnabled} /> diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx index 13a44283d6aa1..3e42859952667 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -7,7 +7,6 @@ const meta: Meta = { component: WorkspaceSettingsPageView, args: { error: undefined, - isSubmitting: false, workspace: MockWorkspace, }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx index 6e23e1e34443d..20bb4664dbd27 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx @@ -5,7 +5,6 @@ import { Workspace } from "api/typesGenerated"; export type WorkspaceSettingsPageViewProps = { error: unknown; - isSubmitting: boolean; workspace: Workspace; onCancel: () => void; onSubmit: ComponentProps["onSubmit"]; @@ -15,7 +14,6 @@ export type WorkspaceSettingsPageViewProps = { export const WorkspaceSettingsPageView: FC = ({ onCancel, onSubmit, - isSubmitting, error, workspace, templatePoliciesEnabled, @@ -32,7 +30,6 @@ export const WorkspaceSettingsPageView: FC = ({ Date: Tue, 31 Oct 2023 19:48:27 +0000 Subject: [PATCH 10/10] storybook: add auto updates story --- .../WorkspaceSettingsPageView.stories.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx index 3e42859952667..1a316b4bb9488 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -14,6 +14,10 @@ const meta: Meta = { export default meta; type Story = StoryObj; -const Example: Story = {}; +export const Example: Story = {}; -export { Example as WorkspaceSettingsPageView }; +export const AutoUpdates: Story = { + args: { + templatePoliciesEnabled: true, + }, +};