Rest protocols now make use of the http method to select the function is needed

This commit is contained in:
Christophe de Vienne 2012-07-11 17:14:14 +02:00
parent 3aa41ec7b1
commit 9d7b2895a5
4 changed files with 133 additions and 12 deletions

View File

@ -14,6 +14,8 @@ Changes
* Tests code coverage is now over 95%.
* RESTful protocol can now use the http method.
0.3 (2012-04-20)
----------------

View File

@ -23,11 +23,14 @@ following paths :
- ``/ws/persons/update``
- ``/ws/persons/destroy``
In a near future, an additional expose option `restverb` will allow
to use the HTTP verb to select the function, in which case the path
will not containt the function name.
In addition to this trivial function mapping, a `method` option can
be given to the `expose` decorator. In such a case, the function
name can be omitted by the caller, and the dispatch will look at the
http method used in the request to select the correct function.
The function parameters can be transmitted in two ways :
The function parameters can be transmitted in two ways (is using
the http method to select the function, one way or the other
may be usable) :
#. As a GET query string or POST form parameters.

View File

@ -24,6 +24,22 @@ class RestProtocol(Protocol):
if path[-1].endswith('.' + self.dataformat):
path[-1] = path[-1][:-len(self.dataformat) - 1]
# Check if the path is actually a function, and if not
# see if the http method make a difference
# TODO Re-think the function lookup phases. Here we are
# doing the job that will be done in a later phase, which
# is sub-optimal
for p, fdef in self.root.getapi():
if p == path:
return path
# No function at this path. Now check for function that have
# this path as a prefix, and declared an http method
for p, fdef in self.root.getapi():
if len(p) == len(path) + 1 and p[:len(path)] == path and \
fdef.extra_options.get('method') == context.request.method:
return p
return path
def read_arguments(self, context):
@ -35,7 +51,7 @@ class RestProtocol(Protocol):
request.headers['Content-Type']:
# The params were read from the body, ignoring the body then
pass
elif len(request.params) and request.body:
elif len(request.params) and request.content_length:
log.warning("The request has both a body and params.")
log.debug("Params: %s" % request.params)
log.debug("Body: %s" % request.body)

View File

@ -14,6 +14,7 @@ import wsme.protocols.restjson
from wsme.protocols.restjson import fromjson, tojson
from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
from wsme.types import isusertype, register_type
from wsme.api import expose, validate
import six
@ -79,6 +80,52 @@ def prepare_result(value, datatype):
return value
class Obj(wsme.types.Base):
id = int
name = wsme.types.text
class CRUDResult(object):
data = Obj
message = wsme.types.text
def __init__(self, data=wsme.types.Unset, message=wsme.types.Unset):
self.data = data
self.message = message
class MiniCrud(object):
@expose(CRUDResult, method='PUT')
@validate(Obj)
def create(self, data):
print(repr(data))
return CRUDResult(data, u('create'))
@expose(CRUDResult, method='GET')
@validate(Obj)
def read(self, ref):
print(repr(ref))
if ref.id == 1:
ref.name = u('test')
return CRUDResult(ref, u('read'))
@expose(CRUDResult, method='POST')
@validate(Obj)
def update(self, data):
print(repr(data))
return CRUDResult(data, u('update'))
@expose(CRUDResult, method='DELETE')
@validate(Obj)
def delete(self, ref):
print(repr(ref))
if ref.id == 1:
ref.name = u('test')
return CRUDResult(ref, u('delete'))
wsme.tests.protocol.WSTestRoot.crud = MiniCrud()
class TestRestJson(wsme.tests.protocol.ProtocolTestCase):
protocol = 'restjson'
@ -256,13 +303,6 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase):
assert r[1] == '''{
"a": 2
}''', r[1]
def test_encode_sample_result(self):
r = self.root.protocols[0].encode_sample_result(
int, 2, True
)
assert r[0] == 'javascript', r[0]
assert r[1] == '''2'''
def test_encode_sample_result(self):
r = self.root.protocols[0].encode_sample_result(
@ -278,3 +318,63 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase):
assert r[1] == '''{
"result": 2
}'''
def test_PUT(self):
data = {"id": 1, "name": u("test")}
content = json.dumps(dict(data=data))
headers = {
'Content-Type': 'application/json',
}
res = self.app.put(
'/crud',
content,
headers=headers,
expect_errors=False)
print("Received:", res.body)
result = json.loads(res.text)
print(result)
assert result['data']['id'] == 1
assert result['data']['name'] == u("test")
assert result['message'] == "create"
def test_GET(self):
headers = {
'Content-Type': 'application/json',
}
res = self.app.get(
'/crud?ref.id=1',
headers=headers,
expect_errors=False)
print("Received:", res.body)
result = json.loads(res.text)
print(result)
assert result['data']['id'] == 1
assert result['data']['name'] == u("test")
assert result['message'] == "read"
def test_POST(self):
headers = {
'Content-Type': 'application/json',
}
res = self.app.post(
'/crud',
json.dumps(dict(data=dict(id=1, name=u('test')))),
headers=headers,
expect_errors=False)
print("Received:", res.body)
result = json.loads(res.text)
print(result)
assert result['data']['id'] == 1
assert result['data']['name'] == u("test")
assert result['message'] == "update"
def test_DELETE(self):
res = self.app.delete(
'/crud.json?ref.id=1',
expect_errors=False)
print("Received:", res.body)
result = json.loads(res.text)
print(result)
assert result['data']['id'] == 1
assert result['data']['name'] == u("test")
assert result['message'] == "delete"