Skip to content

Commit

Permalink
clear out all attribute properties before updating
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 committed Aug 6, 2020
1 parent 8759646 commit 3ad79cd
Showing 1 changed file with 25 additions and 20 deletions.
45 changes: 25 additions & 20 deletions x-pack/plugins/lens/public/persistence/saved_object_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { SavedObjectAttributes } from 'kibana/server';
import { SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/public';
import { Query, Filter } from '../../../../../src/plugins/data/public';

export interface Document {
Expand All @@ -28,20 +27,6 @@ export interface Document {

export const DOC_TYPE = 'lens';

interface SavedObjectClient {
create: (type: string, object: SavedObjectAttributes) => Promise<{ id: string }>;
update: (type: string, id: string, object: SavedObjectAttributes) => Promise<{ id: string }>;
get: (
type: string,
id: string
) => Promise<{
id: string;
type: string;
attributes: SavedObjectAttributes;
error?: { statusCode: number; message: string };
}>;
}

export interface DocumentSaver {
save: (vis: Document) => Promise<{ id: string }>;
}
Expand All @@ -53,9 +38,9 @@ export interface DocumentLoader {
export type SavedObjectStore = DocumentLoader & DocumentSaver;

export class SavedObjectIndexStore implements SavedObjectStore {
private client: SavedObjectClient;
private client: SavedObjectsClientContract;

constructor(client: SavedObjectClient) {
constructor(client: SavedObjectsClientContract) {
this.client = client;
}

Expand All @@ -64,13 +49,33 @@ export class SavedObjectIndexStore implements SavedObjectStore {
// TODO: SavedObjectAttributes should support this kind of object,
// remove this workaround when SavedObjectAttributes is updated.
const attributes = (rest as unknown) as SavedObjectAttributes;

const result = await (id
? this.client.update(DOC_TYPE, id, attributes)
? this.safeUpdate(id, attributes)
: this.client.create(DOC_TYPE, attributes));

return { ...vis, id: result.id };
}

// As Lens is using an object to store its attributes, using the update API
// will merge the new attribute object with the old one, not overwriting deleted
// keys. As Lens is using objects as maps in various places, this is a problem because
// deleted subtrees make it back into the object after a load.
// This function fixes this by doing two updates - one to empty out the document setting
// every key to null, and a second one to load the new content.
private async safeUpdate(id: string, attributes: SavedObjectAttributes) {
const resetAttributes: SavedObjectAttributes = {};
Object.keys(attributes).forEach((key) => {
resetAttributes[key] = null;
});
return (
await this.client.bulkUpdate([
{ type: DOC_TYPE, id, attributes: resetAttributes },
{ type: DOC_TYPE, id, attributes },
])
).savedObjects[1];
}

async load(id: string): Promise<Document> {
const { type, attributes, error } = await this.client.get(DOC_TYPE, id);

Expand All @@ -79,7 +84,7 @@ export class SavedObjectIndexStore implements SavedObjectStore {
}

return {
...attributes,
...(attributes as SavedObjectAttributes),
id,
type,
} as Document;
Expand Down

0 comments on commit 3ad79cd

Please sign in to comment.