← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/bug-mail-information-type into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/bug-mail-information-type into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #989320 in Launchpad itself: "Bug notification emails need information type header"
  https://bugs.launchpad.net/launchpad/+bug/989320

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/bug-mail-information-type/+merge/103793

== Implementation ==

Add X-Launchpad-Bug-Information-Type header to bug notification emails. Retain deprecated X-Launchpad-Bug-Privacy and X-Launchpad-Bug-Security-Related headers for backwards compatibility with scripts, email filters etc.

Simple changes to BugNotificationBuilder.

== Tests ==

Update the relevant doc tests.
Also, a previous branch to fix bug 967115 removed some tests but left unused code in the test module. This obsolete code was removed as a drive by.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/doc/bugnotification-email.txt
  lib/lp/bugs/doc/bugnotification-sending.txt
  lib/lp/bugs/mail/bugnotificationbuilder.py
  lib/lp/bugs/model/tests/test_bug.py

./lib/lp/bugs/doc/bugnotification-email.txt
     216: want has trailing whitespace.
./lib/lp/bugs/doc/bugnotification-sending.txt
     286: want exceeds 78 characters.
     770: source exceeds 78 characters.

-- 
https://code.launchpad.net/~wallyworld/launchpad/bug-mail-information-type/+merge/103793
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/bug-mail-information-type into lp:launchpad.
=== modified file 'lib/lp/bugs/doc/bugnotification-email.txt'
--- lib/lp/bugs/doc/bugnotification-email.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/bugnotification-email.txt	2012-04-27 02:36:27 +0000
@@ -36,6 +36,7 @@
     ...        IBugDelta,
     ...        IBugSet,
     ...        )
+    >>> from lp.registry.enums import InformationType
     >>> from lp.registry.interfaces.person import IPersonSet
 
 
@@ -93,8 +94,8 @@
 
 New security related bugs are sent with a prominent warning:
 
-    >>> changed = bug_four.setSecurityRelated(
-    ...     True, getUtility(ILaunchBag).user)
+    >>> changed = bug_four.transitionToInformationType(
+    ...     InformationType.UNEMBARGOEDSECURITY, getUtility(ILaunchBag).user)
 
     >>> subject, body = generate_bug_add_email(bug_four)
     >>> subject
@@ -107,9 +108,10 @@
     <BLANKLINE>
     ...
 
-But of course, security related bugs are private by default:
+Security related bugs can be made embargoed:
 
-    >>> bug_four.setPrivate(True, getUtility(ILaunchBag).user)
+    >>> bug_four.transitionToInformationType(
+    ...     InformationType.EMBARGOEDSECURITY, getUtility(ILaunchBag).user)
     True
 
     >>> subject, body = generate_bug_add_email(bug_four)
@@ -224,10 +226,9 @@
     >>> login("steve.alexander@xxxxxxxxxxxxxxx")
 
     >>> edited_bug = getUtility(IBugSet).get(6)
-    >>> edited_bug.setPrivate(True, getUtility(ILaunchBag).user)
+    >>> edited_bug.transitionToInformationType(
+    ...     InformationType.EMBARGOEDSECURITY, getUtility(ILaunchBag).user)
     True
-    >>> changed = edited_bug.setSecurityRelated(
-    ...     True, getUtility(ILaunchBag).user)
     >>> bug_delta = BugDelta(
     ...     bug=edited_bug,
     ...     bugurl="http://www.example.com/bugs/6";,
@@ -251,10 +252,8 @@
 Now we set the bug public, and not security-related and check if the
 e-mail sent changed as well.
 
-    >>> edited_bug.setPrivate(False, getUtility(ILaunchBag).user)
-    True
-    >>> changed = edited_bug.setSecurityRelated(
-    ...     False, getUtility(ILaunchBag).user)
+    >>> changed = edited_bug.transitionToInformationType(
+    ...     InformationType.PUBLIC, getUtility(ILaunchBag).user)
     >>> bug_delta = BugDelta(
     ...     bug=edited_bug,
     ...     bugurl="http://www.example.com/bugs/6";,
@@ -532,6 +531,7 @@
     Sender: bounces@xxxxxxxxxxxxx
     X-Launchpad-Bug: product=firefox; ...; assignee=None;
     X-Launchpad-Bug-Tags: bar foo
+    X-Launchpad-Bug-Information-Type: Embargoed Security
     X-Launchpad-Bug-Private: yes
     X-Launchpad-Bug-Security-Vulnerability: yes
     X-Launchpad-Bug-Commenters: name12

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2012-04-19 21:15:25 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2012-04-27 02:36:27 +0000
@@ -421,11 +421,6 @@
     ...         title='Zero-day on Frobulator',
     ...         information_type=InformationType.EMBARGOEDSECURITY))
 
-    >>> sec_vuln_bug.security_related
-    True
-    >>> sec_vuln_bug.private
-    True
-
     >>> notifications = (
     ...     getUtility(IBugNotificationSet).getNotificationsToSend())
     >>> email_notifications = get_email_notifications(notifications)
@@ -730,66 +725,61 @@
     ...     message.get_all('X-Launchpad-Bug-Tags')
 
 
-The X-Launchpad-Bug-Private header
-----------------------------------
+The X-Launchpad-Bug-Information-Type header
+-------------------------------------------
 
 When a notification is sent out about a bug, the
-X-Launchpad-Bug-Private header shows if the bug is marked as
-private. It can have the value "yes" or "no".
+X-Launchpad-Bug-Information-Type header shows the information type value
+assigned to the bug. For backwards compatibility, the X-Launchpad-Bug-Private
+and X-Launchpad-Bug-Security-Vulnerability headers are also set. These headers
+can have the value "yes" or "no".
 
-    >>> bug_three.private
-    False
+    >>> print bug_three.information_type.title
+    Public
 
     >>> for message in trigger_and_get_email_messages(bug_three):
-    ...     print message['To'], message.get_all('X-Launchpad-Bug-Private')
-    test@xxxxxxxxxxxxx ['no']
+    ...     print '%s %s %s %s' % (
+    ...         message['To'],
+    ...         message.get_all('X-Launchpad-Bug-Private'),
+    ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'),
+    ...         message.get_all('X-Launchpad-Bug-Information-Type'))
+    test@xxxxxxxxxxxxx ['no'] ['no'] ['Public']
 
 Predictably, private bugs are sent with a slightly different header:
 
     >>> with lp_dbuser():
-    ...     bug_three.setPrivate(True, sample_person)
-    True
-    >>> bug_three.private
-    True
-
-    >>> for message in trigger_and_get_email_messages(bug_three):
-    ...     print message['To'], message.get_all('X-Launchpad-Bug-Private')
-    foo.bar@xxxxxxxxxxxxx ['yes']
-    mark@xxxxxxxxxxx ['yes']
-    test@xxxxxxxxxxxxx ['yes']
-
-The X-Launchpad-Bug-Security-Vulnerability header
--------------------------------------------------
-
-When a notification is sent out about a bug, the
-X-Launchpad-Bug-Security-Vulnerability header records if the bug is a
-security vulnerability. It can have the value "yes" or "no".
-
-    >>> bug_three.security_related
-    False
-
-    >>> for message in trigger_and_get_email_messages(bug_three):
-    ...     print message['To'], (
-    ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'))
-    foo.bar@xxxxxxxxxxxxx ['no']
-    mark@xxxxxxxxxxx ['no']
-    test@xxxxxxxxxxxxx ['no']
-
-The presence of the security flag on a bug is, surprise, denoted by a
-simple "yes":
+    ...     bug_three.transitionToInformationType(
+    ...         InformationType.USERDATA, sample_person)
+    True
+    >>> print bug_three.information_type.title
+    User Data
+
+    >>> for message in trigger_and_get_email_messages(bug_three):
+    ...     print '%s %s %s %s' % (
+    ...         message['To'], message.get_all('X-Launchpad-Bug-Private'),
+    ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'),
+    ...         message.get_all('X-Launchpad-Bug-Information-Type'))
+    foo.bar@xxxxxxxxxxxxx ['yes'] ['no'] ['User Data']
+    mark@xxxxxxxxxxx ['yes'] ['no']  ['User Data']
+    test@xxxxxxxxxxxxx ['yes'] ['no']  ['User Data']
+
+Now transition the bug to embargoed security:
 
     >>> with lp_dbuser():
-    ...     bug_three.setSecurityRelated(True, getUtility(ILaunchBag).user)
-    True
-    >>> bug_three.security_related
-    True
+    ...     bug_three.transitionToInformationType(
+    ...         InformationType.EMBARGOEDSECURITY, getUtility(ILaunchBag).user)
+    True
+    >>> print bug_three.information_type.title
+    Embargoed Security
 
     >>> for message in trigger_and_get_email_messages(bug_three):
-    ...     print message['To'], (
-    ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'))
-    foo.bar@xxxxxxxxxxxxx ['yes']
-    mark@xxxxxxxxxxx ['yes']
-    test@xxxxxxxxxxxxx ['yes']
+    ...     print '%s %s %s %s' % (
+    ...         message['To'], message.get_all('X-Launchpad-Bug-Private'),
+    ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'),
+    ...         message.get_all('X-Launchpad-Bug-Information-Type'))
+    foo.bar@xxxxxxxxxxxxx ['yes'] ['yes'] ['Embargoed Security']
+    mark@xxxxxxxxxxx ['yes'] ['yes']  ['Embargoed Security']
+    test@xxxxxxxxxxxxx ['yes'] ['yes']  ['Embargoed Security']
 
 
 The X-Launchpad-Bug-Commenters header

=== modified file 'lib/lp/bugs/mail/bugnotificationbuilder.py'
--- lib/lp/bugs/mail/bugnotificationbuilder.py	2012-01-01 02:58:52 +0000
+++ lib/lp/bugs/mail/bugnotificationbuilder.py	2012-04-27 02:36:27 +0000
@@ -18,6 +18,10 @@
 from zope.component import getUtility
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.registry.enums import (
+    PRIVATE_INFORMATION_TYPES,
+    SECURITY_INFORMATION_TYPES,
+    )
 from lp.services.config import config
 from lp.services.helpers import shortlist
 from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
@@ -110,19 +114,21 @@
             self.common_headers.append(
                 ('X-Launchpad-Bug-Tags', ' '.join(bug.tags)))
 
-        # Add the X-Launchpad-Bug-Private header. This is a simple
-        # yes/no value denoting privacy for the bug.
-        if bug.private:
+        self.common_headers.append(
+            ('X-Launchpad-Bug-Information-Type',
+             bug.information_type.title))
+
+        # For backwards compatibility, we still include the
+        # X-Launchpad-Bug-Private and X-Launchpad-Bug-Security-Vulnerability
+        # headers.
+        if bug.information_type in PRIVATE_INFORMATION_TYPES:
             self.common_headers.append(
                 ('X-Launchpad-Bug-Private', 'yes'))
         else:
             self.common_headers.append(
                 ('X-Launchpad-Bug-Private', 'no'))
 
-        # Add the X-Launchpad-Bug-Security-Vulnerability header to
-        # denote security for this bug. This follows the same form as
-        # the -Bug-Private header.
-        if bug.security_related:
+        if bug.information_type in SECURITY_INFORMATION_TYPES:
             self.common_headers.append(
                 ('X-Launchpad-Bug-Security-Vulnerability', 'yes'))
         else:

=== modified file 'lib/lp/bugs/model/tests/test_bug.py'
--- lib/lp/bugs/model/tests/test_bug.py	2012-04-25 20:12:51 +0000
+++ lib/lp/bugs/model/tests/test_bug.py	2012-04-27 02:36:27 +0000
@@ -32,10 +32,8 @@
     BugSubscriptionInfo,
     )
 from lp.bugs.model.bugnotification import BugNotificationRecipient
-from lp.bugs.scripts.bugnotification import get_email_notifications
 from lp.registry.enums import InformationType
 from lp.registry.interfaces.person import PersonVisibility
-from lp.services.database.lpstorm import IStore
 from lp.testing import (
     feature_flags,
     login_person,
@@ -766,59 +764,6 @@
             set((naked_bugtask.pillar.owner, bug_owner, who)),
             subscribers)
 
-    def _fetch_notifications(self, bug, reason_header):
-        store = IStore(BugNotification)
-        return store.find(
-            BugNotification,
-            BugNotificationRecipient.bug_notificationID == BugNotification.id,
-            BugNotificationRecipient.reason_header == reason_header,
-            BugNotification.bug == bug,
-            BugNotification.status == BugNotificationStatus.PENDING,
-            BugNotification.date_emailed == None)
-
-    def _check_email_content(self, message, expected_headers,
-                             expected_body_text):
-        # Ensure that the email header values and message body text is as
-        # expected.
-        headers = {}
-        for header in ['X-Launchpad-Message-Rationale',
-                       'X-Launchpad-Bug-Security-Vulnerability',
-                       'X-Launchpad-Bug-Private']:
-            if message[header]:
-                headers[header] = message[header]
-        self.assertEqual(expected_headers, headers)
-        body_text = message.get_payload(decode=True)
-        self.assertTrue(expected_body_text in body_text)
-
-    def _check_notifications(self, bug, expected_recipients,
-                             expected_body_text, expected_reason_body,
-                             is_private, is_security_related, role):
-        # Ensure that the content of the pending email notifications is
-        # correct.
-        notifications = self._fetch_notifications(bug, role)
-        actual_recipients = []
-        email_notifications = get_email_notifications(notifications)
-        for bug_notifications, omitted, messages in email_notifications:
-            for message in messages:
-                expected_headers = {
-                    'X-Launchpad-Bug-Private':
-                        'yes' if is_private else 'no',
-                    'X-Launchpad-Bug-Security-Vulnerability':
-                        'yes' if is_security_related else 'no',
-                    'X-Launchpad-Message-Rationale': role,
-                }
-                self._check_email_content(
-                    message, expected_headers, expected_body_text)
-            expected_reason_header = role
-            for notification in bug_notifications:
-                for recipient in notification.recipients:
-                    self.assertEqual(
-                        expected_reason_header, recipient.reason_header)
-                    self.assertEqual(
-                        expected_reason_body, recipient.reason_body)
-                    actual_recipients.append(recipient.person)
-        self.assertContentEqual(expected_recipients, actual_recipients)
-
     def test_structural_bug_supervisor_becomes_direct_on_private(self):
         # If a bug supervisor has a structural subscription to the bug, and
         # the bug is marked as private, the supervisor should get a direct


Follow ups