44 * you may not use this file except in compliance with the Elastic License.
55 */
66
7- import { SavedObjectAttributes } from 'kibana/server ' ;
7+ import { SavedObjectAttributes , SavedObjectsClientContract } from 'kibana/public ' ;
88import { Query , Filter } from '../../../../../src/plugins/data/public' ;
99
1010export interface Document {
@@ -27,20 +27,6 @@ export interface Document {
2727
2828export const DOC_TYPE = 'lens' ;
2929
30- interface SavedObjectClient {
31- create : ( type : string , object : SavedObjectAttributes ) => Promise < { id : string } > ;
32- update : ( type : string , id : string , object : SavedObjectAttributes ) => Promise < { id : string } > ;
33- get : (
34- type : string ,
35- id : string
36- ) => Promise < {
37- id : string ;
38- type : string ;
39- attributes : SavedObjectAttributes ;
40- error ?: { statusCode : number ; message : string } ;
41- } > ;
42- }
43-
4430export interface DocumentSaver {
4531 save : ( vis : Document ) => Promise < { id : string } > ;
4632}
@@ -52,9 +38,9 @@ export interface DocumentLoader {
5238export type SavedObjectStore = DocumentLoader & DocumentSaver ;
5339
5440export class SavedObjectIndexStore implements SavedObjectStore {
55- private client : SavedObjectClient ;
41+ private client : SavedObjectsClientContract ;
5642
57- constructor ( client : SavedObjectClient ) {
43+ constructor ( client : SavedObjectsClientContract ) {
5844 this . client = client ;
5945 }
6046
@@ -63,13 +49,33 @@ export class SavedObjectIndexStore implements SavedObjectStore {
6349 // TODO: SavedObjectAttributes should support this kind of object,
6450 // remove this workaround when SavedObjectAttributes is updated.
6551 const attributes = ( rest as unknown ) as SavedObjectAttributes ;
52+
6653 const result = await ( id
67- ? this . client . update ( DOC_TYPE , id , attributes )
54+ ? this . safeUpdate ( id , attributes )
6855 : this . client . create ( DOC_TYPE , attributes ) ) ;
6956
7057 return { ...vis , id : result . id } ;
7158 }
7259
60+ // As Lens is using an object to store its attributes, using the update API
61+ // will merge the new attribute object with the old one, not overwriting deleted
62+ // keys. As Lens is using objects as maps in various places, this is a problem because
63+ // deleted subtrees make it back into the object after a load.
64+ // This function fixes this by doing two updates - one to empty out the document setting
65+ // every key to null, and a second one to load the new content.
66+ private async safeUpdate ( id : string , attributes : SavedObjectAttributes ) {
67+ const resetAttributes : SavedObjectAttributes = { } ;
68+ Object . keys ( attributes ) . forEach ( ( key ) => {
69+ resetAttributes [ key ] = null ;
70+ } ) ;
71+ return (
72+ await this . client . bulkUpdate ( [
73+ { type : DOC_TYPE , id, attributes : resetAttributes } ,
74+ { type : DOC_TYPE , id, attributes } ,
75+ ] )
76+ ) . savedObjects [ 1 ] ;
77+ }
78+
7379 async load ( id : string ) : Promise < Document > {
7480 const { type, attributes, error } = await this . client . get ( DOC_TYPE , id ) ;
7581
@@ -78,7 +84,7 @@ export class SavedObjectIndexStore implements SavedObjectStore {
7884 }
7985
8086 return {
81- ...attributes ,
87+ ...( attributes as SavedObjectAttributes ) ,
8288 id,
8389 type,
8490 } as Document ;
0 commit comments