feat: Add new data operations component (#7755)

* new data ops component

* update actions

* component add on feature

* add copy of old data processing

* Delete data_operations copy.py

* [autofix.ci] apply automated fixes

* lint and format error fixes

* [autofix.ci] apply automated fixes

* add test for the Processing component

* Update data_operations.py

* Update data_operations.py

* [autofix.ci] apply automated fixes

* Update data_operations.py

* Update data_operations.py

* Update data_operations.py

* Update data_operations.py

* update tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Edwin Jose 2025-05-16 10:43:39 -04:00 • committed by GitHub
commit 4a195f73c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 646 additions and 9 deletions

View file

@ -0,0 +1,193 @@
import pytest
from langflow.components.processing.data_operations import DataOperationsComponent
from langflow.schema import Data
from tests.base import ComponentTestBaseWithoutClient
class TestDataOperationsComponent(ComponentTestBaseWithoutClient):
@pytest.fixture
def component_class(self):
"""Return the component class to test."""
return DataOperationsComponent
@pytest.fixture
def default_kwargs(self):
"""Return the default kwargs for the component."""
return {
"data": Data(data={"key1": "value1", "key2": "value2", "key3": "value3"}),
"actions": [{"name": "Select Keys"}],
"select_keys_input": ["key1", "key2"],
}
@pytest.fixture
def file_names_mapping(self):
"""Return an empty list since this component doesn't have version-specific files."""
return []
def test_select_keys(self):
"""Test the Select Keys operation."""
component = DataOperationsComponent(
data=Data(data={"key1": "value1", "key2": "value2", "key3": "value3"}),
operations=[{"name": "Select Keys"}],
select_keys_input=["key1", "key2"],
)
result = component.as_data()
assert isinstance(result, Data)
assert "key1" in result.data
assert "key2" in result.data
assert "key3" not in result.data
assert result.data["key1"] == "value1"
assert result.data["key2"] == "value2"
def test_remove_keys(self):
"""Test the Remove Keys operation."""
component = DataOperationsComponent(
data=Data(data={"key1": "value1", "key2": "value2", "key3": "value3"}),
operations=[{"name": "Remove Keys"}],
remove_keys_input=["key3"],
)
result = component.as_data()
assert isinstance(result, Data)
assert "key1" in result.data
assert "key2" in result.data
assert "key3" not in result.data
def test_rename_keys(self):
"""Test the Rename Keys operation."""
component = DataOperationsComponent(
data=Data(data={"key1": "value1", "key2": "value2"}),
operations=[{"name": "Rename Keys"}],
rename_keys_input={"key1": "new_key1"},
)
result = component.as_data()
assert isinstance(result, Data)
assert "new_key1" in result.data
assert "key1" not in result.data
assert result.data["new_key1"] == "value1"
def test_literal_eval(self):
"""Test the Literal Eval operation."""
component = DataOperationsComponent(
data=Data(data={"list_as_string": "[1, 2, 3]", "dict_as_string": "{'a': 1, 'b': 2}"}),
operations=[{"name": "Literal Eval"}],
)
result = component.as_data()
assert isinstance(result, Data)
assert isinstance(result.data["list_as_string"], list)
assert result.data["list_as_string"] == [1, 2, 3]
assert isinstance(result.data["dict_as_string"], dict)
assert result.data["dict_as_string"] == {"a": 1, "b": 2}
def test_combine(self):
"""Test the Combine operation."""
data1 = Data(data={"key1": "value1"})
data2 = Data(data={"key2": "value2"})
component = DataOperationsComponent(
data=[data1, data2],
operations=[{"name": "Combine"}],
)
result = component.as_data()
assert isinstance(result, Data)
assert "key1" in result.data
assert "key2" in result.data
assert result.data["key1"] == "value1"
assert result.data["key2"] == "value2"
def test_combine_with_overlapping_keys(self):
"""Test the Combine operation with overlapping keys."""
data1 = Data(data={"common_key": "value1", "key1": "value1"})
data2 = Data(data={"common_key": "value2", "key2": "value2"})
component = DataOperationsComponent(
data=[data1, data2],
operations=[{"name": "Combine"}],
)
result = component.as_data()
assert isinstance(result, Data)
assert result.data["common_key"] == ["value1", "value2"] # Combined string values
assert result.data["key1"] == "value1"
assert result.data["key2"] == "value2"
def test_append_update(self):
"""Test the Append or Update Data operation."""
component = DataOperationsComponent(
data=Data(data={"existing_key": "existing_value"}),
operations=[{"name": "Append or Update"}],
append_update_data={"new_key": "new_value", "existing_key": "updated_value"},
)
result = component.as_data()
assert isinstance(result, Data)
assert result.data["existing_key"] == "updated_value"
assert result.data["new_key"] == "new_value"
def test_filter_values(self):
"""Test the Filter Values operation."""
nested_data = {
"items": [
{"id": 1, "name": "Item 1"},
{"id": 2, "name": "Item 2"},
{"id": 3, "name": "Different Item"},
]
}
component = DataOperationsComponent(
data=Data(data=nested_data),
operations=[{"name": "Filter Values"}],
filter_key=["items"],
filter_values={"name": "Item"},
operator="contains",
)
result = component.as_data()
assert isinstance(result, Data)
assert len(result.data["items"]) == 3
assert result.data["items"][0]["id"] == 1
assert result.data["items"][1]["id"] == 2
def test_no_actions(self):
"""Test behavior when no actions are specified."""
component = DataOperationsComponent(
data=Data(data={"key1": "value1"}),
operations=[],
)
result = component.as_data()
assert isinstance(result, Data)
assert result.data == {}
def test_get_normalized_data(self):
"""Test the get_normalized_data helper method."""
component = DataOperationsComponent(
data=Data(data={"key1": "value1"}),
operations=[],
)
# Add data under the "data" key
component.data = Data(data={"test": {"key2": "value2"}})
normalized = component.get_normalized_data()
assert normalized == {"test": {"key2": "value2"}}
# Test without the "data" key
component.data = Data(data={"key3": "value3"})
normalized = component.get_normalized_data()
assert normalized == {"key3": "value3"}
def test_validate_single_data_with_multiple_data(self):
"""Test that operations that don't support multiple data objects raise an error."""
component = DataOperationsComponent(
data=[Data(data={"key1": "value1"}), Data(data={"key2": "value2"})],
operations=[{"name": "Select Keys"}],
select_keys_input=["key1"],
)
with pytest.raises(ValueError, match="Select Keys operation is not supported for multiple data objects"):
component.as_data()