summaryrefslogtreecommitdiff
path: root/wsmeext/extdirect/protocol.py
diff options
context:
space:
mode:
Diffstat (limited to 'wsmeext/extdirect/protocol.py')
-rw-r--r--wsmeext/extdirect/protocol.py450
1 files changed, 0 insertions, 450 deletions
diff --git a/wsmeext/extdirect/protocol.py b/wsmeext/extdirect/protocol.py
deleted file mode 100644
index 23793b4..0000000
--- a/wsmeext/extdirect/protocol.py
+++ /dev/null
@@ -1,450 +0,0 @@
-import datetime
-import decimal
-
-from simplegeneric import generic
-
-from wsme.exc import ClientSideError
-from wsme.protocol import CallContext, Protocol, expose
-from wsme.utils import parse_isodate, parse_isodatetime, parse_isotime
-from wsme.rest.args import from_params
-from wsme.types import iscomplex, isusertype, list_attributes, Unset
-import wsme.types
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-from six import u
-
-
-class APIDefinitionGenerator(object):
- tpl = """\
-Ext.ns("%(rootns)s");
-
-if (!%(rootns)s.wsroot) {
- %(rootns)s.wsroot = "%(webpath)s.
-}
-
-%(descriptors)s
-
-Ext.syncRequire(['Ext.direct.*'], function() {
- %(providers)s
-});
-"""
- descriptor_tpl = """\
-Ext.ns("%(fullns)s");
-
-%(fullns)s.Descriptor = {
- "url": %(rootns)s.wsroot + "extdirect/router/%(ns)s",
- "namespace": "%(fullns)s",
- "type": "remoting",
- "actions": %(actions)s
- "enableBuffer": true
-};
-"""
- provider_tpl = """\
- Ext.direct.Manager.addProvider(%(fullns)s.Descriptor);
-"""
-
- def __init__(self):
- pass
-
- def render(self, rootns, webpath, namespaces, fullns):
- descriptors = u('')
- for ns in sorted(namespaces):
- descriptors += self.descriptor_tpl % {
- 'ns': ns,
- 'rootns': rootns,
- 'fullns': fullns(ns),
- 'actions': '\n'.join((
- ' ' * 4 + line
- for line
- in json.dumps(namespaces[ns], indent=4).split('\n')
- ))
- }
-
- providers = u('')
- for ns in sorted(namespaces):
- providers += self.provider_tpl % {
- 'fullns': fullns(ns)
- }
-
- r = self.tpl % {
- 'rootns': rootns,
- 'webpath': webpath,
- 'descriptors': descriptors,
- 'providers': providers,
- }
- return r
-
-
-@generic
-def fromjson(datatype, value):
- if value is None:
- return None
- if iscomplex(datatype):
- newvalue = datatype()
- for attrdef in list_attributes(datatype):
- if attrdef.name in value:
- setattr(newvalue, attrdef.key,
- fromjson(attrdef.datatype, value[attrdef.name]))
- value = newvalue
- elif isusertype(datatype):
- value = datatype.frombasetype(fromjson(datatype.basetype, value))
- return value
-
-
-@generic
-def tojson(datatype, value):
- if value is None:
- return value
- if iscomplex(datatype):
- d = {}
- for attrdef in list_attributes(datatype):
- attrvalue = getattr(value, attrdef.key)
- if attrvalue is not Unset:
- d[attrdef.name] = tojson(attrdef.datatype, attrvalue)
- value = d
- elif isusertype(datatype):
- value = tojson(datatype.basetype, datatype.tobasetype(value))
- return value
-
-
-@fromjson.when_type(wsme.types.ArrayType)
-def array_fromjson(datatype, value):
- return [fromjson(datatype.item_type, item) for item in value]
-
-
-@tojson.when_type(wsme.types.ArrayType)
-def array_tojson(datatype, value):
- if value is None:
- return value
- return [tojson(datatype.item_type, item) for item in value]
-
-
-@fromjson.when_type(wsme.types.DictType)
-def dict_fromjson(datatype, value):
- if value is None:
- return value
- return dict((
- (fromjson(datatype.key_type, key),
- fromjson(datatype.value_type, value))
- for key, value in value.items()
- ))
-
-
-@tojson.when_type(wsme.types.DictType)
-def dict_tojson(datatype, value):
- if value is None:
- return value
- return dict((
- (tojson(datatype.key_type, key),
- tojson(datatype.value_type, value))
- for key, value in value.items()
- ))
-
-
-@tojson.when_object(wsme.types.bytes)
-def bytes_tojson(datatype, value):
- if value is None:
- return value
- return value.decode('ascii')
-
-
-# raw strings
-@fromjson.when_object(wsme.types.bytes)
-def bytes_fromjson(datatype, value):
- if value is not None:
- value = value.encode('ascii')
- return value
-
-
-# unicode strings
-
-@fromjson.when_object(wsme.types.text)
-def text_fromjson(datatype, value):
- if isinstance(value, wsme.types.bytes):
- return value.decode('utf-8')
- return value
-
-
-# datetime.time
-
-@fromjson.when_object(datetime.time)
-def time_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isotime(value)
-
-
-@tojson.when_object(datetime.time)
-def time_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# datetime.date
-
-@fromjson.when_object(datetime.date)
-def date_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isodate(value)
-
-
-@tojson.when_object(datetime.date)
-def date_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# datetime.datetime
-
-@fromjson.when_object(datetime.datetime)
-def datetime_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isodatetime(value)
-
-
-@tojson.when_object(datetime.datetime)
-def datetime_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# decimal.Decimal
-
-@fromjson.when_object(decimal.Decimal)
-def decimal_fromjson(datatype, value):
- if value is None:
- return value
- return decimal.Decimal(value)
-
-
-@tojson.when_object(decimal.Decimal)
-def decimal_tojson(datatype, value):
- if value is None:
- return value
- return str(value)
-
-
-class ExtCallContext(CallContext):
- def __init__(self, request, namespace, calldata):
- super(ExtCallContext, self).__init__(request)
- self.namespace = namespace
-
- self.tid = calldata['tid']
- self.action = calldata['action']
- self.method = calldata['method']
- self.params = calldata['data']
-
-
-class FormExtCallContext(CallContext):
- def __init__(self, request, namespace):
- super(FormExtCallContext, self).__init__(request)
- self.namespace = namespace
-
- self.tid = request.params['extTID']
- self.action = request.params['extAction']
- self.method = request.params['extMethod']
- self.params = []
-
-
-class ExtDirectProtocol(Protocol):
- """
- ExtDirect protocol.
-
- For more detail on the protocol, see
- http://www.sencha.com/products/extjs/extdirect.
-
- .. autoattribute:: name
- .. autoattribute:: content_types
- """
- name = 'extdirect'
- displayname = 'ExtDirect'
- content_types = ['application/json', 'text/javascript']
-
- def __init__(self, namespace='', params_notation='named', nsfolder=None):
- self.namespace = namespace
- self.appns, self.apins = namespace.rsplit('.', 2) \
- if '.' in namespace else (namespace, '')
- self.default_params_notation = params_notation
- self.appnsfolder = nsfolder
-
- @property
- def api_alias(self):
- if self.appnsfolder:
- alias = '/%s/%s.js' % (
- self.appnsfolder,
- self.apins.replace('.', '/'))
- return alias
-
- def accept(self, req):
- path = req.path
- assert path.startswith(self.root._webpath)
- path = path[len(self.root._webpath):]
-
- return (
- path == self.api_alias or
- path == "/extdirect/api" or
- path.startswith("/extdirect/router")
- )
-
- def iter_calls(self, req):
- path = req.path
-
- assert path.startswith(self.root._webpath)
- path = path[len(self.root._webpath):].strip()
-
- assert path.startswith('/extdirect/router'), path
- path = path[17:].strip('/')
-
- if path:
- namespace = path.split('.')
- else:
- namespace = []
-
- if 'extType' in req.params:
- req.wsme_extdirect_batchcall = False
- yield FormExtCallContext(req, namespace)
- else:
- data = json.loads(req.body.decode('utf8'))
- req.wsme_extdirect_batchcall = isinstance(data, list)
- if not req.wsme_extdirect_batchcall:
- data = [data]
- req.callcount = len(data)
-
- for call in data:
- yield ExtCallContext(req, namespace, call)
-
- def extract_path(self, context):
- path = list(context.namespace)
-
- if context.action:
- path.append(context.action)
-
- path.append(context.method)
-
- return path
-
- def read_std_arguments(self, context):
- funcdef = context.funcdef
- notation = funcdef.extra_options.get('extdirect_params_notation',
- self.default_params_notation)
- args = context.params
- if notation == 'positional':
- kw = dict(
- (argdef.name, fromjson(argdef.datatype, arg))
- for argdef, arg in zip(funcdef.arguments, args)
- )
- elif notation == 'named':
- if len(args) == 0:
- args = [{}]
- elif len(args) > 1:
- raise ClientSideError(
- "Named arguments: takes a single object argument")
- args = args[0]
- kw = dict(
- (argdef.name, fromjson(argdef.datatype, args[argdef.name]))
- for argdef in funcdef.arguments if argdef.name in args
- )
- else:
- raise ValueError("Invalid notation: %s" % notation)
- return kw
-
- def read_form_arguments(self, context):
- kw = {}
- for argdef in context.funcdef.arguments:
- value = from_params(argdef.datatype, context.request.params,
- argdef.name, set())
- if value is not Unset:
- kw[argdef.name] = value
- return kw
-
- def read_arguments(self, context):
- if isinstance(context, ExtCallContext):
- kwargs = self.read_std_arguments(context)
- elif isinstance(context, FormExtCallContext):
- kwargs = self.read_form_arguments(context)
- wsme.runtime.check_arguments(context.funcdef, (), kwargs)
- return kwargs
-
- def encode_result(self, context, result):
- return json.dumps({
- 'type': 'rpc',
- 'tid': context.tid,
- 'action': context.action,
- 'method': context.method,
- 'result': tojson(context.funcdef.return_type, result)
- })
-
- def encode_error(self, context, infos):
- return json.dumps({
- 'type': 'exception',
- 'tid': context.tid,
- 'action': context.action,
- 'method': context.method,
- 'message': '%(faultcode)s: %(faultstring)s' % infos,
- 'where': infos['debuginfo']})
-
- def prepare_response_body(self, request, results):
- r = ",\n".join(results)
- if request.wsme_extdirect_batchcall:
- return "[\n%s\n]" % r
- else:
- return r
-
- def get_response_status(self, request):
- return 200
-
- def get_response_contenttype(self, request):
- return "text/javascript"
-
- def fullns(self, ns):
- return ns and '%s.%s' % (self.namespace, ns) or self.namespace
-
- @expose('/extdirect/api', "text/javascript")
- @expose('${api_alias}', "text/javascript")
- def api(self):
- namespaces = {}
- for path, funcdef in self.root.getapi():
- if len(path) > 1:
- namespace = '.'.join(path[:-2])
- action = path[-2]
- else:
- namespace = ''
- action = ''
- if namespace not in namespaces:
- namespaces[namespace] = {}
- if action not in namespaces[namespace]:
- namespaces[namespace][action] = []
- notation = funcdef.extra_options.get('extdirect_params_notation',
- self.default_params_notation)
- method = {
- 'name': funcdef.name}
-
- if funcdef.extra_options.get('extdirect_formhandler', False):
- method['formHandler'] = True
- method['len'] = 1 if notation == 'named' \
- else len(funcdef.arguments)
- namespaces[namespace][action].append(method)
- webpath = self.root._webpath
- if webpath and not webpath.endswith('/'):
- webpath += '/'
- return APIDefinitionGenerator().render(
- namespaces=namespaces,
- webpath=webpath,
- rootns=self.namespace,
- fullns=self.fullns,
- )
-
- def encode_sample_value(self, datatype, value, format=False):
- r = tojson(datatype, value)
- content = json.dumps(r, ensure_ascii=False, indent=4 if format else 0,
- sort_keys=format)
- return ('javascript', content)