44 * you may not use this file except in compliance with the Elastic License.
55 */
66
7- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
8- import { SavedObjectAttributes } from 'kibana/server' ;
7+ import { SavedObjectAttributes , SavedObjectsClientContract } from 'kibana/public' ;
98import { Query , Filter } from '../../../../../src/plugins/data/public' ;
109
1110export interface Document {
@@ -28,20 +27,6 @@ export interface Document {
2827
2928export const DOC_TYPE = 'lens' ;
3029
31- interface SavedObjectClient {
32- create : ( type : string , object : SavedObjectAttributes ) => Promise < { id : string } > ;
33- update : ( type : string , id : string , object : SavedObjectAttributes ) => Promise < { id : string } > ;
34- get : (
35- type : string ,
36- id : string
37- ) => Promise < {
38- id : string ;
39- type : string ;
40- attributes : SavedObjectAttributes ;
41- error ?: { statusCode : number ; message : string } ;
42- } > ;
43- }
44-
4530export interface DocumentSaver {
4631 save : ( vis : Document ) => Promise < { id : string } > ;
4732}
@@ -53,9 +38,9 @@ export interface DocumentLoader {
5338export type SavedObjectStore = DocumentLoader & DocumentSaver ;
5439
5540export class SavedObjectIndexStore implements SavedObjectStore {
56- private client : SavedObjectClient ;
41+ private client : SavedObjectsClientContract ;
5742
58- constructor ( client : SavedObjectClient ) {
43+ constructor ( client : SavedObjectsClientContract ) {
5944 this . client = client ;
6045 }
6146
@@ -64,13 +49,33 @@ export class SavedObjectIndexStore implements SavedObjectStore {
6449 // TODO: SavedObjectAttributes should support this kind of object,
6550 // remove this workaround when SavedObjectAttributes is updated.
6651 const attributes = ( rest as unknown ) as SavedObjectAttributes ;
52+
6753 const result = await ( id
68- ? this . client . update ( DOC_TYPE , id , attributes )
54+ ? this . safeUpdate ( id , attributes )
6955 : this . client . create ( DOC_TYPE , attributes ) ) ;
7056
7157 return { ...vis , id : result . id } ;
7258 }
7359
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+
7479 async load ( id : string ) : Promise < Document > {
7580 const { type, attributes, error } = await this . client . get ( DOC_TYPE , id ) ;
7681
@@ -79,7 +84,7 @@ export class SavedObjectIndexStore implements SavedObjectStore {
7984 }
8085
8186 return {
82- ...attributes ,
87+ ...( attributes as SavedObjectAttributes ) ,
8388 id,
8489 type,
8590 } as Document ;
0 commit comments