Skip to content
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

Can't use actual extras and convert_to_extras fields when using a custom schema #1894

Merged
merged 7 commits into from Sep 10, 2014

Conversation

amercader
Copy link
Member

TL;DR You could not use actual extras and convert_to_extras fields when using a custom schema because the convert_to_extras function was not working as the validation expected.

Update: This now also includes logic to prevent using a free extra that has the same key as a field from the schema (which will allow to bypass validation)

Take the following custom schema implemented with IDatasetForm (this is similar to the one used on the example_idatasetform):

class MyPlugin(p.SingletonPlugin, p.toolkit.DefaultDatasetForm):

# ...

def create_package_schema
    schema = super(MyPlugin, self).create_package_schema()

    schema['custom_text'] = [ignore_missing, convert_to_extras]
    schema['custom_text2'] = [ignore_missing, convert_to_extras]

    return schema

# ...

If I call validate with this schema without any extras, all works as expected:

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
    }       

    data, errors = validate(data_dict, schema, context)

    '''
    {'custom_text': 'Hi',
     'custom_text2': 'Hi2',
     'extras': [{'key': 'custom_text', 'value': 'Hi'},
                   {'key': 'custom_text2', 'value': 'Hi2'}],
     'name': u'test-dataset',
     'title': u'test-dataset'}
    '''  

One could argue that the fields should not be on the root any more, but that's another matter, we got them on the extras, which is where we needed them to get them stored.

Now, if we provide some "actual" extras in the data_dict things start to fail:

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
        ]

    }

    data, errors = validate(data_dict, schema, context)

    '''
    {'custom_text': 'Hi',
     'custom_text2': 'Hi2',
     'extras': [{'key': 'proper_extra', 'value': 'Bye'}
                   {'key': 'custom_text2', 'value': 'Hi2'}],
     ],
     'name': u'test-dataset',
     'title': u'test-dataset'}
    '''

For every actual extra that you provide on the data_dict, one of the fields that were supposed to be converted to extras will get ignored and not put in extras, which means that they won't get stored (and validation will pass even if you were using not_empty or similar).

Even worse, if there are more actual extras than convert_to_extras fields the dictization will actually raise an exception:

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
            {'key': 'proper_extra2', 'value': 'Bye2'},
        ]

    }

    data, errors = validate(data_dict, schema, context)

    '''
    File "/home/adria/dev/pyenvs/glasgow/src/ckan/ckan/lib/navl/dictization_functions.py", line 235, in validate
        converted_data = unflatten(converted_data)
    File "/home/adria/dev/pyenvs/glasgow/src/ckan/ckan/lib/navl/dictization_functions.py", line 399, in unflatten
        current_pos = current_pos[key]
    IndexError: list index out of range
    '''

The relevant code to understand what's going on is located in dictization_functions.py, in the validatefunction:

    flattened = flatten_dict(data)
    converted_data, errors = _validate(flattened, schema, context)
    converted_data = unflatten(converted_data)

If we look at converted_data just after calling _validate when validating a dict against a schema that uses convert_to_extras, we see that these extras are not added in a "flattened" way, but in an ('extras',) key:

    {('custom_text',): 'Hi',
     ('extras',): [{'key': 'custom_text', 'value': 'Hi'}],
     ('extras', 0, 'key'): u'proper_extra',
     ('extras', 0, 'value'): 'Bye',
     ('name',): u'test-dataset',
     ('title',): u'test-dataset'}

When "unflattening" things, unflatten will replace the n first elements of the ('extras') list with the n actual extras that were provided.

So it's pretty obvious that the culprit is convert_to_extras itself, which is not adding the new extras in an unflattened way, which is the one expected later on.

After applying the patch on this PR, this is how converted_data looks like before unflattening:

    {('custom_text',): 'Hi',
     ('extras', 0, 'key'): u'proper_extra',
     ('extras', 0, 'value'): 'Bye',
     ('extras', 1, 'key'): 'custom_text',
     ('extras', 1, 'value'): 'Hi',
     ('name',): u'test-dataset',
     ('title',): u'test-dataset'}

Which gives the expected:

    {'custom_text': 'Hi',
     'extras': [{'key': u'proper_extra', 'value': 'Bye'},
                {'key': 'custom_text', 'value': 'Hi'}],
     'name': u'test-dataset',
     'title': u'test-dataset'}

`convert_to_extras` was adding the custom fields in an unflattened way
during validation to the object that would be later unflattened. This
worked fine if there were only `convert_to_extras` fields, but not if
you were also providing actual extras, eg

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
        ]

    }

This fixes the `convert_to_extras` output and adds new converters test for it.
amercader added a commit to okfn/ckanext-glasgow that referenced this pull request Aug 21, 2014
Allows to provide arbitrary key/value pairs. Support for harvesting them
and displaying them.

Requires ckan/ckan#1894
@amercader amercader changed the title Can't use actual extras and convert_to_extras fields when using a custom schema [WIP] Can't use actual extras and convert_to_extras fields when using a custom schema Aug 25, 2014
Moved functional tests to ckan/new_tests/logic/test_conversion.py and
added new unit tests for `convert_to_extras` to
ckan/new_tests/logic/test_converters.py
This is included in the context. We pass a copy of the one provided to
validate.
Added a new validator to the 'key' field of the extras schema that
checks if the value is present on the 'schema_fields' list that was
added to the context on the previous commit.
If there is a validation error on the extras, the error summary would
*always* be `Missing Value`. I guess it was assumed that all validation
errors would be that the extra key was missing. The fix is not ideal, as
it still assumes that all errors will be on the 'key' field (there could
be custom validation on the value), but at least it will show more than
one error, and the ones different from `Missing Value`.

This can furhter be improved, eg changing the macro that renders the
error summary on the templates to allow lists of errors, and maybe
showing the error below the actual extra field (using the index?)
I kept the example of removing the default key/value extras, as this is
what most people would want to do anyway, but mentioning that you have
the option of combining them both on CKAN >= 2.2.1
Don't load `example_idatasetform` as this causes failures in other
tests. Rather pass a schema directly.
@amercader amercader changed the title [WIP] Can't use actual extras and convert_to_extras fields when using a custom schema Can't use actual extras and convert_to_extras fields when using a custom schema Aug 26, 2014
@joetsoi joetsoi self-assigned this Aug 28, 2014
@joetsoi joetsoi merged commit 80c8c44 into master Sep 10, 2014
@joetsoi
Copy link
Contributor

joetsoi commented Sep 10, 2014

ping @amercader. merged.

amercader added a commit that referenced this pull request Sep 10, 2014
`convert_to_extras` was adding the custom fields in an unflattened way
during validation to the object that would be later unflattened. This
worked fine if there were only `convert_to_extras` fields, but not if
you were also providing actual extras, eg

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
        ]

    }

This fixes the `convert_to_extras` output and adds new converters test for it.
amercader added a commit that referenced this pull request Sep 10, 2014
Moved functional tests to ckan/new_tests/logic/test_conversion.py and
added new unit tests for `convert_to_extras` to
ckan/new_tests/logic/test_converters.py
amercader added a commit that referenced this pull request Sep 10, 2014
This is included in the context. We pass a copy of the one provided to
validate.
amercader added a commit that referenced this pull request Sep 10, 2014
Added a new validator to the 'key' field of the extras schema that
checks if the value is present on the 'schema_fields' list that was
added to the context on the previous commit.
amercader added a commit that referenced this pull request Sep 10, 2014
If there is a validation error on the extras, the error summary would
*always* be `Missing Value`. I guess it was assumed that all validation
errors would be that the extra key was missing. The fix is not ideal, as
it still assumes that all errors will be on the 'key' field (there could
be custom validation on the value), but at least it will show more than
one error, and the ones different from `Missing Value`.

This can furhter be improved, eg changing the macro that renders the
error summary on the templates to allow lists of errors, and maybe
showing the error below the actual extra field (using the index?)
amercader added a commit that referenced this pull request Sep 10, 2014
For the 2.2.1 branch, only changes in the snippet were added, as the docs page
does not exist here
amercader added a commit that referenced this pull request Sep 10, 2014
Don't load `example_idatasetform` as this causes failures in other
tests. Rather pass a schema directly.
amercader added a commit to okfn/ckanext-glasgow that referenced this pull request Sep 10, 2014
This is for resources. For datasets it's implemented on CKAN core
(2.2.1, see ckan/ckan#1894
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit that referenced this pull request Oct 13, 2014
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
`convert_to_extras` was adding the custom fields in an unflattened way
during validation to the object that would be later unflattened. This
worked fine if there were only `convert_to_extras` fields, but not if
you were also providing actual extras, eg

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
        ]

    }

This fixes the `convert_to_extras` output and adds new converters test for it.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
Moved functional tests to ckan/new_tests/logic/test_conversion.py and
added new unit tests for `convert_to_extras` to
ckan/new_tests/logic/test_converters.py
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
This is included in the context. We pass a copy of the one provided to
validate.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
Added a new validator to the 'key' field of the extras schema that
checks if the value is present on the 'schema_fields' list that was
added to the context on the previous commit.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
…rrors

If there is a validation error on the extras, the error summary would
*always* be `Missing Value`. I guess it was assumed that all validation
errors would be that the extra key was missing. The fix is not ideal, as
it still assumes that all errors will be on the 'key' field (there could
be custom validation on the value), but at least it will show more than
one error, and the ones different from `Missing Value`.

This can furhter be improved, eg changing the macro that renders the
error summary on the templates to allow lists of errors, and maybe
showing the error below the actual extra field (using the index?)
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
I kept the example of removing the default key/value extras, as this is
what most people would want to do anyway, but mentioning that you have
the option of combining them both on CKAN >= 2.2.1

Conflicts:
	doc/extensions/adding-custom-fields.rst
amercader added a commit to okfn/ckan-barnet that referenced this pull request Dec 18, 2014
Don't load `example_idatasetform` as this causes failures in other
tests. Rather pass a schema directly.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
`convert_to_extras` was adding the custom fields in an unflattened way
during validation to the object that would be later unflattened. This
worked fine if there were only `convert_to_extras` fields, but not if
you were also providing actual extras, eg

    data_dict = {
        'name': 'test-dataset',
        'custom_text': 'Hi',
        'custom_text2': 'Hi2',
        'extras': [
            {'key': 'proper_extra', 'value': 'Bye'},
        ]

    }

This fixes the `convert_to_extras` output and adds new converters test for it.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
Moved functional tests to ckan/new_tests/logic/test_conversion.py and
added new unit tests for `convert_to_extras` to
ckan/new_tests/logic/test_converters.py
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
This is included in the context. We pass a copy of the one provided to
validate.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
Added a new validator to the 'key' field of the extras schema that
checks if the value is present on the 'schema_fields' list that was
added to the context on the previous commit.
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
…rrors

If there is a validation error on the extras, the error summary would
*always* be `Missing Value`. I guess it was assumed that all validation
errors would be that the extra key was missing. The fix is not ideal, as
it still assumes that all errors will be on the 'key' field (there could
be custom validation on the value), but at least it will show more than
one error, and the ones different from `Missing Value`.

This can furhter be improved, eg changing the macro that renders the
error summary on the templates to allow lists of errors, and maybe
showing the error below the actual extra field (using the index?)
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
I kept the example of removing the default key/value extras, as this is
what most people would want to do anyway, but mentioning that you have
the option of combining them both on CKAN >= 2.2.1

Conflicts:
	doc/extensions/adding-custom-fields.rst
amercader added a commit to okfn/ckan-barnet that referenced this pull request Feb 26, 2015
Don't load `example_idatasetform` as this causes failures in other
tests. Rather pass a schema directly.
@maxious
Copy link
Member

maxious commented Mar 24, 2015

Note: I got this {"key": ["There is a schema field with the same name"]} error because there actually was two active entries of the same extra key for the same package in the package_extra/package_extra_revision tables for 4 datasets (out of >5000). Easily fixed :)

@smotornyuk smotornyuk deleted the 1894-use-extras-and-convert_to_extras branch December 19, 2018 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants