def extract_value_from_vertex(self, key_path: List[str], attributes: Dict[str, Any]) -> Any: for i, _ in enumerate(key_path): key = join_trimmed_strings(char_to_join=".", str_lst=key_path, num_to_trim=i) value = attributes.get(key, None) if value is not None: return value reversed_key_path = deepcopy(key_path) reversed_key_path.reverse() for i, _ in enumerate(reversed_key_path): key = join_trimmed_strings(char_to_join=".", str_lst=reversed_key_path, num_to_trim=i) value = attributes.get(key, None) if value is not None: return value if attributes.get(CustomAttributes.BLOCK_TYPE) in [ BlockType.VARIABLE.value, BlockType.TF_VARIABLE.value ]: default_val = attributes.get("default") value = None if isinstance(default_val, dict): value = self.extract_value_from_vertex(key_path, default_val) return default_val if not value else value if attributes.get( CustomAttributes.BLOCK_TYPE) == BlockType.OUTPUT.value: return attributes.get("value") return None
def update_attribute(self, attribute_key: str, attribute_value: Any, change_origin_id: int, previous_breadcrumbs: List[int]) -> None: if not previous_breadcrumbs or previous_breadcrumbs[ -1] != change_origin_id: previous_breadcrumbs.append(change_origin_id) self.update_inner_attribute(attribute_key, self.attributes, attribute_value) attribute_key_parts = attribute_key.split(".") if len(attribute_key_parts) == 1: self.attributes[attribute_key] = attribute_value self.changed_attributes[attribute_key] = previous_breadcrumbs return for i in range(len(attribute_key_parts)): key = join_trimmed_strings(char_to_join=".", str_lst=attribute_key_parts, num_to_trim=i) if key.find(".") > -1: self.attributes[key] = attribute_value attribute_value = { attribute_key_parts[len(attribute_key_parts) - 1 - i]: attribute_value } self.changed_attributes[key] = previous_breadcrumbs
def extract_value_from_vertex(self, key_path, attributes): for i in range(len(key_path)): key = join_trimmed_strings(char_to_join=".", str_lst=key_path, num_to_trim=i) value = attributes.get(key, None) if value is not None: return value reversed_key_path = deepcopy(key_path) reversed_key_path.reverse() for i in range(len(reversed_key_path)): key = join_trimmed_strings(char_to_join=".", str_lst=reversed_key_path, num_to_trim=i) value = attributes.get(key, None) if value is not None: return value if attributes.get(CustomAttributes.BLOCK_TYPE) in [BlockType.VARIABLE.value, BlockType.TF_VARIABLE.value]: default_val = attributes.get('default') value = None if isinstance(default_val, dict): value = self.extract_value_from_vertex(key_path, default_val) return default_val if not value else value if attributes.get(CustomAttributes.BLOCK_TYPE) == BlockType.OUTPUT.value: return attributes.get('value') return None
def _build_edges(self): logging.info('Creating edges') self.get_module_vertices_mapping() aliases = self._get_aliases() for origin_node_index, vertex in enumerate(self.vertices): for attribute_key in vertex.attributes: if attribute_key in reserved_attribute_names or attribute_has_nested_attributes( attribute_key, vertex.attributes): continue referenced_vertices = get_referenced_vertices_in_value( value=vertex.attributes[attribute_key], aliases=aliases, resources_types=self.get_resources_types_in_graph()) for vertex_reference in referenced_vertices: # for certain blocks such as data and resource, the block name is composed from several parts. # the purpose of the loop is to avoid not finding the node if the name has several parts sub_values = [ remove_index_pattern_from_str(sub_value) for sub_value in vertex_reference.sub_parts ] for i in range(len(sub_values)): reference_name = join_trimmed_strings( char_to_join=".", str_lst=sub_values, num_to_trim=i) if vertex.module_dependency: dest_node_index = self._find_vertex_index_relative_to_path( vertex_reference.block_type, reference_name, vertex.path, vertex.module_dependency) if dest_node_index == -1: dest_node_index = self._find_vertex_index_relative_to_path( vertex_reference.block_type, reference_name, vertex.path, vertex.path) else: dest_node_index = self._find_vertex_index_relative_to_path( vertex_reference.block_type, reference_name, vertex.path, vertex.module_dependency) if dest_node_index > -1 and origin_node_index > -1: if vertex_reference.block_type == BlockType.MODULE: try: self._connect_module( sub_values, attribute_key, self.vertices[dest_node_index], origin_node_index) except Exception as e: logging.warning( f'Module {self.vertices[dest_node_index]} does not have source attribute, skipping' ) logging.warning(e, stack_info=True) else: self._create_edge(origin_node_index, dest_node_index, attribute_key) break if vertex.block_type == BlockType.MODULE: target_path = vertex.path if vertex.module_dependency != '': target_path = unify_dependency_path( [vertex.module_dependency, vertex.path]) target_variables = list( filter( lambda v: self.vertices[v].module_dependency == target_path, self.vertices_by_block_type.get( BlockType.VARIABLE, {}))) for attribute, value in vertex.attributes.items(): if attribute in MODULE_RESERVED_ATTRIBUTES: continue target_variable = None for v in target_variables: if self.vertices[v].name == attribute: target_variable = v break if target_variable is not None: self._create_edge(target_variable, origin_node_index, 'default') elif vertex.block_type == BlockType.TF_VARIABLE: if vertex.module_dependency != '': target_path = unify_dependency_path( [vertex.module_dependency, vertex.path]) # Assuming the tfvars file is in the same directory as the variables file (best practice) target_variables = list( filter( lambda v: os.path.dirname(self.vertices[v].path) == os. path.dirname(vertex.path), self.vertices_block_name_map.get( BlockType.VARIABLE, {}).get(vertex.name, []))) if len(target_variables) == 1: self._create_edge(target_variables[0], origin_node_index, 'default')