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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy