Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions src/tirith/core/policy_parameterization.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,42 @@ def _replace_vars_in_dict(dictionary: dict, var_dict: dict, not_found_vars: List
"""
Replace the variables in the dictionary with the values from the var_dict

:param dictionary: The dictionary to replace the variables in
:param var_pattern: The pattern to match the variables
:param var_dict: The dictionary containing the variables
:param dictionary: The dictionary to replace the variables in
:param var_dict: The dictionary containing the variables
:param not_found_vars: The list to store the variables that are not found in
"""
for key, value in dictionary.items():
if not isinstance(value, str):
continue
_replace_var_in_dict(dictionary, key, var_dict, not_found_vars)
if isinstance(value, str):
_replace_var_in_dict(dictionary, key, var_dict, not_found_vars)
elif isinstance(value, dict):
_replace_vars_in_dict(value, var_dict, not_found_vars)
elif isinstance(value, list):
_replace_vars_in_list(value, var_dict, not_found_vars)


def _replace_vars_in_list(target_list: list, var_dict: dict, not_found_vars: List[str]):
"""
Replace the variables in the list items with the values from the var_dict

:param target_list: The list to replace the variables in
:param var_dict: The dictionary containing the variables
:param not_found_vars: The list to store the variables that are not found in
"""
for index, item in enumerate(target_list):
if isinstance(item, str):
match = _VAR_PATTERN.match(item)
if not match:
continue
var_name = match.group(1)
var_value = pydash.get(var_dict, var_name, default=_VariableNotFound)
if var_value is _VariableNotFound:
not_found_vars.append(var_name)
continue
target_list[index] = var_value
elif isinstance(item, dict):
_replace_vars_in_dict(item, var_dict, not_found_vars)
elif isinstance(item, list):
_replace_vars_in_list(item, var_dict, not_found_vars)


def _replace_var_in_dict(dictionary: dict, key: str, var_dict: dict, not_found_vars: list):
Expand Down
66 changes: 66 additions & 0 deletions tests/core/test_policy_parameterization.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,72 @@ def test_same_policy_reused_with_different_vars():
assert second["evaluators"][0]["provider_args"]["key_path"] == "second.path"


def test_nested_dict_and_list_substitution():
"""Ensure substitution recurses into nested dicts and lists."""
policy = {
"meta": {"version": ""},
"evaluators": [
{
"id": "check0",
"provider_args": {
"operation_type": "get_value",
"nested_options": {"path": "{{var.my_path}}"},
},
"condition": {
"type": "Equals",
"value": {
"env": "{{var.env}}",
"allowed_regions": ["us-east-1", "{{var.extra_region}}"],
"deep": [{"key": "{{var.deep_key}}"}],
},
},
}
],
"eval_expression": "check0",
}

var_dict = {
"my_path": "config.path",
"env": "production",
"extra_region": "eu-central-1",
"deep_key": "deep_secret",
}

replaced, not_found = get_policy_with_vars_replaced(policy, var_dict)

assert not_found == []
assert replaced["evaluators"][0]["provider_args"]["nested_options"]["path"] == "config.path"
assert replaced["evaluators"][0]["condition"]["value"]["env"] == "production"
assert replaced["evaluators"][0]["condition"]["value"]["allowed_regions"] == ["us-east-1", "eu-central-1"]
assert replaced["evaluators"][0]["condition"]["value"]["deep"][0]["key"] == "deep_secret"


def test_nested_missing_variables_reported():
"""Ensure missing variables inside nested dicts and lists are added to not_found_vars."""
policy = {
"meta": {"version": ""},
"evaluators": [
{
"id": "check0",
"provider_args": {},
"condition": {
"type": "Equals",
"value": {
"nested_dict": {"missing_val": "{{var.missing_in_dict}}"},
"nested_list": ["ok", "{{var.missing_in_list}}"],
},
},
}
],
"eval_expression": "check0",
}

replaced, not_found = get_policy_with_vars_replaced(policy, {})

assert "missing_in_dict" in not_found
assert "missing_in_list" in not_found


# TODO: Create testcases for:
# - test inline vars precendece over var files
# - test undefined vars
Expand Down
Loading