Skip to content

Commit 80e9dbd

Browse files
committed
chore: cleanup, update validation
1 parent 54f1b7d commit 80e9dbd

File tree

3 files changed

+187
-38
lines changed

3 files changed

+187
-38
lines changed

site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { Parameter, ParameterOption } from "api/typesParameter";
1+
import type { WorkspaceBuildParameter } from "api/typesGenerated";
2+
import type {
3+
Parameter,
4+
ParameterOption,
5+
ParameterValidation,
6+
} from "api/typesParameter";
27
import { Badge } from "components/Badge/Badge";
38
import { Checkbox } from "components/Checkbox/Checkbox";
49
import { ExternalImage } from "components/ExternalImage/ExternalImage";
@@ -26,6 +31,7 @@ import {
2631
} from "components/Tooltip/Tooltip";
2732
import { Info, Settings, TriangleAlert } from "lucide-react";
2833
import { type FC, useId } from "react";
34+
import * as Yup from "yup";
2935

3036
export interface DynamicParameterProps {
3137
parameter: Parameter;
@@ -324,7 +330,9 @@ const OptionDisplay: FC<OptionDisplayProps> = ({ option }) => {
324330
<TooltipTrigger asChild>
325331
<Info className="w-3.5 h-3.5 text-content-secondary" />
326332
</TooltipTrigger>
327-
<TooltipContent>{option.description}</TooltipContent>
333+
<TooltipContent side="right" sideOffset={10}>
334+
{option.description}
335+
</TooltipContent>
328336
</Tooltip>
329337
</TooltipProvider>
330338
)}
@@ -357,3 +365,167 @@ const ParameterDiagnostics: FC<ParameterDiagnosticsProps> = ({
357365
</div>
358366
);
359367
};
368+
369+
export const useValidationSchemaForDynamicParameters = (
370+
parameters?: Parameter[],
371+
lastBuildParameters?: WorkspaceBuildParameter[],
372+
): Yup.AnySchema => {
373+
if (!parameters) {
374+
return Yup.object();
375+
}
376+
377+
return Yup.array()
378+
.of(
379+
Yup.object().shape({
380+
name: Yup.string().required(),
381+
value: Yup.string()
382+
.test("verify with template", (val, ctx) => {
383+
const name = ctx.parent.name;
384+
const parameter = parameters.find(
385+
(parameter) => parameter.name === name,
386+
);
387+
if (parameter) {
388+
switch (parameter.type) {
389+
case "number": {
390+
const minValidation = parameter.validations.find(
391+
(v) => v.validation_min !== null,
392+
);
393+
const maxValidation = parameter.validations.find(
394+
(v) => v.validation_max !== null,
395+
);
396+
397+
if (
398+
minValidation?.validation_min &&
399+
!maxValidation &&
400+
Number(val) < minValidation.validation_min
401+
) {
402+
return ctx.createError({
403+
path: ctx.path,
404+
message:
405+
parameterError(parameter, val) ??
406+
`Value must be greater than ${minValidation.validation_min}.`,
407+
});
408+
}
409+
410+
if (
411+
!minValidation &&
412+
maxValidation?.validation_max &&
413+
Number(val) > maxValidation.validation_max
414+
) {
415+
return ctx.createError({
416+
path: ctx.path,
417+
message:
418+
parameterError(parameter, val) ??
419+
`Value must be less than ${maxValidation.validation_max}.`,
420+
});
421+
}
422+
423+
if (
424+
minValidation?.validation_min &&
425+
maxValidation?.validation_max &&
426+
(Number(val) < minValidation.validation_min ||
427+
Number(val) > maxValidation.validation_max)
428+
) {
429+
return ctx.createError({
430+
path: ctx.path,
431+
message:
432+
parameterError(parameter, val) ??
433+
`Value must be between ${minValidation.validation_min} and ${maxValidation.validation_max}.`,
434+
});
435+
}
436+
437+
const monotonicValidation = parameter.validations.find(
438+
(v) => v.validation_monotonic !== null,
439+
);
440+
if (
441+
monotonicValidation?.validation_monotonic &&
442+
lastBuildParameters
443+
) {
444+
const lastBuildParameter = lastBuildParameters.find(
445+
(last: { name: string }) => last.name === name,
446+
);
447+
if (lastBuildParameter) {
448+
switch (monotonicValidation.validation_monotonic) {
449+
case "increasing":
450+
if (Number(lastBuildParameter.value) > Number(val)) {
451+
return ctx.createError({
452+
path: ctx.path,
453+
message: `Value must only ever increase (last value was ${lastBuildParameter.value})`,
454+
});
455+
}
456+
break;
457+
case "decreasing":
458+
if (Number(lastBuildParameter.value) < Number(val)) {
459+
return ctx.createError({
460+
path: ctx.path,
461+
message: `Value must only ever decrease (last value was ${lastBuildParameter.value})`,
462+
});
463+
}
464+
break;
465+
}
466+
}
467+
}
468+
break;
469+
}
470+
case "string": {
471+
const regexValidation = parameter.validations.find(
472+
(v) => v.validation_regex !== null,
473+
);
474+
if (!regexValidation?.validation_regex) {
475+
return true;
476+
}
477+
478+
if (
479+
val &&
480+
!new RegExp(regexValidation.validation_regex).test(val)
481+
) {
482+
return ctx.createError({
483+
path: ctx.path,
484+
message: parameterError(parameter, val),
485+
});
486+
}
487+
break;
488+
}
489+
}
490+
}
491+
return true;
492+
}),
493+
}),
494+
)
495+
.required();
496+
};
497+
498+
const parameterError = (
499+
parameter: Parameter,
500+
value?: string,
501+
): string | undefined => {
502+
const validation_error = parameter.validations.find(
503+
(v) => v.validation_error !== null,
504+
);
505+
const minValidation = parameter.validations.find(
506+
(v) => v.validation_min !== null,
507+
);
508+
const maxValidation = parameter.validations.find(
509+
(v) => v.validation_max !== null,
510+
);
511+
512+
if (!validation_error || !value) {
513+
return;
514+
}
515+
516+
const r = new Map<string, string>([
517+
[
518+
"{min}",
519+
minValidation ? (minValidation.validation_min?.toString() ?? "") : "",
520+
],
521+
[
522+
"{max}",
523+
maxValidation ? (maxValidation.validation_max?.toString() ?? "") : "",
524+
],
525+
["{value}", value],
526+
]);
527+
return validation_error.validation_error.replace(
528+
/{min}|{max}|{value}/g,
529+
(match) => r.get(match) || "",
530+
);
531+
};

site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import type { ApiErrorResponse } from "api/errors";
22
import { checkAuthorization } from "api/queries/authCheck";
33
import {
4-
richParameters,
54
templateByName,
65
templateVersionExternalAuth,
76
templateVersionPresets,
87
} from "api/queries/templates";
98
import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces";
10-
import type {
11-
Template,
12-
TemplateVersionParameter,
13-
Workspace,
14-
} from "api/typesGenerated";
9+
import type { Template, Workspace } from "api/typesGenerated";
1510
import { Loader } from "components/Loader/Loader";
1611
import { useAuthenticated } from "contexts/auth/RequireAuth";
1712
import { useEffectEvent } from "hooks/hookPolyfills";
@@ -29,7 +24,6 @@ import { useMutation, useQuery, useQueryClient } from "react-query";
2924
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
3025
import { pageTitle } from "utils/page";
3126
import type { AutofillBuildParameter } from "utils/richParameters";
32-
import { paramsUsedToCreateWorkspace } from "utils/workspace";
3327
import { CreateWorkspacePageViewExperimental } from "./CreateWorkspacePageViewExperimental";
3428
export const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
3529
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
@@ -101,14 +95,8 @@ const CreateWorkspacePageExperimental: FC = () => {
10195
);
10296
const realizedVersionId =
10397
customVersionId ?? templateQuery.data?.active_version_id;
98+
10499
const organizationId = templateQuery.data?.organization_id;
105-
const richParametersQuery = useQuery({
106-
...richParameters(realizedVersionId ?? ""),
107-
enabled: realizedVersionId !== undefined,
108-
});
109-
const realizedParameters = richParametersQuery.data
110-
? richParametersQuery.data.filter(paramsUsedToCreateWorkspace)
111-
: undefined;
112100

113101
const {
114102
externalAuth,
@@ -118,11 +106,8 @@ const CreateWorkspacePageExperimental: FC = () => {
118106
} = useExternalAuth(realizedVersionId);
119107

120108
const isLoadingFormData =
121-
templateQuery.isLoading ||
122-
permissionsQuery.isLoading ||
123-
richParametersQuery.isLoading;
124-
const loadFormDataError =
125-
templateQuery.error ?? permissionsQuery.error ?? richParametersQuery.error;
109+
templateQuery.isLoading || permissionsQuery.isLoading;
110+
const loadFormDataError = templateQuery.error ?? permissionsQuery.error;
126111

127112
const title = autoCreateWorkspaceMutation.isLoading
128113
? "Creating workspace..."
@@ -205,7 +190,6 @@ const CreateWorkspacePageExperimental: FC = () => {
205190
return [...currentResponse.parameters].sort((a, b) => a.order - b.order);
206191
}, [currentResponse?.parameters]);
207192

208-
// console.log("sortedParams", sortedParams);
209193
return (
210194
<>
211195
<Helmet>
@@ -238,9 +222,6 @@ const CreateWorkspacePageExperimental: FC = () => {
238222
startPollingExternalAuth={startPollingExternalAuth}
239223
hasAllRequiredExternalAuth={hasAllRequiredExternalAuth}
240224
permissions={permissionsQuery.data as CreateWorkspacePermissions}
241-
templateVersionParameters={
242-
realizedParameters as TemplateVersionParameter[]
243-
}
244225
parameters={sortedParams}
245226
presets={templateVersionPresetsQuery.data ?? []}
246227
creatingWorkspace={createWorkspaceMutation.isLoading}

site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import { Switch } from "components/Switch/Switch";
1515
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
1616
import { type FormikContextType, useFormik } from "formik";
1717
import { ArrowLeft } from "lucide-react";
18-
import { DynamicParameter } from "modules/workspaces/DynamicParameter/DynamicParameter";
18+
import {
19+
DynamicParameter,
20+
useValidationSchemaForDynamicParameters,
21+
} from "modules/workspaces/DynamicParameter/DynamicParameter";
1922
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
2023
import {
2124
type FC,
@@ -26,11 +29,7 @@ import {
2629
useState,
2730
} from "react";
2831
import { getFormHelpers, nameValidator } from "utils/formUtils";
29-
import {
30-
type AutofillBuildParameter,
31-
getInitialRichParameterValues,
32-
useValidationSchemaForRichParameters,
33-
} from "utils/richParameters";
32+
import type { AutofillBuildParameter } from "utils/richParameters";
3433
import * as Yup from "yup";
3534
import type {
3635
CreateWorkspaceMode,
@@ -60,7 +59,6 @@ export interface CreateWorkspacePageViewExperimentalProps {
6059
permissions: CreateWorkspacePermissions;
6160
presets: TypesGen.Preset[];
6261
template: TypesGen.Template;
63-
templateVersionParameters: TypesGen.TemplateVersionParameter[];
6462
versionId?: string;
6563
onCancel: () => void;
6664
onSubmit: (
@@ -73,7 +71,7 @@ export interface CreateWorkspacePageViewExperimentalProps {
7371
startPollingExternalAuth: () => void;
7472
}
7573

76-
// const getInitialParameterValues = (
74+
// const getInitialParameterValues1 = (
7775
// params: Parameter[],
7876
// autofillParams?: AutofillBuildParameter[],
7977
// ): WorkspaceBuildParameter[] => {
@@ -95,7 +93,7 @@ export interface CreateWorkspacePageViewExperimentalProps {
9593
// name: parameter.name,
9694
// value:
9795
// autofillParam &&
98-
// // isValidValue(parameter, autofillParam) &&
96+
// isValidValue(parameter, autofillParam) &&
9997
// autofillParam.source !== "user_history"
10098
// ? autofillParam.value
10199
// : parameter.default_value,
@@ -129,7 +127,6 @@ export const CreateWorkspacePageViewExperimental: FC<
129127
permissions,
130128
presets = [],
131129
template,
132-
templateVersionParameters,
133130
versionId,
134131
onSubmit,
135132
onCancel,
@@ -157,9 +154,8 @@ export const CreateWorkspacePageViewExperimental: FC<
157154
},
158155
validationSchema: Yup.object({
159156
name: nameValidator("Workspace Name"),
160-
rich_parameter_values: useValidationSchemaForRichParameters(
161-
templateVersionParameters,
162-
),
157+
rich_parameter_values:
158+
useValidationSchemaForDynamicParameters(parameters),
163159
}),
164160
enableReinitialize: true,
165161
validateOnChange: false,

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