Skip to content

Commit 19afeda

Browse files
authored
feat: improve workspace upgrade flow when template parameters change (#18917)
1 parent d7b1253 commit 19afeda

File tree

4 files changed

+58
-27
lines changed

4 files changed

+58
-27
lines changed

site/src/api/api.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type dayjs from "dayjs";
2424
import userAgentParser from "ua-parser-js";
2525
import { OneWayWebSocket } from "../utils/OneWayWebSocket";
2626
import { delay } from "../utils/delay";
27+
import { type FieldError, isApiError } from "./errors";
2728
import type {
2829
DynamicParametersRequest,
2930
PostWorkspaceUsageRequest,
@@ -390,6 +391,15 @@ export class MissingBuildParameters extends Error {
390391
}
391392
}
392393

394+
export class ParameterValidationError extends Error {
395+
constructor(
396+
public readonly versionId: string,
397+
public readonly validations: FieldError[],
398+
) {
399+
super("Parameters are not valid for new template version");
400+
}
401+
}
402+
393403
export type GetProvisionerJobsParams = {
394404
status?: string;
395405
limit?: number;
@@ -1239,7 +1249,6 @@ class ApiMethods {
12391249
`/api/v2/workspaces/${workspaceId}/builds`,
12401250
data,
12411251
);
1242-
12431252
return response.data;
12441253
};
12451254

@@ -2268,19 +2277,34 @@ class ApiMethods {
22682277

22692278
const activeVersionId = template.active_version_id;
22702279

2271-
let templateParameters: TypesGen.TemplateVersionParameter[] = [];
2272-
22732280
if (isDynamicParametersEnabled) {
2274-
templateParameters = await this.getDynamicParameters(
2275-
activeVersionId,
2276-
workspace.owner_id,
2277-
oldBuildParameters,
2278-
);
2279-
} else {
2280-
templateParameters =
2281-
await this.getTemplateVersionRichParameters(activeVersionId);
2281+
try {
2282+
return await this.postWorkspaceBuild(workspace.id, {
2283+
transition: "start",
2284+
template_version_id: activeVersionId,
2285+
rich_parameter_values: newBuildParameters,
2286+
});
2287+
} catch (error) {
2288+
// If the build failed because of a parameter validation error, then we
2289+
// throw a special sentinel error that can be caught by the caller.
2290+
if (
2291+
isApiError(error) &&
2292+
error.response.status === 400 &&
2293+
error.response.data.validations &&
2294+
error.response.data.validations.length > 0
2295+
) {
2296+
throw new ParameterValidationError(
2297+
activeVersionId,
2298+
error.response.data.validations,
2299+
);
2300+
}
2301+
throw error;
2302+
}
22822303
}
22832304

2305+
const templateParameters =
2306+
await this.getTemplateVersionRichParameters(activeVersionId);
2307+
22842308
const missingParameters = getMissingParameters(
22852309
oldBuildParameters,
22862310
newBuildParameters,

site/src/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TemplateVersionParameter } from "api/typesGenerated";
1+
import type { FieldError } from "api/errors";
22
import { Button } from "components/Button/Button";
33
import {
44
Dialog,
@@ -14,7 +14,7 @@ import { useNavigate } from "react-router-dom";
1414
type UpdateBuildParametersDialogExperimentalProps = {
1515
open: boolean;
1616
onClose: () => void;
17-
missedParameters: TemplateVersionParameter[];
17+
validations: FieldError[];
1818
workspaceOwnerName: string;
1919
workspaceName: string;
2020
templateVersionId: string | undefined;
@@ -23,7 +23,7 @@ type UpdateBuildParametersDialogExperimentalProps = {
2323
export const UpdateBuildParametersDialogExperimental: FC<
2424
UpdateBuildParametersDialogExperimentalProps
2525
> = ({
26-
missedParameters,
26+
validations,
2727
open,
2828
onClose,
2929
workspaceOwnerName,
@@ -47,8 +47,8 @@ export const UpdateBuildParametersDialogExperimental: FC<
4747
<DialogDescription>
4848
This template has{" "}
4949
<strong className="text-content-primary">
50-
{missedParameters.length} new parameter
51-
{missedParameters.length === 1 ? "" : "s"}
50+
{validations.length} parameter
51+
{validations.length === 1 ? "" : "s"}
5252
</strong>{" "}
5353
that must be configured to complete the update.
5454
</DialogDescription>

site/src/modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MissingBuildParameters } from "api/api";
1+
import { MissingBuildParameters, ParameterValidationError } from "api/api";
22
import { isApiError } from "api/errors";
33
import { type ApiError, getErrorMessage } from "api/errors";
44
import {
@@ -192,19 +192,19 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
192192
/>
193193
) : (
194194
<UpdateBuildParametersDialogExperimental
195-
missedParameters={
196-
changeVersionMutation.error instanceof MissingBuildParameters
197-
? changeVersionMutation.error.parameters
195+
validations={
196+
changeVersionMutation.error instanceof ParameterValidationError
197+
? changeVersionMutation.error.validations
198198
: []
199199
}
200-
open={changeVersionMutation.error instanceof MissingBuildParameters}
200+
open={changeVersionMutation.error instanceof ParameterValidationError}
201201
onClose={() => {
202202
changeVersionMutation.reset();
203203
}}
204204
workspaceOwnerName={workspace.owner_name}
205205
workspaceName={workspace.name}
206206
templateVersionId={
207-
changeVersionMutation.error instanceof MissingBuildParameters
207+
changeVersionMutation.error instanceof ParameterValidationError
208208
? changeVersionMutation.error?.versionId
209209
: undefined
210210
}

site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MissingBuildParameters } from "api/api";
1+
import { MissingBuildParameters, ParameterValidationError } from "api/api";
22
import { updateWorkspace } from "api/queries/workspaces";
33
import type {
44
TemplateVersion,
@@ -78,7 +78,10 @@ export const useWorkspaceUpdate = ({
7878
updateWorkspaceMutation.reset();
7979
},
8080
onUpdate: (buildParameters: WorkspaceBuildParameter[]) => {
81-
if (updateWorkspaceMutation.error instanceof MissingBuildParameters) {
81+
if (
82+
updateWorkspaceMutation.error instanceof MissingBuildParameters ||
83+
updateWorkspaceMutation.error instanceof ParameterValidationError
84+
) {
8285
confirmUpdate(buildParameters);
8386
}
8487
},
@@ -154,8 +157,10 @@ const MissingBuildParametersDialog: FC<MissingBuildParametersDialogProps> = ({
154157
const missedParameters =
155158
error instanceof MissingBuildParameters ? error.parameters : [];
156159
const versionId =
157-
error instanceof MissingBuildParameters ? error.versionId : undefined;
158-
const isOpen = error instanceof MissingBuildParameters;
160+
error instanceof ParameterValidationError ? error.versionId : undefined;
161+
const isOpen =
162+
error instanceof MissingBuildParameters ||
163+
error instanceof ParameterValidationError;
159164

160165
return workspace.template_use_classic_parameter_flow ? (
161166
<UpdateBuildParametersDialog
@@ -165,7 +170,9 @@ const MissingBuildParametersDialog: FC<MissingBuildParametersDialogProps> = ({
165170
/>
166171
) : (
167172
<UpdateBuildParametersDialogExperimental
168-
missedParameters={missedParameters}
173+
validations={
174+
error instanceof ParameterValidationError ? error.validations : []
175+
}
169176
open={isOpen}
170177
onClose={dialogProps.onClose}
171178
workspaceOwnerName={workspace.owner_name}

0 commit comments

Comments
 (0)