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.
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.
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.
How much does this break if we switch to unknown? I'm guessing a lot 😓
There was a problem hiding this comment.
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.
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.
🤔 @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.
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) |
joshdover
left a comment
There was a problem hiding this comment.
- Do we have a known use case for async
isExportableimplementations? 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.
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.
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
legrego
left a comment
There was a problem hiding this comment.
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.
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.
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.
🤔 @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
💚 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
isExportableproperty toSavedObjectsTypeManagementDefinitionto let type owners have per-object exportability control.SavedObjectsTypeand underlying types)Checklist