[urlpublish] RESTPathEvaluator now use vid_from_rset
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 20 Jul 2015 13:55:54 +0200
changeset 10582 bc2f6f0d7433
parent 10581 7846d26ff91d
child 10583 97c88aa7c779
[urlpublish] RESTPathEvaluator now use vid_from_rset This avoid cases where vid may be unexpectedly overwritten. For instance when you define a custom vid for a mime type in VID_BY_MIMETYPE, it will currently be overriden by the URL publisher when you access to /<etype> with the proper Accept header. To do so, a new 'check_table' argument is introduced, which may cause bw compatibility problems if the function has been monkey-patched. Also pep8 a bit the tests. Closes #5705835
web/test/unittest_urlpublisher.py
web/views/__init__.py
web/views/urlpublishing.py
--- a/web/test/unittest_urlpublisher.py	Fri Jul 24 16:19:39 2015 +0200
+++ b/web/test/unittest_urlpublisher.py	Mon Jul 20 13:55:54 2015 +0200
@@ -25,7 +25,7 @@
 from cubicweb.rset import ResultSet
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.fake import FakeRequest
-from cubicweb.web import NotFound, Redirect
+from cubicweb.web import NotFound, Redirect, views
 from cubicweb.web.views.urlrewrite import SimpleReqRewriter
 
 
@@ -69,6 +69,7 @@
             self.assertEqual("Any X,AA,AB ORDERBY AB WHERE X is_instance_of CWEType, "
                              "X modification_date AA, X name AB",
                              rset.printable_rql())
+            self.assertEqual(req.form['vid'], 'sameetypelist')
 
     def test_rest_path_by_attr(self):
         with self.admin_access.web_request() as req:
@@ -91,6 +92,7 @@
                              'X firstname AA, X login AB, X modification_date AC, '
                              'X surname AD, X login "admin"',
                              rset.printable_rql())
+            self.assertEqual(req.form['vid'], 'primary')
 
     def test_rest_path_eid(self):
         with self.admin_access.web_request() as req:
@@ -125,6 +127,15 @@
                              'X title "hell\'o"',
                              rset.printable_rql())
 
+    def test_rest_path_use_vid_from_rset(self):
+        with self.admin_access.web_request(headers={'Accept': 'application/rdf+xml'}) as req:
+            views.VID_BY_MIMETYPE['application/rdf+xml'] = 'rdf'
+            try:
+                ctrl, rset = self.process(req, 'CWEType')
+            finally:
+                views.VID_BY_MIMETYPE.pop('application/rdf+xml')
+            self.assertEqual(req.form['vid'], 'rdf')
+
     def test_rest_path_errors(self):
         with self.admin_access.web_request() as req:
             self.assertRaises(NotFound, self.process, req, 'CWUser/eid/30000')
@@ -141,25 +152,24 @@
             self.assertRaises(NotFound, self.process, req, '1/non_action')
             self.assertRaises(NotFound, self.process, req, 'CWUser/login/admin/non_action')
 
-
     def test_regexp_path(self):
         """tests the regexp path resolution"""
         with self.admin_access.web_request() as req:
             ctrl, rset = self.process(req, 'add/Task')
             self.assertEqual(ctrl, 'view')
             self.assertEqual(rset, None)
-            self.assertEqual(req.form, {'etype' : "Task", 'vid' : "creation"})
+            self.assertEqual(req.form, {'etype': "Task", 'vid': "creation"})
             self.assertRaises(NotFound, self.process, req, 'add/foo/bar')
 
     def test_nonascii_path(self):
         oldrules = SimpleReqRewriter.rules
-        SimpleReqRewriter.rules = [(re.compile('/\w+', re.U), dict(vid='foo')),]
+        SimpleReqRewriter.rules = [(re.compile('/\w+', re.U), dict(vid='foo'))]
         with self.admin_access.web_request() as req:
             try:
                 path = str(FakeRequest().url_quote(u'été'))
                 ctrl, rset = self.process(req, path)
                 self.assertEqual(rset, None)
-                self.assertEqual(req.form, {'vid' : "foo"})
+                self.assertEqual(req.form, {'vid': "foo"})
             finally:
                 SimpleReqRewriter.rules = oldrules
 
--- a/web/views/__init__.py	Fri Jul 24 16:19:39 2015 +0200
+++ b/web/views/__init__.py	Mon Jul 20 13:55:54 2015 +0200
@@ -77,7 +77,7 @@
     #'text/xml': 'xml',
     # XXX rss, owl...
 }
-def vid_from_rset(req, rset, schema):
+def vid_from_rset(req, rset, schema, check_table=True):
     """given a result set, return a view id"""
     if rset is None:
         return 'index'
@@ -90,7 +90,7 @@
         return 'noresult'
     # entity result set
     if not schema.eschema(rset.description[0][0]).final:
-        if need_table_view(rset, schema):
+        if check_table and need_table_view(rset, schema):
             return 'table'
         if nb_rows == 1:
             if req.search_state[0] == 'normal':
--- a/web/views/urlpublishing.py	Fri Jul 24 16:19:39 2015 +0200
+++ b/web/views/urlpublishing.py	Mon Jul 20 13:55:54 2015 +0200
@@ -60,7 +60,7 @@
 from rql import TypeResolverException
 
 from cubicweb import RegistryException
-from cubicweb.web import NotFound, Redirect, component
+from cubicweb.web import NotFound, Redirect, component, views
 
 
 class PathDontMatch(Exception):
@@ -201,18 +201,14 @@
             return self.handle_etype_attr(req, cls, attrname, value)
         return self.handle_etype(req, cls)
 
-    def set_vid_for_rset(self, req, cls, rset):# cls is there to ease overriding
+    def set_vid_for_rset(self, req, cls, rset):  # cls is there to ease overriding
         if rset.rowcount == 0:
             raise NotFound()
-        # we've to set a default vid here, since vid_from_rset may try to use a
-        # table view if fetch_rql include some non final relation
-        if rset.rowcount == 1:
-            req.form.setdefault('vid', 'primary')
-        else: # rset.rowcount >= 1
-            if len(rset.column_types(0)) > 1:
-                req.form.setdefault('vid', 'list')
-            else:
-                req.form.setdefault('vid', 'sameetypelist')
+        if 'vid' not in req.form:
+            # check_table=False tells vid_from_rset not to try to use a table view if fetch_rql
+            # include some non final relation
+            req.form['vid'] = views.vid_from_rset(req, rset, req.vreg.schema,
+                                                  check_table=False)
 
     def handle_etype(self, req, cls):
         rset = req.execute(cls.fetch_rql(req.user))