Skip to content

Commit

Permalink
feat(xml,import,export):#1205 add required field to export and option…
Browse files Browse the repository at this point in the history
…ally validate it at import

Signed-off-by: David Wallace <[email protected]>
  • Loading branch information
MyPyDavid committed Jan 23, 2025
1 parent 6fb05a5 commit 530ae47
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 14 deletions.
1 change: 1 addition & 0 deletions rdmo/conditions/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def render_document(self, xml, conditions):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for condition in conditions:
Expand Down
5 changes: 5 additions & 0 deletions rdmo/core/renderers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
from io import StringIO

from django.conf import settings
from django.utils.encoding import smart_str
from django.utils.timezone import get_current_timezone, now
from django.utils.xmlutils import SimplerXMLGenerator
Expand Down Expand Up @@ -51,6 +52,10 @@ def render_document(self, xml, data):
def version(self):
return __version__

@property
def required(self):
return settings.EXPORT_MIN_REQUIRED_VERSION

@property
def created(self):
return now().astimezone(get_current_timezone()).isoformat()
2 changes: 2 additions & 0 deletions rdmo/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@

EXPORT_CONTENT_DISPOSITION = 'attachment'

EXPORT_MIN_REQUIRED_VERSION = '2.1.0'

PROJECT_TABLE_PAGE_SIZE = 20

PROJECT_VISIBILITY = True
Expand Down
7 changes: 7 additions & 0 deletions rdmo/core/tests/test_renderers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from django.conf import settings

from rdmo import __version__

from ..renderers import BaseXMLRenderer


Expand All @@ -7,6 +11,7 @@ def render_document(self, xml, data):
xml.startElement('rdmo', {
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
'version': self.version,
'required': self.required,
'created': self.created
})
self.render_text_element(xml, 'text', {}, data['text'])
Expand All @@ -17,6 +22,8 @@ def test_render():
renderer = TestRenderer()
xml = renderer.render({'text': 'test'})
assert '<text>test</text>' in xml
assert f'version="{__version__}"' in xml
assert f'required="{settings.EXPORT_MIN_REQUIRED_VERSION}"' in xml


def test_render_ascii_code():
Expand Down
47 changes: 33 additions & 14 deletions rdmo/core/xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,38 @@ def validate_root(root: Optional[xmlElement]) -> Tuple[bool, Optional[str]]:


def validate_and_get_xml_version_from_root(root: xmlElement) -> Tuple[Optional[Version], list]:
unparsed_root_version = root.attrib.get('version') or LEGACY_RDMO_XML_VERSION
root_version, rdmo_version = parse(unparsed_root_version), parse(RDMO_INSTANCE_VERSION)
if root_version > rdmo_version:
logger.info('Import failed version validation (%s > %s)', root_version, rdmo_version)
errors = [
_('This RDMO XML file does not have a valid version number.'),
f'XML Version ({root_version}) is greater than RDMO instance version {rdmo_version}'
]
rdmo_version = parse(RDMO_INSTANCE_VERSION)

# Extract version attributes from the XML root
unparsed_required_version = root.attrib.get('required') # New required version field
unparsed_root_version = root.attrib.get('version') or LEGACY_RDMO_XML_VERSION # Fallback to legacy default

# Validate the 'required' attribute if it exists
if unparsed_required_version:
try:
required_version = parse(unparsed_required_version)
except ValueError:
logger.info('Import failed: Invalid "required" format in XML (%s)', unparsed_required_version)
errors = [_('The "required" attribute in this RDMO XML file is not a valid version.')]
return None, errors

if required_version > rdmo_version:
logger.info('Import failed: Required version (%s) > RDMO instance version (%s)', required_version,
rdmo_version)
errors = [
_('This RDMO XML file requires a newer RDMO version to be imported.'),
f'Required version: {required_version}, Current version: {rdmo_version}.'
]
return None, errors

# Fallback to validate the legacy 'version' field
try:
xml_version = parse(unparsed_root_version)
return xml_version, []
except ValueError:
logger.info('Import failed: Invalid "version" format in XML (%s)', unparsed_root_version)
errors = [_('The "version" attribute in this RDMO XML file is not a valid version.')]
return None, errors
return root_version, []


def validate_legacy_elements(elements: dict, root_version: Version) -> list:
Expand Down Expand Up @@ -117,13 +139,13 @@ def parse_xml_to_elements(xml_file=None) -> Tuple[OrderedDict, list]:
return OrderedDict(), errors

# step 3.1.1: validate the legacy elements
legacy_errors = validate_legacy_elements(elements, parse(root.attrib.get('version', LEGACY_RDMO_XML_VERSION)))
legacy_errors = validate_legacy_elements(elements, root_version)
if legacy_errors:
errors.extend(legacy_errors)
return OrderedDict(), errors

# step 4: convert elements from previous versions
elements = convert_elements(elements, parse(root.attrib.get('version', LEGACY_RDMO_XML_VERSION)))
elements = convert_elements(elements, root_version)

# step 5: order the elements and return
# ordering of elements is done in the import_elements function
Expand Down Expand Up @@ -232,9 +254,6 @@ def strip_ns(tag, ns_map):


def convert_elements(elements, version: Version):
if not isinstance(version, Version):
raise TypeError('Version should be of parsed version type.')

if version < parse('2.0.0'):
validate_pre_conversion_for_missing_key_in_legacy_elements(elements, version)
elements = convert_legacy_elements(elements)
Expand Down
1 change: 1 addition & 0 deletions rdmo/domain/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def render_document(self, xml, attributes):
xml.startElement('rdmo', {
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
'version': self.version,
'required': self.required,
'created': self.created
})
for attribute in attributes:
Expand Down
2 changes: 2 additions & 0 deletions rdmo/options/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def render_document(self, xml, optionsets):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for optionset in optionsets:
Expand All @@ -25,6 +26,7 @@ def render_document(self, xml, options):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for option in options:
Expand Down
1 change: 1 addition & 0 deletions rdmo/projects/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def render_document(self, xml, project):
xml.startElement('project', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
self.render_text_element(xml, 'title', {}, project['title'])
Expand Down
5 changes: 5 additions & 0 deletions rdmo/questions/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def render_document(self, xml, catalogs):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for catalog in catalogs:
Expand All @@ -35,6 +36,7 @@ def render_document(self, xml, sections):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for section in sections:
Expand All @@ -49,6 +51,7 @@ def render_document(self, xml, pages):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for page in pages:
Expand All @@ -63,6 +66,7 @@ def render_document(self, xml, questionsets):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for questionset in questionsets:
Expand All @@ -77,6 +81,7 @@ def render_document(self, xml, questions):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for question in questions:
Expand Down
1 change: 1 addition & 0 deletions rdmo/tasks/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def render_document(self, xml, tasks):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for task in tasks:
Expand Down
1 change: 1 addition & 0 deletions rdmo/views/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def render_document(self, xml, views):
xml.startElement('rdmo', {
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
'version': self.version,
'required': self.required,
'created': self.created
})
for view in views:
Expand Down

0 comments on commit 530ae47

Please sign in to comment.