diff --git a/examples/embeddable_examples/public/book/book_embeddable.tsx b/examples/embeddable_examples/public/book/book_embeddable.tsx index faf0cb8ab272c1..0f25d564e5580b 100644 --- a/examples/embeddable_examples/public/book/book_embeddable.tsx +++ b/examples/embeddable_examples/public/book/book_embeddable.tsx @@ -113,7 +113,7 @@ export class BookEmbeddable } public async reload() { - this.attributes = await this.attributeService.unwrapAttributes(this.input); + this.attributes = (await this.attributeService.unwrapAttributes(this.input)).attributes; this.updateOutput({ attributes: this.attributes, diff --git a/examples/embeddable_examples/public/book/book_embeddable_factory.tsx b/examples/embeddable_examples/public/book/book_embeddable_factory.tsx index 727e0fd18a52da..f44446991fde5f 100644 --- a/examples/embeddable_examples/public/book/book_embeddable_factory.tsx +++ b/examples/embeddable_examples/public/book/book_embeddable_factory.tsx @@ -115,11 +115,13 @@ export class BookEmbeddableFactoryDefinition }); } - private async unwrapMethod(savedObjectId: string): Promise { + private async unwrapMethod( + savedObjectId: string + ): Promise<{ attributes: BookSavedObjectAttributes }> { const { savedObjectsClient } = await this.getStartServices(); const savedObject: SimpleSavedObject = await savedObjectsClient.get(this.type, savedObjectId); - return { ...savedObject.attributes }; + return { attributes: { ...savedObject.attributes } }; } private async saveMethod(attributes: BookSavedObjectAttributes, savedObjectId?: string) { diff --git a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts index e718c98cb3626b..920dc0b9d5d80e 100644 --- a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts @@ -75,15 +75,12 @@ const panelsAreEqual = (panelsA: DashboardPanelMap, panelsB: DashboardPanelMap): } // embeddable ids are equal so let's compare individual panels. for (const id of embeddableIdsA) { - if ( - Object.keys( - commonDiff( - panelsA[id] as unknown as DashboardDiffCommon, - panelsB[id] as unknown as DashboardDiffCommon, - ['panelRefName'] - ) - ).length > 0 - ) { + const panelCommonDiff = commonDiff( + panelsA[id] as unknown as DashboardDiffCommon, + panelsB[id] as unknown as DashboardDiffCommon, + ['panelRefName'] + ); + if (Object.keys(panelCommonDiff).length > 0) { return false; } } diff --git a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx index 8229b84d15b546..859e9be9d29ddd 100644 --- a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx @@ -17,14 +17,15 @@ export const mockAttributeService = < V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A; }, - R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput + R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput, + M extends unknown = unknown >( type: string, - options: AttributeServiceOptions, + options: AttributeServiceOptions, customCore?: jest.Mocked -): AttributeService => { +): AttributeService => { const core = customCore ? customCore : coreMock.createStart(); - return new AttributeService( + return new AttributeService( type, jest.fn(), core.i18n.Context, diff --git a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts index ccca51b13f9efb..8b954b7271e410 100644 --- a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ATTRIBUTE_SERVICE_KEY } from './attribute_service'; +import { ATTRIBUTE_SERVICE_KEY, AttributeServiceUnwrapResult } from './attribute_service'; import { mockAttributeService } from './attribute_service.mock'; import { coreMock } from '../../../../../core/public/mocks'; import { OnSaveProps } from 'src/plugins/saved_objects/public/save_modal'; @@ -35,7 +35,10 @@ describe('attributeService', () => { return { id: '123' }; }); }; - const defaultUnwrapMethod = (savedObjectId: string): Promise => { + + const defaultUnwrapMethod = ( + savedObjectId: string + ): Promise> => { return new Promise(() => { return { ...attributes }; }); @@ -104,12 +107,14 @@ describe('attributeService', () => { saveMethod: defaultSaveMethod, checkForDuplicateTitle: jest.fn(), }); - expect(await attributeService.unwrapAttributes(byReferenceInput)).toEqual(byReferenceInput); + expect(await attributeService.unwrapAttributes(byReferenceInput)).toEqual({ + attributes: byReferenceInput, + }); }); it('returns attributes when when given value type input', async () => { const attributeService = mockAttributeService(defaultTestType, options); - expect(await attributeService.unwrapAttributes(byValueInput)).toEqual(attributes); + expect(await attributeService.unwrapAttributes(byValueInput)).toEqual({ attributes }); }); it('runs attributes through a custom unwrap method', async () => { @@ -118,16 +123,20 @@ describe('attributeService', () => { unwrapMethod: (savedObjectId) => { return new Promise((resolve) => { return resolve({ - ...attributes, - testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' }, + attributes: { + ...attributes, + testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' }, + }, }); }); }, checkForDuplicateTitle: jest.fn(), }); expect(await attributeService.unwrapAttributes(byReferenceInput)).toEqual({ - ...attributes, - testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' }, + attributes: { + ...attributes, + testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' }, + }, }); }); }); diff --git a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx index 9eb743a3911c2c..507d2be7198d5b 100644 --- a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx @@ -29,13 +29,28 @@ import { */ export const ATTRIBUTE_SERVICE_KEY = 'attributes'; -export interface AttributeServiceOptions { +export interface GenericAttributes { + title: string; +} +export interface AttributeServiceUnwrapResult< + SavedObjectAttributes extends GenericAttributes, + MetaInfo extends unknown = unknown +> { + attributes: SavedObjectAttributes; + metaInfo?: MetaInfo; +} +export interface AttributeServiceOptions< + SavedObjectAttributes extends GenericAttributes, + MetaInfo extends unknown = unknown +> { saveMethod: ( - attributes: A, + attributes: SavedObjectAttributes, savedObjectId?: string ) => Promise<{ id?: string } | { error: Error }>; checkForDuplicateTitle: (props: OnSaveProps) => Promise; - unwrapMethod?: (savedObjectId: string) => Promise; + unwrapMethod?: ( + savedObjectId: string + ) => Promise>; } export class AttributeService< @@ -43,7 +58,8 @@ export class AttributeService< ValType extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: SavedObjectAttributes; } = EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: SavedObjectAttributes }, - RefType extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput + RefType extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput, + MetaInfo extends unknown = unknown > { constructor( private type: string, @@ -53,7 +69,7 @@ export class AttributeService< ) => void, private i18nContext: I18nStart['Context'], private toasts: NotificationsStart['toasts'], - private options: AttributeServiceOptions, + private options: AttributeServiceOptions, getEmbeddableFactory?: (embeddableFactoryId: string) => EmbeddableFactory ) { if (getEmbeddableFactory) { @@ -64,20 +80,21 @@ export class AttributeService< } } - private async defaultUnwrapMethod(input: RefType): Promise { - return new Promise((resolve) => { - // @ts-ignore - return resolve({ ...input }); - }); + private async defaultUnwrapMethod( + input: RefType + ): Promise> { + return Promise.resolve({ attributes: { ...(input as unknown as SavedObjectAttributes) } }); } - public async unwrapAttributes(input: RefType | ValType): Promise { + public async unwrapAttributes( + input: RefType | ValType + ): Promise> { if (this.inputIsRefType(input)) { return this.options.unwrapMethod ? await this.options.unwrapMethod(input.savedObjectId) : await this.defaultUnwrapMethod(input); } - return input[ATTRIBUTE_SERVICE_KEY]; + return { attributes: input[ATTRIBUTE_SERVICE_KEY] }; } public async wrapAttributes( @@ -126,12 +143,12 @@ export class AttributeService< if (!this.inputIsRefType(input)) { return input; } - const attributes = await this.unwrapAttributes(input); + const { attributes } = await this.unwrapAttributes(input); + const { savedObjectId, ...originalInputToPropagate } = input; return { - ...input, - savedObjectId: undefined, + ...originalInputToPropagate, attributes, - }; + } as unknown as ValType; }; getInputAsRefType = async ( diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 7e393c6fc14e15..0f6f061aaad81e 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -90,11 +90,12 @@ export interface EmbeddableStart extends PersistableStateService( type: string, - options: AttributeServiceOptions - ) => AttributeService; + options: AttributeServiceOptions + ) => AttributeService; } export type EmbeddablePanelHOC = React.FC<{ embeddable: IEmbeddable; hideHeader?: boolean }>; diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index b55d424c0db484..b56e3bb2b59351 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -400,14 +400,18 @@ describe('Lens App', () => { savedObjectId: savedObjectId || 'aaa', })); services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ - sharingSavedObjectProps: { - outcome: 'exactMatch', + metaInfo: { + sharingSavedObjectProps: { + outcome: 'exactMatch', + }, }, - savedObjectId: initialSavedObjectId ?? 'aaa', - references: [], - state: { - query: 'fake query', - filters: [], + attributes: { + savedObjectId: initialSavedObjectId ?? 'aaa', + references: [], + state: { + query: 'fake query', + filters: [], + }, }, } as jest.ResolvedValue); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 4c247c031eac08..ef492a3dc43667 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -8,10 +8,11 @@ import { Embeddable, LensByValueInput, + LensUnwrapMetaInfo, + LensEmbeddableInput, LensByReferenceInput, LensSavedObjectAttributes, - LensEmbeddableInput, - ResolvedLensSavedObjectAttributes, + LensUnwrapResult, } from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; import { Query, TimeRange, Filter, IndexPatternsContract } from 'src/plugins/data/public'; @@ -50,9 +51,11 @@ const defaultSaveMethod = ( return { id: '123' }; }); }; -const defaultUnwrapMethod = (savedObjectId: string): Promise => { +const defaultUnwrapMethod = ( + savedObjectId: string +): Promise<{ attributes: LensSavedObjectAttributes }> => { return new Promise(() => { - return { ...savedVis }; + return { attributes: { ...savedVis } }; }); }; const defaultCheckForDuplicateTitle = (props: OnSaveProps): Promise => { @@ -69,17 +72,22 @@ const options = { const attributeServiceMockFromSavedVis = (document: Document): LensAttributeService => { const core = coreMock.createStart(); const service = new AttributeService< - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, - LensByReferenceInput + LensByReferenceInput, + LensUnwrapMetaInfo >('lens', jest.fn(), core.i18n.Context, core.notifications.toasts, options); service.unwrapAttributes = jest.fn((input: LensByValueInput | LensByReferenceInput) => { return Promise.resolve({ - ...document, - sharingSavedObjectProps: { - outcome: 'exactMatch', + attributes: { + ...document, + }, + metaInfo: { + sharingSavedObjectProps: { + outcome: 'exactMatch', + }, }, - } as ResolvedLensSavedObjectAttributes); + } as LensUnwrapResult); }); service.wrapAttributes = jest.fn(); return service; @@ -92,9 +100,10 @@ describe('embeddable', () => { let trigger: { exec: jest.Mock }; let basePath: IBasePath; let attributeService: AttributeService< - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, - LensByReferenceInput + LensByReferenceInput, + LensUnwrapMetaInfo >; beforeEach(() => { @@ -233,13 +242,17 @@ describe('embeddable', () => { attributeService.unwrapAttributes = jest.fn( (input: LensByValueInput | LensByReferenceInput) => { return Promise.resolve({ - ...savedVis, - sharingSavedObjectProps: { - outcome: 'conflict', - sourceId: '1', - aliasTargetId: '2', + attributes: { + ...savedVis, + }, + metaInfo: { + sharingSavedObjectProps: { + outcome: 'conflict', + sourceId: '1', + aliasTargetId: '2', + }, }, - } as ResolvedLensSavedObjectAttributes); + } as LensUnwrapResult); } ); const embeddable = new Embeddable( diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 7faf873cf0b0a9..ba7d82ce0df2e5 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -63,10 +63,16 @@ import { SharingSavedObjectProps } from '../types'; import type { SpacesPluginStart } from '../../../spaces/public'; export type LensSavedObjectAttributes = Omit; -export interface ResolvedLensSavedObjectAttributes extends LensSavedObjectAttributes { + +export interface LensUnwrapMetaInfo { sharingSavedObjectProps?: SharingSavedObjectProps; } +export interface LensUnwrapResult { + attributes: LensSavedObjectAttributes; + metaInfo?: LensUnwrapMetaInfo; +} + interface LensBaseEmbeddableInput extends EmbeddableInput { filters?: Filter[]; query?: Query; @@ -82,7 +88,7 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { } export type LensByValueInput = { - attributes: ResolvedLensSavedObjectAttributes; + attributes: LensSavedObjectAttributes; } & LensBaseEmbeddableInput; export type LensByReferenceInput = SavedObjectEmbeddableInput & LensBaseEmbeddableInput; @@ -260,18 +266,41 @@ export class Embeddable return this.lensInspector.adapters; } + private maybeAddConflictError( + errors?: ErrorMessage[], + sharingSavedObjectProps?: SharingSavedObjectProps + ) { + const ret = [...(errors || [])]; + + if (sharingSavedObjectProps?.outcome === 'conflict' && !!this.deps.spaces) { + ret.push({ + shortMessage: i18n.translate('xpack.lens.embeddable.legacyURLConflict.shortMessage', { + defaultMessage: `You've encountered a URL conflict`, + }), + longMessage: ( + + ), + }); + } + + return ret?.length ? ret : undefined; + } + async initializeSavedVis(input: LensEmbeddableInput) { - const attrs: ResolvedLensSavedObjectAttributes | false = await this.deps.attributeService + const unwrapResult: LensUnwrapResult | false = await this.deps.attributeService .unwrapAttributes(input) .catch((e: Error) => { this.onFatalError(e); return false; }); - if (!attrs || this.isDestroyed) { + if (!unwrapResult || this.isDestroyed) { return; } - const { sharingSavedObjectProps, ...attributes } = attrs; + const { metaInfo, attributes } = unwrapResult; this.savedVis = { ...attributes, @@ -279,21 +308,7 @@ export class Embeddable savedObjectId: (input as LensByReferenceInput)?.savedObjectId, }; const { ast, errors } = await this.deps.documentToExpression(this.savedVis); - this.errors = errors; - 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.errors = this.maybeAddConflictError(errors, metaInfo?.sharingSavedObjectProps); this.expression = ast ? toExpression(ast) : null; if (this.errors) { this.logError('validation'); diff --git a/x-pack/plugins/lens/public/lens_attribute_service.ts b/x-pack/plugins/lens/public/lens_attribute_service.ts index 30369b0fd586cf..80bdb8ce737b09 100644 --- a/x-pack/plugins/lens/public/lens_attribute_service.ts +++ b/x-pack/plugins/lens/public/lens_attribute_service.ts @@ -9,8 +9,10 @@ import type { CoreStart } from '../../../../src/core/public'; import type { LensPluginStartDependencies } from './plugin'; import type { AttributeService } from '../../../../src/plugins/embeddable/public'; import type { - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, + LensUnwrapMetaInfo, + LensUnwrapResult, LensByReferenceInput, } from './embeddable/embeddable'; import { SavedObjectIndexStore } from './persistence'; @@ -18,9 +20,10 @@ import { checkForDuplicateTitle, OnSaveProps } from '../../../../src/plugins/sav import { DOC_TYPE } from '../common/constants'; export type LensAttributeService = AttributeService< - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, - LensByReferenceInput + LensByReferenceInput, + LensUnwrapMetaInfo >; export function getLensAttributeService( @@ -29,20 +32,20 @@ export function getLensAttributeService( ): LensAttributeService { const savedObjectStore = new SavedObjectIndexStore(core.savedObjects.client); return startDependencies.embeddable.getAttributeService< - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, - LensByReferenceInput + LensByReferenceInput, + LensUnwrapMetaInfo >(DOC_TYPE, { - saveMethod: async (attributes: ResolvedLensSavedObjectAttributes, savedObjectId?: string) => { - const { sharingSavedObjectProps, ...attributesToSave } = attributes; + saveMethod: async (attributes: LensSavedObjectAttributes, savedObjectId?: string) => { const savedDoc = await savedObjectStore.save({ - ...attributesToSave, + ...attributes, savedObjectId, type: DOC_TYPE, }); return { id: savedDoc.savedObjectId }; }, - unwrapMethod: async (savedObjectId: string): Promise => { + unwrapMethod: async (savedObjectId: string): Promise => { const { saved_object: savedObject, outcome, @@ -61,8 +64,12 @@ export function getLensAttributeService( }; return { - sharingSavedObjectProps, - ...document, + attributes: { + ...document, + }, + metaInfo: { + sharingSavedObjectProps, + }, }; }, checkForDuplicateTitle: (props: OnSaveProps) => { diff --git a/x-pack/plugins/lens/public/mocks/services_mock.tsx b/x-pack/plugins/lens/public/mocks/services_mock.tsx index c6db0dfb6aae89..5ec4f8db4a0ed5 100644 --- a/x-pack/plugins/lens/public/mocks/services_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/services_mock.tsx @@ -18,7 +18,8 @@ import { dashboardPluginMock } from '../../../../../src/plugins/dashboard/public import type { LensByValueInput, LensByReferenceInput, - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, + LensUnwrapMetaInfo, } from '../embeddable/embeddable'; import { mockAttributeService, @@ -49,7 +50,9 @@ export const defaultDoc = { } as unknown as Document; export const exactMatchDoc = { - ...defaultDoc, + attributes: { + ...defaultDoc, + }, sharingSavedObjectProps: { outcome: 'exactMatch', }, @@ -83,9 +86,10 @@ export function makeDefaultServices( function makeAttributeService(): LensAttributeService { const attributeServiceMock = mockAttributeService< - ResolvedLensSavedObjectAttributes, + LensSavedObjectAttributes, LensByValueInput, - LensByReferenceInput + LensByReferenceInput, + LensUnwrapMetaInfo >( DOC_TYPE, { diff --git a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap index 0c92267382053c..efde7184ac7314 100644 --- a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap +++ b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap @@ -21,36 +21,38 @@ Object { "isSaveable": true, "persistedDoc": Object { "exactMatchDoc": Object { - "expression": "definitely a valid expression", - "references": Array [ - Object { - "id": "1", - "name": "index-pattern-0", - "type": "index-pattern", - }, - ], - "savedObjectId": "1234", - "sharingSavedObjectProps": Object { - "outcome": "exactMatch", - }, - "state": Object { - "datasourceStates": Object { - "testDatasource": "datasource", - }, - "filters": Array [ + "attributes": Object { + "expression": "definitely a valid expression", + "references": Array [ Object { - "query": Object { - "match_phrase": Object { - "src": "test", - }, - }, + "id": "1", + "name": "index-pattern-0", + "type": "index-pattern", }, ], - "query": "kuery", - "visualization": Object {}, + "savedObjectId": "1234", + "state": Object { + "datasourceStates": Object { + "testDatasource": "datasource", + }, + "filters": Array [ + Object { + "query": Object { + "match_phrase": Object { + "src": "test", + }, + }, + }, + ], + "query": "kuery", + "visualization": Object {}, + }, + "title": "An extremely cool default document!", + "visualizationType": "testVis", + }, + "sharingSavedObjectProps": Object { + "outcome": "exactMatch", }, - "title": "An extremely cool default document!", - "visualizationType": "testVis", }, "references": Array [], "savedObjectId": "1234", diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index 915c56d59dbb32..0944b4bb2cad47 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -45,7 +45,8 @@ export const getPersisted = async ({ }, }; } - const { sharingSavedObjectProps, ...attributes } = result; + const { metaInfo, attributes } = result; + const sharingSavedObjectProps = metaInfo?.sharingSavedObjectProps; if (spaces && sharingSavedObjectProps?.outcome === 'aliasMatch' && history) { // 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' diff --git a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx index ac27ca4398326c..1143bf8f7561e2 100644 --- a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx +++ b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx @@ -64,19 +64,21 @@ describe('Initializing the store', () => { const datasource2State = { datasource2: '' }; const services = makeDefaultServices(); services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ - exactMatchDoc, - visualizationType: 'testVis', - title: '', - state: { - datasourceStates: { - testDatasource: datasource1State, - testDatasource2: datasource2State, + attributes: { + exactMatchDoc, + visualizationType: 'testVis', + title: '', + state: { + datasourceStates: { + testDatasource: datasource1State, + testDatasource2: datasource2State, + }, + visualization: {}, + query: { query: '', language: 'lucene' }, + filters: [], }, - visualization: {}, - query: { query: '', language: 'lucene' }, - filters: [], + references: [], }, - references: [], }); const storeDeps = mockStoreDeps({ @@ -281,10 +283,14 @@ describe('Initializing the store', () => { it('redirects if saved object is an aliasMatch', async () => { const { store, deps } = makeLensStore({ preloadedState }); deps.lensServices.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ - ...defaultDoc, - sharingSavedObjectProps: { - outcome: 'aliasMatch', - aliasTargetId: 'id2', + attributes: { + ...defaultDoc, + }, + metaInfo: { + sharingSavedObjectProps: { + outcome: 'aliasMatch', + aliasTargetId: 'id2', + }, }, }); diff --git a/x-pack/plugins/maps/public/map_attribute_service.ts b/x-pack/plugins/maps/public/map_attribute_service.ts index 85d4d73da82cde..6f610d8a6a2ff0 100644 --- a/x-pack/plugins/maps/public/map_attribute_service.ts +++ b/x-pack/plugins/maps/public/map_attribute_service.ts @@ -22,11 +22,18 @@ export interface SharingSavedObjectProps { } type MapDoc = MapSavedObjectAttributes & { - sharingSavedObjectProps?: SharingSavedObjectProps; references?: SavedObjectReference[]; }; +export interface MapUnwrapMetaInfo { + sharingSavedObjectProps: SharingSavedObjectProps; +} -export type MapAttributeService = AttributeService; +export type MapAttributeService = AttributeService< + MapDoc, + MapByValueInput, + MapByReferenceInput, + MapUnwrapMetaInfo +>; let mapAttributeService: MapAttributeService | null = null; @@ -38,7 +45,8 @@ export function getMapAttributeService(): MapAttributeService { mapAttributeService = getEmbeddableService().getAttributeService< MapDoc, MapByValueInput, - MapByReferenceInput + MapByReferenceInput, + MapUnwrapMetaInfo >(MAP_SAVED_OBJECT_TYPE, { saveMethod: async (attributes: MapDoc, savedObjectId?: string) => { // AttributeService "attributes" contains "references" as a child. @@ -66,7 +74,12 @@ export function getMapAttributeService(): MapAttributeService { )); return { id: savedObject.id }; }, - unwrapMethod: async (savedObjectId: string): Promise => { + unwrapMethod: async ( + savedObjectId: string + ): Promise<{ + attributes: MapDoc; + metaInfo: MapUnwrapMetaInfo; + }> => { const { saved_object: savedObject, outcome, @@ -82,12 +95,16 @@ export function getMapAttributeService(): MapAttributeService { const { attributes } = injectReferences(savedObject); return { - ...attributes, - references: savedObject.references, - sharingSavedObjectProps: { - aliasTargetId, - outcome, - sourceId: savedObjectId, + attributes: { + ...attributes, + references: savedObject.references, + }, + metaInfo: { + sharingSavedObjectProps: { + aliasTargetId, + outcome, + sourceId: savedObjectId, + }, }, }; }, diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 004b88a2426237..3cc891c8bb5074 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -98,11 +98,13 @@ export class SavedMap { description: '', }; } else { - const doc = await getMapAttributeService().unwrapAttributes(this._mapEmbeddableInput); - const { references, sharingSavedObjectProps, ...savedObjectAttributes } = doc; + const { attributes: doc, metaInfo } = await getMapAttributeService().unwrapAttributes( + this._mapEmbeddableInput + ); + const { references, ...savedObjectAttributes } = doc; this._attributes = savedObjectAttributes; - if (sharingSavedObjectProps) { - this._sharingSavedObjectProps = sharingSavedObjectProps; + if (metaInfo?.sharingSavedObjectProps) { + this._sharingSavedObjectProps = metaInfo.sharingSavedObjectProps; } const savedObjectsTagging = getSavedObjectsTagging(); if (savedObjectsTagging && references && references.length) {