ComplianceAsCode/content

View on GitHub
ssg/components.py

Summary

Maintainability
Test Coverage
"""
Common functions for processing Components in SSG
"""

from __future__ import print_function

from collections import defaultdict
import os

import ssg.yaml


def load(components_dir):
    """
    Load components from a specified directory.

    Args:
        components_dir (str): The directory path containing component files.

    Returns:
        dict: A dictionary where the keys are component names and the values are component objects.
    """
    components = {}
    for component_filename in os.listdir(components_dir):
        components_filepath = os.path.join(components_dir, component_filename)
        component = Component(components_filepath)
        components[component.name] = component
    return components


def _reverse_mapping(components, attribute):
    """
    Creates a reverse mapping from the given attribute of components to the component names.

    Args:
        components (dict): A dictionary where keys are component names and values are component objects.
        attribute (str): The attribute of the component object to be used for creating the reverse mapping.

    Returns:
        defaultdict: A dictionary where keys are attribute values and values are lists of
                     component names that have that attribute value.
    """
    mapping = defaultdict(list)
    for component in components.values():
        for item in getattr(component, attribute):
            mapping[item].append(component.name)
    return mapping


def package_component_mapping(components):
    """
    Maps the given components to their respective packages.

    Args:
        components (dict): A dictionary where keys are component names and values are component details.

    Returns:
        dict: A dictionary where keys are package names and values are lists of components
              associated to those packages.
    """
    return _reverse_mapping(components, "packages")


def template_component_mapping(components):
    """
    Maps the given components to their corresponding templates.

    Args:
        components (dict): A dictionary where keys are component names and values are component details.

    Returns:
        dict: A dictionary where keys are template names and values are lists of components that
              use those templates.
    """
    return _reverse_mapping(components, "templates")


def group_component_mapping(components):
    """
    Groups components by their associated groups.

    Args:
        components (dict): A dictionary where keys are component names and values are dictionaries
                           containing component attributes, including a "groups" key.

    Returns:
        dict: A dictionary where keys are group names and values are lists of component names
              that belong to each group.
    """
    return _reverse_mapping(components, "groups")


def rule_component_mapping(components):
    """
    Maps the given components to their corresponding rules.

    Args:
        components (dict): A dictionary where keys are component names and values are component details.

    Returns:
        dict: A dictionary where keys are rule names and values are the corresponding components.
    """
    return _reverse_mapping(components, "rules")


class Component:
    """
    A class to represent a component.
    With regards to the content, a component usually represents a piece of software.

    Attributes:
        name (str): The name of the component.
        rules (list): A list of rules associated with the component.
        packages (list): A list of packages associated with the component.
        templates (list, optional): A list of templates associated with the component. Defaults to an empty list.
        groups (list, optional): A list of groups associated with the component. Defaults to an empty list.
        changelog (list, optional): A list of changelog entries for the component. Defaults to an empty list.
    """
    def __init__(self, filepath):
        yaml_data = ssg.yaml.open_raw(filepath)
        self.name = yaml_data["name"]
        self.rules = yaml_data["rules"]
        self.packages = yaml_data["packages"]
        self.templates = yaml_data.get("templates", [])
        self.groups = yaml_data.get("groups", [])
        self.changelog = yaml_data.get("changelog", [])


def get_rule_to_components_mapping(components):
    """
    Generates a mapping from rule IDs to component names.

    Args:
        components (dict): A dictionary where the keys are component names and the values are component objects.
                           Each component object is expected to have a 'rules' attribute (a list of rule IDs)
                           and a 'name' attribute (the name of the component).

    Returns:
        dict: A dictionary where the keys are rule IDs and the values are lists of component names
              that include the corresponding rule ID.
    """
    rule_to_components = defaultdict(list)
    for component in components.values():
        for rule_id in component.rules:
            rule_to_components[rule_id].append(component.name)
    return rule_to_components