From 39cc2ffc8f131679d9e561c0ab3712f482a8e7ed Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Mon, 26 Nov 2012 01:27:37 +0100 Subject: [PATCH] The tg1 adapter can now expose rest functions outside the WSRoot _and_ enable other other protocols. Soap is tested. --- tests/test_tg1.py | 52 +++++++++++++++++++++++++++--- tox.ini | 2 ++ wsme/tg1.py | 81 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 119 insertions(+), 16 deletions(-) diff --git a/tests/test_tg1.py b/tests/test_tg1.py index 093ab61..f9b0a4c 100644 --- a/tests/test_tg1.py +++ b/tests/test_tg1.py @@ -9,13 +9,21 @@ import unittest import simplejson +from wsmeext.soap.tests import test_soap + + class WSController(WSRoot): pass class Root(RootController): - ws = wsme.tg1.adapt( - WSController(webpath='/ws', protocols=['restjson'])) + ws = WSController(webpath='/ws') + ws.addprotocol( + 'soap', + tns=test_soap.tns, + typenamespace=test_soap.typenamespace + ) + ws = wsme.tg1.adapt(ws) @wsexpose(int) @wsvalidate(int, int) @@ -50,9 +58,45 @@ class TestController(unittest.TestCase): startup.stopTurboGears() config.update({"server_started": False}) - def test_simplecall(self): + def test_restcall(self): response = self.app.post("/multiply", simplejson.dumps({'a': 5, 'b': 10}), - {'Content-Type': 'application/json'}) + {'Content-Type': 'application/json'} + ) print response assert simplejson.loads(response.body) == 50 + + response = self.app.post("/multiply", + simplejson.dumps({'a': 5, 'b': 10}), + {'Content-Type': 'application/json', 'Accept': 'application/json'} + ) + print response + assert simplejson.loads(response.body) == 50 + + response = self.app.post("/multiply", + simplejson.dumps({'a': 5, 'b': 10}), + {'Content-Type': 'application/json', 'Accept': 'text/javascript'} + ) + print response + assert simplejson.loads(response.body) == 50 + + response = self.app.post("/multiply", + simplejson.dumps({'a': 5, 'b': 10}), + {'Content-Type': 'application/json', + 'Accept': 'text/xml'} + ) + print response + assert response.body == "50" + + def test_soap_wsdl(self): + wsdl = self.app.get('/ws/api.wsdl').body + print wsdl + assert 'multiply' in wsdl + + def test_soap_call(self): + ts = test_soap.TestSOAP('test_wsdl') + ts.app = self.app + ts.ws_path = '/ws' + + print ts.ws_path + assert ts.call('multiply', a=5, b=10, _rt=int) == 50 diff --git a/tox.ini b/tox.ini index 25736ee..3a4f2ff 100644 --- a/tox.ini +++ b/tox.ini @@ -48,11 +48,13 @@ commands= [testenv:tg11] basepython=python2.5 deps= + d2to1 nose webtest coverage simplejson commands= + {envbindir}/easy_install https://bitbucket.org/cdevienne/wsme-soap/get/tip.zip {envbindir}/easy_install -i http://www.turbogears.org/1.1/downloads/current/index/ 'TurboGears<1.1.99' {envbindir}/coverage run -p {envbindir}/nosetests tests/test_tg1.py --verbose {posargs} diff --git a/wsme/tg1.py b/wsme/tg1.py index 1a6ff8a..b59e6ca 100644 --- a/wsme/tg1.py +++ b/wsme/tg1.py @@ -13,25 +13,30 @@ from turbogears.startup import call_on_startup, call_on_shutdown from wsme.rest import validate as wsvalidate import wsme.api -import wsme.protocols.restjson +import wsme.rest.args +import wsme.rest.json + +import inspect + +APIPATH_MAXLEN = 50 __all__ = ['adapt', 'wsexpose', 'wsvalidate'] def wsexpose(*args, **kwargs): tg_json_expose = expose( - 'wsmejson', + 'wsmejson:', accept_format='application/json', content_type='application/json', tg_format='json' ) tg_altjson_expose = expose( - 'wsmejson', + 'wsmejson:', accept_format='text/javascript', content_type='application/json' ) tg_xml_expose = expose( - 'wsmxml', + 'wsmexml:', accept_format='text/xml', content_type='text/xml', tg_format='xml' @@ -44,8 +49,7 @@ def wsexpose(*args, **kwargs): @functools.wraps(f) def callfunction(self, *args, **kwargs): - print args, kwargs, cherrypy.request.body - args, kwargs = wsme.protocols.commons.get_args( + args, kwargs = wsme.rest.args.get_args( funcdef, args, kwargs, cherrypy.request.body, cherrypy.request.headers['Content-Type'] @@ -56,9 +60,10 @@ def wsexpose(*args, **kwargs): result=result ) - callfunction = tg_json_expose(callfunction) - callfunction = tg_altjson_expose(callfunction) callfunction = tg_xml_expose(callfunction) + callfunction = tg_altjson_expose(callfunction) + callfunction = tg_json_expose(callfunction) + callfunction._wsme_original_function = f return callfunction return decorate @@ -74,16 +79,38 @@ class AutoJSONTemplate(object): def render(self, info, format="json", fragment=False, template=None): "Renders the template to a string using the provided info." - data = wsme.protocols.restjson.tojson( - info['datatype'], - info['result'] + return wsme.rest.json.encode_result( + info['result'], info['datatype'] ) - return json.dumps(data) def get_content_type(self, user_agent): return "application/json" +class AutoXMLTemplate(object): + def __init__(self, extra_vars_func=None, options=None): + pass + + def load_template(self, templatename): + "There are no actual templates with this engine" + pass + + def render(self, info, format="json", fragment=False, template=None): + "Renders the template to a string using the provided info." + return wsme.rest.xml.encode_result( + info['result'], info['datatype'] + ) + + def get_content_type(self, user_agent): + return "text/xml" + + +import turbogears.view + +turbogears.view.engines['wsmejson'] = AutoJSONTemplate(turbogears.view.stdvars) +turbogears.view.engines['wsmexml'] = AutoXMLTemplate(turbogears.view.stdvars) + + class WSMECherrypyFilter(BaseFilter): def __init__(self, controller): self.controller = controller @@ -109,6 +136,7 @@ class Controller(object): def adapt(wsroot): + wsroot._scan_api = scan_api controller = Controller(wsroot) filter_ = WSMECherrypyFilter(controller) @@ -126,3 +154,32 @@ def adapt(wsroot): call_on_startup.append(install_filter) call_on_shutdown.insert(0, uninstall_filter) return controller + + +import wsme.rest + + +def _scan_api(controller, path=[], objects=[]): + """ + Recursively iterate a controller api entries. + """ + for name in dir(controller): + if name.startswith('_'): + continue + a = getattr(controller, name) + if a in objects: + continue + if inspect.ismethod(a): + if wsme.api.iswsmefunction(a): + yield path + [name], a._wsme_original_function, [controller] + elif inspect.isclass(a): + continue + else: + if len(path) > APIPATH_MAXLEN: + raise ValueError("Path is too long: " + str(path)) + for i in _scan_api(a, path + [name], objects + [a]): + yield i + + +def scan_api(root=None): + return _scan_api(cherrypy.root)