-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Spaces] Space-Aware Saved Objects #18862
Changes from 3 commits
f208d3c
7538bf3
bb3e511
0e2e4e8
a6287cc
7db0a4a
192d9c2
1c4afd8
d573457
d99cec7
7e2d1e3
862752b
a63128c
858eff0
1b95aa0
fc663d9
fc61594
776da8a
d2545d4
1fd7699
9469742
e24578f
5fe4bfd
c6e8925
f4a19ab
dee335b
452de10
6bf3515
8cb871a
53bb020
90892ca
4181c9e
a35d15f
093dd47
6e1c4c4
2195ee0
48c5f23
3a832e9
71f0634
00bd94c
415fa09
8d48c80
bc8aaef
839a0cf
8b1ff6b
299efb8
3d0f6c9
1747a74
78a54d1
2149f87
85b315f
9c69568
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,6 +107,7 @@ export class SavedObjectsClient { | |
async create(type, attributes = {}, options = {}) { | ||
const { | ||
id, | ||
extraBodyProperties = {}, | ||
overwrite = false | ||
} = options; | ||
|
||
|
@@ -120,9 +121,10 @@ export class SavedObjectsClient { | |
index: this._index, | ||
refresh: 'wait_for', | ||
body: { | ||
...extraBodyProperties, | ||
type, | ||
updated_at: time, | ||
[type]: attributes | ||
[type]: attributes, | ||
}, | ||
}); | ||
|
||
|
@@ -167,9 +169,10 @@ export class SavedObjectsClient { | |
} | ||
}, | ||
{ | ||
...object.extraBodyProperties, | ||
type: object.type, | ||
updated_at: time, | ||
[object.type]: object.attributes | ||
[object.type]: object.attributes, | ||
} | ||
]; | ||
}; | ||
|
@@ -272,6 +275,7 @@ export class SavedObjectsClient { | |
sortField, | ||
sortOrder, | ||
fields, | ||
extraFilters, | ||
} = options; | ||
|
||
if (searchFields && !Array.isArray(searchFields)) { | ||
|
@@ -282,6 +286,10 @@ export class SavedObjectsClient { | |
throw new TypeError('options.searchFields must be an array'); | ||
} | ||
|
||
if (extraFilters && !Array.isArray(extraFilters)) { | ||
throw new TypeError('options.extraFilters must be an array'); | ||
} | ||
|
||
const esOptions = { | ||
index: this._index, | ||
size: perPage, | ||
|
@@ -295,7 +303,8 @@ export class SavedObjectsClient { | |
searchFields, | ||
type, | ||
sortField, | ||
sortOrder | ||
sortOrder, | ||
extraFilters | ||
}) | ||
} | ||
}; | ||
|
@@ -342,7 +351,7 @@ export class SavedObjectsClient { | |
* { id: 'foo', type: 'index-pattern' } | ||
* ]) | ||
*/ | ||
async bulkGet(objects = []) { | ||
async bulkGet(objects = [], options = {}) { | ||
if (objects.length === 0) { | ||
return { saved_objects: [] }; | ||
} | ||
|
@@ -357,8 +366,17 @@ export class SavedObjectsClient { | |
} | ||
}); | ||
|
||
const { docs } = response; | ||
|
||
let docsToReturn = docs; | ||
if (typeof options.documentFilter === 'function') { | ||
docsToReturn = docs.filter(options.documentFilter); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filters ES results to only show documents for the active Space |
||
} | ||
|
||
const { extraSourceProperties = [] } = options; | ||
|
||
return { | ||
saved_objects: response.docs.map((doc, i) => { | ||
saved_objects: docsToReturn.map((doc, i) => { | ||
const { id, type } = objects[i]; | ||
|
||
if (!doc.found) { | ||
|
@@ -370,13 +388,21 @@ export class SavedObjectsClient { | |
} | ||
|
||
const time = doc._source.updated_at; | ||
return { | ||
const savedObject = { | ||
id, | ||
type, | ||
...time && { updated_at: time }, | ||
version: doc._version, | ||
attributes: doc._source[type] | ||
attributes: { | ||
...extraSourceProperties | ||
.map(s => doc._source[s]) | ||
.reduce((acc, prop) => ({ ...acc, ...prop }), {}), | ||
|
||
...doc._source[type], | ||
} | ||
}; | ||
|
||
return savedObject; | ||
}) | ||
}; | ||
} | ||
|
@@ -388,7 +414,7 @@ export class SavedObjectsClient { | |
* @param {string} id | ||
* @returns {promise} - { id, type, version, attributes } | ||
*/ | ||
async get(type, id) { | ||
async get(type, id, options = {}) { | ||
const response = await this._callCluster('get', { | ||
id: this._generateEsId(type, id), | ||
type: this._type, | ||
|
@@ -403,14 +429,22 @@ export class SavedObjectsClient { | |
throw errors.createGenericNotFoundError(); | ||
} | ||
|
||
const { extraSourceProperties = [] } = options; | ||
|
||
const { updated_at: updatedAt } = response._source; | ||
|
||
return { | ||
id, | ||
type, | ||
...updatedAt && { updated_at: updatedAt }, | ||
version: response._version, | ||
attributes: response._source[type] | ||
attributes: { | ||
...extraSourceProperties | ||
.map(s => response._source[s]) | ||
.reduce((acc, prop) => ({ ...acc, ...prop }), {}), | ||
|
||
...response._source[type], | ||
} | ||
}; | ||
} | ||
|
||
|
@@ -434,8 +468,9 @@ export class SavedObjectsClient { | |
ignore: [404], | ||
body: { | ||
doc: { | ||
...options.extraBodyProperties, | ||
updated_at: time, | ||
[type]: attributes | ||
[type]: attributes, | ||
} | ||
}, | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { SpacesSavedObjectsClient } from './spaces_saved_objects_client'; | ||
|
||
export function spacesSavedObjectsClientWrapper(baseClient, options) { | ||
return new SpacesSavedObjectsClient({ | ||
baseClient, | ||
...options | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
export class SpacesSavedObjectsClient { | ||
constructor(options) { | ||
const { | ||
request, | ||
baseClient, | ||
spaceUrlContext, | ||
} = options; | ||
|
||
this.errors = baseClient.errors; | ||
|
||
this._client = baseClient; | ||
this._request = request; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we aren't using |
||
|
||
this._spaceUrlContext = spaceUrlContext; | ||
} | ||
|
||
async create(type, attributes = {}, options = {}) { | ||
|
||
if (this._isTypeSpaceAware(type)) { | ||
options.extraBodyProperties = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this method, and some others, mutate the options that are passed into them, which might not be what the consumers are expecting. |
||
...options.extraBodyProperties, | ||
spaceId: await this._getSpaceId() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we already have the spaceId, we shouldn't need to get it again. |
||
}; | ||
} | ||
|
||
return await this._client.create(type, attributes, options); | ||
} | ||
|
||
async bulkCreate(objects, options = {}) { | ||
options.extraBodyProperties = { | ||
...options.extraBodyProperties, | ||
spaceId: await this._getSpaceId() | ||
}; | ||
|
||
return await this._client.bulkCreate(objects, options); | ||
} | ||
|
||
async delete(type, id) { | ||
return await this._client.delete(type, id); | ||
} | ||
|
||
async find(options = {}) { | ||
const spaceOptions = {}; | ||
|
||
if (this._isTypeSpaceAware(options.type)) { | ||
const spaceId = await this._getSpaceId(); | ||
if (spaceId) { | ||
spaceOptions.extraFilters = [{ | ||
term: { | ||
spaceId | ||
} | ||
}]; | ||
} | ||
} | ||
|
||
return await this._client.find({ ...options, ...spaceOptions }); | ||
} | ||
|
||
async bulkGet(objects = []) { | ||
// ES 'mget' does not support queries, so we have to filter results after the fact. | ||
const thisSpaceId = await this._getSpaceId(); | ||
|
||
return await this._client.bulkGet(objects, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a similar fashion to the above, how do you feel about passing in an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it! Done! |
||
extraSourceProperties: ['spaceId'], | ||
documentFilter: (doc) => { | ||
if (!doc.found) return true; | ||
|
||
const { type, spaceId } = doc._source; | ||
|
||
if (this._isTypeSpaceAware(type)) { | ||
return spaceId === thisSpaceId; | ||
} | ||
|
||
return true; | ||
} | ||
}); | ||
} | ||
|
||
async get(type, id) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this also take the |
||
// ES 'get' does not support queries, so we have to filter results after the fact. | ||
let thisSpaceId; | ||
|
||
if (this._isTypeSpaceAware(type)) { | ||
thisSpaceId = await this._getSpaceId(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, if we added the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it! Done! |
||
} | ||
|
||
const response = await this._client.get(type, id, { | ||
extraSourceProperties: ['spaceId'] | ||
}); | ||
|
||
const { spaceId: objectSpaceId } = response.attributes; | ||
|
||
if (objectSpaceId !== thisSpaceId) { | ||
throw this._client.errors.createGenericNotFoundError(); | ||
} | ||
|
||
return response; | ||
} | ||
|
||
async update(type, id, attributes, options = {}) { | ||
attributes.spaceId = await this._getSpaceId(); | ||
return await this._client.update(type, id, attributes, options); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be safe, let's specify the spaceId here always so that they don't accidentally write the document without a spaceId or to the wrong spaceId. |
||
} | ||
|
||
_isTypeSpaceAware(type) { | ||
return type !== 'space'; | ||
} | ||
|
||
async _getSpaceId() { | ||
if (!this._spaceId) { | ||
const { | ||
saved_objects: spaces = [] | ||
} = await this.find({ | ||
type: 'space', | ||
search: `"${this._spaceUrlContext}"`, | ||
search_fields: ['urlContext'], | ||
}); | ||
|
||
if (spaces.length > 0) { | ||
this._spaceId = spaces[0].id; | ||
} | ||
} | ||
|
||
return this._spaceId; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should be able to pull this logic up into the
spaces_saved_object_client
now that we've introducedextraSourceProperties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome, thanks!