Skip to content

Commit ba3b835

Browse files
sreyaaslilac
andauthored
fix: prevent editing build parameters if template requires active version (coder#11117)
Co-authored-by: McKayla Washburn <mckayla@hey.com>
1 parent b7ea330 commit ba3b835

File tree

3 files changed

+215
-99
lines changed

3 files changed

+215
-99
lines changed
Lines changed: 129 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,48 @@
1+
import { useFormik } from "formik";
2+
import { type FC } from "react";
3+
import * as Yup from "yup";
4+
import { Alert } from "components/Alert/Alert";
15
import {
26
FormFields,
37
FormFooter,
48
FormSection,
59
HorizontalForm,
610
} from "components/Form/Form";
711
import { RichParameterInput } from "components/RichParameterInput/RichParameterInput";
8-
import { useFormik } from "formik";
9-
import { FC } from "react";
1012
import {
1113
getInitialRichParameterValues,
1214
useValidationSchemaForRichParameters,
1315
} from "utils/richParameters";
14-
import * as Yup from "yup";
1516
import { getFormHelpers } from "utils/formUtils";
16-
import {
17+
import type {
1718
TemplateVersionParameter,
19+
Workspace,
1820
WorkspaceBuildParameter,
1921
} from "api/typesGenerated";
2022

2123
export type WorkspaceParametersFormValues = {
2224
rich_parameter_values: WorkspaceBuildParameter[];
2325
};
2426

25-
export const WorkspaceParametersForm: FC<{
26-
isSubmitting: boolean;
27+
interface WorkspaceParameterFormProps {
28+
workspace: Workspace;
2729
templateVersionRichParameters: TemplateVersionParameter[];
2830
buildParameters: WorkspaceBuildParameter[];
31+
isSubmitting: boolean;
32+
canChangeVersions: boolean;
2933
error: unknown;
3034
onCancel: () => void;
3135
onSubmit: (values: WorkspaceParametersFormValues) => void;
32-
}> = ({
36+
}
37+
38+
export const WorkspaceParametersForm: FC<WorkspaceParameterFormProps> = ({
39+
workspace,
3340
onCancel,
3441
onSubmit,
3542
templateVersionRichParameters,
3643
buildParameters,
3744
error,
45+
canChangeVersions,
3846
isSubmitting,
3947
}) => {
4048
const form = useFormik<WorkspaceParametersFormValues>({
@@ -65,97 +73,121 @@ export const WorkspaceParametersForm: FC<{
6573
(parameter) => !parameter.mutable,
6674
);
6775

76+
const disabled =
77+
workspace.outdated &&
78+
workspace.template_require_active_version &&
79+
!canChangeVersions;
80+
6881
return (
69-
<HorizontalForm onSubmit={form.handleSubmit} data-testid="form">
70-
{hasNonEphemeralParameters && (
71-
<FormSection
72-
title="Parameters"
73-
description="Settings used by your template"
74-
>
75-
<FormFields>
76-
{templateVersionRichParameters.map((parameter, index) =>
77-
// Since we are adding the values to the form based on the index
78-
// we can't filter them to not loose the right index position
79-
parameter.mutable && !parameter.ephemeral ? (
80-
<RichParameterInput
81-
{...getFieldHelpers(
82-
"rich_parameter_values[" + index + "].value",
83-
)}
84-
disabled={isSubmitting}
85-
key={parameter.name}
86-
onChange={async (value) => {
87-
await form.setFieldValue("rich_parameter_values." + index, {
88-
name: parameter.name,
89-
value: value,
90-
});
91-
}}
92-
parameter={parameter}
93-
/>
94-
) : null,
95-
)}
96-
</FormFields>
97-
</FormSection>
82+
<>
83+
{disabled && (
84+
<Alert severity="warning" css={{ marginBottom: 48 }}>
85+
The template for this workspace requires automatic updates. Update the
86+
workspace to edit parameters.
87+
</Alert>
9888
)}
99-
{hasEphemeralParameters && (
100-
<FormSection
101-
title="Ephemeral Parameters"
102-
description="These parameters only apply for a single workspace start."
103-
>
104-
<FormFields>
105-
{templateVersionRichParameters.map((parameter, index) =>
106-
// Since we are adding the values to the form based on the index
107-
// we can't filter them to not loose the right index position
108-
parameter.mutable && parameter.ephemeral ? (
109-
<RichParameterInput
110-
{...getFieldHelpers(
111-
"rich_parameter_values[" + index + "].value",
112-
)}
113-
disabled={isSubmitting}
114-
key={parameter.name}
115-
onChange={async (value) => {
116-
await form.setFieldValue("rich_parameter_values." + index, {
117-
name: parameter.name,
118-
value: value,
119-
});
120-
}}
121-
parameter={parameter}
122-
/>
123-
) : null,
124-
)}
125-
</FormFields>
126-
</FormSection>
127-
)}
128-
{/* They are displayed here only for visibility purposes */}
129-
{hasImmutableParameters && (
130-
<FormSection
131-
title="Immutable parameters"
132-
description={
133-
<>
134-
These settings <strong>cannot be changed</strong> after creating
135-
the workspace.
136-
</>
137-
}
138-
>
139-
<FormFields>
140-
{templateVersionRichParameters.map((parameter, index) =>
141-
!parameter.mutable ? (
142-
<RichParameterInput
143-
disabled
144-
{...getFieldHelpers(
145-
"rich_parameter_values[" + index + "].value",
146-
)}
147-
key={parameter.name}
148-
parameter={parameter}
149-
onChange={() => {
150-
throw new Error("Immutable parameters cannot be changed");
151-
}}
152-
/>
153-
) : null,
154-
)}
155-
</FormFields>
156-
</FormSection>
157-
)}
158-
<FormFooter onCancel={onCancel} isLoading={isSubmitting} />
159-
</HorizontalForm>
89+
90+
<HorizontalForm onSubmit={form.handleSubmit} data-testid="form">
91+
{hasNonEphemeralParameters && (
92+
<FormSection
93+
title="Parameters"
94+
description="Settings used by your template"
95+
>
96+
<FormFields>
97+
{templateVersionRichParameters.map((parameter, index) =>
98+
// Since we are adding the values to the form based on the index
99+
// we can't filter them to not loose the right index position
100+
parameter.mutable && !parameter.ephemeral ? (
101+
<RichParameterInput
102+
{...getFieldHelpers(
103+
"rich_parameter_values[" + index + "].value",
104+
)}
105+
disabled={isSubmitting || disabled}
106+
key={parameter.name}
107+
onChange={async (value) => {
108+
await form.setFieldValue(
109+
"rich_parameter_values." + index,
110+
{
111+
name: parameter.name,
112+
value: value,
113+
},
114+
);
115+
}}
116+
parameter={parameter}
117+
/>
118+
) : null,
119+
)}
120+
</FormFields>
121+
</FormSection>
122+
)}
123+
{hasEphemeralParameters && (
124+
<FormSection
125+
title="Ephemeral Parameters"
126+
description="These parameters only apply for a single workspace start."
127+
>
128+
<FormFields>
129+
{templateVersionRichParameters.map((parameter, index) =>
130+
// Since we are adding the values to the form based on the index
131+
// we can't filter them to not loose the right index position
132+
parameter.mutable && parameter.ephemeral ? (
133+
<RichParameterInput
134+
{...getFieldHelpers(
135+
"rich_parameter_values[" + index + "].value",
136+
)}
137+
disabled={isSubmitting || disabled}
138+
key={parameter.name}
139+
onChange={async (value) => {
140+
await form.setFieldValue(
141+
"rich_parameter_values." + index,
142+
{
143+
name: parameter.name,
144+
value: value,
145+
},
146+
);
147+
}}
148+
parameter={parameter}
149+
/>
150+
) : null,
151+
)}
152+
</FormFields>
153+
</FormSection>
154+
)}
155+
{/* They are displayed here only for visibility purposes */}
156+
{hasImmutableParameters && (
157+
<FormSection
158+
title="Immutable parameters"
159+
description={
160+
<>
161+
These settings <strong>cannot be changed</strong> after creating
162+
the workspace.
163+
</>
164+
}
165+
>
166+
<FormFields>
167+
{templateVersionRichParameters.map((parameter, index) =>
168+
!parameter.mutable ? (
169+
<RichParameterInput
170+
disabled
171+
{...getFieldHelpers(
172+
"rich_parameter_values[" + index + "].value",
173+
)}
174+
key={parameter.name}
175+
parameter={parameter}
176+
onChange={() => {
177+
throw new Error("Immutable parameters cannot be changed");
178+
}}
179+
/>
180+
) : null,
181+
)}
182+
</FormFields>
183+
</FormSection>
184+
)}
185+
<FormFooter
186+
onCancel={onCancel}
187+
isLoading={isSubmitting}
188+
submitDisabled={disabled}
189+
/>
190+
</HorizontalForm>
191+
</>
160192
);
161193
};

site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.stories.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
MockTemplateVersionParameter2,
88
MockTemplateVersionParameter3,
99
MockWorkspaceBuildParameter3,
10+
MockWorkspace,
11+
MockOutdatedStoppedWorkspaceRequireActiveVersion,
1012
} from "testHelpers/entities";
1113

1214
const meta: Meta<typeof WorkspaceParametersPageView> = {
@@ -15,6 +17,8 @@ const meta: Meta<typeof WorkspaceParametersPageView> = {
1517
args: {
1618
submitError: undefined,
1719
isSubmitting: false,
20+
workspace: MockWorkspace,
21+
canChangeVersions: true,
1822

1923
data: {
2024
buildParameters: [
@@ -48,4 +52,48 @@ export const Empty: Story = {
4852
},
4953
};
5054

55+
export const RequireActiveVersionNoChangeVersion: Story = {
56+
args: {
57+
workspace: MockOutdatedStoppedWorkspaceRequireActiveVersion,
58+
canChangeVersions: false,
59+
data: {
60+
buildParameters: [
61+
MockWorkspaceBuildParameter1,
62+
MockWorkspaceBuildParameter2,
63+
MockWorkspaceBuildParameter3,
64+
],
65+
templateVersionRichParameters: [
66+
MockTemplateVersionParameter1,
67+
MockTemplateVersionParameter2,
68+
{
69+
...MockTemplateVersionParameter3,
70+
mutable: false,
71+
},
72+
],
73+
},
74+
},
75+
};
76+
77+
export const RequireActiveVersionCanChangeVersion: Story = {
78+
args: {
79+
workspace: MockOutdatedStoppedWorkspaceRequireActiveVersion,
80+
canChangeVersions: true,
81+
data: {
82+
buildParameters: [
83+
MockWorkspaceBuildParameter1,
84+
MockWorkspaceBuildParameter2,
85+
MockWorkspaceBuildParameter3,
86+
],
87+
templateVersionRichParameters: [
88+
MockTemplateVersionParameter1,
89+
MockTemplateVersionParameter2,
90+
{
91+
...MockTemplateVersionParameter3,
92+
mutable: false,
93+
},
94+
],
95+
},
96+
},
97+
};
98+
5199
export { Example as WorkspaceParametersPage };

0 commit comments

Comments
 (0)