Added graphql from prototype #162
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
@@ -16,29 +15,25 @@ from werkzeug.exceptions import NotFound
|
||||
|
||||
from bot_api.configuration.api_settings import ApiSettings
|
||||
from bot_api.configuration.authentication_settings import AuthenticationSettings
|
||||
from bot_api.configuration.frontend_settings import FrontendSettings
|
||||
from bot_api.exception.service_error_code_enum import ServiceErrorCode
|
||||
from bot_api.exception.service_exception import ServiceException
|
||||
from bot_api.logging.api_logger import ApiLogger
|
||||
from bot_api.model.error_dto import ErrorDTO
|
||||
from bot_api.route.route import Route
|
||||
from bot_graphql.graphql_service import GraphQLService
|
||||
|
||||
|
||||
class Api(Flask):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: ApiLogger,
|
||||
services: ServiceProviderABC,
|
||||
api_settings: ApiSettings,
|
||||
frontend_settings: FrontendSettings,
|
||||
auth_settings: AuthenticationSettings,
|
||||
graphql: GraphQLService,
|
||||
*args, **kwargs
|
||||
self,
|
||||
logger: ApiLogger,
|
||||
services: ServiceProviderABC,
|
||||
api_settings: ApiSettings,
|
||||
auth_settings: AuthenticationSettings,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
if not args:
|
||||
kwargs.setdefault('import_name', __name__)
|
||||
kwargs.setdefault("import_name", __name__)
|
||||
|
||||
Flask.__init__(self, *args, **kwargs)
|
||||
|
||||
@@ -58,17 +53,21 @@ class Api(Flask):
|
||||
self.register_error_handler(exc_class, self.handle_exception)
|
||||
|
||||
# websockets
|
||||
self._socketio = SocketIO(self, cors_allowed_origins='*', path='/api/socket.io')
|
||||
self._socketio.on_event('connect', self.on_connect)
|
||||
self._socketio.on_event('disconnect', self.on_disconnect)
|
||||
self._socketio = SocketIO(self, cors_allowed_origins="*", path="/api/socket.io")
|
||||
self._socketio.on_event("connect", self.on_connect)
|
||||
self._socketio.on_event("disconnect", self.on_disconnect)
|
||||
|
||||
self._requests = {}
|
||||
|
||||
@staticmethod
|
||||
def _get_methods_from_registered_route() -> Union[list[str], str]:
|
||||
methods = ['Unknown']
|
||||
if request.path in Route.registered_routes and len(Route.registered_routes[request.path]) >= 1 and 'methods' in Route.registered_routes[request.path][1]:
|
||||
methods = Route.registered_routes[request.path][1]['methods']
|
||||
methods = ["Unknown"]
|
||||
if (
|
||||
request.path in Route.registered_routes
|
||||
and len(Route.registered_routes[request.path]) >= 1
|
||||
and "methods" in Route.registered_routes[request.path][1]
|
||||
):
|
||||
methods = Route.registered_routes[request.path][1]["methods"]
|
||||
|
||||
if len(methods) == 1:
|
||||
return methods[0]
|
||||
@@ -79,7 +78,7 @@ class Api(Flask):
|
||||
route = f[0]
|
||||
kwargs = f[1]
|
||||
cls = None
|
||||
qual_name_split = route.__qualname__.split('.')
|
||||
qual_name_split = route.__qualname__.split(".")
|
||||
if len(qual_name_split) > 0:
|
||||
cls_type = vars(sys.modules[route.__module__])[qual_name_split[0]]
|
||||
cls = self._services.get_service(cls_type)
|
||||
@@ -89,7 +88,7 @@ class Api(Flask):
|
||||
self.route(path, **kwargs)(partial_f)
|
||||
|
||||
def handle_exception(self, e: Exception):
|
||||
self._logger.error(__name__, f'Caught error', e)
|
||||
self._logger.error(__name__, f"Caught error", e)
|
||||
|
||||
if isinstance(e, ServiceException):
|
||||
ex: ServiceException = e
|
||||
@@ -102,7 +101,7 @@ class Api(Flask):
|
||||
return jsonify(error.to_dict()), 404
|
||||
else:
|
||||
tracking_id = uuid.uuid4()
|
||||
user_message = f'Tracking Id: {tracking_id}'
|
||||
user_message = f"Tracking Id: {tracking_id}"
|
||||
self._logger.error(__name__, user_message, e)
|
||||
error = ErrorDTO(None, user_message)
|
||||
return jsonify(error.to_dict()), 400
|
||||
@@ -112,47 +111,48 @@ class Api(Flask):
|
||||
self._requests[request] = request_id
|
||||
method = request.access_control_request_method
|
||||
|
||||
self._logger.info(__name__, f'Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}')
|
||||
self._logger.info(
|
||||
__name__,
|
||||
f"Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}",
|
||||
)
|
||||
|
||||
headers = str(request.headers).replace('\n', '\n\t\t')
|
||||
headers = str(request.headers).replace("\n", "\n\t\t")
|
||||
data = request.get_data()
|
||||
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8"))
|
||||
data = "" if len(data) == 0 else str(data.decode(encoding="utf-8"))
|
||||
|
||||
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}')
|
||||
text = textwrap.dedent(
|
||||
f"Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}"
|
||||
)
|
||||
self._logger.trace(__name__, text)
|
||||
|
||||
def after_request_hook(self, response: Response):
|
||||
method = request.access_control_request_method
|
||||
request_id = f'{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}'
|
||||
request_id = f"{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}"
|
||||
if request in self._requests:
|
||||
request_id = self._requests[request]
|
||||
|
||||
self._logger.info(__name__, f'Answered {request_id}')
|
||||
self._logger.info(__name__, f"Answered {request_id}")
|
||||
|
||||
headers = str(request.headers).replace('\n', '\n\t\t')
|
||||
headers = str(request.headers).replace("\n", "\n\t\t")
|
||||
data = request.get_data()
|
||||
data = '' if len(data) == 0 else str(data.decode(encoding="utf-8"))
|
||||
data = "" if len(data) == 0 else str(data.decode(encoding="utf-8"))
|
||||
|
||||
text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}')
|
||||
text = textwrap.dedent(f"Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}")
|
||||
self._logger.trace(__name__, text)
|
||||
|
||||
return response
|
||||
|
||||
def start(self):
|
||||
self._logger.info(__name__, f'Starting API {self._api_settings.host}:{self._api_settings.port}')
|
||||
self._logger.info(__name__, f"Starting API {self._api_settings.host}:{self._api_settings.port}")
|
||||
self._register_routes()
|
||||
self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key)
|
||||
# from waitress import serve
|
||||
# https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html
|
||||
# serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10)
|
||||
wsgi.server(
|
||||
eventlet.listen((self._api_settings.host, self._api_settings.port)),
|
||||
self,
|
||||
log_output=False
|
||||
)
|
||||
wsgi.server(eventlet.listen((self._api_settings.host, self._api_settings.port)), self, log_output=False)
|
||||
|
||||
def on_connect(self):
|
||||
self._logger.info(__name__, f'Client connected')
|
||||
self._logger.info(__name__, f"Client connected")
|
||||
|
||||
def on_disconnect(self):
|
||||
self._logger.info(__name__, f'Client disconnected')
|
||||
self._logger.info(__name__, f"Client disconnected")
|
||||
|
@@ -11,34 +11,30 @@ from bot_graphql.schema import Schema
|
||||
|
||||
|
||||
class GraphQLController:
|
||||
BasePath = f'/api/graphql'
|
||||
BasePath = f"/api/graphql"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: ConfigurationABC,
|
||||
env: ApplicationEnvironmentABC,
|
||||
logger: ApiLogger,
|
||||
schema: Schema,
|
||||
self,
|
||||
config: ConfigurationABC,
|
||||
env: ApplicationEnvironmentABC,
|
||||
logger: ApiLogger,
|
||||
schema: Schema,
|
||||
):
|
||||
self._config = config
|
||||
self._env = env
|
||||
self._logger = logger
|
||||
self._schema = schema
|
||||
|
||||
@Route.get(f'{BasePath}/playground')
|
||||
@Route.get(f"{BasePath}/playground")
|
||||
async def playground(self):
|
||||
return PLAYGROUND_HTML, 200
|
||||
|
||||
@Route.post(f'{BasePath}')
|
||||
@Route.post(f"{BasePath}")
|
||||
async def graphql(self):
|
||||
data = request.get_json()
|
||||
|
||||
# Note: Passing the request to the context is optional.
|
||||
# In Flask, the current request is always accessible as flask.request
|
||||
success, result = graphql_sync(
|
||||
self._schema.schema,
|
||||
data,
|
||||
context_value=request
|
||||
)
|
||||
success, result = graphql_sync(self._schema.schema, data, context_value=request)
|
||||
|
||||
return jsonify(result), 200 if success else 400
|
||||
|
Reference in New Issue
Block a user