From 8a46c088f50cb7b4358f66633773da077156025d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:46:43 +0000 Subject: [PATCH 1/2] fix(declarative): preserve static strings in request_body_json instead of coercing them Co-Authored-By: syed.khadeer@airbyte.io --- .../interpolated_nested_mapping.py | 4 ++ .../test_interpolated_nested_mapping.py | 55 +++++++++++++++++++ ...t_interpolated_request_options_provider.py | 9 +++ 3 files changed, 68 insertions(+) diff --git a/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py b/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py index a053551be7..68bcfe50a0 100644 --- a/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +++ b/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py @@ -39,6 +39,10 @@ def _eval( ) -> Any: # Recursively interpolate dictionaries and lists if isinstance(value, str): + # Static values are returned verbatim so that interpolation does not coerce them to + # another type, e.g. "478" becoming the integer 478. + if "{{" not in value and "{%" not in value: + return value return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs) elif isinstance(value, dict): interpolated_dict = { diff --git a/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py b/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py index 1d5b4b92bf..40bb7fd38a 100644 --- a/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py +++ b/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py @@ -52,3 +52,58 @@ def test(test_name, path, expected_value): interpolated = mapping.eval(config, **{"kwargs": kwargs}) assert dpath.get(interpolated, path) == expected_value + + +@pytest.mark.parametrize( + "mapping, path, expected_value", + [ + pytest.param( + {"clientId": "478"}, + "clientId", + "478", + id="static_digit_only_string", + ), + pytest.param( + {"value": "0012"}, + "value", + "0012", + id="static_leading_zero_string", + ), + pytest.param( + {"value": "None"}, + "value", + "None", + id="static_none_string", + ), + pytest.param( + {"478": "value"}, + "478", + "value", + id="static_digit_only_key", + ), + pytest.param( + {"nested": {"clientId": "478"}}, + "nested/clientId", + "478", + id="static_string_nested_in_dict", + ), + pytest.param( + {"nested": {"values": ["478", "0012", "None"]}}, + "nested/values", + ["478", "0012", "None"], + id="static_strings_nested_in_list", + ), + pytest.param( + {"value": "{{ 1 + 1 }}"}, + "value", + 2, + id="jinja_value_still_coerces", + ), + ], +) +def test_static_strings_are_preserved_and_jinja_values_are_interpolated( + mapping, path, expected_value +): + interpolated = InterpolatedNestedMapping(mapping=mapping, parameters={}).eval({}) + + assert dpath.get(interpolated, path) == expected_value diff --git a/unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py b/unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py index 293eb427fb..eb3b2611b2 100644 --- a/unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py +++ b/unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py @@ -141,6 +141,15 @@ def test_interpolated_request_json(test_name, input_request_json, expected_reque @pytest.mark.parametrize( "test_name, input_request_json, expected_request_json", [ + pytest.param( + "test_static_digit_only_string", + RequestBodyJsonObject( + type="RequestBodyJsonObject", + value={"clientId": "478"}, + ), + {"clientId": "478"}, + id="static_digit_only_string", + ), ( "test_static_json", RequestBodyJsonObject( From ac74d0a013298586e43d678ea7e823ebe59b64a1 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 03:51:12 +0000 Subject: [PATCH 2/2] fix: preserve structured nested literals Co-Authored-By: syed.khadeer@airbyte.io --- .../interpolated_nested_mapping.py | 9 ++++++--- .../test_interpolated_nested_mapping.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py b/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py index 68bcfe50a0..e60071c610 100644 --- a/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +++ b/airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py @@ -39,10 +39,13 @@ def _eval( ) -> Any: # Recursively interpolate dictionaries and lists if isinstance(value, str): - # Static values are returned verbatim so that interpolation does not coerce them to - # another type, e.g. "478" becoming the integer 478. if "{{" not in value and "{%" not in value: - return value + # Preserve structured static literals for request bodies, but avoid coercing scalar + # values such as "478" or "None" through literal evaluation. + evaluated = self._interpolation.eval( + value, config, parameters=self._parameters, **kwargs + ) + return evaluated if isinstance(evaluated, (dict, list)) else value return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs) elif isinstance(value, dict): interpolated_dict = { diff --git a/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py b/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py index 40bb7fd38a..9f7f47ee92 100644 --- a/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py +++ b/unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py @@ -75,6 +75,24 @@ def test(test_name, path, expected_value): "None", id="static_none_string", ), + pytest.param( + {"value": '{"field": "updated_at", "order": "ascending"}'}, + "value", + {"field": "updated_at", "order": "ascending"}, + id="static_structured_dict", + ), + pytest.param( + {"value": "[1, 2, 3]"}, + "value", + [1, 2, 3], + id="static_structured_list", + ), + pytest.param( + {"sort": '{"field": "updated_at", "order": "ascending"}'}, + "sort", + {"field": "updated_at", "order": "ascending"}, + id="static_intercom_sort_json", + ), pytest.param( {"478": "value"}, "478",