mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Improving client side error handling (#76743)
* [ML] Improving client side error handling * adding stacktrace to request errors * copying error parsing to transforms * update snapshot * fixing jest tests * adding test and removing debug log output * updating translations * rewriting error extracting code * fixing tests * removing message bar service * removing test code * updating translations * combining job creation error handling * refactoring error files * updating test * fixing bug in DFA deletion * improving mml warning Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
22b4e40ea0
commit
ea8086b3f2
61 changed files with 591 additions and 939 deletions
|
@ -4,11 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CustomHttpResponseOptions, ResponseError } from 'kibana/server';
|
||||
import Boom from 'boom';
|
||||
import { EsErrorBody } from '../util/errors';
|
||||
|
||||
export interface DeleteDataFrameAnalyticsWithIndexStatus {
|
||||
success: boolean;
|
||||
error?: CustomHttpResponseOptions<ResponseError>;
|
||||
error?: EsErrorBody | Boom;
|
||||
}
|
||||
|
||||
export type IndexName = string;
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* 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 {
|
||||
BoomResponse,
|
||||
extractErrorMessage,
|
||||
MLCustomHttpResponseOptions,
|
||||
MLResponseError,
|
||||
} from './errors';
|
||||
import { ResponseError } from 'kibana/server';
|
||||
|
||||
describe('ML - error message utils', () => {
|
||||
describe('extractErrorMessage', () => {
|
||||
test('returns just the error message', () => {
|
||||
const testMsg = 'Saved object [index-pattern/blahblahblah] not found';
|
||||
|
||||
const bodyWithNestedErrorMsg: MLCustomHttpResponseOptions<MLResponseError> = {
|
||||
body: {
|
||||
message: {
|
||||
msg: testMsg,
|
||||
},
|
||||
},
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithNestedErrorMsg)).toBe(testMsg);
|
||||
|
||||
const bodyWithStringMsg: MLCustomHttpResponseOptions<MLResponseError> = {
|
||||
body: {
|
||||
msg: testMsg,
|
||||
statusCode: 404,
|
||||
response: `{"error":{"reason":"${testMsg}"}}`,
|
||||
},
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithStringMsg)).toBe(testMsg);
|
||||
|
||||
const bodyWithStringMessage: MLCustomHttpResponseOptions<ResponseError> = {
|
||||
body: {
|
||||
message: testMsg,
|
||||
},
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithStringMessage)).toBe(testMsg);
|
||||
|
||||
const bodyWithString: MLCustomHttpResponseOptions<ResponseError> = {
|
||||
body: testMsg,
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithString)).toBe(testMsg);
|
||||
|
||||
const bodyWithError: MLCustomHttpResponseOptions<ResponseError> = {
|
||||
body: new Error(testMsg),
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithError)).toBe(testMsg);
|
||||
|
||||
const bodyWithBoomError: MLCustomHttpResponseOptions<BoomResponse> = {
|
||||
statusCode: 404,
|
||||
body: {
|
||||
data: [],
|
||||
isBoom: true,
|
||||
isServer: false,
|
||||
output: {
|
||||
statusCode: 404,
|
||||
payload: {
|
||||
statusCode: 404,
|
||||
error: testMsg,
|
||||
message: testMsg,
|
||||
},
|
||||
headers: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithBoomError)).toBe(testMsg);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
* 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 { ResponseError, ResponseHeaders } from 'kibana/server';
|
||||
import { isErrorResponse } from '../types/errors';
|
||||
|
||||
export function getErrorMessage(error: any) {
|
||||
if (isErrorResponse(error)) {
|
||||
return `${error.body.error}: ${error.body.message}`;
|
||||
}
|
||||
|
||||
if (typeof error === 'object' && typeof error.message === 'string') {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
return JSON.stringify(error);
|
||||
}
|
||||
|
||||
// Adding temporary types until Kibana ResponseError is updated
|
||||
|
||||
export interface BoomResponse {
|
||||
data: any;
|
||||
isBoom: boolean;
|
||||
isServer: boolean;
|
||||
output: {
|
||||
statusCode: number;
|
||||
payload: {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
};
|
||||
headers: {};
|
||||
};
|
||||
}
|
||||
export type MLResponseError =
|
||||
| {
|
||||
message: {
|
||||
msg: string;
|
||||
};
|
||||
}
|
||||
| { msg: string; statusCode: number; response: string };
|
||||
|
||||
export interface MLCustomHttpResponseOptions<
|
||||
T extends ResponseError | MLResponseError | BoomResponse
|
||||
> {
|
||||
/** HTTP message to send to the client */
|
||||
body?: T;
|
||||
/** HTTP Headers with additional information about response */
|
||||
headers?: ResponseHeaders;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export interface MLErrorObject {
|
||||
message: string;
|
||||
fullErrorMessage?: string; // For use in a 'See full error' popover.
|
||||
statusCode?: number;
|
||||
}
|
||||
|
||||
export const extractErrorProperties = (
|
||||
error:
|
||||
| MLCustomHttpResponseOptions<MLResponseError | ResponseError | BoomResponse>
|
||||
| string
|
||||
| undefined
|
||||
): MLErrorObject => {
|
||||
// extract properties of the error object from within the response error
|
||||
// coming from Kibana, Elasticsearch, and our own ML messages
|
||||
let message = '';
|
||||
let fullErrorMessage;
|
||||
let statusCode;
|
||||
|
||||
if (typeof error === 'string') {
|
||||
return {
|
||||
message: error,
|
||||
};
|
||||
}
|
||||
if (error?.body === undefined) {
|
||||
return {
|
||||
message: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof error.body === 'string') {
|
||||
return {
|
||||
message: error.body,
|
||||
};
|
||||
}
|
||||
if (
|
||||
typeof error.body === 'object' &&
|
||||
'output' in error.body &&
|
||||
error.body.output.payload.message
|
||||
) {
|
||||
return {
|
||||
message: error.body.output.payload.message,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
typeof error.body === 'object' &&
|
||||
'response' in error.body &&
|
||||
typeof error.body.response === 'string'
|
||||
) {
|
||||
const errorResponse = JSON.parse(error.body.response);
|
||||
if ('error' in errorResponse && typeof errorResponse === 'object') {
|
||||
const errorResponseError = errorResponse.error;
|
||||
if ('reason' in errorResponseError) {
|
||||
message = errorResponseError.reason;
|
||||
}
|
||||
if ('caused_by' in errorResponseError) {
|
||||
const causedByMessage = JSON.stringify(errorResponseError.caused_by);
|
||||
// Only add a fullErrorMessage if different to the message.
|
||||
if (causedByMessage !== message) {
|
||||
fullErrorMessage = causedByMessage;
|
||||
}
|
||||
}
|
||||
return {
|
||||
message,
|
||||
fullErrorMessage,
|
||||
statusCode: error.statusCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof error.body === 'object' && 'msg' in error.body && typeof error.body.msg === 'string') {
|
||||
return {
|
||||
message: error.body.msg,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof error.body === 'object' && 'message' in error.body) {
|
||||
if (
|
||||
'attributes' in error.body &&
|
||||
typeof error.body.attributes === 'object' &&
|
||||
error.body.attributes.body?.status !== undefined
|
||||
) {
|
||||
statusCode = error.body.attributes.body.status;
|
||||
|
||||
if (typeof error.body.attributes.body.error?.reason === 'string') {
|
||||
return {
|
||||
message: error.body.attributes.body.error.reason,
|
||||
statusCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof error.body.message === 'string') {
|
||||
return {
|
||||
message: error.body.message,
|
||||
statusCode,
|
||||
};
|
||||
}
|
||||
if (!(error.body.message instanceof Error) && typeof (error.body.message.msg === 'string')) {
|
||||
return {
|
||||
message: error.body.message.msg,
|
||||
statusCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fail return an empty message instead of JSON.stringify
|
||||
return {
|
||||
message: '',
|
||||
};
|
||||
};
|
||||
|
||||
export const extractErrorMessage = (
|
||||
error:
|
||||
| MLCustomHttpResponseOptions<MLResponseError | ResponseError | BoomResponse>
|
||||
| undefined
|
||||
| string
|
||||
): string => {
|
||||
// extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages
|
||||
const errorObj = extractErrorProperties(error);
|
||||
return errorObj.message;
|
||||
};
|
99
x-pack/plugins/ml/common/util/errors/errors.test.ts
Normal file
99
x-pack/plugins/ml/common/util/errors/errors.test.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 Boom from 'boom';
|
||||
|
||||
import { extractErrorMessage, MLHttpFetchError, MLResponseError, EsErrorBody } from './index';
|
||||
|
||||
describe('ML - error message utils', () => {
|
||||
describe('extractErrorMessage', () => {
|
||||
test('returns just the error message', () => {
|
||||
const testMsg = 'Saved object [index-pattern/indexpattern] not found';
|
||||
|
||||
// bad error, return empty string
|
||||
const badError = {} as any;
|
||||
expect(extractErrorMessage(badError)).toBe('');
|
||||
|
||||
// raw es error
|
||||
const esErrorMsg: EsErrorBody = {
|
||||
error: {
|
||||
root_cause: [
|
||||
{
|
||||
type: 'type',
|
||||
reason: 'reason',
|
||||
},
|
||||
],
|
||||
type: 'type',
|
||||
reason: testMsg,
|
||||
},
|
||||
status: 404,
|
||||
};
|
||||
expect(extractErrorMessage(esErrorMsg)).toBe(testMsg);
|
||||
|
||||
// error is basic string
|
||||
const stringMessage = testMsg;
|
||||
expect(extractErrorMessage(stringMessage)).toBe(testMsg);
|
||||
|
||||
// kibana error without attributes
|
||||
const bodyWithoutAttributes: MLHttpFetchError<MLResponseError> = {
|
||||
name: 'name',
|
||||
req: {} as Request,
|
||||
request: {} as Request,
|
||||
message: 'Something else',
|
||||
body: {
|
||||
statusCode: 404,
|
||||
error: 'error',
|
||||
message: testMsg,
|
||||
},
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithoutAttributes)).toBe(testMsg);
|
||||
|
||||
// kibana error with attributes
|
||||
const bodyWithAttributes: MLHttpFetchError<MLResponseError> = {
|
||||
name: 'name',
|
||||
req: {} as Request,
|
||||
request: {} as Request,
|
||||
message: 'Something else',
|
||||
body: {
|
||||
statusCode: 404,
|
||||
error: 'error',
|
||||
message: 'Something else',
|
||||
attributes: {
|
||||
body: {
|
||||
status: 404,
|
||||
error: {
|
||||
reason: testMsg,
|
||||
type: 'type',
|
||||
root_cause: [{ type: 'type', reason: 'reason' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(extractErrorMessage(bodyWithAttributes)).toBe(testMsg);
|
||||
|
||||
// boom error
|
||||
const boomError: Boom<any> = {
|
||||
message: '',
|
||||
reformat: () => '',
|
||||
name: '',
|
||||
data: [],
|
||||
isBoom: true,
|
||||
isServer: false,
|
||||
output: {
|
||||
statusCode: 404,
|
||||
payload: {
|
||||
statusCode: 404,
|
||||
error: testMsg,
|
||||
message: testMsg,
|
||||
},
|
||||
headers: {},
|
||||
},
|
||||
};
|
||||
expect(extractErrorMessage(boomError)).toBe(testMsg);
|
||||
});
|
||||
});
|
||||
});
|
20
x-pack/plugins/ml/common/util/errors/index.ts
Normal file
20
x-pack/plugins/ml/common/util/errors/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { MLRequestFailure } from './request_error';
|
||||
export { extractErrorMessage, extractErrorProperties } from './process_errors';
|
||||
export {
|
||||
ErrorType,
|
||||
EsErrorBody,
|
||||
EsErrorRootCause,
|
||||
MLErrorObject,
|
||||
MLHttpFetchError,
|
||||
MLResponseError,
|
||||
isBoomError,
|
||||
isErrorString,
|
||||
isEsErrorBody,
|
||||
isMLResponseError,
|
||||
} from './types';
|
83
x-pack/plugins/ml/common/util/errors/process_errors.ts
Normal file
83
x-pack/plugins/ml/common/util/errors/process_errors.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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 {
|
||||
ErrorType,
|
||||
MLErrorObject,
|
||||
isBoomError,
|
||||
isErrorString,
|
||||
isEsErrorBody,
|
||||
isMLResponseError,
|
||||
} from './types';
|
||||
|
||||
export const extractErrorProperties = (error: ErrorType): MLErrorObject => {
|
||||
// extract properties of the error object from within the response error
|
||||
// coming from Kibana, Elasticsearch, and our own ML messages
|
||||
|
||||
// some responses contain raw es errors as part of a bulk response
|
||||
// e.g. if some jobs fail the action in a bulk request
|
||||
if (isEsErrorBody(error)) {
|
||||
return {
|
||||
message: error.error.reason,
|
||||
statusCode: error.status,
|
||||
fullError: error,
|
||||
};
|
||||
}
|
||||
|
||||
if (isErrorString(error)) {
|
||||
return {
|
||||
message: error,
|
||||
};
|
||||
}
|
||||
|
||||
if (isBoomError(error)) {
|
||||
return {
|
||||
message: error.output.payload.message,
|
||||
statusCode: error.output.payload.statusCode,
|
||||
};
|
||||
}
|
||||
|
||||
if (error?.body === undefined) {
|
||||
return {
|
||||
message: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof error.body === 'string') {
|
||||
return {
|
||||
message: error.body,
|
||||
};
|
||||
}
|
||||
|
||||
if (isMLResponseError(error)) {
|
||||
if (
|
||||
typeof error.body.attributes === 'object' &&
|
||||
typeof error.body.attributes.body?.error?.reason === 'string'
|
||||
) {
|
||||
return {
|
||||
message: error.body.attributes.body.error.reason,
|
||||
statusCode: error.body.statusCode,
|
||||
fullError: error.body.attributes.body,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
message: error.body.message,
|
||||
statusCode: error.body.statusCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fail return an empty message instead of JSON.stringify
|
||||
return {
|
||||
message: '',
|
||||
};
|
||||
};
|
||||
|
||||
export const extractErrorMessage = (error: ErrorType): string => {
|
||||
// extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages
|
||||
const errorObj = extractErrorProperties(error);
|
||||
return errorObj.message;
|
||||
};
|
26
x-pack/plugins/ml/common/util/errors/request_error.ts
Normal file
26
x-pack/plugins/ml/common/util/errors/request_error.ts
Normal file
|
@ -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.
|
||||
*/
|
||||
|
||||
import { MLErrorObject, ErrorType } from './types';
|
||||
|
||||
export class MLRequestFailure extends Error {
|
||||
constructor(error: MLErrorObject, resp: ErrorType) {
|
||||
super(error.message);
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
|
||||
if (typeof resp !== 'string' && typeof resp !== 'undefined') {
|
||||
if ('body' in resp) {
|
||||
this.stack = JSON.stringify(resp.body, null, 2);
|
||||
} else {
|
||||
try {
|
||||
this.stack = JSON.stringify(resp, null, 2);
|
||||
} catch (e) {
|
||||
// fail silently
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
x-pack/plugins/ml/common/util/errors/types.ts
Normal file
60
x-pack/plugins/ml/common/util/errors/types.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { HttpFetchError } from 'kibana/public';
|
||||
import Boom from 'boom';
|
||||
|
||||
export interface EsErrorRootCause {
|
||||
type: string;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface EsErrorBody {
|
||||
error: {
|
||||
root_cause?: EsErrorRootCause[];
|
||||
caused_by?: EsErrorRootCause;
|
||||
type: string;
|
||||
reason: string;
|
||||
};
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface MLResponseError {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
attributes?: {
|
||||
body: EsErrorBody;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MLErrorObject {
|
||||
message: string;
|
||||
statusCode?: number;
|
||||
fullError?: EsErrorBody;
|
||||
}
|
||||
|
||||
export interface MLHttpFetchError<T> extends HttpFetchError {
|
||||
body: T;
|
||||
}
|
||||
|
||||
export type ErrorType = MLHttpFetchError<MLResponseError> | EsErrorBody | Boom | string | undefined;
|
||||
|
||||
export function isEsErrorBody(error: any): error is EsErrorBody {
|
||||
return error && error.error?.reason !== undefined;
|
||||
}
|
||||
|
||||
export function isErrorString(error: any): error is string {
|
||||
return typeof error === 'string';
|
||||
}
|
||||
|
||||
export function isMLResponseError(error: any): error is MLResponseError {
|
||||
return typeof error.body === 'object' && 'message' in error.body;
|
||||
}
|
||||
|
||||
export function isBoomError(error: any): error is Boom {
|
||||
return error.isBoom === true;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare interface MlMessageBarService {
|
||||
notify: {
|
||||
error(text: any, resp?: any): void;
|
||||
};
|
||||
}
|
||||
|
||||
export const mlMessageBarService: MlMessageBarService;
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* 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 { getToastNotifications } from '../../util/dependency_cache';
|
||||
import { MLRequestFailure } from '../../util/ml_error';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
function errorNotify(text, resp) {
|
||||
let err = null;
|
||||
if (typeof text === 'object' && text.response !== undefined) {
|
||||
resp = text.response;
|
||||
} else if (typeof text === 'object' && text.message !== undefined) {
|
||||
err = new Error(text.message);
|
||||
} else {
|
||||
err = new Error(text);
|
||||
}
|
||||
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addError(new MLRequestFailure(err, resp), {
|
||||
title: i18n.translate('xpack.ml.messagebarService.errorTitle', {
|
||||
defaultMessage: 'An error has occurred',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export const mlMessageBarService = {
|
||||
notify: {
|
||||
error: errorNotify,
|
||||
},
|
||||
};
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
|
@ -51,8 +53,7 @@ import { getPartitioningFieldNames } from '../../../../common/util/job_utils';
|
|||
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { mlJobService } from '../../services/job_service';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||
|
||||
class RuleEditorFlyoutUI extends Component {
|
||||
static propTypes = {
|
||||
|
@ -431,8 +432,8 @@ class RuleEditorFlyoutUI extends Component {
|
|||
values: { jobId },
|
||||
}
|
||||
);
|
||||
if (error.message) {
|
||||
errorMessage += ` : ${error.message}`;
|
||||
if (error.error) {
|
||||
errorMessage += ` : ${extractErrorMessage(error.error)}`;
|
||||
}
|
||||
toasts.addDanger(errorMessage);
|
||||
});
|
||||
|
|
|
@ -146,22 +146,17 @@ export function updateJobRules(job, detectorIndex, rules) {
|
|||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
mlJobService
|
||||
.updateJob(jobId, jobData)
|
||||
.then((resp) => {
|
||||
if (resp.success) {
|
||||
// Refresh the job data in the job service before resolving.
|
||||
mlJobService
|
||||
.refreshJob(jobId)
|
||||
.then(() => {
|
||||
resolve({ success: true });
|
||||
})
|
||||
.catch((refreshResp) => {
|
||||
reject(refreshResp);
|
||||
});
|
||||
} else {
|
||||
reject(resp);
|
||||
}
|
||||
ml.updateJob({ jobId: jobId, job: jobData })
|
||||
.then(() => {
|
||||
// Refresh the job data in the job service before resolving.
|
||||
mlJobService
|
||||
.refreshJob(jobId)
|
||||
.then(() => {
|
||||
resolve({ success: true });
|
||||
})
|
||||
.catch((refreshResp) => {
|
||||
reject(refreshResp);
|
||||
});
|
||||
})
|
||||
.catch((resp) => {
|
||||
reject(resp);
|
||||
|
|
|
@ -8,7 +8,7 @@ exports[`ValidateJob renders button and modal with a message 1`] = `
|
|||
iconSide="right"
|
||||
iconType="questionInCircle"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isLoading={true}
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
>
|
||||
|
@ -18,62 +18,6 @@ exports[`ValidateJob renders button and modal with a message 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<Modal
|
||||
close={[Function]}
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate job {title}"
|
||||
id="xpack.ml.validateJob.modal.validateJobTitle"
|
||||
values={
|
||||
Object {
|
||||
"title": "test-id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MessageList
|
||||
idFilterList={Array []}
|
||||
messages={
|
||||
Array [
|
||||
Object {
|
||||
"fieldName": "airline",
|
||||
"id": "over_field_low_cardinality",
|
||||
"status": "warning",
|
||||
"text": "Cardinality of over_field \\"airline\\" is low and therefore less suitable for population analysis.",
|
||||
"url": "https://www.elastic.co/blog/sizing-machine-learning-with-elasticsearch",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results."
|
||||
id="xpack.ml.validateJob.modal.jobValidationDescriptionText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
defaultMessage="For more information, see {mlJobTipsLink}."
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText"
|
||||
values={
|
||||
Object {
|
||||
"mlJobTipsLink": <EuiLink
|
||||
href="https://www.elastic.co/guide/en/machine-learning/jest-metadata-mock-branch/create-jobs.html#job-tips"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Machine Learning Job Tips"
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText.mlJobTipsLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>
|
||||
</Modal>
|
||||
</div>
|
||||
</Fragment>
|
||||
`;
|
||||
|
@ -108,7 +52,7 @@ exports[`ValidateJob renders the button and modal with a success message 1`] = `
|
|||
iconSide="right"
|
||||
iconType="questionInCircle"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isLoading={true}
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
>
|
||||
|
@ -118,52 +62,6 @@ exports[`ValidateJob renders the button and modal with a success message 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<Modal
|
||||
close={[Function]}
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate job {title}"
|
||||
id="xpack.ml.validateJob.modal.validateJobTitle"
|
||||
values={
|
||||
Object {
|
||||
"title": "test-id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MessageList
|
||||
idFilterList={Array []}
|
||||
messages={Array []}
|
||||
/>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results."
|
||||
id="xpack.ml.validateJob.modal.jobValidationDescriptionText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
defaultMessage="For more information, see {mlJobTipsLink}."
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText"
|
||||
values={
|
||||
Object {
|
||||
"mlJobTipsLink": <EuiLink
|
||||
href="https://www.elastic.co/guide/en/machine-learning/jest-metadata-mock-branch/create-jobs.html#job-tips"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Machine Learning Job Tips"
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText.mlJobTipsLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>
|
||||
</Modal>
|
||||
</div>
|
||||
</Fragment>
|
||||
`;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { FC } from 'react';
|
|||
declare const ValidateJob: FC<{
|
||||
getJobConfig: any;
|
||||
getDuration: any;
|
||||
mlJobService: any;
|
||||
ml: any;
|
||||
embedded?: boolean;
|
||||
setIsValid?: (valid: boolean) => void;
|
||||
idFilterList?: string[];
|
||||
|
|
|
@ -32,6 +32,8 @@ import { getDocLinks } from '../../util/dependency_cache';
|
|||
|
||||
import { VALIDATION_STATUS } from '../../../../common/constants/validation';
|
||||
import { getMostSevereMessageStatus } from '../../../../common/util/validation_utils';
|
||||
import { toastNotificationServiceProvider } from '../../services/toast_notification_service';
|
||||
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
const defaultIconType = 'questionInCircle';
|
||||
const getDefaultState = () => ({
|
||||
|
@ -182,7 +184,7 @@ Modal.propType = {
|
|||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
export class ValidateJob extends Component {
|
||||
export class ValidateJobUI extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = getDefaultState();
|
||||
|
@ -209,25 +211,40 @@ export class ValidateJob extends Component {
|
|||
if (typeof job === 'object') {
|
||||
let shouldShowLoadingIndicator = true;
|
||||
|
||||
this.props.mlJobService.validateJob({ duration, fields, job }).then((data) => {
|
||||
shouldShowLoadingIndicator = false;
|
||||
this.setState({
|
||||
...this.state,
|
||||
ui: {
|
||||
...this.state.ui,
|
||||
iconType: statusToEuiIconType(getMostSevereMessageStatus(data.messages)),
|
||||
isLoading: false,
|
||||
isModalVisible: true,
|
||||
},
|
||||
data,
|
||||
title: job.job_id,
|
||||
});
|
||||
if (typeof this.props.setIsValid === 'function') {
|
||||
this.props.setIsValid(
|
||||
data.messages.some((m) => m.status === VALIDATION_STATUS.ERROR) === false
|
||||
this.props.ml
|
||||
.validateJob({ duration, fields, job })
|
||||
.then((messages) => {
|
||||
shouldShowLoadingIndicator = false;
|
||||
this.setState({
|
||||
...this.state,
|
||||
ui: {
|
||||
...this.state.ui,
|
||||
iconType: statusToEuiIconType(getMostSevereMessageStatus(messages)),
|
||||
isLoading: false,
|
||||
isModalVisible: true,
|
||||
},
|
||||
data: {
|
||||
messages,
|
||||
success: true,
|
||||
},
|
||||
title: job.job_id,
|
||||
});
|
||||
if (typeof this.props.setIsValid === 'function') {
|
||||
this.props.setIsValid(
|
||||
messages.some((m) => m.status === VALIDATION_STATUS.ERROR) === false
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
const { toasts } = this.props.kibana.services.notifications;
|
||||
const toastNotificationService = toastNotificationServiceProvider(toasts);
|
||||
toastNotificationService.displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.jobService.validateJobErrorTitle', {
|
||||
defaultMessage: 'Job Validation Error',
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// wait for 250ms before triggering the loading indicator
|
||||
// to avoid flickering when there's a loading time below
|
||||
|
@ -335,15 +352,17 @@ export class ValidateJob extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
ValidateJob.propTypes = {
|
||||
ValidateJobUI.propTypes = {
|
||||
fields: PropTypes.object,
|
||||
fill: PropTypes.bool,
|
||||
getDuration: PropTypes.func,
|
||||
getJobConfig: PropTypes.func.isRequired,
|
||||
isCurrentJobConfig: PropTypes.bool,
|
||||
isDisabled: PropTypes.bool,
|
||||
mlJobService: PropTypes.object.isRequired,
|
||||
ml: PropTypes.object.isRequired,
|
||||
embedded: PropTypes.bool,
|
||||
setIsValid: PropTypes.func,
|
||||
idFilterList: PropTypes.array,
|
||||
};
|
||||
|
||||
export const ValidateJob = withKibana(ValidateJobUI);
|
||||
|
|
|
@ -16,6 +16,12 @@ jest.mock('../../util/dependency_cache', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../../../src/plugins/kibana_react/public', () => ({
|
||||
withKibana: (comp) => {
|
||||
return comp;
|
||||
},
|
||||
}));
|
||||
|
||||
const job = {
|
||||
job_id: 'test-id',
|
||||
};
|
||||
|
@ -25,11 +31,16 @@ const getJobConfig = () => job;
|
|||
function prepareTest(messages) {
|
||||
const p = Promise.resolve(messages);
|
||||
|
||||
const mlJobService = {
|
||||
validateJob: () => p,
|
||||
const ml = {
|
||||
validateJob: () => Promise.resolve(messages),
|
||||
};
|
||||
const kibana = {
|
||||
services: {
|
||||
notifications: { toasts: { addDanger: jest.fn() } },
|
||||
},
|
||||
};
|
||||
|
||||
const component = <ValidateJob getJobConfig={getJobConfig} mlJobService={mlJobService} />;
|
||||
const component = <ValidateJob getJobConfig={getJobConfig} ml={ml} kibana={kibana} />;
|
||||
|
||||
const wrapper = shallowWithIntl(component);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { distinctUntilChanged, filter } from 'rxjs/operators';
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { Dictionary } from '../../../../common/types/common';
|
||||
import { getErrorMessage } from '../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||
import { SavedSearchQuery } from '../../contexts/ml';
|
||||
import {
|
||||
AnalysisConfig,
|
||||
|
@ -486,7 +486,7 @@ export const loadEvalData = async ({
|
|||
results.eval = evalResult;
|
||||
return results;
|
||||
} catch (e) {
|
||||
results.error = getErrorMessage(e);
|
||||
results.error = extractErrorMessage(e);
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getErrorMessage } from '../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||
|
||||
import { EsSorting, SearchResponse7, UseDataGridReturnType } from '../../components/data_grid';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
|
@ -62,7 +62,7 @@ export const getIndexData = async (
|
|||
setTableItems(docs);
|
||||
setStatus(INDEX_STATUS.LOADED);
|
||||
} catch (e) {
|
||||
setErrorMessage(getErrorMessage(e));
|
||||
setErrorMessage(extractErrorMessage(e));
|
||||
setStatus(INDEX_STATUS.ERROR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useEffect, useState } from 'react';
|
|||
|
||||
import { IndexPattern } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
import { getErrorMessage } from '../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||
|
||||
import { getIndexPatternIdFromName } from '../../util/index_utils';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
|
@ -83,12 +83,12 @@ export const useResultsViewConfig = (jobId: string) => {
|
|||
setIsLoadingJobConfig(false);
|
||||
}
|
||||
} catch (e) {
|
||||
setJobCapsServiceErrorMessage(getErrorMessage(e));
|
||||
setJobCapsServiceErrorMessage(extractErrorMessage(e));
|
||||
setIsLoadingJobConfig(false);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
setJobConfigErrorMessage(getErrorMessage(e));
|
||||
setJobConfigErrorMessage(extractErrorMessage(e));
|
||||
setIsLoadingJobConfig(false);
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
SearchResponse7,
|
||||
UseIndexDataReturnType,
|
||||
} from '../../../../components/data_grid';
|
||||
import { getErrorMessage } from '../../../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../../../common/util/errors';
|
||||
import { INDEX_STATUS } from '../../../common/analytics';
|
||||
import { ml } from '../../../../services/ml_api_service';
|
||||
|
||||
|
@ -94,7 +94,7 @@ export const useIndexData = (
|
|||
setTableItems(docs);
|
||||
setStatus(INDEX_STATUS.LOADED);
|
||||
} catch (e) {
|
||||
setErrorMessage(getErrorMessage(e));
|
||||
setErrorMessage(extractErrorMessage(e));
|
||||
setStatus(INDEX_STATUS.ERROR);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,7 +11,6 @@ import { MlContext } from '../../../../../contexts/ml';
|
|||
import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value';
|
||||
|
||||
import { useCreateAnalyticsForm } from './use_create_analytics_form';
|
||||
import { getErrorMessage } from '../../../../../../../common/util/errors';
|
||||
|
||||
const getMountedHook = () =>
|
||||
mountHook(
|
||||
|
@ -21,28 +20,6 @@ const getMountedHook = () =>
|
|||
)
|
||||
);
|
||||
|
||||
describe('getErrorMessage()', () => {
|
||||
test('verify error message response formats', () => {
|
||||
const customError1 = {
|
||||
body: { statusCode: 403, error: 'Forbidden', message: 'the-error-message' },
|
||||
};
|
||||
const errorMessage1 = getErrorMessage(customError1);
|
||||
expect(errorMessage1).toBe('Forbidden: the-error-message');
|
||||
|
||||
const customError2 = new Error('the-error-message');
|
||||
const errorMessage2 = getErrorMessage(customError2);
|
||||
expect(errorMessage2).toBe('the-error-message');
|
||||
|
||||
const customError3 = { customErrorMessage: 'the-error-message' };
|
||||
const errorMessage3 = getErrorMessage(customError3);
|
||||
expect(errorMessage3).toBe('{"customErrorMessage":"the-error-message"}');
|
||||
|
||||
const customError4 = { message: 'the-error-message' };
|
||||
const errorMessage4 = getErrorMessage(customError4);
|
||||
expect(errorMessage4).toBe('the-error-message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateAnalyticsForm()', () => {
|
||||
test('initialization', () => {
|
||||
const { getLastHookValue } = getMountedHook();
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useReducer } from 'react';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { getErrorMessage } from '../../../../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../../../../common/util/errors';
|
||||
import { DeepReadonly } from '../../../../../../../common/types/common';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
|
@ -115,7 +115,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
refresh();
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: getErrorMessage(e),
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob',
|
||||
{
|
||||
|
@ -178,7 +178,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
});
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: getErrorMessage(e),
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createIndexPatternErrorMessage',
|
||||
{
|
||||
|
@ -199,7 +199,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
);
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: getErrorMessage(e),
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList',
|
||||
{
|
||||
|
@ -225,7 +225,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
});
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: getErrorMessage(e),
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles',
|
||||
{
|
||||
|
@ -260,7 +260,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
refresh();
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: getErrorMessage(e),
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob',
|
||||
{
|
||||
|
|
|
@ -85,12 +85,11 @@ export const deleteAnalyticsAndDestIndex = async (
|
|||
);
|
||||
}
|
||||
if (status.destIndexDeleted?.error) {
|
||||
const error = extractErrorMessage(status.destIndexDeleted.error);
|
||||
toastNotificationService.displayDangerToast(
|
||||
toastNotificationService.displayErrorToast(
|
||||
status.destIndexDeleted.error,
|
||||
i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage', {
|
||||
defaultMessage:
|
||||
'An error occurred deleting destination index {destinationIndex}: {error}',
|
||||
values: { destinationIndex, error },
|
||||
defaultMessage: 'An error occurred deleting destination index {destinationIndex}',
|
||||
values: { destinationIndex },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ import mockAnomalyRecord from './__mocks__/mock_anomaly_record.json';
|
|||
import mockDetectorsByJob from './__mocks__/mock_detectors_by_job.json';
|
||||
import mockJobConfig from './__mocks__/mock_job_config.json';
|
||||
|
||||
jest.mock('../../util/ml_error', () => class MLRequestFailure {});
|
||||
|
||||
jest.mock('../../services/job_service', () => ({
|
||||
mlJobService: {
|
||||
getJob() {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { cloneDeep, isEqual, pick } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
|
@ -28,8 +30,6 @@ import { loadFullJob } from '../utils';
|
|||
import { validateModelMemoryLimit, validateGroupNames, isValidCustomUrls } from '../validate_job';
|
||||
import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service';
|
||||
import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { collapseLiteralStrings } from '../../../../../../shared_imports';
|
||||
import { DATAFEED_STATE } from '../../../../../../common/constants/states';
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
import { difference } from 'lodash';
|
||||
import { getNewJobLimits } from '../../../../services/ml_server_info';
|
||||
import { mlJobService } from '../../../../services/job_service';
|
||||
import { processCreatedBy } from '../../../../../../common/util/job_utils';
|
||||
import { getSavedObjectsClient } from '../../../../util/dependency_cache';
|
||||
import { ml } from '../../../../services/ml_api_service';
|
||||
|
||||
export function saveJob(job, newJobData, finish) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -41,14 +41,9 @@ export function saveJob(job, newJobData, finish) {
|
|||
|
||||
// if anything has changed, post the changes
|
||||
if (Object.keys(jobData).length) {
|
||||
mlJobService
|
||||
.updateJob(job.job_id, jobData)
|
||||
.then((resp) => {
|
||||
if (resp.success) {
|
||||
saveDatafeedWrapper();
|
||||
} else {
|
||||
reject(resp);
|
||||
}
|
||||
ml.updateJob({ jobId: job.job_id, job: jobData })
|
||||
.then(() => {
|
||||
saveDatafeedWrapper();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
|
@ -59,17 +54,17 @@ export function saveJob(job, newJobData, finish) {
|
|||
});
|
||||
}
|
||||
|
||||
function saveDatafeed(datafeedData, job) {
|
||||
function saveDatafeed(datafeedConfig, job) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (Object.keys(datafeedData).length) {
|
||||
if (Object.keys(datafeedConfig).length) {
|
||||
const datafeedId = job.datafeed_config.datafeed_id;
|
||||
mlJobService.updateDatafeed(datafeedId, datafeedData).then((resp) => {
|
||||
if (resp.success) {
|
||||
ml.updateDatafeed({ datafeedId, datafeedConfig })
|
||||
.then(() => {
|
||||
resolve();
|
||||
} else {
|
||||
reject(resp);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
|
@ -25,9 +27,7 @@ import { ml } from '../../../../../services/ml_api_service';
|
|||
import { checkPermission } from '../../../../../capabilities/check_capabilities';
|
||||
import { GroupList } from './group_list';
|
||||
import { NewGroupInput } from './new_group_input';
|
||||
import { mlMessageBarService } from '../../../../../components/messagebar';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { getToastNotificationService } from '../../../../../services/toast_notification_service';
|
||||
|
||||
function createSelectedGroups(jobs, groups) {
|
||||
const jobIds = jobs.map((j) => j.id);
|
||||
|
@ -160,7 +160,7 @@ export class GroupSelector extends Component {
|
|||
// check success of each job update
|
||||
if (resp.hasOwnProperty(jobId)) {
|
||||
if (resp[jobId].success === false) {
|
||||
mlMessageBarService.notify.error(resp[jobId].error);
|
||||
getToastNotificationService().displayErrorToast(resp[jobId].error);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ export class GroupSelector extends Component {
|
|||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,17 +5,19 @@
|
|||
*/
|
||||
|
||||
import { each } from 'lodash';
|
||||
import { mlMessageBarService } from '../../../components/messagebar';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import rison from 'rison-node';
|
||||
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import { toastNotificationServiceProvider } from '../../../services/toast_notification_service';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import {
|
||||
getToastNotificationService,
|
||||
toastNotificationServiceProvider,
|
||||
} from '../../../services/toast_notification_service';
|
||||
import { getToastNotifications } from '../../../util/dependency_cache';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { stringMatch } from '../../../util/string_utils';
|
||||
import { JOB_STATE, DATAFEED_STATE } from '../../../../../common/constants/states';
|
||||
import { parseInterval } from '../../../../../common/util/parse_interval';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { mlCalendarService } from '../../../services/calendar_service';
|
||||
|
||||
export function loadFullJob(jobId) {
|
||||
|
@ -60,7 +62,6 @@ export function forceStartDatafeeds(jobs, start, end, finish = () => {}) {
|
|||
finish();
|
||||
})
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addDanger(
|
||||
i18n.translate('xpack.ml.jobsList.startJobErrorMessage', {
|
||||
|
@ -81,7 +82,6 @@ export function stopDatafeeds(jobs, finish = () => {}) {
|
|||
finish();
|
||||
})
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addDanger(
|
||||
i18n.translate('xpack.ml.jobsList.stopJobErrorMessage', {
|
||||
|
@ -219,9 +219,8 @@ export async function cloneJob(jobId) {
|
|||
|
||||
window.location.href = '#/jobs/new_job';
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addDanger(
|
||||
getToastNotificationService().displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.jobsList.cloneJobErrorMessage', {
|
||||
defaultMessage: 'Could not clone {jobId}. Job could not be found',
|
||||
values: { jobId },
|
||||
|
@ -239,13 +238,11 @@ export function closeJobs(jobs, finish = () => {}) {
|
|||
finish();
|
||||
})
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addDanger(
|
||||
getToastNotificationService().displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.jobsList.closeJobErrorMessage', {
|
||||
defaultMessage: 'Jobs failed to close',
|
||||
}),
|
||||
error
|
||||
})
|
||||
);
|
||||
finish();
|
||||
});
|
||||
|
@ -260,13 +257,11 @@ export function deleteJobs(jobs, finish = () => {}) {
|
|||
finish();
|
||||
})
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addDanger(
|
||||
getToastNotificationService().displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.jobsList.deleteJobErrorMessage', {
|
||||
defaultMessage: 'Jobs failed to delete',
|
||||
}),
|
||||
error
|
||||
})
|
||||
);
|
||||
finish();
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ import { useEffect, useMemo } from 'react';
|
|||
import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator';
|
||||
import { ErrorResponse } from '../../../../../../../common/types/errors';
|
||||
import { MLHttpFetchError, MLResponseError } from '../../../../../../../common/util/errors';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { JobCreator } from '../job_creator';
|
||||
|
||||
|
@ -36,10 +36,10 @@ export const modelMemoryEstimatorProvider = (
|
|||
jobValidator: JobValidator
|
||||
) => {
|
||||
const modelMemoryCheck$ = new Subject<CalculatePayload>();
|
||||
const error$ = new Subject<ErrorResponse['body']>();
|
||||
const error$ = new Subject<MLHttpFetchError<MLResponseError>>();
|
||||
|
||||
return {
|
||||
get error$(): Observable<ErrorResponse['body']> {
|
||||
get error$(): Observable<MLHttpFetchError<MLResponseError>> {
|
||||
return error$.asObservable();
|
||||
},
|
||||
get updates$(): Observable<string> {
|
||||
|
@ -64,7 +64,7 @@ export const modelMemoryEstimatorProvider = (
|
|||
catchError((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Model memory limit could not be calculated', error.body);
|
||||
error$.next(error.body);
|
||||
error$.next(error);
|
||||
// fallback to the default in case estimation failed
|
||||
return of(DEFAULT_MODEL_MEMORY_LIMIT);
|
||||
})
|
||||
|
@ -120,7 +120,8 @@ export const useModelMemoryEstimator = (
|
|||
title: i18n.translate('xpack.ml.newJob.wizard.estimateModelMemoryError', {
|
||||
defaultMessage: 'Model memory limit could not be calculated',
|
||||
}),
|
||||
text: error.message,
|
||||
text:
|
||||
error.body.attributes?.body.error.caused_by?.reason || error.body.message || undefined,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from '../../../../../common/job_creator';
|
||||
import { ml, BucketSpanEstimatorData } from '../../../../../../../services/ml_api_service';
|
||||
import { useMlContext } from '../../../../../../../contexts/ml';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
export enum ESTIMATE_STATUS {
|
||||
NOT_RUNNING,
|
||||
|
@ -68,7 +68,7 @@ export function useEstimateBucketSpan() {
|
|||
const { name, error, message } = await ml.estimateBucketSpan(data);
|
||||
setStatus(ESTIMATE_STATUS.NOT_RUNNING);
|
||||
if (error === true) {
|
||||
mlMessageBarService.notify.error(message);
|
||||
getToastNotificationService().displayErrorToast(message);
|
||||
} else {
|
||||
jobCreator.bucketSpan = name;
|
||||
jobCreatorUpdate();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React, { FC, useContext, useEffect, useState } from 'react';
|
||||
import { EuiHorizontalRule } from '@elastic/eui';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
import { JobCreatorContext } from '../../../job_creator_context';
|
||||
import { CategorizationJobCreator } from '../../../../../common/job_creator';
|
||||
|
@ -94,7 +94,7 @@ export const CategorizationDetectors: FC<Props> = ({ setIsValid }) => {
|
|||
setFieldExamples(null);
|
||||
setValidationChecks([]);
|
||||
setOverallValidStatus(CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID);
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
}
|
||||
} else {
|
||||
setFieldExamples(null);
|
||||
|
|
|
@ -15,7 +15,7 @@ import { AggFieldPair } from '../../../../../../../../../common/types/fields';
|
|||
import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings';
|
||||
import { MetricSelector } from './metric_selector';
|
||||
import { ChartGrid } from './chart_grid';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
interface Props {
|
||||
setIsValid: (na: boolean) => void;
|
||||
|
@ -109,7 +109,7 @@ export const MultiMetricDetectors: FC<Props> = ({ setIsValid }) => {
|
|||
.loadFieldExampleValues(splitField)
|
||||
.then(setFieldValues)
|
||||
.catch((error) => {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
});
|
||||
} else {
|
||||
setFieldValues([]);
|
||||
|
@ -138,7 +138,7 @@ export const MultiMetricDetectors: FC<Props> = ({ setIsValid }) => {
|
|||
);
|
||||
setLineChartsData(resp);
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartsData([]);
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Results, ModelItem, Anomaly } from '../../../../../common/results_loade
|
|||
import { LineChartData } from '../../../../../common/chart_loader';
|
||||
import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings';
|
||||
import { ChartGrid } from './chart_grid';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
export const MultiMetricDetectorsSummary: FC = () => {
|
||||
const { jobCreator: jc, chartLoader, resultsLoader, chartInterval } = useContext(
|
||||
|
@ -43,7 +43,7 @@ export const MultiMetricDetectorsSummary: FC = () => {
|
|||
const tempFieldValues = await chartLoader.loadFieldExampleValues(jobCreator.splitField);
|
||||
setFieldValues(tempFieldValues);
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -75,7 +75,7 @@ export const MultiMetricDetectorsSummary: FC = () => {
|
|||
);
|
||||
setLineChartsData(resp);
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartsData({});
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -17,7 +17,7 @@ import { getChartSettings, defaultChartSettings } from '../../../charts/common/s
|
|||
import { MetricSelector } from './metric_selector';
|
||||
import { SplitFieldSelector } from '../split_field';
|
||||
import { ChartGrid } from './chart_grid';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
interface Props {
|
||||
setIsValid: (na: boolean) => void;
|
||||
|
@ -159,7 +159,7 @@ export const PopulationDetectors: FC<Props> = ({ setIsValid }) => {
|
|||
|
||||
setLineChartsData(resp);
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartsData([]);
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -15,7 +15,7 @@ import { LineChartData } from '../../../../../common/chart_loader';
|
|||
import { Field, AggFieldPair } from '../../../../../../../../../common/types/fields';
|
||||
import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings';
|
||||
import { ChartGrid } from './chart_grid';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
type DetectorFieldValues = Record<number, string[]>;
|
||||
|
||||
|
@ -81,7 +81,7 @@ export const PopulationDetectorsSummary: FC = () => {
|
|||
|
||||
setLineChartsData(resp);
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartsData({});
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -13,7 +13,7 @@ import { newJobCapsService } from '../../../../../../../services/new_job_capabil
|
|||
import { AggFieldPair } from '../../../../../../../../../common/types/fields';
|
||||
import { AnomalyChart, CHART_TYPE } from '../../../charts/anomaly_chart';
|
||||
import { getChartSettings } from '../../../charts/common/settings';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
interface Props {
|
||||
setIsValid: (na: boolean) => void;
|
||||
|
@ -93,7 +93,7 @@ export const SingleMetricDetectors: FC<Props> = ({ setIsValid }) => {
|
|||
setLineChartData(resp);
|
||||
}
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartData({});
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Results, ModelItem, Anomaly } from '../../../../../common/results_loade
|
|||
import { LineChartData } from '../../../../../common/chart_loader';
|
||||
import { AnomalyChart, CHART_TYPE } from '../../../charts/anomaly_chart';
|
||||
import { getChartSettings } from '../../../charts/common/settings';
|
||||
import { mlMessageBarService } from '../../../../../../../components/messagebar';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
const DTR_IDX = 0;
|
||||
|
||||
|
@ -63,7 +63,7 @@ export const SingleMetricDetectorsSummary: FC = () => {
|
|||
setLineChartData(resp);
|
||||
}
|
||||
} catch (error) {
|
||||
mlMessageBarService.notify.error(error);
|
||||
getToastNotificationService().displayErrorToast(error);
|
||||
setLineChartData({});
|
||||
}
|
||||
setLoadingData(false);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { JobRunner } from '../../../../../common/job_runner';
|
||||
import { useMlKibana } from '../../../../../../../contexts/kibana';
|
||||
import { getErrorMessage } from '../../../../../../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../../../../../../common/util/errors';
|
||||
|
||||
// @ts-ignore
|
||||
import { CreateWatchFlyout } from '../../../../../../jobs_list/components/create_watch_flyout/index';
|
||||
|
@ -70,7 +70,7 @@ export const PostSaveOptions: FC<Props> = ({ jobRunner }) => {
|
|||
defaultMessage: `Error starting job`,
|
||||
}
|
||||
),
|
||||
text: getErrorMessage(error),
|
||||
text: extractErrorMessage(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,13 @@ import { JobCreatorContext } from '../job_creator_context';
|
|||
import { JobRunner } from '../../../common/job_runner';
|
||||
import { mlJobService } from '../../../../../services/job_service';
|
||||
import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout';
|
||||
import { getErrorMessage } from '../../../../../../../common/util/errors';
|
||||
import { isSingleMetricJobCreator, isAdvancedJobCreator } from '../../../common/job_creator';
|
||||
import { JobDetails } from './components/job_details';
|
||||
import { DatafeedDetails } from './components/datafeed_details';
|
||||
import { DetectorChart } from './components/detector_chart';
|
||||
import { JobProgress } from './components/job_progress';
|
||||
import { PostSaveOptions } from './components/post_save_options';
|
||||
import { toastNotificationServiceProvider } from '../../../../../services/toast_notification_service';
|
||||
import {
|
||||
convertToAdvancedJob,
|
||||
resetJob,
|
||||
|
@ -72,15 +72,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
const jr = await jobCreator.createAndStartJob();
|
||||
setJobRunner(jr);
|
||||
} catch (error) {
|
||||
// catch and display all job creation errors
|
||||
const { toasts } = notifications;
|
||||
toasts.addDanger({
|
||||
title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', {
|
||||
defaultMessage: `Job creation error`,
|
||||
}),
|
||||
text: getErrorMessage(error),
|
||||
});
|
||||
setCreatingJob(false);
|
||||
handleJobCreationError(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,18 +83,21 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
await jobCreator.createDatafeed();
|
||||
advancedStartDatafeed(jobCreator, navigateToPath);
|
||||
} catch (error) {
|
||||
// catch and display all job creation errors
|
||||
const { toasts } = notifications;
|
||||
toasts.addDanger({
|
||||
title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', {
|
||||
defaultMessage: `Job creation error`,
|
||||
}),
|
||||
text: getErrorMessage(error),
|
||||
});
|
||||
setCreatingJob(false);
|
||||
handleJobCreationError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleJobCreationError(error: any) {
|
||||
const { displayErrorToast } = toastNotificationServiceProvider(notifications.toasts);
|
||||
displayErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', {
|
||||
defaultMessage: `Job creation error`,
|
||||
})
|
||||
);
|
||||
setCreatingJob(false);
|
||||
}
|
||||
|
||||
function viewResults() {
|
||||
const url = mlJobService.createResultsUrl(
|
||||
[jobCreator.jobId],
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, { Fragment, FC, useContext, useState, useEffect } from 'react';
|
|||
import { WizardNav } from '../wizard_nav';
|
||||
import { WIZARD_STEPS, StepProps } from '../step_types';
|
||||
import { JobCreatorContext } from '../job_creator_context';
|
||||
import { mlJobService } from '../../../../../services/job_service';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { ValidateJob } from '../../../../../components/validate_job';
|
||||
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
|
||||
|
||||
|
@ -66,7 +66,7 @@ export const ValidationStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep })
|
|||
<ValidateJob
|
||||
getJobConfig={getJobConfig}
|
||||
getDuration={getDuration}
|
||||
mlJobService={mlJobService}
|
||||
ml={ml}
|
||||
embedded={true}
|
||||
setIsValid={setIsValid}
|
||||
idFilterList={idFilterList}
|
||||
|
|
|
@ -14,15 +14,13 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { ml } from './ml_api_service';
|
||||
|
||||
import { mlMessageBarService } from '../components/messagebar';
|
||||
import { getToastNotifications } from '../util/dependency_cache';
|
||||
import { getToastNotificationService } from '../services/toast_notification_service';
|
||||
import { isWebUrl } from '../util/url_utils';
|
||||
import { ML_DATA_PREVIEW_COUNT } from '../../../common/util/job_utils';
|
||||
import { TIME_FORMAT } from '../../../common/constants/time_format';
|
||||
import { parseInterval } from '../../../common/util/parse_interval';
|
||||
import { toastNotificationServiceProvider } from '../services/toast_notification_service';
|
||||
import { validateTimeRange } from '../util/date_utils';
|
||||
const msgs = mlMessageBarService;
|
||||
|
||||
let jobs = [];
|
||||
let datafeedIds = {};
|
||||
|
||||
|
@ -119,7 +117,6 @@ class JobService {
|
|||
return new Promise((resolve, reject) => {
|
||||
jobs = [];
|
||||
datafeedIds = {};
|
||||
|
||||
ml.getJobs()
|
||||
.then((resp) => {
|
||||
jobs = resp.jobs;
|
||||
|
@ -162,7 +159,6 @@ class JobService {
|
|||
}
|
||||
processBasicJobInfo(this, jobs);
|
||||
this.jobs = jobs;
|
||||
createJobStats(this.jobs, this.jobStats);
|
||||
resolve({ jobs: this.jobs });
|
||||
});
|
||||
})
|
||||
|
@ -176,12 +172,7 @@ class JobService {
|
|||
|
||||
function error(err) {
|
||||
console.log('jobService error getting list of jobs:', err);
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', {
|
||||
defaultMessage: 'Jobs list could not be retrieved',
|
||||
})
|
||||
);
|
||||
msgs.notify.error('', err);
|
||||
getToastNotificationService().displayErrorToast(err);
|
||||
reject({ jobs, err });
|
||||
}
|
||||
});
|
||||
|
@ -248,7 +239,6 @@ class JobService {
|
|||
}
|
||||
}
|
||||
this.jobs = jobs;
|
||||
createJobStats(this.jobs, this.jobStats);
|
||||
resolve({ jobs: this.jobs });
|
||||
});
|
||||
})
|
||||
|
@ -263,12 +253,7 @@ class JobService {
|
|||
|
||||
function error(err) {
|
||||
console.log('JobService error getting list of jobs:', err);
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', {
|
||||
defaultMessage: 'Jobs list could not be retrieved',
|
||||
})
|
||||
);
|
||||
msgs.notify.error('', err);
|
||||
getToastNotificationService().displayErrorToast(err);
|
||||
reject({ jobs, err });
|
||||
}
|
||||
});
|
||||
|
@ -280,9 +265,6 @@ class JobService {
|
|||
|
||||
ml.getDatafeeds(sId)
|
||||
.then((resp) => {
|
||||
// console.log('loadDatafeeds query response:', resp);
|
||||
|
||||
// make deep copy of datafeeds
|
||||
const datafeeds = resp.datafeeds;
|
||||
|
||||
// load datafeeds stats
|
||||
|
@ -309,12 +291,7 @@ class JobService {
|
|||
|
||||
function error(err) {
|
||||
console.log('loadDatafeeds error getting list of datafeeds:', err);
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage', {
|
||||
defaultMessage: 'datafeeds list could not be retrieved',
|
||||
})
|
||||
);
|
||||
msgs.notify.error('', err);
|
||||
getToastNotificationService().displayErrorToast(err);
|
||||
reject({ jobs, err });
|
||||
}
|
||||
});
|
||||
|
@ -415,62 +392,6 @@ class JobService {
|
|||
return tempJob;
|
||||
}
|
||||
|
||||
updateJob(jobId, job) {
|
||||
// return the promise chain
|
||||
return ml
|
||||
.updateJob({ jobId, job })
|
||||
.then(() => {
|
||||
return { success: true };
|
||||
})
|
||||
.catch((err) => {
|
||||
// TODO - all the functions in here should just return the error and not
|
||||
// display the toast, as currently both the component and this service display
|
||||
// errors, so we end up with duplicate toasts.
|
||||
const toastNotifications = getToastNotifications();
|
||||
const toastNotificationService = toastNotificationServiceProvider(toastNotifications);
|
||||
toastNotificationService.displayErrorToast(
|
||||
err,
|
||||
i18n.translate('xpack.ml.jobService.updateJobErrorTitle', {
|
||||
defaultMessage: 'Could not update job: {jobId}',
|
||||
values: { jobId },
|
||||
})
|
||||
);
|
||||
|
||||
console.error('update job', err);
|
||||
return { success: false, message: err };
|
||||
});
|
||||
}
|
||||
|
||||
validateJob(obj) {
|
||||
// return the promise chain
|
||||
return ml
|
||||
.validateJob(obj)
|
||||
.then((messages) => {
|
||||
return { success: true, messages };
|
||||
})
|
||||
.catch((err) => {
|
||||
const toastNotifications = getToastNotifications();
|
||||
const toastNotificationService = toastNotificationServiceProvider(toastNotifications);
|
||||
toastNotificationService.displayErrorToast(
|
||||
err,
|
||||
i18n.translate('xpack.ml.jobService.validateJobErrorTitle', {
|
||||
defaultMessage: 'Job Validation Error',
|
||||
})
|
||||
);
|
||||
|
||||
console.log('validate job', err);
|
||||
return {
|
||||
success: false,
|
||||
messages: [
|
||||
{
|
||||
status: 'error',
|
||||
text: err.message,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// find a job based on the id
|
||||
getJob(jobId) {
|
||||
const job = find(jobs, (j) => {
|
||||
|
@ -638,25 +559,6 @@ class JobService {
|
|||
});
|
||||
}
|
||||
|
||||
updateDatafeed(datafeedId, datafeedConfig) {
|
||||
return ml
|
||||
.updateDatafeed({ datafeedId, datafeedConfig })
|
||||
.then((resp) => {
|
||||
console.log('update datafeed', resp);
|
||||
return { success: true };
|
||||
})
|
||||
.catch((err) => {
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage', {
|
||||
defaultMessage: 'Could not update datafeed: {datafeedId}',
|
||||
values: { datafeedId },
|
||||
})
|
||||
);
|
||||
console.log('update datafeed', err);
|
||||
return { success: false, message: err.message };
|
||||
});
|
||||
}
|
||||
|
||||
// start the datafeed for a given job
|
||||
// refresh the job state on start success
|
||||
startDatafeed(datafeedId, jobId, start, end) {
|
||||
|
@ -677,49 +579,6 @@ class JobService {
|
|||
})
|
||||
.catch((err) => {
|
||||
console.log('jobService error starting datafeed:', err);
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.couldNotStartDatafeedErrorMessage', {
|
||||
defaultMessage: 'Could not start datafeed for {jobId}',
|
||||
values: { jobId },
|
||||
}),
|
||||
err
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// stop the datafeed for a given job
|
||||
// refresh the job state on stop success
|
||||
stopDatafeed(datafeedId, jobId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ml.stopDatafeed({
|
||||
datafeedId,
|
||||
})
|
||||
.then((resp) => {
|
||||
resolve(resp);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('jobService error stopping datafeed:', err);
|
||||
const couldNotStopDatafeedErrorMessage = i18n.translate(
|
||||
'xpack.ml.jobService.couldNotStopDatafeedErrorMessage',
|
||||
{
|
||||
defaultMessage: 'Could not stop datafeed for {jobId}',
|
||||
values: { jobId },
|
||||
}
|
||||
);
|
||||
|
||||
if (err.statusCode === 500) {
|
||||
msgs.notify.error(couldNotStopDatafeedErrorMessage);
|
||||
msgs.notify.error(
|
||||
i18n.translate('xpack.ml.jobService.requestMayHaveTimedOutErrorMessage', {
|
||||
defaultMessage:
|
||||
'Request may have timed out and may still be running in the background.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
msgs.notify.error(couldNotStopDatafeedErrorMessage, err);
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
@ -887,51 +746,6 @@ function processBasicJobInfo(localJobService, jobsList) {
|
|||
return processedJobsList;
|
||||
}
|
||||
|
||||
// Loop through the jobs list and create basic stats
|
||||
// stats are displayed along the top of the Jobs Management page
|
||||
function createJobStats(jobsList, jobStats) {
|
||||
jobStats.activeNodes.value = 0;
|
||||
jobStats.total.value = 0;
|
||||
jobStats.open.value = 0;
|
||||
jobStats.closed.value = 0;
|
||||
jobStats.failed.value = 0;
|
||||
jobStats.activeDatafeeds.value = 0;
|
||||
|
||||
// object to keep track of nodes being used by jobs
|
||||
const mlNodes = {};
|
||||
let failedJobs = 0;
|
||||
|
||||
each(jobsList, (job) => {
|
||||
if (job.state === 'opened') {
|
||||
jobStats.open.value++;
|
||||
} else if (job.state === 'closed') {
|
||||
jobStats.closed.value++;
|
||||
} else if (job.state === 'failed') {
|
||||
failedJobs++;
|
||||
}
|
||||
|
||||
if (job.datafeed_config && job.datafeed_config.state === 'started') {
|
||||
jobStats.activeDatafeeds.value++;
|
||||
}
|
||||
|
||||
if (job.node && job.node.name) {
|
||||
mlNodes[job.node.name] = {};
|
||||
}
|
||||
});
|
||||
|
||||
jobStats.total.value = jobsList.length;
|
||||
|
||||
// // Only show failed jobs if it is non-zero
|
||||
if (failedJobs) {
|
||||
jobStats.failed.value = failedJobs;
|
||||
jobStats.failed.show = true;
|
||||
} else {
|
||||
jobStats.failed.show = false;
|
||||
}
|
||||
|
||||
jobStats.activeNodes.value = Object.keys(mlNodes).length;
|
||||
}
|
||||
|
||||
function createResultsUrlForJobs(jobsList, resultsPage, userTimeRange) {
|
||||
let from = undefined;
|
||||
let to = undefined;
|
||||
|
|
|
@ -62,7 +62,7 @@ export interface BucketSpanEstimatorResponse {
|
|||
name: string;
|
||||
ms: number;
|
||||
error?: boolean;
|
||||
message?: { msg: string } | string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface GetTimeFieldRangeResponse {
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* 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 { ToastInput, ToastOptions, ToastsStart } from 'kibana/public';
|
||||
import { ResponseError } from 'kibana/server';
|
||||
import { useMemo } from 'react';
|
||||
import { useNotifications } from '../contexts/kibana';
|
||||
import {
|
||||
BoomResponse,
|
||||
extractErrorProperties,
|
||||
MLCustomHttpResponseOptions,
|
||||
MLErrorObject,
|
||||
MLResponseError,
|
||||
} from '../../../common/util/errors';
|
||||
|
||||
export type ToastNotificationService = ReturnType<typeof toastNotificationServiceProvider>;
|
||||
|
||||
export function toastNotificationServiceProvider(toastNotifications: ToastsStart) {
|
||||
return {
|
||||
displayDangerToast(toastOrTitle: ToastInput, options?: ToastOptions) {
|
||||
toastNotifications.addDanger(toastOrTitle, options);
|
||||
},
|
||||
|
||||
displaySuccessToast(toastOrTitle: ToastInput, options?: ToastOptions) {
|
||||
toastNotifications.addSuccess(toastOrTitle, options);
|
||||
},
|
||||
|
||||
displayErrorToast(error: any, toastTitle: string) {
|
||||
const errorObj = this.parseErrorMessage(error);
|
||||
if (errorObj.fullErrorMessage !== undefined) {
|
||||
// Provide access to the full error message via the 'See full error' button.
|
||||
toastNotifications.addError(new Error(errorObj.fullErrorMessage), {
|
||||
title: toastTitle,
|
||||
toastMessage: errorObj.message,
|
||||
});
|
||||
} else {
|
||||
toastNotifications.addDanger(
|
||||
{
|
||||
title: toastTitle,
|
||||
text: errorObj.message,
|
||||
},
|
||||
{ toastLifeTimeMs: 30000 }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
parseErrorMessage(
|
||||
error:
|
||||
| MLCustomHttpResponseOptions<MLResponseError | ResponseError | BoomResponse>
|
||||
| undefined
|
||||
| string
|
||||
| MLResponseError
|
||||
): MLErrorObject {
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
'response' in error &&
|
||||
typeof error.response === 'string' &&
|
||||
error.statusCode !== undefined
|
||||
) {
|
||||
// MLResponseError which has been received back as part of a 'successful' response
|
||||
// where the error was passed in a separate property in the response.
|
||||
const wrapMlResponseError = {
|
||||
body: error,
|
||||
statusCode: error.statusCode,
|
||||
};
|
||||
return extractErrorProperties(wrapMlResponseError);
|
||||
}
|
||||
|
||||
return extractErrorProperties(
|
||||
error as
|
||||
| MLCustomHttpResponseOptions<MLResponseError | ResponseError | BoomResponse>
|
||||
| undefined
|
||||
| string
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to use {@link ToastNotificationService} in React components.
|
||||
*/
|
||||
export function useToastNotificationService(): ToastNotificationService {
|
||||
const { toasts } = useNotifications();
|
||||
return useMemo(() => toastNotificationServiceProvider(toasts), []);
|
||||
}
|
|
@ -4,4 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { mlMessageBarService } from './messagebar_service';
|
||||
export {
|
||||
ToastNotificationService,
|
||||
toastNotificationServiceProvider,
|
||||
useToastNotificationService,
|
||||
getToastNotificationService,
|
||||
} from './toast_notification_service';
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { ToastInput, ToastOptions, ToastsStart } from 'kibana/public';
|
||||
import { useMemo } from 'react';
|
||||
import { getToastNotifications } from '../../util/dependency_cache';
|
||||
import { useNotifications } from '../../contexts/kibana';
|
||||
import {
|
||||
ErrorType,
|
||||
extractErrorProperties,
|
||||
MLRequestFailure,
|
||||
} from '../../../../common/util/errors';
|
||||
|
||||
export type ToastNotificationService = ReturnType<typeof toastNotificationServiceProvider>;
|
||||
|
||||
export function toastNotificationServiceProvider(toastNotifications: ToastsStart) {
|
||||
function displayDangerToast(toastOrTitle: ToastInput, options?: ToastOptions) {
|
||||
toastNotifications.addDanger(toastOrTitle, options);
|
||||
}
|
||||
|
||||
function displayWarningToast(toastOrTitle: ToastInput, options?: ToastOptions) {
|
||||
toastNotifications.addWarning(toastOrTitle, options);
|
||||
}
|
||||
|
||||
function displaySuccessToast(toastOrTitle: ToastInput, options?: ToastOptions) {
|
||||
toastNotifications.addSuccess(toastOrTitle, options);
|
||||
}
|
||||
|
||||
function displayErrorToast(error: ErrorType, title?: string) {
|
||||
const errorObj = extractErrorProperties(error);
|
||||
toastNotifications.addError(new MLRequestFailure(errorObj, error), {
|
||||
title:
|
||||
title ??
|
||||
i18n.translate('xpack.ml.toastNotificationService.errorTitle', {
|
||||
defaultMessage: 'An error has occurred',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return { displayDangerToast, displayWarningToast, displaySuccessToast, displayErrorToast };
|
||||
}
|
||||
|
||||
export function getToastNotificationService() {
|
||||
const toastNotifications = getToastNotifications();
|
||||
return toastNotificationServiceProvider(toastNotifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to use {@link ToastNotificationService} in React components.
|
||||
*/
|
||||
export function useToastNotificationService(): ToastNotificationService {
|
||||
const { toasts } = useNotifications();
|
||||
return useMemo(() => toastNotificationServiceProvider(toasts), []);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
import { getToastNotifications } from '../../../util/dependency_cache';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getErrorMessage } from '../../../../../common/util/errors';
|
||||
import { extractErrorMessage } from '../../../../../common/util/errors';
|
||||
|
||||
export async function deleteCalendars(calendarsToDelete, callback) {
|
||||
if (calendarsToDelete === undefined || calendarsToDelete.length === 0) {
|
||||
|
@ -47,7 +47,7 @@ export async function deleteCalendars(calendarsToDelete, callback) {
|
|||
},
|
||||
}
|
||||
),
|
||||
text: getErrorMessage(error),
|
||||
text: extractErrorMessage(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* 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 { KbnError } from '../../../../../../src/plugins/kibana_utils/public';
|
||||
|
||||
export class MLRequestFailure extends KbnError {
|
||||
origError: any;
|
||||
resp: any;
|
||||
// takes an Error object and and optional response object
|
||||
// if error is falsy (null) the response object will be used
|
||||
// notify will show the full expandable stack trace of the response if a response object is used and no error is passed in.
|
||||
constructor(error: any, resp: any) {
|
||||
error = error || {};
|
||||
super(error.message || JSON.stringify(resp));
|
||||
|
||||
this.origError = error;
|
||||
this.resp = typeof resp === 'string' ? JSON.parse(resp) : resp;
|
||||
}
|
||||
}
|
|
@ -118,6 +118,11 @@ export function datafeedsProvider({ asInternalUser }: IScopedClusterClient) {
|
|||
} catch (error) {
|
||||
if (isRequestTimeout(error)) {
|
||||
return fillResultsWithTimeouts(results, datafeedId, datafeedIds, DATAFEED_STATE.STOPPED);
|
||||
} else {
|
||||
results[datafeedId] = {
|
||||
started: false,
|
||||
error: error.body,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -325,19 +325,20 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat
|
|||
success: false,
|
||||
};
|
||||
|
||||
// Check if analyticsId is valid and get destination index
|
||||
if (deleteDestIndex || deleteDestIndexPattern) {
|
||||
try {
|
||||
const { body } = await client.asInternalUser.ml.getDataFrameAnalytics({
|
||||
id: analyticsId,
|
||||
});
|
||||
if (Array.isArray(body.data_frame_analytics) && body.data_frame_analytics.length > 0) {
|
||||
destinationIndex = body.data_frame_analytics[0].dest.index;
|
||||
}
|
||||
} catch (e) {
|
||||
return response.customError(wrapError(e));
|
||||
try {
|
||||
// Check if analyticsId is valid and get destination index
|
||||
const { body } = await client.asInternalUser.ml.getDataFrameAnalytics({
|
||||
id: analyticsId,
|
||||
});
|
||||
if (Array.isArray(body.data_frame_analytics) && body.data_frame_analytics.length > 0) {
|
||||
destinationIndex = body.data_frame_analytics[0].dest.index;
|
||||
}
|
||||
} catch (e) {
|
||||
// exist early if the job doesn't exist
|
||||
return response.customError(wrapError(e));
|
||||
}
|
||||
|
||||
if (deleteDestIndex || deleteDestIndexPattern) {
|
||||
// If user checks box to delete the destinationIndex associated with the job
|
||||
if (destinationIndex && deleteDestIndex) {
|
||||
// Verify if user has privilege to delete the destination index
|
||||
|
@ -349,8 +350,8 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat
|
|||
index: destinationIndex,
|
||||
});
|
||||
destIndexDeleted.success = true;
|
||||
} catch (deleteIndexError) {
|
||||
destIndexDeleted.error = wrapError(deleteIndexError);
|
||||
} catch ({ body }) {
|
||||
destIndexDeleted.error = body;
|
||||
}
|
||||
} else {
|
||||
return response.forbidden();
|
||||
|
@ -366,7 +367,7 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat
|
|||
}
|
||||
destIndexPatternDeleted.success = true;
|
||||
} catch (deleteDestIndexPatternError) {
|
||||
destIndexPatternDeleted.error = wrapError(deleteDestIndexPatternError);
|
||||
destIndexPatternDeleted.error = deleteDestIndexPatternError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,11 +379,8 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat
|
|||
id: analyticsId,
|
||||
});
|
||||
analyticsJobDeleted.success = true;
|
||||
} catch (deleteDFAError) {
|
||||
analyticsJobDeleted.error = wrapError(deleteDFAError);
|
||||
if (analyticsJobDeleted.error.statusCode === 404) {
|
||||
return response.notFound();
|
||||
}
|
||||
} catch ({ body }) {
|
||||
analyticsJobDeleted.error = body;
|
||||
}
|
||||
const results = {
|
||||
analyticsJobDeleted,
|
||||
|
|
31
x-pack/plugins/transform/common/utils/errors.ts
Normal file
31
x-pack/plugins/transform/common/utils/errors.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface ErrorResponse {
|
||||
body: {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
attributes?: any;
|
||||
};
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function isErrorResponse(arg: any): arg is ErrorResponse {
|
||||
return arg?.body?.error !== undefined && arg?.body?.message !== undefined;
|
||||
}
|
||||
|
||||
export function getErrorMessage(error: any) {
|
||||
if (isErrorResponse(error)) {
|
||||
return `${error.body.error}: ${error.body.message}`;
|
||||
}
|
||||
|
||||
if (typeof error === 'object' && typeof error.message === 'string') {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
return JSON.stringify(error);
|
||||
}
|
|
@ -15,7 +15,6 @@ export const useRequest = jest.fn(() => ({
|
|||
|
||||
// just passing through the reimports
|
||||
export {
|
||||
getErrorMessage,
|
||||
getDataGridSchemaFromKibanaFieldType,
|
||||
getFieldsFromKibanaIndexPattern,
|
||||
multiColumnSortFactory,
|
||||
|
|
|
@ -12,7 +12,8 @@ import {
|
|||
DeleteTransformStatus,
|
||||
TransformEndpointRequest,
|
||||
} from '../../../common';
|
||||
import { extractErrorMessage, getErrorMessage } from '../../shared_imports';
|
||||
import { extractErrorMessage } from '../../shared_imports';
|
||||
import { getErrorMessage } from '../../../common/utils/errors';
|
||||
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
||||
import { REFRESH_TRANSFORM_LIST_STATE, refreshTransformList$, TransformListRow } from '../common';
|
||||
import { ToastNotificationText } from '../components';
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
getFieldType,
|
||||
getDataGridSchemaFromKibanaFieldType,
|
||||
getFieldsFromKibanaIndexPattern,
|
||||
getErrorMessage,
|
||||
showDataGridColumnChartErrorMessageToast,
|
||||
useDataGrid,
|
||||
useRenderCellValue,
|
||||
|
@ -21,6 +20,7 @@ import {
|
|||
UseIndexDataReturnType,
|
||||
INDEX_STATUS,
|
||||
} from '../../shared_imports';
|
||||
import { getErrorMessage } from '../../../common/utils/errors';
|
||||
|
||||
import { isDefaultQuery, matchAllQuery, PivotQuery } from '../common';
|
||||
|
||||
|
|
|
@ -18,13 +18,13 @@ import { formatHumanReadableDateTimeSeconds } from '../../shared_imports';
|
|||
import { getNestedProperty } from '../../../common/utils/object_utils';
|
||||
|
||||
import {
|
||||
getErrorMessage,
|
||||
multiColumnSortFactory,
|
||||
useDataGrid,
|
||||
RenderCellValue,
|
||||
UseIndexDataReturnType,
|
||||
INDEX_STATUS,
|
||||
} from '../../shared_imports';
|
||||
import { getErrorMessage } from '../../../common/utils/errors';
|
||||
|
||||
import {
|
||||
getPreviewRequestBody,
|
||||
|
|
|
@ -32,7 +32,7 @@ import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_reac
|
|||
|
||||
import { PROGRESS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants';
|
||||
|
||||
import { getErrorMessage } from '../../../../../shared_imports';
|
||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
||||
import { getTransformProgress, getDiscoverUrl } from '../../../../common';
|
||||
import { useApi } from '../../../../hooks/use_api';
|
||||
|
|
|
@ -16,7 +16,7 @@ import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_reac
|
|||
import { TransformId } from '../../../../../../common';
|
||||
import { isValidIndexName } from '../../../../../../common/utils/es_utils';
|
||||
|
||||
import { getErrorMessage } from '../../../../../shared_imports';
|
||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
||||
import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies';
|
||||
import { ToastNotificationText } from '../../../../components';
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { getErrorMessage } from '../../../../../shared_imports';
|
||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
||||
import {
|
||||
refreshTransformList$,
|
||||
|
|
|
@ -15,7 +15,6 @@ export {
|
|||
|
||||
export {
|
||||
getFieldType,
|
||||
getErrorMessage,
|
||||
extractErrorMessage,
|
||||
formatHumanReadableDateTimeSeconds,
|
||||
getDataGridSchemaFromKibanaFieldType,
|
||||
|
|
|
@ -10890,7 +10890,6 @@
|
|||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage": "データフレーム分析ジョブ{analyticsId}の削除中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsPrivilegeErrorMessage": "ユーザーはインデックス{indexName}を削除する権限がありません。{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage": "データフレーム分析ジョブ{analyticsId}の削除リクエストが受け付けられました。",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage": "ディスティネーションインデックス{destinationIndex}の削除中にエラーが発生しました。{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternErrorMessage": "インデックスパターン{destinationIndex}の削除中にエラーが発生しました。{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternSuccessMessage": "インデックスパターン{destinationIndex}を削除する要求が確認されました。",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexSuccessMessage": "ディスティネーションインデックス{destinationIndex}を削除する要求が確認されました。",
|
||||
|
@ -11358,16 +11357,9 @@
|
|||
"xpack.ml.jobService.activeDatafeedsLabel": "アクティブなデータフィード",
|
||||
"xpack.ml.jobService.activeMLNodesLabel": "アクティブな ML ノード",
|
||||
"xpack.ml.jobService.closedJobsLabel": "ジョブを作成",
|
||||
"xpack.ml.jobService.couldNotStartDatafeedErrorMessage": "{jobId} のデータフィードを開始できませんでした",
|
||||
"xpack.ml.jobService.couldNotStopDatafeedErrorMessage": "{jobId} のデータフィードを停止できませんでした",
|
||||
"xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage": "データフィードを更新できませんでした: {datafeedId}",
|
||||
"xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage": "データフィードリストを取得できませんでした",
|
||||
"xpack.ml.jobService.failedJobsLabel": "失敗したジョブ",
|
||||
"xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage": "ジョブリストを取得できませんでした",
|
||||
"xpack.ml.jobService.openJobsLabel": "ジョブを開く",
|
||||
"xpack.ml.jobService.requestMayHaveTimedOutErrorMessage": "リクエストがタイムアウトし、まだバックグラウンドで実行中の可能性があります。",
|
||||
"xpack.ml.jobService.totalJobsLabel": "合計ジョブ数",
|
||||
"xpack.ml.jobService.updateJobErrorTitle": "ジョブを更新できませんでした: {jobId}",
|
||||
"xpack.ml.jobService.validateJobErrorTitle": "ジョブ検証エラー",
|
||||
"xpack.ml.jobsList.actionExecuteSuccessfullyNotificationMessage": "{successesJobsCount, plural, one{{successJob}} other{# 件のジョブ}} {actionTextPT}成功",
|
||||
"xpack.ml.jobsList.actionFailedNotificationMessage": "{failureId} が {actionText} に失敗しました",
|
||||
|
@ -11572,7 +11564,6 @@
|
|||
"xpack.ml.maxFileSizeSettingsDescription": "ファイルデータビジュアライザーでデータをインポートするときのファイルサイズ上限を設定します。この設定でサポートされている最大値は1 GBです。",
|
||||
"xpack.ml.maxFileSizeSettingsError": "200 MB、1 GBなどの有効なデータサイズにしてください。",
|
||||
"xpack.ml.maxFileSizeSettingsName": "ファイルデータビジュアライザーの最大ファイルアップロードサイズ",
|
||||
"xpack.ml.messagebarService.errorTitle": "エラーが発生しました",
|
||||
"xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 他のすべてのリクエストはキャンセルされました。",
|
||||
"xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "フィールド値の例のサンプルをトークン化することができませんでした。{message}",
|
||||
"xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "権限が不十分なため、フィールド値の例のトークン化を実行できませんでした。そのため、フィールド値を確認し、カテゴリー分けジョブでの使用が適当かを確認することができません。",
|
||||
|
|
|
@ -10896,7 +10896,6 @@
|
|||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage": "删除数据帧分析作业 {analyticsId} 时发生错误",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsPrivilegeErrorMessage": "用户无权删除索引 {indexName}:{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage": "删除的数据帧分析作业 {analyticsId} 的请求已确认。",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage": "删除目标索引 {destinationIndex} 时发生错误:{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternErrorMessage": "删除索引模式 {destinationIndex} 时发生错误:{error}",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternSuccessMessage": "删除索引模式 {destinationIndex} 的请求已确认。",
|
||||
"xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexSuccessMessage": "删除目标索引 {destinationIndex} 的请求已确认。",
|
||||
|
@ -11365,16 +11364,9 @@
|
|||
"xpack.ml.jobService.activeDatafeedsLabel": "活动数据馈送",
|
||||
"xpack.ml.jobService.activeMLNodesLabel": "活动 ML 节点",
|
||||
"xpack.ml.jobService.closedJobsLabel": "已关闭的作业",
|
||||
"xpack.ml.jobService.couldNotStartDatafeedErrorMessage": "无法开始 {jobId} 的数据馈送",
|
||||
"xpack.ml.jobService.couldNotStopDatafeedErrorMessage": "无法停止 {jobId} 的数据馈送",
|
||||
"xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage": "无法更新数据馈送:{datafeedId}",
|
||||
"xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage": "无法检索数据馈送列表",
|
||||
"xpack.ml.jobService.failedJobsLabel": "失败的作业",
|
||||
"xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage": "无法检索作业列表",
|
||||
"xpack.ml.jobService.openJobsLabel": "打开的作业",
|
||||
"xpack.ml.jobService.requestMayHaveTimedOutErrorMessage": "请求可能已超时,并可能仍在后台运行。",
|
||||
"xpack.ml.jobService.totalJobsLabel": "总计作业数",
|
||||
"xpack.ml.jobService.updateJobErrorTitle": "无法更新作业:{jobId}",
|
||||
"xpack.ml.jobService.validateJobErrorTitle": "作业验证错误",
|
||||
"xpack.ml.jobsList.actionExecuteSuccessfullyNotificationMessage": "{successesJobsCount, plural, one{{successJob}} other{# 个作业}}{actionTextPT}已成功",
|
||||
"xpack.ml.jobsList.actionFailedNotificationMessage": "{failureId} 未能{actionText}",
|
||||
|
@ -11579,7 +11571,6 @@
|
|||
"xpack.ml.maxFileSizeSettingsDescription": "设置在文件数据可视化工具中导入数据时的文件大小限制。此设置支持的最高值为 1GB。",
|
||||
"xpack.ml.maxFileSizeSettingsError": "应为有效的数据大小。如 200MB、1GB",
|
||||
"xpack.ml.maxFileSizeSettingsName": "文件数据可视化工具最大文件上传大小",
|
||||
"xpack.ml.messagebarService.errorTitle": "发生了错误",
|
||||
"xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 所有其他请求已取消。",
|
||||
"xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "无法对示例字段值样本进行分词。{message}",
|
||||
"xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "由于权限不足,无法对字段值示例执行分词。因此,无法检查字段值是否适合用于归类作业。",
|
||||
|
|
|
@ -120,7 +120,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
.expect(404);
|
||||
|
||||
expect(body.error).to.eql('Not Found');
|
||||
expect(body.message).to.eql('Not Found');
|
||||
expect(body.message).to.eql('resource_not_found_exception');
|
||||
});
|
||||
|
||||
describe('with deleteDestIndex setting', function () {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue