[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
--- 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))