Skip to content

Commit 3ad79cd

Browse files
committed
clear out all attribute properties before updating
1 parent 8759646 commit 3ad79cd

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

x-pack/plugins/lens/public/persistence/saved_object_store.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
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';
98
import { Query, Filter } from '../../../../../src/plugins/data/public';
109

1110
export interface Document {
@@ -28,20 +27,6 @@ export interface Document {
2827

2928
export 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-
4530
export interface DocumentSaver {
4631
save: (vis: Document) => Promise<{ id: string }>;
4732
}
@@ -53,9 +38,9 @@ export interface DocumentLoader {
5338
export type SavedObjectStore = DocumentLoader & DocumentSaver;
5439

5540
export 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

Comments
 (0)