Skip to content

Commit 1909c9e

Browse files
committed
fix(FR-2862): trigger deploy/add mutation by calling form.submit() directly
handleOk previously called validateFields().then(submit).catch(() => {}), which silently swallowed validation rejections — including transient races when BAIProjectResourceGroupSelect's autoSelectDefault hadn't populated resourceGroup yet — and the deploy mutation never fired. Call activeForm.submit() directly so antd routes to onFinish on success and onFinishFailed on failure, and consume the values arg directly in handlePresetFinish / handleCustomFinish (no redundant validateFields). Add onFinishFailed to the Preset Form for parity with Custom.
1 parent 75b0a6c commit 1909c9e

1 file changed

Lines changed: 104 additions & 104 deletions

File tree

react/src/components/DeploymentAddRevisionModal.tsx

Lines changed: 104 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import {
6363
Typography,
6464
theme,
6565
} from 'antd';
66+
import type { FormInstance } from 'antd';
6667
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
6768
import {
6869
BAIAvailablePresetSelect,
@@ -850,7 +851,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
850851
}
851852
};
852853

853-
const handleCustomFinish = (values: FormValues) => {
854+
const handleCustomFinish = (values: FormValues): void => {
854855
// `setFields` raises an error programmatically — antd's
855856
// `scrollToFirstError` only fires from `onFinishFailed`, so we have
856857
// to nudge the scroll explicitly here.
@@ -966,113 +967,113 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
966967
? (values.commandModelMount ?? '/models')
967968
: values.mountDestination || '/models';
968969

969-
customForm.validateFields().then(() => {
970-
setIsSubmitting(true);
971-
commitAdd({
972-
variables: {
973-
input: {
974-
deploymentId: toLocalId(deploymentId) ?? deploymentId,
975-
clusterConfig: {
976-
mode: clusterMode,
977-
size: values.cluster_size,
978-
},
979-
resourceConfig: {
980-
resourceGroup: { name: values.resourceGroup },
981-
resourceSlots: { entries: slotEntries },
982-
resourceOpts:
983-
optsEntries.length > 0 ? { entries: optsEntries } : null,
984-
},
985-
image: { id: decodedImageId },
986-
modelRuntimeConfig: {
987-
runtimeVariantId: values.runtimeVariantId,
988-
inferenceRuntimeConfig: null,
989-
environ:
990-
environEntries.length > 0 ? { entries: environEntries } : null,
991-
},
992-
modelMountConfig: {
993-
vfolderId: convertToUUID(values.modelFolderId),
994-
mountDestination,
995-
definitionPath: values.definitionPath,
996-
},
997-
modelDefinition,
998-
extraMounts: extraMounts.length > 0 ? extraMounts : null,
999-
options: { autoActivate },
970+
setIsSubmitting(true);
971+
commitAdd({
972+
variables: {
973+
input: {
974+
deploymentId: toLocalId(deploymentId) ?? deploymentId,
975+
clusterConfig: {
976+
mode: clusterMode,
977+
size: values.cluster_size,
1000978
},
979+
resourceConfig: {
980+
resourceGroup: { name: values.resourceGroup },
981+
resourceSlots: { entries: slotEntries },
982+
resourceOpts:
983+
optsEntries.length > 0 ? { entries: optsEntries } : null,
984+
},
985+
image: { id: decodedImageId },
986+
modelRuntimeConfig: {
987+
runtimeVariantId: values.runtimeVariantId,
988+
inferenceRuntimeConfig: null,
989+
environ:
990+
environEntries.length > 0 ? { entries: environEntries } : null,
991+
},
992+
modelMountConfig: {
993+
vfolderId: convertToUUID(values.modelFolderId),
994+
mountDestination,
995+
definitionPath: values.definitionPath,
996+
},
997+
modelDefinition,
998+
extraMounts: extraMounts.length > 0 ? extraMounts : null,
999+
options: { autoActivate },
10011000
},
1002-
onCompleted: (_, errors) => {
1003-
setIsSubmitting(false);
1004-
if (errors && errors.length > 0) {
1005-
const err = errors[0];
1006-
const isInProgress = err?.message?.includes(
1007-
'Another deployment is already in progress',
1008-
);
1009-
message.error(
1010-
isInProgress
1011-
? t('deployment.AnotherDeploymentInProgress')
1012-
: (err?.message ?? t('general.ErrorOccurred')),
1013-
);
1014-
return;
1015-
}
1016-
customForm.resetFields();
1017-
onRequestClose(true);
1018-
},
1019-
onError: (err) => {
1020-
setIsSubmitting(false);
1021-
const isInProgress = err.message?.includes(
1001+
},
1002+
onCompleted: (_, errors) => {
1003+
setIsSubmitting(false);
1004+
if (errors && errors.length > 0) {
1005+
const err = errors[0];
1006+
const isInProgress = err?.message?.includes(
10221007
'Another deployment is already in progress',
10231008
);
10241009
message.error(
10251010
isInProgress
10261011
? t('deployment.AnotherDeploymentInProgress')
1027-
: (err.message ?? t('general.ErrorOccurred')),
1012+
: (err?.message ?? t('general.ErrorOccurred')),
10281013
);
1029-
},
1030-
});
1014+
return;
1015+
}
1016+
customForm.resetFields();
1017+
onRequestClose(true);
1018+
},
1019+
onError: (err) => {
1020+
setIsSubmitting(false);
1021+
const isInProgress = err.message?.includes(
1022+
'Another deployment is already in progress',
1023+
);
1024+
message.error(
1025+
isInProgress
1026+
? t('deployment.AnotherDeploymentInProgress')
1027+
: (err.message ?? t('general.ErrorOccurred')),
1028+
);
1029+
},
10311030
});
10321031
};
10331032

1034-
const handlePresetFinish = () => {
1033+
const handlePresetFinish = (values: PresetFormValues): void => {
10351034
if (!projectId) {
10361035
message.error(t('general.ErrorOccurred'));
10371036
return;
10381037
}
1039-
presetForm.validateFields().then((values) => {
1040-
setIsSubmitting(true);
1041-
commitDeploy({
1042-
variables: {
1043-
vfolderId: convertToUUID(values.modelFolderId),
1044-
input: {
1045-
projectId,
1046-
revisionPresetId: values.revisionPresetId,
1047-
resourceGroup: values.resourceGroup,
1048-
desiredReplicaCount: 1,
1049-
},
1038+
setIsSubmitting(true);
1039+
commitDeploy({
1040+
variables: {
1041+
vfolderId: convertToUUID(values.modelFolderId),
1042+
input: {
1043+
projectId,
1044+
revisionPresetId: values.revisionPresetId,
1045+
resourceGroup: values.resourceGroup,
1046+
desiredReplicaCount: 1,
10501047
},
1051-
onCompleted: (response, errors) => {
1052-
setIsSubmitting(false);
1053-
if (errors && errors.length > 0) {
1054-
const errMsg = errors.map((e) => e.message).join('\n');
1055-
logger.error(
1056-
'[DeploymentAddRevisionModal] deployVfolderV2 returned errors',
1057-
errors,
1058-
);
1059-
message.error(errMsg || t('modelStore.DeployFailed'));
1060-
return;
1061-
}
1062-
const newDeploymentId = String(response.deployVfolderV2.deploymentId);
1063-
message.success(t('modelStore.DeploySuccess'));
1064-
onRequestClose(true);
1065-
navigate(`/deployments/${newDeploymentId}`);
1066-
},
1067-
onError: (error) => {
1068-
setIsSubmitting(false);
1048+
},
1049+
onCompleted: (response, errors) => {
1050+
setIsSubmitting(false);
1051+
if (errors && errors.length > 0) {
1052+
const errMsg = errors.map((e) => e.message).join('\n');
10691053
logger.error(
1070-
'[DeploymentAddRevisionModal] deployVfolderV2 failed',
1071-
error,
1054+
'[DeploymentAddRevisionModal] deployVfolderV2 returned errors',
1055+
errors,
10721056
);
1073-
message.error(error.message || t('modelStore.DeployFailed'));
1074-
},
1075-
});
1057+
message.error(errMsg || t('modelStore.DeployFailed'));
1058+
return;
1059+
}
1060+
const newDeploymentId = response.deployVfolderV2?.deploymentId
1061+
? String(response.deployVfolderV2.deploymentId)
1062+
: undefined;
1063+
message.success(t('modelStore.DeploySuccess'));
1064+
onRequestClose(true);
1065+
if (newDeploymentId) {
1066+
navigate(`/deployments/${newDeploymentId}`);
1067+
}
1068+
},
1069+
onError: (error) => {
1070+
setIsSubmitting(false);
1071+
logger.error(
1072+
'[DeploymentAddRevisionModal] deployVfolderV2 failed',
1073+
error,
1074+
);
1075+
message.error(error.message || t('modelStore.DeployFailed'));
1076+
},
10761077
});
10771078
};
10781079

@@ -1094,16 +1095,14 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
10941095
};
10951096

10961097
const handleOk = () => {
1098+
// Let antd's built-in submit flow handle validation routing:
1099+
// success → `onFinish` (handle*Finish), failure → `onFinishFailed`
1100+
// (handleFinishFailed). Calling `validateFields()` separately and
1101+
// swallowing rejections in a `.catch` would silently drop submits
1102+
// when validation fails — including transient races during the
1103+
// BAIProjectResourceGroupSelect auto-select.
10971104
const activeForm = effectiveMode === 'preset' ? presetForm : customForm;
1098-
activeForm
1099-
.validateFields()
1100-
.then(() => {
1101-
activeForm.submit();
1102-
})
1103-
.catch(() => {
1104-
// Validation failed — the form's `onFinishFailed` handler scrolls
1105-
// to the first errored field.
1106-
});
1105+
activeForm.submit();
11071106
};
11081107

11091108
const okText =
@@ -1187,6 +1186,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
11871186
layout="vertical"
11881187
style={{ marginTop: token.marginXS }}
11891188
onFinish={handlePresetFinish}
1189+
onFinishFailed={handleFinishFailed}
11901190
initialValues={{
11911191
modelFolderId: defaultModelFolderId,
11921192
}}
@@ -1208,7 +1208,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
12081208
/>
12091209
</Form.Item>
12101210
<Form.Item dependencies={['revisionPresetId']} noStyle>
1211-
{({ getFieldValue }) => {
1211+
{({ getFieldValue }: FormInstance<PresetFormValues>) => {
12121212
const selectedId = getFieldValue('revisionPresetId');
12131213
return (
12141214
<Space.Compact>
@@ -1345,7 +1345,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
13451345
</Form.Item>
13461346

13471347
<Form.Item dependencies={['runtimeVariantId']} noStyle>
1348-
{({ getFieldValue }) => {
1348+
{({ getFieldValue }: FormInstance<FormValues>) => {
13491349
const variantId = getFieldValue('runtimeVariantId');
13501350
const variantName = runtimeVariantNameMap[variantId];
13511351
if (!variantName || variantName === 'custom') return null;
@@ -1376,7 +1376,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
13761376
</Form.Item>
13771377

13781378
<Form.Item dependencies={['runtimeVariantId']} noStyle>
1379-
{({ getFieldValue }) => {
1379+
{({ getFieldValue }: FormInstance<FormValues>) => {
13801380
const variantId = getFieldValue('runtimeVariantId');
13811381
const variantName = runtimeVariantNameMap[variantId];
13821382
if (variantName !== 'custom') {
@@ -1400,7 +1400,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
14001400
/>
14011401
</Form.Item>
14021402
<Form.Item dependencies={['customDefinitionMode']} noStyle>
1403-
{({ getFieldValue: getField }) =>
1403+
{({ getFieldValue: getField }: FormInstance<FormValues>) =>
14041404
getField('customDefinitionMode') === 'command' ? (
14051405
<>
14061406
<Form.Item
@@ -1552,7 +1552,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
15521552
'mount_ids',
15531553
]}
15541554
>
1555-
{({ getFieldValue }) => {
1555+
{({ getFieldValue }: FormInstance<FormValues>) => {
15561556
const modelFolderId = getFieldValue('modelFolderId');
15571557
const modelFolderIdNoDash = modelFolderId
15581558
? String(modelFolderId).replace(/-/g, '')

0 commit comments

Comments
 (0)