Skip to content

Commit 530ae47

Browse files
committed
feat(xml,import,export):#1205 add required field to export and optionally validate it at import
Signed-off-by: David Wallace <[email protected]>
1 parent 6fb05a5 commit 530ae47

File tree

11 files changed

+59
-14
lines changed

11 files changed

+59
-14
lines changed

Diff for: rdmo/conditions/renderers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def render_document(self, xml, conditions):
1111
xml.startElement('rdmo', {
1212
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
1313
'version': self.version,
14+
'required': self.required,
1415
'created': self.created
1516
})
1617
for condition in conditions:

Diff for: rdmo/core/renderers.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from io import StringIO
33

4+
from django.conf import settings
45
from django.utils.encoding import smart_str
56
from django.utils.timezone import get_current_timezone, now
67
from django.utils.xmlutils import SimplerXMLGenerator
@@ -51,6 +52,10 @@ def render_document(self, xml, data):
5152
def version(self):
5253
return __version__
5354

55+
@property
56+
def required(self):
57+
return settings.EXPORT_MIN_REQUIRED_VERSION
58+
5459
@property
5560
def created(self):
5661
return now().astimezone(get_current_timezone()).isoformat()

Diff for: rdmo/core/settings.py

+2
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@
295295

296296
EXPORT_CONTENT_DISPOSITION = 'attachment'
297297

298+
EXPORT_MIN_REQUIRED_VERSION = '2.1.0'
299+
298300
PROJECT_TABLE_PAGE_SIZE = 20
299301

300302
PROJECT_VISIBILITY = True

Diff for: rdmo/core/tests/test_renderers.py

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
from django.conf import settings
2+
3+
from rdmo import __version__
4+
15
from ..renderers import BaseXMLRenderer
26

37

@@ -7,6 +11,7 @@ def render_document(self, xml, data):
711
xml.startElement('rdmo', {
812
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
913
'version': self.version,
14+
'required': self.required,
1015
'created': self.created
1116
})
1217
self.render_text_element(xml, 'text', {}, data['text'])
@@ -17,6 +22,8 @@ def test_render():
1722
renderer = TestRenderer()
1823
xml = renderer.render({'text': 'test'})
1924
assert '<text>test</text>' in xml
25+
assert f'version="{__version__}"' in xml
26+
assert f'required="{settings.EXPORT_MIN_REQUIRED_VERSION}"' in xml
2027

2128

2229
def test_render_ascii_code():

Diff for: rdmo/core/xml.py

+33-14
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,38 @@ def validate_root(root: Optional[xmlElement]) -> Tuple[bool, Optional[str]]:
4545

4646

4747
def validate_and_get_xml_version_from_root(root: xmlElement) -> Tuple[Optional[Version], list]:
48-
unparsed_root_version = root.attrib.get('version') or LEGACY_RDMO_XML_VERSION
49-
root_version, rdmo_version = parse(unparsed_root_version), parse(RDMO_INSTANCE_VERSION)
50-
if root_version > rdmo_version:
51-
logger.info('Import failed version validation (%s > %s)', root_version, rdmo_version)
52-
errors = [
53-
_('This RDMO XML file does not have a valid version number.'),
54-
f'XML Version ({root_version}) is greater than RDMO instance version {rdmo_version}'
55-
]
48+
rdmo_version = parse(RDMO_INSTANCE_VERSION)
49+
50+
# Extract version attributes from the XML root
51+
unparsed_required_version = root.attrib.get('required') # New required version field
52+
unparsed_root_version = root.attrib.get('version') or LEGACY_RDMO_XML_VERSION # Fallback to legacy default
53+
54+
# Validate the 'required' attribute if it exists
55+
if unparsed_required_version:
56+
try:
57+
required_version = parse(unparsed_required_version)
58+
except ValueError:
59+
logger.info('Import failed: Invalid "required" format in XML (%s)', unparsed_required_version)
60+
errors = [_('The "required" attribute in this RDMO XML file is not a valid version.')]
61+
return None, errors
62+
63+
if required_version > rdmo_version:
64+
logger.info('Import failed: Required version (%s) > RDMO instance version (%s)', required_version,
65+
rdmo_version)
66+
errors = [
67+
_('This RDMO XML file requires a newer RDMO version to be imported.'),
68+
f'Required version: {required_version}, Current version: {rdmo_version}.'
69+
]
70+
return None, errors
71+
72+
# Fallback to validate the legacy 'version' field
73+
try:
74+
xml_version = parse(unparsed_root_version)
75+
return xml_version, []
76+
except ValueError:
77+
logger.info('Import failed: Invalid "version" format in XML (%s)', unparsed_root_version)
78+
errors = [_('The "version" attribute in this RDMO XML file is not a valid version.')]
5679
return None, errors
57-
return root_version, []
5880

5981

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

119141
# step 3.1.1: validate the legacy elements
120-
legacy_errors = validate_legacy_elements(elements, parse(root.attrib.get('version', LEGACY_RDMO_XML_VERSION)))
142+
legacy_errors = validate_legacy_elements(elements, root_version)
121143
if legacy_errors:
122144
errors.extend(legacy_errors)
123145
return OrderedDict(), errors
124146

125147
# step 4: convert elements from previous versions
126-
elements = convert_elements(elements, parse(root.attrib.get('version', LEGACY_RDMO_XML_VERSION)))
148+
elements = convert_elements(elements, root_version)
127149

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

233255

234256
def convert_elements(elements, version: Version):
235-
if not isinstance(version, Version):
236-
raise TypeError('Version should be of parsed version type.')
237-
238257
if version < parse('2.0.0'):
239258
validate_pre_conversion_for_missing_key_in_legacy_elements(elements, version)
240259
elements = convert_legacy_elements(elements)

Diff for: rdmo/domain/renderers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def render_document(self, xml, attributes):
99
xml.startElement('rdmo', {
1010
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
1111
'version': self.version,
12+
'required': self.required,
1213
'created': self.created
1314
})
1415
for attribute in attributes:

Diff for: rdmo/options/renderers/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def render_document(self, xml, optionsets):
1212
xml.startElement('rdmo', {
1313
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
1414
'version': self.version,
15+
'required': self.required,
1516
'created': self.created
1617
})
1718
for optionset in optionsets:
@@ -25,6 +26,7 @@ def render_document(self, xml, options):
2526
xml.startElement('rdmo', {
2627
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
2728
'version': self.version,
29+
'required': self.required,
2830
'created': self.created
2931
})
3032
for option in options:

Diff for: rdmo/projects/renderers.py

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def render_document(self, xml, project):
77
xml.startElement('project', {
88
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
99
'version': self.version,
10+
'required': self.required,
1011
'created': self.created
1112
})
1213
self.render_text_element(xml, 'title', {}, project['title'])

Diff for: rdmo/questions/renderers/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def render_document(self, xml, catalogs):
2020
xml.startElement('rdmo', {
2121
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
2222
'version': self.version,
23+
'required': self.required,
2324
'created': self.created
2425
})
2526
for catalog in catalogs:
@@ -35,6 +36,7 @@ def render_document(self, xml, sections):
3536
xml.startElement('rdmo', {
3637
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
3738
'version': self.version,
39+
'required': self.required,
3840
'created': self.created
3941
})
4042
for section in sections:
@@ -49,6 +51,7 @@ def render_document(self, xml, pages):
4951
xml.startElement('rdmo', {
5052
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
5153
'version': self.version,
54+
'required': self.required,
5255
'created': self.created
5356
})
5457
for page in pages:
@@ -63,6 +66,7 @@ def render_document(self, xml, questionsets):
6366
xml.startElement('rdmo', {
6467
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
6568
'version': self.version,
69+
'required': self.required,
6670
'created': self.created
6771
})
6872
for questionset in questionsets:
@@ -77,6 +81,7 @@ def render_document(self, xml, questions):
7781
xml.startElement('rdmo', {
7882
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
7983
'version': self.version,
84+
'required': self.required,
8085
'created': self.created
8186
})
8287
for question in questions:

Diff for: rdmo/tasks/renderers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def render_document(self, xml, tasks):
1313
xml.startElement('rdmo', {
1414
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
1515
'version': self.version,
16+
'required': self.required,
1617
'created': self.created
1718
})
1819
for task in tasks:

Diff for: rdmo/views/renderers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def render_document(self, xml, views):
99
xml.startElement('rdmo', {
1010
'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
1111
'version': self.version,
12+
'required': self.required,
1213
'created': self.created
1314
})
1415
for view in views:

0 commit comments

Comments
 (0)