← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/bugtracker-redhat-fixes into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/bugtracker-redhat-fixes into lp:launchpad.

Commit message:
Fix various failures to sync from Red Hat's Bugzilla instance.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1678486 in Launchpad itself: "Enable watching Red Hat Bugzilla bugs"
  https://bugs.launchpad.net/launchpad/+bug/1678486

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/bugtracker-redhat-fixes/+merge/332559

I'm sure there'll be more; this is just what I noticed when syncing a test batch of 10.  All these seem likely to be common.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/bugtracker-redhat-fixes into lp:launchpad.
=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2017-01-06 22:38:06 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2017-10-20 11:30:23 +0000
@@ -533,20 +533,28 @@
     >>> remote_bug = bug_watch_two.remotebug
     >>> transaction.commit()
 
-    >>> bugzilla.fetchComments(remote_bug, ['2', '4'])
-    >>> displayname, email = bugzilla.getPosterForComment(
-    ...     bug_watch_two.remotebug, '4')
+    >>> bugzilla.fetchComments(remote_bug, ['2', '4', '5'])
+    >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '4')
     >>> print displayname, email
     Ford Prefect ford.prefect@xxxxxxxx
 
-If the author's name as stored in the comment isn't a valid email
-address then the method will return displayname = None and will return
-the whole of the author's name in the email field.
-
-    >>> displayname, email = bugzilla.getPosterForComment(
-    ...     bug_watch_two.remotebug, '2')
-    >>> print displayname, email
-    None trillian
+getPosterForComment() handles situations in which only an email address
+is supplied for the 'user' field by returning None as the user's
+displayname. When this is passed to IPersonSet.ensurePerson() a display
+name will be generated for the user from their email address.
+
+    >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '5')
+    >>> print displayname, email
+    None arthur.dent@xxxxxxxxxxxxxxxxx
+
+getPosterForComment() will also return displayname, email tuples in
+cases where the 'user' field is set to a plain username (e.g. 'foo').
+However, in these cases it is the email address that will be set to
+None.
+
+    >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '2')
+    >>> print displayname, email
+    trillian None
 
 
 getMessageForComment()
@@ -609,20 +617,20 @@
         'id': 1})
 
     >>> comment_id
-    '5'
+    '6'
 
 The comment will be stored on the remote server with the other comments.
 
     >>> bugzilla.xmlrpc_transport.print_method_calls = False
     >>> print sorted(bugzilla.getCommentIds(bug_watch.remotebug))
-    ['1', '3', '5']
+    ['1', '3', '6']
 
     >>> remote_bug = bug_watch.remotebug
     >>> transaction.commit()
 
-    >>> bugzilla.fetchComments(remote_bug, ['5'])
+    >>> bugzilla.fetchComments(remote_bug, ['6'])
     >>> message = bugzilla.getMessageForComment(
-    ...     bug_watch.remotebug, '5', sample_person)
+    ...     bug_watch.remotebug, '6', sample_person)
     >>> print message.text_contents
     This is a new remote comment.
     <BLANKLINE>

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2016-09-21 02:49:42 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2017-10-20 11:30:23 +0000
@@ -460,7 +460,7 @@
         'id': 1})
 
     >>> comment_id
-    '5'
+    '6'
 
 The comment will be stored on the remote server with the other comments.
 
@@ -469,13 +469,13 @@
 
     >>> bugzilla.xmlrpc_transport.print_method_calls = False
     >>> print sorted(bugzilla.getCommentIds(remote_bug))
-    ['1', '3', '5']
+    ['1', '3', '6']
 
     >>> transaction.commit()
 
-    >>> bugzilla.fetchComments(remote_bug, ['5'])
+    >>> bugzilla.fetchComments(remote_bug, ['6'])
     >>> message = bugzilla.getMessageForComment(
-    ...     remote_bug, '5', sample_person)
+    ...     remote_bug, '6', sample_person)
     >>> print message.text_contents
     This is a new remote comment.
     <BLANKLINE>

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla.txt	2016-07-04 17:08:29 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla.txt	2017-10-20 11:30:23 +0000
@@ -307,6 +307,10 @@
     'Invalid'
     >>> external_bugzilla.convertRemoteStatus('CLOSED UPSTREAM').title
     "Won't Fix"
+    >>> external_bugzilla.convertRemoteStatus('CLOSED EOL').title
+    'Expired'
+    >>> external_bugzilla.convertRemoteStatus('CLOSED DEFERRED').title
+    'Expired'
 
 If the status can't be converted an UnknownRemoteStatusError will be
 returned.
@@ -406,6 +410,18 @@
     >>> external_bugzilla.convertRemoteImportance('P1').title
     'Low'
 
+A priority or severity of 'UNSPECIFIED' turns into
+BugTaskImportance.UNDECIDED unless the other field gives us something
+better.
+
+    >>> external_bugzilla.convertRemoteImportance('URGENT UNSPECIFIED').title
+    'Critical'
+    >>> external_bugzilla.convertRemoteImportance(
+    ...     'UNSPECIFIED UNSPECIFIED').title
+    'Undecided'
+    >>> external_bugzilla.convertRemoteImportance('UNSPECIFIED').title
+    'Undecided'
+
 Some bugzillas don't provide a value, resulting in blank strings for
 priority and severity.  We simply leave the importance unknown in this
 case.

=== modified file 'lib/lp/bugs/externalbugtracker/bugzilla.py'
--- lib/lp/bugs/externalbugtracker/bugzilla.py	2017-01-14 07:41:41 +0000
+++ lib/lp/bugs/externalbugtracker/bugzilla.py	2017-10-20 11:30:23 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Bugzilla ExternalBugTracker utility."""
@@ -25,6 +25,7 @@
 from zope.component import getUtility
 from zope.interface import implementer
 
+from lp.app.validators.email import valid_email
 from lp.bugs.externalbugtracker.base import (
     BugNotFound,
     BugTrackerAuthenticationError,
@@ -263,19 +264,22 @@
         'p1': BugTaskImportance.LOW,
         'enhancement': BugTaskImportance.WISHLIST,
         'wishlist': BugTaskImportance.WISHLIST,
+        'unspecified': BugTaskImportance.UNDECIDED,
         }
 
     def convertRemoteImportance(self, remote_importance):
         """See `ExternalBugTracker`."""
         words = remote_importance.lower().split()
-        try:
-            return self._importance_lookup[words.pop()]
-        except KeyError:
-            raise UnknownRemoteImportanceError(remote_importance)
-        except IndexError:
-            return BugTaskImportance.UNKNOWN
-
-        return BugTaskImportance.UNKNOWN
+        importance = BugTaskImportance.UNKNOWN
+        while importance in (
+                BugTaskImportance.UNKNOWN, BugTaskImportance.UNDECIDED):
+            try:
+                importance = self._importance_lookup[words.pop()]
+            except KeyError:
+                raise UnknownRemoteImportanceError(remote_importance)
+            except IndexError:
+                break
+        return importance
 
     _status_lookup_titles = 'Bugzilla status', 'Bugzilla resolution'
     _status_lookup = LookupTree(
@@ -296,6 +300,7 @@
                 ('WONTFIX', 'WILL_NOT_FIX', 'NOTOURBUG', 'UPSTREAM',
                  BugTaskStatus.WONTFIX),
                 ('OBSOLETE', 'INSUFFICIENT_DATA', 'INCOMPLETE', 'EXPIRED',
+                 'EOL', 'DEFERRED',
                  BugTaskStatus.EXPIRED),
                 ('INVALID', 'WORKSFORME', 'NOTABUG', 'CANTFIX',
                  'UNREPRODUCIBLE', 'DUPLICATE',
@@ -839,12 +844,18 @@
         comment = self._bugs[actual_bug_id]['comments'][comment_id]
         display_name, email = parseaddr(comment['author'])
 
-        # If the name is empty then we return None so that
-        # IPersonSet.ensurePerson() can actually do something with it.
-        if not display_name:
-            display_name = None
-
-        return (display_name, email)
+        # If the email isn't valid, return the email address as the
+        # display name (a Launchpad Person will be created with this
+        # name).
+        if not valid_email(email):
+            return email, None
+        # If the display name is empty, set it to None so that it's
+        # useable by IPersonSet.ensurePerson().
+        elif display_name == '':
+            return None, email
+        # Both displayname and email are valid, return both.
+        else:
+            return display_name, email
 
     def getMessageForComment(self, remote_bug_id, comment_id, poster):
         """See `ISupportsCommentImport`."""

=== modified file 'lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt	2017-01-06 22:38:06 +0000
+++ lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt	2017-10-20 11:30:23 +0000
@@ -346,6 +346,13 @@
         text: I appear to have become a perfectly safe penguin.
         time: 2008-06-17 20:28:40
     <BLANKLINE>
+        author: arthur.dent@xxxxxxxxxxxxxxxxx
+        bug_id: 2
+        id: 5
+        is_private: False
+        text: I never could get the hang of Thursdays.
+        time: 2008-06-19 09:30:00
+    <BLANKLINE>
     <BLANKLINE>
 
 Passing a list of comment IDs to Bug.comments will cause it to return
@@ -404,6 +411,9 @@
     <BLANKLINE>
         author: Ford Prefect <ford.prefect@xxxxxxxx>
         id: 4
+    <BLANKLINE>
+        author: arthur.dent@xxxxxxxxxxxxxxxxx
+        id: 5
 
     >>> return_dict = server.Bug.comments(
     ...     {'comment_ids': [1, 4], 'include_fields': ('id', 'author')})
@@ -445,12 +455,12 @@
     >>> bugzilla_transport.auth_cookie = 'open sesame'
     >>> return_dict = server.Bug.add_comment({'id': 1, 'comment': comment})
     >>> return_dict
-    {'id': 5}
+    {'id': 6}
 
 The comment will be stored with the other comments on the remote server.
 
     >>> return_dict = server.Bug.comments({
-    ...     'id': [1], 'comment_ids': [5]})
+    ...     'id': [1], 'comment_ids': [6]})
     >>> comments_dict = return_dict['comments']
 
     >>> for comment_id, comment in comments_dict.items():
@@ -458,10 +468,10 @@
     ...     for comment_key in sorted(comment):
     ...         print "    %s: %s" % (
     ...             comment_key, comment[comment_key])
-    Comment 5:
+    Comment 6:
         author: launchpad
         bug_id: 1
-        id: 5
+        id: 6
         is_private: False
         text: I'm supposed to write something apposite here.
         time: ...

=== modified file 'lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt	2016-09-21 02:49:42 +0000
+++ lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt	2017-10-20 11:30:23 +0000
@@ -451,17 +451,17 @@
     >>> return_dict = server.Launchpad.add_comment(
     ...     {'id': 1, 'comment': comment})
     >>> print return_dict['comment_id']
-    5
+    6
 
 The comment will be stored with the other comments on the remote server.
 
-    >>> return_dict = server.Launchpad.comments({'bug_ids': [1], 'ids': [5]})
+    >>> return_dict = server.Launchpad.comments({'bug_ids': [1], 'ids': [6]})
     >>> bugs_dict = return_dict['bugs']
 
     >>> print_bug_comments(bugs_dict, sort_key='id')
     Bug 1:
         author: launchpad
-        id: 5
+        id: 6
         number: 3
         text: Didn't we have a lovely time the day we went to Bangor?
         time: ...

=== modified file 'lib/lp/bugs/tests/externalbugtracker.py'
--- lib/lp/bugs/tests/externalbugtracker.py	2017-01-06 22:38:06 +0000
+++ lib/lp/bugs/tests/externalbugtracker.py	2017-10-20 11:30:23 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Helper classes for testing ExternalSystem."""
@@ -450,7 +450,7 @@
         }
 
     # Comments are mapped to bug IDs.
-    comment_id_index = 4
+    comment_id_index = 5
     new_comment_time = datetime(2008, 6, 20, 11, 42, 42)
     _bug_comments = {
         1: {
@@ -854,13 +854,20 @@
                 'text': "Bring the passengers to the bridge please Marvin.",
                 'time': datetime(2008, 6, 16, 13, 8, 8),
                 },
-             2: {'author': 'Ford Prefect <ford.prefect@xxxxxxxx>',
+            2: {'author': 'Ford Prefect <ford.prefect@xxxxxxxx>',
                 'bug_id': 2,
                 'id': 4,
                 'is_private': False,
                 'text': "I appear to have become a perfectly safe penguin.",
                 'time': datetime(2008, 6, 17, 20, 28, 40),
                 },
+            3: {'author': 'arthur.dent@xxxxxxxxxxxxxxxxx',
+                'bug_id': 2,
+                'id': 5,
+                'is_private': False,
+                'text': "I never could get the hang of Thursdays.",
+                'time': datetime(2008, 6, 19, 9, 30, 0),
+                },
             },
         }
 


Follow ups