@api.validate(resp=Response(HTTP_200=Resp, HTTP_401=None, validate=False), tags=["api", "test"]) def group_score(name): score = ["a", "b", "c", "d", "e"] return jsonify(name=name, score=score) @app.route("/api/file", methods=["POST"]) @api.validate(body=MultipartFormRequest(), resp=Response(HTTP_200=DemoModel)) def upload_file(): files = request.files assert files is not None return jsonify(uid=1, limit=2, name="success") api.register(app) @pytest.fixture(params=[422, 400]) def client(request): api.config.VALIDATION_ERROR_CODE = request.param with app.test_client() as client: yield client @pytest.mark.parametrize("client", [422], indirect=True) def test_flask_validate(client): resp = client.get("/ping") assert resp.status_code == 422 assert resp.headers.get("X-Error") == "Validation Error"
from flask import Blueprint from spectree import SpecTree from pcapi.serialization.utils import before_handler native_v1 = Blueprint("native_v1", __name__) api = SpecTree("flask", MODE="strict", before=before_handler, PATH="/") api.register(native_v1)
}) except OAuth2Client.Exists: return JSONResponse([{ 'loc': ['name'], 'msg': 'Client name already exists for account.', 'type': 'value_error.name_exists' }], status_code=409) return JSONResponse(_client.dict(model=OAuth2ClientResponse), status_code=201) router = Router(routes=[ #Route('/user', user_profile, methods=['POST']), Route('/profile', users.profile, methods=['GET', 'PUT']), Route('/password', users.password, methods=['PUT']), # clients Route('/clients/{client_id:str}', clients_get, methods=['GET']), Route('/clients/{client_id:str}', clients_delete, methods=['DELETE']), Route('/clients', clients_list, methods=['GET']), Route('/clients', clients_post, methods=['POST']), # tokens Route('/token', tokens.token_create, methods=['POST']), Route('/token-refresh', tokens.token_refresh, methods=['POST']), Route(SPEC_URL, lambda request: JSONResponse(_app.spec)), Route('/docs/api', docs, name='api_docs', methods=['GET']), #Route('/docs/api', lambda request, ui='redoc': HTMLResponse( # PAGES['redoc'].format(PREFIX+SPEC_URL))) ]) _app.register(router)
after=api_after_handler, ) def user_score_annotated(name, query: Query, json: JSON, cookies: Cookies): score = [randint(0, json.limit) for _ in range(5)] score.sort(reverse=query.order) assert cookies.pub == "abcdefg" assert request.cookies["pub"] == "abcdefg" return jsonify(name=json.name, score=score) # INFO: ensures that spec is calculated and cached _after_ registering # view functions for validations. This enables tests to access `api.spec` # without app_context. with app.app_context(): api.spec api.register(app) @pytest.fixture def client(): with app.test_client() as client: yield client def test_flask_validate(client): resp = client.get("/ping") assert resp.status_code == 422 assert resp.headers.get("X-Error") == "Validation Error" resp = client.get("/ping", headers={"lang": "en-US"}) assert resp.json == {"msg": "pong"}
def create_app(infer, metric_registry, health_check, req_schema, resp_schema, use_msgpack, config): """ create :class:`falcon` application :param infer: model infer function (contains `preprocess`, `inference`, and `postprocess`) :param metric_registry: Prometheus metric registry :param health_check: model health check function (need examples provided in schema) :param req_schema: request schema defined with :class:`pydantic.BaseModel` :param resp_schema: request schema defined with :class:`pydantic.BaseModel` :param bool use_msgpack: use msgpack for serialization or not (default: JSON) :param config: configs :class:`ventu.config.Config` :return: a :class:`falcon` application """ if use_msgpack: handlers = media.Handlers({ 'application/msgpack': media.MessagePackHandler(), }) app = API(media_type='application/msgpack') app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers else: app = API() api = SpecTree('falcon', title=config.name, version=config.version) logger = logging.getLogger(__name__) VALIDATION_ERROR_COUNTER = Counter( 'validation_error_counter', 'numbers of validation errors', registry=metric_registry, ) def counter_hook(req, resp, resource): if resp.status == HTTP_422: VALIDATION_ERROR_COUNTER.inc() class Homepage: def on_get(self, req, resp): logger.debug('return service endpoints') resp.media = { 'health check': { '/health': 'GET' }, 'metrics': { '/metrics': 'GET' }, 'inference': { '/inference': 'POST' }, 'API document': { '/apidoc/redoc': 'GET', '/apidoc/swagger': 'GET' } } class Metrics: def __init__(self): if os.environ.get('prometheus_multiproc_dir'): multiprocess.MultiProcessCollector(metric_registry) def on_get(self, req, resp): resp.content_type = CONTENT_TYPE_LATEST resp.body = generate_latest(metric_registry) class HealthCheck: @api.validate(resp=Response(HTTP_200=ServiceStatus)) def on_get(self, req, resp): """ Health check """ status = ServiceStatus(inference=StatusEnum.ok) try: health_check() except AssertionError as err: status.inference = StatusEnum.error logger.warning(f'Service health check error: {err}') logger.debug(str(status)) resp.media = status.dict() class Inference: @after(counter_hook) @api.validate(json=req_schema, resp=Response(HTTP_200=resp_schema)) def on_post(self, req, resp): """ Deep learning model inference """ data = req.context.json logger.debug(str(data)) resp.media = infer(data) app.add_route('/', Homepage()) app.add_route('/health', HealthCheck()) app.add_route('/metrics', Metrics()) app.add_route('/inference', Inference()) api.register(app) return app