Merge lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-more-write-api into lp:ubuntu-ci-services-itself

Proposed by Chris Johnston
Status: Merged
Approved by: Chris Johnston
Approved revision: 58
Merged at revision: 60
Proposed branch: lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-more-write-api
Merge into: lp:ubuntu-ci-services-itself
Diff against target: 321 lines (+176/-21)
6 files modified
docs/components/ticket-system.rst (+30/-9)
ticket_system/project/admin.py (+1/-2)
ticket_system/ticket/api.py (+7/-3)
ticket_system/ticket/tests/test_full_read_api.py (+2/-2)
ticket_system/ticket/tests/test_read_api.py (+4/-4)
ticket_system/ticket/tests/test_write_api.py (+132/-1)
To merge this branch: bzr merge lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-more-write-api
Reviewer Review Type Date Requested Status
Francis Ginther Approve
Review via email: mp+199857@code.launchpad.net

Commit message

Add a write API to the TS SubTicket/SPU/Artifact resources

Description of the change

- Removed set ticket status and set subticket status as they will both be done on a pull basis for phase 0.
- Added write API to SubTicket, Artifact, and SPU resources
- Updated docs

To post a comment you must log in.
Revision history for this message
Francis Ginther (fginther) wrote :

The API looks reasonable for what was discussed in IRC. I would recommend an example if someone else was working on the CLI, but in this case it's not needed.

I noticed a few doc rendering issues caused by a missing line between '::' and the following code snippet.

review: Approve
Revision history for this message
Chris Johnston (cjohnston) wrote :

I will fix the doc in the second branch just to not cause any issues with conflicts.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'docs/components/ticket-system.rst'
2--- docs/components/ticket-system.rst 2013-12-17 16:50:29 +0000
3+++ docs/components/ticket-system.rst 2013-12-20 19:48:25 +0000
4@@ -32,6 +32,13 @@
5 * Handle status updates from the Landing Manager.
6 * Provide read API for the Web Server.
7
8+TODO:
9+-----
10+
11+* Bundle the ticket creation API
12+
13+ * For speed, I have left the different APIs to fully create a ticket as seperate. If/when there is time, it would be much improved to bundle this together into one single 'Bundle API'
14+
15 Future
16 ------
17
18@@ -146,6 +153,10 @@
19
20 Create a ticket from one or more source package uploads.
21
22+::
23+
24+ curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"owner": "owner@example.com", "title": "My first ticket", "description": "This if my first ticket. See what it can do", "bug_id": "12345", "added_binaries": "binary-1,binary-2", "removed_binaries": "binary-3,binary-4"}' http://localhost:8000/api/v1/ticket/
25+
26 get_ticket
27 ~~~~~~~~~~
28
29@@ -160,15 +171,25 @@
30
31 Get the ticket status.
32
33-set_ticket_status
34-~~~~~~~~~~~~~~~~~
35-
36-Set the ticket status.
37-
38-set_subticket_status
39-~~~~~~~~~~~~~~~~~~~~
40-
41-Set a subticket status, given the ticket number and the source package name.
42+create_source_package_upload
43+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44+
45+::
46+
47+ curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"sourcepackage": "/api/v1/sourcepackage/X/", "version": "1.0"}' http://localhost:8000/api/v1/spu/
48+
49+create_artifact
50+~~~~~~~~~~~~~~~
51+
52+::
53+
54+ curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my_artifact", "ticket_component": "/api/v1/ticket/X/", "reference": "http://path.to/artifact/", "type": "SPU"}' http://localhost:8000/api/v1/artifact/
55+
56+create_subticket
57+~~~~~~~~~~~~~~~~
58+
59+::
60+ curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"source_package_upload": "/api/v1/spu/X/, "ticket": "/api/v1/ticket/X/, "assignee": "test@example.com"}' http://localhost:8000/api/v1/subticket/
61
62 Person
63 ------
64
65=== modified file 'ticket_system/project/admin.py'
66--- ticket_system/project/admin.py 2013-12-09 19:35:22 +0000
67+++ ticket_system/project/admin.py 2013-12-20 19:48:25 +0000
68@@ -23,9 +23,8 @@
69
70
71 class BinaryPackageAdmin(admin.ModelAdmin):
72- list_filter = ['seeded']
73 search_fields = ['name', 'sourcepackage']
74- list_display = ('name', 'sourcepackage', 'seeded')
75+ list_display = ('name', 'sourcepackage')
76
77 admin.site.register(SourcePackage, SourcePackageAdmin)
78 admin.site.register(BinaryPackage, BinaryPackageAdmin)
79
80=== modified file 'ticket_system/ticket/api.py'
81--- ticket_system/ticket/api.py 2013-12-19 20:25:35 +0000
82+++ ticket_system/ticket/api.py 2013-12-20 19:48:25 +0000
83@@ -35,7 +35,9 @@
84
85 class Meta:
86 queryset = SourcePackageUpload.objects.all()
87- allowed_methods = ['get']
88+ allowed_methods = ['get', 'post']
89+ authorization = Authorization()
90+ resource_name = 'spu'
91
92
93 class SubTicketResource(ModelResource):
94@@ -46,7 +48,8 @@
95
96 class Meta:
97 queryset = SubTicket.objects.all()
98- allowed_methods = ['get']
99+ allowed_methods = ['get', 'post']
100+ authorization = Authorization()
101
102
103 class ArtifactResource(ModelResource):
104@@ -55,7 +58,8 @@
105
106 class Meta:
107 queryset = Artifact.objects.all()
108- allowed_methods = ['get']
109+ allowed_methods = ['get', 'post']
110+ authorization = Authorization()
111
112
113 class FullArtifactResource(ModelResource):
114
115=== modified file 'ticket_system/ticket/tests/test_full_read_api.py'
116--- ticket_system/ticket/tests/test_full_read_api.py 2013-12-19 17:26:40 +0000
117+++ ticket_system/ticket/tests/test_full_read_api.py 2013-12-20 19:48:25 +0000
118@@ -57,7 +57,7 @@
119 u'name': unicode(self.sourcepackage.name)},
120 u'id': self.spu.pk,
121 u'resource_uri':
122- u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
123+ u'/api/v1/spu/{0}/'.format(self.spu.pk)},
124 u'id': self.subticket.pk,
125 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
126 self.subticket.pk)}],
127@@ -107,7 +107,7 @@
128 u'name': unicode(self.sourcepackage.name)},
129 u'id': self.spu.pk,
130 u'resource_uri':
131- u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
132+ u'/api/v1/spu/{0}/'.format(self.spu.pk)},
133 u'id': self.subticket.pk,
134 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
135 self.subticket.pk),
136
137=== modified file 'ticket_system/ticket/tests/test_read_api.py'
138--- ticket_system/ticket/tests/test_read_api.py 2013-12-19 16:07:20 +0000
139+++ ticket_system/ticket/tests/test_read_api.py 2013-12-20 19:48:25 +0000
140@@ -47,7 +47,7 @@
141 u'name': unicode(self.sourcepackage.name)},
142 u'id': self.spu.pk,
143 u'resource_uri':
144- u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
145+ u'/api/v1/spu/{0}/'.format(self.spu.pk)},
146 u'ticket': {
147 u'status': self.ticket.status,
148 u'current_workflow_step':
149@@ -83,7 +83,7 @@
150 u'name': unicode(self.sourcepackage.name)},
151 u'id': self.spu.pk,
152 u'resource_uri':
153- u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
154+ u'/api/v1/spu/{0}/'.format(self.spu.pk)},
155 u'ticket': {
156 u'status': self.ticket.status,
157 u'current_workflow_step':
158@@ -118,7 +118,7 @@
159 })
160
161 def test_get_sourcepackageupload_api(self):
162- obj = self.getResource('sourcepackageupload/')
163+ obj = self.getResource('spu/')
164 self.assertEqual(obj['objects'][0], {
165 u'version': unicode(self.spu.version),
166 u'sourcepackage': {
167@@ -128,5 +128,5 @@
168 u'name': unicode(self.sourcepackage.name)},
169 u'id': self.spu.pk,
170 u'resource_uri':
171- u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk),
172+ u'/api/v1/spu/{0}/'.format(self.spu.pk),
173 })
174
175=== modified file 'ticket_system/ticket/tests/test_write_api.py'
176--- ticket_system/ticket/tests/test_write_api.py 2013-12-19 20:25:35 +0000
177+++ ticket_system/ticket/tests/test_write_api.py 2013-12-20 19:48:25 +0000
178@@ -15,7 +15,7 @@
179
180 from model_mommy import mommy
181 from ci_utils.tastypie.test import TastypieTestCase
182-from ticket.models import Ticket
183+from ticket.models import Ticket, SourcePackageUpload, SubTicket, Artifact
184
185
186 class APICreateTicketResourceTest(TastypieTestCase):
187@@ -59,3 +59,134 @@
188 def test_delete_ticket_not_allowed(self):
189 resp = self.delete(resource=self.detail_url)
190 self.assertHttpMethodNotAllowed(resp)
191+
192+
193+class APICreateSPUResourceTest(TastypieTestCase):
194+
195+ def setUp(self):
196+ super(APICreateSPUResourceTest, self).setUp('/api/v1')
197+ self.sourcepackage = mommy.make('SourcePackage')
198+ self.spu = mommy.make('SourcePackageUpload',
199+ sourcepackage=self.sourcepackage)
200+ self.resource = 'spu/'
201+ self.detail_url = self.resource + '{0}/'.format(self.spu.pk)
202+ self.sourcepackage_uri = '/api/v1/sourcepackage/{0}/'.format(
203+ self.sourcepackage.pk)
204+ self.post_spu_data = {
205+ 'sourcepackage': self.sourcepackage_uri,
206+ 'version': '1.0',
207+ }
208+
209+ def test_post_spu(self):
210+ # Check how many are there first.
211+ self.assertEqual(SourcePackageUpload.objects.count(), 1)
212+ self.post(resource=self.resource, params=self.post_spu_data)
213+ # Verify a new one has been added.
214+ self.assertEqual(SourcePackageUpload.objects.count(), 2)
215+
216+ def test_patch_detail(self):
217+ # Grab the current data & modify it slightly.
218+ original_data = self.getResource(self.detail_url)
219+ new_data = original_data.copy()
220+ new_data['version'] = '2.0'
221+
222+ self.assertEqual(SourcePackageUpload.objects.count(), 1)
223+ resp = self.client.patch('/api/v1/' + self.detail_url, data=new_data)
224+ self.assertHttpMethodNotAllowed(resp)
225+ # Make sure the count hasn't changed & we did an update.
226+ self.assertEqual(SourcePackageUpload.objects.count(), 1)
227+ # Check for updated data.
228+ self.assertEqual(SourcePackageUpload.objects.get(
229+ pk=self.spu.pk).version, self.spu.version)
230+
231+ def test_delete_spu_not_allowed(self):
232+ resp = self.delete(resource=self.detail_url)
233+ self.assertHttpMethodNotAllowed(resp)
234+
235+
236+class APICreateSubTicketResourceTest(TastypieTestCase):
237+
238+ def setUp(self):
239+ super(APICreateSubTicketResourceTest, self).setUp('/api/v1')
240+ self.ticket = mommy.make('Ticket')
241+ self.spu = mommy.make('SourcePackageUpload')
242+ self.subticket = mommy.make('SubTicket', ticket=self.ticket,
243+ source_package_upload=self.spu)
244+ self.resource = 'subticket/'
245+ self.detail_url = self.resource + '{0}/'.format(self.subticket.pk)
246+ self.ticket_uri = '/api/v1/spu/{0}/'.format(self.ticket.pk)
247+ self.spu_uri = '/api/v1/spu/{0}/'.format(self.spu.pk)
248+ self.post_subticket_data = {
249+ 'source_package_upload': self.spu_uri,
250+ 'ticket': self.ticket_uri,
251+ 'assignee': 'test@example.com',
252+ }
253+
254+ def test_post_subticket(self):
255+ # Check how many are there first.
256+ self.assertEqual(SubTicket.objects.count(), 1)
257+ self.post(resource=self.resource, params=self.post_subticket_data)
258+ # Verify a new one has been added.
259+ self.assertEqual(SubTicket.objects.count(), 2)
260+
261+ def test_patch_detail(self):
262+ # Grab the current data & modify it slightly.
263+ original_data = self.getResource(self.detail_url)
264+ new_data = original_data.copy()
265+ new_data['assignee'] = 'me@example.com'
266+
267+ self.assertEqual(SubTicket.objects.count(), 1)
268+ resp = self.client.patch('/api/v1/' + self.detail_url, data=new_data)
269+ self.assertHttpMethodNotAllowed(resp)
270+ # Make sure the count hasn't changed & we did an update.
271+ self.assertEqual(SubTicket.objects.count(), 1)
272+ # Check for updated data.
273+ self.assertEqual(SubTicket.objects.get(pk=self.subticket.pk).assignee,
274+ self.subticket.assignee)
275+
276+ def test_delete_subticket_not_allowed(self):
277+ resp = self.delete(resource=self.detail_url)
278+ self.assertHttpMethodNotAllowed(resp)
279+
280+
281+class APICreateArtifactResourceTest(TastypieTestCase):
282+
283+ def setUp(self):
284+ super(APICreateArtifactResourceTest, self).setUp('/api/v1')
285+ self.subticket = mommy.make('SubTicket')
286+ self.artifact = mommy.make('Artifact', ticket_component=self.subticket)
287+ self.resource = 'artifact/'
288+ self.detail_url = self.resource + '{0}/'.format(self.artifact.pk)
289+ self.subticket_uri = '/api/v1/subticket/{0}/'.format(self.subticket.pk)
290+ self.post_artifact_data = {
291+ 'type': 'SPU',
292+ 'name': 'my_artifact',
293+ 'ticket_component': self.subticket_uri,
294+ 'reference': 'm1CTLg5FHY',
295+ }
296+
297+ def test_post_artifact(self):
298+ # Check how many are there first.
299+ self.assertEqual(Artifact.objects.count(), 1)
300+ self.post(resource=self.resource, params=self.post_artifact_data)
301+ # Verify a new one has been added.
302+ self.assertEqual(Artifact.objects.count(), 2)
303+
304+ def test_patch_detail(self):
305+ # Grab the current data & modify it slightly.
306+ original_data = self.getResource(self.detail_url)
307+ new_data = original_data.copy()
308+ new_data['name'] = 'my_upload'
309+
310+ self.assertEqual(Artifact.objects.count(), 1)
311+ resp = self.client.patch('/api/v1/' + self.detail_url, data=new_data)
312+ self.assertHttpMethodNotAllowed(resp)
313+ # Make sure the count hasn't changed & we did an update.
314+ self.assertEqual(Artifact.objects.count(), 1)
315+ # Check for updated data.
316+ self.assertEqual(Artifact.objects.get(pk=self.artifact.pk).name,
317+ self.artifact.name)
318+
319+ def test_delete_artifact_not_allowed(self):
320+ resp = self.delete(resource=self.detail_url)
321+ self.assertHttpMethodNotAllowed(resp)

Subscribers

People subscribed via source and target branches