Skip to content

Commit 7569db0

Browse files
darynaishchenkooctavia-squidington-iii
and
octavia-squidington-iii
authored
feat(low-code): added flatten_lists option to FlattenFields transformation (#206)
Co-authored-by: octavia-squidington-iii <[email protected]>
1 parent 0e7802a commit 7569db0

File tree

5 files changed

+80
-11
lines changed

5 files changed

+80
-11
lines changed

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,11 @@ definitions:
19041904
type:
19051905
type: string
19061906
enum: [FlattenFields]
1907+
flatten_lists:
1908+
title: Flatten Lists
1909+
description: Whether to flatten lists or leave it as is. Default is True.
1910+
type: boolean
1911+
default: true
19071912
$parameters:
19081913
type: object
19091914
additionalProperties: true

airbyte_cdk/sources/declarative/models/declarative_component_schema.py

+5
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,11 @@ class KeysReplace(BaseModel):
756756

757757
class FlattenFields(BaseModel):
758758
type: Literal["FlattenFields"]
759+
flatten_lists: Optional[bool] = Field(
760+
True,
761+
description="Whether to flatten lists or leave it as is. Default is True.",
762+
title="Flatten Lists",
763+
)
759764
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
760765

761766

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,9 @@ def create_keys_replace_transformation(
658658
def create_flatten_fields(
659659
self, model: FlattenFieldsModel, config: Config, **kwargs: Any
660660
) -> FlattenFields:
661-
return FlattenFields()
661+
return FlattenFields(
662+
flatten_lists=model.flatten_lists if model.flatten_lists is not None else True
663+
)
662664

663665
@staticmethod
664666
def _json_schema_type_name_to_type(value_type: Optional[ValueType]) -> Optional[Type[Any]]:

airbyte_cdk/sources/declarative/transformations/flatten_fields.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
@dataclass
1313
class FlattenFields(RecordTransformation):
14+
flatten_lists: bool = True
15+
1416
def transform(
1517
self,
1618
record: Dict[str, Any],
@@ -39,7 +41,7 @@ def flatten_record(self, record: Dict[str, Any]) -> Dict[str, Any]:
3941
)
4042
stack.append((value, new_key))
4143

42-
elif isinstance(current_record, list):
44+
elif isinstance(current_record, list) and self.flatten_lists:
4345
for i, item in enumerate(current_record):
4446
force_with_parent_name = True
4547
stack.append((item, f"{parent_key}.{i}"))

unit_tests/sources/declarative/transformations/test_flatten_fields.py

+64-9
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,27 @@
88
FlattenFields,
99
)
1010

11+
_FLATTEN_LISTS = True
12+
_DO_NOT_FLATTEN_LISTS = False
13+
1114

1215
@pytest.mark.parametrize(
13-
"input_record, expected_output",
16+
"flatten_lists, input_record, expected_output",
1417
[
15-
({"FirstName": "John", "LastName": "Doe"}, {"FirstName": "John", "LastName": "Doe"}),
16-
({"123Number": 123, "456Another123": 456}, {"123Number": 123, "456Another123": 456}),
17-
(
18+
pytest.param(
19+
_FLATTEN_LISTS,
20+
{"FirstName": "John", "LastName": "Doe"},
21+
{"FirstName": "John", "LastName": "Doe"},
22+
id="flatten simple record with string values",
23+
),
24+
pytest.param(
25+
_FLATTEN_LISTS,
26+
{"123Number": 123, "456Another123": 456},
27+
{"123Number": 123, "456Another123": 456},
28+
id="flatten simple record with int values",
29+
),
30+
pytest.param(
31+
_FLATTEN_LISTS,
1832
{
1933
"NestedRecord": {"FirstName": "John", "LastName": "Doe"},
2034
"456Another123": 456,
@@ -24,12 +38,16 @@
2438
"LastName": "Doe",
2539
"456Another123": 456,
2640
},
41+
id="flatten record with nested dict",
2742
),
28-
(
43+
pytest.param(
44+
_FLATTEN_LISTS,
2945
{"ListExample": [{"A": "a"}, {"A": "b"}]},
3046
{"ListExample.0.A": "a", "ListExample.1.A": "b"},
47+
id="flatten record with list values of dict items",
3148
),
32-
(
49+
pytest.param(
50+
_FLATTEN_LISTS,
3351
{
3452
"MixedCase123": {
3553
"Nested": [{"Key": {"Value": "test1"}}, {"Key": {"Value": "test2"}}]
@@ -41,14 +59,51 @@
4159
"Nested.1.Key.Value": "test2",
4260
"SimpleKey": "SimpleValue",
4361
},
62+
id="flatten record with nested dict of both list and string values",
4463
),
45-
(
64+
pytest.param(
65+
_FLATTEN_LISTS,
4666
{"List": ["Item1", "Item2", "Item3"]},
4767
{"List.0": "Item1", "List.1": "Item2", "List.2": "Item3"},
68+
id="flatten record with list of str values",
69+
),
70+
pytest.param(
71+
_DO_NOT_FLATTEN_LISTS,
72+
{"List": ["Item1", "Item2", "Item3"]},
73+
{"List": ["Item1", "Item2", "Item3"]},
74+
id="flatten record with dict of list values, flatten_lists=False",
75+
),
76+
pytest.param(
77+
_DO_NOT_FLATTEN_LISTS,
78+
{
79+
"RootField": {
80+
"NestedList": [{"Key": {"Value": "test1"}}, {"Key": {"Value": "test2"}}]
81+
},
82+
"SimpleKey": "SimpleValue",
83+
},
84+
{
85+
"NestedList": [{"Key": {"Value": "test1"}}, {"Key": {"Value": "test2"}}],
86+
"SimpleKey": "SimpleValue",
87+
},
88+
id="flatten record with dict of list values and simple key, flatten_lists=False",
89+
),
90+
pytest.param(
91+
_DO_NOT_FLATTEN_LISTS,
92+
{
93+
"RootField": {"List": [{"Key": {"Value": "test1"}}, {"Key": {"Value": "test2"}}]},
94+
"List": [1, 3, 6],
95+
"SimpleKey": "SimpleValue",
96+
},
97+
{
98+
"RootField.List": [{"Key": {"Value": "test1"}}, {"Key": {"Value": "test2"}}],
99+
"List": [1, 3, 6],
100+
"SimpleKey": "SimpleValue",
101+
},
102+
id="flatten record with dict of list values and simple key with duplicated keys, flatten_lists=False",
48103
),
49104
],
50105
)
51-
def test_flatten_fields(input_record, expected_output):
52-
flattener = FlattenFields()
106+
def test_flatten_fields(flatten_lists, input_record, expected_output):
107+
flattener = FlattenFields(flatten_lists=flatten_lists)
53108
flattener.transform(input_record)
54109
assert input_record == expected_output

0 commit comments

Comments
 (0)