-
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-object export custom logic #84980
Comments
Pinging @elastic/kibana-core (Team:Core) |
Thinking out loud here: First, I guess hooks requiring user interaction are out of the question here right? we are only speaking of 'standalone' hooks without prompt to the user or anything?
Then I guess we need to support asynchronous hooks, right? As the hook would need to use the SO api to fetch the additional objects to include in the export. I'm also thinking of the format of such API: I was thinking of adding something like (don't mind the naming) interface EnhancedObjectExport {
obj: SavedObject, // allow to modify the object before export
additionalObjects?: SavedObject[], // allow to include other objects for export
}
SavedObjectsType.onExport(obj: SavedObject): EnhancedObjectExport | Promise<EnhancedObjectExport> However
We could maybe just use the same approach and add an additional interface ExportContext {
savedObjects: {
client: SavedObjectsClientContract;
}
// and more
}
SavedObjectsType.onExport(obj: SavedObject, context: ExportContext): EnhancedObjectExport | Promise<EnhancedObjectExport> But maybe some hooks logic would require to access the owner plugin's API or service, in which case this would not be good enough. We could just propagate the 'raw' Also work noting that once we find the correct pattern for an export hook, adding the equivalent API for import should be rather trivial. |
Correct.
👍 With regard to the API design, I defer to your alls expert opinions :) |
The types of plugins that require custom export hooks are most likely the ones with slightly more complex domains. So I think this should be the preferred way plugins implement their export hook, by calling a service which contains their business logic. But this means they will probably be calling Plugins could "hack" around the lifecycles, but maybe this should be a This does mean there's theoretically a time where Kibana could accept an incoming request to export saved objects before the custom export logic has been registered. But with sync lifecycles this should only be one tick and we could in the future only start the server one tick after start. |
Exposing this new hook API on So basically, even if we do provide the hook API on the start contract, we will still need to pass down the That's the That way, the hook would still belongs to the type definition, and consumers could just use their registered context providers to retrieve their services. The other option, that is closer to your proposal, would be to have this new hook API exposed on the start contract, and have the @rudolf wdyt about that? |
From our recent sync discussion... Although a request scoped ES/SO client might be useful, plugins will primarily be fetching hidden saved objects which isn't possible from a scoped SO client (unless we change that in #82027) |
After thinking a bit more about it, I have a few remarks:
I'm think about the onExport(obj: SavedObject): EnhancedObjectExport the hook registrant will have to perform one ES request for each individual onExport(objs: SavedObject[]): EnhancedObjectExport the registrant would be able to perform a single request for all the
I hear the fact that exposing the API on
If in previous point, I exposed that imho, we should have this hook API exposed on the import { myObjectType } from './saved_objects';
// ... in setup
core.savedObjects.registerType(myObjectType) Having the hook registered via import { createMyObjectType } from './saved_objects';
core.savedObjects.registerType(createMyObjectType(core.getStartContract, () => this.myService)) instead of import { myObjectType } from './saved_objects';
// ... in setup
core.savedObjects.registerType(myObjectType)
core.savedObjects.registerExportHook(myObjectType.type, createMyObjectExportHook(core.getStartContract, () => this.myService)) Note: I'm not 100% sure on that one. Having the hook registered via the type definition would restrict hook registration to the type owner, which makes a lot of sense, and would also restrict one hook (max) per type, which is also something we probably want (multiple hooks transforming the exported objects seems unneeded and unnecessary complex to handle). Does anyone have a stronger opinion on that one?
The proposed API was interface EnhancedObjectExport {
obj: SavedObject, // allow to modify the object before export
additionalObjects?: SavedObject[], // allow to include other objects for export
}
// or SoServiceSetup.registerExportHook
SavedObjectsType.onExport(obj: SavedObject, ctx: ExportContext): EnhancedObjectExport | Promise<EnhancedObjectExport> however, if we follow my first point, and want a hook signature taking all the objects of a given type instead, I'm unsure what we should have. Maybe we just want the hook to return a replacement list that will effectively be used instead of the initial objects for the type? SavedObjectsType.onExport(objs: SavedObject[], ctx: ExportContext): SavedObject[] | Promise<SavedObject[]> with a naive implementation of the exporter code looking like: let actuallyExported: SavedObject[];
const types = splitByType(exportedObject)
for ([type, objects] of types.entries()) {
actuallyExported = [
...actuallyExported,
...(hasHook(type) ? await typeHook(objects) : objects)
]
} does anyone have a better idea?
As already discussed, we need to provide a context to the hook, at minima the current request, to let the consumers eventually create their scoped clients / service. However as @rudolf mentioned here, it is likely that plugins will be fetching hidden types, making preconfigured SO clients not that useful. Which is why I think only exposing the request (at least initially) should be good enough, and would avoid adding overcomplicated routehandler-like registrable context, at least for the initial implementation. |
I just created #87807 as a POC of the proposed implementation. Feedback would be greatly appreciated. |
@XavierM based on the way cases is implemented, do you have any feedback on the proposed API? |
+1
Yeah I'm fine with going with setup for now and working on the developer experience when we move to sync lifecycles and try to reduce the amount of
Don't have a strong opinion either, but I lean towards a single API call because of the benefits you mentioned. Ultimately plugins can easily work around the need to refactor with something like: core.savedObjects.registerType({...mySavedObjectType, onExport: this.onExport})
+1 We can schedule some follow-up work once we have two or more plugins using this API to make improvements if necessary and expanding the export context later won't be a breaking change. |
Yea, after doing the POC for both the import and export hooks, I also think this is slightly better. Will need to update both PRs I guess, as I went the other way in both 😅
That's a good remark. The fact that these are optional allows to exclude it from the 'static' type definition and add it on the fly during the registration from the plugin code if owners need to. |
When alerts and actions are exported their secrets and API keys are intentionally excluded from the export. As such, when these saved-objects are imported, it will require additional effort from the end-user to make these alerts and actions active. The end-user will be warned of this additional requirement per #84977, but we'd like to perform additional logic on export so that these alerts and actions are explicitly marked as disabled. Performing this custom logic on export is safer than doing so on import, because if it fails on import the user will potentially only have some of their saved-objects persisted.
This will also allow us to work-around some of the limitations incurred from using saved-object references to model a composition relationship as originally discussed in #82064. For example, when a case is export we'd like to use the custom logic hook to automatically include the related comments, etc.
The text was updated successfully, but these errors were encountered: