From a379065891f51e6a7ada1b276a9d88f2f09ae001 Mon Sep 17 00:00:00 2001 From: William Luke Date: Thu, 18 Nov 2021 15:38:20 +0300 Subject: [PATCH] switch back to uwsgi --- .../cic_eth/runnable/daemons/server.py | 45 +++++++++++-- .../cic_eth/server/UWSGIOpenAPIRequest.py | 63 +++++++++++++++++++ apps/cic-eth/requirements.txt | 7 ++- docker-compose.yml | 6 +- 4 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 apps/cic-eth/cic_eth/server/UWSGIOpenAPIRequest.py diff --git a/apps/cic-eth/cic_eth/runnable/daemons/server.py b/apps/cic-eth/cic_eth/runnable/daemons/server.py index e8d6ccfe..8c1d4f1b 100644 --- a/apps/cic-eth/cic_eth/runnable/daemons/server.py +++ b/apps/cic-eth/cic_eth/runnable/daemons/server.py @@ -4,10 +4,23 @@ import logging import os import sys import uuid +from os import path from urllib.parse import parse_qsl, urlparse import cic_eth.cli import redis +from cic_eth.api.api_task import Api +from cic_eth.server.UWSGIOpenAPIRequest import UWSGIOpenAPIRequest +from openapi_core import create_spec +from openapi_core.validation.request.datatypes import OpenAPIRequest +from openapi_core.validation.request.validators import RequestValidator +from openapi_spec_validator.schemas import read_yaml_file +from werkzeug.wrappers import Request + +spec_dict = read_yaml_file(path.join(path.dirname( + __file__), '../../server/openapi/server.yaml')) +spec = create_spec(spec_dict) + logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() @@ -21,16 +34,35 @@ args = argparser.parse_args() config = cic_eth.cli.Config.from_args(args, arg_flags, local_arg_flags) celery_app = cic_eth.cli.CeleryApp.from_config(config) -from cic_eth.api.api_task import Api + # uwsgi application def application(env, start_response): - parsed_url = urlparse(env['REQUEST_URI']) # /api + print(spec) + request = Request(env) + oAPIRequest = UWSGIOpenAPIRequest(request) + validator = RequestValidator(spec) + result = validator.validate(oAPIRequest) + + # raise errors if request invalid + # result.raise_for_errors() + if result.errors: + # get list of errors + json_data = json.dumps(list(map(lambda e: str(e), result.errors))) + content = json_data.encode('utf-8') + headers = [] + headers.append(('Content-Length', str(len(content))),) + headers.append(('Access-Control-Allow-Origin', '*',)) + headers.append(('Content-Type', 'application/json',)) + start_response('400 Invalid Request', headers) + return [content] + parsed_url = urlparse(env.get('REQUEST_URI')) # /api path = parsed_url.path + parsed_url.params params = dict(parse_qsl(parsed_url.query)) - request_method = env['REQUEST_METHOD'] + request_method = env.get('REQUEST_METHOD') chain_spec = config.get('CHAIN_SPEC') redis_host = config.get('REDIS_HOST') redis_port = config.get('REDIS_PORT') @@ -86,8 +118,11 @@ def application(env, start_response): m = json.loads(o['data']) print(m['result']) - data = {"path": path, - "request_method": request_method, "result": m} + data = { + "path": path, + "request_method": request_method, + "result": m + } json_data = json.dumps(data) content = json_data.encode('utf-8') headers = [] diff --git a/apps/cic-eth/cic_eth/server/UWSGIOpenAPIRequest.py b/apps/cic-eth/cic_eth/server/UWSGIOpenAPIRequest.py new file mode 100644 index 00000000..7160e62e --- /dev/null +++ b/apps/cic-eth/cic_eth/server/UWSGIOpenAPIRequest.py @@ -0,0 +1,63 @@ +"""OpenAPI core contrib requests requests module""" + +from urllib.parse import parse_qs +from urllib.parse import urlparse + +from requests import Request +from werkzeug.datastructures import Headers +from werkzeug.datastructures import ImmutableMultiDict + +from openapi_core.validation.request.datatypes import OpenAPIRequest +from openapi_core.validation.request.datatypes import RequestParameters + + +class UWSGIOpenAPIRequestFactory: + @classmethod + def create(cls, request): + """ + Converts a requests request to an OpenAPI one + + Internally converts to a `PreparedRequest` first to parse the exact + payload being sent + """ + if isinstance(request, Request): + request = request.prepare() + + # Method + method = request.method.lower() + + + # Preparing a request formats the URL with params, strip them out again + o = urlparse(request.url) + params = parse_qs(o.query) + # extract the URL without query parameters + url = o._replace(query=None).geturl() + + # Order matters because all python requests issued from a session + # include Accept */* which does not necessarily match the content type + mimetype = request.headers.get("Content-Type") or request.headers.get( + "Accept" + ) + + # Headers - request.headers is not an instance of Headers + # which is expected + header = Headers(dict(request.headers)) + + # Body + # TODO: figure out if request._body_position is relevant + body = request.get_data() + + # Path gets deduced by path finder against spec + parameters = RequestParameters( + query=ImmutableMultiDict(params), + header=header, + cookie=request.cookies, + ) + return OpenAPIRequest( + full_url_pattern=url, + method=method, + parameters=parameters, + body=body, + mimetype=mimetype, + ) +UWSGIOpenAPIRequest = UWSGIOpenAPIRequestFactory.create \ No newline at end of file diff --git a/apps/cic-eth/requirements.txt b/apps/cic-eth/requirements.txt index 3de5b7a6..95577468 100644 --- a/apps/cic-eth/requirements.txt +++ b/apps/cic-eth/requirements.txt @@ -3,9 +3,10 @@ chainlib-eth>=0.0.10a16,<0.1.0 semver==2.13.0 crypto-dev-signer>=0.4.15rc2,<0.5.0 uwsgi==2.0.19.1 -graphene>=2.0 flask>=2.0.2 -Flask-GraphQL>=2.0.1 connexion[swagger-ui] == 2.6.0 python_dateutil == 2.6.0 -setuptools >= 21.0.0 \ No newline at end of file +setuptools >= 21.0.0 +openapi-core >= 0.14.2 +openapi-spec-validator >= 0.3.1 +Werkzeug>=2.0.2 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3105ced9..00625eaf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -218,8 +218,10 @@ services: set -a if [[ -f /tmp/cic/config/env_reset ]]; then source /tmp/cic/config/env_reset; fi set +a - pip install -r test_requirements.txt - tail -F ./requirements.txt + /usr/local/bin/uwsgi \ + --wsgi-file /root/cic_eth/runnable/daemons/server.py \ + --http :5000 \ + --pyargv "-vv" cic-eth-tracker: image: ${DEV_DOCKER_REGISTRY:-registry.gitlab.com/grassrootseconomics/cic-internal-integration}/cic-eth:${TAG:-latest}