[CWEP002 migration] support sync_schema_props_perms for computed relations
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 28 Aug 2014 07:44:29 +0200
changeset 9964 f4a3ee05cf9d
parent 9963 5531f5577b50
child 9965 a8769b752299
[CWEP002 migration] support sync_schema_props_perms for computed relations
hooks/syncschema.py
server/migractions.py
server/schemaserial.py
server/test/datacomputed/migratedapp/schema.py
server/test/datacomputed/schema.py
server/test/unittest_migractions.py
--- a/hooks/syncschema.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/hooks/syncschema.py	Thu Aug 28 07:44:29 2014 +0200
@@ -389,6 +389,21 @@
         # XXX revert changes on database
 
 
+class CWComputedRTypeUpdateOp(MemSchemaOperation):
+    """actually update some properties of a computed relation definition"""
+    rschema = entity = rule = None # make pylint happy
+    old_rule = None
+
+    def precommit_event(self):
+        # update the in-memory schema first
+        self.old_rule = self.rschema.rule
+        self.rschema.rule = self.rule
+
+    def revertprecommit_event(self):
+        # revert changes on in memory schema
+        self.rschema.rule = self.old_rule
+
+
 class CWAttributeAddOp(MemSchemaOperation):
     """an attribute relation (CWAttribute) has been added:
     * add the necessary column
@@ -1046,6 +1061,23 @@
                             values=newvalues)
 
 
+class BeforeUpdateCWComputedRTypeHook(SyncSchemaHook):
+    """check name change, handle final"""
+    __regid__ = 'syncupdatecwcomputedrtype'
+    __select__ = SyncSchemaHook.__select__ & is_instance('CWComputedRType')
+    events = ('before_update_entity',)
+
+    def __call__(self):
+        entity = self.entity
+        check_valid_changes(self._cw, entity)
+        if 'rule' in entity.cw_edited:
+            old, new = entity.cw_edited.oldnewvalue('rule')
+            if old != new:
+                rschema = self._cw.vreg.schema.rschema(entity.name)
+                CWComputedRTypeUpdateOp(self._cw, rschema=rschema,
+                                        entity=entity, rule=new)
+
+
 class AfterDelRelationTypeHook(SyncSchemaHook):
     """before deleting a CWAttribute or CWRelation entity:
     * if this is a final or inlined relation definition, instantiate an
--- a/server/migractions.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/server/migractions.py	Thu Aug 28 07:44:29 2014 +0200
@@ -579,6 +579,9 @@
         """
         subjtype, objtype = str(subjtype), str(objtype)
         rschema = self.fs_schema.rschema(rtype)
+        if rschema.rule:
+            raise ExecutionError('Cannot synchronize a relation definition for a '
+                                 'computed relation (%s)' % rschema)
         reporschema = self.repo.schema.rschema(rschema)
         if (subjtype, rschema, objtype) in self._synchronized:
             return
--- a/server/schemaserial.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/server/schemaserial.py	Thu Aug 28 07:44:29 2014 +0200
@@ -615,9 +615,13 @@
     yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
 
 def updaterschema2rql(rschema, eid):
-    relations, values = rschema_relations_values(rschema)
-    values['x'] = eid
-    yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
+    if rschema.rule:
+        yield ('SET X rule %(r)s WHERE X eid %(x)s',
+               {'x': eid, 'r': unicode(rschema.rule)})
+    else:
+        relations, values = rschema_relations_values(rschema)
+        values['x'] = eid
+        yield 'SET %s WHERE X eid %%(x)s' % ','.join(relations), values
 
 def updaterdef2rql(rdef, eid):
     relations, values = _rdef_values(rdef)
--- a/server/test/datacomputed/migratedapp/schema.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/server/test/datacomputed/migratedapp/schema.py	Thu Aug 28 07:44:29 2014 +0200
@@ -49,3 +49,6 @@
     subject = 'Note'
     object = 'Employee'
 
+
+class whatever(ComputedRelation):
+    rule = 'S employees E, O associates E'
--- a/server/test/datacomputed/schema.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/server/test/datacomputed/schema.py	Thu Aug 28 07:44:29 2014 +0200
@@ -48,3 +48,7 @@
 
 class notes(ComputedRelation):
     rule = 'S employees E, O concerns E'
+
+
+class whatever(ComputedRelation):
+    rule = 'S employees E, O concerns E'
--- a/server/test/unittest_migractions.py	Thu Aug 28 08:02:15 2014 +0200
+++ b/server/test/unittest_migractions.py	Thu Aug 28 07:44:29 2014 +0200
@@ -735,6 +735,26 @@
             mh.cmd_drop_relation_type('notes')
         self.assertNotIn('notes', self.schema)
 
+    def test_computed_relation_sync_schema_props_perms(self):
+        self.assertIn('whatever', self.schema)
+        with self.mh() as (cnx, mh):
+            mh.cmd_sync_schema_props_perms('whatever')
+            self.assertEqual(self.schema['whatever'].rule,
+                             'S employees E, O associates E')
+            self.assertEqual(self.schema['whatever'].objects(), ('Company',))
+            self.assertEqual(self.schema['whatever'].subjects(), ('Company',))
+            self.assertFalse(self.table_sql(mh, 'whatever_relation'))
+
+    def test_computed_relation_sync_schema_props_perms_on_rdef(self):
+        self.assertIn('whatever', self.schema)
+        with self.mh() as (cnx, mh):
+            with self.assertRaises(ExecutionError) as exc:
+                mh.cmd_sync_schema_props_perms(
+                    ('Company', 'whatever', 'Person'))
+        self.assertEqual(str(exc.exception),
+                         'Cannot synchronize a relation definition for a computed '
+                         'relation (whatever)')
+
 
 if __name__ == '__main__':
     unittest_main()