From abe9a81c5f9647e96a9a9af777c373fbf1fff6cf Mon Sep 17 00:00:00 2001
From: Daniel <d.hornung@indiscale.com>
Date: Thu, 3 Mar 2022 17:23:07 +0100
Subject: [PATCH] ENH: Better error output in case of wrong server XML.

---
 src/caosdb/common/models.py      | 15 ++++++++++++---
 unittests/data/list_in_value.xml |  2 ++
 unittests/test_issues.py         |  8 +++++++-
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py
index ec45dec0..80a6ee11 100644
--- a/src/caosdb/common/models.py
+++ b/src/caosdb/common/models.py
@@ -55,6 +55,7 @@ from caosdb.exceptions import (AmbiguousEntityError, AuthorizationError,
                                EntityDoesNotExistError, EntityError,
                                EntityHasNoDatatypeError, HTTPURITooLongError,
                                MismatchingEntitiesError, QueryNotUniqueError,
+                               ServerConfigurationException,
                                TransactionError, UniqueNamesError,
                                UnqualifiedParentsError,
                                UnqualifiedPropertiesError)
@@ -1195,6 +1196,10 @@ class Entity(object):
 
 
 def _parse_value(datatype, value):
+    """Parse the value (from XML input) according to the given datatype
+    """
+
+    # Simple values
     if value is None:
         return value
 
@@ -1215,12 +1220,12 @@ def _parse_value(datatype, value):
         else:
             raise ValueError("Boolean value was {}.".format(value))
 
+    # Datetime and text are returned as-is
     if datatype in [DATETIME, TEXT]:
         if isinstance(value, str):
             return value
 
     # deal with collections
-
     if isinstance(datatype, str):
         matcher = re.compile(r"^(?P<col>[^<]+)<(?P<dt>[^>]+)>$")
         m = matcher.match(datatype)
@@ -1245,12 +1250,10 @@ def _parse_value(datatype, value):
 
     # This is for a special case, where the xml parser could not differentiate
     # between single values and lists with one element. As
-
     if hasattr(value, "__len__") and len(value) == 1:
         return _parse_value(datatype, value[0])
 
     # deal with references
-
     if isinstance(value, Entity):
         return value
 
@@ -1266,6 +1269,12 @@ def _parse_value(datatype, value):
             # reference via name
 
             return str(value)
+        except TypeError:
+            # deal with invalid XML: List of values without appropriate datatype
+            if isinstance(value, list):
+                raise ServerConfigurationException(
+                    "The server sent an invalid XML: List valued properties must be announced by "
+                    "the datatype.\n" + f"Datatype: {datatype}\nvalue: {value}")
 
 
 def _log_request(request, xml_body=None):
diff --git a/unittests/data/list_in_value.xml b/unittests/data/list_in_value.xml
index c3627cad..0f92610d 100644
--- a/unittests/data/list_in_value.xml
+++ b/unittests/data/list_in_value.xml
@@ -8,3 +8,5 @@
     <Value>1005</Value>
   </Property>
 </Record>
+
+<!-- Note: This XML is invalid, because list-valued Properties must have a LIST-Datatype -->
diff --git a/unittests/test_issues.py b/unittests/test_issues.py
index 84573c79..1e649db4 100644
--- a/unittests/test_issues.py
+++ b/unittests/test_issues.py
@@ -24,10 +24,16 @@ import os
 import lxml
 import caosdb as db
 
+from pytest import raises
+
 
 def test_issue_100():
     """_parse_value() fails for some list-valued content
     """
+
+    # Parse from (invalid) XML file
     filename = os.path.join(os.path.dirname(__file__), "data", "list_in_value.xml")
     xml_el = lxml.etree.parse(filename).getroot()
-    rec = db.common.models._parse_single_xml_element(xml_el)
+    with raises(db.ServerConfigurationException) as exc_info:
+        db.common.models._parse_single_xml_element(xml_el)
+    assert "invalid XML: List valued properties" in exc_info.value.msg
-- 
GitLab