Author: astaric
Date: Thu May 30 07:52:22 2013
New Revision: 1487776

URL: http://svn.apache.org/r1487776
Log:
Refs #537: Better handling of errors in bhrelations web ui.

Added:
    bloodhound/trunk/bloodhound_relations/bhrelations/tests/web_ui.py
Modified:
    bloodhound/trunk/bloodhound_relations/bhrelations/api.py
    bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html
    bloodhound/trunk/bloodhound_relations/bhrelations/tests/__init__.py
    bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
    bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/api.py?rev=1487776&r1=1487775&r2=1487776&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/api.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/api.py Thu May 30 
07:52:22 2013
@@ -197,6 +197,8 @@ class RelationsSystem(Component):
             self.env, source_resource_instance)
         destination = ResourceIdSerializer.get_resource_id_from_instance(
             self.env, destination_resource_instance)
+        if relation_type not in self.link_ends_map:
+            raise UnknownRelationType(relation_type)
         if when is None:
             when = datetime.now(utc)
         relation = Relation(self.env)
@@ -579,3 +581,6 @@ def unique(seq):
     seen = set()
     return (x for x in seq if x not in seen and not seen.add(x))
 
+
+class UnknownRelationType(ValueError):
+    pass

Modified: 
bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html?rev=1487776&r1=1487775&r2=1487776&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html 
(original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html Thu 
May 30 07:52:22 2013
@@ -37,6 +37,14 @@
 
     <div class="row">
       <div class="span8">
+        <py:if test='error'>
+          <div class="alert alert-error">
+            <span class="label label-important">Oops !</span>
+            Could not create relation.
+            $error
+          </div>
+        </py:if>
+
         <form id="addrelation" class="well form-horizontal" method="post" 
action="">
           <fieldset>
             <legend>Add relation:</legend>
@@ -45,7 +53,7 @@
           <div class="control-group">
             <label class="control-label" for="dest_tid">Related ticket:</label>
             <div class="controls">
-              <input type="text" id="dest_tid" class="span4" name="dest_tid" />
+              <input type="text" id="dest_tid" class="span4" name="dest_tid" 
value="$relation.destination" />
             </div>
           </div>
 
@@ -53,7 +61,7 @@
             <label class="control-label" for="reltype">Relation type:</label>
             <div class="controls">
               <select class="span4" id="reltype" name="reltype">
-                <option py:for="reltype,label in reltypes.iteritems()" 
value="$reltype">$label</option>
+                <option py:for="reltype,label in reltypes.iteritems()" 
value="$reltype" selected="${True if reltype == relation.type else 
None}">$label</option>
               </select>
             </div>
           </div>
@@ -61,7 +69,7 @@
           <div class="control-group">
             <label class="control-label" for="comment">Comment:</label>
             <div class="controls">
-              <textarea name="comment" rows="3" class="span4" />
+              <textarea name="comment" rows="3" 
class="span4">${relation.comment}</textarea>
             </div>
           </div>
 

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/tests/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/tests/__init__.py?rev=1487776&r1=1487775&r2=1487776&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/tests/__init__.py 
(original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/tests/__init__.py Thu May 
30 07:52:22 2013
@@ -25,7 +25,8 @@
 #     import unittest
 import unittest
 
-from bhrelations.tests import api, notification, search, validation
+from bhrelations.tests import api, notification, search, validation, web_ui
+
 
 def suite():
     test_suite = unittest.TestSuite()
@@ -33,7 +34,7 @@ def suite():
     test_suite.addTest(notification.suite())
     test_suite.addTest(search.suite())
     test_suite.addTest(validation.suite())
-
+    test_suite.addTest(web_ui.suite())
     return test_suite
 
 if __name__ == '__main__':

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py?rev=1487776&r1=1487775&r2=1487776&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py Thu May 30 
07:52:22 2013
@@ -27,7 +27,7 @@ from bhrelations.validation import Valid
 from multiproduct.env import ProductEnvironment
 from tests.env import MultiproductTestCase
 from trac.ticket.model import Ticket
-from trac.test import EnvironmentStub, Mock
+from trac.test import EnvironmentStub, Mock, MockPerm
 from trac.core import TracError
 from trac.util.datefmt import utc
 
@@ -79,6 +79,7 @@ class BaseApiApiTestCase(MultiproductTes
         self.req = Mock(href=self.env.href, authname='anonymous', tz=utc,
                         args=dict(action='dummy'),
                         locale=locale_en, lc_time=locale_en)
+        self.req.perm = MockPerm()
         self.relations_system = RelationsSystem(self.env)
         self._upgrade_env()
 

Added: bloodhound/trunk/bloodhound_relations/bhrelations/tests/web_ui.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/tests/web_ui.py?rev=1487776&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/tests/web_ui.py (added)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/tests/web_ui.py Thu May 
30 07:52:22 2013
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+import unittest
+
+from bhrelations.web_ui import RelationManagementModule
+from bhrelations.tests.api import BaseApiApiTestCase
+
+
+class RelationManagementModuleTestCase(BaseApiApiTestCase):
+    def setUp(self):
+        BaseApiApiTestCase.setUp(self)
+        ticket_id = self._insert_ticket(self.env, "Foo")
+        args=dict(action='add', id=ticket_id, dest_tid='', reltype='', 
comment='')
+        self.req.method = 'GET',
+        self.req.args['id'] = ticket_id
+
+    def test_can_process_empty_request(self):
+        data = self.process_request()
+
+        self.assertSequenceEqual(data['relations'], [])
+        self.assertEqual(len(data['reltypes']), 11)
+
+    def test_handles_missing_ticket_id(self):
+        self.req.method = "POST"
+        self.req.args['add'] = 'add'
+
+        data = self.process_request()
+
+        self.assertIn("Invalid ticket", data["error"])
+
+    def test_handles_invalid_ticket_id(self):
+        self.req.method = "POST"
+        self.req.args['add'] = 'add'
+        self.req.args['dest_tid'] = 'no such ticket'
+
+        data = self.process_request()
+
+        self.assertIn("Invalid ticket", data["error"])
+
+    def test_handles_missing_relation_type(self):
+        t2 = self._insert_ticket(self.env, "Bar")
+        self.req.method = "POST"
+        self.req.args['add'] = 'add'
+        self.req.args['dest_tid'] = str(t2)
+
+        data = self.process_request()
+
+        self.assertIn("Unknown relation type", data["error"])
+
+    def test_handles_invalid_relation_type(self):
+        t2 = self._insert_ticket(self.env, "Bar")
+        self.req.method = "POST"
+        self.req.args['add'] = 'add'
+        self.req.args['dest_tid'] = str(t2)
+        self.req.args['reltype'] = 'no such relation'
+
+        data = self.process_request()
+
+        self.assertIn("Unknown relation type", data["error"])
+
+    def test_shows_relation_that_was_just_added(self):
+        t2 = self._insert_ticket(self.env, "Bar")
+        self.req.method = "POST"
+        self.req.args['add'] = 'add'
+        self.req.args['dest_tid'] = str(t2)
+        self.req.args['reltype'] = 'dependson'
+
+        data = self.process_request()
+
+        self.assertEqual(len(data["relations"]), 1)
+
+    def process_request(self):
+        url, data, x = RelationManagementModule(self.env).process_request(
+            self.req)
+        return data
+
+
+def suite():
+    test_suite = unittest.TestSuite()
+    test_suite.addTest(unittest.makeSuite(RelationManagementModuleTestCase, 
'test'))
+    return test_suite
+
+if __name__ == '__main__':
+    unittest.main()

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py?rev=1487776&r1=1487775&r2=1487776&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py Thu May 30 
07:52:22 2013
@@ -35,8 +35,9 @@ from trac.web import IRequestHandler
 from trac.web.chrome import ITemplateProvider, add_warning
 
 from bhrelations.api import RelationsSystem, ResourceIdSerializer, \
-    TicketRelationsSpecifics
+    TicketRelationsSpecifics, UnknownRelationType
 from bhrelations.model import Relation
+from bhrelations.validation import ValidationError
 
 from multiproduct.model import Product
 from multiproduct.env import ProductEnvironment
@@ -66,38 +67,51 @@ class RelationManagementModule(Component
         req.perm.require('TICKET_VIEW')
         relsys = RelationsSystem(self.env)
 
+        data = {
+            'relation': {},
+        }
         if req.method == 'POST':
             # for modifying the relations TICKET_MODIFY is required for
             # both the source and the destination tickets
             req.perm.require('TICKET_MODIFY')
 
-            if req.args.has_key('remove'):
+            if 'remove' in req.args:
                 rellist = req.args.get('sel')
                 if rellist:
                     if isinstance(rellist, basestring):
                         rellist = [rellist, ]
                     self.remove_relations(req, rellist)
-            elif req.args.has_key('add'):
-                dest_tid = req.args.get('dest_tid')
-                reltype = req.args.get('reltype')
-                comment = req.args.get('comment')
-                if dest_tid and reltype:
-                    try:
-                        dest_ticket = self.find_ticket(dest_tid)
-                    except ValueError:
-                        raise TracError(_('Invalid ticket id.'))
-
-                    req.perm.require('TICKET_MODIFY', Resource(dest_ticket.id))
-                    relsys.add(ticket, dest_ticket, reltype, comment,
-                        req.authname)
+            elif 'add' in req.args:
+                relation = dict(
+                    destination=req.args.get('dest_tid', ''),
+                    type=req.args.get('reltype', ''),
+                    comment=req.args.get('comment', ''),
+                )
+                try:
+                    dest_ticket = self.find_ticket(relation['destination'])
+                    req.perm.require('TICKET_MODIFY',
+                                     Resource(dest_ticket.id))
+                    relsys.add(ticket, dest_ticket,
+                               relation['type'],
+                               relation['comment'],
+                               req.authname)
+                except NoSuchTicketError:
+                    data['error'] = _('Invalid ticket id.')
+                except UnknownRelationType:
+                    data['error'] = _('Unknown relation type.')
+                except ValidationError as ex:
+                    data['error'] = ex.message
+                if 'error' in data:
+                    data['relation'] = relation
+
             else:
                 raise TracError(_('Invalid operation.'))
 
-        data = {
+        data.update({
             'ticket': ticket,
             'reltypes': relsys.get_relation_types(),
             'relations': self.get_ticket_relations(ticket),
-        }
+        })
         return 'manage.html', data, None
 
     # ITemplateProvider methods
@@ -149,7 +163,7 @@ class RelationManagementModule(Component
                 resource = ResourceIdSerializer.get_resource_by_id(tid)
                 ticket = trs._create_ticket_by_full_id(resource)
             except:
-                raise ValueError
+                raise NoSuchTicketError
         return ticket
 
     def remove_relations(self, req, rellist):
@@ -164,3 +178,6 @@ class RelationManagementModule(Component
                 add_warning(req,
                     _('Not enough permissions to remove relation "%s"' % 
relid))
 
+
+class NoSuchTicketError(ValueError):
+    pass


Reply via email to