Skip to content

Commit

Permalink
validation: enums validation in marshmallow
Browse files Browse the repository at this point in the history
  • Loading branch information
Pablo Panero committed Oct 29, 2019
1 parent baa3549 commit 798702e
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 49 deletions.
47 changes: 4 additions & 43 deletions invenio_rdm_records/jsonschemas/records/record-v1.0.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"id": "http://localhost/schemas/records/record-v1.0.0.json",
"title": "Invenio Datacite based Record Schema v1.0.0",
"definitions": {
"title_type": {
"description": "Alternative titles types.",
"type": "string",
"enum": [
"AlternativeTitle",
"Subtitle",
"TranslatedTitle",
"Other"
]
},
"description_type": {
"type": "string",
"enum": [
"Abstract",
"Methods",
"SeriesInformation",
"TableOfContents",
"TechnicalInfo",
"Other"
]
}
},
"type": "object",
"additionalProperties": false,
"properties": {
Expand All @@ -47,12 +24,6 @@
"access_right": {
"default": "open",
"description": "Access right for record.",
"enum": [
"open",
"embargoed",
"restricted",
"closed"
],
"type": "string"
},
"additional_descriptions": {
Expand All @@ -66,7 +37,7 @@
},
"description_type": {
"description": "Type of description.",
"$ref": "#/definitions/description_type"
"type": "string"
},
"lang": {
"description": "Language of the description. ISO 639-3 language code.",
Expand All @@ -88,7 +59,7 @@
},
"title_type": {
"description": "Type of title.",
"$ref": "#/definitions/title_type"
"type": "string"
},
"lang": {
"description": "Language of the title. ISO 639-3 language code.",
Expand Down Expand Up @@ -143,12 +114,7 @@
},
"role": {
"description": "",
"type": "string",
"enum": [
"ContactPerson",
"Researcher",
"Other"
]
"type": "string"
}
},
"required": [
Expand Down Expand Up @@ -177,12 +143,7 @@
"format": "date-time"
},
"type": {
"description": "Type of the date interval.",
"enum": [
"Collected",
"Valid",
"Withdrawn"
]
"description": "Type of the date interval."
}
},
"required": [
Expand Down
69 changes: 67 additions & 2 deletions invenio_rdm_records/marshmallow/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,28 @@ class PersonIdsSchemaV1(StrictKeysMixin):
class ContributorSchemaV1(StrictKeysMixin):
"""Contributor schema."""

ROLES = [
"ContactPerson",
"Researcher",
"Other"
]

ids = fields.Nested(PersonIdsSchemaV1, many=True)
name = SanitizedUnicode(required=True)
role = SanitizedUnicode()
affiliations = fields.List(SanitizedUnicode())
email = fields.Email()

@validates('role')
def validate_role(self, value):
"""Validate that the role is one of the allowed ones."""
if value not in self.ROLES:
raise ValidationError(
_('Invalid role. Not one of {allowed}.'.format(
allowed=self.ROLES)),
field_names=['contributor']
)


class ResourceTypeSchemaV1(StrictKeysMixin):
"""Resource type schema."""
Expand Down Expand Up @@ -85,8 +101,14 @@ def dump_openaire_type(self, obj):
class TitleSchemaV1(StrictKeysMixin):
"""Schema for the additional title."""

TITLE_TYPES = [
"AlternativeTitle",
"Subtitle",
"TranslatedTitle",
"Other"
]

title = SanitizedUnicode(required=True, validate=validate.Length(min=3))
# TODO: Shall it be checked against the enum?
title_type = SanitizedUnicode()
lang = SanitizedUnicode()

Expand All @@ -95,13 +117,30 @@ def validate_language(self, value):
"""Validate that language is ISO 639-3 value."""
validate_iso639_3(value)

@validates('title_type')
def validate_title_type(self, value):
"""Validate that the title type is one of the allowed ones."""
if value not in self.TITLE_TYPES:
raise ValidationError(
_('Invalid title type. Not one of {allowed}.'.format(
allowed=self.TITLE_TYPES)),
field_names=['additional_titles']
)


class DescriptionSchemaV1(StrictKeysMixin):
"""Schema for the additional descriptions."""

DESCRIPTION_TYPES = [
"Abstract",
"Methods",
"SeriesInformation",
"TableOfContents",
"TechnicalInfo",
"Other"
]
description = SanitizedUnicode(required=True,
validate=validate.Length(min=3))
# TODO: Shall it be checked against the enum?
description_type = SanitizedUnicode()
lang = SanitizedUnicode()

Expand All @@ -110,15 +149,41 @@ def validate_language(self, value):
"""Validate that language is ISO 639-3 value."""
validate_iso639_3(value)

@validates('description_type')
def validate_description_type(self, value):
"""Validate that the description type is one of the allowed ones."""
if value not in self.DESCRIPTION_TYPES:
raise ValidationError(
_('Invalid description type. Not one of {allowed}.'.format(
allowed=self.DESCRIPTION_TYPES)),
field_names=['additional_descriptions']
)


class DateSchemaV1(StrictKeysMixin):
"""Schema for date intervals."""

DATE_TYPES = [
"Collected",
"Valid",
"Withdrawn"
]

start = DateString()
end = DateString()
type = fields.Str(required=True)
description = fields.Str()

@validates('type')
def validate_type(self, value):
"""Validate that the type is one of the allowed ones."""
if value not in self.DATE_TYPES:
raise ValidationError(
_('Invalid date type. Not one of {allowed}.'.format(
allowed=self.DATE_TYPES)),
field_names=['dates']
)


class RightSchemaV1(StrictKeysMixin):
"""Schema for rights."""
Expand Down
19 changes: 15 additions & 4 deletions tests/unit/test_schemas_json_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_title(val, expected):

@pytest.mark.parametrize(('val', 'expected'), [
([dict(title='Full additional title',
title_type='Title type',
title_type='Other',
lang='eng')], None),
([dict(title='Only required field')], None),
])
Expand All @@ -75,6 +75,8 @@ def test_valid_additional_titles(val, expected):

@pytest.mark.parametrize('val', [
([dict(title_type='Invalid title type', lang='eng')], None),
([dict(title_type='Other', lang='eng')], None),
([dict(title='Invalid lang', title_type='Other', lang='en')], None),
])
def test_invalid_additional_titles(val):
"""Test additional titles."""
Expand All @@ -85,10 +87,10 @@ def test_invalid_additional_titles(val):

@pytest.mark.parametrize(('val', 'expected'), [
([dict(description='Full additional description',
description_type='Description type',
description_type='Other',
lang='eng')], None),
([dict(description='Only required fields',
description_type='Description type')], None),
description_type='Other')], None),
])
def test_valid_additional_descriptions(val, expected):
"""Test additional descriptions."""
Expand All @@ -102,9 +104,13 @@ def test_valid_additional_descriptions(val, expected):


@pytest.mark.parametrize('val', [
([dict(description_type='Invalid no description', lang='eng')], None),
([dict(description_type='Other', lang='eng')], None),
([dict(description='Invalid no description type', lang='eng')], None),
([dict(lang='eng')], None),
([dict(description='Invalid type',
description_type='Invalid Type', lang='eng')], None),
([dict(description='Invalid lang',
description_type='Other', lang='en')], None),
])
def test_invalid_additional_descriptions(val):
"""Test additional descriptions."""
Expand Down Expand Up @@ -169,6 +175,11 @@ def test_dates():
assert 'not be null' in errors['dates'][0]['start'][0]
data, errors = schema.load({'dates': [{'type': 'Valid', 'start': ''}]})
assert 'Not a valid date' in errors['dates'][0]['start'][0]
data, errors = schema.load(
{'dates': [{'type': 'Invalid',
'start': '2019-01-01', 'end': '2019-01-31',
'description': 'Some description'}]})
assert 'Invalid date type' in errors['dates'][0]['type'][0]

# "start" date after "end"
data, errors = schema.load(
Expand Down

0 comments on commit 798702e

Please sign in to comment.