def validate_action_params(parameters, dereferencer, app_name, action_name, action_func, event=''): seen = set() for parameter in parameters: parameter = deref(parameter, dereferencer) name = parameter['name'] if name in seen: raise InvalidApi('Duplicate parameter {0} in api for {1} ' 'for action {2}'.format(name, app_name, action_name)) seen.add(name) if hasattr(action_func, '__arg_names'): method_params = list(action_func.__arg_names) else: method_params = get_function_arg_names(action_func) if method_params and method_params[0] == 'self': method_params.pop(0) if event: method_params.pop(0) if action_func.__event_name != event: logger.warning('In app {0} action {1}, event documented {2} does not match ' 'event specified {3}'.format(app_name, action_name, event, action_func.__event_name)) if not seen == set(method_params): only_in_api = seen - set(method_params) only_in_definition = set(method_params) - seen message = ('Discrepancy between defined parameters in API and in method definition ' 'for app {0} action {1}.'.format(app_name, action_name)) if only_in_api: message += ' Only in API: {0}.'.format(only_in_api) if only_in_definition: message += ' Only in definition: {0}'.format(only_in_definition) raise InvalidApi(message)
def validate_data_in_param(params, data_in_param_name, message_prefix): data_in_param = next((param for param in params if param['name'] == data_in_param_name), None) if data_in_param is None: raise InvalidApi( '{0} has a dataIn param {1} ' 'for which it does not have a ' 'corresponding parameter'.format(message_prefix, data_in_param_name)) elif not data_in_param.get('required', False): raise InvalidApi( '{0} has a dataIn param {1} which is not marked as required in the api. ' 'Add "required: true" to parameter specification for {1}'.format(message_prefix, data_in_param_name))
def _event(func): arg_names = get_function_arg_names(func) if len(arg_names) < 2: raise InvalidApi('Event action has too few parameters. ' 'There must be a "self" and a second parameter to receive data from the event.') @wraps(func) def wrapper(*args, **kwargs): result = AsyncResult() @event_.connect def send(data): if len(kwargs) > 0: result.set(func(args[0], data, **kwargs)) else: result.set(func(args[0], data)) try: result = result.get(timeout=timeout) except Timeout: result = 'Getting event {0} timed out at {1} seconds'.format(event_.name, timeout), 'EventTimedOut' event_.disconnect(send) return format_result(result) tag(wrapper, 'action') wrapper.__arg_names = arg_names wrapper.__event_name = event_.name return wrapper
def _event(func): arg_names = get_function_arg_names(func) if not arg_names or (arg_names[0] == 'self' and len(arg_names) < 2): raise InvalidApi('Event action has too few parameters. ' 'There must be at least one parameter to receive data from the event.') @wraps(func) def wrapper(*args, **kwargs): result = [('Getting event {0} timed out at {1} seconds'.format(event_.name, timeout), 'EventTimedOut')] await_result_condition = Condition() @event_.connect def send(data): await_result_condition.acquire() if len(kwargs) > 0: result.append(func(args[0], data, **kwargs)) else: result.append(func(args[0], data)) await_result_condition.notify() await_result_condition.release() await_result_condition.acquire() while not len(result) >= 2: await_result_condition.wait(timeout=timeout) break await_result_condition.release() event_.disconnect(send) return format_result(result[-1]) tag(wrapper, 'action') wrapper.__arg_names = arg_names wrapper.__event_name = event_.name return wrapper
def validate_actions(actions, dereferencer, app_name): from apps import get_all_actions_for_app, get_app_action defined_actions = get_all_actions_for_app(app_name) seen = set() for action_name, action in actions.items(): if action['run'] not in defined_actions: raise InvalidApi('Action {0} has "run" property {1} ' 'which is not defined in App {2}'.format( action_name, action['run'], app_name)) action = dereferencer(action) action_params = dereferencer(action.get('parameters', [])) event = action.get('event', '') if action_params: validate_action_params(action_params, dereferencer, app_name, action_name, get_app_action(app_name, action['run']), event=event) validate_app_action_return_codes(action.get('returns', []), app_name, action_name) seen.add(action['run']) if seen != set(defined_actions.keys()): logger.warning( 'App {0} has defined the following actions which do not have a corresponding API: ' '{1}'.format(app_name, (set(defined_actions.keys()) - seen)))
def validate_condition_transform_params(spec, app_name, action_type, defined_actions, dereferencer): from apps import get_transform, get_condition seen = set() for action_name, action in spec.items(): action = dereferencer(action) action_params = dereferencer(action.get('parameters', [])) if action['run'] not in defined_actions: raise InvalidApi('{0} action {1} has a "run" param {2} ' 'which is not defined'.format( action_type, action_name, action['run'])) data_in_param_name = action['data_in'] validate_data_in_param( action_params, data_in_param_name, '{0} action {1}'.format(action_type, action_name)) function = get_condition( app_name, action['run']) if action_type == 'Condition' else get_transform( app_name, action['run']) validate_action_params(action_params, dereferencer, action_type, action_name, function) seen.add(action['run']) if seen != set(defined_actions): logger.warning( 'Global {0}s have defined the following actions which do not have a corresponding API: ' '{1}'.format(action_type.lower(), (set(defined_actions) - seen)))
def validate_app_action_return_codes(return_codes, app, action): reserved = [ return_code for return_code in return_codes if return_code in reserved_return_codes ] if reserved: message = 'App {0} action {1} has return codes {2} which are reserved'.format( app, action, reserved) logger.error(message) raise InvalidApi(message)
def dereference(reference, spec, seen, message_prefix): if reference in seen: raise InvalidApi( '{0}: Improper reference path "{1}". Circular reference detected'. format(message_prefix, reference)) seen.add(reference) reference_path = reference.split('/') if not reference_path or not reference_path[0] == '#' or len( reference_path) == 1: raise InvalidApi('{0}: Improperly formatted reference path "{1}". ' 'Proper format is "#/path/to/reference.'.format( message_prefix, reference)) working_schema = spec for path_element in reference_path[1:]: try: working_schema = working_schema[path_element] except KeyError: raise InvalidApi('{0}: Improper reference path "{1}". ' 'Path element "{2}" not found'.format( message_prefix, reference, path_element)) return working_schema
def convert_json(spec, param_in, message_prefix): if 'type' in spec: parameter_type = spec['type'] if parameter_type in TYPE_MAP: try: return convert_primitive_type(param_in, parameter_type) except ValueError: message = ( '{0} has invalid input. ' 'Input {1} could not be converted to type {2}'.format(message_prefix, param_in, parameter_type)) logger.error(message) raise InvalidInput(message) elif parameter_type == 'array': return convert_array(spec, param_in, message_prefix) elif parameter_type == 'object': return __convert_json(spec, param_in, message_prefix) else: raise InvalidApi('{0} has invalid api'.format(message_prefix)) elif 'schema' in spec: return convert_json(spec['schema'], param_in, message_prefix) else: raise InvalidApi('{0} has invalid api'.format(message_prefix))
def validate_definition(definition, dereferencer, definition_name=None): definition = dereferencer(definition) if 'allOf' in definition: for inner_definition in definition['allOf']: validate_definition(inner_definition, dereferencer) else: required = definition.get('required', []) properties = definition.get('properties', {}).keys() extra_properties = list(set(required) - set(properties)) if extra_properties: raise InvalidApi("Required list of properties for definition " "{0} not defined: {1}".format(definition_name, extra_properties))
def validate_flagfilter_params(spec, action_type, defined_actions, dereferencer): seen = set() for action_name, action in spec.items(): action = dereferencer(action) action_params = dereferencer(action.get('parameters', [])) if action['run'] not in defined_actions: raise InvalidApi('{0} action {1} has a "run" param {2} ' 'which is not defined'.format(action_type, action_name, action['run'])) data_in_param_name = action['dataIn'] validate_data_in_param(action_params, data_in_param_name, '{0} action {1}'.format(action_type, action_name)) validate_action_params(action_params, dereferencer, action_type, action_name, defined_actions[action['run']]) seen.add(action['run']) if seen != set(defined_actions.keys()): logger.warning('Global {0}s have defined the following actions which do not have a corresponding API: ' '{1}'.format(action_type.lower(), (set(defined_actions.keys()) - seen)))