mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Visualize] Allows editing broken visualizations caused by runtime fields changes (#94798)
* Add possibility to open visualization when saved field doesn't exists anymore * Fix tests * Fix some remarks * Remove unneeded code * Fix tests * Fix tests * Fix some remarks * Fixed problem with double error popover in visualizations * Fix CI * Fix type * Fix API docs * Don't show error popup for error related to runtime fields * Fix some remarks Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5d54e2990b
commit
e3f31ea9ad
19 changed files with 268 additions and 87 deletions
|
@ -27,6 +27,7 @@ export declare enum KBN_FIELD_TYPES
|
|||
| HISTOGRAM | <code>"histogram"</code> | |
|
||||
| IP | <code>"ip"</code> | |
|
||||
| IP\_RANGE | <code>"ip_range"</code> | |
|
||||
| MISSING | <code>"missing"</code> | |
|
||||
| MURMUR3 | <code>"murmur3"</code> | |
|
||||
| NESTED | <code>"nested"</code> | |
|
||||
| NUMBER | <code>"number"</code> | |
|
||||
|
|
|
@ -27,6 +27,7 @@ export declare enum KBN_FIELD_TYPES
|
|||
| HISTOGRAM | <code>"histogram"</code> | |
|
||||
| IP | <code>"ip"</code> | |
|
||||
| IP\_RANGE | <code>"ip_range"</code> | |
|
||||
| MISSING | <code>"missing"</code> | |
|
||||
| MURMUR3 | <code>"murmur3"</code> | |
|
||||
| NESTED | <code>"nested"</code> | |
|
||||
| NUMBER | <code>"number"</code> | |
|
||||
|
|
|
@ -9,9 +9,9 @@ Merges input$ and output$ streams and debounces emit till next macro-task. Could
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getUpdated$(): Readonly<Rx.Observable<void>>;
|
||||
getUpdated$(): Readonly<Rx.Observable<TEmbeddableInput | TEmbeddableOutput>>;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
`Readonly<Rx.Observable<void>>`
|
||||
`Readonly<Rx.Observable<TEmbeddableInput | TEmbeddableOutput>>`
|
||||
|
||||
|
|
|
@ -80,4 +80,5 @@ export enum KBN_FIELD_TYPES {
|
|||
OBJECT = 'object',
|
||||
NESTED = 'nested',
|
||||
HISTOGRAM = 'histogram',
|
||||
MISSING = 'missing',
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ describe('AggConfigs', () => {
|
|||
describe('#toDsl', () => {
|
||||
beforeEach(() => {
|
||||
indexPattern = stubIndexPattern as IndexPattern;
|
||||
indexPattern.fields.getByName = (name) => (name as unknown) as IndexPatternField;
|
||||
indexPattern.fields.getByName = (name) => (({ name } as unknown) as IndexPatternField);
|
||||
});
|
||||
|
||||
it('uses the sorted aggs', () => {
|
||||
|
|
|
@ -16,16 +16,33 @@ import { AggConfigs, CreateAggConfigParams } from '../agg_configs';
|
|||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import { IBucketAggConfig } from './bucket_agg_type';
|
||||
import { mockAggTypesRegistry } from '../test_helpers';
|
||||
import type { IndexPatternField } from '../../../index_patterns';
|
||||
import { IndexPattern } from '../../../index_patterns/index_patterns/index_pattern';
|
||||
|
||||
const indexPattern = {
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
name: 'machine.os.raw',
|
||||
type: 'string',
|
||||
esTypes: ['string'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
name: 'geo.src',
|
||||
type: 'string',
|
||||
esTypes: ['string'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
} as IndexPattern;
|
||||
|
||||
indexPattern.fields.getByName = (name) => (({ name } as unknown) as IndexPatternField);
|
||||
|
||||
const singleTerm = {
|
||||
aggs: [
|
||||
|
|
|
@ -10,6 +10,8 @@ import { AggConfigs } from '../agg_configs';
|
|||
import { METRIC_TYPES } from '../metrics';
|
||||
import { mockAggTypesRegistry } from '../test_helpers';
|
||||
import { BUCKET_TYPES } from './bucket_agg_types';
|
||||
import type { IndexPatternField } from '../../../index_patterns';
|
||||
import { IndexPattern } from '../../../index_patterns/index_patterns/index_pattern';
|
||||
|
||||
describe('Terms Agg', () => {
|
||||
describe('order agg editor UI', () => {
|
||||
|
@ -17,16 +19,44 @@ describe('Terms Agg', () => {
|
|||
const indexPattern = {
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
fields: {
|
||||
getByName: () => field,
|
||||
filter: () => [field],
|
||||
},
|
||||
} as any;
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
esTypes: ['string'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
name: 'string_field',
|
||||
type: 'string',
|
||||
esTypes: ['string'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
name: 'empty_number_field',
|
||||
type: 'number',
|
||||
esTypes: ['number'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
name: 'number_field',
|
||||
type: 'number',
|
||||
esTypes: ['number'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
],
|
||||
} as IndexPattern;
|
||||
|
||||
const field = {
|
||||
name: 'field',
|
||||
indexPattern,
|
||||
};
|
||||
indexPattern.fields.getByName = (name) => (({ name } as unknown) as IndexPatternField);
|
||||
indexPattern.fields.filter = () => indexPattern.fields;
|
||||
|
||||
return new AggConfigs(
|
||||
indexPattern,
|
||||
|
@ -207,16 +237,28 @@ describe('Terms Agg', () => {
|
|||
const indexPattern = {
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
fields: {
|
||||
getByName: () => field,
|
||||
filter: () => [field],
|
||||
},
|
||||
} as any;
|
||||
fields: [
|
||||
{
|
||||
name: 'string_field',
|
||||
type: 'string',
|
||||
esTypes: ['string'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
name: 'number_field',
|
||||
type: 'number',
|
||||
esTypes: ['number'],
|
||||
aggregatable: true,
|
||||
filterable: true,
|
||||
searchable: true,
|
||||
},
|
||||
],
|
||||
} as IndexPattern;
|
||||
|
||||
const field = {
|
||||
name: 'field',
|
||||
indexPattern,
|
||||
};
|
||||
indexPattern.fields.getByName = (name) => (({ name } as unknown) as IndexPatternField);
|
||||
indexPattern.fields.filter = () => indexPattern.fields;
|
||||
|
||||
const aggConfigs = new AggConfigs(
|
||||
indexPattern,
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IAggConfig } from '../agg_config';
|
||||
import { SavedObjectNotFound } from '../../../../../../plugins/kibana_utils/common';
|
||||
import {
|
||||
SavedFieldNotFound,
|
||||
SavedFieldTypeInvalidForAgg,
|
||||
} from '../../../../../../plugins/kibana_utils/common';
|
||||
import { BaseParamType } from './base';
|
||||
import { propFilter } from '../utils';
|
||||
import { KBN_FIELD_TYPES } from '../../../kbn_field_types/types';
|
||||
|
@ -47,13 +50,49 @@ export class FieldParamType extends BaseParamType {
|
|||
);
|
||||
}
|
||||
|
||||
if (field.scripted) {
|
||||
if (field.type === KBN_FIELD_TYPES.MISSING) {
|
||||
throw new SavedFieldNotFound(
|
||||
i18n.translate(
|
||||
'data.search.aggs.paramTypes.field.notFoundSavedFieldParameterErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'The field "{fieldParameter}" associated with this object no longer exists in the index pattern. Please use another field.',
|
||||
values: {
|
||||
fieldParameter: field.name,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const validField = this.getAvailableFields(aggConfig).find(
|
||||
(f: any) => f.name === field.name
|
||||
);
|
||||
|
||||
if (!validField) {
|
||||
throw new SavedFieldTypeInvalidForAgg(
|
||||
i18n.translate(
|
||||
'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Saved field "{fieldParameter}" of index pattern "{indexPatternTitle}" is invalid for use with the "{aggType}" aggregation. Please select a new field.',
|
||||
values: {
|
||||
fieldParameter: field.name,
|
||||
aggType: aggConfig?.type?.title,
|
||||
indexPatternTitle: aggConfig.getIndexPattern().title,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (validField.scripted) {
|
||||
output.params.script = {
|
||||
source: field.script,
|
||||
lang: field.lang,
|
||||
source: validField.script,
|
||||
lang: validField.lang,
|
||||
};
|
||||
} else {
|
||||
output.params.field = field.name;
|
||||
output.params.field = validField.name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -69,28 +108,15 @@ export class FieldParamType extends BaseParamType {
|
|||
const field = aggConfig.getIndexPattern().fields.getByName(fieldName);
|
||||
|
||||
if (!field) {
|
||||
throw new SavedObjectNotFound('index-pattern-field', fieldName);
|
||||
return new IndexPatternField({
|
||||
type: KBN_FIELD_TYPES.MISSING,
|
||||
name: fieldName,
|
||||
searchable: false,
|
||||
aggregatable: false,
|
||||
});
|
||||
}
|
||||
|
||||
const validField = this.getAvailableFields(aggConfig).find((f: any) => f.name === fieldName);
|
||||
if (!validField) {
|
||||
throw new Error(
|
||||
i18n.translate(
|
||||
'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Saved field "{fieldParameter}" of index pattern "{indexPatternTitle}" is invalid for use with the "{aggType}" aggregation. Please select a new field.',
|
||||
values: {
|
||||
fieldParameter: fieldName,
|
||||
aggType: aggConfig?.type?.title,
|
||||
indexPatternTitle: aggConfig.getIndexPattern().title,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return validField;
|
||||
return field;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1786,6 +1786,8 @@ export enum KBN_FIELD_TYPES {
|
|||
// (undocumented)
|
||||
IP_RANGE = "ip_range",
|
||||
// (undocumented)
|
||||
MISSING = "missing",
|
||||
// (undocumented)
|
||||
MURMUR3 = "murmur3",
|
||||
// (undocumented)
|
||||
NESTED = "nested",
|
||||
|
|
|
@ -1082,6 +1082,8 @@ export enum KBN_FIELD_TYPES {
|
|||
// (undocumented)
|
||||
IP_RANGE = "ip_range",
|
||||
// (undocumented)
|
||||
MISSING = "missing",
|
||||
// (undocumented)
|
||||
MURMUR3 = "murmur3",
|
||||
// (undocumented)
|
||||
NESTED = "nested",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { cloneDeep, isEqual } from 'lodash';
|
||||
import * as Rx from 'rxjs';
|
||||
import { merge } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, map, mapTo, skip } from 'rxjs/operators';
|
||||
import { debounceTime, distinctUntilChanged, map, skip } from 'rxjs/operators';
|
||||
import { RenderCompleteDispatcher } from '../../../../kibana_utils/public';
|
||||
import { Adapters } from '../types';
|
||||
import { IContainer } from '../containers';
|
||||
|
@ -111,10 +111,9 @@ export abstract class Embeddable<
|
|||
* In case corresponding state change triggered `reload` this stream is guarantied to emit later,
|
||||
* which allows to skip any state handling in case `reload` already handled it.
|
||||
*/
|
||||
public getUpdated$(): Readonly<Rx.Observable<void>> {
|
||||
public getUpdated$(): Readonly<Rx.Observable<TEmbeddableInput | TEmbeddableOutput>> {
|
||||
return merge(this.getInput$().pipe(skip(1)), this.getOutput$().pipe(skip(1))).pipe(
|
||||
debounceTime(0),
|
||||
mapTo(undefined)
|
||||
debounceTime(0)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ export abstract class Embeddable<TEmbeddableInput extends EmbeddableInput = Embe
|
|||
getRoot(): IEmbeddable | IContainer;
|
||||
// (undocumented)
|
||||
getTitle(): string;
|
||||
getUpdated$(): Readonly<Rx.Observable<void>>;
|
||||
getUpdated$(): Readonly<Rx.Observable<TEmbeddableInput | TEmbeddableOutput>>;
|
||||
// (undocumented)
|
||||
readonly id: string;
|
||||
// (undocumented)
|
||||
|
|
|
@ -32,7 +32,7 @@ export class DuplicateField extends KbnError {
|
|||
export class SavedObjectNotFound extends KbnError {
|
||||
public savedObjectType: string;
|
||||
public savedObjectId?: string;
|
||||
constructor(type: string, id?: string, link?: string) {
|
||||
constructor(type: string, id?: string, link?: string, customMessage?: string) {
|
||||
const idMsg = id ? ` (id: ${id})` : '';
|
||||
let message = `Could not locate that ${type}${idMsg}`;
|
||||
|
||||
|
@ -40,13 +40,31 @@ export class SavedObjectNotFound extends KbnError {
|
|||
message += `, [click here to re-create it](${link})`;
|
||||
}
|
||||
|
||||
super(message);
|
||||
super(customMessage || message);
|
||||
|
||||
this.savedObjectType = type;
|
||||
this.savedObjectId = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A saved field doesn't exist anymore
|
||||
*/
|
||||
export class SavedFieldNotFound extends KbnError {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A saved field type isn't compatible with aggregation
|
||||
*/
|
||||
export class SavedFieldTypeInvalidForAgg extends KbnError {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This error is for scenarios where a saved object is detected that has invalid JSON properties.
|
||||
* There was a scenario where we were importing objects with double-encoded JSON, and the system
|
||||
|
|
|
@ -11,7 +11,7 @@ import { act } from 'react-dom/test-utils';
|
|||
import { mount, shallow, ReactWrapper } from 'enzyme';
|
||||
import { EuiComboBoxProps, EuiComboBox } from '@elastic/eui';
|
||||
|
||||
import { IAggConfig, IndexPatternField } from 'src/plugins/data/public';
|
||||
import { IAggConfig, IndexPatternField, AggParam } from 'src/plugins/data/public';
|
||||
import { ComboBoxGroupedOptions } from '../../utils';
|
||||
import { FieldParamEditor, FieldParamEditorProps } from './field';
|
||||
import { EditorVisState } from '../sidebar/state/reducers';
|
||||
|
@ -42,7 +42,7 @@ describe('FieldParamEditor component', () => {
|
|||
setTouched = jest.fn();
|
||||
onChange = jest.fn();
|
||||
|
||||
field = { displayName: 'bytes' } as IndexPatternField;
|
||||
field = { displayName: 'bytes', type: 'bytes' } as IndexPatternField;
|
||||
option = { label: 'bytes', target: field };
|
||||
indexedFields = [
|
||||
{
|
||||
|
@ -52,7 +52,16 @@ describe('FieldParamEditor component', () => {
|
|||
];
|
||||
|
||||
defaultProps = {
|
||||
agg: {} as IAggConfig,
|
||||
agg: {
|
||||
type: {
|
||||
params: [
|
||||
({
|
||||
name: 'field',
|
||||
filterFieldTypes: ['bytes'],
|
||||
} as unknown) as AggParam,
|
||||
],
|
||||
},
|
||||
} as IAggConfig,
|
||||
aggParam: {
|
||||
name: 'field',
|
||||
type: 'field',
|
||||
|
|
|
@ -13,7 +13,13 @@ import useMount from 'react-use/lib/useMount';
|
|||
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { AggParam, IAggConfig, IFieldParamType, IndexPatternField } from 'src/plugins/data/public';
|
||||
import {
|
||||
AggParam,
|
||||
IAggConfig,
|
||||
IFieldParamType,
|
||||
IndexPatternField,
|
||||
KBN_FIELD_TYPES,
|
||||
} from '../../../../../plugins/data/public';
|
||||
import { formatListAsProse, parseCommaSeparatedList, useValidation } from './utils';
|
||||
import { AggParamEditorProps } from '../agg_param_props';
|
||||
import { ComboBoxGroupedOptions } from '../../utils';
|
||||
|
@ -55,6 +61,7 @@ function FieldParamEditor({
|
|||
}
|
||||
};
|
||||
const errors = customError ? [customError] : [];
|
||||
let showErrorMessageImmediately = false;
|
||||
|
||||
if (!indexedFields.length) {
|
||||
errors.push(
|
||||
|
@ -69,9 +76,38 @@ function FieldParamEditor({
|
|||
);
|
||||
}
|
||||
|
||||
if (value && value.type === KBN_FIELD_TYPES.MISSING) {
|
||||
errors.push(
|
||||
i18n.translate('visDefaultEditor.controls.field.fieldIsNotExists', {
|
||||
defaultMessage:
|
||||
'The field "{fieldParameter}" associated with this object no longer exists in the index pattern. Please use another field.',
|
||||
values: {
|
||||
fieldParameter: value.name,
|
||||
},
|
||||
})
|
||||
);
|
||||
showErrorMessageImmediately = true;
|
||||
} else if (
|
||||
value &&
|
||||
!getFieldTypes(agg).find((type: string) => type === value.type || type === '*')
|
||||
) {
|
||||
errors.push(
|
||||
i18n.translate('visDefaultEditor.controls.field.invalidFieldForAggregation', {
|
||||
defaultMessage:
|
||||
'Saved field "{fieldParameter}" of index pattern "{indexPatternTitle}" is invalid for use with this aggregation. Please select a new field.',
|
||||
values: {
|
||||
fieldParameter: value?.name,
|
||||
indexPatternTitle: agg.getIndexPattern && agg.getIndexPattern().title,
|
||||
},
|
||||
})
|
||||
);
|
||||
showErrorMessageImmediately = true;
|
||||
}
|
||||
|
||||
const isValid = !!value && !errors.length && !isDirty;
|
||||
// we show an error message right away if there is no compatible fields
|
||||
const showErrorMessage = (showValidation || !indexedFields.length) && !isValid;
|
||||
const showErrorMessage =
|
||||
(showValidation || !indexedFields.length || showErrorMessageImmediately) && !isValid;
|
||||
|
||||
useValidation(setValidity, isValid);
|
||||
useMount(() => {
|
||||
|
@ -122,10 +158,14 @@ function FieldParamEditor({
|
|||
}
|
||||
|
||||
function getFieldTypesString(agg: IAggConfig) {
|
||||
return formatListAsProse(getFieldTypes(agg), { inclusive: false });
|
||||
}
|
||||
|
||||
function getFieldTypes(agg: IAggConfig) {
|
||||
const param =
|
||||
get(agg, 'type.params', []).find((p: AggParam) => p.name === 'field') ||
|
||||
({} as IFieldParamType);
|
||||
return formatListAsProse(parseCommaSeparatedList(param.filterFieldTypes), { inclusive: false });
|
||||
return parseCommaSeparatedList(param.filterFieldTypes || []);
|
||||
}
|
||||
|
||||
export { FieldParamEditor };
|
||||
|
|
|
@ -149,8 +149,9 @@ export class VisualizeEmbeddable
|
|||
}
|
||||
|
||||
this.subscriptions.push(
|
||||
this.getUpdated$().subscribe(() => {
|
||||
this.getUpdated$().subscribe((value) => {
|
||||
const isDirty = this.handleChanges();
|
||||
|
||||
if (isDirty && this.handler) {
|
||||
this.updateHandler();
|
||||
}
|
||||
|
|
|
@ -18,8 +18,17 @@ import { SavedObject } from 'src/plugins/saved_objects/public';
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { ExpressionValueError } from 'src/plugins/expressions/public';
|
||||
import { createSavedSearchesLoader } from '../../../../discover/public';
|
||||
import { SavedFieldNotFound, SavedFieldTypeInvalidForAgg } from '../../../../kibana_utils/common';
|
||||
import { VisualizeServices } from '../types';
|
||||
|
||||
function isErrorRelatedToRuntimeFields(error: ExpressionValueError['error']) {
|
||||
const originalError = error.original || error;
|
||||
return (
|
||||
originalError instanceof SavedFieldNotFound ||
|
||||
originalError instanceof SavedFieldTypeInvalidForAgg
|
||||
);
|
||||
}
|
||||
|
||||
const createVisualizeEmbeddableAndLinkSavedSearch = async (
|
||||
vis: Vis,
|
||||
visualizeServices: VisualizeServices
|
||||
|
@ -37,7 +46,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async (
|
|||
})) as VisualizeEmbeddableContract;
|
||||
|
||||
embeddableHandler.getOutput$().subscribe((output) => {
|
||||
if (output.error) {
|
||||
if (output.error && !isErrorRelatedToRuntimeFields(output.error)) {
|
||||
data.search.showError(
|
||||
((output.error as unknown) as ExpressionValueError['error']).original || output.error
|
||||
);
|
||||
|
|
|
@ -11,13 +11,12 @@ import { EventEmitter } from 'events';
|
|||
import { parse } from 'query-string';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { redirectWhenMissing } from '../../../../../kibana_utils/public';
|
||||
|
||||
import { getVisualizationInstance } from '../get_visualization_instance';
|
||||
import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs';
|
||||
import { SavedVisInstance, VisualizeServices, IEditorController } from '../../types';
|
||||
import { VisualizeConstants } from '../../visualize_constants';
|
||||
import { getVisEditorsRegistry } from '../../../services';
|
||||
import { redirectToSavedObjectPage } from '../utils';
|
||||
|
||||
/**
|
||||
* This effect is responsible for instantiating a saved vis or creating a new one
|
||||
|
@ -43,9 +42,7 @@ export const useSavedVisInstance = (
|
|||
chrome,
|
||||
history,
|
||||
dashboard,
|
||||
setActiveUrl,
|
||||
toastNotifications,
|
||||
http: { basePath },
|
||||
stateTransferService,
|
||||
application: { navigateToApp },
|
||||
} = services;
|
||||
|
@ -131,27 +128,8 @@ export const useSavedVisInstance = (
|
|||
visEditorController,
|
||||
});
|
||||
} catch (error) {
|
||||
const managementRedirectTarget = {
|
||||
app: 'management',
|
||||
path: `kibana/objects/savedVisualizations/${visualizationIdFromUrl}`,
|
||||
};
|
||||
|
||||
try {
|
||||
redirectWhenMissing({
|
||||
history,
|
||||
navigateToApp,
|
||||
toastNotifications,
|
||||
basePath,
|
||||
mapping: {
|
||||
visualization: VisualizeConstants.LANDING_PAGE_PATH,
|
||||
search: managementRedirectTarget,
|
||||
'index-pattern': managementRedirectTarget,
|
||||
'index-pattern-field': managementRedirectTarget,
|
||||
},
|
||||
onBeforeRedirect() {
|
||||
setActiveUrl(VisualizeConstants.LANDING_PAGE_PATH);
|
||||
},
|
||||
})(error);
|
||||
redirectToSavedObjectPage(services, error, visualizationIdFromUrl);
|
||||
} catch (e) {
|
||||
toastNotifications.addWarning({
|
||||
title: i18n.translate('visualize.createVisualization.failedToLoadErrorMessage', {
|
||||
|
|
|
@ -10,6 +10,8 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { ChromeStart, DocLinksStart } from 'kibana/public';
|
||||
import { Filter } from '../../../../data/public';
|
||||
import { redirectWhenMissing } from '../../../../kibana_utils/public';
|
||||
import { VisualizeConstants } from '../visualize_constants';
|
||||
import { VisualizeServices, VisualizeEditorVisInstance } from '../types';
|
||||
|
||||
export const addHelpMenuToAppChrome = (chrome: ChromeStart, docLinks: DocLinksStart) => {
|
||||
|
@ -58,3 +60,36 @@ export const visStateToEditorState = (
|
|||
linked: savedVis && savedVis.id ? !!savedVis.savedSearchId : !!savedVisState.savedSearchId,
|
||||
};
|
||||
};
|
||||
|
||||
export const redirectToSavedObjectPage = (
|
||||
services: VisualizeServices,
|
||||
error: any,
|
||||
savedVisualizationsId?: string
|
||||
) => {
|
||||
const {
|
||||
history,
|
||||
setActiveUrl,
|
||||
toastNotifications,
|
||||
http: { basePath },
|
||||
application: { navigateToApp },
|
||||
} = services;
|
||||
const managementRedirectTarget = {
|
||||
app: 'management',
|
||||
path: `kibana/objects/savedVisualizations/${savedVisualizationsId}`,
|
||||
};
|
||||
redirectWhenMissing({
|
||||
history,
|
||||
navigateToApp,
|
||||
toastNotifications,
|
||||
basePath,
|
||||
mapping: {
|
||||
visualization: VisualizeConstants.LANDING_PAGE_PATH,
|
||||
search: managementRedirectTarget,
|
||||
'index-pattern': managementRedirectTarget,
|
||||
'index-pattern-field': managementRedirectTarget,
|
||||
},
|
||||
onBeforeRedirect() {
|
||||
setActiveUrl(VisualizeConstants.LANDING_PAGE_PATH);
|
||||
},
|
||||
})(error);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue