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

Proposed by Tom Wardill
Status: Merged
Approved by: Tom Wardill
Approved revision: fcbd484fb807a247fc69e4cbfd737e4d2232896c
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~twom/launchpad:oci-recipe-target
Merge into: launchpad:master
Prerequisite: ~twom/launchpad:oci-recipe-name
Diff against target: 467 lines (+339/-0)
6 files modified
database/schema/security.cfg (+11/-0)
lib/lp/registry/configure.zcml (+24/-0)
lib/lp/registry/interfaces/ociproject.py (+90/-0)
lib/lp/registry/model/ociproject.py (+123/-0)
lib/lp/registry/tests/test_ociproject.py (+71/-0)
lib/lp/testing/factory.py (+20/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+373756@code.launchpad.net

Commit message

Implement OCIProject

Description of the change

Add model, interfaces, ZCML and security.cfg

To post a comment you must log in.
~twom/launchpad:oci-recipe-target updated
399e651... by Tom Wardill

Rename IOCIRecipeTargetSource to IOCIRecipeTargetSet

f84a3fc... by Tom Wardill

Add file headers from standard_template

8c59ca2... by Tom Wardill

Docstrings

Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
~twom/launchpad:oci-recipe-target updated
03052cf... by Tom Wardill

Format imports

acef142... by Tom Wardill

person-merge-job needs UPDATE

a905e39... by Tom Wardill

More standard_template application

5895367... by Tom Wardill

Interface changes for removing product

50007ab... by Tom Wardill

Swap distribution for pillar

90bd54c... by Tom Wardill

Refactor for separate OCIRecipeTargetSet

327b7a7... by Tom Wardill

Move to getByDistributionAndName

350f719... by Tom Wardill

Implement IBugTarget

46d29ac... by Tom Wardill

Lint errors

a5f7ee9... by Tom Wardill

Split tests

d047e4a... by Tom Wardill

Lint

546542a... by Tom Wardill

Rename OCIRecipeName to OCIProjectName

5b2b9ee... by Tom Wardill

Rename OCIRecipeTarget to OCIProject

ece95d8... by Tom Wardill

Bug_supervisor doesn't exist anymore

a07881b... by Tom Wardill

Fix rebase problems

f232995... by Tom Wardill

Erroneous whitespace

292ca5c... by Tom Wardill

Rename more Recipe to Project

Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
Colin Watson (cjwatson) :
~twom/launchpad:oci-recipe-target updated
610e814... by Tom Wardill

Wording fixes and we don't need to implement IHasOwner

2aef9e5... by Tom Wardill

More wording fixes

8147347... by Tom Wardill

Test variable fixes

0154ec9... by Tom Wardill

Export pillar as read only

fcbd484... by Tom Wardill

Export pillar

Revision history for this message
Colin Watson (cjwatson) :
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 899ddde..0e0e91e 100644
3--- a/database/schema/security.cfg
4+++ b/database/schema/security.cfg
5@@ -1241,6 +1241,7 @@ 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@@ -1417,6 +1418,7 @@ public.message = SELECT, INSERT
14 public.messagechunk = SELECT, INSERT
15 public.milestone = SELECT
16 public.milestonetag = SELECT
17+public.ociproject = SELECT
18 public.ociprojectname = SELECT
19 public.openididentifier = SELECT
20 public.packagecopyjob = SELECT, INSERT
21@@ -1534,6 +1536,7 @@ public.message = SELECT, INSERT
22 public.messagechunk = SELECT, INSERT
23 public.milestone = SELECT
24 public.milestonetag = SELECT
25+public.ociproject = SELECT
26 public.ociprojectname = SELECT
27 public.openididentifier = SELECT
28 public.packagecopyjob = SELECT, INSERT, UPDATE
29@@ -1637,6 +1640,7 @@ public.message = SELECT, INSERT
30 public.messagechunk = SELECT, INSERT
31 public.milestone = SELECT
32 public.milestonetag = SELECT
33+public.ociproject = SELECT
34 public.ociprojectname = SELECT
35 public.person = SELECT
36 public.personlanguage = SELECT
37@@ -1839,6 +1843,7 @@ public.message = SELECT, INSERT
38 public.messagechunk = SELECT, INSERT
39 public.milestone = SELECT
40 public.milestonetag = SELECT, INSERT, DELETE
41+public.ociproject = SELECT
42 public.ociprojectname = SELECT
43 public.openididentifier = SELECT
44 public.packageset = SELECT
45@@ -1959,6 +1964,7 @@ public.libraryfilealias = SELECT, INSERT
46 public.libraryfilecontent = SELECT, INSERT
47 public.message = SELECT, INSERT
48 public.messagechunk = SELECT, INSERT
49+public.ociproject = SELECT
50 public.ociprojectname = SELECT
51 public.openididentifier = SELECT
52 public.person = SELECT
53@@ -2017,6 +2023,7 @@ public.libraryfilecontent = SELECT, INSERT
54 public.message = SELECT, INSERT
55 public.messagechunk = SELECT, INSERT
56 public.milestone = SELECT
57+public.ociproject = SELECT
58 public.ociprojectname = SELECT
59 public.person = SELECT
60 public.personsettings = SELECT
61@@ -2154,6 +2161,7 @@ public.job = SELECT, INSERT, UPDATE
62 public.message = SELECT, INSERT
63 public.messagechunk = SELECT, INSERT
64 public.milestonetag = SELECT
65+public.ociproject = SELECT
66 public.ociprojectname = SELECT
67 public.person = SELECT, INSERT
68 public.personsettings = SELECT, INSERT
69@@ -2544,6 +2552,7 @@ public.distroseries = SELECT
70 public.emailaddress = SELECT
71 public.gitrepository = SELECT
72 public.job = SELECT, INSERT, UPDATE
73+public.ociproject = SELECT
74 public.ociprojectname = SELECT
75 public.person = SELECT
76 public.packaging = SELECT
77@@ -2567,6 +2576,7 @@ public.bugtask = SELECT
78 public.bugtaskflat = SELECT
79 public.distribution = SELECT
80 public.distroseries = SELECT
81+public.ociproject = SELECT
82 public.ociprojectname = SELECT
83 public.product = SELECT
84 public.productseries = SELECT
85@@ -2579,6 +2589,7 @@ public.branch = SELECT
86 public.distribution = SELECT
87 public.gitrepository = SELECT
88 public.job = SELECT, UPDATE
89+public.ociproject = SELECT
90 public.ociprojectname = SELECT
91 public.person = SELECT
92 public.product = SELECT
93diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml
94index 730efbb..66f1d14 100644
95--- a/lib/lp/registry/configure.zcml
96+++ b/lib/lp/registry/configure.zcml
97@@ -734,6 +734,30 @@
98 interface="lp.registry.interfaces.ociprojectname.IOCIProjectNameSet"/>
99 </class>
100
101+ <!-- OCIProject -->
102+ <class
103+ class="lp.registry.model.ociproject.OCIProject">
104+ <require
105+ permission="launchpad.View"
106+ interface="lp.registry.interfaces.ociproject.IOCIProjectView
107+ lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/>
108+ <require
109+ permission="launchpad.Edit"
110+ set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" />
111+ </class>
112+ <securedutility
113+ class="lp.registry.model.ociproject.OCIProject"
114+ provides="lp.registry.interfaces.ociproject.IOCIProject">
115+ <allow
116+ interface="lp.registry.interfaces.ociproject.IOCIProject"/>
117+ </securedutility>
118+ <securedutility
119+ class="lp.registry.model.ociproject.OCIProjectSet"
120+ provides="lp.registry.interfaces.ociproject.IOCIProjectSet">
121+ <allow
122+ interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/>
123+ </securedutility>
124+
125 <!-- SourcePackageName -->
126
127 <class
128diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
129new file mode 100644
130index 0000000..64bb50c
131--- /dev/null
132+++ b/lib/lp/registry/interfaces/ociproject.py
133@@ -0,0 +1,90 @@
134+# Copyright 2019 Canonical Ltd. This software is licensed under the
135+# GNU Affero General Public License version 3 (see the file LICENSE).
136+
137+"""OCI Project interfaces."""
138+
139+from __future__ import absolute_import, print_function, unicode_literals
140+
141+__metaclass__ = type
142+__all__ = [
143+ 'IOCIProject',
144+ 'IOCIProjectSet',
145+ ]
146+
147+from lazr.restful.declarations import (
148+ export_as_webservice_entry,
149+ exported,
150+ )
151+from lazr.restful.fields import Reference
152+from zope.interface import (
153+ Attribute,
154+ Interface,
155+ )
156+from zope.schema import (
157+ Datetime,
158+ Int,
159+ Text,
160+ )
161+
162+from lp import _
163+from lp.bugs.interfaces.bugtarget import IBugTarget
164+from lp.registry.interfaces.distribution import IDistribution
165+from lp.registry.interfaces.ociprojectname import IOCIProjectName
166+from lp.registry.interfaces.role import IHasOwner
167+from lp.services.fields import PublicPersonChoice
168+
169+
170+class IOCIProjectView(Interface):
171+ """IOCIProject attributes that require launchpad.View permission."""
172+
173+ id = Int(title=_("ID"), required=True, readonly=True)
174+ date_created = exported(
175+ Datetime(title=_("Date created"), required=True), readonly=True)
176+ date_last_modified = exported(
177+ Datetime(title=_("Date last modified"), required=True), readonly=True)
178+
179+ registrant = exported(PublicPersonChoice(
180+ title=_("Registrant"),
181+ description=_("The person that registered this project."),
182+ vocabulary='ValidPersonOrTeam', required=True, readonly=True))
183+
184+
185+class IOCIProjectEditableAttributes(IBugTarget):
186+ """IOCIProject attributes that can be edited.
187+
188+ These attributes need launchpad.View to see, and launchpad.Edit to change.
189+ """
190+
191+ distribution = Reference(
192+ IDistribution,
193+ title=_("The distribution that this OCI project is associated with."))
194+ ociprojectname = exported(Reference(
195+ IOCIProjectName,
196+ title=_("The name of this OCI project."),
197+ required=True,
198+ readonly=True))
199+ description = exported(
200+ Text(title=_("The description for this OCI project.")))
201+ pillar = exported(Reference(
202+ IDistribution,
203+ title=_("The pillar containing this target."), readonly=True))
204+
205+
206+class IOCIProject(IOCIProjectView,
207+ IOCIProjectEditableAttributes):
208+ """A project containing Open Container Initiative recipes."""
209+
210+ export_as_webservice_entry()
211+
212+
213+class IOCIProjectSet(Interface):
214+ """A utility to create and access OCI Projects."""
215+
216+ def new(registrant, pillar, ociprojectname,
217+ date_created=None, description=None,
218+ bug_reporting_guidelines=None, bug_reported_acknowledgement=None,
219+ bugfiling_duplicate_search=False):
220+ """Create an `IOCIProject`."""
221+
222+ def getByDistributionAndName(distribution, name):
223+ """Get the OCIProjects for a given distribution."""
224diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
225new file mode 100644
226index 0000000..45de84d
227--- /dev/null
228+++ b/lib/lp/registry/model/ociproject.py
229@@ -0,0 +1,123 @@
230+# Copyright 2019 Canonical Ltd. This software is licensed under the
231+# GNU Affero General Public License version 3 (see the file LICENSE).
232+
233+"""OCI Project implementation."""
234+
235+from __future__ import absolute_import, print_function, unicode_literals
236+
237+__metaclass__ = type
238+__all__ = [
239+ 'OCIProject',
240+ 'OCIProjectSet',
241+ ]
242+
243+import pytz
244+from storm.locals import (
245+ Bool,
246+ DateTime,
247+ Int,
248+ Reference,
249+ Unicode,
250+ )
251+from zope.interface import implementer
252+
253+from lp.bugs.model.bugtarget import BugTargetBase
254+from lp.registry.interfaces.distribution import IDistribution
255+from lp.registry.interfaces.ociproject import (
256+ IOCIProject,
257+ IOCIProjectSet,
258+ )
259+from lp.registry.model.ociprojectname import OCIProjectName
260+from lp.services.database.constants import DEFAULT
261+from lp.services.database.interfaces import (
262+ IMasterStore,
263+ IStore,
264+ )
265+from lp.services.database.stormbase import StormBase
266+
267+
268+@implementer(IOCIProject)
269+class OCIProject(BugTargetBase, StormBase):
270+ """See `IOCIProject` and `IOCIProjectSet`."""
271+
272+ __storm_table__ = "OCIProject"
273+
274+ id = Int(primary=True)
275+ date_created = DateTime(
276+ name="date_created", tzinfo=pytz.UTC, allow_none=False)
277+ date_last_modified = DateTime(
278+ name="date_last_modified", tzinfo=pytz.UTC, allow_none=False)
279+
280+ registrant_id = Int(name='registrant', allow_none=False)
281+ registrant = Reference(registrant_id, "Person.id")
282+
283+ distribution_id = Int(name="distribution", allow_none=True)
284+ distribution = Reference(distribution_id, "Distribution.id")
285+
286+ ociprojectname_id = Int(name="ociprojectname", allow_none=False)
287+ ociprojectname = Reference(ociprojectname_id, "OCIProjectName.id")
288+
289+ description = Unicode(name="description")
290+
291+ bug_reporting_guidelines = Unicode(name="bug_reporting_guidelines")
292+ bug_reported_acknowledgement = Unicode(name="bug_reported_acknowledgement")
293+ enable_bugfiling_duplicate_search = Bool(
294+ name="enable_bugfiling_duplicate_search")
295+
296+ @property
297+ def pillar(self):
298+ """See `IBugTarget`."""
299+ return self.distribution
300+
301+ @property
302+ def bugtargetname(self):
303+ """See `IBugTarget`."""
304+ return "OCI project %s for %s" % (
305+ self.ociprojectname.name, self.pillar.name)
306+
307+ @property
308+ def bugtargetdisplayname(self):
309+ """See `IBugTarget`."""
310+ return "OCI project %s for %s" % (
311+ self.ociprojectname.name, self.pillar.name)
312+
313+
314+@implementer(IOCIProjectSet)
315+class OCIProjectSet:
316+
317+ def new(self, registrant, pillar, ociprojectname,
318+ date_created=DEFAULT, description=None,
319+ bug_reporting_guidelines=None,
320+ bug_reported_acknowledgement=None,
321+ bugfiling_duplicate_search=False):
322+ """See `IOCIProjectSet`."""
323+ store = IMasterStore(OCIProject)
324+ target = OCIProject()
325+ target.date_created = date_created
326+ target.date_last_modified = date_created
327+
328+ # XXX twom 2019-10-10 This needs to have IProduct support
329+ # when the model supports it
330+ if IDistribution.providedBy(pillar):
331+ target.distribution = pillar
332+ else:
333+ raise ValueError(
334+ 'The target of an OCIProject must be an '
335+ 'IDistribution instance.')
336+
337+ target.registrant = registrant
338+ target.ociprojectname = ociprojectname
339+ target.description = description
340+ target.bug_reporting_guidelines = bug_reporting_guidelines
341+ target.enable_bugfiling_duplicate_search = bugfiling_duplicate_search
342+ store.add(target)
343+ return target
344+
345+ def getByDistributionAndName(self, distribution, name):
346+ """See `IOCIProjectSet`."""
347+ target = IStore(OCIProject).find(
348+ OCIProject,
349+ OCIProject.distribution == distribution,
350+ OCIProject.ociprojectname == OCIProjectName.id,
351+ OCIProjectName.name == name).one()
352+ return target
353diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py
354new file mode 100644
355index 0000000..a68f786
356--- /dev/null
357+++ b/lib/lp/registry/tests/test_ociproject.py
358@@ -0,0 +1,71 @@
359+# Copyright 2019 Canonical Ltd. This software is licensed under the
360+# GNU Affero General Public License version 3 (see the file LICENSE).
361+
362+"""Tests for `OCIProject` and `OCIProjectSet`."""
363+
364+from __future__ import absolute_import, print_function, unicode_literals
365+
366+__metaclass__ = type
367+
368+from zope.component import getUtility
369+
370+from lp.registry.interfaces.ociproject import (
371+ IOCIProject,
372+ IOCIProjectSet,
373+ )
374+from lp.testing import (
375+ admin_logged_in,
376+ person_logged_in,
377+ TestCaseWithFactory,
378+ )
379+from lp.testing.layers import DatabaseFunctionalLayer
380+
381+
382+class TestOCIProject(TestCaseWithFactory):
383+
384+ layer = DatabaseFunctionalLayer
385+
386+ def test_implements_interface(self):
387+ oci_project = self.factory.makeOCIProject()
388+ with admin_logged_in():
389+ self.assertProvides(oci_project, IOCIProject)
390+
391+
392+class TestOCIProjectSet(TestCaseWithFactory):
393+
394+ layer = DatabaseFunctionalLayer
395+
396+ def test_implements_interface(self):
397+ target_set = getUtility(IOCIProjectSet)
398+ with admin_logged_in():
399+ self.assertProvides(target_set, IOCIProjectSet)
400+
401+ def test_new_oci_project(self):
402+ registrant = self.factory.makePerson()
403+ distribution = self.factory.makeDistribution(owner=registrant)
404+ oci_project_name = self.factory.makeOCIProjectName()
405+ target = getUtility(IOCIProjectSet).new(
406+ registrant,
407+ distribution,
408+ oci_project_name)
409+ with person_logged_in(registrant):
410+ self.assertEqual(target.registrant, registrant)
411+ self.assertEqual(target.distribution, distribution)
412+ self.assertEqual(target.pillar, distribution)
413+ self.assertEqual(target.ociprojectname, oci_project_name)
414+
415+ def test_getByDistributionAndName(self):
416+ registrant = self.factory.makePerson()
417+ distribution = self.factory.makeDistribution(owner=registrant)
418+ oci_project = self.factory.makeOCIProject(
419+ registrant=registrant, pillar=distribution)
420+
421+ # Make sure there's more than one to get the result from
422+ self.factory.makeOCIProject(
423+ pillar=self.factory.makeDistribution())
424+
425+ with person_logged_in(registrant):
426+ fetched_result = getUtility(
427+ IOCIProjectSet).getByDistributionAndName(
428+ distribution, oci_project.ociprojectname.name)
429+ self.assertEqual(oci_project, fetched_result)
430diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
431index ed2536b..5c0cc18 100644
432--- a/lib/lp/testing/factory.py
433+++ b/lib/lp/testing/factory.py
434@@ -200,6 +200,7 @@ from lp.registry.interfaces.mailinglist import (
435 from lp.registry.interfaces.mailinglistsubscription import (
436 MailingListAutoSubscribePolicy,
437 )
438+from lp.registry.interfaces.ociproject import IOCIProjectSet
439 from lp.registry.interfaces.ociprojectname import IOCIProjectNameSet
440 from lp.registry.interfaces.packaging import (
441 IPackagingUtil,
442@@ -4902,6 +4903,25 @@ class BareLaunchpadObjectFactory(ObjectFactory):
443 name = self.getUniqueString(u"oci-project-name")
444 return getUtility(IOCIProjectNameSet).new(name)
445
446+ def makeOCIProject(self, registrant=None, pillar=None,
447+ ociprojectname=None, date_created=DEFAULT,
448+ description=None, bug_reporting_guidelines=None,
449+ bug_reported_acknowledgement=None,
450+ bugfiling_duplicate_search=False):
451+ """Make a new OCIProject."""
452+ if registrant is None:
453+ registrant = self.makePerson()
454+ if pillar is None:
455+ pillar = self.makeDistribution()
456+ if ociprojectname is None or isinstance(ociprojectname, six.text_type):
457+ ociprojectname = self.makeOCIProjectName(ociprojectname)
458+ return getUtility(IOCIProjectSet).new(
459+ registrant, pillar, ociprojectname, date_created=date_created,
460+ description=description,
461+ bug_reporting_guidelines=bug_reporting_guidelines,
462+ bug_reported_acknowledgement=bug_reported_acknowledgement,
463+ bugfiling_duplicate_search=bugfiling_duplicate_search)
464+
465
466 # Some factory methods return simple Python types. We don't add
467 # security wrappers for them, as well as for objects created by

Subscribers

People subscribed via source and target branches

to status/vote changes: