Skip to content
Draft
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
3 changes: 3 additions & 0 deletions airbyte_cdk/sources/declarative/interpolation/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ def eval(
return self._literal_eval(self._eval(default, context), valid_types)

def _literal_eval(self, result: Optional[str], valid_types: Optional[Tuple[Type[Any]]]) -> Any:
if valid_types == (str,):
return result

try:
evaluated = ast.literal_eval(result) # type: ignore # result may be None; on error we return it unchanged
except (ValueError, SyntaxError, TypeError):
Expand Down
47 changes: 47 additions & 0 deletions unit_tests/sources/declarative/interpolation/test_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,53 @@ def test_literals(s, value):
assert val == value


@pytest.mark.parametrize(
"value",
[
pytest.param('"nike shoes"', id="double-quoted-string"),
pytest.param("'abc'", id="single-quoted-string"),
pytest.param("plain", id="plain-string"),
pytest.param("123", id="integer-looking-string"),
pytest.param("1.5", id="float-looking-string"),
pytest.param("[1, 2]", id="list-looking-string"),
pytest.param("True", id="boolean-looking-string"),
],
)
def test_eval_with_str_valid_type_returns_rendered_string_as_is(value):
result = JinjaInterpolation().eval(
"{{ record['k'] }}", config={}, valid_types=(str,), record={"k": value}
)

assert result == value
assert isinstance(result, str)


def test_eval_with_str_valid_type_returns_default_as_is():
result = JinjaInterpolation().eval(
"{{ record['missing'] }}",
config={},
default='"quoted default"',
valid_types=(str,),
record={},
)

assert result == '"quoted default"'


def test_eval_without_valid_types_still_literal_evaluates():
result = JinjaInterpolation().eval("{{ record['k'] }}", {}, record={"k": '"abc"'})

assert result == "abc"


def test_eval_with_int_valid_type_still_literal_evaluates():
result = JinjaInterpolation().eval(
"{{ record['k'] }}", {}, valid_types=(int,), record={"k": "123"}
)

assert result == 123


@pytest.mark.parametrize(
"context, input_string, expected_value",
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,13 @@ def test_component_mapping_conditions(manifest, config, expected_conditional_par
"value_type": "string",
"create_or_update": True,
},
{
"type": "ComponentMappingDefinition",
"field_path": ["retriever", "requester", "$parameters", "quoted_string"],
"value": '"quoted resolver value"',
"value_type": "string",
"create_or_update": True,
},
{
"type": "ComponentMappingDefinition",
"field_path": ["retriever", "requester", "$parameters", "json_parsed"],
Expand Down Expand Up @@ -480,6 +487,10 @@ def test_value_type_str_avoids_yaml_parsing():
assert isinstance(params["json_string"], str)
assert params["json_string"] == "[1, 2]"

assert "quoted_string" in params
assert isinstance(params["quoted_string"], str)
assert params["quoted_string"] == '"quoted resolver value"'

# No value_type -> YAML parsed
assert "as_yaml" in params
assert isinstance(params["as_yaml"], bool)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@
{"k": "v", "path": "1"},
id="add an expression evaluated as a string using the value_type field",
),
pytest.param(
{"keys": ['"nike shoes"']},
[(["path"], "{{ record['keys'][0] }}")],
str,
"",
{},
{"keys": ['"nike shoes"'], "path": '"nike shoes"'},
id="keep double-quoted rendered expression as a string",
),
pytest.param(
{"keys": ["'abc'"]},
[(["path"], "{{ record['keys'][0] }}")],
str,
"",
{},
{"keys": ["'abc'"], "path": "'abc'"},
id="keep single-quoted rendered expression as a string",
),
pytest.param(
{"k": "v"},
[(["path"], "static_value"), (["path2"], "static_value2")],
Expand Down
Loading