Files
tryton/trytond/protocols/jsonrpc.py
T

192 lines
5.6 KiB
Python
Raw Normal View History

2014-12-02 12:54:01 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2010-01-20 10:33:44 +01:00
import datetime
from decimal import Decimal
2016-11-05 19:54:12 +01:00
import json
2011-09-17 14:43:05 +02:00
import base64
2016-03-15 16:57:26 +01:00
from werkzeug.wrappers import Response
from werkzeug.exceptions import (
BadRequest, InternalServerError, Conflict, Forbidden, Locked,
TooManyRequests)
2016-03-15 16:57:26 +01:00
from trytond.protocols.wrappers import Request
from trytond.exceptions import (
TrytonException, UserWarning, LoginException, ConcurrencyException,
RateLimitException, MissingDependenciesException)
2020-06-17 00:33:30 +02:00
from trytond.tools import cached_property
2010-01-20 10:33:44 +01:00
class JSONDecoder(object):
decoders = {}
@classmethod
def register(cls, klass, decoder):
assert klass not in cls.decoders
cls.decoders[klass] = decoder
def __call__(self, dct):
if dct.get('__class__') in self.decoders:
return self.decoders[dct['__class__']](dct)
return dct
2020-03-01 12:33:54 +01:00
JSONDecoder.register('datetime',
lambda dct: datetime.datetime(dct['year'], dct['month'], dct['day'],
dct['hour'], dct['minute'], dct['second'], dct['microsecond']))
JSONDecoder.register('date',
lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
JSONDecoder.register('time',
lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
dct['microsecond']))
2015-02-09 22:58:56 +01:00
JSONDecoder.register('timedelta',
lambda dct: datetime.timedelta(seconds=dct['seconds']))
2015-03-16 19:28:34 +01:00
def _bytes_decoder(dct):
cast = bytearray if bytes == str else bytes
2018-06-26 19:11:19 +02:00
return cast(base64.decodebytes(dct['base64'].encode('utf-8')))
2020-03-01 12:33:54 +01:00
2015-03-16 19:28:34 +01:00
JSONDecoder.register('bytes', _bytes_decoder)
JSONDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
2010-01-20 10:33:44 +01:00
2012-04-01 23:08:15 +02:00
2010-01-20 10:33:44 +01:00
class JSONEncoder(json.JSONEncoder):
serializers = {}
@classmethod
def register(cls, klass, encoder):
assert klass not in cls.serializers
cls.serializers[klass] = encoder
2010-01-20 10:33:44 +01:00
def default(self, obj):
marshaller = self.serializers.get(type(obj),
super(JSONEncoder, self).default)
return marshaller(obj)
2020-03-01 12:33:54 +01:00
JSONEncoder.register(datetime.datetime,
lambda o: {
'__class__': 'datetime',
'year': o.year,
'month': o.month,
'day': o.day,
'hour': o.hour,
'minute': o.minute,
'second': o.second,
'microsecond': o.microsecond,
})
JSONEncoder.register(datetime.date,
lambda o: {
'__class__': 'date',
'year': o.year,
'month': o.month,
'day': o.day,
})
JSONEncoder.register(datetime.time,
lambda o: {
'__class__': 'time',
'hour': o.hour,
'minute': o.minute,
'second': o.second,
'microsecond': o.microsecond,
})
2015-02-09 22:58:56 +01:00
JSONEncoder.register(datetime.timedelta,
lambda o: {
'__class__': 'timedelta',
'seconds': o.total_seconds(),
})
2020-03-01 12:33:54 +01:00
def _bytes_encoder(o):
return {
'__class__': 'bytes',
'base64': base64.encodebytes(o).decode('utf-8'),
}
2015-03-16 19:28:34 +01:00
JSONEncoder.register(bytes, _bytes_encoder)
JSONEncoder.register(bytearray, _bytes_encoder)
JSONEncoder.register(Decimal,
lambda o: {
'__class__': 'Decimal',
'decimal': str(o),
})
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
class JSONRequest(Request):
parsed_content_type = 'json'
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
def parsed_data(self):
if self.parsed_content_type in self.environ.get('CONTENT_TYPE', ''):
try:
2016-05-10 10:19:22 +02:00
return json.loads(
self.decoded_data.decode(
self.charset, self.encoding_errors),
object_hook=JSONDecoder())
2016-03-15 16:57:26 +01:00
except Exception:
raise BadRequest('Unable to read JSON request')
else:
raise BadRequest('Not a JSON request')
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
2016-09-27 11:56:19 +02:00
def rpc_method(self):
2021-03-27 19:42:53 +01:00
try:
return self.parsed_data['method']
except Exception:
pass
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
2016-09-27 11:56:19 +02:00
def rpc_params(self):
2021-03-27 19:42:53 +01:00
try:
return self.parsed_data['params']
except Exception:
pass
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
class JSONProtocol:
content_type = 'json'
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@classmethod
def request(cls, environ):
return JSONRequest(environ)
@classmethod
def response(cls, data, request):
2016-09-27 11:56:19 +02:00
try:
parsed_data = request.parsed_data
except BadRequest:
parsed_data = {}
if (isinstance(request, JSONRequest)
and set(parsed_data.keys()) == {'id', 'method', 'params'}):
response = {'id': parsed_data.get('id', 0)}
if isinstance(data, TrytonException):
response['error'] = data.args
elif isinstance(data, Exception):
# report exception back to server
response['error'] = (str(data), data.__format_traceback__)
else:
response['result'] = data
2016-03-15 16:57:26 +01:00
else:
if isinstance(data, UserWarning):
return Conflict(data)
elif isinstance(data, LoginException):
return Forbidden(data)
elif isinstance(data, ConcurrencyException):
return Locked(data)
elif isinstance(data, RateLimitException):
return TooManyRequests(data)
elif isinstance(data, MissingDependenciesException):
return InternalServerError(data)
elif isinstance(data, TrytonException):
return BadRequest(data)
elif isinstance(data, Exception):
return InternalServerError(data)
2016-09-27 11:56:19 +02:00
response = data
2017-03-23 22:51:36 +01:00
return Response(json.dumps(
response, cls=JSONEncoder, separators=(',', ':')),
2016-03-15 16:57:26 +01:00
content_type='application/json')