Ver código fonte

Fix and improve version handling; revert to version.

Stefano Cossu 6 anos atrás
pai
commit
1dfad98d38

+ 12 - 1
lakesuperior/api/resource.py

@@ -164,7 +164,7 @@ def get(uid, repr_options={}):
     - incl_children: include children URIs. Default: True.
     - embed_children: Embed full graph of all child resources. Default: False
     """
-    rsrc = LdpFactory.from_stored(uid, repr_options)
+    rsrc = LdpFactory.from_stored(uid, repr_opts=repr_options)
     # Load graph before leaving the transaction.
     rsrc.imr
 
@@ -329,6 +329,17 @@ def delete(uid, soft=True):
     return ret
 
 
+@transaction(True)
+def revert_to_version(uid, ver_uid):
+    """
+    Restore a resource to a previous version state.
+
+    :param str uid: Resource UID.
+    :param str ver_uid: Version UID.
+    """
+    return LdpFactory.from_stored(uid).revert_to_version(ver_uid)
+
+
 @transaction(True)
 def resurrect(uid):
     """

+ 1 - 1
lakesuperior/endpoints/ldp.py

@@ -446,7 +446,7 @@ def patch_version(uid, ver_uid):
     :param str ver_uid: Version UID.
     """
     try:
-        LdpFactory.from_stored(uid).revert_to_version(ver_uid)
+        rsrc_api.revert_to_version(uid, rsrc_uid)
     except ResourceNotExistsError as e:
         return str(e), 404
     except InvalidResourceError as e:

+ 3 - 5
lakesuperior/model/ldp_factory.py

@@ -42,7 +42,7 @@ class LdpFactory:
 
 
     @staticmethod
-    def from_stored(uid, repr_opts={}, **kwargs):
+    def from_stored(uid, ver_label=None, repr_opts={}, strict=True, **kwargs):
         """
         Create an instance for retrieval purposes.
 
@@ -55,12 +55,10 @@ class LdpFactory:
         :param  uid: UID of the instance.
         """
         #logger.info('Retrieving stored resource: {}'.format(uid))
-        imr_urn = nsc['fcres'][uid]
-
-        rsrc_meta = rdfly.get_metadata(uid)
+        rsrc_meta = rdfly.get_metadata(uid, strict=strict)
         #logger.debug('Extracted metadata: {}'.format(
         #        pformat(set(rsrc_meta))))
-        rdf_types = set(rsrc_meta[imr_urn : RDF.type])
+        rdf_types = set(rsrc_meta[nsc['fcres'][uid] : RDF.type])
 
         if LDP_NR_TYPE in rdf_types:
             logger.info('Resource is a LDP-NR.')

+ 27 - 13
lakesuperior/model/ldpr.py

@@ -1,4 +1,5 @@
 import logging
+import re
 
 from abc import ABCMeta
 from collections import defaultdict
@@ -98,6 +99,30 @@ class Ldpr(metaclass=ABCMeta):
     }
     """Predicates to remove when a resource is replaced."""
 
+    _ignore_version_preds = {
+        nsc['fcrepo'].hasParent,
+        nsc['fcrepo'].hasVersions,
+        nsc['fcrepo'].hasVersion,
+        nsc['premis'].hasMessageDigest,
+        nsc['ldp'].contains,
+    }
+    """Predicates that don't get versioned."""
+
+    _ignore_version_types = {
+        nsc['fcrepo'].Binary,
+        nsc['fcrepo'].Container,
+        nsc['fcrepo'].Pairtree,
+        nsc['fcrepo'].Resource,
+        nsc['fcrepo'].Version,
+        nsc['ldp'].BasicContainer,
+        nsc['ldp'].Container,
+        nsc['ldp'].DirectContainer,
+        nsc['ldp'].Resource,
+        nsc['ldp'].RDFSource,
+        nsc['ldp'].NonRDFSource,
+    }
+    """RDF types that don't get versioned."""
+
 
     ## MAGIC METHODS ##
 
@@ -504,19 +529,8 @@ class Ldpr(metaclass=ABCMeta):
         ver_add_gr.add((ver_uri, RDF.type, nsc['fcrepo'].Version))
         for t in self.imr:
             if (
-                t[1] == RDF.type and t[2] in {
-                    nsc['fcrepo'].Binary,
-                    nsc['fcrepo'].Container,
-                    nsc['fcrepo'].Resource,
-                }
-            ) or (
-                t[1] in {
-                    nsc['fcrepo'].hasParent,
-                    nsc['fcrepo'].hasVersions,
-                    nsc['fcrepo'].hasVersion,
-                    nsc['premis'].hasMessageDigest,
-                }
-            ):
+                t[1] == RDF.type and t[2] in self._ignore_version_types
+            ) or t[1] in self._ignore_version_preds:
                 pass
             else:
                 ver_add_gr.add((

+ 48 - 1
tests/api/test_resource_api.py

@@ -46,7 +46,7 @@ def ic_rdf():
 
 
 @pytest.mark.usefixtures('db')
-class TestResourceApi:
+class TestResourceCRUD:
     '''
     Test interaction with the Resource API.
     '''
@@ -361,3 +361,50 @@ class TestResourceApi:
             top_cont_rsrc.uri: nsc['dcterms'].relation:
             nsc['fcres'][target_uid]]
 
+
+
+@pytest.mark.usefixtures('db')
+class TestResourceVersioning:
+    '''
+    Test resource version lifecycle.
+    '''
+    def test_create_version(self):
+        """
+        Create a version snapshot.
+        """
+        uid = '/test_version1'
+        rdf_data = b'<> <http://purl.org/dc/terms/title> "Original title." .'
+        update_str = '''DELETE {
+        <> <http://purl.org/dc/terms/title> "Original title." .
+        } INSERT {
+        <> <http://purl.org/dc/terms/title> "Title #2." .
+        } WHERE {
+        }'''
+        rsrc_api.create_or_replace(uid, rdf_data=rdf_data, rdf_fmt='turtle')
+        ver_uid = rsrc_api.create_version(uid, 'v1').split('fcr:versions/')[-1]
+
+        rsrc_api.update(uid, update_str)
+        current = rsrc_api.get(uid)
+        assert (
+            (current.uri, nsc['dcterms'].title, Literal('Title #2.'))
+            in current.imr)
+
+        v1 = rsrc_api.get_version(uid, ver_uid)
+        assert (
+            (v1.identifier, nsc['dcterms'].title, Literal('Original title.'))
+            in set(v1))
+
+
+    def test_revert_to_version(self):
+        """
+        Test reverting to a previous version.
+
+        Uses assets from previous test.
+        """
+        uid = '/test_version1'
+        ver_uid = 'v1'
+        rsrc_api.revert_to_version(uid, ver_uid)
+        rev = rsrc_api.get(uid)
+        assert (
+            (rev.uri, nsc['dcterms'].title, Literal('Original title.'))
+            in rev.imr)