← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/explode-spec-exploder into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/explode-spec-exploder into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/explode-spec-exploder/+merge/98560

Remove the obsolete spec wiki notification exploder. As described in bug #54005, the wikis stopped sending mailing to it nearly 2 years ago.
-- 
https://code.launchpad.net/~wgrant/launchpad/explode-spec-exploder/+merge/98560
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/explode-spec-exploder into lp:launchpad.
=== removed file 'lib/lp/blueprints/doc/spec-mail-exploder.txt'
--- lib/lp/blueprints/doc/spec-mail-exploder.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/blueprints/doc/spec-mail-exploder.txt	1970-01-01 00:00:00 +0000
@@ -1,270 +0,0 @@
-The Spec Mail Exploder
-======================
-
-There's an address, notifications@xxxxxxxxxxxxxxxxxxx, which looks at
-all incoming email to see if it looks like a change notifications from a
-Moin wiki. If it receive such notification, it will look up the relevant
-Launchpad specification, and re-send the mail to the spec's related
-people.
-
-First, let's take a look at how a Moin change notification looks like:
-
-    >>> from lp.services.mail.tests.helpers import read_test_message
-    >>> moin_change = read_test_message('moin-change.txt')
-
-It contains some headers, which are of no use to us, and the body is
-base64 encoded.
-
-    >>> print str(moin_change) #doctest: -NORMALIZE_WHITESPACE
-    From webmaster@xxxxxxxxxx Mon Mar 20 10:31:59 2006
-    Return-path: <webmaster@xxxxxxxxxx>
-    Content-Type: text/plain; charset="utf-8"
-    MIME-Version: 1.0
-    Content-Transfer-Encoding: base64
-    From: Launchpad Wiki <webmaster@xxxxxxxxxx>
-    To: Launchpad Wiki <webmaster@xxxxxxxxxx>
-    Date: Mon, 20 Mar 2006 10:26:28 -0000
-    Message-ID: <20060320102628.17391.34951@xxxxxxxxxxxxxxxxx>
-    Subject: [Launchpad Wiki] Update of "MediaIntegrityCheck" by ...
-    Status: RO
-    Content-Length: 1220
-    <BLANKLINE>
-    RGVhciBXaWtpIHVzZXIsDQo...
-
-Let's take a look at the decode body, since we'll have to use it in
-order to extract any useful information about the wiki page.
-
-    >>> print moin_change.get_payload(decode=True)
-    Dear Wiki user,
-    <BLANKLINE>
-    You have subscribed to a wiki page or wiki category on "Ubuntu Wiki"
-    for change notification.
-    <BLANKLINE>
-    The following page has been changed by JamesHenstridge:
-    https://wiki.ubuntu.com/MediaIntegrityCheck
-    <BLANKLINE>
-    The comment on the change is:
-    A comment about the change
-    <BLANKLINE>
-    -----------------------------------------------------------------...
-    ...
-
-
-The Mail Handler
-----------------
-
-The mail handler that handles mail on notifications@xxxxxxxxxxxxxxxxxxx
-is BlueprintHandler.
-
-    >>> from lp.services.config import config
-    >>> from lp.services.mail.handlers import mail_handlers
-    >>> handler = mail_handlers.get(config.launchpad.specs_domain)
-    >>> handler is not None
-    True
-
-It has a helper method to help associate a URL with a specification. We
-use this method instead of simply ISpecificationSet.getByURL() since the
-Ubuntu wikis are special, they have more than one domain name mapping to
-the same wiki. So if we start with the correct URL, of course we get the
-specification with that URL.
-
-    >>> spec = handler._getSpecByURL(
-    ...     'https://wiki.ubuntu.com/MediaIntegrityCheck')
-    >>> spec.specurl
-    u'https://wiki.ubuntu.com/MediaIntegrityCheck'
-
-But if someone would edit the same wiki page on wiki.edubuntu.org, we
-would want the same spec's subscribers to be notified about it.
-
-    >>> spec = handler._getSpecByURL(
-    ...     'https://wiki.edubuntu.org/MediaIntegrityCheck')
-    >>> spec.specurl
-    u'https://wiki.ubuntu.com/MediaIntegrityCheck'
-
-And the same with wiki.kubuntu.org URLs.
-
-    >>> spec = handler._getSpecByURL(
-    ...     'https://wiki.kubuntu.org/MediaIntegrityCheck')
-    >>> spec.specurl
-    u'https://wiki.ubuntu.com/MediaIntegrityCheck'
-
-If no matching specification is found, None is returned.
-
-    >>> spec = handler._getSpecByURL(
-    ...     'https://wiki.kubuntu.org/NonExistant')
-    >>> spec is None
-    True
-
-We use a logger to see what the handler does:
-
-    >>> from lp.services.scripts import log
-    >>> OLD_LOG_LEVEL = log._log.getEffectiveLevel()
-    >>> log._log.setLevel(10)
-
-Now, if we pass the email to it, we can see that it finds the correct
-spec, and mails it to the related people.
-
-    >>> handler.process(
-    ...     moin_change, 'notifications@xxxxxxxxxxxxxxxxxxx', log=log)
-    True
-
-    >>> log._log.setLevel(OLD_LOG_LEVEL)
-
-    >>> from lp.blueprints.interfaces.specification import ISpecificationSet
-    >>> spec = getUtility(ISpecificationSet).getByURL(
-    ...     'https://wiki.ubuntu.com/MediaIntegrityCheck')
-    >>> spec.notificationRecipientAddresses()
-    ['test@xxxxxxxxxxxxx']
-
-Let's look at the email that got sent:
-
-    >>> import transaction
-    >>> from lp.services.mail import stub
-    >>> transaction.commit()
-    >>> len(stub.test_emails)
-    1
-
-The email got sent to all related people:
-
-    >>> from_addr, to_addrs, raw_message = stub.test_emails.pop()
-    >>> to_addrs == spec.notificationRecipientAddresses()
-    True
-
-We practically bounced the message, we didn't change anything about it,
-except for setting the Sender header to our bounce address:
-
-    >>> import email
-    >>> sent_msg = email.message_from_string(raw_message)
-    >>> sent_body = sent_msg.get_payload(decode=True)
-    >>> sent_body == moin_change.get_payload(decode=True)
-    True
-
-    >>> sent_msg['To'] == moin_change['To']
-    True
-
-    >>> sent_msg['From'] == moin_change['From']
-    True
-
-    >>> sent_msg['Subject'] == moin_change['Subject']
-    True
-
-    >>> sent_msg['Sender'] == config.canonical.bounce_address
-    True
-
-If we get a change notification about a spec which doesn't exist in
-Launchpad, nothing happens.
-
-    >>> log._log.setLevel(10)
-    >>> moin_change = read_test_message('moin-change-nonexistant.txt')
-    >>> handler.process(
-    ...     moin_change, 'notifications@xxxxxxxxxxxxxxxxxxx', log=log)
-    True
-
-    >>> print getUtility(ISpecificationSet).getByURL(
-    ...     'https://wiki.ubuntu.com/NonExistant')
-    None
-
-In order to prevent loops, we set the X-Loop header:
-
-    >>> sent_msg['X-Loop']
-    'notifications@xxxxxxxxxxxxxxxxxxx'
-
-So that if we someone re-sends the notification we sent to us, we'll
-simply emit a warning and drop the email.
-
-    >>> class FakeFileAlias:
-    ...     http_url = 'http://librarian/foo.txt'
-    >>> del sent_msg['Sender']
-    >>> sent_msg['Sender'] = 'webmaster@xxxxxxxxxx'
-    >>> handler.process(
-    ...     sent_msg, 'notifications@xxxxxxxxxxxxxxxxxxx',
-    ...     filealias=FakeFileAlias(), log=log)
-    WARNING...:Got back a notification we sent: http://librarian/foo.txt
-    True
-
-    >>> log._log.setLevel(OLD_LOG_LEVEL)
-
-No emails were sent:
-
-    >>> transaction.commit()
-    >>> len(stub.test_emails)
-    0
-
-To prevent bad things happening if someone subscribes this address to
-specifications for example, we ignore all email that are sent from
-Launchpad:
-
-    >>> del sent_msg['X-Loop']
-    >>> del sent_msg['Sender']
-    >>> sent_msg['Sender'] = config.canonical.bounce_address
-    >>> log._log.setLevel(10)
-    >>> handler.process(
-    ...     sent_msg, 'notifications@xxxxxxxxxxxxxxxxxxx',
-    ...     filealias=FakeFileAlias(), log=log)
-    WARNING...:We received an email from Launchpad: http://librarian/foo.txt
-    True
-
-    >>> log._log.setLevel(OLD_LOG_LEVEL)
-
-Again, no emails were sent:
-
-    >>> transaction.commit()
-    >>> len(stub.test_emails)
-    0
-
-Let's pass a notification from wiki.kubuntu.org regarding
-MediaIntegrityCheck to see that the correct specification will be found,
-and that the notification will be forwarded to the specification's
-subscribers:
-
-    >>> kubuntu_change = read_test_message('moin-change-kubuntu.txt')
-    >>> log._log.setLevel(10)
-    >>> handler.process(
-    ...     kubuntu_change, 'notifications@xxxxxxxxxxxxxxxxxxx', log=log)
-    True
-
-    >>> log._log.setLevel(OLD_LOG_LEVEL)
-
-    >>> transaction.commit()
-    >>> len(stub.test_emails)
-    1
-
-    >>> from_addr, to_addrs, raw_message = stub.test_emails.pop()
-    >>> to_addrs == spec.notificationRecipientAddresses()
-    True
-
-Lastly, let's simulate sending a moin notification, and use handleMail()
-instead to ensure that handler above handles the email. This will make
-sure that the handler is setup properly to handle unknown users, since
-webmaster@xxxxxxxxxx doesn't belong to any Person in Launchpad:
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> getUtility(IPersonSet).getByEmail('webmaster@xxxxxxxxxx') is None
-    True
-
-    >>> moin_change = read_test_message('moin-change.txt')
-    >>> moin_change['X-Launchpad-Original-To'] = "notifications@%s" % (
-    ...     config.launchpad.specs_domain)
-    >>> moin_change['Sender'] = 'webmaster@xxxxxxxxxx'
-
-    >>> from lp.services.mail.incoming import handleMail
-    >>> from lp.services.mail.sendmail import sendmail
-    >>> sendmail(moin_change, bulk=False)
-    '...'
-
-    >>> transaction.commit()
-    >>> handleMail()
-
-    >>> transaction.commit()
-    >>> len(stub.test_emails)
-    1
-
-    >>> from_addr, to_addrs, raw_message = stub.test_emails.pop()
-    >>> to_addrs == spec.notificationRecipientAddresses()
-    True
-
-    >>> sent_msg = email.message_from_string(raw_message)
-    >>> sent_msg['Message-Id'] == moin_change['Message-Id']
-    True
-
-

=== removed file 'lib/lp/blueprints/mail/handler.py'
--- lib/lp/blueprints/mail/handler.py	2012-01-01 02:58:52 +0000
+++ lib/lp/blueprints/mail/handler.py	1970-01-01 00:00:00 +0000
@@ -1,126 +0,0 @@
-# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Handle incoming Blueprints email."""
-
-__metaclass__ = type
-__all__ = [
-    "BlueprintHandler",
-    ]
-
-import re
-from urlparse import urlunparse
-
-from zope.component import getUtility
-from zope.interface import implements
-
-from lp.blueprints.interfaces.specification import ISpecificationSet
-from lp.services.config import config
-from lp.services.mail.helpers import get_main_body
-from lp.services.mail.interfaces import IMailHandler
-from lp.services.mail.sendmail import sendmail
-from lp.services.webapp import urlparse
-
-
-MOIN_URL_RE = re.compile(r'(https?://[^ \r\n]+)')
-
-
-def get_spec_url_from_moin_mail(moin_text):
-    """Extract a specification URL from Moin change notification."""
-    if not isinstance(moin_text, basestring):
-        return None
-    match = MOIN_URL_RE.search(moin_text)
-    if match:
-        return match.group(1)
-    else:
-        return None
-
-
-class BlueprintHandler:
-    """Handles emails sent to specs.launchpad.net."""
-
-    implements(IMailHandler)
-
-    allow_unknown_users = True
-
-    _spec_changes_address = re.compile(r'^notifications@.*')
-
-    # The list of hosts where the Ubuntu wiki is located. We could do a
-    # more general solution, but this kind of setup is unusual, and it
-    # will be mainly the Ubuntu and Launchpad wikis that will use this
-    # notification forwarder.
-    UBUNTU_WIKI_HOSTS = [
-        'wiki.ubuntu.com', 'wiki.edubuntu.org', 'wiki.kubuntu.org']
-
-    def _getSpecByURL(self, url):
-        """Returns a spec that is associated with the URL.
-
-        It takes into account that the same Ubuntu wiki is on three
-        different hosts.
-        """
-        scheme, host, path, params, query, fragment = urlparse(url)
-        if host in self.UBUNTU_WIKI_HOSTS:
-            for ubuntu_wiki_host in self.UBUNTU_WIKI_HOSTS:
-                possible_url = urlunparse(
-                    (scheme, ubuntu_wiki_host, path, params, query,
-                     fragment))
-                spec = getUtility(ISpecificationSet).getByURL(possible_url)
-                if spec is not None:
-                    return spec
-        else:
-            return getUtility(ISpecificationSet).getByURL(url)
-
-    def get_spec_url_from_email(self, signed_msg):
-        """Return the first url found in the email body."""
-        mail_body = get_main_body(signed_msg)
-        return get_spec_url_from_moin_mail(mail_body)
-
-    def process(self, signed_msg, to_addr, filealias=None, log=None):
-        """See IMailHandler."""
-        match = self._spec_changes_address.match(to_addr)
-        if not match:
-            # We handle only spec-changes at the moment.
-            return False
-        our_address = "notifications@%s" % config.launchpad.specs_domain
-        # Check for emails that we sent.
-        xloop = signed_msg['X-Loop']
-        if xloop and our_address in signed_msg.get_all('X-Loop'):
-            if log and filealias:
-                log.warning(
-                    'Got back a notification we sent: %s' %
-                    filealias.http_url)
-            return True
-        # Check for emails that Launchpad sent us.
-        if signed_msg['Sender'] == config.canonical.bounce_address:
-            if log and filealias:
-                log.warning('We received an email from Launchpad: %s'
-                            % filealias.http_url)
-            return True
-        # When sending the email, the sender will be set so that it's
-        # clear that we're the one sending the email, not the original
-        # sender.
-        del signed_msg['Sender']
-
-        spec_url = self.get_spec_url_from_email(signed_msg)
-        if spec_url is not None:
-            if log is not None:
-                log.debug('Found a spec URL: %s' % spec_url)
-            spec = self._getSpecByURL(spec_url)
-            if spec is not None:
-                if log is not None:
-                    log.debug('Found a corresponding spec: %s' % spec.name)
-                # Add an X-Loop header, in order to prevent mail loop.
-                signed_msg.add_header('X-Loop', our_address)
-                notification_addresses = spec.notificationRecipientAddresses()
-                if log is not None:
-                    log.debug(
-                        'Sending notification to: %s' %
-                            ', '.join(notification_addresses))
-                sendmail(signed_msg, to_addrs=notification_addresses)
-
-            elif log is not None:
-                log.debug(
-                    "Didn't find a corresponding spec for %s" % spec_url)
-        elif log is not None:
-            log.debug("Didn't find a specification URL")
-        return True

=== removed file 'lib/lp/blueprints/mail/tests/test_handler.py'
--- lib/lp/blueprints/mail/tests/test_handler.py	2012-01-01 02:58:52 +0000
+++ lib/lp/blueprints/mail/tests/test_handler.py	1970-01-01 00:00:00 +0000
@@ -1,83 +0,0 @@
-# Copyright 2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Testing the Blueprint email handler."""
-
-__metaclass__ = type
-
-from testtools.matchers import (
-    Equals,
-    Is,
-    )
-
-from lp.blueprints.mail.handler import (
-    BlueprintHandler,
-    get_spec_url_from_moin_mail,
-    )
-from lp.testing import (
-    TestCase,
-    TestCaseWithFactory,
-    )
-from lp.testing.layers import DatabaseFunctionalLayer
-
-
-class TestGetSpecUrlFromMoinMail(TestCase):
-    """Tests for get_spec_url_from_moin_mail."""
-
-    def test_invalid_params(self):
-        # Only strings and unicode are OK.
-        self.assertThat(get_spec_url_from_moin_mail(None), Is(None))
-        self.assertThat(get_spec_url_from_moin_mail(42), Is(None))
-        self.assertThat(get_spec_url_from_moin_mail(object()), Is(None))
-
-    def test_missing_urls(self):
-        # Strings with missing URLs also return None
-        self.assertThat(
-            get_spec_url_from_moin_mail('nothing here'),
-            Is(None))
-
-    def test_string_contains_url(self):
-        body = """
-            Testing a big string
-
-            An url, http://example.com/foo in a string
-            """
-        self.assertThat(
-            get_spec_url_from_moin_mail(body),
-            Equals('http://example.com/foo'))
-
-    def test_two_urls(self):
-        # Given two urls, only the first is returned.
-        body = """
-            Testing two urls:
-            http://example.com/first
-            http://example.com/second
-            """
-        self.assertThat(
-            get_spec_url_from_moin_mail(body),
-            Equals('http://example.com/first'))
-
-    def test_unicode(self):
-        # Unicode strings and urls are fine.
-        body = u"""
-            Testing unicode:
-            http://example.com/\N{SNOWMAN}
-            """
-        self.assertThat(
-            get_spec_url_from_moin_mail(body),
-            Equals(u'http://example.com/\N{SNOWMAN}'))
-
-
-class TestBlueprintEmailHandler(TestCaseWithFactory):
-
-    layer = DatabaseFunctionalLayer
-
-    def test_find_url_in_multipart_message(self):
-        """Multipart email is common, and we should be able to handle it."""
-        message = self.factory.makeSignedMessage(
-            body="An url http://example.com/foo in the body.",
-            attachment_contents="Nothing here either")
-        handler = BlueprintHandler()
-        self.assertThat(
-            handler.get_spec_url_from_email(message),
-            Equals('http://example.com/foo'))

=== modified file 'lib/lp/blueprints/tests/test_doc.py'
--- lib/lp/blueprints/tests/test_doc.py	2011-12-22 11:12:50 +0000
+++ lib/lp/blueprints/tests/test_doc.py	2012-03-21 02:58:23 +0000
@@ -5,30 +5,13 @@
 Run the doctests and pagetests.
 """
 
-import logging
 import os
 
-from lp.services.mail.tests.test_doc import ProcessMailLayer
 from lp.services.testing import build_test_suite
-from lp.testing.systemdocs import (
-    LayeredDocFileSuite,
-    setUp,
-    tearDown,
-    )
 
 
 here = os.path.dirname(os.path.realpath(__file__))
 
 
-special = {
-    'spec-mail-exploder.txt': LayeredDocFileSuite(
-        "../doc/spec-mail-exploder.txt",
-        setUp=setUp, tearDown=tearDown,
-        layer=ProcessMailLayer,
-        stdout_logging=True,
-        stdout_logging_level=logging.WARNING),
-    }
-
-
 def test_suite():
-    return build_test_suite(here, special)
+    return build_test_suite(here)

=== modified file 'lib/lp/services/mail/handlers.py'
--- lib/lp/services/mail/handlers.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/mail/handlers.py	2012-03-21 02:58:23 +0000
@@ -7,7 +7,6 @@
     ]
 
 from lp.answers.mail.handler import AnswerTrackerHandler
-from lp.blueprints.mail.handler import BlueprintHandler
 from lp.bugs.mail.handler import MaloneHandler
 from lp.code.mail.codehandler import CodeHandler
 from lp.services.config import config
@@ -18,7 +17,6 @@
 
     DEFAULT = (
         (config.launchpad.bugs_domain, MaloneHandler),
-        (config.launchpad.specs_domain, BlueprintHandler),
         (config.answertracker.email_domain, AnswerTrackerHandler),
         # XXX flacoste 2007-04-23 Backward compatibility for old domain.
         # We probably want to remove it in the future.