launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #12803
[Merge] lp:~abentley/launchpad/model-product-info-type into lp:launchpad
Aaron Bentley has proposed merging lp:~abentley/launchpad/model-product-info-type into lp:launchpad.
Commit message:
Implement Product.information_type in Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1060384 in Launchpad itself: "Product.information_type is not exposed via ORM"
https://bugs.launchpad.net/launchpad/+bug/1060384
For more details, see:
https://code.launchpad.net/~abentley/launchpad/model-product-info-type/+merge/127558
= Summary =
Provide Product.information_type via Storm
== Proposed fix ==
Add Product.information_type. Implement IInformationType, remove IProduct.information_type
== Pre-implementation notes ==
None
== LOC Rationale ==
Part of Private Projects
== Implementation details ==
The database column has already landed; this adds support to Storm.
The information_type move is to reduce duplication and to simplify security policy implementation.
information_type is nullable because the database permits this and existing data contains NULLs. Once a garbo job has removed all NULLs, it can be updated to prohibit nulls.
== Tests ==
bin/test -t test_product_information_type test_product
== Demo and Q/A ==
View a product page. If nothing's broken, it's good.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/app/model/launchpad.py
lib/lp/blueprints/model/specification.py
lib/lp/bugs/model/bug.py
lib/lp/registry/configure.zcml
lib/lp/registry/interfaces/product.py
lib/lp/registry/model/product.py
lib/lp/registry/tests/test_product.py
lib/lp/testing/factory.py
--
https://code.launchpad.net/~abentley/launchpad/model-product-info-type/+merge/127558
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/model-product-info-type into lp:launchpad.
=== modified file 'lib/lp/app/model/launchpad.py'
--- lib/lp/app/model/launchpad.py 2012-02-28 04:24:19 +0000
+++ lib/lp/app/model/launchpad.py 2012-10-02 19:14:21 +0000
@@ -7,6 +7,7 @@
__all__ = [
'ExceptionPrivacy',
+ 'InformationTypeMixin',
'Privacy',
]
@@ -17,6 +18,7 @@
Unauthorized,
)
+from lp.app.enums import PRIVATE_INFORMATION_TYPES
from lp.app.interfaces.launchpad import IPrivacy
@@ -38,3 +40,11 @@
else:
private = False
super(ExceptionPrivacy, self).__init__(error, private)
+
+
+class InformationTypeMixin:
+ """"Common functionality for classes implementing IInformationType."""
+
+ @property
+ def private(self):
+ return self.information_type in PRIVATE_INFORMATION_TYPES
=== modified file 'lib/lp/blueprints/model/specification.py'
--- lib/lp/blueprints/model/specification.py 2012-09-27 14:30:40 +0000
+++ lib/lp/blueprints/model/specification.py 2012-10-02 19:14:21 +0000
@@ -47,11 +47,11 @@
from lp.app.enums import (
InformationType,
- PRIVATE_INFORMATION_TYPES,
PUBLIC_INFORMATION_TYPES,
)
from lp.app.errors import UserCannotUnsubscribePerson
from lp.app.interfaces.informationtype import IInformationType
+from lp.app.model.launchpad import InformationTypeMixin
from lp.blueprints.adapters import SpecificationDelta
from lp.blueprints.enums import (
NewSpecificationDefinitionStatus,
@@ -157,7 +157,7 @@
}
-class Specification(SQLBase, BugLinkTargetMixin):
+class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
"""See ISpecification."""
implements(ISpecification, IBugLinkTarget, IInformationType)
@@ -891,10 +891,6 @@
reconcile_access_for_artifact(self, information_type, [self.target])
return True
- @property
- def private(self):
- return self.information_type in PRIVATE_INFORMATION_TYPES
-
@cachedproperty
def _known_viewers(self):
"""A set of known persons able to view the specifcation."""
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2012-09-28 06:15:58 +0000
+++ lib/lp/bugs/model/bug.py 2012-10-02 19:14:21 +0000
@@ -93,6 +93,7 @@
)
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.app.interfaces.services import IService
+from lp.app.model.launchpad import InformationTypeMixin
from lp.app.validators import LaunchpadValidationError
from lp.bugs.adapters.bug import convert_to_information_type
from lp.bugs.adapters.bugchange import (
@@ -303,7 +304,7 @@
self.user = user
-class Bug(SQLBase):
+class Bug(SQLBase, InformationTypeMixin):
"""A bug."""
implements(IBug)
@@ -363,10 +364,6 @@
latest_patch_uploaded = UtcDateTimeCol(default=None)
@property
- def private(self):
- return self.information_type in PRIVATE_INFORMATION_TYPES
-
- @property
def security_related(self):
return self.information_type in SECURITY_INFORMATION_TYPES
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2012-09-25 04:29:34 +0000
+++ lib/lp/registry/configure.zcml 2012-10-02 19:14:21 +0000
@@ -1241,6 +1241,11 @@
<require
permission="launchpad.Edit"
interface="lp.registry.interfaces.product.IProductEditRestricted"/>
+ <allow
+ interface="lp.app.interfaces.informationtype.IInformationType"/>
+ <require
+ permission="launchpad.Edit"
+ set_schema="lp.app.interfaces.informationtype.IInformationType"/>
<require
permission="launchpad.Edit"
set_attributes="answers_usage blueprints_usage codehosting_usage
=== modified file 'lib/lp/registry/interfaces/product.py'
--- lib/lp/registry/interfaces/product.py 2012-09-28 06:15:58 +0000
+++ lib/lp/registry/interfaces/product.py 2012-10-02 19:14:21 +0000
@@ -73,7 +73,6 @@
from lp import _
from lp.answers.interfaces.questiontarget import IQuestionTarget
-from lp.app.enums import InformationType
from lp.app.errors import NameLookupFailed
from lp.app.interfaces.headings import IRootContext
from lp.app.interfaces.launchpad import (
@@ -450,13 +449,6 @@
'and security policy will apply to this project.')),
exported_as='project_group')
- information_type = exported(
- Choice(
- title=_('Information Type'), vocabulary=InformationType,
- required=True, readonly=True,
- description=_(
- 'The type of of data contained in this project.')))
-
owner = exported(
PersonChoice(
title=_('Maintainer'),
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2012-09-28 19:59:35 +0000
+++ lib/lp/registry/model/product.py 2012-10-02 19:14:21 +0000
@@ -73,6 +73,7 @@
ServiceUsage,
)
from lp.app.errors import NotFoundError
+from lp.app.interfaces.informationtype import IInformationType
from lp.app.interfaces.launchpad import (
IHasIcon,
IHasLogo,
@@ -81,6 +82,7 @@
ILaunchpadUsage,
IServiceUsage,
)
+from lp.app.model.launchpad import InformationTypeMixin
from lp.blueprints.enums import (
SpecificationDefinitionStatus,
SpecificationFilter,
@@ -323,13 +325,15 @@
HasAliasMixin, StructuralSubscriptionTargetMixin,
HasMilestonesMixin, OfficialBugTagTargetMixin, HasBranchesMixin,
HasCustomLanguageCodesMixin, HasMergeProposalsMixin,
- HasCodeImportsMixin, TranslationPolicyMixin):
+ HasCodeImportsMixin, InformationTypeMixin,
+ TranslationPolicyMixin):
"""A Product."""
implements(
IBugSummaryDimension, IFAQTarget, IHasBugSupervisor,
IHasCustomLanguageCodes, IHasIcon, IHasLogo, IHasMugshot,
- IHasOOPSReferences, ILaunchpadUsage, IProduct, IServiceUsage)
+ IHasOOPSReferences, ILaunchpadUsage, IProduct, IServiceUsage,
+ IInformationType,)
_table = 'Product'
@@ -405,7 +409,7 @@
"""
return None
- @date_next_suggest_packaging.setter
+ @date_next_suggest_packaging.setter # pyflakes:ignore
def date_next_suggest_packaging(self, value):
"""See `IProduct`
@@ -413,15 +417,8 @@
"""
pass
- @property
- def information_type(self):
- """See `IProduct`
-
- Place holder for a db column.
- XXX: rharding 2012-09-10 bug=1048720: Waiting on db patch to connect
- into place.
- """
- pass
+ information_type = EnumCol(
+ enum=InformationType, default=InformationType.PUBLIC)
security_contact = None
@@ -1599,12 +1596,15 @@
sourceforgeproject=None, programminglang=None,
project_reviewed=False, mugshot=None, logo=None,
icon=None, licenses=None, license_info=None,
- registrant=None, bug_supervisor=None, driver=None):
+ registrant=None, bug_supervisor=None, driver=None,
+ information_type=None):
"""See `IProductSet`."""
if registrant is None:
registrant = owner
if licenses is None:
licenses = set()
+ if information_type is None:
+ information_type = InformationType.PUBLIC
product = Product(
owner=owner, registrant=registrant, name=name,
displayname=displayname, title=title, project=project,
@@ -1615,7 +1615,8 @@
programminglang=programminglang,
project_reviewed=project_reviewed,
icon=icon, logo=logo, mugshot=mugshot, license_info=license_info,
- bug_supervisor=bug_supervisor, driver=driver)
+ bug_supervisor=bug_supervisor, driver=driver,
+ information_type=information_type)
# Set up the sharing policies and product licence.
bug_sharing_policy_to_use = BugSharingPolicy.PUBLIC
=== modified file 'lib/lp/registry/tests/test_product.py'
--- lib/lp/registry/tests/test_product.py 2012-09-28 06:15:58 +0000
+++ lib/lp/registry/tests/test_product.py 2012-10-02 19:14:21 +0000
@@ -7,6 +7,7 @@
import datetime
import pytz
+from storm.locals import Store
from testtools.matchers import MatchesAll
import transaction
from zope.component import getUtility
@@ -28,6 +29,7 @@
ILaunchpadUsage,
IServiceUsage,
)
+from lp.app.interfaces.informationtype import IInformationType
from lp.app.interfaces.services import IService
from lp.bugs.interfaces.bugsummary import IBugSummaryDimension
from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
@@ -111,6 +113,7 @@
IHasLogo,
IHasMugshot,
IHasOOPSReferences,
+ IInformationType,
ILaunchpadUsage,
IServiceUsage,
]
@@ -390,6 +393,29 @@
expected = [InformationType.PROPRIETARY]
self.assertContentEqual(expected, [policy.type for policy in aps])
+ def test_product_information_type(self):
+ # Product is created with specified information_type
+ product = self.factory.makeProduct(
+ information_type=InformationType.EMBARGOED)
+ self.assertEqual(InformationType.EMBARGOED, product.information_type)
+ # Owner can set information_type
+ with person_logged_in(product.owner):
+ product.information_type = InformationType.PROPRIETARY
+ self.assertEqual(InformationType.PROPRIETARY, product.information_type)
+ # Database persists information_type value
+ store = Store.of(product)
+ store.flush()
+ store.reset()
+ product = store.get(Product, product.id)
+ self.assertEqual(InformationType.PROPRIETARY, product.information_type)
+
+ def test_product_information_type_default(self):
+ # Default information_type is PUBLIC
+ owner = self.factory.makePerson()
+ product = getUtility(IProductSet).createProduct(
+ owner, 'fnord', 'Fnord', 'Fnord', 'test 1', 'test 2')
+ self.assertEqual(InformationType.PUBLIC, product.information_type)
+
class TestProductBugInformationTypes(TestCaseWithFactory):
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-10-02 07:08:13 +0000
+++ lib/lp/testing/factory.py 2012-10-02 19:14:21 +0000
@@ -964,7 +964,8 @@
title=None, summary=None, official_malone=None,
translations_usage=None, bug_supervisor=None, private_bugs=False,
driver=None, icon=None, bug_sharing_policy=None,
- branch_sharing_policy=None, specification_sharing_policy=None):
+ branch_sharing_policy=None, specification_sharing_policy=None,
+ information_type=None):
"""Create and return a new, arbitrary Product."""
if owner is None:
owner = self.makePerson()
@@ -993,7 +994,8 @@
licenses=licenses,
project=project,
registrant=registrant,
- icon=icon)
+ icon=icon,
+ information_type=information_type)
naked_product = removeSecurityProxy(product)
if official_malone is not None:
naked_product.official_malone = official_malone
Follow ups