def _update_relative_paths()

in samcli/commands/_utils/template.py [0:0]


def _update_relative_paths(template_dict, original_root, new_root):
    """
    SAM/CloudFormation template can contain certain properties whose value is a relative path to a local file/folder.
    This path is usually relative to the template's location. If the template is being moved from original location
    ``original_root`` to new location ``new_root``, use this method to update these paths to be
    relative to ``new_root``.

    After this method is complete, it is safe to write the template to ``new_root`` without
    breaking any relative paths.

    This methods updates resource properties supported by ``aws cloudformation package`` command:
    https://docs.aws.amazon.com/cli/latest/reference/cloudformation/package.html

    If a property is either an absolute path or a S3 URI, this method will not update them.


    Parameters
    ----------
    template_dict : dict
        Dictionary containing template contents. This dictionary will be updated & written to ``dest`` location.

    original_root : str
        Path to the directory where all paths were originally set relative to. This is usually the directory
        containing the template originally

    new_root : str
        Path to the new directory that all paths set relative to after this method completes.

    Returns
    -------
    Updated dictionary

    """

    for resource_type, properties in template_dict.get("Metadata", {}).items():
        if resource_type not in METADATA_WITH_LOCAL_PATHS:
            # Unknown resource. Skipping
            continue

        for path_prop_name in METADATA_WITH_LOCAL_PATHS[resource_type]:
            path = properties.get(path_prop_name)

            updated_path = _resolve_relative_to(path, original_root, new_root)
            if not updated_path:
                # This path does not need to get updated
                continue

            properties[path_prop_name] = updated_path

    for _, resource in template_dict.get("Resources", {}).items():
        resource_type = resource.get("Type")

        if resource_type not in RESOURCES_WITH_LOCAL_PATHS:
            # Unknown resource. Skipping
            continue

        for path_prop_name in RESOURCES_WITH_LOCAL_PATHS[resource_type]:
            properties = resource.get("Properties", {})

            if (
                resource_type in [AWS_SERVERLESS_FUNCTION, AWS_LAMBDA_FUNCTION]
                and properties.get("PackageType", ZIP) == IMAGE
            ):
                if not properties.get("ImageUri"):
                    continue
                resolved_image_archive_path = _resolve_relative_to(properties.get("ImageUri"), original_root, new_root)
                if not resolved_image_archive_path or not pathlib.Path(resolved_image_archive_path).is_file():
                    continue

            # SAM GraphQLApi has many instances of CODE_ARTIFACT_PROPERTY and all of them must be updated
            if resource_type == AWS_SERVERLESS_GRAPHQLAPI and path_prop_name == graphql_api.CODE_ARTIFACT_PROPERTY:
                # to be able to set different nested properties to S3 uri, paths are necessary
                # jmespath doesn't provide that functionality, thus custom implementation
                paths_values = graphql_api.find_all_paths_and_values(path_prop_name, properties)
                for property_path, property_value in paths_values:
                    updated_path = _resolve_relative_to(property_value, original_root, new_root)
                    if not updated_path:
                        # This path does not need to get updated
                        continue
                    set_value_from_jmespath(properties, property_path, updated_path)

            path = jmespath.search(path_prop_name, properties)
            updated_path = _resolve_relative_to(path, original_root, new_root)

            if not updated_path:
                # This path does not need to get updated
                continue

            set_value_from_jmespath(properties, path_prop_name, updated_path)

        metadata = resource.get("Metadata", {})
        if ASSET_PATH_METADATA_KEY in metadata:
            path = metadata.get(ASSET_PATH_METADATA_KEY, "")
            updated_path = _resolve_relative_to(path, original_root, new_root)
            if not updated_path:
                # This path does not need to get updated
                continue
            metadata[ASSET_PATH_METADATA_KEY] = updated_path

    # AWS::Includes can be anywhere within the template dictionary. Hence we need to recurse through the
    # dictionary in a separate method to find and update relative paths in there
    template_dict = _update_aws_include_relative_path(template_dict, original_root, new_root)

    return template_dict