Skip to content
Merged
22 changes: 15 additions & 7 deletions x-pack/plugins/lens/public/embeddable/embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -108,6 +106,7 @@ export interface LensEmbeddableDeps {
getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions'];
capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean };
usageCollection?: UsageCollectionSetup;
spaces?: SpacesPluginStart;
}

export class Embeddable
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/lens/public/embeddable/embeddable_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,6 +39,7 @@ export interface LensEmbeddableStartServices {
documentToExpression: (
doc: Document
) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>;
spaces?: SpacesPluginStart;
}

export class EmbeddableFactory implements EmbeddableFactoryDefinition {
Expand Down Expand Up @@ -90,6 +92,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
capabilities,
usageCollection,
inspector,
spaces,
} = await this.getStartServices();

const { Embeddable } = await import('../async_services');
Expand All @@ -110,6 +113,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
canSaveVisualizations: Boolean(capabilities.visualize.save),
},
usageCollection,
spaces,
},
input,
parent
Expand Down
64 changes: 2 additions & 62 deletions x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -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} />,
});
1 change: 1 addition & 0 deletions x-pack/plugins/lens/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export class LensPlugin {
uiActions: plugins.uiActions,
usageCollection,
inspector: plugins.inspector,
spaces: plugins.spaces,
};
};

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"savedObjectsTagging",
"charts",
"security",
"spaces",
"usageCollection"
],
"ui": true,
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/maps/public/embeddable/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
45 changes: 31 additions & 14 deletions x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -66,6 +67,7 @@ import {
getCoreI18n,
getHttp,
getChartsPaletteServiceGetColor,
getSpacesApi,
getSearchService,
} from '../kibana_services';
import { LayerDescriptor, MapExtent } from '../../common/descriptor_types';
Expand Down Expand Up @@ -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
);
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/public/kibana_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
35 changes: 32 additions & 3 deletions x-pack/plugins/maps/public/map_attribute_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down Expand Up @@ -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
);
Expand All @@ -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(
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/maps/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,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']>;
Expand Down Expand Up @@ -113,6 +114,7 @@ export interface MapsPluginStartDependencies {
savedObjectsTagging?: SavedObjectTaggingPluginStart;
presentationUtil: PresentationUtilPluginStart;
security: SecurityPluginStart;
spaces?: SpacesPluginStart;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/maps/public/render_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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'}
/>
);
Expand Down
Loading