Update
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "jsonrpc"
|
name = "jsonrpc"
|
||||||
version = "1.0"
|
version = "2.0"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="RemiZOffAlex", email="remizoffalex@gmail.com" },
|
{ name="RemiZOffAlex", email="remizoffalex@gmail.com" },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
__author__ = 'RemiZOffAlex'
|
__author__ = 'RemiZOffAlex'
|
||||||
__email__ = 'remizoffalex@mail.ru'
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ class Response:
|
|||||||
|
|
||||||
|
|
||||||
class Method:
|
class Method:
|
||||||
def __init__(self, function, middlewares = [], debug: bool = False):
|
def __init__(self, function, middlewares=[], debug: bool = False):
|
||||||
self.function = function
|
self.function = function
|
||||||
self.middlewares = middlewares
|
self.middlewares = middlewares
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
@@ -65,11 +64,9 @@ class Method:
|
|||||||
response = middleware(query)
|
response = middleware(query)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
response = self.exec_function(query)
|
response = self.exec_function(query)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<{}>'.format(self.function)
|
return '<{}>'.format(self.function)
|
||||||
|
|
||||||
@@ -86,15 +83,15 @@ class JSONRPC:
|
|||||||
self.methods = {}
|
self.methods = {}
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
|
||||||
def method(self, name: str, middlewares = None):
|
def method(self, name: str, middlewares=None):
|
||||||
"""Декоратор метода
|
"""Декоратор метода
|
||||||
"""
|
"""
|
||||||
assert len(name) > 0, 'Не указано имя метода'
|
assert len(name) > 0, 'Не указано имя метода'
|
||||||
|
|
||||||
def wrap(function):
|
def wrap(function):
|
||||||
method = Method(
|
method = Method(
|
||||||
function = function,
|
function=function,
|
||||||
middlewares = middlewares
|
middlewares=middlewares
|
||||||
)
|
)
|
||||||
self.methods[name] = method
|
self.methods[name] = method
|
||||||
return function
|
return function
|
||||||
@@ -151,31 +148,31 @@ class JSONRPC:
|
|||||||
def validate(self, query):
|
def validate(self, query):
|
||||||
"""Валидация запроса
|
"""Валидация запроса
|
||||||
"""
|
"""
|
||||||
keys = query.keys()
|
|
||||||
if 'id' not in query:
|
|
||||||
return InvalidRequestError(
|
|
||||||
id=query['id'],
|
|
||||||
message=f'Некорректный запрос: {query}'
|
|
||||||
).__json__()
|
|
||||||
|
|
||||||
if 'method' not in query:
|
if 'method' not in query:
|
||||||
return InvalidRequestError(
|
result = InvalidRequestError(
|
||||||
id=query['id'],
|
|
||||||
message=f'Некорректный запрос: {query}'
|
message=f'Некорректный запрос: {query}'
|
||||||
).__json__()
|
)
|
||||||
|
if 'id' in query:
|
||||||
|
result.id = query['id']
|
||||||
|
return result
|
||||||
|
|
||||||
def process(self, query):
|
def process(self, query):
|
||||||
"""Выполнение метода
|
"""Выполнение метода
|
||||||
"""
|
"""
|
||||||
self.validate(query)
|
result = self.validate(query)
|
||||||
|
if isinstance(result, JSONRPCError):
|
||||||
|
return result
|
||||||
name = query['method']
|
name = query['method']
|
||||||
if name not in self.methods:
|
if name not in self.methods:
|
||||||
|
if 'id' in query:
|
||||||
|
__id = query['id']
|
||||||
|
else:
|
||||||
|
__id = None
|
||||||
result = MethodNotFoundError(
|
result = MethodNotFoundError(
|
||||||
query['id'],
|
message=f'Метод не найден: {name}',
|
||||||
message=f'Метод не найден: {name}'
|
id=__id
|
||||||
)
|
)
|
||||||
return result.__json__()
|
return result
|
||||||
|
|
||||||
|
|
||||||
method = self.methods[name]
|
method = self.methods[name]
|
||||||
|
|
||||||
@@ -198,11 +195,14 @@ class JSONRPC:
|
|||||||
# message=traceback.format_exc()
|
# message=traceback.format_exc()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
response = Response(
|
if 'id' in query:
|
||||||
id=query['id'],
|
response = Response(
|
||||||
result=response
|
id=query['id'],
|
||||||
)
|
result=response
|
||||||
result = response.__json__()
|
)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
result = response
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __call__(self, queries):
|
def __call__(self, queries):
|
||||||
@@ -213,7 +213,9 @@ class JSONRPC:
|
|||||||
elif isinstance(queries, list):
|
elif isinstance(queries, list):
|
||||||
result = []
|
result = []
|
||||||
for query in queries:
|
for query in queries:
|
||||||
result.append(self.process(query))
|
response = self.process(query)
|
||||||
|
if response:
|
||||||
|
result.append(response)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
|||||||
@@ -2,19 +2,30 @@ __author__ = 'RemiZOffAlex'
|
|||||||
__email__ = 'remizoffalex@mail.ru'
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import aiohttp_jinja2
|
import aiohttp_jinja2
|
||||||
|
|
||||||
from aiohttp.web import Response, json_response
|
from aiohttp.web import Response, json_response
|
||||||
|
|
||||||
from .. import JSONRPC
|
from .. import JSONRPC
|
||||||
|
from ..exceptions import ParseError
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
pathlib.Path(__file__).parent.resolve()
|
pathlib.Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
class APIHandler:
|
class APIHandler:
|
||||||
def __init__(self, jsonrpc):
|
def __init__(
|
||||||
|
self,
|
||||||
|
jsonrpc,
|
||||||
|
debug: bool = False
|
||||||
|
):
|
||||||
self.jsonrpc = jsonrpc
|
self.jsonrpc = jsonrpc
|
||||||
|
self.debug = debug
|
||||||
|
if self.debug:
|
||||||
|
log.debug('Connect JSON-RPC to aiohttp complete')
|
||||||
|
|
||||||
@aiohttp_jinja2.template('api_browse.html')
|
@aiohttp_jinja2.template('api_browse.html')
|
||||||
async def get(self, request) -> Response:
|
async def get(self, request) -> Response:
|
||||||
@@ -25,9 +36,16 @@ class APIHandler:
|
|||||||
return pagedata
|
return pagedata
|
||||||
|
|
||||||
async def post(self, request) -> Response:
|
async def post(self, request) -> Response:
|
||||||
json_data = await request.json()
|
try:
|
||||||
result = self.jsonrpc(json_data)
|
json_data = await request.json()
|
||||||
return json_response(result)
|
result = self.jsonrpc(json_data)
|
||||||
|
return json_response(result)
|
||||||
|
except ValueError as e:
|
||||||
|
log.error('invalid json: %s', request_data)
|
||||||
|
log.exception(e)
|
||||||
|
raise ParseError(
|
||||||
|
message={'message': 'Invalid JSON: {0!r}'.format(request_data)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def api_init(app, jsonrpc: JSONRPC, rule: str = '/api'):
|
def api_init(app, jsonrpc: JSONRPC, rule: str = '/api'):
|
||||||
@@ -38,6 +56,6 @@ def api_init(app, jsonrpc: JSONRPC, rule: str = '/api'):
|
|||||||
pathlib.Path(__file__).parent.resolve() / 'templates'
|
pathlib.Path(__file__).parent.resolve() / 'templates'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
handler = APIView(jsonrpc)
|
handler = APIHandler(jsonrpc)
|
||||||
app.router.add_route('GET', rule, handler.get)
|
app.router.add_route('GET', rule, handler.get)
|
||||||
app.router.add_route('POST', rule, handler.post)
|
app.router.add_route('POST', rule, handler.post)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from flask.views import MethodView
|
|||||||
from flask import jsonify, Response, request, render_template
|
from flask import jsonify, Response, request, render_template
|
||||||
|
|
||||||
from ..exceptions import ParseError
|
from ..exceptions import ParseError
|
||||||
|
from ..serialize import JSONRPCEncoder
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -20,9 +21,13 @@ def to_json(
|
|||||||
try:
|
try:
|
||||||
return json.loads(request_data)
|
return json.loads(request_data)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
print('invalid json:', request_data)
|
||||||
log.error('invalid json: %s', request_data)
|
log.error('invalid json: %s', request_data)
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
raise ParseError(data={'message': 'Invalid JSON: {0!r}'.format(request_data)})
|
result = ParseError(
|
||||||
|
message={'message': 'Invalid JSON: {0!r}'.format(request_data)}
|
||||||
|
)
|
||||||
|
return result
|
||||||
# raise ValueError('Invalid JSON')
|
# raise ValueError('Invalid JSON')
|
||||||
|
|
||||||
|
|
||||||
@@ -43,8 +48,12 @@ class APIView(MethodView):
|
|||||||
return body
|
return body
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
|
if self.debug:
|
||||||
|
print('request.data', request.data)
|
||||||
json_data = to_json(request.data)
|
json_data = to_json(request.data)
|
||||||
result = self.jsonrpc(json_data)
|
result = self.jsonrpc(json_data)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
|
print(result)
|
||||||
log.debug(result)
|
log.debug(result)
|
||||||
return jsonify(result)
|
# return jsonify(result)
|
||||||
|
return json.dumps(result, cls=JSONRPCEncoder)
|
||||||
|
|||||||
@@ -30,12 +30,14 @@ class Client:
|
|||||||
self.url,
|
self.url,
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=self.headers
|
headers=self.headers
|
||||||
).json()
|
)
|
||||||
|
print('content', response.content)
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
assert 'jsonrpc' in response
|
assert 'jsonrpc' in result
|
||||||
assert 'id' in response
|
assert 'id' in result
|
||||||
assert response["jsonrpc"] in ['2.0', '3.0']
|
assert result["jsonrpc"] in ['2.0', '3.0']
|
||||||
if '3.0' in response["jsonrpc"]:
|
if '3.0' in result["jsonrpc"]:
|
||||||
assert 'meta' in response
|
assert 'meta' in result
|
||||||
|
|
||||||
return response
|
return result
|
||||||
|
|||||||
19
src/jsonrpc/serialize.py
Normal file
19
src/jsonrpc/serialize.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .exceptions import JSONRPCError
|
||||||
|
|
||||||
|
|
||||||
|
class JSONRPCEncoder(json.JSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, datetime):
|
||||||
|
return obj.isoformat()
|
||||||
|
elif isinstance(obj, JSONRPCError):
|
||||||
|
return obj.__json__()
|
||||||
|
elif hasattr(obj, '__dict__'):
|
||||||
|
return obj.__dict__
|
||||||
|
return super().default(obj)
|
||||||
Reference in New Issue
Block a user