Merge ~twom/launchpad:oci-recipe-targetseries into launchpad:master

Proposed by Tom Wardill
Status: Merged
Approved by: Tom Wardill
Approved revision: af9c2624bd9bd5fcdb6b09c1dd23f4f7c15ee1ed
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~twom/launchpad:oci-recipe-targetseries
Merge into: launchpad:master
Prerequisite: ~twom/launchpad:oci-recipe-target
Diff against target: 671 lines (+403/-30)
10 files modified
database/schema/security.cfg (+10/-2)
lib/lp/registry/configure.zcml (+14/-0)
lib/lp/registry/interfaces/ociproject.py (+31/-24)
lib/lp/registry/interfaces/ociprojectseries.py (+82/-0)
lib/lp/registry/model/ociproject.py (+23/-0)
lib/lp/registry/model/ociprojectseries.py (+65/-0)
lib/lp/registry/tests/test_ociproject.py (+37/-0)
lib/lp/registry/tests/test_ociprojectseries.py (+99/-0)
lib/lp/security.py (+25/-0)
lib/lp/testing/factory.py (+17/-4)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+374036@code.launchpad.net

Commit message

Add implementation for OCIProjectSeries

Description of the change

Adds interface, model, tests, zcml and security.cfg changes for OCIProjectSeries

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
Colin Watson (cjwatson) wrote :

Looks mostly fine, thanks, but some minor attention-to-detail kinds of things. Also, lint complains:

./lib/lp/registry/interfaces/ociproject.py
      37: 'IHasOwner' imported but unused
      22: 'Attribute' imported but unused
./lib/lp/registry/interfaces/ociprojectseries.py
      15: 'export_as_webservice_entry' imported but unused
      32: 'IPerson' imported but unused
      15: 'exported' imported but unused
./lib/lp/registry/model/ociproject.py
     109: E303 too many blank lines (3)
./lib/lp/registry/model/ociprojectseries.py
      28: 'IMasterStore' imported but unused
./lib/lp/registry/tests/test_ociproject.py
      55: local variable 'second_series' is assigned to but never used
./lib/lp/registry/tests/test_ociprojectseries.py
      10: 'getUtility' imported but unused

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/database/schema/security.cfg b/database/schema/security.cfg
index 0e0e91e..06da0d4 100644
--- a/database/schema/security.cfg
+++ b/database/schema/security.cfg
@@ -1241,8 +1241,6 @@ public.ociproject = SELECT, INSERT, UPDATE, DELETE
1241public.ociprojectname = SELECT, INSERT, UPDATE1241public.ociprojectname = SELECT, INSERT, UPDATE
1242public.ociprojectseries = SELECT, INSERT, UPDATE, DELETE1242public.ociprojectseries = SELECT, INSERT, UPDATE, DELETE
1243public.openididentifier = SELECT1243public.openididentifier = SELECT
1244public.ociproject = SELECT, INSERT, UPDATE, DELETE
1245public.ociprojectname = SELECT, INSERT, UPDATE
1246public.packageupload = SELECT, INSERT, UPDATE1244public.packageupload = SELECT, INSERT, UPDATE
1247public.packageuploadbuild = SELECT, INSERT, UPDATE1245public.packageuploadbuild = SELECT, INSERT, UPDATE
1248public.packageuploadcustom = SELECT, INSERT, UPDATE1246public.packageuploadcustom = SELECT, INSERT, UPDATE
@@ -1420,6 +1418,7 @@ public.milestone = SELECT
1420public.milestonetag = SELECT1418public.milestonetag = SELECT
1421public.ociproject = SELECT1419public.ociproject = SELECT
1422public.ociprojectname = SELECT1420public.ociprojectname = SELECT
1421public.ociprojectseries = SELECT
1423public.openididentifier = SELECT1422public.openididentifier = SELECT
1424public.packagecopyjob = SELECT, INSERT1423public.packagecopyjob = SELECT, INSERT
1425public.packagediff = SELECT, INSERT, UPDATE, DELETE1424public.packagediff = SELECT, INSERT, UPDATE, DELETE
@@ -1538,6 +1537,7 @@ public.milestone = SELECT
1538public.milestonetag = SELECT1537public.milestonetag = SELECT
1539public.ociproject = SELECT1538public.ociproject = SELECT
1540public.ociprojectname = SELECT1539public.ociprojectname = SELECT
1540public.ociprojectseries = SELECT
1541public.openididentifier = SELECT1541public.openididentifier = SELECT
1542public.packagecopyjob = SELECT, INSERT, UPDATE1542public.packagecopyjob = SELECT, INSERT, UPDATE
1543public.packagediff = SELECT, UPDATE1543public.packagediff = SELECT, UPDATE
@@ -1642,6 +1642,7 @@ public.milestone = SELECT
1642public.milestonetag = SELECT1642public.milestonetag = SELECT
1643public.ociproject = SELECT1643public.ociproject = SELECT
1644public.ociprojectname = SELECT1644public.ociprojectname = SELECT
1645public.ociprojectseries = SELECT
1645public.person = SELECT1646public.person = SELECT
1646public.personlanguage = SELECT1647public.personlanguage = SELECT
1647public.personsettings = SELECT1648public.personsettings = SELECT
@@ -1845,6 +1846,7 @@ public.milestone = SELECT
1845public.milestonetag = SELECT, INSERT, DELETE1846public.milestonetag = SELECT, INSERT, DELETE
1846public.ociproject = SELECT1847public.ociproject = SELECT
1847public.ociprojectname = SELECT1848public.ociprojectname = SELECT
1849public.ociprojectseries = SELECT
1848public.openididentifier = SELECT1850public.openididentifier = SELECT
1849public.packageset = SELECT1851public.packageset = SELECT
1850public.packagesetgroup = SELECT1852public.packagesetgroup = SELECT
@@ -1966,6 +1968,7 @@ public.message = SELECT, INSERT
1966public.messagechunk = SELECT, INSERT1968public.messagechunk = SELECT, INSERT
1967public.ociproject = SELECT1969public.ociproject = SELECT
1968public.ociprojectname = SELECT1970public.ociprojectname = SELECT
1971public.ociprojectseries = SELECT
1969public.openididentifier = SELECT1972public.openididentifier = SELECT
1970public.person = SELECT1973public.person = SELECT
1971public.product = SELECT1974public.product = SELECT
@@ -2025,6 +2028,7 @@ public.messagechunk = SELECT, INSERT
2025public.milestone = SELECT2028public.milestone = SELECT
2026public.ociproject = SELECT2029public.ociproject = SELECT
2027public.ociprojectname = SELECT2030public.ociprojectname = SELECT
2031public.ociprojectseries = SELECT
2028public.person = SELECT2032public.person = SELECT
2029public.personsettings = SELECT2033public.personsettings = SELECT
2030public.previewdiff = SELECT, INSERT2034public.previewdiff = SELECT, INSERT
@@ -2163,6 +2167,7 @@ public.messagechunk = SELECT, INSERT
2163public.milestonetag = SELECT2167public.milestonetag = SELECT
2164public.ociproject = SELECT2168public.ociproject = SELECT
2165public.ociprojectname = SELECT2169public.ociprojectname = SELECT
2170public.ociprojectseries = SELECT
2166public.person = SELECT, INSERT2171public.person = SELECT, INSERT
2167public.personsettings = SELECT, INSERT2172public.personsettings = SELECT, INSERT
2168public.product = SELECT, INSERT, UPDATE2173public.product = SELECT, INSERT, UPDATE
@@ -2554,6 +2559,7 @@ public.gitrepository = SELECT
2554public.job = SELECT, INSERT, UPDATE2559public.job = SELECT, INSERT, UPDATE
2555public.ociproject = SELECT2560public.ociproject = SELECT
2556public.ociprojectname = SELECT2561public.ociprojectname = SELECT
2562public.ociprojectseries = SELECT
2557public.person = SELECT2563public.person = SELECT
2558public.packaging = SELECT2564public.packaging = SELECT
2559public.product = SELECT, UPDATE2565public.product = SELECT, UPDATE
@@ -2578,6 +2584,7 @@ public.distribution = SELECT
2578public.distroseries = SELECT2584public.distroseries = SELECT
2579public.ociproject = SELECT2585public.ociproject = SELECT
2580public.ociprojectname = SELECT2586public.ociprojectname = SELECT
2587public.ociprojectseries = SELECT
2581public.product = SELECT2588public.product = SELECT
2582public.productseries = SELECT2589public.productseries = SELECT
2583public.sourcepackagename = SELECT2590public.sourcepackagename = SELECT
@@ -2591,6 +2598,7 @@ public.gitrepository = SELECT
2591public.job = SELECT, UPDATE2598public.job = SELECT, UPDATE
2592public.ociproject = SELECT2599public.ociproject = SELECT
2593public.ociprojectname = SELECT2600public.ociprojectname = SELECT
2601public.ociprojectseries = SELECT
2594public.person = SELECT2602public.person = SELECT
2595public.product = SELECT2603public.product = SELECT
2596public.snap = SELECT2604public.snap = SELECT
diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml
index 66f1d14..c5b2673 100644
--- a/lib/lp/registry/configure.zcml
+++ b/lib/lp/registry/configure.zcml
@@ -743,6 +743,7 @@
743 lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/>743 lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/>
744 <require744 <require
745 permission="launchpad.Edit"745 permission="launchpad.Edit"
746 interface="lp.registry.interfaces.ociproject.IOCIProjectEdit"
746 set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" />747 set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" />
747 </class>748 </class>
748 <securedutility749 <securedutility
@@ -758,6 +759,19 @@
758 interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/>759 interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/>
759 </securedutility>760 </securedutility>
760761
762 <!-- OCIProjectSeries -->
763 <class
764 class="lp.registry.model.ociprojectseries.OCIProjectSeries">
765 <require
766 permission="launchpad.View"
767 interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesView
768 lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes"/>
769 <require
770 permission="launchpad.Edit"
771 interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEdit"
772 set_schema="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes" />
773 </class>
774
761 <!-- SourcePackageName -->775 <!-- SourcePackageName -->
762776
763 <class777 <class
diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
index 64bb50c..100dccd 100644
--- a/lib/lp/registry/interfaces/ociproject.py
+++ b/lib/lp/registry/interfaces/ociproject.py
@@ -11,15 +11,11 @@ __all__ = [
11 'IOCIProjectSet',11 'IOCIProjectSet',
12 ]12 ]
1313
14from lazr.restful.declarations import (14from lazr.restful.fields import (
15 export_as_webservice_entry,15 CollectionField,
16 exported,16 Reference,
17 )
18from lazr.restful.fields import Reference
19from zope.interface import (
20 Attribute,
21 Interface,
22 )17 )
18from zope.interface import Interface
23from zope.schema import (19from zope.schema import (
24 Datetime,20 Datetime,
25 Int,21 Int,
@@ -30,7 +26,8 @@ from lp import _
30from lp.bugs.interfaces.bugtarget import IBugTarget26from lp.bugs.interfaces.bugtarget import IBugTarget
31from lp.registry.interfaces.distribution import IDistribution27from lp.registry.interfaces.distribution import IDistribution
32from lp.registry.interfaces.ociprojectname import IOCIProjectName28from lp.registry.interfaces.ociprojectname import IOCIProjectName
33from lp.registry.interfaces.role import IHasOwner29from lp.registry.interfaces.series import SeriesStatus
30from lp.services.database.constants import DEFAULT
34from lp.services.fields import PublicPersonChoice31from lp.services.fields import PublicPersonChoice
3532
3633
@@ -38,15 +35,20 @@ class IOCIProjectView(Interface):
38 """IOCIProject attributes that require launchpad.View permission."""35 """IOCIProject attributes that require launchpad.View permission."""
3936
40 id = Int(title=_("ID"), required=True, readonly=True)37 id = Int(title=_("ID"), required=True, readonly=True)
41 date_created = exported(38 date_created = Datetime(
42 Datetime(title=_("Date created"), required=True), readonly=True)39 title=_("Date created"), required=True, readonly=True)
43 date_last_modified = exported(40 date_last_modified = Datetime(
44 Datetime(title=_("Date last modified"), required=True), readonly=True)41 title=_("Date last modified"), required=True, readonly=True)
4542
46 registrant = exported(PublicPersonChoice(43 registrant = PublicPersonChoice(
47 title=_("Registrant"),44 title=_("Registrant"),
48 description=_("The person that registered this project."),45 description=_("The person that registered this project."),
49 vocabulary='ValidPersonOrTeam', required=True, readonly=True))46 vocabulary='ValidPersonOrTeam', required=True, readonly=True)
47
48 series = CollectionField(
49 title=_("Series inside this OCI project."),
50 # Really IOCIProjectSeries
51 value_type=Reference(schema=Interface))
5052
5153
52class IOCIProjectEditableAttributes(IBugTarget):54class IOCIProjectEditableAttributes(IBugTarget):
@@ -58,24 +60,29 @@ class IOCIProjectEditableAttributes(IBugTarget):
58 distribution = Reference(60 distribution = Reference(
59 IDistribution,61 IDistribution,
60 title=_("The distribution that this OCI project is associated with."))62 title=_("The distribution that this OCI project is associated with."))
61 ociprojectname = exported(Reference(63 ociprojectname = Reference(
62 IOCIProjectName,64 IOCIProjectName,
63 title=_("The name of this OCI project."),65 title=_("The name of this OCI project."),
64 required=True,66 required=True,
65 readonly=True))67 readonly=True)
66 description = exported(68 description = Text(title=_("The description for this OCI project."))
67 Text(title=_("The description for this OCI project.")))69 pillar = Reference(
68 pillar = exported(Reference(
69 IDistribution,70 IDistribution,
70 title=_("The pillar containing this target."), readonly=True))71 title=_("The pillar containing this target."), readonly=True)
72
7173
74class IOCIProjectEdit(Interface):
75 """IOCIProject attributes that require launchpad.Edit permission."""
7276
73class IOCIProject(IOCIProjectView,77 def newSeries(name, summary, registrant,
78 status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT):
79 """Creates a new `IOCIProjectSeries`."""
80
81
82class IOCIProject(IOCIProjectView, IOCIProjectEdit,
74 IOCIProjectEditableAttributes):83 IOCIProjectEditableAttributes):
75 """A project containing Open Container Initiative recipes."""84 """A project containing Open Container Initiative recipes."""
7685
77 export_as_webservice_entry()
78
7986
80class IOCIProjectSet(Interface):87class IOCIProjectSet(Interface):
81 """A utility to create and access OCI Projects."""88 """A utility to create and access OCI Projects."""
diff --git a/lib/lp/registry/interfaces/ociprojectseries.py b/lib/lp/registry/interfaces/ociprojectseries.py
82new file mode 10064489new file mode 100644
index 0000000..8ef9705
--- /dev/null
+++ b/lib/lp/registry/interfaces/ociprojectseries.py
@@ -0,0 +1,82 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Interfaces to allow bug filing on multiple versions of an OCI Project."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9__all__ = [
10 'IOCIProjectSeries',
11 'IOCIProjectSeriesEditableAttributes',
12 'IOCIProjectSeriesView',
13 ]
14
15from lazr.restful.fields import Reference
16from zope.interface import Interface
17from zope.schema import (
18 Choice,
19 Datetime,
20 Int,
21 Text,
22 TextLine,
23 )
24
25from lp import _
26from lp.app.validators.name import name_validator
27from lp.registry.interfaces.ociproject import IOCIProject
28from lp.registry.interfaces.series import SeriesStatus
29from lp.services.fields import PublicPersonChoice
30
31
32class IOCIProjectSeriesView(Interface):
33 """IOCIProjectSeries attributes that require launchpad.View permission."""
34
35 id = Int(title=_("ID"), required=True, readonly=True)
36
37 ociproject = Reference(
38 IOCIProject,
39 title=_("The OCI project that this series belongs to."),
40 required=True, readonly=True)
41
42 date_created = Datetime(
43 title=_("Date created"), required=True, readonly=True,
44 description=_(
45 "The date on which this series was created in Launchpad."))
46
47 registrant = PublicPersonChoice(
48 title=_("Registrant"),
49 description=_("The person that registered this series."),
50 vocabulary='ValidPersonOrTeam', required=True, readonly=True)
51
52
53class IOCIProjectSeriesEditableAttributes(Interface):
54 """IOCIProjectSeries attributes that can be edited.
55
56 These attributes need launchpad.View to see, and launchpad.Edit to change.
57 """
58
59 name = TextLine(
60 title=_("Name"), constraint=name_validator,
61 required=True, readonly=False,
62 description=_("The name of this series."))
63
64 summary = Text(
65 title=_("Summary"), required=True, readonly=False,
66 description=_("A brief summary of this series."))
67
68 status = Choice(
69 title=_("Status"), required=True,
70 vocabulary=SeriesStatus)
71
72
73class IOCIProjectSeriesEdit(Interface):
74 """IOCIProjectSeries attributes that require launchpad.Edit permission."""
75
76
77class IOCIProjectSeries(IOCIProjectSeriesView, IOCIProjectSeriesEdit,
78 IOCIProjectSeriesEditableAttributes):
79 """A series of an Open Container Initiative project.
80
81 This is used to allow tracking bugs against multiple versions of images.
82 """
diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
index 45de84d..7250692 100644
--- a/lib/lp/registry/model/ociproject.py
+++ b/lib/lp/registry/model/ociproject.py
@@ -27,7 +27,9 @@ from lp.registry.interfaces.ociproject import (
27 IOCIProject,27 IOCIProject,
28 IOCIProjectSet,28 IOCIProjectSet,
29 )29 )
30from lp.registry.interfaces.series import SeriesStatus
30from lp.registry.model.ociprojectname import OCIProjectName31from lp.registry.model.ociprojectname import OCIProjectName
32from lp.registry.model.ociprojectseries import OCIProjectSeries
31from lp.services.database.constants import DEFAULT33from lp.services.database.constants import DEFAULT
32from lp.services.database.interfaces import (34from lp.services.database.interfaces import (
33 IMasterStore,35 IMasterStore,
@@ -81,6 +83,27 @@ class OCIProject(BugTargetBase, StormBase):
81 return "OCI project %s for %s" % (83 return "OCI project %s for %s" % (
82 self.ociprojectname.name, self.pillar.name)84 self.ociprojectname.name, self.pillar.name)
8385
86 def newSeries(self, name, summary, registrant,
87 status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT):
88 """See `IOCIProject`."""
89 series = OCIProjectSeries(
90 ociproject=self,
91 name=name,
92 summary=summary,
93 registrant=registrant,
94 status=status,
95 )
96 return series
97
98 @property
99 def series(self):
100 """See `IOCIProject`."""
101 ret = IStore(OCIProjectSeries).find(
102 OCIProjectSeries,
103 OCIProjectSeries.ociproject == self
104 ).order_by(OCIProjectSeries.date_created)
105 return ret
106
84107
85@implementer(IOCIProjectSet)108@implementer(IOCIProjectSet)
86class OCIProjectSet:109class OCIProjectSet:
diff --git a/lib/lp/registry/model/ociprojectseries.py b/lib/lp/registry/model/ociprojectseries.py
87new file mode 100644110new file mode 100644
index 0000000..04a81e9
--- /dev/null
+++ b/lib/lp/registry/model/ociprojectseries.py
@@ -0,0 +1,65 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Model implementing `IOCIProjectSeries`."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9__all__ = [
10 'OCIProjectSeries',
11 ]
12
13import pytz
14from storm.locals import (
15 DateTime,
16 Int,
17 Reference,
18 Unicode,
19 )
20from zope.interface import implementer
21
22from lp.app.validators.name import valid_name
23from lp.registry.errors import InvalidName
24from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
25from lp.registry.interfaces.series import SeriesStatus
26from lp.services.database.constants import DEFAULT
27from lp.services.database.enumcol import DBEnum
28from lp.services.database.stormbase import StormBase
29
30
31@implementer(IOCIProjectSeries)
32class OCIProjectSeries(StormBase):
33 """See `IOCIProjectSeries`."""
34
35 __storm_table__ = "OCIProjectSeries"
36
37 id = Int(primary=True)
38
39 ociproject_id = Int(name='ociproject', allow_none=False)
40 ociproject = Reference(ociproject_id, "OCIProject.id")
41
42 name = Unicode(name="name", allow_none=False)
43
44 summary = Unicode(name="summary", allow_none=False)
45
46 date_created = DateTime(
47 name="date_created", tzinfo=pytz.UTC, allow_none=False)
48
49 registrant_id = Int(name='registrant', allow_none=False)
50 registrant = Reference(registrant_id, "Person.id")
51
52 status = DBEnum(
53 name='status', allow_none=False, enum=SeriesStatus)
54
55 def __init__(self, ociproject, name, summary,
56 registrant, status, date_created=DEFAULT):
57 if not valid_name(name):
58 raise InvalidName(
59 "%s is not a valid name for an OCI project series." % name)
60 self.name = name
61 self.ociproject = ociproject
62 self.summary = summary
63 self.registrant = registrant
64 self.status = status
65 self.date_created = date_created
diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py
index a68f786..d170bbb 100644
--- a/lib/lp/registry/tests/test_ociproject.py
+++ b/lib/lp/registry/tests/test_ociproject.py
@@ -7,12 +7,15 @@ from __future__ import absolute_import, print_function, unicode_literals
77
8__metaclass__ = type8__metaclass__ = type
99
10from testtools.testcase import ExpectedException
10from zope.component import getUtility11from zope.component import getUtility
12from zope.security.interfaces import Unauthorized
1113
12from lp.registry.interfaces.ociproject import (14from lp.registry.interfaces.ociproject import (
13 IOCIProject,15 IOCIProject,
14 IOCIProjectSet,16 IOCIProjectSet,
15 )17 )
18from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
16from lp.testing import (19from lp.testing import (
17 admin_logged_in,20 admin_logged_in,
18 person_logged_in,21 person_logged_in,
@@ -30,6 +33,40 @@ class TestOCIProject(TestCaseWithFactory):
30 with admin_logged_in():33 with admin_logged_in():
31 self.assertProvides(oci_project, IOCIProject)34 self.assertProvides(oci_project, IOCIProject)
3235
36 def test_newSeries(self):
37 driver = self.factory.makePerson()
38 distribution = self.factory.makeDistribution(driver=driver)
39 registrant = self.factory.makePerson()
40 oci_project = self.factory.makeOCIProject(pillar=distribution)
41 with person_logged_in(driver):
42 series = oci_project.newSeries(
43 'test-series',
44 'test-summary',
45 registrant)
46 self.assertProvides(series, IOCIProjectSeries)
47
48 def test_newSeries_bad_permissions(self):
49 distribution = self.factory.makeDistribution()
50 registrant = self.factory.makePerson()
51 oci_project = self.factory.makeOCIProject(pillar=distribution)
52 with ExpectedException(Unauthorized):
53 oci_project.newSeries(
54 'test-series',
55 'test-summary',
56 registrant)
57
58 def test_series(self):
59 driver = self.factory.makePerson()
60 distribution = self.factory.makeDistribution(driver=driver)
61 first_oci_project = self.factory.makeOCIProject(pillar=distribution)
62 second_oci_project = self.factory.makeOCIProject(pillar=distribution)
63 with person_logged_in(driver):
64 first_series = self.factory.makeOCIProjectSeries(
65 oci_project=first_oci_project)
66 self.factory.makeOCIProjectSeries(
67 oci_project=second_oci_project)
68 self.assertContentEqual([first_series], first_oci_project.series)
69
3370
34class TestOCIProjectSet(TestCaseWithFactory):71class TestOCIProjectSet(TestCaseWithFactory):
3572
diff --git a/lib/lp/registry/tests/test_ociprojectseries.py b/lib/lp/registry/tests/test_ociprojectseries.py
36new file mode 10064473new file mode 100644
index 0000000..3d4e6cf
--- /dev/null
+++ b/lib/lp/registry/tests/test_ociprojectseries.py
@@ -0,0 +1,99 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Test OCIProjectSeries."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9
10from testtools.matchers import MatchesStructure
11from testtools.testcase import ExpectedException
12from zope.security.interfaces import Unauthorized
13
14from lp.registry.errors import InvalidName
15from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
16from lp.registry.interfaces.series import SeriesStatus
17from lp.registry.model.ociprojectseries import OCIProjectSeries
18from lp.services.database.constants import UTC_NOW
19from lp.testing import (
20 person_logged_in,
21 TestCaseWithFactory,
22 )
23from lp.testing.layers import DatabaseFunctionalLayer
24
25
26class TestOCIProjectSeries(TestCaseWithFactory):
27
28 layer = DatabaseFunctionalLayer
29
30 def test_implements_interface(self):
31 name = 'test-name'
32 oci_project = self.factory.makeOCIProject()
33 summary = 'test_summary'
34 registrant = self.factory.makePerson()
35 status = SeriesStatus.DEVELOPMENT
36 project_series = OCIProjectSeries(
37 oci_project, name, summary, registrant, status)
38 self.assertProvides(project_series, IOCIProjectSeries)
39
40 def test_init(self):
41 name = 'test-name'
42 oci_project = self.factory.makeOCIProject()
43 summary = 'test_summary'
44 registrant = self.factory.makePerson()
45 status = SeriesStatus.DEVELOPMENT
46 date_created = UTC_NOW
47 project_series = OCIProjectSeries(
48 oci_project, name, summary, registrant, status, date_created)
49 self.assertThat(
50 project_series, MatchesStructure.byEquality(
51 ociproject=project_series.ociproject,
52 name=project_series.name,
53 summary=project_series.summary,
54 registrant=project_series.registrant,
55 status=project_series.status,
56 date_created=project_series.date_created))
57
58 def test_invalid_name(self):
59 name = 'invalid%20name'
60 oci_project = self.factory.makeOCIProject()
61 summary = 'test_summary'
62 registrant = self.factory.makePerson()
63 status = SeriesStatus.DEVELOPMENT
64 with ExpectedException(InvalidName):
65 OCIProjectSeries(oci_project, name, summary, registrant, status)
66
67 def test_edit_permissions_invalid(self):
68 name = 'test-name'
69 summary = 'test_summary'
70 registrant = self.factory.makePerson()
71 another_person = self.factory.makePerson()
72
73 driver = self.factory.makePerson()
74 distribution = self.factory.makeDistribution(driver=driver)
75
76 with person_logged_in(driver):
77 project_series = self.factory.makeOCIProject(
78 pillar=distribution).newSeries(
79 name, summary, registrant)
80
81 with person_logged_in(another_person):
82 with ExpectedException(Unauthorized):
83 project_series.name = 'not-allowed'
84
85 def test_edit_permissions_valid(self):
86 name = 'test-name'
87 summary = 'test_summary'
88 registrant = self.factory.makePerson()
89
90 driver = self.factory.makePerson()
91 distribution = self.factory.makeDistribution(driver=driver)
92
93 with person_logged_in(driver):
94 project_series = self.factory.makeOCIProject(
95 pillar=distribution).newSeries(
96 name, summary, registrant)
97 project_series.name = 'allowed'
98
99 self.assertEqual(project_series.name, 'allowed')
diff --git a/lib/lp/security.py b/lib/lp/security.py
index 050c1fe..21caa71 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -136,6 +136,8 @@ from lp.registry.interfaces.nameblacklist import (
136 INameBlacklist,136 INameBlacklist,
137 INameBlacklistSet,137 INameBlacklistSet,
138 )138 )
139from lp.registry.interfaces.ociproject import IOCIProject
140from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
139from lp.registry.interfaces.packaging import IPackaging141from lp.registry.interfaces.packaging import IPackaging
140from lp.registry.interfaces.person import (142from lp.registry.interfaces.person import (
141 IPerson,143 IPerson,
@@ -3430,3 +3432,26 @@ class EditSnapBase(EditByRegistryExpertsOrAdmins):
34303432
3431class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):3433class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
3432 usedfor = ISnapBaseSet3434 usedfor = ISnapBaseSet
3435
3436
3437class EditOCIProject(AuthorizationBase):
3438 permission = 'launchpad.Edit'
3439 usedfor = IOCIProject
3440
3441 def checkAuthenticated(self, user):
3442 """Maintainers, drivers, and admins can drive projects."""
3443 # XXX twom 2019-10-29 This ideally shouldn't be driver, but a
3444 # new role name that cascades upwards from the OCIProject
3445 # to the pillar
3446 return (user.in_admin or
3447 user.isDriver(self.obj.pillar))
3448
3449
3450class EditOCIProjectSeries(AuthorizationBase):
3451 permission = 'launchpad.Edit'
3452 usedfor = IOCIProjectSeries
3453
3454 def checkAuthenticated(self, user):
3455 """Maintainers, drivers, and admins can drive projects."""
3456 return (user.in_admin or
3457 user.isDriver(self.obj.ociproject.pillar))
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 5c0cc18..39d2725 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -4904,10 +4904,10 @@ class BareLaunchpadObjectFactory(ObjectFactory):
4904 return getUtility(IOCIProjectNameSet).new(name)4904 return getUtility(IOCIProjectNameSet).new(name)
49054905
4906 def makeOCIProject(self, registrant=None, pillar=None,4906 def makeOCIProject(self, registrant=None, pillar=None,
4907 ociprojectname=None, date_created=DEFAULT,4907 ociprojectname=None, date_created=DEFAULT,
4908 description=None, bug_reporting_guidelines=None,4908 description=None, bug_reporting_guidelines=None,
4909 bug_reported_acknowledgement=None,4909 bug_reported_acknowledgement=None,
4910 bugfiling_duplicate_search=False):4910 bugfiling_duplicate_search=False):
4911 """Make a new OCIProject."""4911 """Make a new OCIProject."""
4912 if registrant is None:4912 if registrant is None:
4913 registrant = self.makePerson()4913 registrant = self.makePerson()
@@ -4922,6 +4922,19 @@ class BareLaunchpadObjectFactory(ObjectFactory):
4922 bug_reported_acknowledgement=bug_reported_acknowledgement,4922 bug_reported_acknowledgement=bug_reported_acknowledgement,
4923 bugfiling_duplicate_search=bugfiling_duplicate_search)4923 bugfiling_duplicate_search=bugfiling_duplicate_search)
49244924
4925 def makeOCIProjectSeries(self, name=None, summary=None, registrant=None,
4926 oci_project=None, **kwargs):
4927 """Make a new OCIProjectSeries attached to an OCIProject."""
4928 if name is None:
4929 name = self.getUniqueString(u"oci-project-series-name")
4930 if summary is None:
4931 summary = self.getUniqueString(u"oci-project-series-summary")
4932 if registrant is None:
4933 registrant = self.makePerson()
4934 if oci_project is None:
4935 oci_project = self.makeOCIProject(**kwargs)
4936 return oci_project.newSeries(name, summary, registrant)
4937
49254938
4926# Some factory methods return simple Python types. We don't add4939# Some factory methods return simple Python types. We don't add
4927# security wrappers for them, as well as for objects created by4940# security wrappers for them, as well as for objects created by

Subscribers

People subscribed via source and target branches

to status/vote changes: