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
1diff --git a/database/schema/security.cfg b/database/schema/security.cfg
2index 0e0e91e..06da0d4 100644
3--- a/database/schema/security.cfg
4+++ b/database/schema/security.cfg
5@@ -1241,8 +1241,6 @@ public.ociproject = SELECT, INSERT, UPDATE, DELETE
6 public.ociprojectname = SELECT, INSERT, UPDATE
7 public.ociprojectseries = SELECT, INSERT, UPDATE, DELETE
8 public.openididentifier = SELECT
9-public.ociproject = SELECT, INSERT, UPDATE, DELETE
10-public.ociprojectname = SELECT, INSERT, UPDATE
11 public.packageupload = SELECT, INSERT, UPDATE
12 public.packageuploadbuild = SELECT, INSERT, UPDATE
13 public.packageuploadcustom = SELECT, INSERT, UPDATE
14@@ -1420,6 +1418,7 @@ public.milestone = SELECT
15 public.milestonetag = SELECT
16 public.ociproject = SELECT
17 public.ociprojectname = SELECT
18+public.ociprojectseries = SELECT
19 public.openididentifier = SELECT
20 public.packagecopyjob = SELECT, INSERT
21 public.packagediff = SELECT, INSERT, UPDATE, DELETE
22@@ -1538,6 +1537,7 @@ public.milestone = SELECT
23 public.milestonetag = SELECT
24 public.ociproject = SELECT
25 public.ociprojectname = SELECT
26+public.ociprojectseries = SELECT
27 public.openididentifier = SELECT
28 public.packagecopyjob = SELECT, INSERT, UPDATE
29 public.packagediff = SELECT, UPDATE
30@@ -1642,6 +1642,7 @@ public.milestone = SELECT
31 public.milestonetag = SELECT
32 public.ociproject = SELECT
33 public.ociprojectname = SELECT
34+public.ociprojectseries = SELECT
35 public.person = SELECT
36 public.personlanguage = SELECT
37 public.personsettings = SELECT
38@@ -1845,6 +1846,7 @@ public.milestone = SELECT
39 public.milestonetag = SELECT, INSERT, DELETE
40 public.ociproject = SELECT
41 public.ociprojectname = SELECT
42+public.ociprojectseries = SELECT
43 public.openididentifier = SELECT
44 public.packageset = SELECT
45 public.packagesetgroup = SELECT
46@@ -1966,6 +1968,7 @@ public.message = SELECT, INSERT
47 public.messagechunk = SELECT, INSERT
48 public.ociproject = SELECT
49 public.ociprojectname = SELECT
50+public.ociprojectseries = SELECT
51 public.openididentifier = SELECT
52 public.person = SELECT
53 public.product = SELECT
54@@ -2025,6 +2028,7 @@ public.messagechunk = SELECT, INSERT
55 public.milestone = SELECT
56 public.ociproject = SELECT
57 public.ociprojectname = SELECT
58+public.ociprojectseries = SELECT
59 public.person = SELECT
60 public.personsettings = SELECT
61 public.previewdiff = SELECT, INSERT
62@@ -2163,6 +2167,7 @@ public.messagechunk = SELECT, INSERT
63 public.milestonetag = SELECT
64 public.ociproject = SELECT
65 public.ociprojectname = SELECT
66+public.ociprojectseries = SELECT
67 public.person = SELECT, INSERT
68 public.personsettings = SELECT, INSERT
69 public.product = SELECT, INSERT, UPDATE
70@@ -2554,6 +2559,7 @@ public.gitrepository = SELECT
71 public.job = SELECT, INSERT, UPDATE
72 public.ociproject = SELECT
73 public.ociprojectname = SELECT
74+public.ociprojectseries = SELECT
75 public.person = SELECT
76 public.packaging = SELECT
77 public.product = SELECT, UPDATE
78@@ -2578,6 +2584,7 @@ public.distribution = SELECT
79 public.distroseries = SELECT
80 public.ociproject = SELECT
81 public.ociprojectname = SELECT
82+public.ociprojectseries = SELECT
83 public.product = SELECT
84 public.productseries = SELECT
85 public.sourcepackagename = SELECT
86@@ -2591,6 +2598,7 @@ public.gitrepository = SELECT
87 public.job = SELECT, UPDATE
88 public.ociproject = SELECT
89 public.ociprojectname = SELECT
90+public.ociprojectseries = SELECT
91 public.person = SELECT
92 public.product = SELECT
93 public.snap = SELECT
94diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml
95index 66f1d14..c5b2673 100644
96--- a/lib/lp/registry/configure.zcml
97+++ b/lib/lp/registry/configure.zcml
98@@ -743,6 +743,7 @@
99 lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/>
100 <require
101 permission="launchpad.Edit"
102+ interface="lp.registry.interfaces.ociproject.IOCIProjectEdit"
103 set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" />
104 </class>
105 <securedutility
106@@ -758,6 +759,19 @@
107 interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/>
108 </securedutility>
109
110+ <!-- OCIProjectSeries -->
111+ <class
112+ class="lp.registry.model.ociprojectseries.OCIProjectSeries">
113+ <require
114+ permission="launchpad.View"
115+ interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesView
116+ lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes"/>
117+ <require
118+ permission="launchpad.Edit"
119+ interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEdit"
120+ set_schema="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes" />
121+ </class>
122+
123 <!-- SourcePackageName -->
124
125 <class
126diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
127index 64bb50c..100dccd 100644
128--- a/lib/lp/registry/interfaces/ociproject.py
129+++ b/lib/lp/registry/interfaces/ociproject.py
130@@ -11,15 +11,11 @@ __all__ = [
131 'IOCIProjectSet',
132 ]
133
134-from lazr.restful.declarations import (
135- export_as_webservice_entry,
136- exported,
137- )
138-from lazr.restful.fields import Reference
139-from zope.interface import (
140- Attribute,
141- Interface,
142+from lazr.restful.fields import (
143+ CollectionField,
144+ Reference,
145 )
146+from zope.interface import Interface
147 from zope.schema import (
148 Datetime,
149 Int,
150@@ -30,7 +26,8 @@ from lp import _
151 from lp.bugs.interfaces.bugtarget import IBugTarget
152 from lp.registry.interfaces.distribution import IDistribution
153 from lp.registry.interfaces.ociprojectname import IOCIProjectName
154-from lp.registry.interfaces.role import IHasOwner
155+from lp.registry.interfaces.series import SeriesStatus
156+from lp.services.database.constants import DEFAULT
157 from lp.services.fields import PublicPersonChoice
158
159
160@@ -38,15 +35,20 @@ class IOCIProjectView(Interface):
161 """IOCIProject attributes that require launchpad.View permission."""
162
163 id = Int(title=_("ID"), required=True, readonly=True)
164- date_created = exported(
165- Datetime(title=_("Date created"), required=True), readonly=True)
166- date_last_modified = exported(
167- Datetime(title=_("Date last modified"), required=True), readonly=True)
168+ date_created = Datetime(
169+ title=_("Date created"), required=True, readonly=True)
170+ date_last_modified = Datetime(
171+ title=_("Date last modified"), required=True, readonly=True)
172
173- registrant = exported(PublicPersonChoice(
174+ registrant = PublicPersonChoice(
175 title=_("Registrant"),
176 description=_("The person that registered this project."),
177- vocabulary='ValidPersonOrTeam', required=True, readonly=True))
178+ vocabulary='ValidPersonOrTeam', required=True, readonly=True)
179+
180+ series = CollectionField(
181+ title=_("Series inside this OCI project."),
182+ # Really IOCIProjectSeries
183+ value_type=Reference(schema=Interface))
184
185
186 class IOCIProjectEditableAttributes(IBugTarget):
187@@ -58,24 +60,29 @@ class IOCIProjectEditableAttributes(IBugTarget):
188 distribution = Reference(
189 IDistribution,
190 title=_("The distribution that this OCI project is associated with."))
191- ociprojectname = exported(Reference(
192+ ociprojectname = Reference(
193 IOCIProjectName,
194 title=_("The name of this OCI project."),
195 required=True,
196- readonly=True))
197- description = exported(
198- Text(title=_("The description for this OCI project.")))
199- pillar = exported(Reference(
200+ readonly=True)
201+ description = Text(title=_("The description for this OCI project."))
202+ pillar = Reference(
203 IDistribution,
204- title=_("The pillar containing this target."), readonly=True))
205+ title=_("The pillar containing this target."), readonly=True)
206+
207
208+class IOCIProjectEdit(Interface):
209+ """IOCIProject attributes that require launchpad.Edit permission."""
210
211-class IOCIProject(IOCIProjectView,
212+ def newSeries(name, summary, registrant,
213+ status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT):
214+ """Creates a new `IOCIProjectSeries`."""
215+
216+
217+class IOCIProject(IOCIProjectView, IOCIProjectEdit,
218 IOCIProjectEditableAttributes):
219 """A project containing Open Container Initiative recipes."""
220
221- export_as_webservice_entry()
222-
223
224 class IOCIProjectSet(Interface):
225 """A utility to create and access OCI Projects."""
226diff --git a/lib/lp/registry/interfaces/ociprojectseries.py b/lib/lp/registry/interfaces/ociprojectseries.py
227new file mode 100644
228index 0000000..8ef9705
229--- /dev/null
230+++ b/lib/lp/registry/interfaces/ociprojectseries.py
231@@ -0,0 +1,82 @@
232+# Copyright 2019 Canonical Ltd. This software is licensed under the
233+# GNU Affero General Public License version 3 (see the file LICENSE).
234+
235+"""Interfaces to allow bug filing on multiple versions of an OCI Project."""
236+
237+from __future__ import absolute_import, print_function, unicode_literals
238+
239+__metaclass__ = type
240+__all__ = [
241+ 'IOCIProjectSeries',
242+ 'IOCIProjectSeriesEditableAttributes',
243+ 'IOCIProjectSeriesView',
244+ ]
245+
246+from lazr.restful.fields import Reference
247+from zope.interface import Interface
248+from zope.schema import (
249+ Choice,
250+ Datetime,
251+ Int,
252+ Text,
253+ TextLine,
254+ )
255+
256+from lp import _
257+from lp.app.validators.name import name_validator
258+from lp.registry.interfaces.ociproject import IOCIProject
259+from lp.registry.interfaces.series import SeriesStatus
260+from lp.services.fields import PublicPersonChoice
261+
262+
263+class IOCIProjectSeriesView(Interface):
264+ """IOCIProjectSeries attributes that require launchpad.View permission."""
265+
266+ id = Int(title=_("ID"), required=True, readonly=True)
267+
268+ ociproject = Reference(
269+ IOCIProject,
270+ title=_("The OCI project that this series belongs to."),
271+ required=True, readonly=True)
272+
273+ date_created = Datetime(
274+ title=_("Date created"), required=True, readonly=True,
275+ description=_(
276+ "The date on which this series was created in Launchpad."))
277+
278+ registrant = PublicPersonChoice(
279+ title=_("Registrant"),
280+ description=_("The person that registered this series."),
281+ vocabulary='ValidPersonOrTeam', required=True, readonly=True)
282+
283+
284+class IOCIProjectSeriesEditableAttributes(Interface):
285+ """IOCIProjectSeries attributes that can be edited.
286+
287+ These attributes need launchpad.View to see, and launchpad.Edit to change.
288+ """
289+
290+ name = TextLine(
291+ title=_("Name"), constraint=name_validator,
292+ required=True, readonly=False,
293+ description=_("The name of this series."))
294+
295+ summary = Text(
296+ title=_("Summary"), required=True, readonly=False,
297+ description=_("A brief summary of this series."))
298+
299+ status = Choice(
300+ title=_("Status"), required=True,
301+ vocabulary=SeriesStatus)
302+
303+
304+class IOCIProjectSeriesEdit(Interface):
305+ """IOCIProjectSeries attributes that require launchpad.Edit permission."""
306+
307+
308+class IOCIProjectSeries(IOCIProjectSeriesView, IOCIProjectSeriesEdit,
309+ IOCIProjectSeriesEditableAttributes):
310+ """A series of an Open Container Initiative project.
311+
312+ This is used to allow tracking bugs against multiple versions of images.
313+ """
314diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
315index 45de84d..7250692 100644
316--- a/lib/lp/registry/model/ociproject.py
317+++ b/lib/lp/registry/model/ociproject.py
318@@ -27,7 +27,9 @@ from lp.registry.interfaces.ociproject import (
319 IOCIProject,
320 IOCIProjectSet,
321 )
322+from lp.registry.interfaces.series import SeriesStatus
323 from lp.registry.model.ociprojectname import OCIProjectName
324+from lp.registry.model.ociprojectseries import OCIProjectSeries
325 from lp.services.database.constants import DEFAULT
326 from lp.services.database.interfaces import (
327 IMasterStore,
328@@ -81,6 +83,27 @@ class OCIProject(BugTargetBase, StormBase):
329 return "OCI project %s for %s" % (
330 self.ociprojectname.name, self.pillar.name)
331
332+ def newSeries(self, name, summary, registrant,
333+ status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT):
334+ """See `IOCIProject`."""
335+ series = OCIProjectSeries(
336+ ociproject=self,
337+ name=name,
338+ summary=summary,
339+ registrant=registrant,
340+ status=status,
341+ )
342+ return series
343+
344+ @property
345+ def series(self):
346+ """See `IOCIProject`."""
347+ ret = IStore(OCIProjectSeries).find(
348+ OCIProjectSeries,
349+ OCIProjectSeries.ociproject == self
350+ ).order_by(OCIProjectSeries.date_created)
351+ return ret
352+
353
354 @implementer(IOCIProjectSet)
355 class OCIProjectSet:
356diff --git a/lib/lp/registry/model/ociprojectseries.py b/lib/lp/registry/model/ociprojectseries.py
357new file mode 100644
358index 0000000..04a81e9
359--- /dev/null
360+++ b/lib/lp/registry/model/ociprojectseries.py
361@@ -0,0 +1,65 @@
362+# Copyright 2019 Canonical Ltd. This software is licensed under the
363+# GNU Affero General Public License version 3 (see the file LICENSE).
364+
365+"""Model implementing `IOCIProjectSeries`."""
366+
367+from __future__ import absolute_import, print_function, unicode_literals
368+
369+__metaclass__ = type
370+__all__ = [
371+ 'OCIProjectSeries',
372+ ]
373+
374+import pytz
375+from storm.locals import (
376+ DateTime,
377+ Int,
378+ Reference,
379+ Unicode,
380+ )
381+from zope.interface import implementer
382+
383+from lp.app.validators.name import valid_name
384+from lp.registry.errors import InvalidName
385+from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
386+from lp.registry.interfaces.series import SeriesStatus
387+from lp.services.database.constants import DEFAULT
388+from lp.services.database.enumcol import DBEnum
389+from lp.services.database.stormbase import StormBase
390+
391+
392+@implementer(IOCIProjectSeries)
393+class OCIProjectSeries(StormBase):
394+ """See `IOCIProjectSeries`."""
395+
396+ __storm_table__ = "OCIProjectSeries"
397+
398+ id = Int(primary=True)
399+
400+ ociproject_id = Int(name='ociproject', allow_none=False)
401+ ociproject = Reference(ociproject_id, "OCIProject.id")
402+
403+ name = Unicode(name="name", allow_none=False)
404+
405+ summary = Unicode(name="summary", allow_none=False)
406+
407+ date_created = DateTime(
408+ name="date_created", tzinfo=pytz.UTC, allow_none=False)
409+
410+ registrant_id = Int(name='registrant', allow_none=False)
411+ registrant = Reference(registrant_id, "Person.id")
412+
413+ status = DBEnum(
414+ name='status', allow_none=False, enum=SeriesStatus)
415+
416+ def __init__(self, ociproject, name, summary,
417+ registrant, status, date_created=DEFAULT):
418+ if not valid_name(name):
419+ raise InvalidName(
420+ "%s is not a valid name for an OCI project series." % name)
421+ self.name = name
422+ self.ociproject = ociproject
423+ self.summary = summary
424+ self.registrant = registrant
425+ self.status = status
426+ self.date_created = date_created
427diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py
428index a68f786..d170bbb 100644
429--- a/lib/lp/registry/tests/test_ociproject.py
430+++ b/lib/lp/registry/tests/test_ociproject.py
431@@ -7,12 +7,15 @@ from __future__ import absolute_import, print_function, unicode_literals
432
433 __metaclass__ = type
434
435+from testtools.testcase import ExpectedException
436 from zope.component import getUtility
437+from zope.security.interfaces import Unauthorized
438
439 from lp.registry.interfaces.ociproject import (
440 IOCIProject,
441 IOCIProjectSet,
442 )
443+from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
444 from lp.testing import (
445 admin_logged_in,
446 person_logged_in,
447@@ -30,6 +33,40 @@ class TestOCIProject(TestCaseWithFactory):
448 with admin_logged_in():
449 self.assertProvides(oci_project, IOCIProject)
450
451+ def test_newSeries(self):
452+ driver = self.factory.makePerson()
453+ distribution = self.factory.makeDistribution(driver=driver)
454+ registrant = self.factory.makePerson()
455+ oci_project = self.factory.makeOCIProject(pillar=distribution)
456+ with person_logged_in(driver):
457+ series = oci_project.newSeries(
458+ 'test-series',
459+ 'test-summary',
460+ registrant)
461+ self.assertProvides(series, IOCIProjectSeries)
462+
463+ def test_newSeries_bad_permissions(self):
464+ distribution = self.factory.makeDistribution()
465+ registrant = self.factory.makePerson()
466+ oci_project = self.factory.makeOCIProject(pillar=distribution)
467+ with ExpectedException(Unauthorized):
468+ oci_project.newSeries(
469+ 'test-series',
470+ 'test-summary',
471+ registrant)
472+
473+ def test_series(self):
474+ driver = self.factory.makePerson()
475+ distribution = self.factory.makeDistribution(driver=driver)
476+ first_oci_project = self.factory.makeOCIProject(pillar=distribution)
477+ second_oci_project = self.factory.makeOCIProject(pillar=distribution)
478+ with person_logged_in(driver):
479+ first_series = self.factory.makeOCIProjectSeries(
480+ oci_project=first_oci_project)
481+ self.factory.makeOCIProjectSeries(
482+ oci_project=second_oci_project)
483+ self.assertContentEqual([first_series], first_oci_project.series)
484+
485
486 class TestOCIProjectSet(TestCaseWithFactory):
487
488diff --git a/lib/lp/registry/tests/test_ociprojectseries.py b/lib/lp/registry/tests/test_ociprojectseries.py
489new file mode 100644
490index 0000000..3d4e6cf
491--- /dev/null
492+++ b/lib/lp/registry/tests/test_ociprojectseries.py
493@@ -0,0 +1,99 @@
494+# Copyright 2019 Canonical Ltd. This software is licensed under the
495+# GNU Affero General Public License version 3 (see the file LICENSE).
496+
497+"""Test OCIProjectSeries."""
498+
499+from __future__ import absolute_import, print_function, unicode_literals
500+
501+__metaclass__ = type
502+
503+from testtools.matchers import MatchesStructure
504+from testtools.testcase import ExpectedException
505+from zope.security.interfaces import Unauthorized
506+
507+from lp.registry.errors import InvalidName
508+from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
509+from lp.registry.interfaces.series import SeriesStatus
510+from lp.registry.model.ociprojectseries import OCIProjectSeries
511+from lp.services.database.constants import UTC_NOW
512+from lp.testing import (
513+ person_logged_in,
514+ TestCaseWithFactory,
515+ )
516+from lp.testing.layers import DatabaseFunctionalLayer
517+
518+
519+class TestOCIProjectSeries(TestCaseWithFactory):
520+
521+ layer = DatabaseFunctionalLayer
522+
523+ def test_implements_interface(self):
524+ name = 'test-name'
525+ oci_project = self.factory.makeOCIProject()
526+ summary = 'test_summary'
527+ registrant = self.factory.makePerson()
528+ status = SeriesStatus.DEVELOPMENT
529+ project_series = OCIProjectSeries(
530+ oci_project, name, summary, registrant, status)
531+ self.assertProvides(project_series, IOCIProjectSeries)
532+
533+ def test_init(self):
534+ name = 'test-name'
535+ oci_project = self.factory.makeOCIProject()
536+ summary = 'test_summary'
537+ registrant = self.factory.makePerson()
538+ status = SeriesStatus.DEVELOPMENT
539+ date_created = UTC_NOW
540+ project_series = OCIProjectSeries(
541+ oci_project, name, summary, registrant, status, date_created)
542+ self.assertThat(
543+ project_series, MatchesStructure.byEquality(
544+ ociproject=project_series.ociproject,
545+ name=project_series.name,
546+ summary=project_series.summary,
547+ registrant=project_series.registrant,
548+ status=project_series.status,
549+ date_created=project_series.date_created))
550+
551+ def test_invalid_name(self):
552+ name = 'invalid%20name'
553+ oci_project = self.factory.makeOCIProject()
554+ summary = 'test_summary'
555+ registrant = self.factory.makePerson()
556+ status = SeriesStatus.DEVELOPMENT
557+ with ExpectedException(InvalidName):
558+ OCIProjectSeries(oci_project, name, summary, registrant, status)
559+
560+ def test_edit_permissions_invalid(self):
561+ name = 'test-name'
562+ summary = 'test_summary'
563+ registrant = self.factory.makePerson()
564+ another_person = self.factory.makePerson()
565+
566+ driver = self.factory.makePerson()
567+ distribution = self.factory.makeDistribution(driver=driver)
568+
569+ with person_logged_in(driver):
570+ project_series = self.factory.makeOCIProject(
571+ pillar=distribution).newSeries(
572+ name, summary, registrant)
573+
574+ with person_logged_in(another_person):
575+ with ExpectedException(Unauthorized):
576+ project_series.name = 'not-allowed'
577+
578+ def test_edit_permissions_valid(self):
579+ name = 'test-name'
580+ summary = 'test_summary'
581+ registrant = self.factory.makePerson()
582+
583+ driver = self.factory.makePerson()
584+ distribution = self.factory.makeDistribution(driver=driver)
585+
586+ with person_logged_in(driver):
587+ project_series = self.factory.makeOCIProject(
588+ pillar=distribution).newSeries(
589+ name, summary, registrant)
590+ project_series.name = 'allowed'
591+
592+ self.assertEqual(project_series.name, 'allowed')
593diff --git a/lib/lp/security.py b/lib/lp/security.py
594index 050c1fe..21caa71 100644
595--- a/lib/lp/security.py
596+++ b/lib/lp/security.py
597@@ -136,6 +136,8 @@ from lp.registry.interfaces.nameblacklist import (
598 INameBlacklist,
599 INameBlacklistSet,
600 )
601+from lp.registry.interfaces.ociproject import IOCIProject
602+from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
603 from lp.registry.interfaces.packaging import IPackaging
604 from lp.registry.interfaces.person import (
605 IPerson,
606@@ -3430,3 +3432,26 @@ class EditSnapBase(EditByRegistryExpertsOrAdmins):
607
608 class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
609 usedfor = ISnapBaseSet
610+
611+
612+class EditOCIProject(AuthorizationBase):
613+ permission = 'launchpad.Edit'
614+ usedfor = IOCIProject
615+
616+ def checkAuthenticated(self, user):
617+ """Maintainers, drivers, and admins can drive projects."""
618+ # XXX twom 2019-10-29 This ideally shouldn't be driver, but a
619+ # new role name that cascades upwards from the OCIProject
620+ # to the pillar
621+ return (user.in_admin or
622+ user.isDriver(self.obj.pillar))
623+
624+
625+class EditOCIProjectSeries(AuthorizationBase):
626+ permission = 'launchpad.Edit'
627+ usedfor = IOCIProjectSeries
628+
629+ def checkAuthenticated(self, user):
630+ """Maintainers, drivers, and admins can drive projects."""
631+ return (user.in_admin or
632+ user.isDriver(self.obj.ociproject.pillar))
633diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
634index 5c0cc18..39d2725 100644
635--- a/lib/lp/testing/factory.py
636+++ b/lib/lp/testing/factory.py
637@@ -4904,10 +4904,10 @@ class BareLaunchpadObjectFactory(ObjectFactory):
638 return getUtility(IOCIProjectNameSet).new(name)
639
640 def makeOCIProject(self, registrant=None, pillar=None,
641- ociprojectname=None, date_created=DEFAULT,
642- description=None, bug_reporting_guidelines=None,
643- bug_reported_acknowledgement=None,
644- bugfiling_duplicate_search=False):
645+ ociprojectname=None, date_created=DEFAULT,
646+ description=None, bug_reporting_guidelines=None,
647+ bug_reported_acknowledgement=None,
648+ bugfiling_duplicate_search=False):
649 """Make a new OCIProject."""
650 if registrant is None:
651 registrant = self.makePerson()
652@@ -4922,6 +4922,19 @@ class BareLaunchpadObjectFactory(ObjectFactory):
653 bug_reported_acknowledgement=bug_reported_acknowledgement,
654 bugfiling_duplicate_search=bugfiling_duplicate_search)
655
656+ def makeOCIProjectSeries(self, name=None, summary=None, registrant=None,
657+ oci_project=None, **kwargs):
658+ """Make a new OCIProjectSeries attached to an OCIProject."""
659+ if name is None:
660+ name = self.getUniqueString(u"oci-project-series-name")
661+ if summary is None:
662+ summary = self.getUniqueString(u"oci-project-series-summary")
663+ if registrant is None:
664+ registrant = self.makePerson()
665+ if oci_project is None:
666+ oci_project = self.makeOCIProject(**kwargs)
667+ return oci_project.newSeries(name, summary, registrant)
668+
669
670 # Some factory methods return simple Python types. We don't add
671 # security wrappers for them, as well as for objects created by

Subscribers

People subscribed via source and target branches

to status/vote changes: