diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 263198871f07a..2e0ab2401c70f 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -7,6 +7,7 @@ import { isEqual, uniqBy } from 'lodash'; import React from 'react'; +import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; import type { ExecutionContextSearch, @@ -41,11 +42,7 @@ import { ReferenceOrValueEmbeddable, } from '../../../../../src/plugins/embeddable/public'; import { Document, injectFilterReferences } from '../persistence'; -import { - ExpressionWrapper, - ExpressionWrapperProps, - savedObjectConflictError, -} from './expression_wrapper'; +import { ExpressionWrapper, ExpressionWrapperProps } from './expression_wrapper'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { isLensBrushEvent, @@ -63,6 +60,7 @@ import { LensAttributeService } from '../lens_attribute_service'; import type { ErrorMessage } from '../editor_frame_service/types'; import { getLensInspectorService, LensInspector } from '../lens_inspector_service'; import { SharingSavedObjectProps } from '../types'; +import type { SpacesPluginStart } from '../../../spaces/public'; export type LensSavedObjectAttributes = Omit; export interface ResolvedLensSavedObjectAttributes extends LensSavedObjectAttributes { @@ -108,6 +106,7 @@ export interface LensEmbeddableDeps { getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions']; capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean }; usageCollection?: UsageCollectionSetup; + spaces?: SpacesPluginStart; } export class Embeddable @@ -281,8 +280,17 @@ export class Embeddable }; const { ast, errors } = await this.deps.documentToExpression(this.savedVis); this.errors = errors; - if (sharingSavedObjectProps?.outcome === 'conflict') { - const conflictError = savedObjectConflictError(sharingSavedObjectProps.errorJSON!); + if (sharingSavedObjectProps?.outcome === 'conflict' && this.deps.spaces) { + const conflictError = { + shortMessage: i18n.translate('xpack.lens.embeddable.legacyURLConflict.shortMessage', { + defaultMessage: `You've encountered a URL conflict`, + }), + longMessage: ( + + ), + }; this.errors = this.errors ? [...this.errors, conflictError] : [conflictError]; } this.expression = ast ? toExpression(ast) : null; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts index 954905c51a4b7..e51ec4c3e5588 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts @@ -24,6 +24,7 @@ import { LensAttributeService } from '../lens_attribute_service'; import { DOC_TYPE } from '../../common/constants'; import { ErrorMessage } from '../editor_frame_service/types'; import { extract, inject } from '../../common/embeddable_factory'; +import type { SpacesPluginStart } from '../../../spaces/public'; export interface LensEmbeddableStartServices { timefilter: TimefilterContract; @@ -38,6 +39,7 @@ export interface LensEmbeddableStartServices { documentToExpression: ( doc: Document ) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>; + spaces?: SpacesPluginStart; } export class EmbeddableFactory implements EmbeddableFactoryDefinition { @@ -90,6 +92,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { capabilities, usageCollection, inspector, + spaces, } = await this.getStartServices(); const { Embeddable } = await import('../async_services'); @@ -110,6 +113,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { canSaveVisualizations: Boolean(capabilities.visualize.save), }, usageCollection, + spaces, }, input, parent diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index c827fe74cc52b..3de914d13d69d 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -5,20 +5,10 @@ * 2.0. */ -import React, { useState } from 'react'; +import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiIcon, - EuiEmptyPrompt, - EuiButtonEmpty, - EuiCallOut, - EuiSpacer, - EuiLink, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon, EuiEmptyPrompt } from '@elastic/eui'; import { ExpressionRendererEvent, ReactExpressionRendererType, @@ -28,7 +18,6 @@ import type { KibanaExecutionContext } from 'src/core/public'; import { ExecutionContextSearch } from 'src/plugins/data/public'; import { DefaultInspectorAdapters, RenderMode } from 'src/plugins/expressions'; import classNames from 'classnames'; -import { i18n } from '@kbn/i18n'; import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper'; import { ErrorMessage } from '../editor_frame_service/types'; import { LensInspector } from '../lens_inspector_service'; @@ -172,52 +161,3 @@ export function ExpressionWrapper({ ); } - -const SavedObjectConflictMessage = ({ json }: { json: string }) => { - const [expandError, setExpandError] = useState(false); - return ( - <> - - {i18n.translate('xpack.lens.embeddable.legacyURLConflict.documentationLinkText', { - defaultMessage: 'legacy URL alias', - })} - - ), - }} - /> - - {expandError ? ( - - ) : ( - setExpandError(true)}> - {i18n.translate('xpack.lens.embeddable.legacyURLConflict.expandError', { - defaultMessage: `Show more`, - })} - - )} - - ); -}; - -export const savedObjectConflictError = (json: string): ErrorMessage => ({ - shortMessage: i18n.translate('xpack.lens.embeddable.legacyURLConflict.shortMessage', { - defaultMessage: `You've encountered a URL conflict`, - }), - longMessage: , -}); diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 7891b5990989c..1532b2b099104 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -212,6 +212,7 @@ export class LensPlugin { uiActions: plugins.uiActions, usageCollection, inspector: plugins.inspector, + spaces: plugins.spaces, }; }; diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index b55dbb1d49455..da410e35fe723 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -28,6 +28,7 @@ "savedObjectsTagging", "charts", "security", + "spaces", "usageCollection" ], "ui": true, diff --git a/x-pack/plugins/maps/public/_index.scss b/x-pack/plugins/maps/public/_index.scss index 01363209cfffd..a1ee0059420f7 100644 --- a/x-pack/plugins/maps/public/_index.scss +++ b/x-pack/plugins/maps/public/_index.scss @@ -13,4 +13,5 @@ @import 'connected_components/index'; @import 'components/index'; @import 'classes/index'; -@import 'animations'; \ No newline at end of file +@import 'animations'; +@import 'embeddable/index'; diff --git a/x-pack/plugins/maps/public/embeddable/_index.scss b/x-pack/plugins/maps/public/embeddable/_index.scss new file mode 100644 index 0000000000000..00b89bc6ad43e --- /dev/null +++ b/x-pack/plugins/maps/public/embeddable/_index.scss @@ -0,0 +1,7 @@ +.mapEmbeddedError { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + overflow: auto; +} diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 5a37e8ee8e9ba..aac0943c24361 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -12,6 +12,7 @@ import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; +import { EuiEmptyPrompt } from '@elastic/eui'; import { Embeddable, IContainer, @@ -65,6 +66,7 @@ import { getCoreI18n, getHttp, getChartsPaletteServiceGetColor, + getSpacesApi, getSearchService, } from '../kibana_services'; import { LayerDescriptor, MapExtent } from '../../common/descriptor_types'; @@ -338,22 +340,37 @@ export class MapEmbeddable return; } - const I18nContext = getCoreI18n().Context; + const sharingSavedObjectProps = this._savedMap.getSharingSavedObjectProps(); + const spaces = getSpacesApi(); + const content = + sharingSavedObjectProps && spaces && sharingSavedObjectProps?.outcome === 'conflict' ? ( +
+ +
+ ) : ( + + ); + const I18nContext = getCoreI18n().Context; render( - - - + {content} , this._domNode ); diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index c4340a051a07d..0156a699eca98 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -53,6 +53,7 @@ export const getNavigateToApp = () => coreStart.application.navigateToApp; export const getSavedObjectsTagging = () => pluginsStart.savedObjectsTagging; export const getPresentationUtilContext = () => pluginsStart.presentationUtil.ContextProvider; export const getSecurityService = () => pluginsStart.security; +export const getSpacesApi = () => pluginsStart.spaces; // xpack.maps.* kibana.yml settings from this plugin let mapAppConfig: MapsConfigType; diff --git a/x-pack/plugins/maps/public/map_attribute_service.ts b/x-pack/plugins/maps/public/map_attribute_service.ts index 5f7c45b1b42d7..ab380ca5a6b66 100644 --- a/x-pack/plugins/maps/public/map_attribute_service.ts +++ b/x-pack/plugins/maps/public/map_attribute_service.ts @@ -14,8 +14,18 @@ import { checkForDuplicateTitle, OnSaveProps } from '../../../../src/plugins/sav import { getCoreOverlays, getEmbeddableService, getSavedObjectsClient } from './kibana_services'; import { extractReferences, injectReferences } from '../common/migrations/references'; import { MapByValueInput, MapByReferenceInput } from './embeddable/types'; +import { getSpacesApi } from './kibana_services'; -type MapDoc = MapSavedObjectAttributes & { references?: SavedObjectReference[] }; +export interface SharingSavedObjectProps { + outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; + aliasTargetId?: string; + errorJSON?: string; +} + +type MapDoc = MapSavedObjectAttributes & { + sharingSavedObjectProps?: SharingSavedObjectProps; + references?: SavedObjectReference[]; +}; export type MapAttributeService = AttributeService; @@ -58,7 +68,11 @@ export function getMapAttributeService(): MapAttributeService { return { id: savedObject.id }; }, unwrapMethod: async (savedObjectId: string): Promise => { - const savedObject = await getSavedObjectsClient().get( + const { + saved_object: savedObject, + outcome, + alias_target_id: aliasTargetId, + } = await getSavedObjectsClient().resolve( MAP_SAVED_OBJECT_TYPE, savedObjectId ); @@ -68,7 +82,22 @@ export function getMapAttributeService(): MapAttributeService { } const { attributes } = injectReferences(savedObject); - return { ...attributes, references: savedObject.references }; + return { + ...attributes, + references: savedObject.references, + sharingSavedObjectProps: { + aliasTargetId, + outcome, + errorJSON: + outcome === 'conflict' && getSpacesApi() + ? JSON.stringify({ + targetType: MAP_SAVED_OBJECT_TYPE, + sourceId: savedObjectId, + targetSpace: (await getSpacesApi()!.getActiveSpace()).id, + }) + : undefined, + }, + }; }, checkForDuplicateTitle: (props: OnSaveProps) => { return checkForDuplicateTitle( diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index cef40f11b4443..7fa2ead1bbe72 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -74,7 +74,8 @@ import { MapsAppRegionMapLocatorDefinition, MapsAppTileMapLocatorDefinition, } from './locators'; -import { SecurityPluginStart } from '../../security/public'; +import type { SecurityPluginStart } from '../../security/public'; +import type { SpacesPluginStart } from '../../spaces/public'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -103,6 +104,7 @@ export interface MapsPluginStartDependencies { savedObjectsTagging?: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; security: SecurityPluginStart; + spaces?: SpacesPluginStart; } /** diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index de67939b1a42a..3eefaeb6f7a9b 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Router, Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { AppMountParameters } from 'kibana/public'; +import type { AppMountParameters } from 'kibana/public'; import { getCoreChrome, getCoreI18n, @@ -98,6 +98,7 @@ export async function renderApp( setHeaderActionMenu={setHeaderActionMenu} stateTransfer={stateTransfer} originatingApp={originatingApp} + history={history} key={routeProps.match.params.savedMapId ? routeProps.match.params.savedMapId : 'new'} /> ); diff --git a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx index 2a8e5ae8306d3..03ecfb736148f 100644 --- a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx @@ -18,6 +18,7 @@ import { getCoreChrome, getMapsCapabilities, getNavigation, + getSpacesApi, getTimeFilter, getToasts, } from '../../../kibana_services'; @@ -40,7 +41,8 @@ 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 { getEditPath, getFullPath, APP_ID } from '../../../../common/constants'; +import { getMapEmbeddableDisplayName } from '../../../../common/i18n_getters'; import { getInitialQuery, getInitialRefreshConfig, @@ -85,6 +87,7 @@ export interface Props { isSaveDisabled: boolean; query: Query | undefined; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; + history: AppMountParameters['history']; } export interface State { @@ -347,6 +350,16 @@ export class MapApp extends React.Component { return; } + const sharingSavedObjectProps = this.props.savedMap.getSharingSavedObjectProps(); + const spaces = getSpacesApi(); + if (spaces && sharingSavedObjectProps?.outcome === 'aliasMatch') { + // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash + const newObjectId = sharingSavedObjectProps?.aliasTargetId; // This is always defined if outcome === 'aliasMatch' + const newPath = `${getEditPath(newObjectId)}${this.props.history.location.hash}`; + await spaces.ui.redirectLegacyUrl(newPath, getMapEmbeddableDisplayName()); + return; + } + this.props.savedMap.setBreadcrumbs(); getCoreChrome().docTitle.change(this.props.savedMap.getTitle()); const savedObjectId = this.props.savedMap.getSavedObjectId(); @@ -437,6 +450,21 @@ export class MapApp extends React.Component { this._onFiltersChange([...this.props.filters, ...newFilters]); }; + _renderLegacyUrlConflict() { + const sharingSavedObjectProps = this.props.savedMap.getSharingSavedObjectProps(); + const spaces = getSpacesApi(); + return spaces && sharingSavedObjectProps?.outcome === 'conflict' + ? spaces.ui.components.getLegacyUrlConflict({ + objectNoun: getMapEmbeddableDisplayName(), + currentObjectId: this.props.savedMap.getSavedObjectId()!, + otherObjectId: sharingSavedObjectProps.aliasTargetId!, + otherObjectPath: `${getEditPath(sharingSavedObjectProps.aliasTargetId!)}${ + this.props.history.location.hash + }`, + }) + : null; + } + render() { if (!this.state.initialized) { return null; @@ -447,6 +475,7 @@ export class MapApp extends React.Component { {this._renderTopNav()}

{`screenTitle placeholder`}

+ {this._renderLegacyUrlConflict()} { return ( { getSpaceList: jest.fn(), getLegacyUrlConflict: jest.fn(), getSpaceAvatar: jest.fn(), + getSavedObjectConflictMessage: jest.fn(), }; return mock; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx new file mode 100644 index 0000000000000..66b2a5652057a --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx @@ -0,0 +1,19 @@ +/* + * 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 React from 'react'; + +import type { SavedObjectConflictMessageProps } from '../types'; + +export const getSavedObjectConflictMessage = async (): Promise< + React.FC +> => { + const { SavedObjectConflictMessage } = await import('./saved_object_conflict_message'); + return (props: SavedObjectConflictMessageProps) => { + return ; + }; +}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts index c0828e3b5331d..fa641d03fd715 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts @@ -6,4 +6,5 @@ */ export { getShareToSpaceFlyoutComponent } from './share_to_space_flyout'; +export { getSavedObjectConflictMessage } from './get_saved_object_conflict_message'; export { getLegacyUrlConflict } from './legacy_url_conflict'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx new file mode 100644 index 0000000000000..22a1ad7cd20aa --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx @@ -0,0 +1,56 @@ +/* + * 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 { EuiButtonEmpty, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; +import React, { useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { SavedObjectConflictMessageProps } from '../types'; + +export const SavedObjectConflictMessage = ({ json }: SavedObjectConflictMessageProps) => { + const [expandError, setExpandError] = useState(false); + return ( + <> + + {i18n.translate('xpack.spaces.legacyURLConflict.documentationLinkText', { + defaultMessage: 'legacy URL alias', + })} + + ), + }} + /> + + {expandError ? ( + + ) : ( + setExpandError(true)}> + {i18n.translate('xpack.spaces.legacyURLConflict.expandError', { + defaultMessage: `Show more`, + })} + + )} + + ); +}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts index fe90ee8d6a8a9..465fd179c8441 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts @@ -5,10 +5,15 @@ * 2.0. */ -export { getShareToSpaceFlyoutComponent, getLegacyUrlConflict } from './components'; +export { + getShareToSpaceFlyoutComponent, + getLegacyUrlConflict, + getSavedObjectConflictMessage, +} from './components'; export { createRedirectLegacyUrl } from './utils'; export type { LegacyUrlConflictProps, ShareToSpaceFlyoutProps, ShareToSpaceSavedObjectTarget, + SavedObjectConflictMessageProps, } from './types'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts index 1beccaa546282..21290f2b90de5 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts @@ -140,3 +140,10 @@ export interface ShareToSpaceSavedObjectTarget { */ noun?: string; } + +/** + * Properties for the SavedObjectConflictMessage component. + */ +export interface SavedObjectConflictMessageProps { + json: string; +} diff --git a/x-pack/plugins/spaces/public/ui_api/components.tsx b/x-pack/plugins/spaces/public/ui_api/components.tsx index a33480712ffae..71dc2e34a0d70 100644 --- a/x-pack/plugins/spaces/public/ui_api/components.tsx +++ b/x-pack/plugins/spaces/public/ui_api/components.tsx @@ -14,6 +14,7 @@ import { getCopyToSpaceFlyoutComponent } from '../copy_saved_objects_to_space'; import type { PluginsStart } from '../plugin'; import { getLegacyUrlConflict, + getSavedObjectConflictMessage, getShareToSpaceFlyoutComponent, } from '../share_saved_objects_to_space'; import { getSpaceAvatarComponent } from '../space_avatar'; @@ -56,5 +57,6 @@ export const getComponents = ({ getSpaceList: wrapLazy(getSpaceListComponent), getLegacyUrlConflict: wrapLazy(() => getLegacyUrlConflict({ getStartServices })), getSpaceAvatar: wrapLazy(getSpaceAvatarComponent), + getSavedObjectConflictMessage: wrapLazy(() => getSavedObjectConflictMessage()), }; }; diff --git a/x-pack/plugins/spaces/public/ui_api/types.ts b/x-pack/plugins/spaces/public/ui_api/types.ts index 5048e5a9b9652..67e43f0cd31a6 100644 --- a/x-pack/plugins/spaces/public/ui_api/types.ts +++ b/x-pack/plugins/spaces/public/ui_api/types.ts @@ -12,6 +12,7 @@ import type { CoreStart } from 'src/core/public'; import type { CopyToSpaceFlyoutProps } from '../copy_saved_objects_to_space'; import type { LegacyUrlConflictProps, + SavedObjectConflictMessageProps, ShareToSpaceFlyoutProps, } from '../share_saved_objects_to_space'; import type { SpaceAvatarProps } from '../space_avatar'; @@ -109,4 +110,8 @@ export interface SpacesApiUiComponent { * Displays an avatar for the given space. */ getSpaceAvatar: LazyComponentFn; + /** + * Displays a saved object conflict message that directs user to disable legacy URL alias + */ + getSavedObjectConflictMessage: LazyComponentFn; }