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 | 1241 | public.ociprojectname = SELECT, INSERT, UPDATE | 1241 | public.ociprojectname = SELECT, INSERT, UPDATE |
7 | 1242 | public.ociprojectseries = SELECT, INSERT, UPDATE, DELETE | 1242 | public.ociprojectseries = SELECT, INSERT, UPDATE, DELETE |
8 | 1243 | public.openididentifier = SELECT | 1243 | public.openididentifier = SELECT |
9 | 1244 | public.ociproject = SELECT, INSERT, UPDATE, DELETE | ||
10 | 1245 | public.ociprojectname = SELECT, INSERT, UPDATE | ||
11 | 1246 | public.packageupload = SELECT, INSERT, UPDATE | 1244 | public.packageupload = SELECT, INSERT, UPDATE |
12 | 1247 | public.packageuploadbuild = SELECT, INSERT, UPDATE | 1245 | public.packageuploadbuild = SELECT, INSERT, UPDATE |
13 | 1248 | public.packageuploadcustom = SELECT, INSERT, UPDATE | 1246 | public.packageuploadcustom = SELECT, INSERT, UPDATE |
14 | @@ -1420,6 +1418,7 @@ public.milestone = SELECT | |||
15 | 1420 | public.milestonetag = SELECT | 1418 | public.milestonetag = SELECT |
16 | 1421 | public.ociproject = SELECT | 1419 | public.ociproject = SELECT |
17 | 1422 | public.ociprojectname = SELECT | 1420 | public.ociprojectname = SELECT |
18 | 1421 | public.ociprojectseries = SELECT | ||
19 | 1423 | public.openididentifier = SELECT | 1422 | public.openididentifier = SELECT |
20 | 1424 | public.packagecopyjob = SELECT, INSERT | 1423 | public.packagecopyjob = SELECT, INSERT |
21 | 1425 | public.packagediff = SELECT, INSERT, UPDATE, DELETE | 1424 | public.packagediff = SELECT, INSERT, UPDATE, DELETE |
22 | @@ -1538,6 +1537,7 @@ public.milestone = SELECT | |||
23 | 1538 | public.milestonetag = SELECT | 1537 | public.milestonetag = SELECT |
24 | 1539 | public.ociproject = SELECT | 1538 | public.ociproject = SELECT |
25 | 1540 | public.ociprojectname = SELECT | 1539 | public.ociprojectname = SELECT |
26 | 1540 | public.ociprojectseries = SELECT | ||
27 | 1541 | public.openididentifier = SELECT | 1541 | public.openididentifier = SELECT |
28 | 1542 | public.packagecopyjob = SELECT, INSERT, UPDATE | 1542 | public.packagecopyjob = SELECT, INSERT, UPDATE |
29 | 1543 | public.packagediff = SELECT, UPDATE | 1543 | public.packagediff = SELECT, UPDATE |
30 | @@ -1642,6 +1642,7 @@ public.milestone = SELECT | |||
31 | 1642 | public.milestonetag = SELECT | 1642 | public.milestonetag = SELECT |
32 | 1643 | public.ociproject = SELECT | 1643 | public.ociproject = SELECT |
33 | 1644 | public.ociprojectname = SELECT | 1644 | public.ociprojectname = SELECT |
34 | 1645 | public.ociprojectseries = SELECT | ||
35 | 1645 | public.person = SELECT | 1646 | public.person = SELECT |
36 | 1646 | public.personlanguage = SELECT | 1647 | public.personlanguage = SELECT |
37 | 1647 | public.personsettings = SELECT | 1648 | public.personsettings = SELECT |
38 | @@ -1845,6 +1846,7 @@ public.milestone = SELECT | |||
39 | 1845 | public.milestonetag = SELECT, INSERT, DELETE | 1846 | public.milestonetag = SELECT, INSERT, DELETE |
40 | 1846 | public.ociproject = SELECT | 1847 | public.ociproject = SELECT |
41 | 1847 | public.ociprojectname = SELECT | 1848 | public.ociprojectname = SELECT |
42 | 1849 | public.ociprojectseries = SELECT | ||
43 | 1848 | public.openididentifier = SELECT | 1850 | public.openididentifier = SELECT |
44 | 1849 | public.packageset = SELECT | 1851 | public.packageset = SELECT |
45 | 1850 | public.packagesetgroup = SELECT | 1852 | public.packagesetgroup = SELECT |
46 | @@ -1966,6 +1968,7 @@ public.message = SELECT, INSERT | |||
47 | 1966 | public.messagechunk = SELECT, INSERT | 1968 | public.messagechunk = SELECT, INSERT |
48 | 1967 | public.ociproject = SELECT | 1969 | public.ociproject = SELECT |
49 | 1968 | public.ociprojectname = SELECT | 1970 | public.ociprojectname = SELECT |
50 | 1971 | public.ociprojectseries = SELECT | ||
51 | 1969 | public.openididentifier = SELECT | 1972 | public.openididentifier = SELECT |
52 | 1970 | public.person = SELECT | 1973 | public.person = SELECT |
53 | 1971 | public.product = SELECT | 1974 | public.product = SELECT |
54 | @@ -2025,6 +2028,7 @@ public.messagechunk = SELECT, INSERT | |||
55 | 2025 | public.milestone = SELECT | 2028 | public.milestone = SELECT |
56 | 2026 | public.ociproject = SELECT | 2029 | public.ociproject = SELECT |
57 | 2027 | public.ociprojectname = SELECT | 2030 | public.ociprojectname = SELECT |
58 | 2031 | public.ociprojectseries = SELECT | ||
59 | 2028 | public.person = SELECT | 2032 | public.person = SELECT |
60 | 2029 | public.personsettings = SELECT | 2033 | public.personsettings = SELECT |
61 | 2030 | public.previewdiff = SELECT, INSERT | 2034 | public.previewdiff = SELECT, INSERT |
62 | @@ -2163,6 +2167,7 @@ public.messagechunk = SELECT, INSERT | |||
63 | 2163 | public.milestonetag = SELECT | 2167 | public.milestonetag = SELECT |
64 | 2164 | public.ociproject = SELECT | 2168 | public.ociproject = SELECT |
65 | 2165 | public.ociprojectname = SELECT | 2169 | public.ociprojectname = SELECT |
66 | 2170 | public.ociprojectseries = SELECT | ||
67 | 2166 | public.person = SELECT, INSERT | 2171 | public.person = SELECT, INSERT |
68 | 2167 | public.personsettings = SELECT, INSERT | 2172 | public.personsettings = SELECT, INSERT |
69 | 2168 | public.product = SELECT, INSERT, UPDATE | 2173 | public.product = SELECT, INSERT, UPDATE |
70 | @@ -2554,6 +2559,7 @@ public.gitrepository = SELECT | |||
71 | 2554 | public.job = SELECT, INSERT, UPDATE | 2559 | public.job = SELECT, INSERT, UPDATE |
72 | 2555 | public.ociproject = SELECT | 2560 | public.ociproject = SELECT |
73 | 2556 | public.ociprojectname = SELECT | 2561 | public.ociprojectname = SELECT |
74 | 2562 | public.ociprojectseries = SELECT | ||
75 | 2557 | public.person = SELECT | 2563 | public.person = SELECT |
76 | 2558 | public.packaging = SELECT | 2564 | public.packaging = SELECT |
77 | 2559 | public.product = SELECT, UPDATE | 2565 | public.product = SELECT, UPDATE |
78 | @@ -2578,6 +2584,7 @@ public.distribution = SELECT | |||
79 | 2578 | public.distroseries = SELECT | 2584 | public.distroseries = SELECT |
80 | 2579 | public.ociproject = SELECT | 2585 | public.ociproject = SELECT |
81 | 2580 | public.ociprojectname = SELECT | 2586 | public.ociprojectname = SELECT |
82 | 2587 | public.ociprojectseries = SELECT | ||
83 | 2581 | public.product = SELECT | 2588 | public.product = SELECT |
84 | 2582 | public.productseries = SELECT | 2589 | public.productseries = SELECT |
85 | 2583 | public.sourcepackagename = SELECT | 2590 | public.sourcepackagename = SELECT |
86 | @@ -2591,6 +2598,7 @@ public.gitrepository = SELECT | |||
87 | 2591 | public.job = SELECT, UPDATE | 2598 | public.job = SELECT, UPDATE |
88 | 2592 | public.ociproject = SELECT | 2599 | public.ociproject = SELECT |
89 | 2593 | public.ociprojectname = SELECT | 2600 | public.ociprojectname = SELECT |
90 | 2601 | public.ociprojectseries = SELECT | ||
91 | 2594 | public.person = SELECT | 2602 | public.person = SELECT |
92 | 2595 | public.product = SELECT | 2603 | public.product = SELECT |
93 | 2596 | public.snap = SELECT | 2604 | 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 | 743 | lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/> | 743 | lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes"/> |
100 | 744 | <require | 744 | <require |
101 | 745 | permission="launchpad.Edit" | 745 | permission="launchpad.Edit" |
102 | 746 | interface="lp.registry.interfaces.ociproject.IOCIProjectEdit" | ||
103 | 746 | set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" /> | 747 | set_schema="lp.registry.interfaces.ociproject.IOCIProjectEditableAttributes" /> |
104 | 747 | </class> | 748 | </class> |
105 | 748 | <securedutility | 749 | <securedutility |
106 | @@ -758,6 +759,19 @@ | |||
107 | 758 | interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/> | 759 | interface="lp.registry.interfaces.ociproject.IOCIProjectSet"/> |
108 | 759 | </securedutility> | 760 | </securedutility> |
109 | 760 | 761 | ||
110 | 762 | <!-- OCIProjectSeries --> | ||
111 | 763 | <class | ||
112 | 764 | class="lp.registry.model.ociprojectseries.OCIProjectSeries"> | ||
113 | 765 | <require | ||
114 | 766 | permission="launchpad.View" | ||
115 | 767 | interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesView | ||
116 | 768 | lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes"/> | ||
117 | 769 | <require | ||
118 | 770 | permission="launchpad.Edit" | ||
119 | 771 | interface="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEdit" | ||
120 | 772 | set_schema="lp.registry.interfaces.ociprojectseries.IOCIProjectSeriesEditableAttributes" /> | ||
121 | 773 | </class> | ||
122 | 774 | |||
123 | 761 | <!-- SourcePackageName --> | 775 | <!-- SourcePackageName --> |
124 | 762 | 776 | ||
125 | 763 | <class | 777 | <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 | 11 | 'IOCIProjectSet', | 11 | 'IOCIProjectSet', |
132 | 12 | ] | 12 | ] |
133 | 13 | 13 | ||
142 | 14 | from lazr.restful.declarations import ( | 14 | from lazr.restful.fields import ( |
143 | 15 | export_as_webservice_entry, | 15 | CollectionField, |
144 | 16 | exported, | 16 | Reference, |
137 | 17 | ) | ||
138 | 18 | from lazr.restful.fields import Reference | ||
139 | 19 | from zope.interface import ( | ||
140 | 20 | Attribute, | ||
141 | 21 | Interface, | ||
145 | 22 | ) | 17 | ) |
146 | 18 | from zope.interface import Interface | ||
147 | 23 | from zope.schema import ( | 19 | from zope.schema import ( |
148 | 24 | Datetime, | 20 | Datetime, |
149 | 25 | Int, | 21 | Int, |
150 | @@ -30,7 +26,8 @@ from lp import _ | |||
151 | 30 | from lp.bugs.interfaces.bugtarget import IBugTarget | 26 | from lp.bugs.interfaces.bugtarget import IBugTarget |
152 | 31 | from lp.registry.interfaces.distribution import IDistribution | 27 | from lp.registry.interfaces.distribution import IDistribution |
153 | 32 | from lp.registry.interfaces.ociprojectname import IOCIProjectName | 28 | from lp.registry.interfaces.ociprojectname import IOCIProjectName |
155 | 33 | from lp.registry.interfaces.role import IHasOwner | 29 | from lp.registry.interfaces.series import SeriesStatus |
156 | 30 | from lp.services.database.constants import DEFAULT | ||
157 | 34 | from lp.services.fields import PublicPersonChoice | 31 | from lp.services.fields import PublicPersonChoice |
158 | 35 | 32 | ||
159 | 36 | 33 | ||
160 | @@ -38,15 +35,20 @@ class IOCIProjectView(Interface): | |||
161 | 38 | """IOCIProject attributes that require launchpad.View permission.""" | 35 | """IOCIProject attributes that require launchpad.View permission.""" |
162 | 39 | 36 | ||
163 | 40 | id = Int(title=_("ID"), required=True, readonly=True) | 37 | id = Int(title=_("ID"), required=True, readonly=True) |
168 | 41 | date_created = exported( | 38 | date_created = Datetime( |
169 | 42 | Datetime(title=_("Date created"), required=True), readonly=True) | 39 | title=_("Date created"), required=True, readonly=True) |
170 | 43 | date_last_modified = exported( | 40 | date_last_modified = Datetime( |
171 | 44 | Datetime(title=_("Date last modified"), required=True), readonly=True) | 41 | title=_("Date last modified"), required=True, readonly=True) |
172 | 45 | 42 | ||
174 | 46 | registrant = exported(PublicPersonChoice( | 43 | registrant = PublicPersonChoice( |
175 | 47 | title=_("Registrant"), | 44 | title=_("Registrant"), |
176 | 48 | description=_("The person that registered this project."), | 45 | description=_("The person that registered this project."), |
178 | 49 | vocabulary='ValidPersonOrTeam', required=True, readonly=True)) | 46 | vocabulary='ValidPersonOrTeam', required=True, readonly=True) |
179 | 47 | |||
180 | 48 | series = CollectionField( | ||
181 | 49 | title=_("Series inside this OCI project."), | ||
182 | 50 | # Really IOCIProjectSeries | ||
183 | 51 | value_type=Reference(schema=Interface)) | ||
184 | 50 | 52 | ||
185 | 51 | 53 | ||
186 | 52 | class IOCIProjectEditableAttributes(IBugTarget): | 54 | class IOCIProjectEditableAttributes(IBugTarget): |
187 | @@ -58,24 +60,29 @@ class IOCIProjectEditableAttributes(IBugTarget): | |||
188 | 58 | distribution = Reference( | 60 | distribution = Reference( |
189 | 59 | IDistribution, | 61 | IDistribution, |
190 | 60 | title=_("The distribution that this OCI project is associated with.")) | 62 | title=_("The distribution that this OCI project is associated with.")) |
192 | 61 | ociprojectname = exported(Reference( | 63 | ociprojectname = Reference( |
193 | 62 | IOCIProjectName, | 64 | IOCIProjectName, |
194 | 63 | title=_("The name of this OCI project."), | 65 | title=_("The name of this OCI project."), |
195 | 64 | required=True, | 66 | required=True, |
200 | 65 | readonly=True)) | 67 | readonly=True) |
201 | 66 | description = exported( | 68 | description = Text(title=_("The description for this OCI project.")) |
202 | 67 | Text(title=_("The description for this OCI project."))) | 69 | pillar = Reference( |
199 | 68 | pillar = exported(Reference( | ||
203 | 69 | IDistribution, | 70 | IDistribution, |
205 | 70 | title=_("The pillar containing this target."), readonly=True)) | 71 | title=_("The pillar containing this target."), readonly=True) |
206 | 72 | |||
207 | 71 | 73 | ||
208 | 74 | class IOCIProjectEdit(Interface): | ||
209 | 75 | """IOCIProject attributes that require launchpad.Edit permission.""" | ||
210 | 72 | 76 | ||
212 | 73 | class IOCIProject(IOCIProjectView, | 77 | def newSeries(name, summary, registrant, |
213 | 78 | status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT): | ||
214 | 79 | """Creates a new `IOCIProjectSeries`.""" | ||
215 | 80 | |||
216 | 81 | |||
217 | 82 | class IOCIProject(IOCIProjectView, IOCIProjectEdit, | ||
218 | 74 | IOCIProjectEditableAttributes): | 83 | IOCIProjectEditableAttributes): |
219 | 75 | """A project containing Open Container Initiative recipes.""" | 84 | """A project containing Open Container Initiative recipes.""" |
220 | 76 | 85 | ||
221 | 77 | export_as_webservice_entry() | ||
222 | 78 | |||
223 | 79 | 86 | ||
224 | 80 | class IOCIProjectSet(Interface): | 87 | class IOCIProjectSet(Interface): |
225 | 81 | """A utility to create and access OCI Projects.""" | 88 | """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 | 82 | new file mode 100644 | 89 | 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 | 1 | # Copyright 2019 Canonical Ltd. This software is licensed under the | ||
233 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
234 | 3 | |||
235 | 4 | """Interfaces to allow bug filing on multiple versions of an OCI Project.""" | ||
236 | 5 | |||
237 | 6 | from __future__ import absolute_import, print_function, unicode_literals | ||
238 | 7 | |||
239 | 8 | __metaclass__ = type | ||
240 | 9 | __all__ = [ | ||
241 | 10 | 'IOCIProjectSeries', | ||
242 | 11 | 'IOCIProjectSeriesEditableAttributes', | ||
243 | 12 | 'IOCIProjectSeriesView', | ||
244 | 13 | ] | ||
245 | 14 | |||
246 | 15 | from lazr.restful.fields import Reference | ||
247 | 16 | from zope.interface import Interface | ||
248 | 17 | from zope.schema import ( | ||
249 | 18 | Choice, | ||
250 | 19 | Datetime, | ||
251 | 20 | Int, | ||
252 | 21 | Text, | ||
253 | 22 | TextLine, | ||
254 | 23 | ) | ||
255 | 24 | |||
256 | 25 | from lp import _ | ||
257 | 26 | from lp.app.validators.name import name_validator | ||
258 | 27 | from lp.registry.interfaces.ociproject import IOCIProject | ||
259 | 28 | from lp.registry.interfaces.series import SeriesStatus | ||
260 | 29 | from lp.services.fields import PublicPersonChoice | ||
261 | 30 | |||
262 | 31 | |||
263 | 32 | class IOCIProjectSeriesView(Interface): | ||
264 | 33 | """IOCIProjectSeries attributes that require launchpad.View permission.""" | ||
265 | 34 | |||
266 | 35 | id = Int(title=_("ID"), required=True, readonly=True) | ||
267 | 36 | |||
268 | 37 | ociproject = Reference( | ||
269 | 38 | IOCIProject, | ||
270 | 39 | title=_("The OCI project that this series belongs to."), | ||
271 | 40 | required=True, readonly=True) | ||
272 | 41 | |||
273 | 42 | date_created = Datetime( | ||
274 | 43 | title=_("Date created"), required=True, readonly=True, | ||
275 | 44 | description=_( | ||
276 | 45 | "The date on which this series was created in Launchpad.")) | ||
277 | 46 | |||
278 | 47 | registrant = PublicPersonChoice( | ||
279 | 48 | title=_("Registrant"), | ||
280 | 49 | description=_("The person that registered this series."), | ||
281 | 50 | vocabulary='ValidPersonOrTeam', required=True, readonly=True) | ||
282 | 51 | |||
283 | 52 | |||
284 | 53 | class IOCIProjectSeriesEditableAttributes(Interface): | ||
285 | 54 | """IOCIProjectSeries attributes that can be edited. | ||
286 | 55 | |||
287 | 56 | These attributes need launchpad.View to see, and launchpad.Edit to change. | ||
288 | 57 | """ | ||
289 | 58 | |||
290 | 59 | name = TextLine( | ||
291 | 60 | title=_("Name"), constraint=name_validator, | ||
292 | 61 | required=True, readonly=False, | ||
293 | 62 | description=_("The name of this series.")) | ||
294 | 63 | |||
295 | 64 | summary = Text( | ||
296 | 65 | title=_("Summary"), required=True, readonly=False, | ||
297 | 66 | description=_("A brief summary of this series.")) | ||
298 | 67 | |||
299 | 68 | status = Choice( | ||
300 | 69 | title=_("Status"), required=True, | ||
301 | 70 | vocabulary=SeriesStatus) | ||
302 | 71 | |||
303 | 72 | |||
304 | 73 | class IOCIProjectSeriesEdit(Interface): | ||
305 | 74 | """IOCIProjectSeries attributes that require launchpad.Edit permission.""" | ||
306 | 75 | |||
307 | 76 | |||
308 | 77 | class IOCIProjectSeries(IOCIProjectSeriesView, IOCIProjectSeriesEdit, | ||
309 | 78 | IOCIProjectSeriesEditableAttributes): | ||
310 | 79 | """A series of an Open Container Initiative project. | ||
311 | 80 | |||
312 | 81 | This is used to allow tracking bugs against multiple versions of images. | ||
313 | 82 | """ | ||
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 | 27 | IOCIProject, | 27 | IOCIProject, |
320 | 28 | IOCIProjectSet, | 28 | IOCIProjectSet, |
321 | 29 | ) | 29 | ) |
322 | 30 | from lp.registry.interfaces.series import SeriesStatus | ||
323 | 30 | from lp.registry.model.ociprojectname import OCIProjectName | 31 | from lp.registry.model.ociprojectname import OCIProjectName |
324 | 32 | from lp.registry.model.ociprojectseries import OCIProjectSeries | ||
325 | 31 | from lp.services.database.constants import DEFAULT | 33 | from lp.services.database.constants import DEFAULT |
326 | 32 | from lp.services.database.interfaces import ( | 34 | from lp.services.database.interfaces import ( |
327 | 33 | IMasterStore, | 35 | IMasterStore, |
328 | @@ -81,6 +83,27 @@ class OCIProject(BugTargetBase, StormBase): | |||
329 | 81 | return "OCI project %s for %s" % ( | 83 | return "OCI project %s for %s" % ( |
330 | 82 | self.ociprojectname.name, self.pillar.name) | 84 | self.ociprojectname.name, self.pillar.name) |
331 | 83 | 85 | ||
332 | 86 | def newSeries(self, name, summary, registrant, | ||
333 | 87 | status=SeriesStatus.DEVELOPMENT, date_created=DEFAULT): | ||
334 | 88 | """See `IOCIProject`.""" | ||
335 | 89 | series = OCIProjectSeries( | ||
336 | 90 | ociproject=self, | ||
337 | 91 | name=name, | ||
338 | 92 | summary=summary, | ||
339 | 93 | registrant=registrant, | ||
340 | 94 | status=status, | ||
341 | 95 | ) | ||
342 | 96 | return series | ||
343 | 97 | |||
344 | 98 | @property | ||
345 | 99 | def series(self): | ||
346 | 100 | """See `IOCIProject`.""" | ||
347 | 101 | ret = IStore(OCIProjectSeries).find( | ||
348 | 102 | OCIProjectSeries, | ||
349 | 103 | OCIProjectSeries.ociproject == self | ||
350 | 104 | ).order_by(OCIProjectSeries.date_created) | ||
351 | 105 | return ret | ||
352 | 106 | |||
353 | 84 | 107 | ||
354 | 85 | @implementer(IOCIProjectSet) | 108 | @implementer(IOCIProjectSet) |
355 | 86 | class OCIProjectSet: | 109 | class OCIProjectSet: |
356 | diff --git a/lib/lp/registry/model/ociprojectseries.py b/lib/lp/registry/model/ociprojectseries.py | |||
357 | 87 | new file mode 100644 | 110 | 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 | 1 | # Copyright 2019 Canonical Ltd. This software is licensed under the | ||
363 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
364 | 3 | |||
365 | 4 | """Model implementing `IOCIProjectSeries`.""" | ||
366 | 5 | |||
367 | 6 | from __future__ import absolute_import, print_function, unicode_literals | ||
368 | 7 | |||
369 | 8 | __metaclass__ = type | ||
370 | 9 | __all__ = [ | ||
371 | 10 | 'OCIProjectSeries', | ||
372 | 11 | ] | ||
373 | 12 | |||
374 | 13 | import pytz | ||
375 | 14 | from storm.locals import ( | ||
376 | 15 | DateTime, | ||
377 | 16 | Int, | ||
378 | 17 | Reference, | ||
379 | 18 | Unicode, | ||
380 | 19 | ) | ||
381 | 20 | from zope.interface import implementer | ||
382 | 21 | |||
383 | 22 | from lp.app.validators.name import valid_name | ||
384 | 23 | from lp.registry.errors import InvalidName | ||
385 | 24 | from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries | ||
386 | 25 | from lp.registry.interfaces.series import SeriesStatus | ||
387 | 26 | from lp.services.database.constants import DEFAULT | ||
388 | 27 | from lp.services.database.enumcol import DBEnum | ||
389 | 28 | from lp.services.database.stormbase import StormBase | ||
390 | 29 | |||
391 | 30 | |||
392 | 31 | @implementer(IOCIProjectSeries) | ||
393 | 32 | class OCIProjectSeries(StormBase): | ||
394 | 33 | """See `IOCIProjectSeries`.""" | ||
395 | 34 | |||
396 | 35 | __storm_table__ = "OCIProjectSeries" | ||
397 | 36 | |||
398 | 37 | id = Int(primary=True) | ||
399 | 38 | |||
400 | 39 | ociproject_id = Int(name='ociproject', allow_none=False) | ||
401 | 40 | ociproject = Reference(ociproject_id, "OCIProject.id") | ||
402 | 41 | |||
403 | 42 | name = Unicode(name="name", allow_none=False) | ||
404 | 43 | |||
405 | 44 | summary = Unicode(name="summary", allow_none=False) | ||
406 | 45 | |||
407 | 46 | date_created = DateTime( | ||
408 | 47 | name="date_created", tzinfo=pytz.UTC, allow_none=False) | ||
409 | 48 | |||
410 | 49 | registrant_id = Int(name='registrant', allow_none=False) | ||
411 | 50 | registrant = Reference(registrant_id, "Person.id") | ||
412 | 51 | |||
413 | 52 | status = DBEnum( | ||
414 | 53 | name='status', allow_none=False, enum=SeriesStatus) | ||
415 | 54 | |||
416 | 55 | def __init__(self, ociproject, name, summary, | ||
417 | 56 | registrant, status, date_created=DEFAULT): | ||
418 | 57 | if not valid_name(name): | ||
419 | 58 | raise InvalidName( | ||
420 | 59 | "%s is not a valid name for an OCI project series." % name) | ||
421 | 60 | self.name = name | ||
422 | 61 | self.ociproject = ociproject | ||
423 | 62 | self.summary = summary | ||
424 | 63 | self.registrant = registrant | ||
425 | 64 | self.status = status | ||
426 | 65 | 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 | 7 | 7 | ||
433 | 8 | __metaclass__ = type | 8 | __metaclass__ = type |
434 | 9 | 9 | ||
435 | 10 | from testtools.testcase import ExpectedException | ||
436 | 10 | from zope.component import getUtility | 11 | from zope.component import getUtility |
437 | 12 | from zope.security.interfaces import Unauthorized | ||
438 | 11 | 13 | ||
439 | 12 | from lp.registry.interfaces.ociproject import ( | 14 | from lp.registry.interfaces.ociproject import ( |
440 | 13 | IOCIProject, | 15 | IOCIProject, |
441 | 14 | IOCIProjectSet, | 16 | IOCIProjectSet, |
442 | 15 | ) | 17 | ) |
443 | 18 | from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries | ||
444 | 16 | from lp.testing import ( | 19 | from lp.testing import ( |
445 | 17 | admin_logged_in, | 20 | admin_logged_in, |
446 | 18 | person_logged_in, | 21 | person_logged_in, |
447 | @@ -30,6 +33,40 @@ class TestOCIProject(TestCaseWithFactory): | |||
448 | 30 | with admin_logged_in(): | 33 | with admin_logged_in(): |
449 | 31 | self.assertProvides(oci_project, IOCIProject) | 34 | self.assertProvides(oci_project, IOCIProject) |
450 | 32 | 35 | ||
451 | 36 | def test_newSeries(self): | ||
452 | 37 | driver = self.factory.makePerson() | ||
453 | 38 | distribution = self.factory.makeDistribution(driver=driver) | ||
454 | 39 | registrant = self.factory.makePerson() | ||
455 | 40 | oci_project = self.factory.makeOCIProject(pillar=distribution) | ||
456 | 41 | with person_logged_in(driver): | ||
457 | 42 | series = oci_project.newSeries( | ||
458 | 43 | 'test-series', | ||
459 | 44 | 'test-summary', | ||
460 | 45 | registrant) | ||
461 | 46 | self.assertProvides(series, IOCIProjectSeries) | ||
462 | 47 | |||
463 | 48 | def test_newSeries_bad_permissions(self): | ||
464 | 49 | distribution = self.factory.makeDistribution() | ||
465 | 50 | registrant = self.factory.makePerson() | ||
466 | 51 | oci_project = self.factory.makeOCIProject(pillar=distribution) | ||
467 | 52 | with ExpectedException(Unauthorized): | ||
468 | 53 | oci_project.newSeries( | ||
469 | 54 | 'test-series', | ||
470 | 55 | 'test-summary', | ||
471 | 56 | registrant) | ||
472 | 57 | |||
473 | 58 | def test_series(self): | ||
474 | 59 | driver = self.factory.makePerson() | ||
475 | 60 | distribution = self.factory.makeDistribution(driver=driver) | ||
476 | 61 | first_oci_project = self.factory.makeOCIProject(pillar=distribution) | ||
477 | 62 | second_oci_project = self.factory.makeOCIProject(pillar=distribution) | ||
478 | 63 | with person_logged_in(driver): | ||
479 | 64 | first_series = self.factory.makeOCIProjectSeries( | ||
480 | 65 | oci_project=first_oci_project) | ||
481 | 66 | self.factory.makeOCIProjectSeries( | ||
482 | 67 | oci_project=second_oci_project) | ||
483 | 68 | self.assertContentEqual([first_series], first_oci_project.series) | ||
484 | 69 | |||
485 | 33 | 70 | ||
486 | 34 | class TestOCIProjectSet(TestCaseWithFactory): | 71 | class TestOCIProjectSet(TestCaseWithFactory): |
487 | 35 | 72 | ||
488 | diff --git a/lib/lp/registry/tests/test_ociprojectseries.py b/lib/lp/registry/tests/test_ociprojectseries.py | |||
489 | 36 | new file mode 100644 | 73 | 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 | 1 | # Copyright 2019 Canonical Ltd. This software is licensed under the | ||
495 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
496 | 3 | |||
497 | 4 | """Test OCIProjectSeries.""" | ||
498 | 5 | |||
499 | 6 | from __future__ import absolute_import, print_function, unicode_literals | ||
500 | 7 | |||
501 | 8 | __metaclass__ = type | ||
502 | 9 | |||
503 | 10 | from testtools.matchers import MatchesStructure | ||
504 | 11 | from testtools.testcase import ExpectedException | ||
505 | 12 | from zope.security.interfaces import Unauthorized | ||
506 | 13 | |||
507 | 14 | from lp.registry.errors import InvalidName | ||
508 | 15 | from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries | ||
509 | 16 | from lp.registry.interfaces.series import SeriesStatus | ||
510 | 17 | from lp.registry.model.ociprojectseries import OCIProjectSeries | ||
511 | 18 | from lp.services.database.constants import UTC_NOW | ||
512 | 19 | from lp.testing import ( | ||
513 | 20 | person_logged_in, | ||
514 | 21 | TestCaseWithFactory, | ||
515 | 22 | ) | ||
516 | 23 | from lp.testing.layers import DatabaseFunctionalLayer | ||
517 | 24 | |||
518 | 25 | |||
519 | 26 | class TestOCIProjectSeries(TestCaseWithFactory): | ||
520 | 27 | |||
521 | 28 | layer = DatabaseFunctionalLayer | ||
522 | 29 | |||
523 | 30 | def test_implements_interface(self): | ||
524 | 31 | name = 'test-name' | ||
525 | 32 | oci_project = self.factory.makeOCIProject() | ||
526 | 33 | summary = 'test_summary' | ||
527 | 34 | registrant = self.factory.makePerson() | ||
528 | 35 | status = SeriesStatus.DEVELOPMENT | ||
529 | 36 | project_series = OCIProjectSeries( | ||
530 | 37 | oci_project, name, summary, registrant, status) | ||
531 | 38 | self.assertProvides(project_series, IOCIProjectSeries) | ||
532 | 39 | |||
533 | 40 | def test_init(self): | ||
534 | 41 | name = 'test-name' | ||
535 | 42 | oci_project = self.factory.makeOCIProject() | ||
536 | 43 | summary = 'test_summary' | ||
537 | 44 | registrant = self.factory.makePerson() | ||
538 | 45 | status = SeriesStatus.DEVELOPMENT | ||
539 | 46 | date_created = UTC_NOW | ||
540 | 47 | project_series = OCIProjectSeries( | ||
541 | 48 | oci_project, name, summary, registrant, status, date_created) | ||
542 | 49 | self.assertThat( | ||
543 | 50 | project_series, MatchesStructure.byEquality( | ||
544 | 51 | ociproject=project_series.ociproject, | ||
545 | 52 | name=project_series.name, | ||
546 | 53 | summary=project_series.summary, | ||
547 | 54 | registrant=project_series.registrant, | ||
548 | 55 | status=project_series.status, | ||
549 | 56 | date_created=project_series.date_created)) | ||
550 | 57 | |||
551 | 58 | def test_invalid_name(self): | ||
552 | 59 | name = 'invalid%20name' | ||
553 | 60 | oci_project = self.factory.makeOCIProject() | ||
554 | 61 | summary = 'test_summary' | ||
555 | 62 | registrant = self.factory.makePerson() | ||
556 | 63 | status = SeriesStatus.DEVELOPMENT | ||
557 | 64 | with ExpectedException(InvalidName): | ||
558 | 65 | OCIProjectSeries(oci_project, name, summary, registrant, status) | ||
559 | 66 | |||
560 | 67 | def test_edit_permissions_invalid(self): | ||
561 | 68 | name = 'test-name' | ||
562 | 69 | summary = 'test_summary' | ||
563 | 70 | registrant = self.factory.makePerson() | ||
564 | 71 | another_person = self.factory.makePerson() | ||
565 | 72 | |||
566 | 73 | driver = self.factory.makePerson() | ||
567 | 74 | distribution = self.factory.makeDistribution(driver=driver) | ||
568 | 75 | |||
569 | 76 | with person_logged_in(driver): | ||
570 | 77 | project_series = self.factory.makeOCIProject( | ||
571 | 78 | pillar=distribution).newSeries( | ||
572 | 79 | name, summary, registrant) | ||
573 | 80 | |||
574 | 81 | with person_logged_in(another_person): | ||
575 | 82 | with ExpectedException(Unauthorized): | ||
576 | 83 | project_series.name = 'not-allowed' | ||
577 | 84 | |||
578 | 85 | def test_edit_permissions_valid(self): | ||
579 | 86 | name = 'test-name' | ||
580 | 87 | summary = 'test_summary' | ||
581 | 88 | registrant = self.factory.makePerson() | ||
582 | 89 | |||
583 | 90 | driver = self.factory.makePerson() | ||
584 | 91 | distribution = self.factory.makeDistribution(driver=driver) | ||
585 | 92 | |||
586 | 93 | with person_logged_in(driver): | ||
587 | 94 | project_series = self.factory.makeOCIProject( | ||
588 | 95 | pillar=distribution).newSeries( | ||
589 | 96 | name, summary, registrant) | ||
590 | 97 | project_series.name = 'allowed' | ||
591 | 98 | |||
592 | 99 | 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 | 136 | INameBlacklist, | 136 | INameBlacklist, |
599 | 137 | INameBlacklistSet, | 137 | INameBlacklistSet, |
600 | 138 | ) | 138 | ) |
601 | 139 | from lp.registry.interfaces.ociproject import IOCIProject | ||
602 | 140 | from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries | ||
603 | 139 | from lp.registry.interfaces.packaging import IPackaging | 141 | from lp.registry.interfaces.packaging import IPackaging |
604 | 140 | from lp.registry.interfaces.person import ( | 142 | from lp.registry.interfaces.person import ( |
605 | 141 | IPerson, | 143 | IPerson, |
606 | @@ -3430,3 +3432,26 @@ class EditSnapBase(EditByRegistryExpertsOrAdmins): | |||
607 | 3430 | 3432 | ||
608 | 3431 | class EditSnapBaseSet(EditByRegistryExpertsOrAdmins): | 3433 | class EditSnapBaseSet(EditByRegistryExpertsOrAdmins): |
609 | 3432 | usedfor = ISnapBaseSet | 3434 | usedfor = ISnapBaseSet |
610 | 3435 | |||
611 | 3436 | |||
612 | 3437 | class EditOCIProject(AuthorizationBase): | ||
613 | 3438 | permission = 'launchpad.Edit' | ||
614 | 3439 | usedfor = IOCIProject | ||
615 | 3440 | |||
616 | 3441 | def checkAuthenticated(self, user): | ||
617 | 3442 | """Maintainers, drivers, and admins can drive projects.""" | ||
618 | 3443 | # XXX twom 2019-10-29 This ideally shouldn't be driver, but a | ||
619 | 3444 | # new role name that cascades upwards from the OCIProject | ||
620 | 3445 | # to the pillar | ||
621 | 3446 | return (user.in_admin or | ||
622 | 3447 | user.isDriver(self.obj.pillar)) | ||
623 | 3448 | |||
624 | 3449 | |||
625 | 3450 | class EditOCIProjectSeries(AuthorizationBase): | ||
626 | 3451 | permission = 'launchpad.Edit' | ||
627 | 3452 | usedfor = IOCIProjectSeries | ||
628 | 3453 | |||
629 | 3454 | def checkAuthenticated(self, user): | ||
630 | 3455 | """Maintainers, drivers, and admins can drive projects.""" | ||
631 | 3456 | return (user.in_admin or | ||
632 | 3457 | 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 | 4904 | return getUtility(IOCIProjectNameSet).new(name) | 4904 | return getUtility(IOCIProjectNameSet).new(name) |
639 | 4905 | 4905 | ||
640 | 4906 | def makeOCIProject(self, registrant=None, pillar=None, | 4906 | def makeOCIProject(self, registrant=None, pillar=None, |
645 | 4907 | ociprojectname=None, date_created=DEFAULT, | 4907 | ociprojectname=None, date_created=DEFAULT, |
646 | 4908 | description=None, bug_reporting_guidelines=None, | 4908 | description=None, bug_reporting_guidelines=None, |
647 | 4909 | bug_reported_acknowledgement=None, | 4909 | bug_reported_acknowledgement=None, |
648 | 4910 | bugfiling_duplicate_search=False): | 4910 | bugfiling_duplicate_search=False): |
649 | 4911 | """Make a new OCIProject.""" | 4911 | """Make a new OCIProject.""" |
650 | 4912 | if registrant is None: | 4912 | if registrant is None: |
651 | 4913 | registrant = self.makePerson() | 4913 | registrant = self.makePerson() |
652 | @@ -4922,6 +4922,19 @@ class BareLaunchpadObjectFactory(ObjectFactory): | |||
653 | 4922 | bug_reported_acknowledgement=bug_reported_acknowledgement, | 4922 | bug_reported_acknowledgement=bug_reported_acknowledgement, |
654 | 4923 | bugfiling_duplicate_search=bugfiling_duplicate_search) | 4923 | bugfiling_duplicate_search=bugfiling_duplicate_search) |
655 | 4924 | 4924 | ||
656 | 4925 | def makeOCIProjectSeries(self, name=None, summary=None, registrant=None, | ||
657 | 4926 | oci_project=None, **kwargs): | ||
658 | 4927 | """Make a new OCIProjectSeries attached to an OCIProject.""" | ||
659 | 4928 | if name is None: | ||
660 | 4929 | name = self.getUniqueString(u"oci-project-series-name") | ||
661 | 4930 | if summary is None: | ||
662 | 4931 | summary = self.getUniqueString(u"oci-project-series-summary") | ||
663 | 4932 | if registrant is None: | ||
664 | 4933 | registrant = self.makePerson() | ||
665 | 4934 | if oci_project is None: | ||
666 | 4935 | oci_project = self.makeOCIProject(**kwargs) | ||
667 | 4936 | return oci_project.newSeries(name, summary, registrant) | ||
668 | 4937 | |||
669 | 4925 | 4938 | ||
670 | 4926 | # Some factory methods return simple Python types. We don't add | 4939 | # Some factory methods return simple Python types. We don't add |
671 | 4927 | # security wrappers for them, as well as for objects created by | 4940 | # 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