Merge ~twom/launchpad:oci-recipe-targetseries into launchpad:master
- Git
- lp:~twom/launchpad
- oci-recipe-targetseries
- Merge into 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) |
||||
Related bugs: |
|
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
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/database/schema/security.cfg b/database/schema/security.cfg |
2 | index 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 |
94 | diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml |
95 | index 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 |
126 | diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py |
127 | index 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.""" |
226 | diff --git a/lib/lp/registry/interfaces/ociprojectseries.py b/lib/lp/registry/interfaces/ociprojectseries.py |
227 | new file mode 100644 |
228 | index 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 | + """ |
314 | diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py |
315 | index 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: |
356 | diff --git a/lib/lp/registry/model/ociprojectseries.py b/lib/lp/registry/model/ociprojectseries.py |
357 | new file mode 100644 |
358 | index 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 |
427 | diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py |
428 | index 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 | |
488 | diff --git a/lib/lp/registry/tests/test_ociprojectseries.py b/lib/lp/registry/tests/test_ociprojectseries.py |
489 | new file mode 100644 |
490 | index 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') |
593 | diff --git a/lib/lp/security.py b/lib/lp/security.py |
594 | index 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)) |
633 | diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py |
634 | index 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 |
Looks mostly fine, thanks, but some minor attention-to-detail kinds of things. Also, lint complains:
./lib/lp/ registry/ interfaces/ ociproject. py registry/ interfaces/ ociprojectserie s.py as_webservice_ entry' imported but unused registry/ model/ociprojec t.py registry/ model/ociprojec tseries. py registry/ tests/test_ ociproject. py registry/ tests/test_ ociprojectserie s.py
37: 'IHasOwner' imported but unused
22: 'Attribute' imported but unused
./lib/lp/
15: 'export_
32: 'IPerson' imported but unused
15: 'exported' imported but unused
./lib/lp/
109: E303 too many blank lines (3)
./lib/lp/
28: 'IMasterStore' imported but unused
./lib/lp/
55: local variable 'second_series' is assigned to but never used
./lib/lp/
10: 'getUtility' imported but unused