From 5d60284380a7ef33ce55f373758d9c9429315f86 Mon Sep 17 00:00:00 2001 From: Erlend vollset Date: Mon, 2 Dec 2024 18:04:12 +0100 Subject: [PATCH] Add test for exists behaviour on lists of direct relations (#2005) Co-authored-by: cognite-bulldozer[bot] <51074376+cognite-bulldozer[bot]@users.noreply.github.com> --- .../client/_api/data_modeling/instances.py | 5 +- .../data_classes/data_modeling/__init__.py | 4 + .../test_data_modeling/test_instances.py | 84 ++++++++++++++++++- 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/cognite/client/_api/data_modeling/instances.py b/cognite/client/_api/data_modeling/instances.py index b6700f633f..c1ad578182 100644 --- a/cognite/client/_api/data_modeling/instances.py +++ b/cognite/client/_api/data_modeling/instances.py @@ -869,9 +869,10 @@ def _create_other_params( other_params: dict[str, Any] = {"includeTyping": include_typing} if sources: other_params["sources"] = [source.dump() for source in SourceSelector._load_list(sources)] - if with_properties := [s["viewId"] for s in other_params["sources"] if "properties" in s]: + if with_properties := [s["source"] for s in other_params["sources"] if "properties" in s]: raise ValueError( - f"Properties are not supported in this context. Got in source argument for view IDs {with_properties}" + f"Selecting properties is not supported in this context. " + f"Received in `sources` argument for views: {with_properties}." ) if sort: if isinstance(sort, (InstanceSort, dict)): diff --git a/cognite/client/data_classes/data_modeling/__init__.py b/cognite/client/data_classes/data_modeling/__init__.py index b74a96c749..2afd086075 100644 --- a/cognite/client/data_classes/data_modeling/__init__.py +++ b/cognite/client/data_classes/data_modeling/__init__.py @@ -67,6 +67,8 @@ InstancesDeleteResult, InstanceSort, InstancesResult, + InvolvedContainers, + InvolvedViews, Node, NodeApply, NodeApplyList, @@ -189,6 +191,8 @@ "InstancesDeleteResult", "InstancesResult", "InstanceApply", + "InvolvedViews", + "InvolvedContainers", "query", "PropertyOptions", "TypedEdgeApply", diff --git a/tests/tests_integration/test_api/test_data_modeling/test_instances.py b/tests/tests_integration/test_api/test_data_modeling/test_instances.py index f5c6db1481..3489e6b784 100644 --- a/tests/tests_integration/test_api/test_data_modeling/test_instances.py +++ b/tests/tests_integration/test_api/test_data_modeling/test_instances.py @@ -11,6 +11,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes.aggregations import HistogramValue from cognite.client.data_classes.data_modeling import ( + Container, ContainerApply, ContainerProperty, DataModel, @@ -44,7 +45,7 @@ filters, query, ) -from cognite.client.data_classes.data_modeling.data_types import UnitReference +from cognite.client.data_classes.data_modeling.data_types import DirectRelation, Int64, UnitReference from cognite.client.data_classes.data_modeling.instances import ( InstanceInspectResult, InstanceInspectResults, @@ -392,6 +393,38 @@ def as_write(self) -> Person: ) +@pytest.fixture(scope="session") +def container_with_all_the_types(cognite_client: CogniteClient, integration_test_space: Space) -> Container: + container = ContainerApply( + space=integration_test_space.space, + external_id="test_container_all_the_types", + properties={ + "int_array": ContainerProperty(type=Int64(is_list=True)), + "direct_relation_array": ContainerProperty(type=DirectRelation(is_list=True)), + }, + used_for="all", + ) + + return cognite_client.data_modeling.containers.apply(container) + + +@pytest.fixture(scope="session") +def view_with_all_the_types( + cognite_client: CogniteClient, integration_test_space: Space, container_with_all_the_types: Container +) -> View: + container_id = container_with_all_the_types.as_id() + view = ViewApply( + space=integration_test_space.space, + external_id="test_view_all_the_types", + version="v1", + properties={ + "int_array": MappedPropertyApply(container_id, "int_array"), + "direct_relation_array": MappedPropertyApply(container_id, "direct_relation_array"), + }, + ) + return cognite_client.data_modeling.views.apply(view) + + class TestInstancesAPI: def test_list_nodes(self, cognite_client: CogniteClient, movie_nodes: NodeList) -> None: is_prefix = filters.Or( @@ -1129,6 +1162,55 @@ def test_inspecting_instances( assert inspect_result.edges[0].inspection_results.involved_views == [] assert inspect_result.edges[0].inspection_results.involved_containers == [] + def test_exists_filter_on_lists( + self, + cognite_client: CogniteClient, + integration_test_space: Space, + container_with_all_the_types: Container, + view_with_all_the_types: View, + ) -> None: + container_id = container_with_all_the_types.as_id() + random_prefix = random_string(10) + nodes = [ + NodeApply( + space=integration_test_space.space, + external_id=random_prefix + "testnode_null", + sources=[NodeOrEdgeData(container_id, {"direct_relation_array": None})], + ), + NodeApply( + space=integration_test_space.space, + external_id=random_prefix + "testnode_empty", + sources=[NodeOrEdgeData(container_id, {"direct_relation_array": []})], + ), + NodeApply( + space=integration_test_space.space, + external_id=random_prefix + "testnode_non_empty", + sources=[ + NodeOrEdgeData( + container_id, + { + "direct_relation_array": [ + {"space": integration_test_space.space, "externalId": "testnode_empty"} + ] + }, + ) + ], + ), + ] + try: + cognite_client.data_modeling.instances.apply(nodes) + res = cognite_client.data_modeling.instances.list( + filter=filters.SpaceFilter(integration_test_space.space) + & filters.Exists(container_id.as_property_ref("direct_relation_array")) + & filters.Prefix(["node", "externalId"], random_prefix), + instance_type="node", + sources=view_with_all_the_types.as_id(), + ) + expected = set(node.external_id.removeprefix(random_prefix) for node in res.as_ids()) + assert expected == {"testnode_empty", "testnode_non_empty"} + finally: + cognite_client.data_modeling.instances.delete([node.as_id() for node in nodes]) + class TestInstancesSync: def test_sync_movies_released_in_1994(self, cognite_client: CogniteClient, movie_view: View) -> None: