[Maps] Switch to SavedObjectClient.resolve (#112606)
* [Maps] Switch to SavedObjectClient.resolve * spaces tslint * aliasMatch * maps eslint * update lens to use spaces.ui.components.getSavedObjectConflictMessage * lens eslint * spaces eslint * review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
31515e90fd
commit
a9a923d5ee
|
@ -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<Document, 'savedObjectId' | 'type'>;
|
||||
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.deps.spaces.ui.components.getSavedObjectConflictMessage
|
||||
json={sharingSavedObjectProps.errorJSON!}
|
||||
/>
|
||||
),
|
||||
};
|
||||
this.errors = this.errors ? [...this.errors, conflictError] : [conflictError];
|
||||
}
|
||||
this.expression = ast ? toExpression(ast) : null;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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({
|
|||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const SavedObjectConflictMessage = ({ json }: { json: string }) => {
|
||||
const [expandError, setExpandError] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.lens.embeddable.legacyURLConflict.longMessage"
|
||||
defaultMessage="Disable the {documentationLink} associated with this object."
|
||||
values={{
|
||||
documentationLink: (
|
||||
<EuiLink
|
||||
external
|
||||
href="https://www.elastic.co/guide/en/kibana/master/legacy-url-aliases.html"
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate('xpack.lens.embeddable.legacyURLConflict.documentationLinkText', {
|
||||
defaultMessage: 'legacy URL alias',
|
||||
})}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{expandError ? (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.lens.embeddable.legacyURLConflict.expandErrorText', {
|
||||
defaultMessage: `This object has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`,
|
||||
values: { json },
|
||||
})}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
/>
|
||||
) : (
|
||||
<EuiButtonEmpty onClick={() => setExpandError(true)}>
|
||||
{i18n.translate('xpack.lens.embeddable.legacyURLConflict.expandError', {
|
||||
defaultMessage: `Show more`,
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const savedObjectConflictError = (json: string): ErrorMessage => ({
|
||||
shortMessage: i18n.translate('xpack.lens.embeddable.legacyURLConflict.shortMessage', {
|
||||
defaultMessage: `You've encountered a URL conflict`,
|
||||
}),
|
||||
longMessage: <SavedObjectConflictMessage json={json} />,
|
||||
});
|
||||
|
|
|
@ -212,6 +212,7 @@ export class LensPlugin {
|
|||
uiActions: plugins.uiActions,
|
||||
usageCollection,
|
||||
inspector: plugins.inspector,
|
||||
spaces: plugins.spaces,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
"savedObjectsTagging",
|
||||
"charts",
|
||||
"security",
|
||||
"spaces",
|
||||
"usageCollection"
|
||||
],
|
||||
"ui": true,
|
||||
|
|
|
@ -5,4 +5,12 @@
|
|||
flex: 1 1 100%;
|
||||
z-index: 1;
|
||||
min-height: 0; // Absolute must for Firefox to scroll contents
|
||||
}
|
||||
|
||||
.mapEmbeddedError {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: auto;
|
||||
}
|
|
@ -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,
|
||||
|
@ -66,6 +67,7 @@ import {
|
|||
getCoreI18n,
|
||||
getHttp,
|
||||
getChartsPaletteServiceGetColor,
|
||||
getSpacesApi,
|
||||
getSearchService,
|
||||
} from '../kibana_services';
|
||||
import { LayerDescriptor, MapExtent } from '../../common/descriptor_types';
|
||||
|
@ -353,23 +355,38 @@ export class MapEmbeddable
|
|||
return;
|
||||
}
|
||||
|
||||
const I18nContext = getCoreI18n().Context;
|
||||
const sharingSavedObjectProps = this._savedMap.getSharingSavedObjectProps();
|
||||
const spaces = getSpacesApi();
|
||||
const content =
|
||||
sharingSavedObjectProps && spaces && sharingSavedObjectProps?.outcome === 'conflict' ? (
|
||||
<div className="mapEmbeddedError">
|
||||
<EuiEmptyPrompt
|
||||
iconType="alert"
|
||||
iconColor="danger"
|
||||
data-test-subj="embeddable-maps-failure"
|
||||
body={spaces.ui.components.getSavedObjectConflictMessage({
|
||||
json: sharingSavedObjectProps.errorJSON!,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<MapContainer
|
||||
onSingleValueTrigger={this.onSingleValueTrigger}
|
||||
addFilters={this.input.hideFilterActions ? null : this.addFilters}
|
||||
getFilterActions={this.getFilterActions}
|
||||
getActionContext={this.getActionContext}
|
||||
renderTooltipContent={this._renderTooltipContent}
|
||||
title={this.getTitle()}
|
||||
description={this.getDescription()}
|
||||
waitUntilTimeLayersLoad$={waitUntilTimeLayersLoad$(this._savedMap.getStore())}
|
||||
isSharable={this._isSharable}
|
||||
/>
|
||||
);
|
||||
|
||||
const I18nContext = getCoreI18n().Context;
|
||||
render(
|
||||
<Provider store={this._savedMap.getStore()}>
|
||||
<I18nContext>
|
||||
<MapContainer
|
||||
onSingleValueTrigger={this.onSingleValueTrigger}
|
||||
addFilters={this.input.hideFilterActions ? null : this.addFilters}
|
||||
getFilterActions={this.getFilterActions}
|
||||
getActionContext={this.getActionContext}
|
||||
renderTooltipContent={this._renderTooltipContent}
|
||||
title={this.getTitle()}
|
||||
description={this.getDescription()}
|
||||
waitUntilTimeLayersLoad$={waitUntilTimeLayersLoad$(this._savedMap.getStore())}
|
||||
isSharable={this._isSharable}
|
||||
/>
|
||||
</I18nContext>
|
||||
<I18nContext>{content}</I18nContext>
|
||||
</Provider>,
|
||||
this._domNode
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<MapDoc, MapByValueInput, MapByReferenceInput>;
|
||||
|
||||
|
@ -58,7 +68,11 @@ export function getMapAttributeService(): MapAttributeService {
|
|||
return { id: savedObject.id };
|
||||
},
|
||||
unwrapMethod: async (savedObjectId: string): Promise<MapDoc> => {
|
||||
const savedObject = await getSavedObjectsClient().get<MapSavedObjectAttributes>(
|
||||
const {
|
||||
saved_object: savedObject,
|
||||
outcome,
|
||||
alias_target_id: aliasTargetId,
|
||||
} = await getSavedObjectsClient().resolve<MapSavedObjectAttributes>(
|
||||
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(
|
||||
|
|
|
@ -82,7 +82,8 @@ import {
|
|||
tileMapRenderer,
|
||||
tileMapVisType,
|
||||
} from './legacy_visualizations';
|
||||
import { SecurityPluginStart } from '../../security/public';
|
||||
import type { SecurityPluginStart } from '../../security/public';
|
||||
import type { 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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'}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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<Props, State> {
|
|||
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<Props, State> {
|
|||
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<Props, State> {
|
|||
{this._renderTopNav()}
|
||||
<h1 className="euiScreenReaderOnly">{`screenTitle placeholder`}</h1>
|
||||
<div id="react-maps-root">
|
||||
{this._renderLegacyUrlConflict()}
|
||||
<MapContainer
|
||||
addFilters={this._addFilter}
|
||||
title={this.props.savedMap.getAttributes().title}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { AppMountParameters } from 'kibana/public';
|
||||
import { EmbeddableStateTransfer } from 'src/plugins/embeddable/public';
|
||||
import type { AppMountParameters } from 'kibana/public';
|
||||
import type { EmbeddableStateTransfer } from 'src/plugins/embeddable/public';
|
||||
import { MapApp } from './map_app';
|
||||
import { SavedMap, getInitialLayersFromUrlParam } from './saved_map';
|
||||
import { MapEmbeddableInput } from '../../embeddable/types';
|
||||
|
@ -20,6 +20,7 @@ interface Props {
|
|||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
stateTransfer: EmbeddableStateTransfer;
|
||||
originatingApp?: string;
|
||||
history: AppMountParameters['history'];
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -69,6 +70,7 @@ export class MapPage extends Component<Props, State> {
|
|||
return (
|
||||
<Provider store={this.state.savedMap.getStore()}>
|
||||
<MapApp
|
||||
history={this.props.history}
|
||||
savedMap={this.state.savedMap}
|
||||
onAppLeave={this.props.onAppLeave}
|
||||
setHeaderActionMenu={this.props.setHeaderActionMenu}
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
setHiddenLayers,
|
||||
} from '../../../actions';
|
||||
import { getIsLayerTOCOpen, getOpenTOCDetails } from '../../../selectors/ui_selectors';
|
||||
import { getMapAttributeService } from '../../../map_attribute_service';
|
||||
import { getMapAttributeService, SharingSavedObjectProps } from '../../../map_attribute_service';
|
||||
import { OnSaveProps } from '../../../../../../../src/plugins/saved_objects/public';
|
||||
import { MapByReferenceInput, MapEmbeddableInput } from '../../../embeddable/types';
|
||||
import {
|
||||
|
@ -50,6 +50,7 @@ import { whenLicenseInitialized } from '../../../licensed_features';
|
|||
|
||||
export class SavedMap {
|
||||
private _attributes: MapSavedObjectAttributes | null = null;
|
||||
private _sharingSavedObjectProps: SharingSavedObjectProps | null = null;
|
||||
private readonly _defaultLayers: LayerDescriptor[];
|
||||
private readonly _embeddableId?: string;
|
||||
private _initialLayerListConfig: LayerDescriptor[] = [];
|
||||
|
@ -98,8 +99,11 @@ export class SavedMap {
|
|||
};
|
||||
} else {
|
||||
const doc = await getMapAttributeService().unwrapAttributes(this._mapEmbeddableInput);
|
||||
const { references, ...savedObjectAttributes } = doc;
|
||||
const { references, sharingSavedObjectProps, ...savedObjectAttributes } = doc;
|
||||
this._attributes = savedObjectAttributes;
|
||||
if (sharingSavedObjectProps) {
|
||||
this._sharingSavedObjectProps = sharingSavedObjectProps;
|
||||
}
|
||||
const savedObjectsTagging = getSavedObjectsTagging();
|
||||
if (savedObjectsTagging && references && references.length) {
|
||||
this._tags = savedObjectsTagging.ui.getTagIdsFromReferences(references);
|
||||
|
@ -274,6 +278,10 @@ export class SavedMap {
|
|||
return this._attributes;
|
||||
}
|
||||
|
||||
public getSharingSavedObjectProps(): SharingSavedObjectProps | null {
|
||||
return this._sharingSavedObjectProps;
|
||||
}
|
||||
|
||||
public isByValue(): boolean {
|
||||
const hasSavedObjectId = !!this.getSavedObjectId();
|
||||
return getIsAllowByValueEmbeddables() && !!this._originatingApp && !hasSavedObjectId;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
{ "path": "../licensing/tsconfig.json" },
|
||||
{ "path": "../file_upload/tsconfig.json" },
|
||||
{ "path": "../saved_objects_tagging/tsconfig.json" },
|
||||
{ "path": "../security/tsconfig.json" }
|
||||
{ "path": "../security/tsconfig.json" },
|
||||
{ "path": "../spaces/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ const createApiUiComponentsMock = () => {
|
|||
getSpaceList: jest.fn(),
|
||||
getLegacyUrlConflict: jest.fn(),
|
||||
getSpaceAvatar: jest.fn(),
|
||||
getSavedObjectConflictMessage: jest.fn(),
|
||||
};
|
||||
|
||||
return mock;
|
||||
|
|
|
@ -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<SavedObjectConflictMessageProps>
|
||||
> => {
|
||||
const { SavedObjectConflictMessage } = await import('./saved_object_conflict_message');
|
||||
return (props: SavedObjectConflictMessageProps) => {
|
||||
return <SavedObjectConflictMessage {...props} />;
|
||||
};
|
||||
};
|
|
@ -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';
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyURLConflict.longMessage"
|
||||
defaultMessage="Disable the {documentationLink} associated with this object."
|
||||
values={{
|
||||
documentationLink: (
|
||||
<EuiLink
|
||||
external
|
||||
href="https://www.elastic.co/guide/en/kibana/master/legacy-url-aliases.html"
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate('xpack.spaces.legacyURLConflict.documentationLinkText', {
|
||||
defaultMessage: 'legacy URL alias',
|
||||
})}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{expandError ? (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.spaces.legacyURLConflict.expandErrorText', {
|
||||
defaultMessage: `This object has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`,
|
||||
values: { json },
|
||||
})}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
/>
|
||||
) : (
|
||||
<EuiButtonEmpty onClick={() => setExpandError(true)}>
|
||||
{i18n.translate('xpack.spaces.legacyURLConflict.expandError', {
|
||||
defaultMessage: `Show more`,
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -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';
|
||||
|
|
|
@ -140,3 +140,10 @@ export interface ShareToSpaceSavedObjectTarget {
|
|||
*/
|
||||
noun?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the SavedObjectConflictMessage component.
|
||||
*/
|
||||
export interface SavedObjectConflictMessageProps {
|
||||
json: string;
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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<SpaceAvatarProps>;
|
||||
/**
|
||||
* Displays a saved object conflict message that directs user to disable legacy URL alias
|
||||
*/
|
||||
getSavedObjectConflictMessage: LazyComponentFn<SavedObjectConflictMessageProps>;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue