def action_register(self, rendered_action_data, action_data): """Register arbitrary items to the registry""" for key, value in remove_keys( rendered_action_data, 'loop_index' ).items(): data = remove_keys(action_data.copy(), 'loop_index') data['register'] = key self.add_to_registry(data, value)
def add_rendered_action(self, action_data, rendered_action_data): """Add rendered action to be written in validation file""" data = remove_keys( action_data, 'loop_index', '_values', 'log_message', deep=True ) def persist_factory(a_dict): """Generated data should be persisted in validation file""" for k, v in a_dict.items(): if not isinstance(v, dict): continue if 'from_factory' in v: a_dict[k] = rendered_action_data[k] else: persist_factory(v) persist_factory(data) if data.get('with_items'): data['loop_index'] = rendered_action_data['loop_index'] self.rendered_actions.append(data)
def save_rendered_data(result, filepath): """Save the result of rendering in a new file to be used for validation""" file_format = os.path.splitext(filepath)[-1] if file_format not in ('.json', '.yml', '.yaml', '.py'): raise ValueError("Invalid filename extension") data = OrderedDict({ 'input_filename': result.input_filename, 'config': dict(result.config), 'vars': dict(result.vars), 'actions': result.rendered_actions }) data['config']['mode'] = 'validate' data['config'] = remove_keys(data['config'], 'output') # should remove username, password, hostname ? data['config'] = remove_nones(data['config']) directory, filename = os.path.split(filepath) if not directory and data.get('input_filename'): directory = os.path.dirname(result.input_filename) if not filename.startswith('validation'): filename = "validation_{0}".format(filename) filepath = os.path.join(directory, filename) with open(filepath, 'w') as output: if file_format in ('.yml', '.yaml'): output.write(yaml.dump(data)) elif file_format == '.json': output.write(json.dumps(data, indent=4)) elif file_format == '.py': output.write("data = {0}".format(pformat(dict(data)))) result.logger.debug("Validation data saved in %s", filepath)
def build_search(self, rendered_action_data, action_data, context=None): """Build search data and returns a dict containing elements - data Dictionary of parsed rendered_action_data to be used to i nstantiate an object to searched without raw_query. - options if `search_options` are specified it is passed to `.search(**options)` - searchable Returns boolean True if model inherits from EntitySearchMixin, else alternative search must be implemented. if `search_query` is available in action_data it will be used instead of rendered_action_data. """ # if with_items, get current loop_index reference or 0 loop_index = rendered_action_data.get('loop_index', 0) if 'search_query' not in action_data: data = rendered_action_data else: search_data = action_data['search_query'] if isinstance(search_data, dict): items = action_data.get('with_items') if items and isinstance(items, list): items = [ Template(item).render(**self.context) for item in items ] elif items and isinstance(items, string_types): items = eval(items, None, self.context) if not isinstance(items, Sequence): raise AttributeError( "with_items must be sequence type") else: # as there is no with_items, a single item list will # ensure the addition of a single one. items = [None, ] new_context = {} new_context.update(self.context) new_context.update(context or {}) new_context['item'] = items[loop_index] new_context['loop_index'] = loop_index data = search_data.copy() self.render_action_data(data, new_context) elif isinstance(search_data, Sequence): data = { key: rendered_action_data[key] for key in search_data } else: raise ValueError("search_query bad formatted") model_name = action_data['model'] model = getattr(entities, model_name) data = remove_keys(data, 'loop_index') options = self.build_search_options(data, action_data) # force_raw = action_data.get( # 'force_raw', False # ) or action_data['model'].lower() in self.force_raw_search search = { 'data': data, 'options': options, 'searchable': issubclass(model, EntitySearchMixin) } return search
def execute(self, mode=None): """Iterates the entities property described in YAML file and parses its values, variables and substitutions depending on `mode` execute `populate` or `validate` """ mode = mode or self.mode self.logger.info("Starting in %s mode", mode) for action_data in self.actions: action = action_data.get('action', 'create') if action_data.get('when'): if not eval(action_data.get('when'), None, self.context): continue log_message = action_data['log_message'] = Template( action_data.get( 'log', action_data.get('register', 'executing...') ) ).render(**self.context) getattr(self.logger, action_data.get('level', 'info').lower())( '%s: %s', action.upper() if not ( mode == 'validate' and action == 'create' ) else "VALIDATE", log_message ) actions_list = self.render(action_data, action) for rendered_action_data in actions_list: if action not in self.crud_actions: # find the method named as action_name action_name = "action_{0}".format(action.lower()) getattr(self, action_name)( rendered_action_data, action_data ) self.context.update(self.registry) # check if it is better to do that in method self.add_rendered_action(action_data, rendered_action_data) # execute above method and continue to next item continue # Executed only for crud_actions model_name = action_data['model'].lower() method = getattr( self, '{0}_{1}'.format(mode, model_name), None ) search = self.build_search( rendered_action_data, action_data ) entity_data = remove_keys( rendered_action_data, 'loop_index' ) if method: method(entity_data, action_data, search, action) else: # execute self.populate or self.validate getattr(self, mode)( entity_data, action_data, search, action ) # ensure context is updated with latest created entities self.context.update(self.registry) self.add_rendered_action( action_data, rendered_action_data )