roster/src/variable_substitution.py

126 lines
4.2 KiB
Python

# variable_substitution.py
#
# Copyright 2025 Pavel Baksy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
import re
from typing import Dict, List, Tuple, Set
from .models import HttpRequest, Environment
class VariableSubstitution:
"""Handles variable substitution with {{variable_name}} syntax."""
# Pattern to match {{variable_name}} where variable_name is alphanumeric + underscore
VARIABLE_PATTERN = re.compile(r'\{\{(\w+)\}\}')
@staticmethod
def find_variables(text: str) -> List[str]:
"""
Extract all {{variable_name}} references from text.
Args:
text: The text to search for variable references
Returns:
List of variable names found (without the {{ }} delimiters)
"""
if not text:
return []
return VariableSubstitution.VARIABLE_PATTERN.findall(text)
@staticmethod
def substitute(text: str, variables: Dict[str, str]) -> Tuple[str, List[str]]:
"""
Replace {{variable_name}} with values from variables dict.
Args:
text: The text containing variable placeholders
variables: Dictionary mapping variable names to their values
Returns:
Tuple of (substituted_text, list_of_undefined_variables)
"""
if not text:
return text, []
undefined_vars = []
def replace_var(match):
var_name = match.group(1)
if var_name in variables:
value = variables[var_name]
# Replace with value or empty string if value is None/empty
return value if value else ""
else:
# Variable not defined - track it and replace with empty string
undefined_vars.append(var_name)
return ""
substituted = VariableSubstitution.VARIABLE_PATTERN.sub(replace_var, text)
return substituted, undefined_vars
@staticmethod
def substitute_request(request: HttpRequest, environment: Environment) -> Tuple[HttpRequest, Set[str]]:
"""
Substitute variables in all request fields (URL, headers, body).
Args:
request: The HTTP request with variable placeholders
environment: The environment containing variable values
Returns:
Tuple of (new_request_with_substitutions, set_of_undefined_variables)
"""
all_undefined = set()
# Substitute URL
new_url, url_undefined = VariableSubstitution.substitute(
request.url, environment.variables
)
all_undefined.update(url_undefined)
# Substitute headers (both keys and values)
new_headers = {}
for key, value in request.headers.items():
new_key, key_undefined = VariableSubstitution.substitute(
key, environment.variables
)
new_value, value_undefined = VariableSubstitution.substitute(
value, environment.variables
)
all_undefined.update(key_undefined)
all_undefined.update(value_undefined)
new_headers[new_key] = new_value
# Substitute body
new_body, body_undefined = VariableSubstitution.substitute(
request.body, environment.variables
)
all_undefined.update(body_undefined)
# Create new HttpRequest with substituted values
new_request = HttpRequest(
method=request.method,
url=new_url,
headers=new_headers,
body=new_body,
syntax=request.syntax
)
return new_request, all_undefined