From 85b58be15d0fdbecec4bad690a6d849cd88236d4 Mon Sep 17 00:00:00 2001
From: Pierre-Yves David <pierre-yves.david@octobus.net>
Date: Mon, 9 Sep 2024 12:16:03 +0200
Subject: [PATCH] QualifiedSWHID: add a "to_dict" method to help serialization

Before, there was no clean way to feed a qswhid to a json encoder.
---
 swh/model/swhids.py            | 26 +++++++++++++++++---------
 swh/model/tests/test_swhids.py | 18 ++++++++++++++++++
 2 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/swh/model/swhids.py b/swh/model/swhids.py
index ef04b969..d456b40c 100644
--- a/swh/model/swhids.py
+++ b/swh/model/swhids.py
@@ -123,6 +123,9 @@ class _BaseSWHID(Generic[_TObjectType]):
             )
 
     def __str__(self) -> str:
+        return self._format_core_swhid()
+
+    def _format_core_swhid(self) -> str:
         return SWHID_SEP.join(
             [
                 self.namespace,
@@ -291,8 +294,9 @@ class QualifiedSWHID(_BaseSWHID[ObjectType]):
     when the anchor denotes a snapshot, the root directory is the one pointed to by HEAD
     (possibly indirectly), and undefined if such a reference is missing"""
 
+    Lines = Tuple[int, Optional[int]]
     lines = attr.ib(
-        type=Optional[Tuple[int, Optional[int]]],
+        type=Optional[Lines],
         default=None,
         validator=type_validator(),
         converter=_parse_lines_qualifier,
@@ -321,6 +325,17 @@ class QualifiedSWHID(_BaseSWHID[ObjectType]):
                 params={"type": value.object_type.value},
             )
 
+    def to_dict(self) -> Dict[str, Optional[str | bytes | CoreSWHID | Lines]]:
+        """Returns a dictionary version of this QSWHID for json serialization"""
+        return {
+            "swhid": self._format_core_swhid(),
+            "origin": self.origin,
+            "visit": self.visit,
+            "anchor": self.anchor,
+            "path": self.path,
+            "lines": self.lines,
+        }
+
     def qualifiers(self) -> Dict[str, str]:
         """Returns URL-escaped qualifiers of this SWHID, for use in serialization"""
         origin = self.origin
@@ -350,14 +365,7 @@ class QualifiedSWHID(_BaseSWHID[ObjectType]):
         return {k: v for (k, v) in d.items() if v is not None}
 
     def __str__(self) -> str:
-        swhid = SWHID_SEP.join(
-            [
-                self.namespace,
-                str(self.scheme_version),
-                self.object_type.value,
-                hash_to_hex(self.object_id),
-            ]
-        )
+        swhid = self._format_core_swhid()
         qualifiers = self.qualifiers()
         if qualifiers:
             for k, v in qualifiers.items():
diff --git a/swh/model/tests/test_swhids.py b/swh/model/tests/test_swhids.py
index 4236b8e1..09d503d5 100644
--- a/swh/model/tests/test_swhids.py
+++ b/swh/model/tests/test_swhids.py
@@ -407,6 +407,24 @@ def test_QualifiedSWHID_init(object_type, qualifiers, expected):
         assert QualifiedSWHID.from_string(expected) == swhid
 
 
+@pytest.mark.parametrize(
+    "object_type,qualifiers",
+    [
+        (type_, dict_)
+        for (type_, dict_, str_or_exc) in QSWHID_EXPECTED
+        if isinstance(str_or_exc, str)
+    ],
+)
+def test_QualifiedSWHID_to_dict(object_type, qualifiers):
+    qswhid = QualifiedSWHID(object_type=object_type, object_id=_x(HASH), **qualifiers)
+    d = qswhid.to_dict()
+    swhid = CoreSWHID.from_string(d.pop("swhid"))
+    other = QualifiedSWHID(
+        object_type=swhid.object_type, object_id=swhid.object_id, **d
+    )
+    assert qswhid == other
+
+
 def test_QualifiedSWHID_hash():
     object_id = _x("94a9ed024d3859793618152ea559a168bbcbb5e2")
 
-- 
GitLab