def wsexpose(*args, **kwargs): pecan_json_decorate = pecan.expose( template='wsmejson:', content_type='application/json', generic=False) pecan_xml_decorate = pecan.expose( template='wsmexml:', content_type='application/xml', generic=False ) sig = wsme.signature(*args, **kwargs) def decorate(f): sig(f) funcdef = wsme.api.FunctionDefinition.get(f) funcdef.resolve_types(wsme.types.registry) @functools.wraps(f) def callfunction(self, *args, **kwargs): try: args, kwargs = wsme.rest.args.get_args( funcdef, args, kwargs, pecan.request.params, None, pecan.request.body, pecan.request.content_type ) if funcdef.pass_request: kwargs[funcdef.pass_request] = pecan.request result = f(self, *args, **kwargs) # NOTE: Support setting of status_code with default 201 pecan.response.status = funcdef.status_code if isinstance(result, wsme.api.Response): pecan.response.status = result.status_code result = result.obj except Exception: raise data = wsme.api.format_exception( sys.exc_info(), pecan.conf.get('wsme', {}).get('debug', False) ) if data['faultcode'] == 'Client': pecan.response.status = 400 else: pecan.response.status = 500 return data return dict( datatype=funcdef.return_type, result=result ) pecan_xml_decorate(callfunction) pecan_json_decorate(callfunction) pecan.util._cfg(callfunction)['argspec'] = inspect.getargspec(f) callfunction._wsme_definition = funcdef return callfunction return decorate
def wsexpose(*args, **kwargs): pecan_json_decorate = pecan.expose(template='wsmejson:', content_type='application/json', generic=False) pecan_xml_decorate = pecan.expose(template='wsmexml:', content_type='application/xml', generic=False) sig = wsme.signature(*args, **kwargs) def decorate(f): sig(f) funcdef = wsme.api.FunctionDefinition.get(f) funcdef.resolve_types(wsme.types.registry) @functools.wraps(f) def callfunction(self, *args, **kwargs): try: args, kwargs = wsme.rest.args.get_args( funcdef, args, kwargs, pecan.request.params, None, pecan.request.body, pecan.request.content_type) if funcdef.pass_request: kwargs[funcdef.pass_request] = pecan.request result = f(self, *args, **kwargs) # NOTE: Support setting of status_code with default 201 pecan.response.status = funcdef.status_code if isinstance(result, wsme.api.Response): pecan.response.status = result.status_code result = result.obj except Exception: raise data = wsme.api.format_exception( sys.exc_info(), pecan.conf.get('wsme', {}).get('debug', False)) if data['faultcode'] == 'Client': pecan.response.status = 400 else: pecan.response.status = 500 return data return dict(datatype=funcdef.return_type, result=result) pecan_xml_decorate(callfunction) pecan_json_decorate(callfunction) pecan.util._cfg(callfunction)['argspec'] = inspect.getargspec(f) callfunction._wsme_definition = funcdef return callfunction return decorate
def expose(f): f = pecan.expose(*vargs, **vkwargs)(f) @functools.wraps(f) def callfunction(*args, **kwargs): return f(*args, body=deserialize(schema), **kwargs) return callfunction
def expose(f): f = pecan.expose(*vargs, **vkwargs)(f) @functools.wraps(f) def callfunction(*args, **kwargs): params = jsonutils.loads(pecan.request.body) try: schema(params) except voluptuous.Error as e: pecan.abort(400, "Invalid input: %s" % e) return f(*args, body=params, **kwargs) return callfunction
def test_generics_not_allowed(self): class C(object): def _default(self): pass def _lookup(self): pass def _route(self): pass for method in (C._default, C._lookup, C._route): self.assertRaises( ValueError, expose(generic=True), getattr(method, '__func__', method) )
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type="application/json", method=None): """ :param arg_types: A list of types for the function arguments (e.g. [str, str, int, bool]). :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ # Late import to avoid very expensive in-direct import (~1 second) when this function # is not called / used import jsonschema import pecan from webob import exc from st2common.util.jsonify import json_encode from st2common.util.api import get_exception_for_type_error from st2common.util.api import get_exception_for_uncaught_api_error pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): args = list(args) more = [args.pop(0)] def cast_value(value_type, value): if value_type == bool: def cast_func(value): return value.lower() in ["1", "true"] else: cast_func = value_type result = cast_func(value) return result if body_cls: if pecan.request.body: data = pecan.request.json obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError(detail=e.message, comment=traceback.format_exc()) else: obj = None more.append(obj) if arg_types: # Cast and transform arguments based on the provided arg_types specification result_args, result_kwargs = get_controller_args_for_types( func=f, arg_types=arg_types, args=args, kwargs=kwargs ) more = more + result_args kwargs.update(result_kwargs) args = tuple(more) + tuple(args) noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: e = get_exception_for_type_error(func=f, exc=e) raise e except Exception as e: e = get_exception_for_uncaught_api_error(func=f, exc=e) raise e if status_code: pecan.response.status = status_code if content_type == "application/json": if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
pass @staticmethod def render(template_path, namespace): if 'faultcode' in namespace: return wsme.rest.xml.encode_error(None, namespace) return wsme.rest.xml.encode_result( namespace['result'], namespace['datatype'] ) pecan.templating._builtin_renderers['wsmejson'] = JSonRenderer pecan.templating._builtin_renderers['wsmexml'] = XMLRenderer pecan_json_decorate = pecan.expose( template='wsmejson:', content_type='application/json', generic=False) pecan_xml_decorate = pecan.expose( template='wsmexml:', content_type='application/xml', generic=False ) pecan_text_xml_decorate = pecan.expose( template='wsmexml:', content_type='text/xml', generic=False ) def wsexpose(*args, **kwargs): sig = wsme.signature(*args, **kwargs)
def expose(*args, **kwargs): kwargs.setdefault('content_type', 'application/json') kwargs.setdefault('template', 'json') return pecan.expose(*args, **kwargs)
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json'): """ :param arg_types: A list of types for the function arguments. :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): args = list(args) types = copy.copy(arg_types) more = [args.pop(0)] if types: argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning( "Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} obj = body_cls(**data) try: obj.validate() except jsonschema.ValidationError as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError( detail=e.message, comment=traceback.format_exc()) more.append(obj) args = tuple(more) + tuple(args) noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) result = f(*args, **kwargs) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result, indent=None) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json', method=None): """ :param arg_types: A list of types for the function arguments (e.g. [str, str, int, bool]). :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ # Late import to avoid very expensive in-direct import (~1 second) when this function # is not called / used import jsonschema import pecan from webob import exc from st2common.util.jsonify import json_encode from st2common.util.api import get_exception_for_type_error from st2common.util.api import get_exception_for_uncaught_api_error pecan_json_decorate = pecan.expose( content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): args = list(args) more = [args.pop(0)] def cast_value(value_type, value): if value_type == bool: def cast_func(value): return value.lower() in ['1', 'true'] else: cast_func = value_type result = cast_func(value) return result if body_cls: if pecan.request.body: data = pecan.request.json obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError(detail=e.message, comment=traceback.format_exc()) else: obj = None more.append(obj) if arg_types: # Cast and transform arguments based on the provided arg_types specification result_args, result_kwargs = get_controller_args_for_types(func=f, arg_types=arg_types, args=args, kwargs=kwargs) more = more + result_args kwargs.update(result_kwargs) args = tuple(more) + tuple(args) noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: e = get_exception_for_type_error(func=f, exc=e) raise e except Exception as e: e = get_exception_for_uncaught_api_error(func=f, exc=e) raise e if status_code: pecan.response.status = status_code if content_type == 'application/json': if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json'): """ :param arg_types: A list of types for the function arguments. :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): function_name = f.__name__ args = list(args) types = copy.copy(arg_types) more = [args.pop(0)] if types: argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning( "Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError( detail=e.message, comment=traceback.format_exc()) # Set default pack if one is not provided for resource create if function_name == 'post' and not hasattr(obj, 'pack'): extra = { 'resource_api': obj, 'default_pack_name': DEFAULT_PACK_NAME } LOG.debug( 'Pack not provided in the body, setting a default pack name', extra=extra) setattr(obj, 'pack', DEFAULT_PACK_NAME) more.append(obj) args = tuple(more) + tuple(args) noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: message = str(e) # Invalid number of arguments passed to the function meaning invalid path was # requested # Note: The check is hacky, but it works for now. func_name = f.__name__ pattern = '%s\(\) takes exactly \d+ arguments \(\d+ given\)' % ( func_name) if re.search(pattern, message): raise exc.HTTPNotFound() else: raise e if status_code: pecan.response.status = status_code if content_type == 'application/json': if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
def delete(self, nodeid): response.status = http_client.NO_CONTENT return {'result': 'Call the method named delete', 'id': nodeid} @expose(template='json') def start(self, nodeid): response.status = http_client.ACCEPTED return {'result': 'Call the method named start', 'id': nodeid} def power_off(self, nodeid): response.status = http_client.ACCEPTED return {'result': 'Call the method named power_off', 'id': nodeid} setattr(NodeController, 'power-operate', expose(template='json')( six.get_method_function(NodeController.power_operate))) class VersionController(rest.RestController): @expose(template='json') def _default(self): return {'Version': 'v1.0'} class RootController(rest.RestController): node = NodeController() version = VersionController() @expose() def _route(self, args, request):
def jsexpose(*argtypes, **opts): content_type = opts.get('content_type', 'application/json') pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): try: args = list(args) types = list(argtypes) more = [args.pop(0)] if len(types): argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning( "Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass body_cls = opts.get('body') if body_cls and pecan.request.body: try: obj = body_cls(**pecan.request.json) except jsonschema.exceptions.ValidationError as e: return _handle_error(http_client.BAD_REQUEST, e) more.append(obj) args = tuple(more) + tuple(args) status_code = opts.get('status_code') noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result) else: return result except exc.HTTPException as e: return _handle_error(e.wsgi_response.status, e) except Exception as e: return _handle_error(http_client.INTERNAL_SERVER_ERROR, e) pecan_json_decorate(callfunction) return callfunction return decorate
# License for the specific language governing permissions and limitations # under the License. import functools from http import client as http_client import json import sys import traceback from oslo_config import cfg from oslo_log import log import pecan LOG = log.getLogger(__name__) pecan_json_decorate = pecan.expose(content_type='application/json', generic=False) def expose(status_code=None): def decorate(f): @functools.wraps(f) def callfunction(self, *args, **kwargs): try: result = f(self, *args, **kwargs) if status_code: pecan.response.status = status_code except Exception: try: exception_info = sys.exc_info() orig_exception = exception_info[1]
def expose(*args, **kwargs): """Helper function so we don't have to specify json for everything.""" kwargs.setdefault('content_type', 'application/json') kwargs.setdefault('template', 'json') return pecan.expose(*args, **kwargs)
def wsexpose(*args, **kwargs): pecan_json_decorate = pecan.expose( template='wsmejson:', content_type='application/json', generic=False) pecan_xml_decorate = pecan.expose( template='wsmexml:', content_type='application/xml', generic=False ) pecan_text_xml_decorate = pecan.expose( template='wsmexml:', content_type='text/xml', generic=False ) sig = wsme.signature(*args, **kwargs) def decorate(f): sig(f) funcdef = wsme.api.FunctionDefinition.get(f) funcdef.resolve_types(wsme.types.registry) @functools.wraps(f) def callfunction(self, *args, **kwargs): try: args, kwargs = wsme.rest.args.get_args( funcdef, args, kwargs, pecan.request.params, None, pecan.request.body, pecan.request.content_type ) if funcdef.pass_request: kwargs[funcdef.pass_request] = pecan.request result = f(self, *args, **kwargs) # NOTE: Support setting of status_code with default 201 pecan.response.status = funcdef.status_code if isinstance(result, wsme.api.Response): pecan.response.status = result.status_code result = result.obj except: try: exception_info = sys.exc_info() orig_exception = exception_info[1] orig_code = getattr(orig_exception, 'code', None) data = wsme.api.format_exception( exception_info, pecan.conf.get('wsme', {}).get('debug', False) ) finally: del exception_info if orig_code and is_valid_code(orig_code): pecan.response.status = orig_code else: pecan.response.status = 500 return data if funcdef.return_type is None: pecan.request.pecan['content_type'] = None pecan.response.content_type = None return '' return dict( datatype=funcdef.return_type, result=result ) pecan_xml_decorate(callfunction) pecan_text_xml_decorate(callfunction) pecan_json_decorate(callfunction) pecan.util._cfg(callfunction)['argspec'] = inspect.getargspec(f) callfunction._wsme_definition = funcdef return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json'): """ :param arg_types: A list of types for the function arguments. :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose( content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): args = list(args) types = copy.copy(arg_types) more = [args.pop(0)] if types: argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning("Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} obj = body_cls(**data) try: obj.validate() except jsonschema.ValidationError as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError(detail=e.message, comment=traceback.format_exc()) more.append(obj) args = tuple(more) + tuple(args) noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) result = f(*args, **kwargs) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result, indent=None) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
@staticmethod def __init__(path, extra_vars): pass @staticmethod def render(template_path, namespace): if 'faultcode' in namespace: return encode_error(None, namespace) result = encode_result(namespace['result'], namespace['datatype']) return result pecan.templating._builtin_renderers['wsmejson'] = JSonRenderer pecan_json_decorate = pecan.expose(template='wsmejson:', content_type='application/json', generic=False) def expose(*args, **kwargs): sig = wsme.signature(*args, **kwargs) def decorate(f): sig(f) funcdef = wsme.api.FunctionDefinition.get(f) funcdef.resolve_types(atypes.registry) @functools.wraps(f) def callfunction(self, *args, **kwargs): return_type = funcdef.return_type
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json', method=None): """ :param arg_types: A list of types for the function arguments (e.g. [str, str, int, bool]). :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose( content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): function_name = f.__name__ args = list(args) more = [args.pop(0)] def cast_value(value_type, value): if value_type == bool: def cast_func(value): return value.lower() in ['1', 'true'] else: cast_func = value_type result = cast_func(value) return result if body_cls: if pecan.request.body: data = pecan.request.json obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError(detail=e.message, comment=traceback.format_exc()) # Set default pack if one is not provided for resource create if function_name == 'post' and not hasattr(obj, 'pack'): extra = { 'resource_api': obj, 'default_pack_name': DEFAULT_PACK_NAME } LOG.debug('Pack not provided in the body, setting a default pack name', extra=extra) setattr(obj, 'pack', DEFAULT_PACK_NAME) else: obj = None more.append(obj) if arg_types: # Cast and transform arguments based on the provided arg_types specification result_args, result_kwargs = get_controller_args_for_types(func=f, arg_types=arg_types, args=args, kwargs=kwargs) more = more + result_args kwargs.update(result_kwargs) args = tuple(more) + tuple(args) noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: e = get_exception_for_type_error(func=f, exc=e) raise e if status_code: pecan.response.status = status_code if content_type == 'application/json': if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(*argtypes, **opts): content_type = opts.get('content_type', 'application/json') pecan_json_decorate = pecan.expose( content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): try: args = list(args) types = list(argtypes) more = [args.pop(0)] if len(types): argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning("Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass body_cls = opts.get('body') if body_cls: if pecan.request.body: try: obj = body_cls(**pecan.request.json) except jsonschema.exceptions.ValidationError as e: return _handle_error(http_client.BAD_REQUEST, e) more.append(obj) else: more.append(None) args = tuple(more) + tuple(args) status_code = opts.get('status_code') noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result) else: return result except exc.HTTPException as e: return _handle_error(e.wsgi_response.status, e) except Exception as e: return _handle_error(http_client.INTERNAL_SERVER_ERROR, e) pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type="application/json"): """ :param arg_types: A list of types for the function arguments. :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): function_name = f.__name__ args = list(args) types = copy.copy(arg_types) more = [args.pop(0)] if types: argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning("Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest(detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError(detail=e.message, comment=traceback.format_exc()) # Set default pack if one is not provided for resource create if function_name == "post" and not hasattr(obj, "pack"): extra = {"resource_api": obj, "default_pack_name": DEFAULT_PACK_NAME} LOG.debug("Pack not provided in the body, setting a default pack name", extra=extra) setattr(obj, "pack", DEFAULT_PACK_NAME) more.append(obj) args = tuple(more) + tuple(args) noop_codes = [http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: message = str(e) # Invalid number of arguments passed to the function meaning invalid path was # requested # Note: The check is hacky, but it works for now. func_name = f.__name__ pattern = "%s\(\) takes exactly \d+ arguments \(\d+ given\)" % (func_name) if re.search(pattern, message): raise exc.HTTPNotFound() else: raise e if status_code: pecan.response.status = status_code if content_type == "application/json": if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(*argtypes, **opts): content_type = opts.get('content_type', 'application/json') pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): params = getattr(pecan.request, 'params', {}) if QUERY_PARAM_ATTRIBUTE_NAME in params and QUERY_PARAM_ATTRIBUTE_NAME in kwargs: # Remove auth token if one is provided via query params del kwargs[QUERY_PARAM_ATTRIBUTE_NAME] try: args = list(args) types = list(argtypes) more = [args.pop(0)] if len(types): argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning( "Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass body_cls = opts.get('body') if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} try: obj = body_cls(**data) except jsonschema.exceptions.ValidationError as e: return _handle_error(e, http_client.BAD_REQUEST) more.append(obj) args = tuple(more) + tuple(args) status_code = opts.get('status_code') noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result) else: return result except exc.HTTPException as e: LOG.exception('API call failed.') # Exception contains pecan.response.header + more. This is per implementation # of the WSGIHTTPException type from WebOb. return _handle_error(e, e.wsgi_response.status_code, e.wsgi_response.body, e.headers) except Exception as e: LOG.exception('API call failed.') return _handle_error(e, http_client.INTERNAL_SERVER_ERROR) pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json'): """ :param arg_types: A list of types for the function arguments. :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): controller = args[0] if args else None # Note: We use getattr since in some places (tests) request is mocked params = getattr(pecan.request, 'params', {}) method = getattr(pecan.request, 'method', None) path = getattr(pecan.request, 'path', None) remote_addr = getattr(pecan.request, 'remote_addr', None) # Common request information included in the log context request_info = { 'method': method, 'path': path, 'remote_addr': remote_addr } # Log the incoming request values = copy.copy(request_info) values['filters'] = kwargs LOG.info('%(method)s %(path)s with filters=%(filters)s' % values, extra=values) if QUERY_PARAM_ATTRIBUTE_NAME in params and QUERY_PARAM_ATTRIBUTE_NAME in kwargs: # Remove auth token if one is provided via query params del kwargs[QUERY_PARAM_ATTRIBUTE_NAME] try: args = list(args) types = copy.copy(arg_types) more = [args.pop(0)] if types: argspec = inspect.getargspec(f) names = argspec.args[1:] for name in names: try: a = args.pop(0) more.append(types.pop(0)(a)) except IndexError: try: kwargs[name] = types.pop(0)(kwargs[name]) except IndexError: LOG.warning( "Type definition for '%s' argument of '%s' " "is missing.", name, f.__name__) except KeyError: pass if body_cls: if pecan.request.body: data = pecan.request.json else: data = {} try: obj = body_cls(**data) except jsonschema.exceptions.ValidationError as e: return _handle_error(e, http_client.BAD_REQUEST) more.append(obj) args = tuple(more) + tuple(args) noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) # Log the outgoing response values = copy.copy(request_info) values[ 'status_code'] = status_code or pecan.response.status function_name = f.__name__ controller_name = controller.__class__.__name__ log_result = True log_result &= function_name not in RESPONSE_LOGGING_METHOD_NAME_BLACKLIST log_result &= controller_name not in RESPONSE_LOGGING_CONTROLLER_NAME_BLACKLIST if log_result: values['result'] = result log_msg = '%(method)s %(path)s result=%(result)s' % values else: # Note: We don't want to include a result for some # methods which have a large result log_msg = '%(method)s %(path)s' % values LOG.info(log_msg, extra=values) if status_code: pecan.response.status = status_code if content_type == 'application/json': return json_encode(result) else: return result except exc.HTTPUnauthorized as e: LOG.debug('API call failed: %s' % (str(e))) return _handle_error(e, e.wsgi_response.status_code, e.wsgi_response.body, e.headers) except exc.HTTPException as e: LOG.exception('API call failed: %s' % (str(e))) # Exception contains pecan.response.header + more. This is per implementation # of the WSGIHTTPException type from WebOb. return _handle_error(e, e.wsgi_response.status_code, e.wsgi_response.body, e.headers) except Exception as e: LOG.exception('API call failed: %s' % (str(e))) return _handle_error(e, http_client.INTERNAL_SERVER_ERROR) pecan_json_decorate(callfunction) return callfunction return decorate
def jsexpose(arg_types=None, body_cls=None, status_code=None, content_type='application/json', method=None): """ :param arg_types: A list of types for the function arguments (e.g. [str, str, int, bool]). :type arg_types: ``list`` :param body_cls: Request body class. If provided, this class will be used to create an instance out of the request body. :type body_cls: :class:`object` :param status_code: Response status code. :type status_code: ``int`` :param content_type: Response content type. :type content_type: ``str`` """ pecan_json_decorate = pecan.expose(content_type=content_type, generic=False) def decorate(f): @functools.wraps(f) def callfunction(*args, **kwargs): function_name = f.__name__ args = list(args) more = [args.pop(0)] def cast_value(value_type, value): if value_type == bool: def cast_func(value): return value.lower() in ['1', 'true'] else: cast_func = value_type result = cast_func(value) return result if body_cls: if pecan.request.body: data = pecan.request.json obj = body_cls(**data) try: obj = obj.validate() except (jsonschema.ValidationError, ValueError) as e: raise exc.HTTPBadRequest( detail=e.message, comment=traceback.format_exc()) except Exception as e: raise exc.HTTPInternalServerError( detail=e.message, comment=traceback.format_exc()) # Set default pack if one is not provided for resource create if function_name == 'post' and not hasattr(obj, 'pack'): extra = { 'resource_api': obj, 'default_pack_name': DEFAULT_PACK_NAME } LOG.debug( 'Pack not provided in the body, setting a default pack name', extra=extra) setattr(obj, 'pack', DEFAULT_PACK_NAME) else: obj = None more.append(obj) if arg_types: # Cast and transform arguments based on the provided arg_types specification result_args, result_kwargs = get_controller_args_for_types( func=f, arg_types=arg_types, args=args, kwargs=kwargs) more = more + result_args kwargs.update(result_kwargs) args = tuple(more) + tuple(args) noop_codes = [ http_client.NOT_IMPLEMENTED, http_client.METHOD_NOT_ALLOWED, http_client.FORBIDDEN ] if status_code and status_code in noop_codes: pecan.response.status = status_code return json_encode(None) try: result = f(*args, **kwargs) except TypeError as e: e = get_exception_for_type_error(func=f, exc=e) raise e if status_code: pecan.response.status = status_code if content_type == 'application/json': if is_debugging_enabled(): indent = 4 else: indent = None return json_encode(result, indent=indent) else: return result pecan_json_decorate(callfunction) return callfunction return decorate