Skip to content

Commit 46c9dd5

Browse files
author
Aaron Caldwell
committed
Add handling for saved map loading. First pass, functionality to be verified
1 parent 45b1334 commit 46c9dd5

File tree

9 files changed

+109
-21
lines changed

9 files changed

+109
-21
lines changed

x-pack/plugins/maps/kibana.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"savedObjectsTagging",
3030
"charts",
3131
"security",
32-
"usageCollection"
32+
"usageCollection",
33+
"spaces"
3334
],
3435
"ui": true,
3536
"server": true,

x-pack/plugins/maps/public/embeddable/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface MapEmbeddableConfig {
2222
}
2323

2424
interface MapEmbeddableState {
25+
savedObjectId?: string;
2526
isLayerTOCOpen?: boolean;
2627
openTOCDetails?: string[];
2728
mapCenter?: MapCenterAndZoom;

x-pack/plugins/maps/public/lazy_load_bundle/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
99
import { IndexPatternsContract } from 'src/plugins/data/public/index_patterns';
10-
import { AppMountParameters } from 'kibana/public';
10+
import { AppMountParameters, HttpStart } from 'kibana/public';
1111
import { IContainer } from '../../../../../src/plugins/embeddable/public';
1212
import { LayerDescriptor } from '../../common/descriptor_types';
1313
import type {
@@ -21,6 +21,7 @@ import type { CreateLayerDescriptorParams } from '../classes/sources/es_search_s
2121
import type { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest';
2222
import type { CreateTileMapLayerDescriptorParams } from '../classes/layers/create_tile_map_layer_descriptor';
2323
import type { CreateRegionMapLayerDescriptorParams } from '../classes/layers/create_region_map_layer_descriptor';
24+
import { SpacesPluginStart } from '../../../spaces/public';
2425

2526
let loadModulesPromise: Promise<LazyLoadedMapModules>;
2627

@@ -32,7 +33,12 @@ export interface LazyLoadedMapModules {
3233
) => MapEmbeddableType;
3334
getIndexPatternService: () => IndexPatternsContract;
3435
getMapsCapabilities: () => any;
35-
renderApp: (params: AppMountParameters, AppUsageTracker: React.FC) => Promise<() => void>;
36+
renderApp: (
37+
params: AppMountParameters,
38+
AppUsageTracker: React.FC,
39+
http: HttpStart,
40+
spacesApi?: SpacesPluginStart
41+
) => Promise<() => void>;
3642
createSecurityLayerDescriptors: (
3743
indexPatternId: string,
3844
indexPatternTitle: string

x-pack/plugins/maps/public/map_attribute_service.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { SavedObjectReference } from 'src/core/types';
9+
import { ResolvedSimpleSavedObject } from 'kibana/public';
910
import { AttributeService } from '../../../../src/plugins/embeddable/public';
1011
import { MapSavedObjectAttributes } from '../common/map_saved_object_type';
1112
import { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
@@ -15,11 +16,38 @@ import { getCoreOverlays, getEmbeddableService, getSavedObjectsClient } from './
1516
import { extractReferences, injectReferences } from '../common/migrations/references';
1617
import { MapByValueInput, MapByReferenceInput } from './embeddable/types';
1718

18-
type MapDoc = MapSavedObjectAttributes & { references?: SavedObjectReference[] };
19+
type MapDoc = MapSavedObjectAttributes & {
20+
references?: SavedObjectReference[];
21+
};
1922

2023
export type MapAttributeService = AttributeService<MapDoc, MapByValueInput, MapByReferenceInput>;
2124

2225
let mapAttributeService: MapAttributeService | null = null;
26+
let resolveResult: ResolvedSimpleSavedObject<MapSavedObjectAttributes> | undefined;
27+
export async function resolveSavedObject(
28+
savedObjectId: string
29+
): Promise<{
30+
mapDoc: MapDoc;
31+
resolvedSavedObject: ResolvedSimpleSavedObject<MapSavedObjectAttributes>;
32+
}> {
33+
if (!resolveResult || resolveResult.saved_object.id !== savedObjectId) {
34+
resolveResult = await getSavedObjectsClient().resolve<MapSavedObjectAttributes>(
35+
MAP_SAVED_OBJECT_TYPE,
36+
savedObjectId
37+
);
38+
}
39+
const savedObject = resolveResult.saved_object;
40+
41+
if (savedObject.error) {
42+
throw savedObject.error;
43+
}
44+
45+
const { attributes } = injectReferences(savedObject);
46+
return {
47+
mapDoc: { ...attributes, references: savedObject.references },
48+
resolvedSavedObject: resolveResult,
49+
};
50+
}
2351

2452
export function getMapAttributeService(): MapAttributeService {
2553
if (mapAttributeService) {
@@ -58,17 +86,8 @@ export function getMapAttributeService(): MapAttributeService {
5886
return { id: savedObject.id };
5987
},
6088
unwrapMethod: async (savedObjectId: string): Promise<MapDoc> => {
61-
const savedObject = await getSavedObjectsClient().get<MapSavedObjectAttributes>(
62-
MAP_SAVED_OBJECT_TYPE,
63-
savedObjectId
64-
);
65-
66-
if (savedObject.error) {
67-
throw savedObject.error;
68-
}
69-
70-
const { attributes } = injectReferences(savedObject);
71-
return { ...attributes, references: savedObject.references };
89+
const { mapDoc } = await resolveSavedObject(savedObjectId);
90+
return mapDoc;
7291
},
7392
checkForDuplicateTitle: (props: OnSaveProps) => {
7493
return checkForDuplicateTitle(

x-pack/plugins/maps/public/plugin.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import {
8383
tileMapVisType,
8484
} from './legacy_visualizations';
8585
import { SecurityPluginStart } from '../../security/public';
86+
import { SpacesPluginStart } from '../../spaces/public';
8687

8788
export interface MapsPluginSetupDependencies {
8889
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
@@ -112,6 +113,7 @@ export interface MapsPluginStartDependencies {
112113
savedObjectsTagging?: SavedObjectTaggingPluginStart;
113114
presentationUtil: PresentationUtilPluginStart;
114115
security: SecurityPluginStart;
116+
spaces?: SpacesPluginStart;
115117
}
116118

117119
/**
@@ -179,10 +181,13 @@ export class MapsPlugin
179181
euiIconType: APP_ICON_SOLUTION,
180182
category: DEFAULT_APP_CATEGORIES.kibana,
181183
async mount(params: AppMountParameters) {
184+
const [coreStart, pluginsStart] = await core.getStartServices();
185+
const { http } = coreStart;
186+
const { spaces: spacesApi } = pluginsStart as MapsPluginStartDependencies;
182187
const UsageTracker =
183188
plugins.usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment;
184189
const { renderApp } = await lazyLoadMapModules();
185-
return renderApp(params, UsageTracker);
190+
return renderApp(params, UsageTracker, http, spacesApi);
186191
},
187192
});
188193

x-pack/plugins/maps/public/render_app.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React from 'react';
99
import { render, unmountComponentAtNode } from 'react-dom';
1010
import { Router, Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom';
1111
import { i18n } from '@kbn/i18n';
12-
import { AppMountParameters } from 'kibana/public';
12+
import { AppMountParameters, HttpStart } from 'kibana/public';
1313
import {
1414
getCoreChrome,
1515
getCoreI18n,
@@ -26,6 +26,7 @@ import {
2626
import { ListPage, MapPage } from './routes';
2727
import { MapByValueInput, MapByReferenceInput } from './embeddable/types';
2828
import { APP_ID } from '../common/constants';
29+
import { SpacesPluginStart } from '../../spaces/public';
2930

3031
export let goToSpecifiedPath: (path: string) => void;
3132
export let kbnUrlStateStorage: IKbnUrlStateStorage;
@@ -63,7 +64,9 @@ function setAppChrome() {
6364

6465
export async function renderApp(
6566
{ element, history, onAppLeave, setHeaderActionMenu }: AppMountParameters,
66-
AppUsageTracker: React.FC
67+
AppUsageTracker: React.FC,
68+
http: HttpStart,
69+
spacesApi?: SpacesPluginStart
6770
) {
6871
goToSpecifiedPath = (path) => history.push(path);
6972
kbnUrlStateStorage = createKbnUrlStateStorage({
@@ -98,6 +101,8 @@ export async function renderApp(
98101
setHeaderActionMenu={setHeaderActionMenu}
99102
stateTransfer={stateTransfer}
100103
originatingApp={originatingApp}
104+
spacesApi={spacesApi}
105+
http={http}
101106
/>
102107
);
103108
}

x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
import React from 'react';
99
import _ from 'lodash';
1010
import { finalize, switchMap, tap } from 'rxjs/operators';
11+
import { EuiSpacer } from '@elastic/eui';
1112
import { i18n } from '@kbn/i18n';
12-
import { AppLeaveAction, AppMountParameters } from 'kibana/public';
13+
import { AppLeaveAction, AppMountParameters, HttpStart } from 'kibana/public';
1314
import { Adapters } from 'src/plugins/embeddable/public';
1415
import { Subscription } from 'rxjs';
1516
import {
@@ -54,13 +55,17 @@ import {
5455
unsavedChangesWarning,
5556
} from '../saved_map';
5657
import { waitUntilTimeLayersLoad$ } from './wait_until_time_layers_load';
58+
import { SpacesPluginStart } from '../../../../../spaces/public';
59+
import { resolveSavedObject } from '../../../map_attribute_service';
5760

5861
interface MapRefreshConfig {
5962
isPaused: boolean;
6063
interval: number;
6164
}
6265

6366
export interface Props {
67+
spacesApi?: SpacesPluginStart;
68+
http: HttpStart;
6469
savedMap: SavedMap;
6570
// saveCounter used to trigger MapApp render after SaveMap.save
6671
saveCounter: number;
@@ -97,6 +102,7 @@ export interface State {
97102
savedQuery?: SavedQuery;
98103
isRefreshPaused: boolean;
99104
refreshInterval: number;
105+
savedObjectWarning: unknown;
100106
}
101107

102108
export class MapApp extends React.Component<Props, State> {
@@ -115,6 +121,7 @@ export class MapApp extends React.Component<Props, State> {
115121
initialized: false,
116122
isRefreshPaused: true,
117123
refreshInterval: 0,
124+
savedObjectWarning: null,
118125
};
119126
}
120127

@@ -363,6 +370,7 @@ export class MapApp extends React.Component<Props, State> {
363370
}
364371

365372
this._initMapAndLayerSettings(this.props.savedMap.getAttributes());
373+
await this.getLegacyUrlConflictCallout(savedObjectId);
366374

367375
this.setState({ initialized: true });
368376
}
@@ -434,6 +442,41 @@ export class MapApp extends React.Component<Props, State> {
434442
);
435443
}
436444

445+
async getLegacyUrlConflictCallout(savedObjectId: string | undefined) {
446+
if (!savedObjectId) {
447+
return;
448+
}
449+
await this.props.savedMap.whenReady();
450+
const { resolvedSavedObject } = await resolveSavedObject(savedObjectId);
451+
// This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario
452+
if (this.props.spacesApi && resolvedSavedObject) {
453+
if (resolvedSavedObject.outcome === 'conflict') {
454+
// We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a
455+
// callout with a warning for the user, and provide a way for them to navigate to the other object.
456+
const currentObjectId = resolvedSavedObject.saved_object.id;
457+
const otherObjectId = resolvedSavedObject.alias_target_id!; // This is always defined if outcome === 'conflict'
458+
// const otherObjectPath = this.props.http.basePath.prepend(
459+
// `${VIEW_NOTE_PATH}/${otherObjectId}${window.location.hash}`
460+
// );
461+
const otherObjectPath = window.location.href.replace(currentObjectId, otherObjectId);
462+
console.log(this.props.http.basePath);
463+
this.setState({
464+
savedObjectWarning: (
465+
<>
466+
{this.props.spacesApi.ui.components.getLegacyUrlConflict({
467+
objectNoun: 'Saved map',
468+
currentObjectId,
469+
otherObjectId,
470+
otherObjectPath,
471+
})}
472+
<EuiSpacer />
473+
</>
474+
),
475+
});
476+
}
477+
}
478+
}
479+
437480
_addFilter = async (newFilters: Filter[]) => {
438481
newFilters.forEach((filter) => {
439482
filter.$state = { store: esFilters.FilterStateStore.APP_STATE };
@@ -451,6 +494,7 @@ export class MapApp extends React.Component<Props, State> {
451494
{this._renderTopNav()}
452495
<h1 className="euiScreenReaderOnly">{`screenTitle placeholder`}</h1>
453496
<div id="react-maps-root">
497+
{this.state.savedObjectWarning}
454498
<MapContainer
455499
addFilters={this._addFilter}
456500
title={this.props.savedMap.getAttributes().title}

x-pack/plugins/maps/public/routes/map_page/map_page.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
import React, { Component } from 'react';
99
import { Provider } from 'react-redux';
10-
import { AppMountParameters } from 'kibana/public';
10+
import { AppMountParameters, HttpStart } from 'kibana/public';
1111
import { EmbeddableStateTransfer } from 'src/plugins/embeddable/public';
1212
import { MapApp } from './map_app';
1313
import { SavedMap, getInitialLayersFromUrlParam } from './saved_map';
1414
import { MapEmbeddableInput } from '../../embeddable/types';
15+
import { SpacesPluginStart } from '../../../../spaces/public';
16+
import { resolveSavedObject } from '../../map_attribute_service';
1517

1618
interface Props {
1719
mapEmbeddableInput?: MapEmbeddableInput;
@@ -20,6 +22,8 @@ interface Props {
2022
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
2123
stateTransfer: EmbeddableStateTransfer;
2224
originatingApp?: string;
25+
spacesApi?: SpacesPluginStart;
26+
http: HttpStart;
2327
}
2428

2529
interface State {
@@ -69,6 +73,8 @@ export class MapPage extends Component<Props, State> {
6973
return (
7074
<Provider store={this.state.savedMap.getStore()}>
7175
<MapApp
76+
spacesApi={this.props.spacesApi}
77+
http={this.props.http}
7278
savedMap={this.state.savedMap}
7379
onAppLeave={this.props.onAppLeave}
7480
setHeaderActionMenu={this.props.setHeaderActionMenu}

x-pack/plugins/maps/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
{ "path": "../licensing/tsconfig.json" },
3737
{ "path": "../file_upload/tsconfig.json" },
3838
{ "path": "../saved_objects_tagging/tsconfig.json" },
39-
{ "path": "../security/tsconfig.json" }
39+
{ "path": "../security/tsconfig.json" },
40+
{ "path": "../spaces/tsconfig.json" }
4041
]
4142
}

0 commit comments

Comments
 (0)