mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[maps] fix map application is broken if you open a map with layers or sources that do not exist (#176419)
Fixes https://github.com/elastic/kibana/issues/176318
PR adds try/catch blocks around all usages of `createInstanceError`.
### Test steps
1) start kibana with `yarn start --run-examples`
2) create new map, add new layer `Weather data provided by NOAA`
3) save map
4) re-start kibana with `yarn start`
5) open map saved in step 3. Map should open and layer shoould display
error in legend
<img width="500" alt="Screenshot 2024-02-07 at 8 13 01 PM"
src="a70e7ee9
-fe30-499a-add5-8686fb591ce6">
---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2d15f36edf
commit
d404ea46e3
6 changed files with 164 additions and 56 deletions
89
x-pack/plugins/maps/public/classes/layers/invalid_layer.ts
Normal file
89
x-pack/plugins/maps/public/classes/layers/invalid_layer.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { LayerDescriptor } from '../../../common/descriptor_types';
|
||||
import { AbstractLayer } from './layer';
|
||||
import { AbstractSource } from '../sources/source';
|
||||
import { IStyle } from '../styles/style';
|
||||
|
||||
class InvalidSource extends AbstractSource {
|
||||
constructor(id?: string) {
|
||||
super({
|
||||
id,
|
||||
type: 'INVALID',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidLayer extends AbstractLayer {
|
||||
private readonly _error: Error;
|
||||
private readonly _style: IStyle;
|
||||
|
||||
constructor(layerDescriptor: LayerDescriptor, error: Error) {
|
||||
super({
|
||||
layerDescriptor,
|
||||
source: new InvalidSource(layerDescriptor.sourceDescriptor?.id),
|
||||
});
|
||||
this._error = error;
|
||||
this._style = {
|
||||
getType() {
|
||||
return 'INVALID';
|
||||
},
|
||||
renderEditor() {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getErrors() {
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.maps.invalidLayer.errorTitle', {
|
||||
defaultMessage: `Unable to create layer`,
|
||||
}),
|
||||
body: this._error.message,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getStyleForEditing() {
|
||||
return this._style;
|
||||
}
|
||||
|
||||
getStyle() {
|
||||
return this._style;
|
||||
}
|
||||
|
||||
getCurrentStyle() {
|
||||
return this._style;
|
||||
}
|
||||
|
||||
getMbLayerIds() {
|
||||
return [];
|
||||
}
|
||||
|
||||
ownsMbLayerId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ownsMbSourceId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
syncLayerWithMB() {}
|
||||
|
||||
getLayerTypeIconName() {
|
||||
return 'error';
|
||||
}
|
||||
}
|
|
@ -245,7 +245,7 @@ export class AbstractLayer implements ILayer {
|
|||
const sourceDisplayName = source
|
||||
? await source.getDisplayName()
|
||||
: await this.getSource().getDisplayName();
|
||||
return sourceDisplayName || `Layer ${this._descriptor.id}`;
|
||||
return sourceDisplayName || this._descriptor.id;
|
||||
}
|
||||
|
||||
async getAttributions(): Promise<Attribution[]> {
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
import { VectorStyle } from '../classes/styles/vector/vector_style';
|
||||
import { isLayerGroup, LayerGroup } from '../classes/layers/layer_group';
|
||||
import { HeatmapLayer } from '../classes/layers/heatmap_layer';
|
||||
import { InvalidLayer } from '../classes/layers/invalid_layer';
|
||||
import { getTimeFilter } from '../kibana_services';
|
||||
import { getChartsPaletteServiceGetColor } from '../reducers/non_serializable_instances';
|
||||
import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/copy_persistent_state';
|
||||
|
@ -76,54 +77,58 @@ export function createLayerInstance(
|
|||
customIcons: CustomIcon[],
|
||||
chartsPaletteServiceGetColor?: (value: string) => string | null
|
||||
): ILayer {
|
||||
if (layerDescriptor.type === LAYER_TYPE.LAYER_GROUP) {
|
||||
return new LayerGroup({ layerDescriptor: layerDescriptor as LayerGroupDescriptor });
|
||||
}
|
||||
try {
|
||||
if (layerDescriptor.type === LAYER_TYPE.LAYER_GROUP) {
|
||||
return new LayerGroup({ layerDescriptor: layerDescriptor as LayerGroupDescriptor });
|
||||
}
|
||||
|
||||
const source: ISource = createSourceInstance(layerDescriptor.sourceDescriptor);
|
||||
switch (layerDescriptor.type) {
|
||||
case LAYER_TYPE.RASTER_TILE:
|
||||
return new RasterTileLayer({ layerDescriptor, source: source as IRasterSource });
|
||||
case LAYER_TYPE.EMS_VECTOR_TILE:
|
||||
return new EmsVectorTileLayer({
|
||||
layerDescriptor: layerDescriptor as EMSVectorTileLayerDescriptor,
|
||||
source: source as EMSTMSSource,
|
||||
});
|
||||
case LAYER_TYPE.HEATMAP:
|
||||
return new HeatmapLayer({
|
||||
layerDescriptor: layerDescriptor as HeatmapLayerDescriptor,
|
||||
source: source as ESGeoGridSource,
|
||||
});
|
||||
case LAYER_TYPE.GEOJSON_VECTOR:
|
||||
return new GeoJsonVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
joins: createJoinInstances(
|
||||
layerDescriptor as VectorLayerDescriptor,
|
||||
source as IVectorSource
|
||||
),
|
||||
customIcons,
|
||||
chartsPaletteServiceGetColor,
|
||||
});
|
||||
case LAYER_TYPE.BLENDED_VECTOR:
|
||||
return new BlendedVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
customIcons,
|
||||
chartsPaletteServiceGetColor,
|
||||
});
|
||||
case LAYER_TYPE.MVT_VECTOR:
|
||||
return new MvtVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
joins: createJoinInstances(
|
||||
layerDescriptor as VectorLayerDescriptor,
|
||||
source as IVectorSource
|
||||
),
|
||||
customIcons,
|
||||
});
|
||||
default:
|
||||
throw new Error(`Unrecognized layerType ${layerDescriptor.type}`);
|
||||
const source: ISource = createSourceInstance(layerDescriptor.sourceDescriptor);
|
||||
switch (layerDescriptor.type) {
|
||||
case LAYER_TYPE.RASTER_TILE:
|
||||
return new RasterTileLayer({ layerDescriptor, source: source as IRasterSource });
|
||||
case LAYER_TYPE.EMS_VECTOR_TILE:
|
||||
return new EmsVectorTileLayer({
|
||||
layerDescriptor: layerDescriptor as EMSVectorTileLayerDescriptor,
|
||||
source: source as EMSTMSSource,
|
||||
});
|
||||
case LAYER_TYPE.HEATMAP:
|
||||
return new HeatmapLayer({
|
||||
layerDescriptor: layerDescriptor as HeatmapLayerDescriptor,
|
||||
source: source as ESGeoGridSource,
|
||||
});
|
||||
case LAYER_TYPE.GEOJSON_VECTOR:
|
||||
return new GeoJsonVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
joins: createJoinInstances(
|
||||
layerDescriptor as VectorLayerDescriptor,
|
||||
source as IVectorSource
|
||||
),
|
||||
customIcons,
|
||||
chartsPaletteServiceGetColor,
|
||||
});
|
||||
case LAYER_TYPE.BLENDED_VECTOR:
|
||||
return new BlendedVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
customIcons,
|
||||
chartsPaletteServiceGetColor,
|
||||
});
|
||||
case LAYER_TYPE.MVT_VECTOR:
|
||||
return new MvtVectorLayer({
|
||||
layerDescriptor: layerDescriptor as VectorLayerDescriptor,
|
||||
source: source as IVectorSource,
|
||||
joins: createJoinInstances(
|
||||
layerDescriptor as VectorLayerDescriptor,
|
||||
source as IVectorSource
|
||||
),
|
||||
customIcons,
|
||||
});
|
||||
default:
|
||||
throw new Error(`Unrecognized layerType ${layerDescriptor.type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
return new InvalidLayer(layerDescriptor, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
es_point_to_point: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
es_top_hits: { min: 1, max: 1, total: 2, avg: 0.07142857142857142 },
|
||||
es_agg_heatmap: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
esql: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
esql: { min: 1, max: 1, total: 2, avg: 0.07142857142857142 },
|
||||
kbn_tms_raster: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
ems_basemap: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
ems_region: { min: 1, max: 1, total: 1, avg: 0.03571428571428571 },
|
||||
|
@ -81,8 +81,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
min: 0,
|
||||
},
|
||||
dataSourcesCount: {
|
||||
avg: 1.1785714285714286,
|
||||
max: 6,
|
||||
avg: 1.2142857142857142,
|
||||
max: 7,
|
||||
min: 1,
|
||||
},
|
||||
emsVectorLayersCount: {
|
||||
|
@ -104,8 +104,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
min: 1,
|
||||
},
|
||||
GEOJSON_VECTOR: {
|
||||
avg: 0.8214285714285714,
|
||||
max: 5,
|
||||
avg: 0.8571428571428571,
|
||||
max: 6,
|
||||
min: 1,
|
||||
},
|
||||
HEATMAP: {
|
||||
|
@ -125,8 +125,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
},
|
||||
},
|
||||
layersCount: {
|
||||
avg: 1.2142857142857142,
|
||||
max: 7,
|
||||
avg: 1.25,
|
||||
max: 8,
|
||||
min: 1,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -18,6 +18,20 @@ export default function ({ getPageObjects, getService }) {
|
|||
await PageObjects.maps.loadSavedMap('layer with errors');
|
||||
});
|
||||
|
||||
describe('Layer with invalid descriptor', () => {
|
||||
const INVALID_LAYER_NAME = 'fff76ebb-57a6-4067-a373-1d191b9bd1a3';
|
||||
|
||||
it('should diplay error icon in legend', async () => {
|
||||
await PageObjects.maps.hasErrorIconExistsOrFail(INVALID_LAYER_NAME);
|
||||
});
|
||||
|
||||
it('should allow deletion of layer', async () => {
|
||||
await PageObjects.maps.removeLayer(INVALID_LAYER_NAME);
|
||||
const exists = await PageObjects.maps.doesLayerExist(INVALID_LAYER_NAME);
|
||||
expect(exists).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Layer with EsError', () => {
|
||||
after(async () => {
|
||||
await inspector.close();
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue