It seems there is a need for some kind of plugin architecture. Sometimes this can be achieved via hooks and HOC however if there are many popular 'plugins' wrapping many HOCs is not a fantastic API.
I've created this issue to discuss.
Some examples that might be 'plugins' include:
I'm very pro plugins as I think they help find limitations in the core product. An example of this is orderablesortable fields. To support this I think we need to make some changes to the core platform and if other plugin ideas surface requirements that would be great!
I'd love to see 'plugins' stay really simple. essentially a better API for HOCs something like:
keystone.createList('User', Users,
{
plugins: [
withTimestamps(timestampPluginOptions),
withRoles(rolePluginOptions),
]
}
);
I think plugins should be able to add fields, add hooks and register their own hooks.
In this comment I'll outline a proposal for adding list plugins as a mechanism for adding functionality to a List.
A new field will be added to the config object consumed by createList called plugins, which takes a list of functions with the signature config => config. Each of these functions will have an opportunity to take the config object and return either a new config or a modified config.
The plugin function can make changes such as adding new fields or hooks, adding new options to existing fields (e.g. adding access control), or modifying existing fields.
A trivial example would be
keystone.createList('User', {
fields: { ... },
hooks: { ... },
plugins: [config => ({...config, fields: { ...config.fields, newField: { type: Text })],
}
createList would apply each of the plugins in the order specified, allowing for chaining of different plugins.
The following two pieces of code would be equivalent.
keystone.createList('User', {..., plugins: [plugin1, plugin2] })
keystone.createList('User', plugin2(plugin1({...})))
In this sense, the plugins option is simply a convenience wrapper around what is already possible. The advantage is that it gives us a well defined pattern which people can follow. It also allows the plugin list to be defined programatically.
The plugin function can make changes such as adding new fields or hooks
@timleslie on Hooks, is this possible to modify existing hooks config item to also accept array and run them async as part of this PR? (we discussed over slack). instead of plugin wrap the field hook in async they can push to hooks like 'field.hooks.push(async () => {})`
Changing the hooks API is out of scope for this API. It's definitely still on the radar though, I think the appropriate pattern will land once we get this plugin API taken care of and a few different types of plugins start to come in.
makes sense.
for the help of community with plugins, I strongly feel that we should delay any initialization of field or list from config until the call to connect is made (or split connect in initialize and connect - so that we can target code which can access uninitialized keystone as well as initialized but not connected kesytone)
makes sense.
for the help of community with plugins, I strongly feel that we should delay any initialization of field or list from config until the call to
connectis made (or split connect ininitializeandconnect- so that we can target code which can access uninitialized keystone as well as initialized but notconnectedkesytone)
I'm not sure I understand the desire for this. Can you give an example of a use case where it would be useful to do something "uninitialized" state?
@timleslie I have taken another look. One of the use case is to add fields later by things like Auth Strategies, right now if you want to capture social auth username into User collection, You have to do it manually in schema. If we delay list.initFields or introduce another method like List.addField we can push new fields even after the keystone.createList is called.
this scenario is still not possible by list plugins.
edit: created another issues #1498 for this
It looks like there hasn't been any activity here in over 6 months. Sorry about that! We've flagged this issue for special attention. It wil be manually reviewed by maintainers, not automatically closed. If you have any additional information please leave us a comment. It really helps! Thank you for you contribution. :)
Most helpful comment
List Plugin API Proposal
In this comment I'll outline a proposal for adding
list pluginsas a mechanism for adding functionality to aList.A new field will be added to the
configobject consumed bycreateListcalledplugins, which takes a list of functions with the signatureconfig => config. Each of these functions will have an opportunity to take the config object and return either a new config or a modified config.The plugin function can make changes such as adding new fields or hooks, adding new options to existing fields (e.g. adding access control), or modifying existing fields.
A trivial example would be
createListwould apply each of the plugins in the order specified, allowing for chaining of different plugins.The following two pieces of code would be equivalent.
In this sense, the
pluginsoption is simply a convenience wrapper around what is already possible. The advantage is that it gives us a well defined pattern which people can follow. It also allows the plugin list to be defined programatically.