Merge lp:~sinzui/launchpad/system-bug-comments-api into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 16331
Proposed branch: lp:~sinzui/launchpad/system-bug-comments-api
Merge into: lp:launchpad
Diff against target: 73 lines (+32/-0)
3 files modified
lib/lp/bugs/tests/test_bug_messages_webservice.py (+19/-0)
lib/lp/services/messages/interfaces/message.py (+9/-0)
lib/lp/services/messages/model/message.py (+4/-0)
To merge this branch: bzr merge lp:~sinzui/launchpad/system-bug-comments-api
Reviewer Review Type Date Requested Status
Brad Crittenden (community) Approve
Review via email: mp+137308@code.launchpad.net

Commit message

Do not make links to a message's parent message.

Description of the change

A bug message is accessible over the API via two approaches. Iterating
over bug.messages or directly loading the message via its URL
(eg, +bug/1004834/comments/2). The latter case can oops with
NoCanonicalUrl (OOPS-2f0df95d0ba20796d46cfe2a1b3c3fa1) because the parent
message is not one of the bug's messages, it it does not have an index number.
bug.message returns IIndexedMessage which sets the parent to None to enforce
non-threaded messaging in bugs, but the latter case an IMessage which is
threaded. Both IIndexedMessage and IMessage claim to be bug messages, but
we know that the DB is storing all messages in the table.

--------------------------------------------------------------------

RULES

    Pre-implementation: no one
    * IMessage was exported as an accident of exporting bug messages, and
      they are now permanently in the API. They claim to just be bug messages
      as can be seen by their URL.
    * Since bug messages are not threaded, and we cannot unexport the
      parent attribute, we can follow IIndexedMessage's example by ensuring
      None is the value returned.
    * Export a replacement parent attr on the API that retrofits the beta
      api to return None.

QA

    * visit https://api.qastaging.launchpad.net/devel/ubuntu/+source/request-tracker3.8/+bug/1004834/comments/2
    * Verify the page loads.

LoC

    I have a 16,000 line credit this week.

LINT

    lib/lp/bugs/tests/test_bug_messages_webservice.py
    lib/lp/services/messages/interfaces/message.py
    lib/lp/services/messages/model/message.py

TEST

    ./bin/test -vvc -t MessageTraversal lp.bugs.tests.test_bug_messages_webservice

IMPLEMENTATION

I added getAPIParent to IMessage and exported it using accessor_for(parent)
to ensure the API never sees a parent message.
    lib/lp/bugs/tests/test_bug_messages_webservice.py
    lib/lp/services/messages/interfaces/message.py
    lib/lp/services/messages/model/message.py

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Hi Curtis,

* typo: representtion

* typo: s/it is might/it might

Otherwise it looks good. Thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/tests/test_bug_messages_webservice.py'
--- lib/lp/bugs/tests/test_bug_messages_webservice.py 2012-09-18 18:36:09 +0000
+++ lib/lp/bugs/tests/test_bug_messages_webservice.py 2012-11-30 20:14:21 +0000
@@ -50,6 +50,25 @@
50 messages,50 messages,
51 expected_messages)51 expected_messages)
5252
53 def test_message_with_parent(self):
54 # The API exposes the parent attribute IMessage that is hidden by
55 # IIndexedMessage. The representation cannot make a link to the
56 # parent message because it might switch to another context
57 # object that is not exposed or the user may not have access to.
58 message_1 = self.factory.makeMessage()
59 message_2 = self.factory.makeMessage()
60 message_2.parent = message_1
61 bug = self.factory.makeBug()
62 bug.linkMessage(message_2)
63 user = self.factory.makePerson()
64 lp_bug = self.wsObject(bug, user)
65 for lp_message in lp_bug.messages:
66 # An IIndexedMessage's representation.
67 self.assertIs(None, lp_message.parent)
68 # An IMessage's representation.
69 lp_message = self.wsObject(message_2, user)
70 self.assertIs(None, lp_message.parent)
71
5372
54class TestSetCommentVisibility(TestCaseWithFactory):73class TestSetCommentVisibility(TestCaseWithFactory):
55 """Tests who can successfully set comment visibility."""74 """Tests who can successfully set comment visibility."""
5675
=== modified file 'lib/lp/services/messages/interfaces/message.py'
--- lib/lp/services/messages/interfaces/message.py 2012-04-27 19:03:32 +0000
+++ lib/lp/services/messages/interfaces/message.py 2012-11-30 20:14:21 +0000
@@ -23,8 +23,11 @@
2323
24from lazr.delegates import delegates24from lazr.delegates import delegates
25from lazr.restful.declarations import (25from lazr.restful.declarations import (
26 accessor_for,
26 export_as_webservice_entry,27 export_as_webservice_entry,
28 export_read_operation,
27 exported,29 exported,
30 operation_for_version,
28 )31 )
29from lazr.restful.fields import (32from lazr.restful.fields import (
30 CollectionField,33 CollectionField,
@@ -108,6 +111,12 @@
108 def __iter__():111 def __iter__():
109 """Iterate over all the message chunks."""112 """Iterate over all the message chunks."""
110113
114 @accessor_for(parent)
115 @export_read_operation()
116 @operation_for_version('beta')
117 def getAPIParent():
118 """Return None because messages are not threaded over the API."""
119
111120
112# Fix for self-referential schema.121# Fix for self-referential schema.
113IMessage['parent'].schema = IMessage122IMessage['parent'].schema = IMessage
114123
=== modified file 'lib/lp/services/messages/model/message.py'
--- lib/lp/services/messages/model/message.py 2012-04-27 19:03:32 +0000
+++ lib/lp/services/messages/model/message.py 2012-11-30 20:14:21 +0000
@@ -163,6 +163,10 @@
163 # interface because it is used as a UI field in MessageAddView163 # interface because it is used as a UI field in MessageAddView
164 content = None164 content = None
165165
166 def getAPIParent(self):
167 """See `IMessage`."""
168 return None
169
166170
167def get_parent_msgids(parsed_message):171def get_parent_msgids(parsed_message):
168 """Returns a list of message ids the mail was a reply to.172 """Returns a list of message ids the mail was a reply to.