mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[8.0] [maps] fix Dashboards with by-value maps are broken when copied to new space (#125599) (#125887)
* merge with main * update expect
This commit is contained in:
parent
dd1ed799e4
commit
2c775e32a0
11 changed files with 226 additions and 17 deletions
49
x-pack/plugins/maps/common/embeddable/extract.test.ts
Normal file
49
x-pack/plugins/maps/common/embeddable/extract.test.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { extract } from './extract';
|
||||
|
||||
test('Should return original state and empty references with by-reference embeddable state', () => {
|
||||
const mapByReferenceInput = {
|
||||
id: '2192e502-0ec7-4316-82fb-c9bbf78525c4',
|
||||
type: 'map',
|
||||
};
|
||||
|
||||
expect(extract!(mapByReferenceInput)).toEqual({
|
||||
state: mapByReferenceInput,
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('Should update state with refNames with by-value embeddable state', () => {
|
||||
const mapByValueInput = {
|
||||
id: '8d62c3f0-c61f-4c09-ac24-9b8ee4320e20',
|
||||
attributes: {
|
||||
layerListJSON:
|
||||
'[{"sourceDescriptor":{"indexPatternId":"90943e30-9a47-11e8-b64d-95841ca0b247"}}]',
|
||||
},
|
||||
type: 'map',
|
||||
};
|
||||
|
||||
expect(extract!(mapByValueInput)).toEqual({
|
||||
references: [
|
||||
{
|
||||
id: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
||||
name: 'layer_0_source_index_pattern',
|
||||
type: 'index-pattern',
|
||||
},
|
||||
],
|
||||
state: {
|
||||
id: '8d62c3f0-c61f-4c09-ac24-9b8ee4320e20',
|
||||
attributes: {
|
||||
layerListJSON:
|
||||
'[{"sourceDescriptor":{"indexPatternRefName":"layer_0_source_index_pattern"}}]',
|
||||
},
|
||||
type: 'map',
|
||||
},
|
||||
});
|
||||
});
|
34
x-pack/plugins/maps/common/embeddable/extract.ts
Normal file
34
x-pack/plugins/maps/common/embeddable/extract.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EmbeddableRegistryDefinition } from 'src/plugins/embeddable/server';
|
||||
import { MapEmbeddablePersistableState } from './types';
|
||||
import { MapSavedObjectAttributes } from '../map_saved_object_type';
|
||||
import { extractReferences } from '../migrations/references';
|
||||
|
||||
export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
|
||||
const typedState = state as MapEmbeddablePersistableState;
|
||||
|
||||
// by-reference embeddable
|
||||
if (!('attributes' in typedState) || typedState.attributes === undefined) {
|
||||
// No references to extract for by-reference embeddable since all references are stored with by-reference saved object
|
||||
return { state, references: [] };
|
||||
}
|
||||
|
||||
// by-value embeddable
|
||||
const { attributes, references } = extractReferences({
|
||||
attributes: typedState.attributes as MapSavedObjectAttributes,
|
||||
});
|
||||
|
||||
return {
|
||||
state: {
|
||||
...state,
|
||||
attributes,
|
||||
},
|
||||
references,
|
||||
};
|
||||
};
|
9
x-pack/plugins/maps/common/embeddable/index.ts
Normal file
9
x-pack/plugins/maps/common/embeddable/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { extract } from './extract';
|
||||
export { inject } from './inject';
|
51
x-pack/plugins/maps/common/embeddable/inject.test.ts
Normal file
51
x-pack/plugins/maps/common/embeddable/inject.test.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { inject } from './inject';
|
||||
|
||||
test('Should return original state with by-reference embeddable state', () => {
|
||||
const mapByReferenceInput = {
|
||||
id: '2192e502-0ec7-4316-82fb-c9bbf78525c4',
|
||||
type: 'map',
|
||||
};
|
||||
|
||||
const refernces = [
|
||||
{
|
||||
name: 'panel_2192e502-0ec7-4316-82fb-c9bbf78525c4',
|
||||
type: 'map',
|
||||
id: '7f92d7d0-8e5f-11ec-9477-312c8a6de896',
|
||||
},
|
||||
];
|
||||
|
||||
expect(inject!(mapByReferenceInput, refernces)).toEqual(mapByReferenceInput);
|
||||
});
|
||||
|
||||
test('Should inject refNames with by-value embeddable state', () => {
|
||||
const mapByValueInput = {
|
||||
id: '8d62c3f0-c61f-4c09-ac24-9b8ee4320e20',
|
||||
attributes: {
|
||||
layerListJSON:
|
||||
'[{"sourceDescriptor":{"indexPatternRefName":"layer_0_source_index_pattern"}}]',
|
||||
},
|
||||
type: 'map',
|
||||
};
|
||||
const refernces = [
|
||||
{
|
||||
name: 'layer_0_source_index_pattern',
|
||||
type: 'index-pattern',
|
||||
id: 'changed_index_pattern_id',
|
||||
},
|
||||
];
|
||||
|
||||
expect(inject!(mapByValueInput, refernces)).toEqual({
|
||||
id: '8d62c3f0-c61f-4c09-ac24-9b8ee4320e20',
|
||||
attributes: {
|
||||
layerListJSON: '[{"sourceDescriptor":{"indexPatternId":"changed_index_pattern_id"}}]',
|
||||
},
|
||||
type: 'map',
|
||||
});
|
||||
});
|
44
x-pack/plugins/maps/common/embeddable/inject.ts
Normal file
44
x-pack/plugins/maps/common/embeddable/inject.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EmbeddableRegistryDefinition } from 'src/plugins/embeddable/server';
|
||||
import { MapEmbeddablePersistableState } from './types';
|
||||
import { MapSavedObjectAttributes } from '../map_saved_object_type';
|
||||
import { extractReferences, injectReferences } from '../migrations/references';
|
||||
|
||||
export const inject: EmbeddableRegistryDefinition['inject'] = (state, references) => {
|
||||
const typedState = state as MapEmbeddablePersistableState;
|
||||
|
||||
// by-reference embeddable
|
||||
if (!('attributes' in typedState) || typedState.attributes === undefined) {
|
||||
return typedState;
|
||||
}
|
||||
|
||||
// by-value embeddable
|
||||
try {
|
||||
// run embeddable state through extract logic to ensure any state with hard coded ids is replace with refNames
|
||||
// refName generation will produce consistent values allowing inject logic to then replace refNames with current ids.
|
||||
const { attributes: attributesWithNoHardCodedIds } = extractReferences({
|
||||
attributes: typedState.attributes as MapSavedObjectAttributes,
|
||||
});
|
||||
|
||||
const { attributes: attributesWithInjectedIds } = injectReferences({
|
||||
attributes: attributesWithNoHardCodedIds,
|
||||
references,
|
||||
});
|
||||
return {
|
||||
...typedState,
|
||||
attributes: attributesWithInjectedIds,
|
||||
};
|
||||
} catch (error) {
|
||||
// inject exception prevents entire dashboard from display
|
||||
// Instead of throwing, swallow error and let dashboard display
|
||||
// Errors will surface in map panel. Any layer that failed injection will surface the error in the legend
|
||||
// Users can then manually edit map to resolve any problems.
|
||||
return typedState;
|
||||
}
|
||||
};
|
13
x-pack/plugins/maps/common/embeddable/types.ts
Normal file
13
x-pack/plugins/maps/common/embeddable/types.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SerializableRecord } from '@kbn/utility-types';
|
||||
import { EmbeddableStateWithType } from 'src/plugins/embeddable/common';
|
||||
|
||||
export type MapEmbeddablePersistableState = EmbeddableStateWithType & {
|
||||
attributes: SerializableRecord;
|
||||
};
|
|
@ -6,16 +6,15 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EmbeddableStateWithType } from 'src/plugins/embeddable/common';
|
||||
import {
|
||||
EmbeddableFactoryDefinition,
|
||||
IContainer,
|
||||
} from '../../../../../src/plugins/embeddable/public';
|
||||
import { MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
|
||||
import { getMapEmbeddableDisplayName } from '../../common/i18n_getters';
|
||||
import { MapByReferenceInput, MapEmbeddableInput, MapByValueInput } from './types';
|
||||
import { extract, inject } from '../../common/embeddable';
|
||||
import { MapByReferenceInput, MapEmbeddableInput } from './types';
|
||||
import { lazyLoadMapModules } from '../lazy_load_bundle';
|
||||
import { extractReferences } from '../../common/migrations/references';
|
||||
|
||||
export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
type = MAP_SAVED_OBJECT_TYPE;
|
||||
|
@ -63,17 +62,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
);
|
||||
};
|
||||
|
||||
extract(state: EmbeddableStateWithType) {
|
||||
const maybeMapByValueInput = state as EmbeddableStateWithType | MapByValueInput;
|
||||
inject = inject;
|
||||
|
||||
if ((maybeMapByValueInput as MapByValueInput).attributes !== undefined) {
|
||||
const { references } = extractReferences({
|
||||
attributes: (maybeMapByValueInput as MapByValueInput).attributes,
|
||||
});
|
||||
|
||||
return { state, references };
|
||||
}
|
||||
|
||||
return { state, references: [] };
|
||||
}
|
||||
extract = extract;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@ describe('saved object migrations and embeddable migrations', () => {
|
|||
const savedObjectMigrationVersions = Object.keys(savedObjectMigrations).filter((version) => {
|
||||
return semverGte(version, '7.13.0');
|
||||
});
|
||||
const embeddableMigrationVersions = Object.keys(embeddableMigrations);
|
||||
const embeddableMigrationVersions = Object.keys(embeddableMigrations).filter((key) => {
|
||||
// filter out embeddable only migration keys
|
||||
return !['8.0.1'].includes(key);
|
||||
});
|
||||
expect(savedObjectMigrationVersions.sort()).toEqual(embeddableMigrationVersions.sort());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { SerializableRecord } from '@kbn/utility-types';
|
|||
import { MapSavedObjectAttributes } from '../common/map_saved_object_type';
|
||||
import { moveAttribution } from '../common/migrations/move_attribution';
|
||||
import { setEmsTmsDefaultModes } from '../common/migrations/set_ems_tms_default_modes';
|
||||
import { extractReferences } from '../common/migrations/references';
|
||||
|
||||
/*
|
||||
* Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard.
|
||||
|
@ -42,4 +43,17 @@ export const embeddableMigrations = {
|
|||
return state;
|
||||
}
|
||||
},
|
||||
'8.0.1': (state: SerializableRecord) => {
|
||||
try {
|
||||
const { attributes } = extractReferences(state as { attributes: MapSavedObjectAttributes });
|
||||
return {
|
||||
...state,
|
||||
attributes,
|
||||
} as SerializableRecord;
|
||||
} catch (e) {
|
||||
// Do not fail migration
|
||||
// Maps application can display error when viewed
|
||||
return state;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js';
|
|||
import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js';
|
||||
import { registerMapsUsageCollector } from './maps_telemetry/collectors/register';
|
||||
import { APP_ID, APP_ICON, MAP_SAVED_OBJECT_TYPE, getFullPath } from '../common/constants';
|
||||
import { extract, inject } from '../common/embeddable';
|
||||
import { mapSavedObjects, mapsTelemetrySavedObjects } from './saved_objects';
|
||||
import { MapsXPackConfig } from '../config';
|
||||
// @ts-ignore
|
||||
|
@ -225,6 +226,8 @@ export class MapsPlugin implements Plugin {
|
|||
plugins.embeddable.registerEmbeddableFactory({
|
||||
id: MAP_SAVED_OBJECT_TYPE,
|
||||
migrations: embeddableMigrations,
|
||||
inject,
|
||||
extract,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -90,7 +90,7 @@ export default function ({ getService }) {
|
|||
}
|
||||
expect(panels.length).to.be(1);
|
||||
expect(panels[0].type).to.be('map');
|
||||
expect(panels[0].version).to.be('8.0.0');
|
||||
expect(panels[0].version).to.be('8.0.1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue