-
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: Provide ability to remove SO type from global SO HTTP API without hiding from the client #147150
Comments
Pinging @elastic/kibana-core (Team:Core) |
In sprint planning the question was raised about how deletes might work if a saved object is no longer exposed from the generic HTTP APIs. Similar to We are tracking the request for custom delete logic in Ensuring referential integrity for saved objects deleted from saved objects management and if teams need this for We should work with global experience to agree about the long term vision for deleting. |
Proposal: (Updated)# skip to the bottom for a summary #
|
option | global SO client | HTTP | remarks |
---|---|---|---|
hidden=false, hiddenFromHttpApis=true | Y | X | what we want to achieve |
hidden=true, hiddenFromHttpApis=true | X | X | no effect, except for bulkDelete with force: true (kbnArchiver dependency) |
hidden=false, hiddenFromHttpApis=false | Y | Y | behavior for visible types today |
hidden=true, hiddenFromHttpApis=false | X | X | doesn't make sense |
Declaring hiddenFromHttpApis
for already hidden types either has no effect or doesn't make sense and could lead to unexpected (possibly unintended?) behavior. As @rudolf pointed out, we'd also likely break test suites since kbnArchiver
depends on bulkDelete
and the way force
works today.
To keep things simple, I think we'd introduce the fewest side effects by only allowing hiddenFromHttpApis
to be configured for non-hidden types and have the registerType
API throw a validation error for types registered with hidden:true
and hiddenFromHttpApis !== undefined
:
const validateType = ({ name, management, hidden, hiddenFromHttpApis }: SavedObjectsType) => {
if (management) {
...
if (management.visibleInManagement !== undefined && !management.importableAndExportable) {
...
}
}
if (hidden && hiddenFromHttpApis !== undefined) {
throw new Error(
`Type ${name}: 'hiddenFromHttpApis' cannot be defined when specifying 'hidden: true'`
);
}
};
How do we handle the new parameter in the bulk APIs?
Keep it simple:
Don't allow any bulk APIs to work with non-standard SO types (hidden
: false
&& hiddenFromHttpApis
: undefined
// <- defaults):
For example, bulkGet
:
export const registerBulkGetRoute = (
router: InternalSavedObjectRouter,
{ coreUsageData }: RouteDependencies
) => {
router.post(
{
path: '/_bulk_get',
...
},
catchAndReturnBoomErrors(async (context, req, res) => {
let itemsToGet = req.body;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {});
const { getClient, typeRegistry } = (await context.core).savedObjects;
const allTypesVisibleToHttpApis = typeRegistry
.getVisibleToHttpApisTypes() // only types with `hidden:false` && `hiddenFromHttpApis:false`
.map((fullType) => fullType.name);
throwOnGloballyHiddenTypes(
allTypesVisibleToHttpApis,
itemsToGet.map(({ type }) => type)
);
}
const result = await getClient().bulkGet([...itemsToGet]);
return res.ok({ body: result });
})
);
};
Limited overrides:
If we need to be a bit more lenient, we could consider adding an optional query param to override this behavior, much in the same way using force:true
in bulkDelete
) force-deletes all objects in multiple namespaces:
export const registerBulkGetRoute = (
router: InternalSavedObjectRouter,
{ coreUsageData }: RouteDependencies
) => {
router.post(
{
path: '/_bulk_get',
validate: {
query: schema.object({ ignoreHiddenFromHttpApis: schema.maybe(schema.boolean()) }),
body: schema.arrayOf(
schema.object({
...
})
),
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
let itemsToGet = req.body;
...
if (!ignoreHiddenFromHttpApis) {
throwOnGloballyHiddenTypes(allTypesVisibleToHttpApis, itemsToGet.map(({ type }) => type));
}
const result = await client.bulkGet([...itemsToGet]);
return res.ok({ body: result });
})
);
};
The added benefit is that it will allow kbnArchiver
to keep the same behavior for bulkDelete
, thereby not breaking tests!
Other options:
Other options I explored:
- filter out the types hidden from the HTTP API and pass the rest along, adding error to the response body for those types (mutate response from client)
- config to hide specific apis per type:
enum HttpApiToHide {
get = 'GET',
update = 'UPDATE',
delete = 'DELETE',
resolve = 'RESOLVE',
...
bulk_get = 'BULK_GET'
'*' = 'ALL'
}
SavedObjectsType.hiddenFromHTTPApis: HttpApiToHide[]
- a few others that didn't go very far
It becomes really complicated really fast, leads to unpredictable behavior, and adds more inconsistency in an already inconsistent system (SOR handling of hidden types in bulk APIs). Then of course, there's the added complexity of private saved objects that are on the cards.
Our safest bet is to leave customized behavior and configuration up to saved objects' type owners, which is where we're trying to go.
Summary:
After all that, here's what I propose:
TypeRegistry:
- types with
hidden:true
cannot set thehiddenFromHttpApis
flag. Ensured through validation on type registration. - for
hidden:false
types,SavedObjectTypeRegistry.isHiddenFromHttpApis
defaults to false. Note: calling the method will return false for hidden types too, as it doesn't take thehidden
property into account. The method is not a setter though, so we should be ok here.
Non-bulk Routes
- Routes where we could use the simple blocking implementation:
get
,update
,create
,resolve
,delete
(throw when the type hashiddenFromHttpApis: true
). Fordelete
, that would override theforce
behavior too. - More logic is needed for
find
: any types provided to the request that aren't both visible and visible to the http APIS will throw an error in the handler. import/export
: support for hidden types was added recently. Assuming that we don't then want to remove support for visible types, we can leave these untouched.- Uneffected routes:
'/_migrate'
,'/_resolve_import_errors'
,'/deprecations/_delete_unknown_types'
, legacy import/exports
Bulk Routes
- throw on any saved object type in the payload with
hiddenFromHttpApis:true
(default) - query param to override the blocking mechanisms and risk the consequences of defaulting to the response from the SO respository.
- Use the query param to ensure we don't break tests (kbnArchiver can override the new behavior)
@elastic/kibana-core Please review the proposal and LMK WYT |
The overall proposal makes sense to me. Minor NITs / remarks:
|
I wonder if we should make more explicit that we want most/all SOs to be Should we flip the definition to It requires a noisier PR, but I think it is closer to what we want to achieve in the long run: delete the HTTP APIs (and the option that we are adding today). WDYT? |
👍
👍
We might want to leverage something similar to how The trouble with that of course is forcing teams to implement a list view where SO's can be deleted from the UI. They'd have to implement a list view anyway and expose a delete method.
It's worth thinking about. |
We need to double check if hiddenFromHttpApi saved object types might break test suites using |
In addition, we agreed as a team to take the approach of flipping the definition from Update: Introducing a new parameter that's |
@rudolf The SO HTTP API routes all still implement |
@elastic/kibana-core I've updated the design proposal after exploring more. The short version is summarized at the end. PTAL. |
Overall the proposal looks good to me. I think the only major concern I have would be about the The reason I dislike this part, in addition to the fact that we would kinda be leaking 'dev/test-only' options in production, is that long term, the goal is to fully get rid of these global SO HTTP APIs, so this workaround will not work then. Which is why ihmo, we should think of a long term solution right now to avoid being blocked later. Or at least, if we go with the work around now for simplicity's sake, we should at least have an idea of how we would address this mid/long term when we'll effectively remove those APIs. A solution I've been thinking about was to develop an The major concern about this proposal is to check if that could work with the way cloud is running our FTR test suites against their environment, as that means they would be forced to also install / use this custom @rudolf wdyt? And just another nitpick:
I would just throw if the type explicitly specifies |
Yea, I wasn't happy with this part either, precisely because we'd eventually need another solution for For now, I thought to at least get something in place so that teams can start migrating to their own APIs and follow up with a long-term solution. The workaround buys us a little time to explore options.
I like where you're going with this! I've created a dedicated issue we can use to explore the idea and discuss other options if a dedicated plugin won't work on Cloud.
I ran into issues when allowing hidden types to set the flag but those can be worked around, even if it means more verbose conditional checks. |
@rudolf @pgayvallet I'd prefer to handle the That way we unblock other teams from migrating off of the API's with less risk of breaking tests (and fewer |
I had a look at how So I kinda see the following options:
Even though I would lean towards (2) as a more ideal long term solution (3) sounds like the least amount of work while still providing what teams need. I agree with @pgayvallet that dedicated testing-only APIs would be preferable, even if these are just a copy-paste of the "real" APIs.
Yes, that should be dead / unecessary code and safe to remove. But if all these APIs are getting removed in the medium term then the priority of polishing this is quite low, I wouldn't bother. |
We'll need to add |
Open questions:
|
Sounds reasonable to me, we're preserving the same behavior for unknown types that way.
I'd say yes.
Yes we should. We need to create a dedicated SOM HTTP API for delete operations performed from the SOM UI instead of using the client-side SO client to do so. |
## Summary In the near future we will remove Saved Object (SO) HTTP APIs. This PR deprecates all browser-side SO types and interfaces. General comments on the approach taken here: * Add a deprecation notice that links to a GitHub issue that can be referenced by all teams * Do not break existing imports/exports * Mocks are also an indication of SO browser-side use, added deprecation notices * Some common types must also be deprecated, some may remain. For those to be removed they are moved to a separate file, with a deprecated type-alias re-exported ## Notes to reviewers * Easiest way to get an overview of the changes is to have the file-tree open in the "Files changed" view * Are there any other ways browser-side code can get knowledge of Saved Objects? * Please go through some client code and test that the DX is working as expected (the GitHub issue is discoverable) ## Related * #147150 * elastic/dev#2194 Co-authored-by: kibanamachine <[email protected]>
## Summary In the near future we will remove Saved Object (SO) HTTP APIs. This PR deprecates all browser-side SO types and interfaces. General comments on the approach taken here: * Add a deprecation notice that links to a GitHub issue that can be referenced by all teams * Do not break existing imports/exports * Mocks are also an indication of SO browser-side use, added deprecation notices * Some common types must also be deprecated, some may remain. For those to be removed they are moved to a separate file, with a deprecated type-alias re-exported ## Notes to reviewers * Easiest way to get an overview of the changes is to have the file-tree open in the "Files changed" view * Are there any other ways browser-side code can get knowledge of Saved Objects? * Please go through some client code and test that the DX is working as expected (the GitHub issue is discoverable) ## Related * elastic#147150 * elastic/dev#2194 Co-authored-by: kibanamachine <[email protected]>
… API without hiding from the client (#149166) Co-authored-by: kibanamachine <[email protected]> resolves #147150
Plugins currently use
hidden=true
to prevent exposing saved objects to the SO HTTP APIs.However, this also hides their SO types from the server-side
SavedObjectsClient
which requires them to explicitly enable these types by creating a new client withincludedHiddenTypes
. This means plugins wanting to adopt an API layer must refactor their code. At the same time, hiding the type from the client doesn’t really benefit plugins.Can we make plugin authors' lives easier by introducing a new saved object type
option
that only stops exposing a type over the SO HTTP APIs?Scope for this issue:
hidden
andhideFromHttpApi
) might interact with each other.delete
functionality of Saved Objects Management (ATM,hidden
types throw an error when trying to delete them from SOM when they are exposed in SOM).The text was updated successfully, but these errors were encountered: