Merge lp:~cjohnston/ubuntu-ci-services-itself/ticket-artifact into lp:ubuntu-ci-services-itself
- ticket-artifact
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Chris Johnston |
Approved revision: | 134 |
Merged at revision: | 139 |
Proposed branch: | lp:~cjohnston/ubuntu-ci-services-itself/ticket-artifact |
Merge into: | lp:ubuntu-ci-services-itself |
Diff against target: |
820 lines (+366/-138) 10 files modified
docs/components/ticket-system.rst (+13/-1) ticket_system/ticket/admin.py (+11/-3) ticket_system/ticket/api.py (+37/-16) ticket_system/ticket/migrations/0001_initial.py (+40/-19) ticket_system/ticket/models.py (+33/-9) ticket_system/ticket/tests/test_full_read_api.py (+64/-40) ticket_system/ticket/tests/test_models.py (+28/-10) ticket_system/ticket/tests/test_read_api.py (+57/-15) ticket_system/ticket/tests/test_write_api.py (+74/-20) ticket_system/ticket_system/urls.py (+9/-5) |
To merge this branch: | bzr merge lp:~cjohnston/ubuntu-ci-services-itself/ticket-artifact |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Francis Ginther | Approve | ||
Chris Johnston (community) | Needs Resubmitting | ||
Review via email:
|
Commit message
Add the ability for an artifact to be attached to a ticket as well as a subticket. Also add 'image' as an available artifact type
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) wrote : | # |
The attempt to merge lp:~cjohnston/ubuntu-ci-services-itself/ticket-artifact into lp:ubuntu-ci-services-itself failed. Below is the output from the failed tests.
New python executable in /tmp/tmp.
Installing distribute.
Installing pip....
== Testing ci-utils ....
running develop
running egg_info
creating ci_utils.egg-info
writing requirements to ci_utils.
writing ci_utils.
writing top-level names to ci_utils.
writing dependency_links to ci_utils.
writing manifest file 'ci_utils.
reading manifest file 'ci_utils.
writing manifest file 'ci_utils.
running build_ext
Creating /tmp/tmp.
Adding ci-utils 0.1 to easy-install.pth file
Installed /tmp/tarmac/
Processing dependencies for ci-utils==0.1
Searching for testtools
Reading http://
Best match: testtools 0.9.34
Downloading https:/
Processing testtools-
Running testtools-
Adding testtools 0.9.34 to easy-install.pth file
Installed /tmp/tmp.
Searching for restish==0.12.1
Reading http://
Best match: restish 0.12.1
Downloading https:/
Processing restish-
Running restish-
Adding restish 0.12.1 to easy-install.pth file
Installed /tmp/tmp.
Searching for python-subunit
Reading http://
Reading http://
Best match: python-subunit 0.0.16
Downloading https:/
Processing python-
Running python-
Adding python-subunit 0.0.16 to easy-install.pth file
Installing subunit-filter script to /tmp/tmp.
Installing subunit-2to1 script to /tmp/tmp.
Installing subunit-ls script to /tmp/tmp.
Installing tap2subunit script to /tmp/tmp.
Installing subunit-1to2 script to /tmp/tmp.
Installing subunit2pyunit script to /tmp/tmp.
Installing subunit2junitxml script to /tmp/tmp.
Installing subunit-tags script to /tmp/tmp.
Installing subunit-notify...
Preview Diff
1 | === modified file 'docs/components/ticket-system.rst' |
2 | --- docs/components/ticket-system.rst 2014-01-21 19:36:04 +0000 |
3 | +++ docs/components/ticket-system.rst 2014-01-24 17:44:23 +0000 |
4 | @@ -277,11 +277,23 @@ |
5 | Create artifact |
6 | ~~~~~~~~~~~~~~~ |
7 | |
8 | + |
9 | +*Ticket* |
10 | + |
11 | +Valid types are: "RESULTS", "LOGS", "IMAGE" |
12 | + |
13 | +:: |
14 | + |
15 | + curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my_artifact", "ticket": "/api/v1/ticket/X/", "reference": "http://path.to/artifact/", "type": "IMAGE"}' http://localhost:8000/api/v1/ticketartifact/ |
16 | + |
17 | + |
18 | +*Subticket* |
19 | + |
20 | Valid types are: "SPU", "RESULTS", "LOGS" |
21 | |
22 | :: |
23 | |
24 | - curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my_artifact", "subticket": "/api/v1/subticket/X/", "reference": "http://path.to/artifact/", "type": "SPU"}' http://localhost:8000/api/v1/artifact/ |
25 | + curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my_artifact", "subticket": "/api/v1/subticket/X/", "reference": "http://path.to/artifact/", "type": "SPU"}' http://localhost:8000/api/v1/subticketartifact/ |
26 | |
27 | Create subticket |
28 | ~~~~~~~~~~~~~~~~ |
29 | |
30 | === modified file 'ticket_system/ticket/admin.py' |
31 | --- ticket_system/ticket/admin.py 2013-12-20 21:12:13 +0000 |
32 | +++ ticket_system/ticket/admin.py 2014-01-24 17:44:23 +0000 |
33 | @@ -14,7 +14,8 @@ |
34 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
35 | |
36 | from django.contrib import admin |
37 | -from ticket.models import Ticket, SubTicket, SourcePackageUpload, Artifact |
38 | +from ticket.models import (Ticket, SubTicket, SourcePackageUpload, |
39 | + TicketArtifact, SubTicketArtifact) |
40 | |
41 | |
42 | class TicketAdmin(admin.ModelAdmin): |
43 | @@ -34,7 +35,13 @@ |
44 | list_display = ('sourcepackage', 'version') |
45 | |
46 | |
47 | -class ArtifactAdmin(admin.ModelAdmin): |
48 | +class TicketArtifactAdmin(admin.ModelAdmin): |
49 | + list_filter = ['type'] |
50 | + search_fields = ['name'] |
51 | + list_display = ('name', 'ticket', 'type') |
52 | + |
53 | + |
54 | +class SubTicketArtifactAdmin(admin.ModelAdmin): |
55 | list_filter = ['type'] |
56 | search_fields = ['name'] |
57 | list_display = ('name', 'subticket', 'type') |
58 | @@ -42,4 +49,5 @@ |
59 | admin.site.register(Ticket, TicketAdmin) |
60 | admin.site.register(SubTicket, SubTicketAdmin) |
61 | admin.site.register(SourcePackageUpload, SourcePackageUploadAdmin) |
62 | -admin.site.register(Artifact, ArtifactAdmin) |
63 | +admin.site.register(TicketArtifact, TicketArtifactAdmin) |
64 | +admin.site.register(SubTicketArtifact, SubTicketArtifactAdmin) |
65 | |
66 | === modified file 'ticket_system/ticket/api.py' |
67 | --- ticket_system/ticket/api.py 2014-01-21 19:36:04 +0000 |
68 | +++ ticket_system/ticket/api.py 2014-01-24 17:44:23 +0000 |
69 | @@ -17,7 +17,8 @@ |
70 | from tastypie.constants import ALL |
71 | from tastypie.authorization import Authorization |
72 | from tastypie.resources import ModelResource |
73 | -from ticket.models import (Ticket, SubTicket, SourcePackageUpload, Artifact, |
74 | +from ticket.models import (Ticket, SubTicket, SourcePackageUpload, |
75 | + TicketArtifact, SubTicketArtifact, |
76 | SubTicketWorkflowStep, SubTicketWorkflowStepStatus, |
77 | TicketWorkflowStep, TicketWorkflowStepStatus, |
78 | get_enum_title) |
79 | @@ -91,19 +92,36 @@ |
80 | authorization = Authorization() |
81 | |
82 | |
83 | -class ArtifactResource(ModelResource): |
84 | - subticket = fields.ToOneField(SubTicketResource, 'subticket', full=True) |
85 | - |
86 | - class Meta: |
87 | - queryset = Artifact.objects.all() |
88 | - allowed_methods = ['get', 'post'] |
89 | - authorization = Authorization() |
90 | - |
91 | - |
92 | -class FullArtifactResource(ModelResource): |
93 | - |
94 | - class Meta: |
95 | - queryset = Artifact.objects.all() |
96 | +class TicketArtifactResource(ModelResource): |
97 | + ticket = fields.ToOneField(TicketResource, 'ticket', null=True, full=True) |
98 | + |
99 | + class Meta: |
100 | + queryset = TicketArtifact.objects.all() |
101 | + allowed_methods = ['get', 'post'] |
102 | + authorization = Authorization() |
103 | + |
104 | + |
105 | +class SubTicketArtifactResource(ModelResource): |
106 | + subticket = fields.ToOneField(SubTicketResource, 'subticket', null=True, |
107 | + full=True) |
108 | + |
109 | + class Meta: |
110 | + queryset = SubTicketArtifact.objects.all() |
111 | + allowed_methods = ['get', 'post'] |
112 | + authorization = Authorization() |
113 | + |
114 | + |
115 | +class FullTicketArtifactResource(ModelResource): |
116 | + |
117 | + class Meta: |
118 | + queryset = TicketArtifact.objects.all() |
119 | + allowed_methods = ['get'] |
120 | + |
121 | + |
122 | +class FullSubTicketArtifactResource(ModelResource): |
123 | + |
124 | + class Meta: |
125 | + queryset = SubTicketArtifact.objects.all() |
126 | allowed_methods = ['get'] |
127 | |
128 | |
129 | @@ -111,8 +129,8 @@ |
130 | source_package_upload = fields.ToOneField(SourcePackageUploadResource, |
131 | 'source_package_upload', |
132 | full=True) |
133 | - artifact = fields.ToManyField(FullArtifactResource, 'artifact_set', |
134 | - full=True) |
135 | + artifact = fields.ToManyField(FullSubTicketArtifactResource, |
136 | + 'subticketartifact_set', full=True) |
137 | |
138 | class Meta: |
139 | queryset = SubTicket.objects.all() |
140 | @@ -122,6 +140,9 @@ |
141 | class FullTicketResource(TicketTranslatedResource): |
142 | subticket = fields.ToManyField(FullSubTicketResource, 'subticket_set', |
143 | full=True) |
144 | + artifact = fields.ToManyField(FullTicketArtifactResource, |
145 | + 'ticketartifact_set', null=True, |
146 | + full=True) |
147 | |
148 | class Meta: |
149 | queryset = Ticket.objects.all() |
150 | |
151 | === modified file 'ticket_system/ticket/migrations/0001_initial.py' |
152 | --- ticket_system/ticket/migrations/0001_initial.py 2013-12-20 21:12:13 +0000 |
153 | +++ ticket_system/ticket/migrations/0001_initial.py 2014-01-24 17:44:23 +0000 |
154 | @@ -44,15 +44,25 @@ |
155 | )) |
156 | db.send_create_signal(u'ticket', ['SubTicket']) |
157 | |
158 | - # Adding model 'Artifact' |
159 | - db.create_table('artifact', ( |
160 | - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
161 | - ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
162 | - ('subticket', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SubTicket'])), |
163 | - ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
164 | - ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
165 | - )) |
166 | - db.send_create_signal(u'ticket', ['Artifact']) |
167 | + # Adding model 'SubTicketArtifact' |
168 | + db.create_table('subticket_artifact', ( |
169 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
170 | + ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
171 | + ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
172 | + ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
173 | + ('subticket', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SubTicket'], null=True)), |
174 | + )) |
175 | + db.send_create_signal(u'ticket', ['SubTicketArtifact']) |
176 | + |
177 | + # Adding model 'TicketArtifact' |
178 | + db.create_table('ticket_artifact', ( |
179 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
180 | + ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
181 | + ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
182 | + ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
183 | + ('ticket', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.Ticket'], null=True)), |
184 | + )) |
185 | + db.send_create_signal(u'ticket', ['TicketArtifact']) |
186 | |
187 | |
188 | def backwards(self, orm): |
189 | @@ -65,8 +75,11 @@ |
190 | # Deleting model 'SubTicket' |
191 | db.delete_table('subticket') |
192 | |
193 | - # Deleting model 'Artifact' |
194 | - db.delete_table('artifact') |
195 | + # Deleting model 'SubTicketArtifact' |
196 | + db.delete_table('subticket_artifact') |
197 | + |
198 | + # Deleting model 'TicketArtifact' |
199 | + db.delete_table('ticket_artifact') |
200 | |
201 | |
202 | models = { |
203 | @@ -75,14 +88,6 @@ |
204 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
205 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
206 | }, |
207 | - u'ticket.artifact': { |
208 | - 'Meta': {'object_name': 'Artifact', 'db_table': "'artifact'"}, |
209 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
210 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
211 | - 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
212 | - 'subticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']"}), |
213 | - 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
214 | - }, |
215 | u'ticket.sourcepackageupload': { |
216 | 'Meta': {'object_name': 'SourcePackageUpload', 'db_table': "'sourcepackageupload'"}, |
217 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
218 | @@ -98,6 +103,14 @@ |
219 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
220 | 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"}) |
221 | }, |
222 | + u'ticket.subticketartifact': { |
223 | + 'Meta': {'object_name': 'SubTicketArtifact', 'db_table': "'subticket_artifact'"}, |
224 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
225 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
226 | + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
227 | + 'subticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']", 'null': 'True'}), |
228 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
229 | + }, |
230 | u'ticket.ticket': { |
231 | 'Meta': {'object_name': 'Ticket', 'db_table': "'ticket'"}, |
232 | 'added_binaries': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'null': 'True', 'blank': 'True'}), |
233 | @@ -112,6 +125,14 @@ |
234 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
235 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
236 | 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
237 | + }, |
238 | + u'ticket.ticketartifact': { |
239 | + 'Meta': {'object_name': 'TicketArtifact', 'db_table': "'ticket_artifact'"}, |
240 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
241 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
242 | + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
243 | + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']", 'null': 'True'}), |
244 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
245 | } |
246 | } |
247 | |
248 | |
249 | === modified file 'ticket_system/ticket/models.py' |
250 | --- ticket_system/ticket/models.py 2014-01-16 16:02:10 +0000 |
251 | +++ ticket_system/ticket/models.py 2014-01-24 17:44:23 +0000 |
252 | @@ -157,20 +157,44 @@ |
253 | return "{} {}".format(self.ticket, self.source_package_upload) |
254 | |
255 | |
256 | -class Artifact(models.Model): |
257 | +class ArtifactBase(models.Model): |
258 | class Meta: |
259 | - db_table = 'artifact' |
260 | + abstract = True |
261 | |
262 | - ARTIFACT_TYPE = ( |
263 | - ('RESULTS', 'Test Results'), |
264 | - ('SPU', 'Source Package Upload'), |
265 | - ('LOGS', 'Logs'), |
266 | - ) |
267 | - type = models.CharField(choices=ARTIFACT_TYPE, max_length=4096) |
268 | - subticket = models.ForeignKey(SubTicket) |
269 | # 'reference' provided by the artifact manager |
270 | reference = models.CharField(max_length=4096) |
271 | name = models.CharField(max_length=4096) |
272 | |
273 | + |
274 | +class SubTicketArtifact(ArtifactBase): |
275 | + SUBTICKET_ARTIFACT_TYPE = ( |
276 | + ('RESULTS', 'Test Results'), |
277 | + ('SPU', 'Source Package Upload'), |
278 | + ('LOGS', 'Logs'), |
279 | + ) |
280 | + |
281 | + class Meta: |
282 | + db_table = 'subticket_artifact' |
283 | + |
284 | + type = models.CharField(choices=SUBTICKET_ARTIFACT_TYPE, max_length=4096) |
285 | + subticket = models.ForeignKey(SubTicket, null=True) |
286 | + |
287 | + def __unicode__(self): |
288 | + return "{} {}".format(self.name, self.type) |
289 | + |
290 | + |
291 | +class TicketArtifact(ArtifactBase): |
292 | + TICKET_ARTIFACT_TYPE = ( |
293 | + ('RESULTS', 'Test Results'), |
294 | + ('LOGS', 'Logs'), |
295 | + ('IMAGE', 'Image'), |
296 | + ) |
297 | + |
298 | + class Meta: |
299 | + db_table = 'ticket_artifact' |
300 | + |
301 | + type = models.CharField(choices=TICKET_ARTIFACT_TYPE, max_length=4096) |
302 | + ticket = models.ForeignKey(Ticket, null=True) |
303 | + |
304 | def __unicode__(self): |
305 | return "{} {}".format(self.name, self.type) |
306 | |
307 | === modified file 'ticket_system/ticket/tests/test_full_read_api.py' |
308 | --- ticket_system/ticket/tests/test_full_read_api.py 2014-01-22 17:14:18 +0000 |
309 | +++ ticket_system/ticket/tests/test_full_read_api.py 2014-01-24 17:44:23 +0000 |
310 | @@ -34,31 +34,41 @@ |
311 | sourcepackage=self.sourcepackage) |
312 | self.subticket = mommy.make('SubTicket', ticket=self.ticket, |
313 | source_package_upload=self.spu) |
314 | - self.artifact = mommy.make('Artifact', subticket=self.subticket) |
315 | - self.maxDiff = None |
316 | + self.artifact_1 = mommy.make('TicketArtifact', ticket=self.ticket) |
317 | + self.artifact_2 = mommy.make('SubTicketArtifact', |
318 | + subticket=self.subticket) |
319 | |
320 | def test_get_fullticket_api(self): |
321 | obj = self.getResource('fullticket/') |
322 | self.assertEqual(obj['objects'][0], { |
323 | - u'status': get_enum_title(self.ticket.status, |
324 | - TicketWorkflowStepStatus), |
325 | - u'current_workflow_step': get_enum_title( |
326 | - self.ticket.current_workflow_step, TicketWorkflowStep), |
327 | + u'added_binaries': self.ticket.added_binaries, |
328 | + u'artifact': [{ |
329 | + u'resource_uri': u'/api/v1/fullticketartifact/{0}/'.format( |
330 | + self.artifact_1.pk), |
331 | + u'type': unicode(self.artifact_1.type), |
332 | + u'id': self.artifact_1.pk, |
333 | + u'reference': unicode(self.artifact_1.reference), |
334 | + u'name': unicode(self.artifact_1.name)}], |
335 | + u'status': unicode(get_enum_title(self.ticket.status, |
336 | + TicketWorkflowStepStatus)), |
337 | + u'current_workflow_step': unicode(get_enum_title( |
338 | + self.ticket.current_workflow_step, TicketWorkflowStep)), |
339 | u'description': unicode(self.ticket.description), |
340 | u'title': unicode(self.ticket.title), |
341 | u'subticket': [{ |
342 | - u'status': get_enum_title(self.subticket.status, |
343 | - SubTicketWorkflowStepStatus), |
344 | - u'current_workflow_step': get_enum_title( |
345 | + u'status': unicode(get_enum_title(self.subticket.status, |
346 | + SubTicketWorkflowStepStatus)), |
347 | + u'current_workflow_step': unicode(get_enum_title( |
348 | self.subticket.current_workflow_step, |
349 | - SubTicketWorkflowStep), |
350 | + SubTicketWorkflowStep)), |
351 | u'artifact': [{ |
352 | - u'resource_uri': u'/api/v1/fullartifact/{0}/'.format( |
353 | - self.artifact.pk), |
354 | - u'type': unicode(self.artifact.type), |
355 | - u'id': self.artifact.pk, |
356 | - u'reference': unicode(self.artifact.reference), |
357 | - u'name': unicode(self.artifact.name)}], |
358 | + u'resource_uri': |
359 | + u'/api/v1/fullsubticketartifact/{0}/'.format( |
360 | + self.artifact_2.pk), |
361 | + u'type': unicode(self.artifact_2.type), |
362 | + u'id': self.artifact_2.pk, |
363 | + u'reference': unicode(self.artifact_2.reference), |
364 | + u'name': unicode(self.artifact_2.name)}], |
365 | u'assignee': unicode(self.subticket.assignee), |
366 | u'source_package_upload': { |
367 | u'version': unicode(self.spu.version), |
368 | @@ -82,36 +92,46 @@ |
369 | u'resource_uri': u'/api/v1/fullticket/{0}/'.format(self.ticket.pk), |
370 | u'updated': unicode(self.ticket.updated.strftime( |
371 | "%Y-%m-%dT%H:%M:%S.%f")), |
372 | - u'added_binaries': self.ticket.added_binaries, |
373 | u'removed_binaries': self.ticket.removed_binaries, |
374 | }) |
375 | |
376 | - def test_get_fullartifact_api(self): |
377 | - obj = self.getResource('fullartifact/') |
378 | - self.assertEqual(obj['objects'][0], { |
379 | - u'resource_uri': u'/api/v1/fullartifact/{0}/'.format( |
380 | - self.artifact.pk), |
381 | - u'type': unicode(self.artifact.type), |
382 | - u'id': self.artifact.pk, |
383 | - u'reference': unicode(self.artifact.reference), |
384 | - u'name': unicode(self.artifact.name), |
385 | + def test_get_fullticketartifact_api(self): |
386 | + obj = self.getResource('fullticketartifact/') |
387 | + self.assertEqual(obj['objects'][0], { |
388 | + u'resource_uri': u'/api/v1/fullticketartifact/{0}/'.format( |
389 | + self.artifact_1.pk), |
390 | + u'type': unicode(self.artifact_1.type), |
391 | + u'id': self.artifact_1.pk, |
392 | + u'reference': unicode(self.artifact_1.reference), |
393 | + u'name': unicode(self.artifact_1.name), |
394 | + }) |
395 | + |
396 | + def test_get_fullsubticketartifact_api(self): |
397 | + obj = self.getResource('fullsubticketartifact/') |
398 | + self.assertEqual(obj['objects'][0], { |
399 | + u'resource_uri': u'/api/v1/fullsubticketartifact/{0}/'.format( |
400 | + self.artifact_2.pk), |
401 | + u'type': unicode(self.artifact_2.type), |
402 | + u'id': self.artifact_2.pk, |
403 | + u'reference': unicode(self.artifact_2.reference), |
404 | + u'name': unicode(self.artifact_2.name), |
405 | }) |
406 | |
407 | def test_get_fullsubticket_api(self): |
408 | obj = self.getResource('fullsubticket/') |
409 | self.assertEqual(obj['objects'][0], { |
410 | - u'status': get_enum_title(self.subticket.status, |
411 | - SubTicketWorkflowStepStatus), |
412 | - u'current_workflow_step': get_enum_title( |
413 | + u'status': unicode(get_enum_title(self.subticket.status, |
414 | + SubTicketWorkflowStepStatus)), |
415 | + u'current_workflow_step': unicode(get_enum_title( |
416 | self.subticket.current_workflow_step, |
417 | - SubTicketWorkflowStep), |
418 | + SubTicketWorkflowStep)), |
419 | u'artifact': [{ |
420 | - u'resource_uri': u'/api/v1/fullartifact/{0}/'.format( |
421 | - self.artifact.pk), |
422 | - u'type': unicode(self.artifact.type), |
423 | - u'id': self.artifact.pk, |
424 | - u'reference': unicode(self.artifact.reference), |
425 | - u'name': unicode(self.artifact.name)}], |
426 | + u'resource_uri': u'/api/v1/fullsubticketartifact/{0}/'.format( |
427 | + self.artifact_2.pk), |
428 | + u'type': unicode(self.artifact_2.type), |
429 | + u'id': self.artifact_2.pk, |
430 | + u'reference': unicode(self.artifact_2.reference), |
431 | + u'name': unicode(self.artifact_2.name)}], |
432 | u'assignee': unicode(self.subticket.assignee), |
433 | u'source_package_upload': { |
434 | u'version': unicode(self.spu.version), |
435 | @@ -149,16 +169,20 @@ |
436 | sourcepackage=self.sourcepackage) |
437 | self.subticket_1 = mommy.make('SubTicket', ticket=self.ticket_1, |
438 | source_package_upload=self.spu) |
439 | - self.artifact_1 = mommy.make('Artifact', subticket=self.subticket_1) |
440 | + self.artifact_1 = mommy.make('SubTicketArtifact', |
441 | + subticket=self.subticket_1) |
442 | self.subticket_2 = mommy.make('SubTicket', ticket=self.ticket_2, |
443 | source_package_upload=self.spu) |
444 | - self.artifact_2 = mommy.make('Artifact', subticket=self.subticket_2) |
445 | + self.artifact_2 = mommy.make('SubTicketArtifact', |
446 | + subticket=self.subticket_2) |
447 | self.subticket_3 = mommy.make('SubTicket', ticket=self.ticket_3, |
448 | source_package_upload=self.spu) |
449 | - self.artifact_3 = mommy.make('Artifact', subticket=self.subticket_3) |
450 | + self.artifact_3 = mommy.make('SubTicketArtifact', |
451 | + subticket=self.subticket_3) |
452 | self.subticket_4 = mommy.make('SubTicket', ticket=self.ticket_4, |
453 | source_package_upload=self.spu) |
454 | - self.artifact_4 = mommy.make('Artifact', subticket=self.subticket_4) |
455 | + self.artifact_4 = mommy.make('SubTicketArtifact', |
456 | + subticket=self.subticket_4) |
457 | |
458 | def test_queued_ticket_next(self): |
459 | obj = self.getResource('next/') |
460 | |
461 | === modified file 'ticket_system/ticket/tests/test_models.py' |
462 | --- ticket_system/ticket/tests/test_models.py 2014-01-09 21:08:05 +0000 |
463 | +++ ticket_system/ticket/tests/test_models.py 2014-01-24 17:44:23 +0000 |
464 | @@ -16,10 +16,10 @@ |
465 | from django.db import IntegrityError |
466 | from django.test import TestCase |
467 | from project.models import SourcePackage |
468 | -from ticket.models import (Artifact, SourcePackageUpload, SubTicket, |
469 | - SubTicketWorkflowStep, SubTicketWorkflowStepStatus, |
470 | - Ticket, TicketWorkflowStep, |
471 | - TicketWorkflowStepStatus) |
472 | +from ticket.models import (TicketArtifact, SubTicketArtifact, SubTicket, |
473 | + SourcePackageUpload, SubTicketWorkflowStep, |
474 | + SubTicketWorkflowStepStatus, Ticket, |
475 | + TicketWorkflowStep, TicketWorkflowStepStatus) |
476 | |
477 | |
478 | def create_sourcepackage(name='my-package'): |
479 | @@ -72,9 +72,18 @@ |
480 | return subticket |
481 | |
482 | |
483 | -def create_artifact(name="test.log", subticket=None, |
484 | - reference='123abc'): |
485 | - artifact = Artifact() |
486 | +def create_ticketartifact(name="test.log", ticket=None, reference='123abc'): |
487 | + artifact = TicketArtifact() |
488 | + artifact.name = name |
489 | + artifact.ticket = ticket |
490 | + artifact.reference = reference |
491 | + artifact.save() |
492 | + return artifact |
493 | + |
494 | + |
495 | +def create_subticketartifact(name="test.log", subticket=None, |
496 | + reference='123abc'): |
497 | + artifact = SubTicketArtifact() |
498 | artifact.name = name |
499 | artifact.subticket = subticket |
500 | artifact.reference = reference |
501 | @@ -91,10 +100,10 @@ |
502 | self.subticket = create_subticket(ticket=self.ticket, |
503 | assignee='test@example.com', |
504 | source_package_upload=self.spu) |
505 | - self.artifact_1 = create_artifact(subticket=self.subticket) |
506 | |
507 | - def test_creating_an_artifact(self): |
508 | - artifact_in_database = Artifact.objects.exclude( |
509 | + def test_creating_an_artifact_subticket(self): |
510 | + self.artifact_1 = create_subticketartifact(subticket=self.subticket) |
511 | + artifact_in_database = SubTicketArtifact.objects.exclude( |
512 | subticket=None) |
513 | self.assertEquals(len(artifact_in_database), 1) |
514 | only_artifact_in_database = artifact_in_database[0] |
515 | @@ -102,6 +111,15 @@ |
516 | |
517 | self.assertEquals(only_artifact_in_database.name, "test.log") |
518 | |
519 | + def test_creating_an_artifact_ticket(self): |
520 | + self.artifact_2 = create_ticketartifact(ticket=self.ticket) |
521 | + artifact_in_database = TicketArtifact.objects.exclude(ticket=None) |
522 | + self.assertEquals(len(artifact_in_database), 1) |
523 | + only_artifact_in_database = artifact_in_database[0] |
524 | + self.assertEquals(only_artifact_in_database, self.artifact_2) |
525 | + |
526 | + self.assertEquals(only_artifact_in_database.name, "test.log") |
527 | + |
528 | def test_creating_a_ticket(self): |
529 | ticket_in_database = Ticket.objects.all() |
530 | self.assertEquals(len(ticket_in_database), 1) |
531 | |
532 | === modified file 'ticket_system/ticket/tests/test_read_api.py' |
533 | --- ticket_system/ticket/tests/test_read_api.py 2014-01-22 17:14:18 +0000 |
534 | +++ ticket_system/ticket/tests/test_read_api.py 2014-01-24 17:44:23 +0000 |
535 | @@ -34,13 +34,16 @@ |
536 | sourcepackage=self.sourcepackage) |
537 | self.subticket = mommy.make('SubTicket', ticket=self.ticket, |
538 | source_package_upload=self.spu) |
539 | - self.artifact = mommy.make('Artifact', subticket=self.subticket) |
540 | + self.artifact_1 = mommy.make('SubTicketArtifact', |
541 | + subticket=self.subticket) |
542 | + self.artifact_2 = mommy.make('TicketArtifact', ticket=self.ticket) |
543 | + self.maxDiff = None |
544 | |
545 | - def test_get_artifact_api(self): |
546 | - obj = self.getResource('artifact/') |
547 | + def test_get_artifact_api_subticket(self): |
548 | + obj = self.getResource('subticketartifact/') |
549 | self.assertEqual(obj['objects'][0], { |
550 | - u'name': unicode(self.artifact.name), |
551 | - u'reference': unicode(self.artifact.reference), |
552 | + u'name': unicode(self.artifact_1.name), |
553 | + u'reference': unicode(self.artifact_1.reference), |
554 | u'subticket': { |
555 | u'status': unicode(get_enum_title(self.subticket.status, |
556 | SubTicketWorkflowStepStatus)), |
557 | @@ -81,9 +84,41 @@ |
558 | u'id': self.subticket.pk, |
559 | u'resource_uri': u'/api/v1/subticket/{0}/'.format( |
560 | self.subticket.pk)}, |
561 | - u'type': unicode(self.artifact.type), |
562 | - u'id': self.artifact.pk, |
563 | - u'resource_uri': u'/api/v1/artifact/{0}/'.format(self.artifact.pk), |
564 | + u'type': unicode(self.artifact_1.type), |
565 | + u'id': self.artifact_1.pk, |
566 | + u'resource_uri': u'/api/v1/subticketartifact/{0}/'.format( |
567 | + self.artifact_1.pk), |
568 | + }) |
569 | + |
570 | + def test_get_artifact_api_ticket(self): |
571 | + obj = self.getResource('ticketartifact/') |
572 | + self.assertEqual(obj['objects'][0], { |
573 | + u'name': unicode(self.artifact_2.name), |
574 | + u'reference': unicode(self.artifact_2.reference), |
575 | + u'ticket': { |
576 | + u'added_binaries': self.ticket.added_binaries, |
577 | + u'status': unicode(get_enum_title(self.ticket.status, |
578 | + TicketWorkflowStepStatus)), |
579 | + u'created': unicode(self.ticket.created.strftime( |
580 | + "%Y-%m-%dT%H:%M:%S.%f")), |
581 | + u'current_workflow_step': unicode(get_enum_title( |
582 | + self.ticket.current_workflow_step, |
583 | + TicketWorkflowStep)), |
584 | + u'description': unicode(self.ticket.description), |
585 | + u'title': unicode(self.ticket.title), |
586 | + u'bug_id': self.ticket.bug_id, |
587 | + u'owner': unicode(self.ticket.owner), |
588 | + u'removed_binaries': self.ticket.removed_binaries, |
589 | + u'base_image': unicode(self.ticket.base_image), |
590 | + u'id': self.ticket.pk, |
591 | + u'resource_uri': u'/api/v1/ticket/{0}/'.format( |
592 | + self.ticket.pk), |
593 | + u'updated': unicode(self.ticket.updated.strftime( |
594 | + "%Y-%m-%dT%H:%M:%S.%f"))}, |
595 | + u'type': unicode(self.artifact_2.type), |
596 | + u'id': self.artifact_2.pk, |
597 | + u'resource_uri': u'/api/v1/ticketartifact/{0}/'.format( |
598 | + self.artifact_2.pk), |
599 | }) |
600 | |
601 | def test_get_subticket_api(self): |
602 | @@ -284,25 +319,32 @@ |
603 | sourcepackage=self.sourcepackage) |
604 | self.subticket_1 = mommy.make('SubTicket', ticket=self.ticket_1, |
605 | source_package_upload=self.spu) |
606 | - self.artifact_1 = mommy.make('Artifact', subticket=self.subticket_1) |
607 | + self.artifact_1 = mommy.make('SubTicketArtifact', |
608 | + subticket=self.subticket_1) |
609 | self.subticket_2 = mommy.make('SubTicket', ticket=self.ticket_2, |
610 | source_package_upload=self.spu) |
611 | - self.artifact_2 = mommy.make('Artifact', subticket=self.subticket_2) |
612 | + self.artifact_2 = mommy.make('SubTicketArtifact', |
613 | + subticket=self.subticket_2) |
614 | self.subticket_3 = mommy.make('SubTicket', ticket=self.ticket_3, |
615 | source_package_upload=self.spu) |
616 | - self.artifact_3 = mommy.make('Artifact', subticket=self.subticket_3) |
617 | + self.artifact_3 = mommy.make('SubTicketArtifact', |
618 | + subticket=self.subticket_3) |
619 | self.subticket_4 = mommy.make('SubTicket', ticket=self.ticket_4, |
620 | source_package_upload=self.spu) |
621 | - self.artifact_4 = mommy.make('Artifact', subticket=self.subticket_4) |
622 | + self.artifact_4 = mommy.make('SubTicketArtifact', |
623 | + subticket=self.subticket_4) |
624 | self.subticket_5 = mommy.make('SubTicket', ticket=self.ticket_5, |
625 | source_package_upload=self.spu) |
626 | - self.artifact_5 = mommy.make('Artifact', subticket=self.subticket_5) |
627 | + self.artifact_5 = mommy.make('SubTicketArtifact', |
628 | + subticket=self.subticket_5) |
629 | self.subticket_6 = mommy.make('SubTicket', ticket=self.ticket_6, |
630 | source_package_upload=self.spu) |
631 | - self.artifact_6 = mommy.make('Artifact', subticket=self.subticket_6) |
632 | + self.artifact_6 = mommy.make('SubTicketArtifact', |
633 | + subticket=self.subticket_6) |
634 | self.subticket_7 = mommy.make('SubTicket', ticket=self.ticket_7, |
635 | source_package_upload=self.spu) |
636 | - self.artifact_7 = mommy.make('Artifact', subticket=self.subticket_7) |
637 | + self.artifact_7 = mommy.make('SubTicketArtifact', |
638 | + subticket=self.subticket_7) |
639 | |
640 | def test_total_open_tickets(self): |
641 | obj = self.getResource('opentickets/') |
642 | |
643 | === modified file 'ticket_system/ticket/tests/test_write_api.py' |
644 | --- ticket_system/ticket/tests/test_write_api.py 2014-01-24 15:10:17 +0000 |
645 | +++ ticket_system/ticket/tests/test_write_api.py 2014-01-24 17:44:23 +0000 |
646 | @@ -17,9 +17,9 @@ |
647 | from model_mommy.recipe import Recipe, seq |
648 | from ci_utils.tastypie.test import TastypieTestCase |
649 | from project.models import SourcePackage |
650 | -from ticket.models import (Artifact, SourcePackageUpload, SubTicket, |
651 | +from ticket.models import (TicketArtifact, SourcePackageUpload, SubTicket, |
652 | SubTicketWorkflowStep, SubTicketWorkflowStepStatus, |
653 | - Ticket, TicketWorkflowStep, |
654 | + Ticket, TicketWorkflowStep, SubTicketArtifact, |
655 | TicketWorkflowStepStatus) |
656 | |
657 | |
658 | @@ -132,7 +132,7 @@ |
659 | 'assignee': 'test@example.com', |
660 | } |
661 | |
662 | - def test_post_subticket(self): |
663 | + def test_post_subticket_subticket(self): |
664 | # Check how many are there first. |
665 | self.assertEqual(SubTicket.objects.count(), 1) |
666 | self.post(resource=self.resource, params=self.post_subticket_data) |
667 | @@ -159,32 +159,86 @@ |
668 | self.assertHttpMethodNotAllowed(resp) |
669 | |
670 | |
671 | -class APICreateArtifactResourceTest(TastypieTestCase): |
672 | - |
673 | - def setUp(self): |
674 | - super(APICreateArtifactResourceTest, self).setUp('/api/v1') |
675 | +class APICreateTicketArtifactResourceTest(TastypieTestCase): |
676 | + |
677 | + def setUp(self): |
678 | + super(APICreateTicketArtifactResourceTest, self).setUp('/api/v1') |
679 | + self.ticket = mommy.make('Ticket') |
680 | + self.artifact = mommy.make('TicketArtifact', |
681 | + ticket=self.ticket) |
682 | + self.resource = 'ticketartifact/' |
683 | + self.detail_url = self.resource + '{0}/'.format(self.artifact.pk) |
684 | + self.ticket_uri = '/api/v1/ticket/{0}/'.format(self.ticket.pk) |
685 | + self.post_artifact_data_ticket = { |
686 | + 'type': 'LOGS', |
687 | + 'name': 'my_artifact', |
688 | + 'ticket': self.ticket_uri, |
689 | + 'reference': 'http://www.example.com/m1CTLg5FHY', |
690 | + } |
691 | + |
692 | + def test_post_artifact_ticket(self): |
693 | + """ |
694 | + Test creating an artifact for a ticket via the API |
695 | + """ |
696 | + # Check how many are there first. |
697 | + self.assertEqual(TicketArtifact.objects.count(), 1) |
698 | + self.post(resource=self.resource, |
699 | + params=self.post_artifact_data_ticket) |
700 | + # Verify a new one has been added. |
701 | + self.assertEqual(TicketArtifact.objects.count(), 2) |
702 | + |
703 | + def test_patch_detail(self): |
704 | + # Grab the current data & modify it slightly. |
705 | + original_data = self.getResource(self.detail_url) |
706 | + new_data = original_data.copy() |
707 | + new_data['name'] = 'my_upload' |
708 | + |
709 | + self.assertEqual(TicketArtifact.objects.count(), 1) |
710 | + resp = self.client.patch('/api/v1/' + self.detail_url, data=new_data) |
711 | + self.assertHttpMethodNotAllowed(resp) |
712 | + # Make sure the count hasn't changed & we did an update. |
713 | + self.assertEqual(TicketArtifact.objects.count(), 1) |
714 | + # Check for updated data. |
715 | + self.assertEqual(TicketArtifact.objects.get( |
716 | + pk=self.artifact.pk).name, self.artifact.name) |
717 | + |
718 | + def test_delete_artifact_not_allowed(self): |
719 | + resp = self.delete(resource=self.detail_url) |
720 | + self.assertHttpMethodNotAllowed(resp) |
721 | + |
722 | + |
723 | +class APICreateSubTicketArtifactResourceTest(TastypieTestCase): |
724 | + |
725 | + def setUp(self): |
726 | + super(APICreateSubTicketArtifactResourceTest, self).setUp('/api/v1') |
727 | self.sourcepackage = sourcepackage_recipe.make() |
728 | self.spu = mommy.make('SourcePackageUpload', |
729 | sourcepackage=self.sourcepackage) |
730 | - self.subticket = mommy.make('SubTicket', |
731 | + self.ticket = mommy.make('Ticket') |
732 | + self.subticket = mommy.make('SubTicket', ticket=self.ticket, |
733 | source_package_upload=self.spu) |
734 | - self.artifact = mommy.make('Artifact', subticket=self.subticket) |
735 | - self.resource = 'artifact/' |
736 | + self.artifact = mommy.make('SubTicketArtifact', |
737 | + subticket=self.subticket) |
738 | + self.resource = 'subticketartifact/' |
739 | self.detail_url = self.resource + '{0}/'.format(self.artifact.pk) |
740 | self.subticket_uri = '/api/v1/subticket/{0}/'.format(self.subticket.pk) |
741 | - self.post_artifact_data = { |
742 | + self.post_artifact_data_subticket = { |
743 | 'type': 'SPU', |
744 | 'name': 'my_artifact', |
745 | 'subticket': self.subticket_uri, |
746 | - 'reference': 'm1CTLg5FHY', |
747 | + 'reference': 'http://www.example.com/m1CTLg5FHY', |
748 | } |
749 | |
750 | - def test_post_artifact(self): |
751 | + def test_post_artifact_subticket(self): |
752 | + """ |
753 | + Test creating an artifact for a subticket via the API |
754 | + """ |
755 | # Check how many are there first. |
756 | - self.assertEqual(Artifact.objects.count(), 1) |
757 | - self.post(resource=self.resource, params=self.post_artifact_data) |
758 | + self.assertEqual(SubTicketArtifact.objects.count(), 1) |
759 | + self.post(resource=self.resource, |
760 | + params=self.post_artifact_data_subticket) |
761 | # Verify a new one has been added. |
762 | - self.assertEqual(Artifact.objects.count(), 2) |
763 | + self.assertEqual(SubTicketArtifact.objects.count(), 2) |
764 | |
765 | def test_patch_detail(self): |
766 | # Grab the current data & modify it slightly. |
767 | @@ -192,14 +246,14 @@ |
768 | new_data = original_data.copy() |
769 | new_data['name'] = 'my_upload' |
770 | |
771 | - self.assertEqual(Artifact.objects.count(), 1) |
772 | + self.assertEqual(SubTicketArtifact.objects.count(), 1) |
773 | resp = self.client.patch('/api/v1/' + self.detail_url, data=new_data) |
774 | self.assertHttpMethodNotAllowed(resp) |
775 | # Make sure the count hasn't changed & we did an update. |
776 | - self.assertEqual(Artifact.objects.count(), 1) |
777 | + self.assertEqual(SubTicketArtifact.objects.count(), 1) |
778 | # Check for updated data. |
779 | - self.assertEqual(Artifact.objects.get(pk=self.artifact.pk).name, |
780 | - self.artifact.name) |
781 | + self.assertEqual(SubTicketArtifact.objects.get( |
782 | + pk=self.artifact.pk).name, self.artifact.name) |
783 | |
784 | def test_delete_artifact_not_allowed(self): |
785 | resp = self.delete(resource=self.detail_url) |
786 | |
787 | === modified file 'ticket_system/ticket_system/urls.py' |
788 | --- ticket_system/ticket_system/urls.py 2014-01-20 16:41:12 +0000 |
789 | +++ ticket_system/ticket_system/urls.py 2014-01-24 17:44:23 +0000 |
790 | @@ -18,12 +18,14 @@ |
791 | from tastypie.api import Api |
792 | from people.api import PersonResource |
793 | from project.api import SourcePackageResource, BinaryPackageResource |
794 | -from ticket.api import (TicketResource, SubTicketResource, ArtifactResource, |
795 | +from ticket.api import (TicketResource, SubTicketResource, |
796 | + TicketArtifactResource, SubTicketArtifactResource, |
797 | SourcePackageUploadResource, FullSubTicketResource, |
798 | - FullTicketResource, FullArtifactResource, |
799 | + FullTicketResource, FullTicketArtifactResource, |
800 | TicketStatusResource, TicketUpdateStatusResource, |
801 | SubTicketUpdateStatusResource, NextTicketResource, |
802 | - SubTicketStatusResource, OpenTicketResource) |
803 | + SubTicketStatusResource, OpenTicketResource, |
804 | + FullSubTicketArtifactResource) |
805 | |
806 | admin.autodiscover() |
807 | v1_api = Api(api_name='v1') |
808 | @@ -33,8 +35,10 @@ |
809 | v1_api.register(TicketResource()) |
810 | v1_api.register(SubTicketResource()) |
811 | v1_api.register(SourcePackageUploadResource()) |
812 | -v1_api.register(ArtifactResource()) |
813 | -v1_api.register(FullArtifactResource()) |
814 | +v1_api.register(TicketArtifactResource()) |
815 | +v1_api.register(SubTicketArtifactResource()) |
816 | +v1_api.register(FullTicketArtifactResource()) |
817 | +v1_api.register(FullSubTicketArtifactResource()) |
818 | v1_api.register(FullSubTicketResource()) |
819 | v1_api.register(FullTicketResource()) |
820 | v1_api.register(SubTicketUpdateStatusResource()) |
Looks good.