-
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
[Saved Objects] Adds managed to import options #155677
[Saved Objects] Adds managed to import options #155677
Conversation
… flag that is set by default to false during import and legacy alias creation - this is fine, a legacyUrlAlias is a SO and they all need managed set anyway
Pinging @elastic/kibana-core (Team:Core) |
@elastic/kibana-security I had to update a few tests. The changes are limited to one-liners adding the prop. |
}; | ||
const createSavedObjectsResult = await createSavedObjects(createSavedObjectsParams); | ||
errorAccumulator = [...errorAccumulator, ...createSavedObjectsResult.errors]; | ||
|
||
const successResults = createSavedObjectsResult.createdObjects.map((createdObject) => { | ||
const { type, id, destinationId, originId } = createdObject; | ||
const { type, id, destinationId, originId, managed: createdObjectManaged } = createdObject; |
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.
ts
complains about mirrored types
@@ -139,8 +148,8 @@ describe('collectSavedObjects()', () => { | |||
const result = await collectSavedObjects({ readStream, supportedTypes, objectLimit }); | |||
|
|||
const collectedObjects = [ | |||
{ ...obj1, typeMigrationVersion: '' }, | |||
{ ...obj2, typeMigrationVersion: '' }, | |||
{ ...obj1, typeMigrationVersion: '', managed: false }, |
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.
collectObjects
calls createSavedObjects
that calls savedObjectsClient.bulkCreate
, which will set the default if not already specified on the doc.
return { | ||
...obj, | ||
...(!obj.migrationVersion && !obj.typeMigrationVersion ? { typeMigrationVersion: '' } : {}), | ||
// override any managed flag on an object with that given as an option otherwise set the default to avoid having to do that with a core migration transform | ||
// this is a bulk opperation, applied to all objects being imported | ||
...{ managed: managed ?? obj.managed ?? false }, |
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'm not making any assumptions about how to handle defaults for overwriting all abjects' managed flag if there isn't a managed
option passed to the call. This allows us to retain the flag on objects being imported if it's already present on the object.
...object, | ||
...(references && { references }), | ||
...(originId && { originId }), | ||
...{ managed: managed ?? object.managed ?? false }, // trictly speaking this shouldn't be needed since the objects passed in should already have the flag set |
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.
Strictly speaking this shouldn't be needed since the objects passed in should already have the flag set
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.
Self review comments.
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.
Changes in Spaces API integration test LGTM.
}); | ||
|
||
describe('managed option', () => { | ||
test('if not provided, does not override existing property when already present, defaults to false for others', async () => { |
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.
Just looked briefly, but I think this test should focus on asserting the paramaters that we send to the create API
expect(mockCreateSavedObjects).toHaveBeenCalledWith(createSavedObjectsParams)
We already test createSavedObjects
function's behaviour in other places, so mocking ES responses and checking the success results are less useful and I think these are already covered in existing tests.
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.
We already indirectly test that the params don't include the new option in creates saved objects
.
I'll be more explicit in this one then.
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 refactored all of them in this group of tests to assert on the params rather than the objects getting created.
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.
LGTM!
Great job!! 🧡
...cts/core-saved-objects-import-export-server-internal/src/import/lib/collect_saved_objects.ts
Outdated
Show resolved
Hide resolved
Co-authored-by: Alejandro Fernández Haro <[email protected]>
@elasticmachine merge upstream |
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Public APIs missing comments
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
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.
LGT(K)M
A few questions and NITs:
/** | ||
* Flag indicating if a saved object is managed by Kibana (default=false). | ||
* Only used when upserting a saved object. If the saved object already | ||
* exist this option has no effect. | ||
*/ | ||
managed?: boolean; |
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.
What's the purpose of surfacing this managed
flag on our legacy url alias definition?
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.
It's to set all objects created during import
as managed
if the option is provided. Legacy alias get created if they need to and the repository will set the default for managed as false if it's not specified.
In short: to bypass adding a default.
@@ -91,6 +91,7 @@ export interface SavedObjectsImportFailure { | |||
* If `overwrite` is specified, an attempt was made to overwrite an existing object. | |||
*/ | |||
overwrite?: boolean; | |||
managed?: boolean; |
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.
Same question here, I don't see it as being a problem, but just so I understand, why do we need to surface the managed
flag on SO import failures/successes?
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.
As a reference, that's all.
@@ -170,6 +177,7 @@ export async function importSavedObjectsFromStream({ | |||
type, | |||
id, | |||
meta, | |||
managed: createdObjectManaged ?? managed, |
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.
NIT: we're passing managed
to createSavedObjects
, so in theory all the created objects will have correct value set, right? So I think managed: createdObjectManaged
would have the same effect?
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.
yea, it would.
@@ -235,6 +243,7 @@ export async function resolveSavedObjectsImportErrors({ | |||
...(overwrite && { overwrite }), | |||
...(destinationId && { destinationId }), | |||
...(destinationId && !originId && !createNewCopies && { createNewCopy: true }), | |||
...{ managed: createdObject.managed ?? managed ?? false }, // double sure that this already exists but doing a check just in case |
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.
NIT: Yeah, I think managed: createdObject.managed
would be sufficient (and we don't need the spread operator here given the value will always be present)
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.
As per the comment: I was worried about missing some indirect method that's called. We can change that later if needs be,
@@ -69,6 +69,14 @@ export interface SavedObjectsImportOptions { | |||
* different Kibana versions (e.g. generate legacy URL aliases for all imported objects that have to change IDs). | |||
*/ | |||
compatibilityMode?: boolean; | |||
/** | |||
* If true, will import as a managed object, else will import as not managed. |
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.
If true, will import as a managed object, else will import as not managed.
This description seems false. From the my understanding of the code:
- if
true
=> all imported objects will havemanaged: true
- if
false
=> all imported objects will havemanaged: false
- if unset => all imported objects will retain the value their currently had for
managed
, defaulting tofalse
if it was unset.
Or did I misunderstood the code?
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.
You understand the code correctly and that's what the comment implies (rather poorly it seems 😓 )
/** | ||
* If true, will import as a managed object, else will import as not managed. | ||
* | ||
* This can be leveraged by applications to e.g. prevent edits to a managed | ||
* saved object. Instead, users can be guided to create a copy first and | ||
* make their edits to the copy. | ||
*/ | ||
managed?: boolean; |
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.
(same as previous comment)
resp.body.saved_objects[0].updated_at = mockDate; | ||
resp.body.saved_objects[1].updated_at = mockDate; | ||
resp.body.saved_objects[2].updated_at = mockDate; | ||
resp.body.saved_objects[3].updated_at = mockDate; | ||
resp.body.saved_objects[0].created_at = mockDate; | ||
resp.body.saved_objects[1].created_at = mockDate; | ||
resp.body.saved_objects[2].created_at = mockDate; | ||
resp.body.saved_objects[3].created_at = mockDate; |
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.
NIT: be lazy. for obj in resp.body.saved_objects do ...
Or, if the intent of the test is to only test the managed
property, we can be even more lazy and just map(obj => { id, managed})
and only assert against those values.
Summary
Part of #140364
This PR is a follow up from #154515, where we added a new root property to all saved objects to indicate if they are managed or unmanaged. The property allows plugin developers to implement custom read-only UI's for managed objects.
In this PR, we implement a way to declare if objects should be
managed
or not, through the Saved Objects' import API.Note:
managed
on import (set a default) if it's already defined on the object that's being imported[1].managed
specified will default tofalse
.SavedObjectsImportOptions
now includes an optionalmanaged?: boolean
that, when specified will be applied to all the imports.For example:
obj1
hasmanaged:true
,obj2
doesn't have that set,import options include
managed:false
Result: Both obj1 & obj2 get created as
managed:false
How to test this:
Ensure the default is set on import:
Run Kibana locally on an ES snapshot using any license
In Saved Objects Management (SOM), import previously exported saved objects from before 8.8.0.
Inspect one of the objects from SOM and ensure the raw
json
output includesmanaged: false
Import objects with
managed
already setFrom SOM, inspect and copy a Saved Object to clipboard
In a text editor, change
managed: false
tomanaged: true
(not recommended for anything other than testing).Either delete the original object and import the object you edited. Inspect it and ensure
managed
is set totrue
.Import with declaring the option as true
We haven't added the option to the Saved Object HTTP APIs on purpose. The intent is to restrict managed/unmanaged to plugins only, using core.savedObjects.getImporter(client)
As a demo, I've hard-coded the option in the call to
importer.import({...})
in the saved objects service itself.To test this, run the demo PR and follow the same process as in "Ensure the default is set on import", but this time, all the objects that were imported will be flagged as
managed
.Note: the sample datasets already have
managed: false
in installing them. If one exports those and re-imports them (create new copies), we'll override the prop.[1] This allows us to import explicit
managed/unmanaged
objects as-is (or similar) objects that were exported.Not overriding the managed property does bring a risk that folks 'fiddle' with their raw documents to change that flag. We don't support manipulating raw documents. If they are changed, we can't guarantee that things will just work™.
A typical scenario where this could happen is if someone wants to create an editable copy of a
managed
object. Mitigation: we need the UI to support copy & edit and explicitly setmanaged
:false
during import/create/bulk_createChecklist
Delete any items that are not applicable to this PR.
Risk Matrix
Delete this section if it is not applicable to this PR.
Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release.
When forming the risk matrix, consider some of the following examples and how they may potentially impact the change:
managed
.