-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Maps] Add handling for shareable saved objects #111195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
46c9dd5
9129fb4
21408a5
3f903ad
8fae482
4e01fc4
f13622b
1ef7e21
f65fda3
de9fc4c
a99dac2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,7 @@ import { | |
| tileMapVisType, | ||
| } from './legacy_visualizations'; | ||
| import { SecurityPluginStart } from '../../security/public'; | ||
| import { SpacesPluginStart } from '../../spaces/public'; | ||
|
|
||
| export interface MapsPluginSetupDependencies { | ||
| expressions: ReturnType<ExpressionsPublicPlugin['setup']>; | ||
|
|
@@ -112,6 +113,7 @@ export interface MapsPluginStartDependencies { | |
| savedObjectsTagging?: SavedObjectTaggingPluginStart; | ||
| presentationUtil: PresentationUtilPluginStart; | ||
| security: SecurityPluginStart; | ||
| spaces?: SpacesPluginStart; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -179,10 +181,13 @@ export class MapsPlugin | |
| euiIconType: APP_ICON_SOLUTION, | ||
| category: DEFAULT_APP_CATEGORIES.kibana, | ||
| async mount(params: AppMountParameters) { | ||
| const [coreStart, pluginsStart] = await core.getStartServices(); | ||
| const { http } = coreStart; | ||
| const { spaces: spacesApi } = pluginsStart as MapsPluginStartDependencies; | ||
| const UsageTracker = | ||
| plugins.usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; | ||
| const { renderApp } = await lazyLoadMapModules(); | ||
| return renderApp(params, UsageTracker); | ||
| return renderApp(params, UsageTracker, http, spacesApi); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure passing spaces into renderApp this way is needed. Spaces is a |
||
| }, | ||
| }); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /* | ||
| * 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 { SavedObjectReference } from 'src/core/types'; | ||
| import { ResolvedSimpleSavedObject } from 'kibana/public'; | ||
| import { AttributeService } from '../../../../src/plugins/embeddable/public'; | ||
| import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; | ||
| import { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; | ||
| import { getSavedObjectsClient } from './kibana_services'; | ||
| import { injectReferences } from '../common/migrations/references'; | ||
| import { MapByValueInput, MapByReferenceInput } from './embeddable/types'; | ||
|
|
||
| type MapDoc = MapSavedObjectAttributes & { | ||
| references?: SavedObjectReference[]; | ||
| }; | ||
|
|
||
| export type MapAttributeService = AttributeService<MapDoc, MapByValueInput, MapByReferenceInput>; | ||
|
|
||
| let resolveResult: ResolvedSimpleSavedObject<MapSavedObjectAttributes> | undefined; | ||
| export async function resolveSavedObject( | ||
| savedObjectId: string | ||
| ): Promise<{ | ||
| mapDoc: MapDoc; | ||
| resolvedSavedObject: ResolvedSimpleSavedObject<MapSavedObjectAttributes>; | ||
| }> { | ||
| if (!resolveResult || resolveResult.saved_object.id !== savedObjectId) { | ||
| resolveResult = await getSavedObjectsClient().resolve<MapSavedObjectAttributes>( | ||
| MAP_SAVED_OBJECT_TYPE, | ||
| savedObjectId | ||
| ); | ||
| } | ||
| const savedObject = resolveResult.saved_object; | ||
|
|
||
| if (savedObject.error) { | ||
| throw savedObject.error; | ||
| } | ||
|
|
||
| const { attributes } = injectReferences(savedObject); | ||
| return { | ||
| mapDoc: { ...attributes, references: savedObject.references }, | ||
| resolvedSavedObject: resolveResult, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,9 @@ | |
| import React from 'react'; | ||
| import _ from 'lodash'; | ||
| import { finalize, switchMap, tap } from 'rxjs/operators'; | ||
| import { EuiSpacer } from '@elastic/eui'; | ||
| import { i18n } from '@kbn/i18n'; | ||
| import { AppLeaveAction, AppMountParameters } from 'kibana/public'; | ||
| import { AppLeaveAction, AppMountParameters, HttpStart } from 'kibana/public'; | ||
| import { Adapters } from 'src/plugins/embeddable/public'; | ||
| import { Subscription } from 'rxjs'; | ||
| import type { Query, Filter, TimeRange, IndexPattern } from 'src/plugins/data/common'; | ||
|
|
@@ -40,7 +41,7 @@ import { getIndexPatternsFromIds } from '../../../index_pattern_util'; | |
| import { getTopNavConfig } from '../top_nav_config'; | ||
| import { goToSpecifiedPath } from '../../../render_app'; | ||
| import { MapSavedObjectAttributes } from '../../../../common/map_saved_object_type'; | ||
| import { getFullPath, APP_ID } from '../../../../common/constants'; | ||
| import { getFullPath, getEditPath, APP_ID } from '../../../../common/constants'; | ||
| import { | ||
| getInitialQuery, | ||
| getInitialRefreshConfig, | ||
|
|
@@ -50,13 +51,17 @@ import { | |
| unsavedChangesWarning, | ||
| } from '../saved_map'; | ||
| import { waitUntilTimeLayersLoad$ } from './wait_until_time_layers_load'; | ||
| import { SpacesPluginStart } from '../../../../../spaces/public'; | ||
| import { resolveSavedObject } from '../../../resolve_saved_object'; | ||
|
|
||
| interface MapRefreshConfig { | ||
| isPaused: boolean; | ||
| interval: number; | ||
| } | ||
|
|
||
| export interface Props { | ||
| spacesApi?: SpacesPluginStart; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Get spacesStartApi from kibana_services instead of passing in to be consistent with how other dependencies are provided |
||
| http: HttpStart; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. http does not need to be passed to MapApp, its never used. This dependency chain starts all the way in RenderApp. No need to pass http to RenderApp in public/plugin |
||
| savedMap: SavedMap; | ||
| // saveCounter used to trigger MapApp render after SaveMap.save | ||
| saveCounter: number; | ||
|
|
@@ -93,6 +98,7 @@ export interface State { | |
| savedQuery?: SavedQuery; | ||
| isRefreshPaused: boolean; | ||
| refreshInterval: number; | ||
| savedObjectWarning: unknown; | ||
| } | ||
|
|
||
| export class MapApp extends React.Component<Props, State> { | ||
|
|
@@ -111,6 +117,7 @@ export class MapApp extends React.Component<Props, State> { | |
| initialized: false, | ||
| isRefreshPaused: true, | ||
| refreshInterval: 0, | ||
| savedObjectWarning: null, | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -356,6 +363,7 @@ export class MapApp extends React.Component<Props, State> { | |
| this.props.savedMap.getTitle(), | ||
| savedObjectId | ||
| ); | ||
| await this.getLegacyUrlConflictCallout(savedObjectId); | ||
| } | ||
|
|
||
| this._initMapAndLayerSettings(this.props.savedMap.getAttributes()); | ||
|
|
@@ -430,6 +438,28 @@ export class MapApp extends React.Component<Props, State> { | |
| ); | ||
| } | ||
|
|
||
| async getLegacyUrlConflictCallout(savedObjectId: string) { | ||
| const { resolvedSavedObject } = await resolveSavedObject(savedObjectId); | ||
| if (this.props.spacesApi && resolvedSavedObject?.outcome === 'conflict') { | ||
| const currentObjectId = resolvedSavedObject.saved_object.id; | ||
| const otherObjectId = resolvedSavedObject.alias_target_id!; | ||
| const otherObjectPath = getEditPath(otherObjectId); | ||
| this.setState({ | ||
| savedObjectWarning: ( | ||
| <> | ||
| {this.props.spacesApi.ui.components.getLegacyUrlConflict({ | ||
| objectNoun: 'saved map', | ||
| currentObjectId, | ||
| otherObjectId, | ||
| otherObjectPath, | ||
| })} | ||
| <EuiSpacer /> | ||
| </> | ||
| ), | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| _addFilter = async (newFilters: Filter[]) => { | ||
| newFilters.forEach((filter) => { | ||
| filter.$state = { store: esFilters.FilterStateStore.APP_STATE }; | ||
|
|
@@ -447,6 +477,7 @@ export class MapApp extends React.Component<Props, State> { | |
| {this._renderTopNav()} | ||
| <h1 className="euiScreenReaderOnly">{`screenTitle placeholder`}</h1> | ||
| <div id="react-maps-root"> | ||
| {this.state.savedObjectWarning} | ||
| <MapContainer | ||
| addFilters={this._addFilter} | ||
| title={this.props.savedMap.getAttributes().title} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of handing saved object check seperatly in MapEmbeddable and MapApp, how about moving logic into a new SavedMap called something like "hasSavedObjectIdConflict"? That way all checking logic can just be part of SavedMap and consolidated into a single location.