[schema] Use hook instead of adding composite=True on seda_data_object_reference_id
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 07 Nov 2017 12:13:32 +0100
changeset 2870 c463fd270e81
parent 2868 9a71e061a886
child 2871 20d1509fb15a
[schema] Use hook instead of adding composite=True on seda_data_object_reference_id Because when this is not a simplified profile, we don't want the "auto-deletion" behaviour, ie deleting the data object when its reference is deleted. But since we still want this for simplified profiles, reimplement this behaviour using hook+op. Removing composite marker also implies to add/change some setup in various places controlled by the SEDA compound graph, which only consider composite relation. Add individual explanation as comment for posterity. At some point it would probably be desirable to allow *additional relations to follow* in the compound graph (we can only specify relations to filter out for now). Closes #17113413
cubicweb_seda/entities/__init__.py
cubicweb_seda/hooks.py
cubicweb_seda/migration/0.12.0_Any.py
cubicweb_seda/schema/__init__.py
test/test_schema.py
--- a/cubicweb_seda/entities/__init__.py	Tue Nov 07 11:44:09 2017 +0100
+++ b/cubicweb_seda/entities/__init__.py	Tue Nov 07 12:13:32 2017 +0100
@@ -1,4 +1,4 @@
-# copyright 2016-2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr -- mailto:contact@logilab.fr
 #
 # This program is free software: you can redistribute it and/or modify it under
@@ -172,6 +172,9 @@
     __select__ = is_instance('SEDAArchiveUnit')
     rtype = 'clone_of'
     skiprtypes = ('container',)
+    # this relation isn't composite but it should be followed for cloning since
+    # it's an intra-container relation
+    follow_relations = [('seda_data_object_reference_id', 'subject')]
 
     def clone_into(self, clone):
         """Recursivily clone the container graph of this entity into `clone`."""
@@ -208,7 +211,7 @@
         assert cls
         vreg.register(cls)
         if etype in ('SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
-            # control parent relation order
+            # insert seda_data_object_reference_id as potential parent,
+            # necessary in case of data object in a component archive unit
             cls.parent_relations = list(cls.parent_relations)
-            if cls.parent_relations[0][0] == 'seda_data_object_reference_id':
-                cls.parent_relations = cls.parent_relations[::-1]
+            cls.parent_relations.append(('seda_data_object_reference_id', 'object'))
--- a/cubicweb_seda/hooks.py	Tue Nov 07 11:44:09 2017 +0100
+++ b/cubicweb_seda/hooks.py	Tue Nov 07 12:13:32 2017 +0100
@@ -279,6 +279,34 @@
             self.do_ref.cw_set(user_cardinality=self.do.user_cardinality)
 
 
+class SimplifiedProfileDelRefROHook(hook.Hook):
+    """Hook triggering an operation to delete the data object associated to a
+    reference when the later is deleted from a simplified profile.
+    """
+    __regid__ = 'seda.doref.del'
+    __select__ = hook.Hook.__select__ & hook.match_rtype('seda_data_object_reference_id')
+    events = ('after_delete_relation',)
+
+    def __call__(self):
+        ref = self._cw.entity_from_eid(self.eidfrom)
+        bdo = self._cw.entity_from_eid(self.eidto)
+        if ref.cw_etype == 'SEDADataObjectReference' and bdo.cw_etype == 'SEDABinaryDataObject':
+            container = bdo.seda_binary_data_object[0] if bdo.seda_binary_data_object else None
+            if container and container.simplified_profile:
+                SimplifiedProfileDelRefROOp(self._cw, do_ref=ref, do=bdo)
+
+
+class SimplifiedProfileDelRefROOp(hook.Operation):
+    """Delete data object whose former reference has been deleted in the
+    transaction. Expected to be run only if the profile is a simplified
+    profile.
+    """
+
+    def precommit_event(self):
+        if self.cnx.deleted_in_transaction(self.do_ref.eid):
+            self.do.cw_delete()
+
+
 class CheckProfileSEDACompatiblityOp(hook.DataOperationMixIn, hook.LateOperation):
     """Data operation that will check compatibility of a SEDA profile upon modification.
 
@@ -488,5 +516,10 @@
         for rtype, role in parent_rdefs:
             if SEDA_PARENT_RTYPES.setdefault(rtype, role) != role:
                 raise Exception('inconsistent relation', rtype, role)
+    # manually add seda_data_object_reference_id since it's not composite and as
+    # such not considered as part of the graph, but it has to be watched by hook
+    # triggering the operation to set the container for the case of component
+    # archive unit
+    SEDA_PARENT_RTYPES['seda_data_object_reference_id'] = 'object'
     for rdef, role in iter_all_rdefs(vreg.schema, 'SEDAArchiveTransfer'):
         ON_COMMIT_ADD_RELATIONS.add(str(rdef.rtype))
--- a/cubicweb_seda/migration/0.12.0_Any.py	Tue Nov 07 11:44:09 2017 +0100
+++ b/cubicweb_seda/migration/0.12.0_Any.py	Tue Nov 07 12:13:32 2017 +0100
@@ -10,3 +10,4 @@
 
 sync_schema_props_perms('seda_mime_type_to')
 sync_schema_props_perms('seda_format_id_to')
+sync_schema_props_perms('seda_data_object_reference_id')
--- a/cubicweb_seda/schema/__init__.py	Tue Nov 07 11:44:09 2017 +0100
+++ b/cubicweb_seda/schema/__init__.py	Tue Nov 07 12:13:32 2017 +0100
@@ -162,11 +162,6 @@
 language_code.constraints[0].max = 7
 
 
-from cubicweb_seda.schema.seda2 import data_object_reference_data_object_reference_id  # noqa
-
-data_object_reference_data_object_reference_id.composite = 'subject'
-
-
 def post_build_callback(schema):
     from cubicweb_seda import seda_profile_container_def, iter_all_rdefs
 
--- a/test/test_schema.py	Tue Nov 07 11:44:09 2017 +0100
+++ b/test/test_schema.py	Tue Nov 07 12:13:32 2017 +0100
@@ -258,6 +258,27 @@
                                                 'delete': (),
                                                 'read': ('managers', 'users', 'guests')})
 
+    def test_reference_to_data_object_is_not_composite(self):
+        self._test_reference_to_data_object_composite(False)
+
+    def test_simplified_reference_to_data_object_is_composite(self):
+        self._test_reference_to_data_object_composite(True)
+
+    def _test_reference_to_data_object_composite(self, simplified):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile',
+                                         simplified_profile=simplified)
+            unit, unit_alt, unit_alt_seq = testutils.create_archive_unit(transfer)
+            bdo = testutils.create_data_object(unit_alt_seq,
+                                               seda_binary_data_object=transfer)
+            cnx.commit()
+            reference = bdo.reverse_seda_data_object_reference_id[0]
+            reference.cw_delete()
+            cnx.commit()
+            self.assertEqual(
+                len(cnx.execute('Any X WHERE X eid %(x)s', {'x': bdo.eid})),
+                0 if simplified else 1)
+
 
 class SecurityTC(CubicWebTC):