Merge lp:~stevenk/launchpad/information_type-vocab into lp:launchpad

Proposed by Steve Kowalik on 2012-04-11
Status: Merged
Approved by: Steve Kowalik on 2012-04-11
Approved revision: no longer in the source branch.
Merged at revision: 15078
Proposed branch: lp:~stevenk/launchpad/information_type-vocab
Merge into: lp:launchpad
Diff against target: 266 lines (+162/-3)
6 files modified
lib/lp/bugs/browser/bug.py (+27/-1)
lib/lp/bugs/browser/tests/test_bugview.py (+20/-1)
lib/lp/registry/tests/test_information_type_vocabulary.py (+65/-0)
lib/lp/registry/vocabularies.py (+31/-0)
lib/lp/registry/vocabularies.zcml (+7/-1)
lib/lp/services/features/flags.py (+12/-0)
To merge this branch: bzr merge lp:~stevenk/launchpad/information_type-vocab
Reviewer Review Type Date Requested Status
William Grant code 2012-04-11 Approve on 2012-04-11
Review via email: mp+101486@code.launchpad.net

Commit Message

Add a vocabulary for the InformationType enum, which allows us to change what is displayed via feature flags.

Description of the Change

Add a vocabulary for the InformationType enum. I have added two feature flags, one that changes User Data to display as Private, and the other that hides Proprietary. I have pulled in some code into BugView which is tested, but not used yet to make later branches a little smaller.

This branch isn't testable on it's own, it is just setting up plumbing for the upcoming UI branches.

To post a comment you must log in.
William Grant (wgrant) wrote :

25 + cache.objects['information_types'] = [
26 + {'value': term.value, 'description': term.description,
27 + 'name': term.title} for term in InformationTypeVocabulary()]

This seems like a common pattern. Is there a helper? If not, might it be an idea to add one?

28 + cache.objects['private_types'] = [
29 + type.value for type in PRIVATE_INFORMATION_TYPES]

type.value is the integer. Enums are not represented by integers, except at the database layer.

30 + cache.objects['initial_information_type'] = (
31 + IBug(self.context).information_type.value)

Likewise. But isn't this already in LP.cache.context?

40 + @property
41 + def information_type(self):

I'd like a comment stating that this whole mess can be replaced with bug.information_type.title once we switch to showing User Data.

83 + def test_information_type_feature_flag(self):

This could do with a comment and a better name.

115 + def test_getTermByToken(self):
116 + vocab = InformationTypeVocabulary()
117 + term = vocab.getTermByToken('PUBLIC')
118 + self.assertEqual(InformationType.PUBLIC, term.value)
119 + self.assertEqual('PUBLIC', term.token)
120 + self.assertEqual('Public', term.title)
121 + self.assertEqual(
122 + InformationType.PUBLIC.description, term.description)

Do you know about MatchesStructure?

214 + term = SimpleTerm(type, type.name, title)
215 + term.description = description

Does the constructor seriously not take description?

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bug.py'
2--- lib/lp/bugs/browser/bug.py 2012-04-03 06:14:09 +0000
3+++ lib/lp/bugs/browser/bug.py 2012-04-11 11:58:19 +0000
4@@ -95,7 +95,12 @@
5 from lp.bugs.model.structuralsubscription import (
6 get_structural_subscriptions_for_bug,
7 )
8-from lp.registry.enums import InformationType
9+from lp.registry.enums import (
10+ InformationType,
11+ PRIVATE_INFORMATION_TYPES,
12+ )
13+from lp.registry.vocabularies import InformationTypeVocabulary
14+from lp.services.features import getFeatureFlag
15 from lp.services.fields import DuplicateBug
16 from lp.services.librarian.browser import ProxiedLibraryFileAlias
17 from lp.services.mail.mailwrapper import MailWrapper
18@@ -548,6 +553,15 @@
19 all the pages off IBugTask instead of IBug.
20 """
21
22+ def initialize(self):
23+ super(BugView, self).initialize()
24+ cache = IJSONRequestCache(self.request)
25+ cache.objects['information_types'] = [
26+ {'value': term.value, 'description': term.description,
27+ 'name': term.title} for term in InformationTypeVocabulary()]
28+ cache.objects['private_types'] = [
29+ type.name for type in PRIVATE_INFORMATION_TYPES]
30+
31 @cachedproperty
32 def page_description(self):
33 return IBug(self.context).description
34@@ -596,6 +610,18 @@
35 return ProxiedLibraryFileAlias(
36 attachment.libraryfile, attachment).http_url
37
38+ @property
39+ def information_type(self):
40+ # This can be replaced with just a return when the feature flag is
41+ # dropped.
42+ title = self.context.information_type.title
43+ show_userdata_as_private = bool(getFeatureFlag(
44+ 'disclosure.display_userdata_as_private.enabled'))
45+ if (
46+ self.context.information_type == InformationType.USERDATA and
47+ show_userdata_as_private):
48+ return 'Private'
49+ return title
50
51 class BugActivity(BugView):
52
53
54=== modified file 'lib/lp/bugs/browser/tests/test_bugview.py'
55--- lib/lp/bugs/browser/tests/test_bugview.py 2012-01-01 02:58:52 +0000
56+++ lib/lp/bugs/browser/tests/test_bugview.py 2012-04-11 11:58:19 +0000
57@@ -1,4 +1,4 @@
58-# Copyright 2010 Canonical Ltd. This software is licensed under the
59+# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
60 # GNU Affero General Public License version 3 (see the file LICENSE).
61
62 __metaclass__ = type
63@@ -6,6 +6,8 @@
64 from zope.security.proxy import removeSecurityProxy
65
66 from lp.bugs.browser.bug import BugView
67+from lp.registry.enums import InformationType
68+from lp.services.features.testing import FeatureFixture
69 from lp.services.webapp.servers import LaunchpadTestRequest
70 from lp.testing import (
71 login,
72@@ -54,3 +56,20 @@
73 ['patch'],
74 [attachment['attachment'].title
75 for attachment in self.view.patches])
76+
77+ def test_information_type(self):
78+ self.bug.transitionToInformationType(
79+ InformationType.USERDATA, self.bug.owner)
80+ self.assertEqual(
81+ self.bug.information_type.title, self.view.information_type)
82+
83+ def test_userdata_shown_as_private(self):
84+ # When the display_userdata_as_private feature flag is enabled, the
85+ # information_type is shown as 'Private'.
86+ self.bug.transitionToInformationType(
87+ InformationType.USERDATA, self.bug.owner)
88+ feature_flag = {
89+ 'disclosure.display_userdata_as_private.enabled': 'on'}
90+ with FeatureFixture(feature_flag):
91+ view = BugView(self.bug, LaunchpadTestRequest())
92+ self.assertEqual('Private', view.information_type)
93
94=== added file 'lib/lp/registry/tests/test_information_type_vocabulary.py'
95--- lib/lp/registry/tests/test_information_type_vocabulary.py 1970-01-01 00:00:00 +0000
96+++ lib/lp/registry/tests/test_information_type_vocabulary.py 2012-04-11 11:58:19 +0000
97@@ -0,0 +1,65 @@
98+# Copyright 2012 Canonical Ltd. This software is licensed under the
99+# GNU Affero General Public License version 3 (see the file LICENSE).
100+
101+"""Test the Distribution Source Package vocabulary."""
102+
103+__metaclass__ = type
104+
105+
106+from testtools.matchers import MatchesStructure
107+
108+from lp.registry.enums import InformationType
109+from lp.registry.vocabularies import InformationTypeVocabulary
110+from lp.services.features.testing import FeatureFixture
111+from lp.testing import TestCase
112+from lp.testing.layers import DatabaseFunctionalLayer
113+
114+
115+class TestInformationTypeVocabulary(TestCase):
116+
117+ layer = DatabaseFunctionalLayer
118+
119+ def test_getTermByToken(self):
120+ vocab = InformationTypeVocabulary()
121+ self.assertThat(
122+ vocab.getTermByToken('PUBLIC'),
123+ MatchesStructure.byEquality(
124+ value=InformationType.PUBLIC,
125+ token='PUBLIC',
126+ title='Public',
127+ description=InformationType.PUBLIC.description))
128+
129+ def test_proprietary_disabled(self):
130+ feature_flag = {
131+ 'disclosure.proprietary_information_type.disabled': 'on'}
132+ with FeatureFixture(feature_flag):
133+ vocab = InformationTypeVocabulary()
134+ self.assertRaises(
135+ LookupError, vocab.getTermByToken, 'PROPRIETARY')
136+
137+ def test_proprietary_enabled(self):
138+ vocab = InformationTypeVocabulary()
139+ term = vocab.getTermByToken('PROPRIETARY')
140+ self.assertEqual('Proprietary', term.title)
141+
142+ def test_display_userdata_as_private(self):
143+ feature_flag = {
144+ 'disclosure.display_userdata_as_private.enabled': 'on'}
145+ with FeatureFixture(feature_flag):
146+ vocab = InformationTypeVocabulary()
147+ term = vocab.getTermByToken('USERDATA')
148+ self.assertEqual('Private', term.title)
149+ self.assertEqual(
150+ "Only users with permission to see the project's artifacts "
151+ "containing\nprivate information can see this "
152+ "information.\n",
153+ term.description)
154+
155+ def test_userdata(self):
156+ vocab = InformationTypeVocabulary()
157+ term = vocab.getTermByToken('USERDATA')
158+ self.assertEqual('User Data', term.title)
159+ self.assertEqual(
160+ "Only users with permission to see the project's artifacts "
161+ "containing\nuser data can see this information.\n",
162+ term.description)
163
164=== modified file 'lib/lp/registry/vocabularies.py'
165--- lib/lp/registry/vocabularies.py 2012-03-27 21:34:58 +0000
166+++ lib/lp/registry/vocabularies.py 2012-04-11 11:58:19 +0000
167@@ -39,6 +39,7 @@
168 'FeaturedProjectVocabulary',
169 'FilteredDistroSeriesVocabulary',
170 'FilteredProductSeriesVocabulary',
171+ 'InformationTypeVocabulary',
172 'KarmaCategoryVocabulary',
173 'MilestoneVocabulary',
174 'NewPillarShareeVocabulary',
175@@ -102,6 +103,7 @@
176 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
177 from lp.blueprints.interfaces.specification import ISpecification
178 from lp.bugs.interfaces.bugtask import IBugTask
179+from lp.registry.enums import InformationType
180 from lp.registry.interfaces.accesspolicy import IAccessPolicySource
181 from lp.registry.interfaces.distribution import (
182 IDistribution,
183@@ -172,6 +174,7 @@
184 SQLBase,
185 sqlvalues,
186 )
187+from lp.services.features import getFeatureFlag
188 from lp.services.helpers import (
189 ensure_unicode,
190 shortlist,
191@@ -2220,3 +2223,31 @@
192 SQL('DistributionSourcePackage.id = SearchableDSP.id'))
193
194 return CountableIterator(dsps.count(), dsps, self.toTerm)
195+
196+
197+class InformationTypeVocabulary(SimpleVocabulary):
198+
199+ def __init__(self):
200+ types = [
201+ InformationType.PUBLIC,
202+ InformationType.UNEMBARGOEDSECURITY,
203+ InformationType.EMBARGOEDSECURITY,
204+ InformationType.USERDATA]
205+ proprietary_disabled = bool(getFeatureFlag(
206+ 'disclosure.proprietary_information_type.disabled'))
207+ show_userdata_as_private = bool(getFeatureFlag(
208+ 'disclosure.display_userdata_as_private.enabled'))
209+ if not proprietary_disabled:
210+ types.append(InformationType.PROPRIETARY)
211+ terms = []
212+ for type in types:
213+ title = type.title
214+ description = type.description
215+ if type == InformationType.USERDATA and show_userdata_as_private:
216+ title = 'Private'
217+ description = (
218+ description.replace('user data', 'private information'))
219+ term = SimpleTerm(type, type.name, title)
220+ term.description = description
221+ terms.append(term)
222+ super(InformationTypeVocabulary, self).__init__(terms)
223
224=== modified file 'lib/lp/registry/vocabularies.zcml'
225--- lib/lp/registry/vocabularies.zcml 2012-03-27 04:06:21 +0000
226+++ lib/lp/registry/vocabularies.zcml 2012-04-11 11:58:19 +0000
227@@ -1,4 +1,4 @@
228-<!-- Copyright 2009 Canonical Ltd. This software is licensed under the
229+<!-- Copyright 2009-2012 Canonical Ltd. This software is licensed under the
230 GNU Affero General Public License version 3 (see the file LICENSE).
231 -->
232
233@@ -536,4 +536,10 @@
234 permission="zope.Public"
235 attributes="name title description"/>
236 </class>
237+ <class
238+ class="lp.registry.vocabularies.InformationTypeVocabulary">
239+ <require
240+ permission="zope.Public"
241+ attributes="name title description"/>
242+ </class>
243 </configure>
244
245=== modified file 'lib/lp/services/features/flags.py'
246--- lib/lp/services/features/flags.py 2012-04-09 14:17:07 +0000
247+++ lib/lp/services/features/flags.py 2012-04-11 11:58:19 +0000
248@@ -321,6 +321,18 @@
249 '',
250 '',
251 ''),
252+ ('disclosure.proprietary_information_type.disabled',
253+ 'boolean',
254+ 'If true, disables the PROPRIETARY information_type for bugs.',
255+ '',
256+ '',
257+ ''),
258+ ('disclosure.display_userdata_as_private.enabled',
259+ 'boolean',
260+ 'If true, displays the USERDATA information_type as Private.',
261+ '',
262+ '',
263+ ''),
264 ])
265
266 # The set of all flag names that are documented.