ckan icon indicating copy to clipboard operation
ckan copied to clipboard

:construction_worker: IUserForm implementation :construction_worker:

Open tino097 opened this issue 9 months ago • 5 comments

Fixes #6070

This PR is still WIP and im hopping to finish it soon after the few things we would need to decide on.

Becase it was really tricky to separate the private and public data in same column, i decided to add separate one called, for now, plugin_data (this was disscused some time ago)

However, I'm still figuring out an effective method to provide information which plugin is providing custom data so this solution is currently using public namespace.

@ckan/core I would appreciate any suggestions or feedback.

tino097 avatar May 03 '24 17:05 tino097

@wardi thanks for the ValidatorFactory tip, now we can get the plugins name.

tino097 avatar May 07 '24 14:05 tino097

So i will need some suggestion about the naming of the plugin_data

I considered few options as plugin_fields, extra fileds, custom_data but im not sure if those can be explainatory enough

tino097 avatar May 09 '24 21:05 tino097

For the option to keep the data in one column, when we will have multiple extensions implementing this interface, we would endup having following structure

    "plugin_data": {
                       "example_plugin1": {
                                "private": {
                                     "key1": "value1",
                                     "key2": "value2"
                                },
                                "public": {
                                     "key1": "value1",
                                     "key2": "value2"
                                }
                       },
                        "example_plugin2": {
                                "private": {
                                     "key1": "value1",
                                     "key2": "value2"
                                },
                                "public": {
                                     "key1": "value1",
                                     "key2": "value2"
                                }
                       }
           } 

With this mixed public and private data per extension, i will have some questions and considerations

  • How should validators be structured to handle both public and private data?
  • How we will define the default schema ?

Any feedback and suggestions are appriciated

tino097 avatar May 30 '24 12:05 tino097

My suggestion comes from Ian's comment here

You are now exposing the whole plugin_data on the model dictization stage. And, in addition, you have load_plugin_data which can copy some of the plugin_data items on the top level of the user dictionary. For example, if I have the plugin_data: {custom: {KEY: VALUE}} and I patch show_user_schema with {KEY: [load_plugin_data('custom')]}, I'll get the following result:

user == {
  "name": "sergey",
  "KEY": "VALUE",
  "plugin_data": {"custom": {"KEY": "VALUE"}}
}

You see, the whole plugin_data is unconditionally shown and any attempt to use load_plugin_data leads to data duplication.

Now we go back to Ian's idea. Imagine that the whole plugin_data is never shown, i.e., it's always private. There are no private/public sections inside plugin data - the whole plugin_data attribute is hidden.

If you want to make something "public", you just add the load_plugin_data validator to the show_user_schema. As a result, a specific key from the private plugin_data gets copied on the top-level of the user dictionary and becomes public.

And here comes that point that was unclear to you before. If you just do as I suggested before, you'll see that load_plugin_data cannot pluck properties from plugin_data. As you are removing plugin_data on the dictization level to make it invisible, data.get("plugin_data") line from the load_plugin_data validator returns nothing.

To overcome this, you can move plugin_data into context on dictization level:

def user_dictize(...):
  ...
  context['plugin_data'] = result_dict.pop('plugin_data', {})

And then, inside the load_plugin_data validator, you can access plugin_data from the context(instead of data):

def load_plugin_data(key, data, error, CONTEXT):
   plugin_data = CONTEXT.get("plugin_data")
   ...

smotornyuk avatar Jun 03 '24 23:06 smotornyuk

@wardi ive followed the other extras implementations.

tino097 avatar Aug 01 '24 16:08 tino097