-
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
[CM] Improve CRUD and RPC interfaces #153527
Comments
As mentioned in our previous meeting I would like to understand better the why we would put in place those restriction for the CRUD + search on a content provider which is directly tight to their plugin and UI. No CM UIs is consuming those.
This is not true for the inputs. Those are strict and restrictive. Except for the
As library authors we actually mean that These are my concerns:
Let's keep this one in the backlog until all content is migrated and Serverless/BWC ready and we have a clear reason why to enforce a common DB schema on the content CRUD + search methods. |
@sebelga, I don't think it is only the cache. For example, it seems that with the current approach we also won't be able to build a
Regarding the cache, I agree it wouldn't be easy on the client side, but I think it also doesn't have to be ideal either:
Promise would work if we increase
I am struggling to see the problem here. If we have schemas - they will fail in runtime on schema validation. If we don't have schemas they will fail in runtime inside the business logic. It is a bug anyways that needs to be fixed by a team. If they tricked typescript, it is their fault.
I don't know how much of the overhead from onboarding perspective these restrictions create :( |
Why not? There is nothing that stops us to require it in the server (or browser) side registry. // Teams decide to align all their interface
register({
id: 'foo',
msearch: {
extractCommonFields: [{
// teams have decided to unify **on their own decision** all their CRUD + search api
// so they can declare a single handler here. We didn't enforce anything
handler: mySuperHandlerThatWorksWithAllCrud,
operations: ['*'], // optional. If not provided the default is ['*']
}]
}
});
// Teams prefer to keep their implementation as is
register({
id: 'foo',
msearch: {
// teams prefer to keep their responses as is as it is simpler to expose 3 handler for the common fields.
// We didn't enforce anything
extractCommonFields: [{
handler: getHandlerToCommonField,
operations: ['get', 'bulkGet'],
}, {
handler: createUpdateHandlerToCommonField,
operations: ['create', 'update'],
}, {
handler: searchHandlerToCommonField,
operations: ['search'],
}]
}
});
I always said that we need to have the
I reiterate my initial comment: we need to have an actual problem to solve before optimising pre-maturely. It is not worth risking the onboarding to CM (and Serverless effort) for this optimisation IMO.
The important part of your sentence is the "if". We don't enforce schemas to be provided anymore since we added support for BWC. It is the responsibility of each team to provide their schemas and run the validation inside each of their storage instance methods. Fixing this issue implies that we would actually enforce them to provide a
My point is: each team should be able to decide what makes their life easier. If they indeed return the same object in all their CRUD + search, they will have an easier life for BWC. Only 1 object to up/down transform. If they prefer to do it differently and version multiple objects, why do we want to enforce anything? We can have recommendations, but I don't see (at this stage) the need to enforce anything. |
I don't think it is some heavy unjustified restriction. On contrary, I think it is expected that a CRUD API operates on top of some object type. For example:
interface CRUD<O> {
create: () => { obj: O }
read: (id: string) => { obj: O }
}
// test:
obj = create()
obj == read(obj.id)
interface CRUD<O> {
read: (id: string): { obj: O }
readBulk: (ids: string[]): { objs: O[] }
}
// test:
[read(id)] == readBulk([id]) Most of the HTTP APIs you will see (Stripe, Shopify, GitHub, all GraphQL APIs) do exactly that: there is one schema of a content type and it is reused everywhere. The reason why we need this is to be able to rely on this CRUD API to build any of the content management features. For example, we should be able to execute |
Ok. I will not try to convince you more. But really, whatever the outcome of this issue is I would not do it before full BWC and Serverless preparation for all content is finished and fully tested. We don't need it for our Component library as:
|
I don't see how search layer will help here, nor we will have it any time soon, if ever. In my opinion a "CRUD" API which does not behave as a CRUD API (where all inputs and all outputs of every method can be anything) is not useful. It is tech debt, and onboarding any content on top of it is will just add more tech debt on top of that tech debt. Whereas it should be trivial to simply return the same object shape per type in all CRUD methods that should return an object. interface CRUD<Object> {
get: () => Object;
bulkGet: () => Object[];
create: () => Object;
// etc..
} instead of: interface NotReallyCRUD {
get: () => any;
bulkGet: () => any;
create: () => any;
// etc..
} The BWC proposal started way before the Serverless project started. As part of it, the removal of the Saved Object client is not strictly necessary to support Zero Downtime Upgrades in Serverless. Instead, the safest and fastest way to support ZDU would be to build into the Saved Object client support of ZDU, for example, by using the object versioning library you have created. Now, the main benefit we get from removing the Saved Object client is that the teams, who used it on the client side, will now have the chance to decouple their SO object schemas from the HTTP schema. It is exactly the opportunity for the teams to figure out what the object schema over the HTTP of their object should be. Create that one schema and use it in all HTTP responses. It should not be that the CRUD methods do not provide any internal guarantees, there should be at least some consistency between the methods:
|
It is not "anything". It is very well typed and I think that is what you are missing
Note how We, as library author, provide a Generic that can return anything (the teams are then responsible to declare their schemas). As far as I know we don't have any mandate to tell teams what they should be returning and how consistent the response should be across their CRUD. Specially when we are so close to Serverless and all the unknown that will come with it. We should not come at this stage with new hard requirements on top of the CM migration (and make onboarding more difficult).
Indeed. They have the opportunity to do it and we can give them guidance. But should not be forced to do it just because we think it would be a more (useful, semantically correct, nice...) API. |
I am not missing that. I am not talking about how it looks like from the Maps team perspective. They can cast
It is not "their CRUD", it is CRUD for the Content Management. It should have some basic consistency guarantees, so we can build Content Management features on top of it. (Also, if they do create their own CRUD, I would argue they should indeed return objects of the same shape from |
From CM perspective.... // map
{
name: string;
description: string;
}
// dashboard
{
title: string;
text: string;
} Two content type. Two completely different objects. Both are
Let's then define the exact requirements to build the CM features on top of it.
I will reiterate one more time: even if teams would try their best to have a common TS object returned, we would still need runtime validation to guarantee that the objects are indeed the same (with the risk to break Kibana if old SO don't adhere to the validation schema). |
We still need the handler to covert to I don't think it makes anyones life harder to return objects of the same shape from Having one shape of a content type is done exactly to simplify everything. APIs we have in Kibana return the same shape of object from their respective Public APIs you will see on the net—Stripe, GitHub, Shopify, AWS, GCP, and more—all define a content type's shape once, and return it in the same shape from different API calls. |
Agree with all that you say. It is a great recommendation for teams as it will make their life easier. We can show them those public APIs that adhere to this recommended way of doing a CRUD api. For us, it doesn't really matter as we won't use it.
I've shown you above that is not the case.
Yes. 100% agree. I am not trying to say that is not a best practice to do so. I am simply saying that we don't have a mandate to enforce it right now without a compelling reason (e.g. a feature we have to build blocked by it). Anyway, let's leave it here. Let's onboard all the content to CM and we'll retake the conversation then. |
So, if you agree and it will make their life easier, then there is no reason to not do it.
For us it matter a lot. We don't care what is inside their content item object, for us it is (And, no, we don't need to runtime validate it. At least I don't see why we should.)
(pseudocode:) interface ContentStorage<T> {
get: () => T,
getBulk: () => T[],
create: () => T,
update: () => T,
delete: () => { success: boolean },
search: () => T[],
}
You literally showed your "update" method which needs to be fixed. And the reason we are removing the Saved Object client is to exactly to fix it - to decouple database schema from network schema.
I don't think we can onboard anyone until it is fixed. |
We should improve the interfaces of Content Management CRUD layer and RPC. Currently, the CRUD layer is very permissive: it allows the storage layer to specify as its inputs and outputs for each CRUD method almost any interface, without any consistency between the methods. We should:
ContentStorage
interfaceContentCrud
classget
andbulkGet
(and other procedures) should return the content items in the same shape.get
— well defined content item interface that is returned, with ability to add more fields later.bulkGet
— should semantically be equivalent toget
, with the only difference that it receives a list of IDs and returns a list of content items.create
— should return a content item, and return it same asget
.update
— should be able to return the updated content item, same asget
.search
bulkGet
any
, only leaveunknown
for the actual content item object.any
types usage in CRUD request and response.unknown
used, but only for:options
, which we pass throughThe
ContentStorage
interface could look something like this:The text was updated successfully, but these errors were encountered: