;
- const snapshotIds = ids.split(',');
+
const response: {
itemsDeleted: Array<{ snapshot: string; repository: string }>;
errors: any[];
@@ -198,17 +200,13 @@ export function registerSnapshotsRoutes({
errors: [],
};
+ const snapshots = req.body;
+
try {
// We intentially perform deletion requests sequentially (blocking) instead of in parallel (non-blocking)
// because there can only be one snapshot deletion task performed at a time (ES restriction).
- for (let i = 0; i < snapshotIds.length; i++) {
- // IDs come in the format of `repository-name/snapshot-name`
- // Extract the two parts by splitting at last occurrence of `/` in case
- // repository name contains '/` (from older versions)
- const id = snapshotIds[i];
- const indexOfDivider = id.lastIndexOf('/');
- const snapshot = id.substring(indexOfDivider + 1);
- const repository = id.substring(0, indexOfDivider);
+ for (let i = 0; i < snapshots.length; i++) {
+ const { snapshot, repository } = snapshots[i];
await callAsCurrentUser('snapshot.delete', { snapshot, repository })
.then(() => response.itemsDeleted.push({ snapshot, repository }))
diff --git a/x-pack/plugins/snapshot_restore/server/services/license.ts b/x-pack/plugins/snapshot_restore/server/services/license.ts
index 31d3654c51e3e..0a4748bd0ace0 100644
--- a/x-pack/plugins/snapshot_restore/server/services/license.ts
+++ b/x-pack/plugins/snapshot_restore/server/services/license.ts
@@ -53,12 +53,12 @@ export class License {
});
}
- guardApiRoute(handler: RequestHandler) {
+ guardApiRoute(handler: RequestHandler
) {
const license = this;
return function licenseCheck(
ctx: RequestHandlerContext,
- request: KibanaRequest,
+ request: KibanaRequest
,
response: KibanaResponseFactory
) {
const licenseStatus = license.getStatus();
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 149c13ea55fb2..b655a1c3dc885 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -9999,9 +9999,6 @@
"xpack.ml.models.jobService.deletingJob": "削除中",
"xpack.ml.models.jobService.jobHasNoDatafeedErrorMessage": "ジョブにデータフィードがありません",
"xpack.ml.models.jobService.requestToActionTimedOutErrorMessage": "「{id}」を{action}するリクエストがタイムアウトしました。{extra}",
- "xpack.ml.models.jobValidation.analysisConfigIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
- "xpack.ml.models.jobValidation.detectorsAreNotArrayErrorMessage": "無効な {invalidParamName}:配列でなければなりません。",
- "xpack.ml.models.jobValidation.jobIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
"xpack.ml.models.jobValidation.messages.bucketSpanEmptyMessage": "バケットスパンフィールドを指定する必要があります。",
"xpack.ml.models.jobValidation.messages.bucketSpanEstimationMismatchHeading": "バケットスパン",
"xpack.ml.models.jobValidation.messages.bucketSpanEstimationMismatchMessage": "現在のバケットスパンは {currentBucketSpan} ですが、バケットスパンの予測からは {estimateBucketSpan} が返されました。",
@@ -10059,7 +10056,6 @@
"xpack.ml.models.jobValidation.messages.timeRangeBeforeEpochMessage": "選択された、または利用可能な時間範囲には、UNIX 時間の開始以前のタイムスタンプのデータが含まれています。01/01/1970 00:00:00 (UTC) よりも前のタイムスタンプは機械学習ジョブでサポートされていません。",
"xpack.ml.models.jobValidation.messages.timeRangeShortHeading": "時間範囲",
"xpack.ml.models.jobValidation.messages.timeRangeShortMessage": "選択された、または利用可能な時間範囲が短すぎます。推奨最低時間範囲は {minTimeSpanReadable} で、バケットスパンの {bucketSpanCompareFactor} 倍です。",
- "xpack.ml.models.jobValidation.payloadIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
"xpack.ml.models.jobValidation.unknownMessageIdErrorMessage": "{messageId} (不明なメッセージ ID)",
"xpack.ml.models.jobValidation.validateJobObject.analysisConfigIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
"xpack.ml.models.jobValidation.validateJobObject.dataDescriptionIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index bd22f2a14a292..8e3108d30515b 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -10005,9 +10005,6 @@
"xpack.ml.models.jobService.deletingJob": "正在删除",
"xpack.ml.models.jobService.jobHasNoDatafeedErrorMessage": "作业没有数据馈送",
"xpack.ml.models.jobService.requestToActionTimedOutErrorMessage": "对 {action} “{id}” 的请求超时。{extra}",
- "xpack.ml.models.jobValidation.analysisConfigIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
- "xpack.ml.models.jobValidation.detectorsAreNotArrayErrorMessage": "无效的 {invalidParamName}:需要是数组。",
- "xpack.ml.models.jobValidation.jobIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
"xpack.ml.models.jobValidation.messages.bucketSpanEmptyMessage": "必须指定存储桶跨度字段。",
"xpack.ml.models.jobValidation.messages.bucketSpanEstimationMismatchHeading": "存储桶跨度",
"xpack.ml.models.jobValidation.messages.bucketSpanEstimationMismatchMessage": "当前存储桶跨度为 {currentBucketSpan},但存储桶跨度估计返回 {estimateBucketSpan}。",
@@ -10065,7 +10062,6 @@
"xpack.ml.models.jobValidation.messages.timeRangeBeforeEpochMessage": "选定或可用时间范围包含时间戳在 UNIX epoch 开始之前的数据。Machine Learning 作业不支持在 01/01/1970 00:00:00 (UTC) 之前的时间戳。",
"xpack.ml.models.jobValidation.messages.timeRangeShortHeading": "时间范围",
"xpack.ml.models.jobValidation.messages.timeRangeShortMessage": "选定或可用时间范围可能过短。建议的最小时间范围应至少为 {minTimeSpanReadable} 且是存储桶跨度的 {bucketSpanCompareFactor} 倍。",
- "xpack.ml.models.jobValidation.payloadIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
"xpack.ml.models.jobValidation.unknownMessageIdErrorMessage": "{messageId}(未知消息 ID)",
"xpack.ml.models.jobValidation.validateJobObject.analysisConfigIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
"xpack.ml.models.jobValidation.validateJobObject.dataDescriptionIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap
index 727cf2518549d..55c2f591d084f 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap
@@ -745,7 +745,7 @@ exports[`MonitorList component renders the monitor list 1`] = `
scope="col"
>
-
@@ -1092,7 +1092,7 @@ exports[`MonitorList component renders the monitor list 1`] = `
TLS Certificate
-
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx
index ae07cd8dddfb4..69ffe4e4a6dc6 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx
@@ -144,7 +144,7 @@ export const MonitorListComponent: React.FC = ({
),
},
{
- align: 'center' as const,
+ align: 'left' as const,
field: 'state.tls',
name: labels.TLS_COLUMN_LABEL,
render: (tls: any) => ,
diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.js b/x-pack/test/api_integration/apis/management/index_management/mapping.js
index fa0f6e04a7a4d..2b786542553de 100644
--- a/x-pack/test/api_integration/apis/management/index_management/mapping.js
+++ b/x-pack/test/api_integration/apis/management/index_management/mapping.js
@@ -32,7 +32,7 @@ export default function({ getService }) {
const { body } = await getIndexMapping(index).expect(200);
- expect(body.mapping).to.eql(mappings);
+ expect(body.mappings).to.eql(mappings);
});
});
}
diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
index 255afecde74cb..c2d904e379dd6 100644
--- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
+++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
@@ -145,7 +145,7 @@ export default ({ getService }: FtrProviderContext) => {
expect(body.count).to.eql(2);
expect(body.jobs.length).to.eql(2);
expect(body.jobs[0].job_id).to.eql(`${jobId}_1`);
- expect(body.jobs[0]).to.keys(
+ expect(body.jobs[0]).to.have.keys(
'timing_stats',
'state',
'forecasts_stats',
@@ -178,7 +178,7 @@ export default ({ getService }: FtrProviderContext) => {
expect(body.count).to.eql(1);
expect(body.jobs.length).to.eql(1);
expect(body.jobs[0].job_id).to.eql(`${jobId}_1`);
- expect(body.jobs[0]).to.keys(
+ expect(body.jobs[0]).to.have.keys(
'timing_stats',
'state',
'forecasts_stats',
@@ -197,7 +197,7 @@ export default ({ getService }: FtrProviderContext) => {
expect(body.count).to.eql(2);
expect(body.jobs.length).to.eql(2);
expect(body.jobs[0].job_id).to.eql(`${jobId}_1`);
- expect(body.jobs[0]).to.keys(
+ expect(body.jobs[0]).to.have.keys(
'timing_stats',
'state',
'forecasts_stats',
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/index.ts b/x-pack/test/api_integration/apis/ml/job_validation/index.ts
index fa894de839cd2..774f2ef7b4016 100644
--- a/x-pack/test/api_integration/apis/ml/job_validation/index.ts
+++ b/x-pack/test/api_integration/apis/ml/job_validation/index.ts
@@ -10,5 +10,6 @@ export default function({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./bucket_span_estimator'));
loadTestFile(require.resolve('./calculate_model_memory_limit'));
loadTestFile(require.resolve('./cardinality'));
+ loadTestFile(require.resolve('./validate'));
});
}
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/validate.ts b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts
new file mode 100644
index 0000000000000..aaeead57345bc
--- /dev/null
+++ b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts
@@ -0,0 +1,391 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { FtrProviderContext } from '../../../ftr_provider_context';
+import { USER } from '../../../../functional/services/machine_learning/security_common';
+
+const COMMON_HEADERS = {
+ 'kbn-xsrf': 'some-xsrf-token',
+};
+
+// eslint-disable-next-line import/no-default-export
+export default ({ getService }: FtrProviderContext) => {
+ const esArchiver = getService('esArchiver');
+ const supertest = getService('supertestWithoutAuth');
+ const ml = getService('ml');
+
+ describe('Validate job', function() {
+ before(async () => {
+ await esArchiver.loadIfNeeded('ml/ecommerce');
+ await ml.testResources.setKibanaTimeZoneToUTC();
+ });
+
+ after(async () => {
+ await ml.api.cleanMlIndices();
+ });
+
+ it(`should recognize a valid job configuration`, async () => {
+ const requestBody = {
+ duration: { start: 1586995459000, end: 1589672736000 },
+ job: {
+ job_id: 'test',
+ description: '',
+ groups: [],
+ analysis_config: {
+ bucket_span: '15m',
+ detectors: [{ function: 'mean', field_name: 'products.discount_amount' }],
+ influencers: [],
+ summary_count_field_name: 'doc_count',
+ },
+ data_description: { time_field: 'order_date' },
+ analysis_limits: { model_memory_limit: '11MB' },
+ model_plot_config: { enabled: true },
+ datafeed_config: {
+ datafeed_id: 'datafeed-test',
+ job_id: 'test',
+ indices: ['ft_ecommerce'],
+ query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+ aggregations: {
+ buckets: {
+ date_histogram: { field: 'order_date', fixed_interval: '90000ms' },
+ aggregations: {
+ 'products.discount_amount': { avg: { field: 'products.discount_amount' } },
+ order_date: { max: { field: 'order_date' } },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const { body } = await supertest
+ .post('/api/ml/validate/job')
+ .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
+ .set(COMMON_HEADERS)
+ .send(requestBody)
+ .expect(200);
+
+ expect(body).to.eql([
+ {
+ id: 'job_id_valid',
+ heading: 'Job ID format is valid',
+ text:
+ 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, starts and ends with an alphanumeric character, and is no more than 64 characters long.',
+ url:
+ 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-job-resource',
+ status: 'success',
+ },
+ {
+ id: 'detectors_function_not_empty',
+ heading: 'Detector functions',
+ text: 'Presence of detector functions validated in all detectors.',
+ url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors',
+ status: 'success',
+ },
+ {
+ id: 'success_bucket_span',
+ bucketSpan: '15m',
+ heading: 'Bucket span',
+ text: 'Format of "15m" is valid and passed validation checks.',
+ url:
+ 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#bucket-span',
+ status: 'success',
+ },
+ {
+ id: 'success_time_range',
+ heading: 'Time range',
+ text: 'Valid and long enough to model patterns in the data.',
+ status: 'success',
+ },
+ {
+ id: 'success_mml',
+ heading: 'Model memory limit',
+ text: 'Valid and within the estimated model memory limit.',
+ url:
+ 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#model-memory-limits',
+ status: 'success',
+ },
+ ]);
+ });
+
+ it('should recognize a basic invalid job configuration and skip advanced checks', async () => {
+ const requestBody = {
+ duration: { start: 1586995459000, end: 1589672736000 },
+ job: {
+ job_id: '-(*&^',
+ description: '',
+ groups: [],
+ analysis_config: {
+ bucket_span: '15m',
+ detectors: [{ function: 'mean', field_name: 'products.discount_amount' }],
+ influencers: [],
+ summary_count_field_name: 'doc_count',
+ },
+ data_description: { time_field: 'order_date' },
+ analysis_limits: { model_memory_limit: '11MB' },
+ model_plot_config: { enabled: true },
+ datafeed_config: {
+ datafeed_id: 'datafeed-test',
+ job_id: 'test',
+ indices: ['ft_ecommerce'],
+ query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+ aggregations: {
+ buckets: {
+ date_histogram: { field: 'order_date', fixed_interval: '90000ms' },
+ aggregations: {
+ 'products.discount_amount': { avg: { field: 'products.discount_amount' } },
+ order_date: { max: { field: 'order_date' } },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const { body } = await supertest
+ .post('/api/ml/validate/job')
+ .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
+ .set(COMMON_HEADERS)
+ .send(requestBody)
+ .expect(200);
+
+ expect(body).to.eql([
+ {
+ id: 'job_id_invalid',
+ text:
+ '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/master/ml-job-resource.html#ml-job-resource',
+ status: 'error',
+ },
+ {
+ id: 'detectors_function_not_empty',
+ heading: 'Detector functions',
+ text: 'Presence of detector functions validated in all detectors.',
+ url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors',
+ status: 'success',
+ },
+ {
+ id: 'bucket_span_valid',
+ bucketSpan: '15m',
+ heading: 'Bucket span',
+ text: 'Format of "15m" is valid.',
+ url:
+ 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-analysisconfig',
+ status: 'success',
+ },
+ {
+ id: 'skipped_extended_tests',
+ text:
+ 'Skipped additional checks because the basic requirements of the job configuration were not met.',
+ status: 'warning',
+ },
+ ]);
+ });
+
+ it('should recognize non-basic issues in job configuration', async () => {
+ const requestBody = {
+ duration: { start: 1586995459000, end: 1589672736000 },
+ job: {
+ job_id: 'test',
+ description: '',
+ groups: [],
+ analysis_config: {
+ bucket_span: '1000000m',
+ detectors: [
+ {
+ function: 'mean',
+ field_name: 'products.base_price',
+ // some high cardinality field
+ partition_field_name: 'order_id',
+ },
+ ],
+ influencers: ['order_id'],
+ },
+ data_description: { time_field: 'order_date' },
+ analysis_limits: { model_memory_limit: '1MB' },
+ model_plot_config: { enabled: true },
+ datafeed_config: {
+ datafeed_id: 'datafeed-test',
+ job_id: 'test',
+ indices: ['ft_ecommerce'],
+ query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+ aggregations: {
+ buckets: {
+ date_histogram: { field: 'order_date', fixed_interval: '90000ms' },
+ aggregations: {
+ 'products.discount_amount': { avg: { field: 'products.discount_amount' } },
+ order_date: { max: { field: 'order_date' } },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const { body } = await supertest
+ .post('/api/ml/validate/job')
+ .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
+ .set(COMMON_HEADERS)
+ .send(requestBody)
+ .expect(200);
+
+ expect(body).to.eql([
+ {
+ id: 'job_id_valid',
+ heading: 'Job ID format is valid',
+ text:
+ 'Lowercase alphanumeric (a-z and 0-9) characters, hyphens or underscores, starts and ends with an alphanumeric character, and is no more than 64 characters long.',
+ url:
+ 'https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-job-resource.html#ml-job-resource',
+ status: 'success',
+ },
+ {
+ id: 'detectors_function_not_empty',
+ heading: 'Detector functions',
+ text: 'Presence of detector functions validated in all detectors.',
+ url: 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#detectors',
+ status: 'success',
+ },
+ {
+ id: 'cardinality_model_plot_high',
+ modelPlotCardinality: 4711,
+ text:
+ 'The estimated cardinality of 4711 of fields relevant to creating model plots might result in resource intensive jobs.',
+ status: 'warning',
+ },
+ {
+ id: 'cardinality_partition_field',
+ fieldName: 'order_id',
+ text:
+ 'Cardinality of partition_field "order_id" is above 1000 and might result in high memory usage.',
+ url:
+ 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#cardinality',
+ status: 'warning',
+ },
+ {
+ id: 'bucket_span_high',
+ heading: 'Bucket span',
+ text:
+ 'Bucket span is 1 day or more. Be aware that days are considered as UTC days, not local days.',
+ url:
+ 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#bucket-span',
+ status: 'info',
+ },
+ {
+ bucketSpanCompareFactor: 25,
+ id: 'time_range_short',
+ minTimeSpanReadable: '2 hours',
+ heading: 'Time range',
+ text:
+ 'The selected or available time range might be too short. The recommended minimum time range should be at least 2 hours and 25 times the bucket span.',
+ status: 'warning',
+ },
+ {
+ id: 'success_influencers',
+ text: 'Influencer configuration passed the validation checks.',
+ url: 'https://www.elastic.co/guide/en/machine-learning/master/ml-influencers.html',
+ status: 'success',
+ },
+ {
+ id: 'half_estimated_mml_greater_than_mml',
+ mml: '1MB',
+ text:
+ 'The specified model memory limit is less than half of the estimated model memory limit and will likely hit the hard limit.',
+ url:
+ 'https://www.elastic.co/guide/en/machine-learning/master/create-jobs.html#model-memory-limits',
+ status: 'warning',
+ },
+ ]);
+ });
+
+ it('should not validate configuration in case request payload is invalid', async () => {
+ const requestBody = {
+ duration: { start: 1586995459000, end: 1589672736000 },
+ job: {
+ job_id: 'test',
+ description: '',
+ groups: [],
+ // missing analysis_config
+ data_description: { time_field: 'order_date' },
+ analysis_limits: { model_memory_limit: '11MB' },
+ model_plot_config: { enabled: true },
+ datafeed_config: {
+ datafeed_id: 'datafeed-test',
+ job_id: 'test',
+ indices: ['ft_ecommerce'],
+ query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+ aggregations: {
+ buckets: {
+ date_histogram: { field: 'order_date', fixed_interval: '90000ms' },
+ aggregations: {
+ 'products.discount_amount': { avg: { field: 'products.discount_amount' } },
+ order_date: { max: { field: 'order_date' } },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const { body } = await supertest
+ .post('/api/ml/validate/job')
+ .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
+ .set(COMMON_HEADERS)
+ .send(requestBody)
+ .expect(400);
+
+ expect(body.error).to.eql('Bad Request');
+ expect(body.message).to.eql(
+ '[request body.job.analysis_config.detectors]: expected value of type [array] but got [undefined]'
+ );
+ });
+
+ it('should not validate if the user does not have required permissions', async () => {
+ const requestBody = {
+ job: {
+ job_id: 'test',
+ description: '',
+ groups: [],
+ analysis_config: {
+ bucket_span: '15m',
+ detectors: [{ function: 'mean', field_name: 'products.discount_amount' }],
+ influencers: [],
+ summary_count_field_name: 'doc_count',
+ },
+ data_description: { time_field: 'order_date' },
+ analysis_limits: { model_memory_limit: '11MB' },
+ model_plot_config: { enabled: true },
+ datafeed_config: {
+ datafeed_id: 'datafeed-test',
+ job_id: 'test',
+ indices: ['ft_ecommerce'],
+ query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+ aggregations: {
+ buckets: {
+ date_histogram: { field: 'order_date', fixed_interval: '90000ms' },
+ aggregations: {
+ 'products.discount_amount': { avg: { field: 'products.discount_amount' } },
+ order_date: { max: { field: 'order_date' } },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const { body } = await supertest
+ .post('/api/ml/validate/job')
+ .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
+ .set(COMMON_HEADERS)
+ .send(requestBody)
+ .expect(404);
+
+ expect(body.error).to.eql('Not Found');
+ expect(body.message).to.eql('Not Found');
+ });
+ });
+};
diff --git a/x-pack/test_utils/jest/config.js b/x-pack/test_utils/jest/config.js
index 6ca2818f9e6af..66b88cbdeba17 100644
--- a/x-pack/test_utils/jest/config.js
+++ b/x-pack/test_utils/jest/config.js
@@ -13,7 +13,6 @@ export default {
'/legacy/server',
'/legacy/common',
'/test_utils/jest/integration_tests',
- '/test_utils/jest/contract_tests',
],
collectCoverageFrom: ['legacy/plugins/**/*.js', 'legacy/common/**/*.js', 'legacy/server/**/*.js'],
moduleNameMapper: {