From d8af951e7ebe19e764dcc95d2b5b635f6110fdd1 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 30 Jun 2020 10:06:34 -0400 Subject: [PATCH 1/9] wip: add edit action to dfanalytics table --- .../components/analytics_list/action_edit.tsx | 65 +++++++++++++++++++ .../components/analytics_list/actions.tsx | 6 ++ 2 files changed, 71 insertions(+) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx new file mode 100644 index 0000000000000..b6423a96c1037 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext, useState, FC } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; + +import { + checkPermission, + createPermissionFailureMessage, +} from '../../../../../capabilities/check_capabilities'; + +// import { EditAnalyticsFlyout } from '../edit_analytics_flyout'; + +interface EditActionProps { + item: DataFrameAnalyticsListRow; +} + +export const EditAction: FC = ({ item }) => { + const canCreateDataFrameAnalytics: boolean = checkPermission('canCreateDataFrameAnalytics'); + + const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); + const closeFlyout = () => setIsFlyoutVisible(false); + const showFlyout = () => setIsFlyoutVisible(true); + + const buttonEditText = i18n.translate('xpack.ml.dataframe.analyticsList.editActionName', { + defaultMessage: 'Edit', + }); + + const editButton = ( + + {buttonEditText} + + ); + + // if (!canCreateTransform) { + // const content = createCapabilityFailureMessage('canStartStopTransform'); + + // return ( + // + // {editButton} + // + // ); + // } + + return ( + <> + {editButton} + {/* {isFlyoutVisible && } */} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index b47b23f668530..b03a3a4c4edb2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -27,6 +27,7 @@ import { getResultsUrl, isDataFrameAnalyticsRunning, DataFrameAnalyticsListRow } import { stopAnalytics } from '../../services/analytics_service'; import { StartAction } from './action_start'; +import { EditAction } from './action_edit'; import { DeleteAction } from './action_delete'; interface Props { @@ -133,6 +134,11 @@ export const getActions = ( return stopButton; }, }, + { + render: (item: DataFrameAnalyticsListRow) => { + return ; + }, + }, { render: (item: DataFrameAnalyticsListRow) => { return ; From 0ad6733f134515521473d95d4d87dcc913b04ba1 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 30 Jun 2020 17:34:37 -0400 Subject: [PATCH 2/9] add update endpoint and edit flyout --- .../components/analytics_list/action_edit.tsx | 15 +- .../analytics_list/edit_analytics_flyout.tsx | 231 ++++++++++++++++++ .../ml_api_service/data_frame_analytics.ts | 8 + .../ml/server/client/elasticsearch_ml.ts | 15 ++ .../ml/server/routes/data_frame_analytics.ts | 40 +++ .../routes/schemas/data_analytics_schema.ts | 6 + 6 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx index b6423a96c1037..d8f72909f8c54 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx @@ -4,18 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useState, FC } from 'react'; +import React, { useState, FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; // EuiToolTip import { checkPermission, - createPermissionFailureMessage, + // createPermissionFailureMessage, } from '../../../../../capabilities/check_capabilities'; +import { DataFrameAnalyticsListRow } from './common'; -// import { EditAnalyticsFlyout } from '../edit_analytics_flyout'; +import { EditAnalyticsFlyout } from './edit_analytics_flyout'; interface EditActionProps { item: DataFrameAnalyticsListRow; @@ -46,8 +47,8 @@ export const EditAction: FC = ({ item }) => { ); - // if (!canCreateTransform) { - // const content = createCapabilityFailureMessage('canStartStopTransform'); + // if (!canCreateDataFrameAnalytics) { + // const content = createCapabilityFailureMessage('canCreateDataFrameAnalytics'); // return ( // @@ -59,7 +60,7 @@ export const EditAction: FC = ({ item }) => { return ( <> {editButton} - {/* {isFlyoutVisible && } */} + {isFlyoutVisible && } ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx new file mode 100644 index 0000000000000..249f6b5d80d1e --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -0,0 +1,231 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useEffect, useState } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiForm, + EuiFormRow, + EuiOverlayMask, + EuiSelect, + EuiTitle, +} from '@elastic/eui'; + +import { ml } from '../../../../../services/ml_api_service'; +import { memoryInputValidator } from '../../../../../../../common/util/validators'; +import { DataFrameAnalyticsListRow, DATA_FRAME_TASK_STATE } from './common'; + +interface EditAnalyticsJobFlyoutProps { + closeFlyout: () => void; + item: DataFrameAnalyticsListRow; +} + +export const EditAnalyticsFlyout: FC = ({ closeFlyout, item }) => { + const [allowLazyStart, setAllowLazyStart] = useState(''); + const [description, setDescription] = useState(''); + const [modelMemoryLimit, setModelMemoryLimit] = useState(''); + const [mmlValidationError, setMmlValidationError] = useState(''); + + const { id: jobId, config } = item; + const { state } = item.stats; + + const updateButtonDisabled = + allowLazyStart === '' && description === '' && modelMemoryLimit === ''; + + // TODO: only create this function once + const mmLValidator = memoryInputValidator(); + + useEffect(() => { + // validate mml and create validation message + if (modelMemoryLimit !== '') { + const validationResult = mmLValidator(modelMemoryLimit); + if (validationResult !== null && validationResult.invalidUnits) { + setMmlValidationError( + i18n.translate('xpack.ml.dataframe.analytics.create.modelMemoryUnitsInvalidError', { + defaultMessage: 'Model memory limit data unit unrecognized. It must be {str}', + values: { str: validationResult.invalidUnits.allowedUnits }, + }) + ); + } else { + setMmlValidationError(undefined); + } + } + }, [modelMemoryLimit]); + + const onSubmit = async () => { + const updateConfig = Object.assign( + {}, + allowLazyStart && { allow_lazy_start: allowLazyStart }, + description && { description }, + modelMemoryLimit && { model_memory_limit: modelMemoryLimit } + ); + + try { + await ml.dataFrameAnalytics.updateDataFrameAnalytics(jobId, updateConfig); + // TODO: show some success toast + closeFlyout(); + } catch (e) { + // TODO: show error callouts + // eslint-disable-next-line + console.log('--- ERROR ---', JSON.stringify(e, null, 2)); // remove + } + }; + + return ( + + + + +

+ {i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutTitle', { + defaultMessage: 'Edit {jobId}', + values: { + jobId, + }, + })} +

+
+
+ + + + setAllowLazyStart(e.target.value)} + /> + + + setDescription(e.target.value)} + aria-label={i18n.translate( + 'xpack.ml.dataframe.analyticsList.editFlyout.descriptionAriaLabel', + { + defaultMessage: 'Update the job description.', + } + )} + /> + + + setModelMemoryLimit(e.target.value)} + aria-label={i18n.translate( + 'xpack.ml.dataframe.analyticsList.editFlyout.modelMemoryLimitAriaLabel', + { + defaultMessage: 'Update the model memory limit.', + } + )} + /> + + + + + + + + {i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutCancelButtonText', { + defaultMessage: 'Cancel', + })} + + + + + {i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutUpdateButtonText', { + defaultMessage: 'Update', + })} + + + + +
+
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts index 7cdd5478e3983..3d12ccb5f84c7 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts @@ -72,6 +72,14 @@ export const dataFrameAnalytics = { body, }); }, + updateDataFrameAnalytics(analyticsId: string, updateConfig: any) { + const body = JSON.stringify(updateConfig); + return http({ + path: `${basePath()}/data_frame/analytics/${analyticsId}/_update`, + method: 'POST', + body, + }); + }, evaluateDataFrameAnalytics(evaluateConfig: any) { const body = JSON.stringify(evaluateConfig); return http({ diff --git a/x-pack/plugins/ml/server/client/elasticsearch_ml.ts b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts index 07159534e1e2c..24c80c450f61a 100644 --- a/x-pack/plugins/ml/server/client/elasticsearch_ml.ts +++ b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts @@ -223,6 +223,21 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any) method: 'POST', }); + ml.updateDataFrameAnalytics = ca({ + urls: [ + { + fmt: '/_ml/data_frame/analytics/<%=analyticsId%>/_update', + req: { + analyticsId: { + type: 'string', + }, + }, + }, + ], + needBody: true, + method: 'POST', + }); + ml.deleteJob = ca({ urls: [ { diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index e2601c7ad6a2e..24be23332e4cf 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -10,6 +10,7 @@ import { analyticsAuditMessagesProvider } from '../models/data_frame_analytics/a import { RouteInitialization } from '../types'; import { dataAnalyticsJobConfigSchema, + dataAnalyticsJobUpdateSchema, dataAnalyticsEvaluateSchema, dataAnalyticsExplainSchema, analyticsIdSchema, @@ -483,6 +484,45 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat }) ); + /** + * @apiGroup DataFrameAnalytics + * + * @api {post} /api/ml/data_frame/analytics/:analyticsId/_update Update specified analytics job + * @apiName UpdateDataFrameAnalyticsJob + * @apiDescription Updates a data frame analytics job. + * + * @apiSchema (params) analyticsIdSchema + */ + router.post( + { + path: '/api/ml/data_frame/analytics/{analyticsId}/_update', + validate: { + params: analyticsIdSchema, + body: dataAnalyticsJobUpdateSchema, + }, + options: { + tags: ['access:ml:canCreateDataFrameAnalytics'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const { analyticsId } = request.params; + const results = await context.ml!.mlClient.callAsCurrentUser( + 'ml.updateDataFrameAnalytics', + { + body: request.body, + analyticsId, + } + ); + return response.ok({ + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + /** * @apiGroup DataFrameAnalytics * diff --git a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts b/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts index e6b4e4ccf8582..5469c2fefdf33 100644 --- a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts @@ -69,6 +69,12 @@ export const deleteDataFrameAnalyticsJobSchema = schema.object({ deleteDestIndexPattern: schema.maybe(schema.boolean()), }); +export const dataAnalyticsJobUpdateSchema = schema.object({ + description: schema.maybe(schema.string()), + model_memory_limit: schema.maybe(schema.string()), + allow_lazy_start: schema.maybe(schema.boolean()), +}); + export const stopsDataFrameAnalyticsJobQuerySchema = schema.object({ force: schema.maybe(schema.boolean()), }); From 59e7f217e3248a2b033c363a07979ed06691c2a6 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 1 Jul 2020 13:12:05 -0400 Subject: [PATCH 3/9] show success and error toasts. close flyout and refresh on success --- .../analytics_list/edit_analytics_flyout.tsx | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index 249f6b5d80d1e..407a1dc4b89d9 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -25,20 +25,29 @@ import { EuiTitle, } from '@elastic/eui'; +import { useMlKibana } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; import { memoryInputValidator } from '../../../../../../../common/util/validators'; import { DataFrameAnalyticsListRow, DATA_FRAME_TASK_STATE } from './common'; +import { useRefreshAnalyticsList } from '../../../../common/analytics'; interface EditAnalyticsJobFlyoutProps { closeFlyout: () => void; item: DataFrameAnalyticsListRow; } +let mmLValidator: any; + export const EditAnalyticsFlyout: FC = ({ closeFlyout, item }) => { const [allowLazyStart, setAllowLazyStart] = useState(''); const [description, setDescription] = useState(''); const [modelMemoryLimit, setModelMemoryLimit] = useState(''); - const [mmlValidationError, setMmlValidationError] = useState(''); + const [mmlValidationError, setMmlValidationError] = useState(); + + const { + services: { notifications }, + } = useMlKibana(); + const { refresh } = useRefreshAnalyticsList(); const { id: jobId, config } = item; const { state } = item.stats; @@ -46,10 +55,10 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo const updateButtonDisabled = allowLazyStart === '' && description === '' && modelMemoryLimit === ''; - // TODO: only create this function once - const mmLValidator = memoryInputValidator(); - useEffect(() => { + if (mmLValidator === undefined) { + mmLValidator = memoryInputValidator(); + } // validate mml and create validation message if (modelMemoryLimit !== '') { const validationResult = mmLValidator(modelMemoryLimit); @@ -63,6 +72,8 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo } else { setMmlValidationError(undefined); } + } else { + setMmlValidationError(undefined); } }, [modelMemoryLimit]); @@ -76,12 +87,26 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo try { await ml.dataFrameAnalytics.updateDataFrameAnalytics(jobId, updateConfig); - // TODO: show some success toast + notifications.toasts.addSuccess( + i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutSuccessMessage', { + defaultMessage: 'Analytics job {jobId} has been updated.', + values: { jobId }, + }) + ); + refresh(); closeFlyout(); } catch (e) { - // TODO: show error callouts // eslint-disable-next-line - console.log('--- ERROR ---', JSON.stringify(e, null, 2)); // remove + console.error(e); + + notifications.toasts.addDanger( + i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutErrorMessage', { + defaultMessage: 'Could not save changes to {jobId}', + values: { + jobId, + }, + }) + ); } }; From 0db640dec0bb9ace4a773eaef15fedf79d6ac9d8 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 1 Jul 2020 14:15:03 -0400 Subject: [PATCH 4/9] show permission message in edit action --- .../components/analytics_list/action_edit.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx index d8f72909f8c54..041b52d0322c4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_edit.tsx @@ -8,12 +8,9 @@ import React, { useState, FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty } from '@elastic/eui'; // EuiToolTip +import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; -import { - checkPermission, - // createPermissionFailureMessage, -} from '../../../../../capabilities/check_capabilities'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; import { DataFrameAnalyticsListRow } from './common'; import { EditAnalyticsFlyout } from './edit_analytics_flyout'; @@ -47,15 +44,18 @@ export const EditAction: FC = ({ item }) => { ); - // if (!canCreateDataFrameAnalytics) { - // const content = createCapabilityFailureMessage('canCreateDataFrameAnalytics'); - - // return ( - // - // {editButton} - // - // ); - // } + if (!canCreateDataFrameAnalytics) { + return ( + + {editButton} + + ); + } return ( <> From ffedb7cd437861adf880771b001b04de250e3980 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 2 Jul 2020 11:11:09 -0400 Subject: [PATCH 5/9] update types --- x-pack/plugins/ml/common/util/validators.ts | 2 ++ .../data_frame_analytics/common/analytics.ts | 7 ++++++- .../data_frame_analytics/common/index.ts | 1 + .../analytics_list/edit_analytics_flyout.tsx | 16 +++++++++++----- .../ml_api_service/data_frame_analytics.ts | 7 +++++-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/ml/common/util/validators.ts b/x-pack/plugins/ml/common/util/validators.ts index 5dcdec0553106..c14c20917a136 100644 --- a/x-pack/plugins/ml/common/util/validators.ts +++ b/x-pack/plugins/ml/common/util/validators.ts @@ -67,6 +67,8 @@ export function requiredValidator() { export type ValidationResult = object | null; +export type MemoryInputValidatorResult = { invalidUnits: { allowedUnits: string } } | null; + export function memoryInputValidator(allowedUnits = ALLOWED_DATA_UNITS) { return (value: any) => { if (typeof value !== 'string' || value === '') { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 5715687402bcb..aa637f71db1cc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -327,9 +327,14 @@ export const isClassificationEvaluateResponse = ( ); }; +export interface UpdateDataFrameAnalyticsConfig { + allow_lazy_start?: string; + description?: string; + model_memory_limit?: string; +} + export interface DataFrameAnalyticsConfig { id: DataFrameAnalyticsId; - // Description attribute is not supported yet description?: string; dest: { index: IndexName; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts index 58343e26153cc..65531009e4436 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts @@ -13,6 +13,7 @@ export { useRefreshAnalyticsList, DataFrameAnalyticsId, DataFrameAnalyticsConfig, + UpdateDataFrameAnalyticsConfig, IndexName, IndexPattern, REFRESH_ANALYTICS_LIST_STATE, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index 407a1dc4b89d9..9f31154baccdd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -27,16 +27,22 @@ import { import { useMlKibana } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; -import { memoryInputValidator } from '../../../../../../../common/util/validators'; +import { + memoryInputValidator, + MemoryInputValidatorResult, +} from '../../../../../../../common/util/validators'; import { DataFrameAnalyticsListRow, DATA_FRAME_TASK_STATE } from './common'; -import { useRefreshAnalyticsList } from '../../../../common/analytics'; +import { + useRefreshAnalyticsList, + UpdateDataFrameAnalyticsConfig, +} from '../../../../common/analytics'; interface EditAnalyticsJobFlyoutProps { closeFlyout: () => void; item: DataFrameAnalyticsListRow; } -let mmLValidator: any; +let mmLValidator: (value: any) => MemoryInputValidatorResult; export const EditAnalyticsFlyout: FC = ({ closeFlyout, item }) => { const [allowLazyStart, setAllowLazyStart] = useState(''); @@ -78,7 +84,7 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo }, [modelMemoryLimit]); const onSubmit = async () => { - const updateConfig = Object.assign( + const updateConfig: UpdateDataFrameAnalyticsConfig | {} = Object.assign( {}, allowLazyStart && { allow_lazy_start: allowLazyStart }, description && { description }, @@ -101,7 +107,7 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo notifications.toasts.addDanger( i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutErrorMessage', { - defaultMessage: 'Could not save changes to {jobId}', + defaultMessage: 'Could not save changes to analytics job {jobId}', values: { jobId, }, diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts index 3d12ccb5f84c7..7de39d91047ef 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts @@ -8,7 +8,10 @@ import { http } from '../http_service'; import { basePath } from './index'; import { DataFrameAnalyticsStats } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; -import { DataFrameAnalyticsConfig } from '../../data_frame_analytics/common'; +import { + DataFrameAnalyticsConfig, + UpdateDataFrameAnalyticsConfig, +} from '../../data_frame_analytics/common'; import { DeepPartial } from '../../../../common/types/common'; import { DeleteDataFrameAnalyticsWithIndexStatus } from '../../../../common/types/data_frame_analytics'; @@ -72,7 +75,7 @@ export const dataFrameAnalytics = { body, }); }, - updateDataFrameAnalytics(analyticsId: string, updateConfig: any) { + updateDataFrameAnalytics(analyticsId: string, updateConfig: UpdateDataFrameAnalyticsConfig) { const body = JSON.stringify(updateConfig); return http({ path: `${basePath()}/data_frame/analytics/${analyticsId}/_update`, From 9ac1b43457875fde6af3687bef9960a7a228a34a Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 2 Jul 2020 14:01:11 -0400 Subject: [PATCH 6/9] disable update button if mml not valid --- .../components/analytics_list/edit_analytics_flyout.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index 9f31154baccdd..d0bd886ad8ff5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -58,8 +58,10 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo const { id: jobId, config } = item; const { state } = item.stats; + // Disable if all fields are empty or mml is not valid const updateButtonDisabled = - allowLazyStart === '' && description === '' && modelMemoryLimit === ''; + (allowLazyStart === '' && description === '' && modelMemoryLimit === '') || + mmlValidationError !== undefined; useEffect(() => { if (mmLValidator === undefined) { @@ -176,7 +178,9 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo ]} value={allowLazyStart} hasNoInitialSelection={true} - onChange={(e: any) => setAllowLazyStart(e.target.value)} + onChange={(e: React.ChangeEvent) => + setAllowLazyStart(e.target.value) + } /> Date: Mon, 6 Jul 2020 12:26:01 -0400 Subject: [PATCH 7/9] show error in toast, init values are config values --- .../analytics_list/edit_analytics_flyout.tsx | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index d0bd886ad8ff5..dad07b822471a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -31,6 +31,7 @@ import { memoryInputValidator, MemoryInputValidatorResult, } from '../../../../../../../common/util/validators'; +import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { DataFrameAnalyticsListRow, DATA_FRAME_TASK_STATE } from './common'; import { useRefreshAnalyticsList, @@ -45,9 +46,13 @@ interface EditAnalyticsJobFlyoutProps { let mmLValidator: (value: any) => MemoryInputValidatorResult; export const EditAnalyticsFlyout: FC = ({ closeFlyout, item }) => { - const [allowLazyStart, setAllowLazyStart] = useState(''); - const [description, setDescription] = useState(''); - const [modelMemoryLimit, setModelMemoryLimit] = useState(''); + const { id: jobId, config } = item; + const { state } = item.stats; + const initialAllowLazyStart = config.allow_lazy_start ? String(config.allow_lazy_start) : ''; + + const [allowLazyStart, setAllowLazyStart] = useState(initialAllowLazyStart); + const [description, setDescription] = useState(config.description || ''); + const [modelMemoryLimit, setModelMemoryLimit] = useState(config.model_memory_limit); const [mmlValidationError, setMmlValidationError] = useState(); const { @@ -55,13 +60,8 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo } = useMlKibana(); const { refresh } = useRefreshAnalyticsList(); - const { id: jobId, config } = item; - const { state } = item.stats; - // Disable if all fields are empty or mml is not valid - const updateButtonDisabled = - (allowLazyStart === '' && description === '' && modelMemoryLimit === '') || - mmlValidationError !== undefined; + const updateButtonDisabled = mmlValidationError !== undefined; useEffect(() => { if (mmLValidator === undefined) { @@ -86,10 +86,11 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo }, [modelMemoryLimit]); const onSubmit = async () => { - const updateConfig: UpdateDataFrameAnalyticsConfig | {} = Object.assign( - {}, - allowLazyStart && { allow_lazy_start: allowLazyStart }, - description && { description }, + const updateConfig: UpdateDataFrameAnalyticsConfig = Object.assign( + { + allow_lazy_start: allowLazyStart, + description, + }, modelMemoryLimit && { model_memory_limit: modelMemoryLimit } ); @@ -107,14 +108,15 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo // eslint-disable-next-line console.error(e); - notifications.toasts.addDanger( - i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutErrorMessage', { + notifications.toasts.addDanger({ + title: i18n.translate('xpack.ml.dataframe.analyticsList.editFlyoutErrorMessage', { defaultMessage: 'Could not save changes to analytics job {jobId}', values: { jobId, }, - }) - ); + }), + text: extractErrorMessage(e), + }); } }; @@ -177,7 +179,6 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo }, ]} value={allowLazyStart} - hasNoInitialSelection={true} onChange={(e: React.ChangeEvent) => setAllowLazyStart(e.target.value) } @@ -193,7 +194,6 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo > setDescription(e.target.value)} aria-label={i18n.translate( @@ -222,7 +222,6 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo > Date: Mon, 6 Jul 2020 12:28:28 -0400 Subject: [PATCH 8/9] fix undefined check for allow lazy start --- .../components/analytics_list/edit_analytics_flyout.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index dad07b822471a..5b7257f85d589 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -48,7 +48,8 @@ let mmLValidator: (value: any) => MemoryInputValidatorResult; export const EditAnalyticsFlyout: FC = ({ closeFlyout, item }) => { const { id: jobId, config } = item; const { state } = item.stats; - const initialAllowLazyStart = config.allow_lazy_start ? String(config.allow_lazy_start) : ''; + const initialAllowLazyStart = + config.allow_lazy_start !== undefined ? String(config.allow_lazy_start) : ''; const [allowLazyStart, setAllowLazyStart] = useState(initialAllowLazyStart); const [description, setDescription] = useState(config.description || ''); From 4783bab233f94fe3754dd87ac957d1cf49d3f8d8 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 6 Jul 2020 12:52:35 -0400 Subject: [PATCH 9/9] prevent update if mml is empty --- .../components/analytics_list/edit_analytics_flyout.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx index 5b7257f85d589..b6aed9321e4e3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/edit_analytics_flyout.tsx @@ -61,7 +61,7 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo } = useMlKibana(); const { refresh } = useRefreshAnalyticsList(); - // Disable if all fields are empty or mml is not valid + // Disable if mml is not valid const updateButtonDisabled = mmlValidationError !== undefined; useEffect(() => { @@ -82,7 +82,11 @@ export const EditAnalyticsFlyout: FC = ({ closeFlyo setMmlValidationError(undefined); } } else { - setMmlValidationError(undefined); + setMmlValidationError( + i18n.translate('xpack.ml.dataframe.analytics.create.modelMemoryEmptyError', { + defaultMessage: 'Model memory limit must not be empty', + }) + ); } }, [modelMemoryLimit]);