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

Proposed by Chris Johnston
Status: Merged
Approved by: Chris Johnston
Approved revision: 38
Merged at revision: 41
Proposed branch: lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-read-api
Merge into: lp:ubuntu-ci-services-itself
Diff against target: 513 lines (+417/-4)
8 files modified
docs/components/ticket-system.rst (+7/-0)
ticket_system/ticket/api.py (+89/-0)
ticket_system/ticket/migrations/0001_initial.py (+2/-2)
ticket_system/ticket/models.py (+2/-2)
ticket_system/ticket/tests/__init__.py (+18/-0)
ticket_system/ticket/tests/test_full_read_api.py (+126/-0)
ticket_system/ticket/tests/test_read_api.py (+163/-0)
ticket_system/ticket_system/urls.py (+10/-0)
To merge this branch: bzr merge lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-read-api
Reviewer Review Type Date Requested Status
Ursula Junque (community) Approve
Francis Ginther Approve
Review via email: mp+198870@code.launchpad.net

Commit message

Add read API to tickets

To post a comment you must log in.
31. By Andy Doan

[r=Francis Ginther] Disable the requirement for ApiKey authentication for TastyPie componenents.

For the first phase of this project we are going to disable authentication. By doing this, we get the added bonus of not having to lug around the ugly "USE_TZ=True" hack. from Andy Doan

32. By Andy Doan

[r=Evan Dandrea] make the ticket-system deployable via django charm from Andy Doan

33. By Chris Johnston

[r=Francis Ginther, Andy Doan] Add a write API for adding people, update docs with API examples from Chris Johnston

34. By Andy Doan

[r=Francis Ginther] Disable the requirement for ApiKey authentication for TastyPie componenents.

For the first phase of this project we are going to disable authentication. By doing this, we get the added bonus of not having to lug around the ugly "USE_TZ=True" hack. from Andy Doan

35. By Andy Doan

[r=Francis Ginther] this moves some utility code out of the branch-source-builder and into ci-utils for others to use. from Andy Doan

36. By Andy Doan

[r=Vincent Ladeuil] configure a default LP user for the ppa-assigner in the juju deployer

also fixes a typo/bug for the dependencies
  from Andy Doan

37. By Andy Doan

[r=Vincent Ladeuil] add documentation on how to set up an oauth token from Andy Doan

38. By Chris Johnston

Fix conflict

Revision history for this message
Ursula Junque (ursinha) wrote :

Hi Chris,

I have one question:

Other components will need to reach subtickets directly to update their status, for example the Lander needs to reach the subticket to mark it as built. AFAIK all other components will use the Ticket ID to reach it, so we'd need to do something like api/v1/ticket/foobar/123, 'foobar' being the source package name and 123 the ticket ID. Is that possible with tastypie? If not, we could do something like 'foobar-123' and parse it. This way you wouldn't need to expose subticket directly, as it would be only used internally, considering the fullticket displays all subtickets and for WebUI purposes that should be enough.

Of course, this approach works considering there will be only one source package upload in active state per source package in a ticket -- others can correct me if I'm wrong and we should consider several landings of the same SP in a single ticket.

Cheers,

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

I believe what your referring to here can be accomplished by nested
resources from tastypie, however IMO it is outside the scope of this MP.
This MP is purely for consuming data from the ticket system, mainly for the
web UI. Any other concerns with this MP?

Revision history for this message
Francis Ginther (fginther) wrote :

For the initial phase, the lander and other internal components will not be sending updates via the REST API. The only consumers for the this API should be the front end web ui.

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

Francis, how will the ticket statuses be kept up to date?

Revision history for this message
Francis Ginther (fginther) wrote :

> Francis, how will the ticket statuses be kept up to date?

The original design had the ticket system polling the lander for progress. This was updated to use a message queue instead. The ticket system provides a message queue handle to the lander as part of the execute_request call. The lander then uses this to provide regular progress updates (every 60 seconds or sooner if status changes). The exact details of what is in the message haven't been designed yet, but it would probably have to provide the status of all lander stages in each message. For example:

{"Package Building": "Completed",
 "Image Building": "In Progress",
 "Image Testing": "Not Started",
 "Package Publishing": "Not Started"}

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

Ok, so the TS will be polling the message queue looking for status changes?

Revision history for this message
Ursula Junque (ursinha) wrote :

> Ok, so the TS will be polling the message queue looking for status changes?

That's my understanding, confirmed by fginther:
<Ursinha> fginther, let me see if I got the message queue idea: "Lander" updates whatever it is in the message queue, ticket system polls the message queue for updates, therefore there's no need of a write API to update ticket status for phase 0?
<fginther> Ursinha, yes, you're correct

(slightly edited for simplicity :))

Revision history for this message
Andy Doan (doanac) wrote :

On Mon, Dec 16, 2013 at 7:31 PM, Ursula Junque <email address hidden> wrote:

> That's my understanding, confirmed by fginther:
> <Ursinha> fginther, let me see if I got the message queue idea: "Lander"
> updates whatever it is in the message queue, ticket system polls the
> message queue for updates, therefore there's no need of a write API to
> update ticket status for phase 0?
> <fginther> Ursinha, yes, you're correct
>

We have a "rabbit worker" example that you can steal from to get started:

http://bazaar.launchpad.net/~canonical-ci-engineering/ubuntu-ci-services-itself/trunk/view/head:/branch-source-builder/run_worker

Its juju deployable with:

http://bazaar.launchpad.net/~canonical-ci-engineering/ubuntu-ci-services-itself/trunk/view/head:/juju-deployer/branch-source-builder.yaml#L17

I'm guessing your "on_message" calls would basically be updates to the
django model or something

Revision history for this message
Ursula Junque (ursinha) wrote :

For phase 0 I think this is okay, I'll make the necessary changes for subticket when we need it later.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'docs/components/ticket-system.rst'
--- docs/components/ticket-system.rst 2013-12-12 16:53:14 +0000
+++ docs/components/ticket-system.rst 2013-12-16 14:22:25 +0000
@@ -137,6 +137,9 @@
137137
138List all open tickets.138List all open tickets.
139139
140::
141
142 curl --dump-header - http://162.213.34.2:8000/api/v1/ticket/
140143
141create_ticket144create_ticket
142~~~~~~~~~~~~~145~~~~~~~~~~~~~
@@ -148,6 +151,10 @@
148151
149Return the ticket given its id.152Return the ticket given its id.
150153
154::
155
156 curl --dump-header - http://162.213.34.2:8000/api/v1/ticket/1/
157
151get_ticket_status158get_ticket_status
152~~~~~~~~~~~~~~~~~159~~~~~~~~~~~~~~~~~
153160
154161
=== added file 'ticket_system/ticket/api.py'
--- ticket_system/ticket/api.py 1970-01-01 00:00:00 +0000
+++ ticket_system/ticket/api.py 2013-12-16 14:22:25 +0000
@@ -0,0 +1,89 @@
1# Ubuntu Continuous Integration Engine
2# Copyright 2013 Canonical Ltd.
3
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU Affero General Public License version 3, as
6# published by the Free Software Foundation.
7
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU Affero General Public License for more details.
12
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16from tastypie import fields
17from tastypie.resources import ModelResource
18from ticket.models import Ticket, SubTicket, SourcePackageUpload, Artifact
19
20from people.api import PersonResource
21from project.api import SourcePackageResource
22
23
24class TicketResource(ModelResource):
25 owner = fields.ToOneField(PersonResource, 'owner', full=True)
26
27 class Meta:
28 queryset = Ticket.objects.all()
29 allowed_methods = ['get']
30
31
32class SourcePackageUploadResource(ModelResource):
33 sourcepackage = fields.ToOneField(SourcePackageResource, 'sourcepackage',
34 full=True)
35
36 class Meta:
37 queryset = SourcePackageUpload.objects.all()
38 allowed_methods = ['get']
39
40
41class SubTicketResource(ModelResource):
42 assignee = fields.ToOneField(PersonResource, 'assignee', full=True)
43 ticket = fields.ToOneField(TicketResource, 'ticket', full=True)
44 source_package_upload = fields.ToOneField(SourcePackageUploadResource,
45 'source_package_upload',
46 full=True)
47
48 class Meta:
49 queryset = SubTicket.objects.all()
50 allowed_methods = ['get']
51
52
53class ArtifactResource(ModelResource):
54 ticket_component = fields.ToOneField(SubTicketResource, 'ticket_component',
55 full=True)
56
57 class Meta:
58 queryset = Artifact.objects.all()
59 allowed_methods = ['get']
60
61
62class FullArtifactResource(ModelResource):
63
64 class Meta:
65 queryset = Artifact.objects.all()
66 allowed_methods = ['get']
67
68
69class FullSubTicketResource(ModelResource):
70 assignee = fields.ToOneField(PersonResource, 'assignee', full=True)
71 source_package_upload = fields.ToOneField(SourcePackageUploadResource,
72 'source_package_upload',
73 full=True)
74 artifact = fields.ToManyField(FullArtifactResource, 'artifact_set',
75 full=True)
76
77 class Meta:
78 queryset = SubTicket.objects.all()
79 allowed_methods = ['get']
80
81
82class FullTicketResource(ModelResource):
83 owner = fields.ToOneField(PersonResource, 'owner', full=True)
84 subticket = fields.ToManyField(FullSubTicketResource, 'subticket_set',
85 full=True)
86
87 class Meta:
88 queryset = Ticket.objects.all()
89 allowed_methods = ['get']
090
=== modified file 'ticket_system/ticket/migrations/0001_initial.py'
--- ticket_system/ticket/migrations/0001_initial.py 2013-12-11 17:18:58 +0000
+++ ticket_system/ticket/migrations/0001_initial.py 2013-12-16 14:22:25 +0000
@@ -44,7 +44,7 @@
44 db.create_table('artifact', (44 db.create_table('artifact', (
45 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),45 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
46 ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)),46 ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)),
47 ('ticket_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SubTicket'], null=True, blank=True)),47 ('ticket_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SubTicket'])),
48 ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)),48 ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)),
49 ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)),49 ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)),
50 ))50 ))
@@ -83,7 +83,7 @@
83 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),83 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),84 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
85 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),85 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
86 'ticket_component': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']", 'null': 'True', 'blank': 'True'}),86 'ticket_component': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']"}),
87 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})87 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
88 },88 },
89 u'ticket.sourcepackageupload': {89 u'ticket.sourcepackageupload': {
9090
=== modified file 'ticket_system/ticket/models.py'
--- ticket_system/ticket/models.py 2013-12-11 18:03:39 +0000
+++ ticket_system/ticket/models.py 2013-12-16 14:22:25 +0000
@@ -140,7 +140,7 @@
140 source_package_upload = models.ForeignKey(SourcePackageUpload)140 source_package_upload = models.ForeignKey(SourcePackageUpload)
141141
142 def __unicode__(self):142 def __unicode__(self):
143 return "{} {}".format(self.ticket, self.sourcepackageupload)143 return "{} {}".format(self.ticket, self.source_package_upload)
144144
145145
146class Artifact(models.Model):146class Artifact(models.Model):
@@ -153,7 +153,7 @@
153 ('LOGS', 'Logs'),153 ('LOGS', 'Logs'),
154 )154 )
155 type = models.CharField(choices=ARTIFACT_TYPE, max_length=4096)155 type = models.CharField(choices=ARTIFACT_TYPE, max_length=4096)
156 ticket_component = models.ForeignKey(SubTicket, blank=True, null=True)156 ticket_component = models.ForeignKey(SubTicket)
157 # 'reference' provided by the artifact manager157 # 'reference' provided by the artifact manager
158 reference = models.CharField(max_length=4096)158 reference = models.CharField(max_length=4096)
159 name = models.CharField(max_length=4096)159 name = models.CharField(max_length=4096)
160160
=== added directory 'ticket_system/ticket/tests'
=== added file 'ticket_system/ticket/tests/__init__.py'
--- ticket_system/ticket/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ ticket_system/ticket/tests/__init__.py 2013-12-16 14:22:25 +0000
@@ -0,0 +1,18 @@
1# Ubuntu Continuous Integration Engine
2# Copyright 2013 Canonical Ltd.
3
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU Affero General Public License version 3, as
6# published by the Free Software Foundation.
7
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU Affero General Public License for more details.
12
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16from test_models import *
17from test_read_api import *
18from test_full_read_api import *
019
=== added file 'ticket_system/ticket/tests/test_full_read_api.py'
--- ticket_system/ticket/tests/test_full_read_api.py 1970-01-01 00:00:00 +0000
+++ ticket_system/ticket/tests/test_full_read_api.py 2013-12-16 14:22:25 +0000
@@ -0,0 +1,126 @@
1# Ubuntu Continuous Integration Engine
2# Copyright 2013 Canonical Ltd.
3
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU Affero General Public License version 3, as
6# published by the Free Software Foundation.
7
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU Affero General Public License for more details.
12
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16from model_mommy import mommy
17from ci_utils.tastypie.test import TastypieTestCase
18
19
20class TicketReadAPITest(TastypieTestCase):
21
22 def setUp(self):
23 super(TicketReadAPITest, self).setUp('/api/v1')
24 self.person = mommy.make('Person')
25 self.ticket = mommy.make('Ticket', owner=self.person)
26 self.sourcepackage = mommy.make('SourcePackage')
27 self.spu = mommy.make('SourcePackageUpload',
28 sourcepackage=self.sourcepackage)
29 self.subticket = mommy.make('SubTicket', ticket=self.ticket,
30 assignee=self.person,
31 source_package_upload=self.spu)
32 self.artifact = mommy.make('Artifact', ticket_component=self.subticket)
33 self.maxDiff = None
34
35 def test_get_fullticket_api(self):
36 obj = self.getResource('fullticket/')
37 self.assertEqual(obj['objects'][0], {
38 u'status': self.ticket.status,
39 u'current_workflow_step': self.ticket.current_workflow_step,
40 u'description': unicode(self.ticket.description),
41 u'title': unicode(self.ticket.title),
42 u'subticket': [{
43 u'status': self.subticket.status,
44 u'current_workflow_step': self.subticket.current_workflow_step,
45 u'artifact': [{
46 u'resource_uri': u'/api/v1/fullartifact/{0}/'.format(
47 self.artifact.pk),
48 u'type': unicode(self.artifact.type),
49 u'id': self.artifact.pk,
50 u'reference': unicode(self.artifact.reference),
51 u'name': unicode(self.artifact.name)}],
52 u'assignee': {
53 u'resource_uri': u'/api/v1/person/{0}/'.format(
54 self.person.pk),
55 u'is_team': self.person.is_team,
56 u'email': unicode(self.person.email),
57 u'name': unicode(self.person.name),
58 u'id': self.person.pk},
59 u'source_package_upload': {
60 u'version': unicode(self.spu.version),
61 u'sourcepackage': {
62 u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format(
63 self.sourcepackage.pk),
64 u'id': self.sourcepackage.pk,
65 u'name': unicode(self.sourcepackage.name)},
66 u'id': self.spu.pk,
67 u'resource_uri':
68 u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
69 u'id': self.subticket.pk,
70 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
71 self.subticket.pk)}],
72 u'bug_id': self.ticket.bug_id,
73 u'owner': {
74 u'resource_uri': u'/api/v1/person/{0}/'.format(self.person.pk),
75 u'is_team': self.person.is_team,
76 u'email': unicode(self.person.email),
77 u'name': unicode(self.person.name),
78 u'id': self.person.pk},
79 u'base_image': unicode(self.ticket.base_image),
80 u'id': self.ticket.pk,
81 u'resource_uri': u'/api/v1/fullticket/{0}/'.format(self.ticket.pk),
82 })
83
84 def test_get_fullartifact_api(self):
85 obj = self.getResource('fullartifact/')
86 self.assertEqual(obj['objects'][0], {
87 u'resource_uri': u'/api/v1/fullartifact/{0}/'.format(
88 self.artifact.pk),
89 u'type': unicode(self.artifact.type),
90 u'id': self.artifact.pk,
91 u'reference': unicode(self.artifact.reference),
92 u'name': unicode(self.artifact.name),
93 })
94
95 def test_get_fullsubticket_api(self):
96 obj = self.getResource('fullsubticket/')
97 self.assertEqual(obj['objects'][0], {
98 u'status': self.subticket.status,
99 u'current_workflow_step': self.subticket.current_workflow_step,
100 u'artifact': [{
101 u'resource_uri': u'/api/v1/fullartifact/{0}/'.format(
102 self.artifact.pk),
103 u'type': unicode(self.artifact.type),
104 u'id': self.artifact.pk,
105 u'reference': unicode(self.artifact.reference),
106 u'name': unicode(self.artifact.name)}],
107 u'assignee': {
108 u'resource_uri': u'/api/v1/person/{0}/'.format(self.person.pk),
109 u'is_team': self.person.is_team,
110 u'email': unicode(self.person.email),
111 u'name': unicode(self.person.name),
112 u'id': self.person.pk},
113 u'source_package_upload': {
114 u'version': unicode(self.spu.version),
115 u'sourcepackage': {
116 u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format(
117 self.sourcepackage.pk),
118 u'id': self.sourcepackage.pk,
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'id': self.subticket.pk,
124 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
125 self.subticket.pk),
126 })
0127
=== renamed file 'ticket_system/ticket/tests.py' => 'ticket_system/ticket/tests/test_models.py'
=== added file 'ticket_system/ticket/tests/test_read_api.py'
--- ticket_system/ticket/tests/test_read_api.py 1970-01-01 00:00:00 +0000
+++ ticket_system/ticket/tests/test_read_api.py 2013-12-16 14:22:25 +0000
@@ -0,0 +1,163 @@
1# Ubuntu Continuous Integration Engine
2# Copyright 2013 Canonical Ltd.
3
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU Affero General Public License version 3, as
6# published by the Free Software Foundation.
7
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU Affero General Public License for more details.
12
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16from model_mommy import mommy
17from ci_utils.tastypie.test import TastypieTestCase
18
19
20class TicketReadAPITest(TastypieTestCase):
21
22 def setUp(self):
23 super(TicketReadAPITest, self).setUp('/api/v1')
24 self.person = mommy.make('Person')
25 self.ticket = mommy.make('Ticket', owner=self.person)
26 self.sourcepackage = mommy.make('SourcePackage')
27 self.spu = mommy.make('SourcePackageUpload',
28 sourcepackage=self.sourcepackage)
29 self.subticket = mommy.make('SubTicket', ticket=self.ticket,
30 assignee=self.person,
31 source_package_upload=self.spu)
32 self.artifact = mommy.make('Artifact', ticket_component=self.subticket)
33
34 def test_get_artifact_api(self):
35 obj = self.getResource('artifact/')
36 self.assertEqual(obj['objects'][0], {
37 u'name': unicode(self.artifact.name),
38 u'reference': unicode(self.artifact.reference),
39 u'ticket_component': {
40 u'status': self.subticket.status,
41 u'current_workflow_step': self.subticket.current_workflow_step,
42 u'assignee': {
43 u'resource_uri': u'/api/v1/person/{0}/'.format(
44 self.person.pk),
45 u'is_team': self.person.is_team,
46 u'email': unicode(self.person.email),
47 u'name': unicode(self.person.name),
48 u'id': self.person.pk},
49 u'source_package_upload': {
50 u'version': unicode(self.spu.version),
51 u'sourcepackage': {
52 u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format(
53 self.sourcepackage.pk),
54 u'id': self.sourcepackage.pk,
55 u'name': unicode(self.sourcepackage.name)},
56 u'id': self.spu.pk,
57 u'resource_uri':
58 u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
59 u'ticket': {
60 u'status': self.ticket.status,
61 u'current_workflow_step':
62 self.ticket.current_workflow_step,
63 u'description': unicode(self.ticket.description),
64 u'title': unicode(self.ticket.title),
65 u'bug_id': self.ticket.bug_id,
66 u'owner': {
67 u'resource_uri': u'/api/v1/person/{0}/'.format(
68 self.person.pk),
69 u'is_team': self.person.is_team,
70 u'email': unicode(self.person.email),
71 u'name': unicode(self.person.name),
72 u'id': self.person.pk},
73 u'base_image': unicode(self.ticket.base_image),
74 u'id': self.ticket.pk,
75 u'resource_uri': u'/api/v1/ticket/{0}/'.format(
76 self.ticket.pk)},
77 u'id': self.subticket.pk,
78 u'resource_uri': u'/api/v1/subticket/{0}/'.format(
79 self.subticket.pk)},
80 u'type': unicode(self.artifact.type),
81 u'id': self.artifact.pk,
82 u'resource_uri': u'/api/v1/artifact/{0}/'.format(self.artifact.pk),
83 })
84
85 def test_get_subticket_api(self):
86 obj = self.getResource('subticket/')
87 self.assertEqual(obj['objects'][0], {
88 u'status': self.subticket.status,
89 u'current_workflow_step': self.subticket.current_workflow_step,
90 u'assignee': {
91 u'resource_uri': u'/api/v1/person/{0}/'.format(
92 self.person.pk),
93 u'is_team': self.person.is_team,
94 u'email': unicode(self.person.email),
95 u'name': unicode(self.person.name),
96 u'id': self.person.pk},
97 u'source_package_upload': {
98 u'version': unicode(self.spu.version),
99 u'sourcepackage': {
100 u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format(
101 self.sourcepackage.pk),
102 u'id': self.sourcepackage.pk,
103 u'name': unicode(self.sourcepackage.name)},
104 u'id': self.spu.pk,
105 u'resource_uri':
106 u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk)},
107 u'ticket': {
108 u'status': self.ticket.status,
109 u'current_workflow_step':
110 self.ticket.current_workflow_step,
111 u'description': unicode(self.ticket.description),
112 u'title': unicode(self.ticket.title),
113 u'bug_id': self.ticket.bug_id,
114 u'owner': {
115 u'resource_uri': u'/api/v1/person/{0}/'.format(
116 self.person.pk),
117 u'is_team': self.person.is_team,
118 u'email': unicode(self.person.email),
119 u'name': unicode(self.person.name),
120 u'id': self.person.pk},
121 u'base_image': unicode(self.ticket.base_image),
122 u'id': self.ticket.pk,
123 u'resource_uri': u'/api/v1/ticket/{0}/'.format(
124 self.ticket.pk)},
125 u'id': self.subticket.pk,
126 u'resource_uri': u'/api/v1/subticket/{0}/'.format(
127 self.subticket.pk),
128 })
129
130 def test_get_ticket_api(self):
131 obj = self.getResource('ticket/')
132 self.assertEqual(obj['objects'][0], {
133 u'status': self.ticket.status,
134 u'current_workflow_step':
135 self.ticket.current_workflow_step,
136 u'description': unicode(self.ticket.description),
137 u'title': unicode(self.ticket.title),
138 u'bug_id': self.ticket.bug_id,
139 u'owner': {
140 u'resource_uri': u'/api/v1/person/{0}/'.format(self.person.pk),
141 u'is_team': self.person.is_team,
142 u'email': unicode(self.person.email),
143 u'name': unicode(self.person.name),
144 u'id': self.person.pk},
145 u'base_image': unicode(self.ticket.base_image),
146 u'id': self.ticket.pk,
147 u'resource_uri': u'/api/v1/ticket/{0}/'.format(
148 self.ticket.pk),
149 })
150
151 def test_get_sourcepackageupload_api(self):
152 obj = self.getResource('sourcepackageupload/')
153 self.assertEqual(obj['objects'][0], {
154 u'version': unicode(self.spu.version),
155 u'sourcepackage': {
156 u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format(
157 self.sourcepackage.pk),
158 u'id': self.sourcepackage.pk,
159 u'name': unicode(self.sourcepackage.name)},
160 u'id': self.spu.pk,
161 u'resource_uri':
162 u'/api/v1/sourcepackageupload/{0}/'.format(self.spu.pk),
163 })
0164
=== modified file 'ticket_system/ticket_system/urls.py'
--- ticket_system/ticket_system/urls.py 2013-12-09 19:35:22 +0000
+++ ticket_system/ticket_system/urls.py 2013-12-16 14:22:25 +0000
@@ -18,12 +18,22 @@
18from tastypie.api import Api18from tastypie.api import Api
19from people.api import PersonResource19from people.api import PersonResource
20from project.api import SourcePackageResource, BinaryPackageResource20from project.api import SourcePackageResource, BinaryPackageResource
21from ticket.api import (TicketResource, SubTicketResource, ArtifactResource,
22 SourcePackageUploadResource, FullSubTicketResource,
23 FullTicketResource, FullArtifactResource)
2124
22admin.autodiscover()25admin.autodiscover()
23v1_api = Api(api_name='v1')26v1_api = Api(api_name='v1')
24v1_api.register(PersonResource())27v1_api.register(PersonResource())
25v1_api.register(SourcePackageResource())28v1_api.register(SourcePackageResource())
26v1_api.register(BinaryPackageResource())29v1_api.register(BinaryPackageResource())
30v1_api.register(TicketResource())
31v1_api.register(SubTicketResource())
32v1_api.register(SourcePackageUploadResource())
33v1_api.register(ArtifactResource())
34v1_api.register(FullArtifactResource())
35v1_api.register(FullSubTicketResource())
36v1_api.register(FullTicketResource())
2737
28urlpatterns = patterns(38urlpatterns = patterns(
29 '',39 '',

Subscribers

People subscribed via source and target branches