-
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
add isExportable
SO export API
#101860
add isExportable
SO export API
#101860
Conversation
// first, evict current objects that are not exportable | ||
const { | ||
exportable: untransformedExportableInitialObjects, | ||
nonExportable: nonExportableInitialObjects, | ||
} = await splitByExportability(currentObjects, isExportable); |
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.
See #99680 (comment) about the logic of the order.
registerType: (type: SavedObjectsType) => void; | ||
registerType: <Attributes = any>(type: SavedObjectsType<Attributes>) => void; |
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.
Added generic for the attributes on SavedObjectsType
, SavedObjectsTypeManagementDefinition
and underlying types. However for BWC, I was forced to default to any
instead of unknown
for the public API.
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.
How much does this break if we switch to unknown
? I'm guessing a lot 😓
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, basically all types registering a getUrl
or getTitle
management handler, which is a lot. This is why I don't think we should do it in this PR. I can open a follow-up and ping the teams, though
const EMPTY_RESULT = { | ||
excludedObjects: [], | ||
excludedObjectsCount: 0, |
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.
Took me a while to understand why adding the new properties at the end of the definition was still causing a failure. TIL, for some test suites, we're asserting against the stringified so the properties needs to be in the exact same order.
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.
🤔 @jportner do recall why we are doing a stringified test here? Can we make this more robust by sorting the keys before stringifying, and switch to using json-stable-stringify
instead?
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.
IIRC it's because the export API returns Content-Type: application/ndjson
and the current approach was cleaner. Otherwise we would have to do this
diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts
index d9ebbac8102..ea474a88092 100644
--- a/x-pack/test/saved_object_api_integration/common/suites/export.ts
+++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts
@@ -135,8 +135,6 @@ export const createRequest = ({ type, id }: ExportTestCase) =>
const getTestTitle = ({ failure, title }: ExportTestCase) =>
`${failure?.reason || 'success'} ["${title}"]`;
-const EMPTY_RESULT = { exportedCount: 0, missingRefCount: 0, missingReferences: [] };
-
export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>) {
const expectSavedObjectForbiddenBulkGet = expectResponses.forbiddenTypes('bulk_get');
const expectResponseBody = (testCase: ExportTestCase): ExpectResponseBody => async (
@@ -151,7 +149,12 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
} else if (failure.statusCode === 200) {
// "find" was unauthorized, which returns an empty result
expect(response.body).not.to.have.property('error');
- expect(response.text).to.equal(JSON.stringify(EMPTY_RESULT));
+ const exportDetails = JSON.parse(response.text);
+ expect(exportDetails).to.eql({
+ exportedCount: 0,
+ missingRefCount: 0,
+ missingReferences: [],
+ });
} else {
throw new Error(`Unexpected failure status code: ${failure.statusCode}`);
}
The success case assertions further down also have to parse the ndjson.
Pinging @elastic/kibana-core (Team:Core) |
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 have a known use case for async
isExportable
implementations? If not, this could be something we opt not to support at this time to avoid any performance issues with this new hook.
registerType: (type: SavedObjectsType) => void; | ||
registerType: <Attributes = any>(type: SavedObjectsType<Attributes>) => void; |
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.
How much does this break if we switch to unknown
? I'm guessing a lot 😓
src/core/server/saved_objects/export/collect_exported_objects.ts
Outdated
Show resolved
Hide resolved
}, | ||
] | ||
`); | ||
Array [ |
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.
Ugh all these snapshots shakes fist
src/plugins/saved_objects_management/public/lib/extract_export_details.test.ts
Outdated
Show resolved
Hide resolved
...ins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx
Outdated
Show resolved
Hide resolved
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.
A few nits below, and a general comment:
I want to make sure that we don't use this new isExportable
function to enforce authorization/access control. We currently grant users with the Saved Objects Management
feature access to any exportable saved object type, so if we start relying on isExportable
to refine that authorization, then we will have a mismatch of expectations. It doesn't sound like that's the intent here, but I wanted to double check.
src/core/server/saved_objects/export/collect_exported_objects.ts
Outdated
Show resolved
Hide resolved
src/core/server/saved_objects/export/collect_exported_objects.ts
Outdated
Show resolved
Hide resolved
const nonExportableObjects: SavedObject[] = []; | ||
await Promise.all( | ||
objects.map(async (obj) => { | ||
const exportable = await isExportable(obj); |
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 saved object migrations have taught me anything, it's that plugin authors can/will create buggy functions to interrogate their saved objects. Can we be defensive here, and handle any thrown errors explicitly? I'm open to suggestions, but I'm thinking we could either:
- fail open, and treat failed checks as "exportable"
- fail closed, and treat failed checks as "unexportable"
- raise our own error in response to a failed check
Option 3 is my least favorite, because it prevents administrators from exporting saved objects due to a bug in our code, without any recourse. That said, I don't know the intent of preventing objects from being excluded, so I don't know if it's safe for us to always fail open/closed.
Perhaps the safest option is to "fail closed", and report the failure within the export metadata and/or in a server log entry. What do you think?
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 saved object migrations have taught me anything, it's that plugin authors can/will create buggy functions to interrogate their saved objects
Can't disagree on that
Perhaps the safest option is to "fail closed", and report the failure within the export metadata and/or in a server log entry
That's a perfect use case for the currently unused SavedObjectsExportExcludedObject.reason
src/core/server/saved_objects/export/collect_exported_objects.ts
Outdated
Show resolved
Hide resolved
src/core/server/saved_objects/export/collect_exported_objects.ts
Outdated
Show resolved
Hide resolved
const EMPTY_RESULT = { | ||
excludedObjects: [], | ||
excludedObjectsCount: 0, |
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.
🤔 @jportner do recall why we are doing a stringified test here? Can we make this more robust by sorting the keys before stringifying, and switch to using json-stable-stringify
instead?
I confirm that it is not the intent at all. This is only required for the '8.0 exportability' project, and AFAIK it may even be only temporary. See #99680 and #50266 (comment) for more context. |
@joshdover @legrego I think I addressed all of your feedbacks. PTAL |
test/plugin_functional/test_suites/saved_objects_management/export_transform.ts
Outdated
Show resolved
Hide resolved
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, thanks for the edits!
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
💚 Build SucceededMetrics [docs]Public APIs missing comments
Async chunks
Public APIs missing exports
History
To update your PR or re-run it, just comment with: |
* add `isExportable` SO export API * add warning when export contains excluded objects * add FTR test * fix API integration assertions * lint * fix assertions again * doc * update generated doc * fix esarchiver paths * use maps instead of objects * SavedObjectsExportablePredicate is no longer async * more docs * generated doc * use info instead of warning when export contains excluded objects * try/catch on isExportable call and add exclusion reason * add FTR test for errored objects * log error if isExportable throws
* add `isExportable` SO export API (#101860) * add `isExportable` SO export API * add warning when export contains excluded objects * add FTR test * fix API integration assertions * lint * fix assertions again * doc * update generated doc * fix esarchiver paths * use maps instead of objects * SavedObjectsExportablePredicate is no longer async * more docs * generated doc * use info instead of warning when export contains excluded objects * try/catch on isExportable call and add exclusion reason * add FTR test for errored objects * log error if isExportable throws * fix dataset for 7.x
Summary
Fix #99680
isExportable
property toSavedObjectsTypeManagementDefinition
to let type owners have per-object exportability control.SavedObjectsType
and underlying types)Checklist