From 27d086bca2ff4b7d4780abe893ccd4308b63846d Mon Sep 17 00:00:00 2001 From: allegroai <> Date: Mon, 3 May 2021 18:11:46 +0300 Subject: [PATCH] Fix schema for Task.runtime Add infrastructure for API calls limits handling --- apiserver/schema/services/tasks.conf | 10 +++++++ apiserver/server_init/request_handlers.py | 26 ++++++++++++++--- apiserver/service_repo/service_repo.py | 34 +++++++++++++---------- apiserver/service_repo/validators.py | 10 +------ 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/apiserver/schema/services/tasks.conf b/apiserver/schema/services/tasks.conf index 4be36e9..cfe2e9d 100644 --- a/apiserver/schema/services/tasks.conf +++ b/apiserver/schema/services/tasks.conf @@ -617,6 +617,11 @@ _definitions { "$ref": "#/definitions/configuration_item" } } + runtime { + description: "Task runtime mapping" + type: object + additionalProperties: true + } } } task_urls { @@ -1361,6 +1366,11 @@ edit { type: object additionalProperties { type: string } } + runtime { + description: "Task runtime mapping" + type: object + additionalProperties: true + } } } } diff --git a/apiserver/server_init/request_handlers.py b/apiserver/server_init/request_handlers.py index c75c399..bc7203d 100644 --- a/apiserver/server_init/request_handlers.py +++ b/apiserver/server_init/request_handlers.py @@ -1,6 +1,9 @@ +from functools import partial + from flask import request, Response, redirect from werkzeug.exceptions import BadRequest +from apiserver.apierrors import APIError from apiserver.apierrors.base import BaseError from apiserver.config_repo import config from apiserver.service_repo import ServiceRepo, APICall @@ -25,7 +28,10 @@ class RequestHandlers: try: call = self._create_api_call(request) - content, content_type = ServiceRepo.handle_call(call) + load_data_callback = partial(self._load_call_data, req=request) + content, content_type = ServiceRepo.handle_call( + call, load_data_callback=load_data_callback + ) if call.result.redirect: response = redirect(call.result.redirect.url, call.result.redirect.code) @@ -137,9 +143,6 @@ class RequestHandlers: auth_cookie=auth_cookie, ) - # Update call data from request - self._update_call_data(call, req) - except PathParsingError as ex: call = self._call_or_empty_with_error(call, req, ex.args[0], 400) call.log_api = False @@ -156,3 +159,18 @@ class RequestHandlers: ) return call + + def _load_call_data(self, call: APICall, req): + """Update call data from request""" + try: + self._update_call_data(call, req) + except BadRequest as ex: + call.set_error_result(msg=ex.description, code=400) + except BaseError as ex: + call.set_error_result(msg=ex.msg, code=ex.code, subcode=ex.subcode) + except APIError as ex: + call.set_error_result( + msg=ex.msg, code=ex.code, subcode=ex.subcode, error_data=ex.error_data + ) + except Exception as ex: + call.set_error_result(msg=ex.args[0] if ex.args else type(ex).__name__) diff --git a/apiserver/service_repo/service_repo.py b/apiserver/service_repo/service_repo.py index 2c02004..3ed5522 100644 --- a/apiserver/service_repo/service_repo.py +++ b/apiserver/service_repo/service_repo.py @@ -13,7 +13,12 @@ from .apicall import APICall from .endpoint import Endpoint from .errors import MalformedPathError, InvalidVersionError, CallFailedError from .util import parse_return_stack_on_code -from .validators import validate_all +from .validators import ( + validate_data, + validate_auth, + validate_role, + validate_impersonation, +) log = config.logger(__file__) @@ -227,18 +232,6 @@ class ServiceRepo(object): return True return subcode in subcode_list - @classmethod - def _validate_call(cls, call: APICall) -> Optional[Endpoint]: - endpoint = cls._resolve_endpoint_from_call(call) - if call.failed: - return - validate_all(call, endpoint) - return endpoint - - @classmethod - def validate_call(cls, call: APICall): - cls._validate_call(call) - @classmethod def _get_company( cls, call: APICall, endpoint: Endpoint = None, ignore_error: bool = False @@ -252,7 +245,7 @@ class ServiceRepo(object): return call.identity.company @classmethod - def handle_call(cls, call: APICall): + def handle_call(cls, call: APICall, load_data_callback: Callable = None): try: if call.failed: raise CallFailedError() @@ -262,7 +255,18 @@ class ServiceRepo(object): if call.failed: raise CallFailedError() - validate_all(call, endpoint) + validate_auth(endpoint, call) + validate_role(endpoint, call) + if validate_impersonation(endpoint, call): + # if impersonating, validate role again + validate_role(endpoint, call) + + if load_data_callback: + load_data_callback(call) + if call.failed: + raise CallFailedError() + + validate_data(call, endpoint) if call.failed: raise CallFailedError() diff --git a/apiserver/service_repo/validators.py b/apiserver/service_repo/validators.py index 4e1ca86..530bc63 100644 --- a/apiserver/service_repo/validators.py +++ b/apiserver/service_repo/validators.py @@ -14,17 +14,9 @@ from .errors import CallParsingError log = config.logger(__file__) -def validate_all(call: APICall, endpoint: Endpoint): +def validate_data(call: APICall, endpoint: Endpoint): """ Perform all required call/endpoint validation, update call result appropriately """ try: - validate_auth(endpoint, call) - - validate_role(endpoint, call) - - if validate_impersonation(endpoint, call): - # if impersonating, validate role again - validate_role(endpoint, call) - # todo: remove vaildate_required_fields once all endpoints have json schema validate_required_fields(endpoint, call)