From 6e9cc05862a04d1df1bd6e40865154edddb7c4ae Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Wed, 11 Sep 2019 12:11:34 +0200 Subject: [PATCH 1/8] [ML] Enhance job id error message --- .../plugins/ml/server/models/job_validation/messages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js index 72b26d2163045..ba5686263c8ba 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js @@ -248,7 +248,7 @@ export const getMessages = () => { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdInvalidMessage', { defaultMessage: 'The job name is invalid. It can contain lowercase alphanumeric (a-z and 0-9) characters, ' + - 'hyphens or underscores and must start and end with an alphanumeric character.', + 'hyphens or underscores and must start and end with an alphanumeric character. The max length is 64 characters.', }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, @@ -259,7 +259,7 @@ export const getMessages = () => { }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character.', + 'starts and ends with an alphanumeric character, and is less than 64 characters.', }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, From c479a446595820cb168f064302faf2be1decb510 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Fri, 13 Sep 2019 18:32:23 +0200 Subject: [PATCH 2/8] [ML] maxLengthValidator, job id validation messages --- .../plugins/ml/common/util/job_utils.js | 7 ++++- .../plugins/ml/common/util/validators.test.ts | 22 ++++++++++++++++ .../plugins/ml/common/util/validators.ts | 26 +++++++++++++++++++ .../server/models/job_validation/messages.js | 14 ++++++++-- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 x-pack/legacy/plugins/ml/common/util/validators.test.ts create mode 100644 x-pack/legacy/plugins/ml/common/util/validators.ts diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.js b/x-pack/legacy/plugins/ml/common/util/job_utils.js index b3d3e182ee426..49e3608f09151 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.js @@ -12,6 +12,7 @@ import numeral from '@elastic/numeral'; import { ALLOWED_DATA_UNITS } from '../constants/validation'; import { parseInterval } from './parse_interval'; +import { maxLengthValidator } from './validators'; // work out the default frequency based on the bucket_span in seconds export function calculateDatafeedFrequencyDefaultSeconds(bucketSpanSeconds) { @@ -223,7 +224,7 @@ export function mlFunctionToESAggregation(functionName) { // Job name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores; // it must also start and end with an alphanumeric character' export function isJobIdValid(jobId) { - return (jobId.match(/^[a-z0-9\-\_]{1,64}$/g) && !jobId.match(/^([_-].*)?(.*[_-])?$/g)) ? true : false; + return (jobId.match(/^[a-z0-9\-\_]+$/g) && !jobId.match(/^([_-].*)?(.*[_-])?$/g)) ? true : false; } // To get median data for jobs and charts we need to use Elasticsearch's @@ -271,6 +272,7 @@ export function basicJobValidation(job, fields, limits, skipMmlChecks = false) { let valid = true; if (job) { + const JOB_ID_MAX_LENGTH = 64; // Job details if (_.isEmpty(job.job_id)) { messages.push({ id: 'job_id_empty' }); @@ -278,6 +280,9 @@ export function basicJobValidation(job, fields, limits, skipMmlChecks = false) { } else if (isJobIdValid(job.job_id) === false) { messages.push({ id: 'job_id_invalid' }); valid = false; + } else if (maxLengthValidator(JOB_ID_MAX_LENGTH)(job.job_id)) { + messages.push({ id: 'invalid_max_length', maxLength: JOB_ID_MAX_LENGTH }); + valid = false; } else { messages.push({ id: 'job_id_valid' }); } diff --git a/x-pack/legacy/plugins/ml/common/util/validators.test.ts b/x-pack/legacy/plugins/ml/common/util/validators.test.ts new file mode 100644 index 0000000000000..8b55e955a3953 --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/util/validators.test.ts @@ -0,0 +1,22 @@ +/* + * 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 { maxLengthValidator } from './validators'; + +describe('maxLengthValidator', () => { + test('should allow a valid input', () => { + expect(maxLengthValidator(2)('xx')).toBe(null); + }); + + test('should describe an invalid input', () => { + expect(maxLengthValidator(3)('example')).toEqual({ + maxLength: { + requiredLength: 3, + actualLength: 7, + }, + }); + }); +}); diff --git a/x-pack/legacy/plugins/ml/common/util/validators.ts b/x-pack/legacy/plugins/ml/common/util/validators.ts new file mode 100644 index 0000000000000..7688376f1453c --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/util/validators.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Provides a validator function for maximum allowed input length. + * @param maxLength Maximum length allowed. + */ +export function maxLengthValidator( + maxLength: number +): (value: string) => { maxLength: { requiredLength: number; actualLength: number } } | null { + return value => { + if (value && value.length > maxLength) { + return { + maxLength: { + requiredLength: maxLength, + actualLength: value.length, + }, + }; + } else { + return null; + } + }; +} diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js index ba5686263c8ba..1906db3da8a21 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js @@ -248,7 +248,17 @@ export const getMessages = () => { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdInvalidMessage', { defaultMessage: 'The job name is invalid. It can contain lowercase alphanumeric (a-z and 0-9) characters, ' + - 'hyphens or underscores and must start and end with an alphanumeric character. The max length is 64 characters.', + 'hyphens or underscores and must start and end with an alphanumeric character.', + }), + url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' + }, + invalid_max_length: { + status: 'ERROR', + text: i18n.translate('xpack.ml.validation.messages.invalidMaxLengthErrorMessage', { + defaultMessage: 'The max length is {maxLength, plural, one {# character} other {# characters}}.', + values: { + maxLength: '{{maxLength}}', + }, }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, @@ -278,7 +288,7 @@ export const getMessages = () => { }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character.', + 'starts and ends with an alphanumeric character, and is less than 64 characters.', }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, From dbc81e1ffd8e67dd53b4069be93a279074a146c2 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Mon, 16 Sep 2019 15:36:17 +0200 Subject: [PATCH 3/8] [ML] job id and job group id validation messages --- .../plugins/ml/common/constants/validation.ts | 2 ++ .../plugins/ml/common/util/job_utils.js | 29 +++++++------------ .../plugins/ml/common/util/validators.ts | 21 ++++++-------- .../new_job_new/common/job_validator/util.ts | 26 ++++++++++++++++- .../server/models/job_validation/messages.js | 29 +++++++++++++++---- 5 files changed, 69 insertions(+), 38 deletions(-) diff --git a/x-pack/legacy/plugins/ml/common/constants/validation.ts b/x-pack/legacy/plugins/ml/common/constants/validation.ts index c71db4dca3c99..780e5ca3ac441 100644 --- a/x-pack/legacy/plugins/ml/common/constants/validation.ts +++ b/x-pack/legacy/plugins/ml/common/constants/validation.ts @@ -14,3 +14,5 @@ export enum VALIDATION_STATUS { export const SKIP_BUCKET_SPAN_ESTIMATION = true; export const ALLOWED_DATA_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + +export const JOB_ID_MAX_LENGTH = 64; diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.js b/x-pack/legacy/plugins/ml/common/util/job_utils.js index 49e3608f09151..fb1ea7677b83a 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.js @@ -10,7 +10,7 @@ import _ from 'lodash'; import semver from 'semver'; import numeral from '@elastic/numeral'; -import { ALLOWED_DATA_UNITS } from '../constants/validation'; +import { ALLOWED_DATA_UNITS, JOB_ID_MAX_LENGTH } from '../constants/validation'; import { parseInterval } from './parse_interval'; import { maxLengthValidator } from './validators'; @@ -272,7 +272,6 @@ export function basicJobValidation(job, fields, limits, skipMmlChecks = false) { let valid = true; if (job) { - const JOB_ID_MAX_LENGTH = 64; // Job details if (_.isEmpty(job.job_id)) { messages.push({ id: 'job_id_empty' }); @@ -281,7 +280,7 @@ export function basicJobValidation(job, fields, limits, skipMmlChecks = false) { messages.push({ id: 'job_id_invalid' }); valid = false; } else if (maxLengthValidator(JOB_ID_MAX_LENGTH)(job.job_id)) { - messages.push({ id: 'invalid_max_length', maxLength: JOB_ID_MAX_LENGTH }); + messages.push({ id: 'job_id_invalid_max_length', maxLength: JOB_ID_MAX_LENGTH }); valid = false; } else { messages.push({ id: 'job_id_valid' }); @@ -492,22 +491,14 @@ export function validateModelMemoryLimitUnits(job) { } export function validateGroupNames(job) { - const messages = []; - let valid = true; - if (job.groups !== undefined) { - let groupIdValid = true; - job.groups.forEach(group => { - if (isJobIdValid(group) === false) { - groupIdValid = false; - valid = false; - } - }); - if (job.groups.length > 0 && groupIdValid) { - messages.push({ id: 'job_group_id_valid' }); - } else if (job.groups.length > 0 && !groupIdValid) { - messages.push({ id: 'job_group_id_invalid' }); - } - } + const { groups = [] } = job; + const errorMessages = [ + ...groups.some(group => !isJobIdValid(group)) ? [{ id: 'job_group_id_invalid' }] : [], + ...groups.some(group => maxLengthValidator(JOB_ID_MAX_LENGTH)(group)) ? [{ id: 'job_group_id_invalid_max_length' }] : [], + ]; + const valid = errorMessages.length === 0; + const messages = valid ? [{ id: 'job_group_id_valid' }] : errorMessages; + return { valid, messages, diff --git a/x-pack/legacy/plugins/ml/common/util/validators.ts b/x-pack/legacy/plugins/ml/common/util/validators.ts index 7688376f1453c..746b9ac3de080 100644 --- a/x-pack/legacy/plugins/ml/common/util/validators.ts +++ b/x-pack/legacy/plugins/ml/common/util/validators.ts @@ -11,16 +11,13 @@ export function maxLengthValidator( maxLength: number ): (value: string) => { maxLength: { requiredLength: number; actualLength: number } } | null { - return value => { - if (value && value.length > maxLength) { - return { - maxLength: { - requiredLength: maxLength, - actualLength: value.length, - }, - }; - } else { - return null; - } - }; + return value => + value && value.length > maxLength + ? { + maxLength: { + requiredLength: maxLength, + actualLength: value.length, + }, + } + : null; } diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts index 784114364c94d..288c24e9e40cd 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { BasicValidations } from './job_validator'; import { Job } from '../job_creator/configs'; -import { ALLOWED_DATA_UNITS } from '../../../../../common/constants/validation'; +import { ALLOWED_DATA_UNITS, JOB_ID_MAX_LENGTH } from '../../../../../common/constants/validation'; import { newJobLimits } from '../../../new_job/utils/new_job_defaults'; import { ValidationResults, ValidationMessage } from '../../../../../common/util/job_utils'; import { ExistingJobsAndGroups } from '../../../../services/job_service'; @@ -32,6 +32,18 @@ export function populateValidationMessages( } ); basicValidations.jobId.message = msg; + } else if (validationResults.contains('job_id_invalid_max_length')) { + basicValidations.jobId.valid = false; + basicValidations.jobId.message = i18n.translate( + 'xpack.ml.newJob.wizard.validateJob.jobIdInvalidMaxLengthErrorMessage', + { + defaultMessage: + "Job name can't contain more than {maxLength, plural, one {# character} other {# characters}}.", + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, + } + ); } else if (validationResults.contains('job_id_already_exists')) { basicValidations.jobId.valid = false; const msg = i18n.translate('xpack.ml.newJob.wizard.validateJob.jobNameAlreadyExists', { @@ -52,6 +64,18 @@ export function populateValidationMessages( } ); basicValidations.groupIds.message = msg; + } else if (validationResults.contains('job_group_id_invalid_max_length')) { + basicValidations.groupIds.valid = false; + basicValidations.groupIds.message = i18n.translate( + 'xpack.ml.newJob.wizard.validateJob.jobGroupMaxLengthDescription', + { + defaultMessage: + "Job group name can't contain more than {maxLength, plural, one {# character} other {# characters}}.", + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, + } + ); } else if (validationResults.contains('job_group_id_already_exists')) { basicValidations.groupIds.valid = false; const msg = i18n.translate('xpack.ml.newJob.wizard.validateJob.groupNameAlreadyExists', { diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js index 1906db3da8a21..d8b7fdf4b874f 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; +import { JOB_ID_MAX_LENGTH } from '../../../common/constants/validation'; let messages; @@ -252,12 +253,12 @@ export const getMessages = () => { }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, - invalid_max_length: { + job_id_invalid_max_length: { status: 'ERROR', - text: i18n.translate('xpack.ml.validation.messages.invalidMaxLengthErrorMessage', { - defaultMessage: 'The max length is {maxLength, plural, one {# character} other {# characters}}.', + text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdInvalidMaxLengthErrorMessage', { + defaultMessage: 'Job name can\'t contain more than {maxLength, plural, one {# character} other {# characters}}.', values: { - maxLength: '{{maxLength}}', + maxLength: JOB_ID_MAX_LENGTH, }, }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' @@ -269,7 +270,10 @@ export const getMessages = () => { }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character, and is less than 64 characters.', + 'starts and ends with an alphanumeric character, and is less than {maxLength, plural, one {# character} other {# characters}}.', + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, @@ -281,6 +285,16 @@ export const getMessages = () => { }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, + job_group_id_invalid_max_length: { + status: 'ERROR', + text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage', { + defaultMessage: 'Job group name can\'t contain more than {maxLength, plural, one {# character} other {# characters}}.', + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, + }), + url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' + }, job_group_id_valid: { status: 'SUCCESS', heading: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading', { @@ -288,7 +302,10 @@ export const getMessages = () => { }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character, and is less than 64 characters.', + 'starts and ends with an alphanumeric character, and is less than {maxLength, plural, one {# character} other {# characters}}.', + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, From 5666aecafaa1a7050ed5543090041f9b238a7986 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Mon, 16 Sep 2019 17:17:24 +0200 Subject: [PATCH 4/8] [ML] fix i18n --- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 2 files changed, 4 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d481827ffe5fc..bf26eab59e12a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6423,11 +6423,9 @@ "xpack.ml.models.jobValidation.messages.influencerLowSuggestionsMessage": "影響因子が構成されていません。{influencerSuggestion} を 1 つまたは複数使用することをお勧めします。", "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMessage": "ジョブグループ名の 1 つが無効です。アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading": "ジョブグループ ID のフォーマットは有効です。", - "xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage": "アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobIdEmptyMessage": "ジョブ名フィールドは未入力のままにできません。", "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "ジョブ名が無効です。アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "ジョブ ID のフォーマットは有効です。", - "xpack.ml.models.jobValidation.messages.jobIdValidMessage": "アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.mmlGreaterThanMaxMmlMessage": "モデルメモリー制限が、このクラスターに構成された最大モデルメモリー制限を超えています。", "xpack.ml.models.jobValidation.messages.mmlValueInvalidMessage": "{mml} はモデルメモリー制限の有効な値ではありません。この値は最低 1MB で、バイト (例: 10MB) で指定する必要があります。", "xpack.ml.models.jobValidation.messages.skippedExtendedTestsMessage": "ジョブの構成の基本要件が満たされていないため、他のチェックをスキップしました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 32211c71d2fa8..925fe8fabada7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6566,11 +6566,9 @@ "xpack.ml.models.jobValidation.messages.influencerLowSuggestionsMessage": "尚未配置任何影响因素。考虑使用一个或多个 {influencerSuggestion}。", "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMessage": "有一个作业组名称无效。它们可以包含小写字母数字(a-z 和 0-9)字符、连字符或下划线,必须以字母数字字符开头和结尾", "xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading": "作业 ID 格式有效。", - "xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage": "小写字母数字(a-z 和 0-9)字符、连字符或下划线,以字母数字字符开头和结尾。", "xpack.ml.models.jobValidation.messages.jobIdEmptyMessage": "作业名称字段不得为空。", "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "作业名称无效。其可以包含小写字母数字(a-z 和 0-9)字符、连字符或下划线,必须以字母数字字符开头和结尾", "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "作业 ID 格式有效。", - "xpack.ml.models.jobValidation.messages.jobIdValidMessage": "小写字母数字(a-z 和 0-9)字符、连字符或下划线,以字母数字字符开头和结尾。", "xpack.ml.models.jobValidation.messages.mmlGreaterThanMaxMmlMessage": "模型内存限制大于为此集群配置的最大模型内存限制。", "xpack.ml.models.jobValidation.messages.mmlValueInvalidMessage": "{mml} 不是有效的模型内存限制值。该值需要至少 1MB,且应以字节为单位(例如 10MB)指定。", "xpack.ml.models.jobValidation.messages.skippedExtendedTestsMessage": "已跳过其他检查,因为未满足作业配置的基本要求。", From 0e3bc0ea32e064eddad885172854227336cd1643 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Mon, 16 Sep 2019 17:41:19 +0200 Subject: [PATCH 5/8] [ML] rewording --- .../new_job_new/common/job_validator/util.ts | 6 +++--- .../ml/server/models/job_validation/messages.js | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts index 288c24e9e40cd..cfcfde26149df 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_validator/util.ts @@ -27,7 +27,7 @@ export function populateValidationMessages( 'xpack.ml.newJob.wizard.validateJob.jobNameAllowedCharactersDescription', { defaultMessage: - 'Job name can contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores; ' + + 'Job ID can contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores; ' + 'must start and end with an alphanumeric character', } ); @@ -38,7 +38,7 @@ export function populateValidationMessages( 'xpack.ml.newJob.wizard.validateJob.jobIdInvalidMaxLengthErrorMessage', { defaultMessage: - "Job name can't contain more than {maxLength, plural, one {# character} other {# characters}}.", + 'Job ID must be no more than {maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, @@ -70,7 +70,7 @@ export function populateValidationMessages( 'xpack.ml.newJob.wizard.validateJob.jobGroupMaxLengthDescription', { defaultMessage: - "Job group name can't contain more than {maxLength, plural, one {# character} other {# characters}}.", + 'Job group name must be no more than {maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js index d8b7fdf4b874f..76d718025731c 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js @@ -241,14 +241,14 @@ export const getMessages = () => { job_id_empty: { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdEmptyMessage', { - defaultMessage: 'The job name field must not be empty.', + defaultMessage: 'Job ID field must not be empty.', }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' }, job_id_invalid: { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdInvalidMessage', { - defaultMessage: 'The job name is invalid. It can contain lowercase alphanumeric (a-z and 0-9) characters, ' + + defaultMessage: 'Job ID is invalid. It can contain lowercase alphanumeric (a-z and 0-9) characters, ' + 'hyphens or underscores and must start and end with an alphanumeric character.', }), url: 'https://www.elastic.co/guide/en/elasticsearch/reference/{{version}}/ml-job-resource.html#ml-job-resource' @@ -256,7 +256,7 @@ export const getMessages = () => { job_id_invalid_max_length: { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdInvalidMaxLengthErrorMessage', { - defaultMessage: 'Job name can\'t contain more than {maxLength, plural, one {# character} other {# characters}}.', + defaultMessage: 'Job ID must be no more than {maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, @@ -266,11 +266,12 @@ export const getMessages = () => { job_id_valid: { status: 'SUCCESS', heading: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdValidHeading', { - defaultMessage: 'Job id format is valid', + defaultMessage: 'Job ID format is valid', }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character, and is less than {maxLength, plural, one {# character} other {# characters}}.', + 'starts and ends with an alphanumeric character, and is no more than ' + + '{maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, @@ -288,7 +289,7 @@ export const getMessages = () => { job_group_id_invalid_max_length: { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage', { - defaultMessage: 'Job group name can\'t contain more than {maxLength, plural, one {# character} other {# characters}}.', + defaultMessage: 'Job group name must be more than {maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, @@ -302,7 +303,8 @@ export const getMessages = () => { }), text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage', { defaultMessage: 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, ' + - 'starts and ends with an alphanumeric character, and is less than {maxLength, plural, one {# character} other {# characters}}.', + 'starts and ends with an alphanumeric character, and is no more than ' + + '{maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, }, From 025f91b6af65e67224644328b9efceedcefdf53d Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Mon, 16 Sep 2019 18:09:44 +0200 Subject: [PATCH 6/8] [ML] job id length validation for analytics --- .../plugins/ml/common/util/job_utils.d.ts | 2 ++ .../data_frame_analytics/common/analytics.ts | 5 ----- .../data_frame_analytics/common/index.ts | 1 - .../create_analytics_form.tsx | 18 +++++++++++++++++- .../hooks/use_create_analytics_form/reducer.ts | 10 +++++++--- .../hooks/use_create_analytics_form/state.ts | 1 + 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts b/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts index 0217ddcdc2cfe..d43279f4d822e 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts @@ -27,3 +27,5 @@ export function basicJobValidation( export const ML_MEDIAN_PERCENTS: number; export const ML_DATA_PREVIEW_COUNT: number; + +export function isJobIdValid(jobId: string): boolean; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts index 95310b414d6f9..fe1856866e811 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts @@ -9,11 +9,6 @@ import { BehaviorSubject } from 'rxjs'; import { filter, distinctUntilChanged } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -// @ts-ignore -import { isJobIdValid } from '../../../common/util/job_utils'; - -export const isAnalyticsIdValid = isJobIdValid; - export type IndexName = string; export type IndexPattern = string; export type DataFrameAnalyticsId = string; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts index 99d2c347cbe28..cf7d44ab48b4c 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts @@ -6,7 +6,6 @@ export { getAnalysisType, - isAnalyticsIdValid, isOutlierAnalysis, refreshAnalyticsList$, useRefreshAnalyticsList, diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx index eb59bc9af164b..39e9ce0a81e50 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx @@ -25,6 +25,7 @@ import { metadata } from 'ui/metadata'; import { INDEX_PATTERN_ILLEGAL_CHARACTERS } from 'ui/index_patterns'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; +import { JOB_ID_MAX_LENGTH } from '../../../../../../common/constants/validation'; // based on code used by `ui/index_patterns` internally // remove the space character from the list of illegal characters @@ -53,6 +54,7 @@ export const CreateAnalyticsForm: FC = ({ actions, sta jobIdEmpty, jobIdExists, jobIdValid, + jobIdInvalidMaxLength, sourceIndex, sourceIndexNameEmpty, sourceIndexNameValid, @@ -106,7 +108,7 @@ export const CreateAnalyticsForm: FC = ({ actions, sta label={i18n.translate('xpack.ml.dataframe.analytics.create.jobIdLabel', { defaultMessage: 'Job ID', })} - isInvalid={(!jobIdEmpty && !jobIdValid) || jobIdExists} + isInvalid={(!jobIdEmpty && !jobIdValid) || jobIdExists || jobIdInvalidMaxLength} error={[ ...(!jobIdEmpty && !jobIdValid ? [ @@ -123,6 +125,20 @@ export const CreateAnalyticsForm: FC = ({ actions, sta }), ] : []), + ...(jobIdInvalidMaxLength + ? [ + i18n.translate( + 'xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage', + { + defaultMessage: + 'Job ID must be no more than {maxLength, plural, one {# character} other {# characters}} long.', + values: { + maxLength: JOB_ID_MAX_LENGTH, + }, + } + ), + ] + : []), ]} > { const { jobConfig } = state; @@ -189,7 +190,10 @@ export function reducer(state: State, action: Action): State { if (action.payload.jobId !== undefined) { newFormState.jobIdExists = state.jobIds.some(id => newFormState.jobId === id); newFormState.jobIdEmpty = newFormState.jobId === ''; - newFormState.jobIdValid = isAnalyticsIdValid(newFormState.jobId); + newFormState.jobIdValid = isJobIdValid(newFormState.jobId); + newFormState.jobIdInvalidMaxLength = !!maxLengthValidator(JOB_ID_MAX_LENGTH)( + newFormState.jobId + ); } if (action.payload.sourceIndex !== undefined) { diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index a4a0087c9d869..b101373a4c2c8 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -32,6 +32,7 @@ export interface State { jobId: DataFrameAnalyticsId; jobIdExists: boolean; jobIdEmpty: boolean; + jobIdInvalidMaxLength?: boolean; jobIdValid: boolean; sourceIndex: EsIndexName; sourceIndexNameEmpty: boolean; From 628b8cbe6c7d95f384d5fafde37b653ae249f475 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Tue, 17 Sep 2019 09:54:27 +0200 Subject: [PATCH 7/8] [ML] no valid message in case of empty job groups --- x-pack/legacy/plugins/ml/common/util/job_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.js b/x-pack/legacy/plugins/ml/common/util/job_utils.js index fb1ea7677b83a..dae60b12b0d59 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.js @@ -497,7 +497,7 @@ export function validateGroupNames(job) { ...groups.some(group => maxLengthValidator(JOB_ID_MAX_LENGTH)(group)) ? [{ id: 'job_group_id_invalid_max_length' }] : [], ]; const valid = errorMessages.length === 0; - const messages = valid ? [{ id: 'job_group_id_valid' }] : errorMessages; + const messages = (valid && groups.length) ? [{ id: 'job_group_id_valid' }] : errorMessages; return { valid, From 2bc9962878b88ae9376b1bb31bd182239a45f508 Mon Sep 17 00:00:00 2001 From: Dmitrii Arnautov Date: Tue, 17 Sep 2019 11:40:30 +0200 Subject: [PATCH 8/8] [ML] fix typo --- .../legacy/plugins/ml/server/models/job_validation/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js index 76d718025731c..cfc0a0c3041ac 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/messages.js @@ -289,7 +289,7 @@ export const getMessages = () => { job_group_id_invalid_max_length: { status: 'ERROR', text: i18n.translate('xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage', { - defaultMessage: 'Job group name must be more than {maxLength, plural, one {# character} other {# characters}} long.', + defaultMessage: 'Job group name must be no more than {maxLength, plural, one {# character} other {# characters}} long.', values: { maxLength: JOB_ID_MAX_LENGTH, },