Skip to content

Commit db26b4e

Browse files
committed
Merge REST API changes
2 parents 7a5587c + a911acd commit db26b4e

File tree

13 files changed

+1799
-11
lines changed

13 files changed

+1799
-11
lines changed

CHANGES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ Features:
3131
- Support for Python 3 (3.4 and later). See doc/upgrading.txt for
3232
details of what is required to move an existing tracker from Python
3333
2 to Python 3 (Joseph Myers, Christof Meerwald)
34+
- Merge the Google Summer of Code Project of 2015, the implementation of
35+
a REST-API for Roundup. This was implemented by Chau Nguyen under the
36+
supervision of Ezio Melotti. Some additions were made, most notably we
37+
never destroy an object in the database but retire them with the
38+
DELETE method. We also don't allow to DELETE a whole class. Python3
39+
support was also fixed and we have cherry-picked two patches from the
40+
bugs.python.org branch in the files affected by the REST-API changes.
3441

3542
Fixed:
3643

doc/acknowledgements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ John Mitchell,
204204
Ramiro Morales,
205205
Toni Mueller,
206206
Joseph Myers,
207+
Chau Nguyen,
207208
Stefan Niederhauser,
208209
Truls E. Næss,
209210
Bryce L Nordgren,

roundup/actions.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Copyright (C) 2009 Stefan Seefeld
33
# All rights reserved.
44
# For license terms see the file COPYING.txt.
5+
# Actions used in REST and XMLRPC APIs
56
#
67

78
from roundup.exceptions import Unauthorised
@@ -40,7 +41,19 @@ def gettext(self, msgid):
4041
_ = gettext
4142

4243

43-
class Retire(Action):
44+
class PermCheck(Action):
45+
def permission(self, designator):
46+
47+
classname, itemid = hyperdb.splitDesignator(designator)
48+
perm = self.db.security.hasPermission
49+
50+
if not perm('Retire', self.db.getuid(), classname=classname
51+
, itemid=itemid):
52+
raise Unauthorised(self._('You do not have permission to retire '
53+
'or restore the %(classname)s class.')
54+
%locals())
55+
56+
class Retire(PermCheck):
4457

4558
def handle(self, designator):
4659

@@ -57,12 +70,13 @@ def handle(self, designator):
5770
self.db.commit()
5871

5972

60-
def permission(self, designator):
73+
class Restore(PermCheck):
74+
75+
def handle(self, designator):
6176

6277
classname, itemid = hyperdb.splitDesignator(designator)
6378

64-
if not self.db.security.hasPermission('Edit', self.db.getuid(),
65-
classname=classname, itemid=itemid):
66-
raise Unauthorised(self._('You do not have permission to '
67-
'retire the %(classname)s class.')%classname)
68-
79+
# do the restore
80+
self.db.getclass(classname).restore(itemid)
81+
self.db.commit()
82+

roundup/cgi/client.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class SysCallError(Exception):
3535
from roundup.mailer import Mailer, MessageSendError
3636
from roundup.cgi import accept_language
3737
from roundup import xmlrpc
38+
from roundup import rest
3839

3940
from roundup.anypy.cookie_ import CookieError, BaseCookie, SimpleCookie, \
4041
get_cookie_date
@@ -363,6 +364,10 @@ def __init__(self, instance, request, env, form=None, translator=None):
363364
# see if we need to re-parse the environment for the form (eg Zope)
364365
if form is None:
365366
self.form = cgi.FieldStorage(fp=request.rfile, environ=env)
367+
# In some case (e.g. content-type application/xml), cgi
368+
# will not parse anything. Fake a list property in this case
369+
if self.form.list is None:
370+
self.form.list = []
366371
else:
367372
self.form = form
368373

@@ -421,9 +426,14 @@ def setTranslator(self, translator=None):
421426
def main(self):
422427
""" Wrap the real main in a try/finally so we always close off the db.
423428
"""
429+
xmlrpc_enabled = self.instance.config.WEB_ENABLE_XMLRPC
430+
rest_enabled = self.instance.config.WEB_ENABLE_REST
424431
try:
425-
if self.path == 'xmlrpc':
432+
if xmlrpc_enabled and self.path == 'xmlrpc':
426433
self.handle_xmlrpc()
434+
elif rest_enabled and (self.path == 'rest' or
435+
self.path[:5] == 'rest/'):
436+
self.handle_rest()
427437
else:
428438
self.inner_main()
429439
finally:
@@ -480,6 +490,24 @@ def handle_xmlrpc(self):
480490
self.setHeader("Content-Length", str(len(output)))
481491
self.write(output)
482492

493+
def handle_rest(self):
494+
# Set the charset and language
495+
self.determine_charset()
496+
self.determine_language()
497+
# Open the database as the correct user.
498+
# TODO: add everything to RestfulDispatcher
499+
self.determine_user()
500+
self.check_anonymous_access()
501+
502+
# Call rest library to handle the request
503+
handler = rest.RestfulInstance(self, self.db)
504+
output = handler.dispatch(self.env['REQUEST_METHOD'], self.path,
505+
self.form)
506+
507+
# self.setHeader("Content-Type", "text/xml")
508+
self.setHeader("Content-Length", str(len(output)))
509+
self.write(output)
510+
483511
def add_ok_message(self, msg, escape=True):
484512
add_message(self._ok_message, msg, escape)
485513

@@ -1347,6 +1375,10 @@ def determine_context(self, dre=re.compile(r'([^\d]+)0*(\d+)')):
13471375
klass = self.db.getclass(self.classname)
13481376
except KeyError:
13491377
raise NotFound('%s/%s'%(self.classname, self.nodeid))
1378+
if int(self.nodeid) > 2**31:
1379+
# Postgres will complain with a ProgrammingError
1380+
# if we try to pass in numbers that are too large
1381+
raise NotFound('%s/%s'%(self.classname, self.nodeid))
13501382
if not klass.hasnode(self.nodeid):
13511383
raise NotFound('%s/%s'%(self.classname, self.nodeid))
13521384
# with a designator, we default to item view

roundup/configuration.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,18 @@ def str2value(self, value):
727727
are logged into roundup and open a roundup link
728728
from a source other than roundup (e.g. link in
729729
email)."""),
730+
(BooleanOption, 'enable_xmlrpc', "yes",
731+
"""Whether to enable the XMLRPC API in the roundup web
732+
interface. By default the XMLRPC endpoint is the string 'xmlrpc'
733+
after the roundup web url configured in the 'tracker' section.
734+
If this variable is set to 'no', the xmlrpc path has no special meaning
735+
and will yield an error message."""),
736+
(BooleanOption, 'enable_rest', "yes",
737+
"""Whether to enable the REST API in the roundup web
738+
interface. By default the REST endpoint is the string 'rest' plus any
739+
additional REST-API parameters after the roundup web url configured in
740+
the tracker section. If this variable is set to 'no', the rest path has
741+
no special meaning and will yield an error message."""),
730742
(CsrfSettingOption, 'csrf_enforce_token', "yes",
731743
"""How do we deal with @csrf fields in posted forms.
732744
Set this to 'required' to block the post and notify

0 commit comments

Comments
 (0)