diff --git a/cognite_toolkit/_cdf_tk/storageio/_applications.py b/cognite_toolkit/_cdf_tk/storageio/_applications.py index 7ee406422a..f5c31bf33b 100644 --- a/cognite_toolkit/_cdf_tk/storageio/_applications.py +++ b/cognite_toolkit/_cdf_tk/storageio/_applications.py @@ -214,7 +214,7 @@ def _populate_id_cache(self, data_chunk: Sequence[IndustrialCanvas]) -> None: self.client.lookup.files.external_id(list(file_ids)) def _dump_resource(self, canvas: IndustrialCanvas) -> dict[str, JsonVal]: - dumped = canvas.as_write().dump() + dumped = canvas.as_write().dump(keep_existing_version=False) references = dumped.get("containerReferences", []) if not isinstance(references, list): return dumped @@ -230,10 +230,18 @@ def _dump_resource(self, canvas: IndustrialCanvas) -> dict[str, JsonVal]: properties = source["properties"] if not isinstance(properties, dict): continue + reference_type = properties.get("containerReferenceType") + if ( + reference_type + in { + "charts", + "dataGrid", + } + ): # These container reference types are special cases with a resourceId statically set to -1, which is why we skip them + continue resource_id = properties.pop("resourceId", None) if not isinstance(resource_id, int): continue - reference_type = properties.get("containerReferenceType") if reference_type == "asset": external_id = self.client.lookup.assets.external_id(resource_id) elif reference_type == "timeseries": diff --git a/tests/test_unit/conftest.py b/tests/test_unit/conftest.py index a9b1499b5c..d56a0c0ed3 100644 --- a/tests/test_unit/conftest.py +++ b/tests/test_unit/conftest.py @@ -242,6 +242,7 @@ def asset_centric_canvas() -> tuple[IndustrialCanvas, NodeList[InstanceSource]]: }, "space": "IndustrialCanvasInstanceSpace", "version": 14, + "existingVersion": 14, } ], "containerReferences": [ @@ -270,6 +271,7 @@ def asset_centric_canvas() -> tuple[IndustrialCanvas, NodeList[InstanceSource]]: }, "space": "IndustrialCanvasInstanceSpace", "version": 1, + "existingVersion": 1, }, { "createdTime": 1751540275336, @@ -296,6 +298,7 @@ def asset_centric_canvas() -> tuple[IndustrialCanvas, NodeList[InstanceSource]]: }, "space": "IndustrialCanvasInstanceSpace", "version": 1, + "existingVersion": 1, }, { "createdTime": 1751540544349, @@ -322,6 +325,61 @@ def asset_centric_canvas() -> tuple[IndustrialCanvas, NodeList[InstanceSource]]: }, "space": "IndustrialCanvasInstanceSpace", "version": 4, + "existingVersion": 4, + }, + { + "createdTime": 1751540600000, + "externalId": "495af88f-fe1d-403d-91b1-76ef9f80f265_chart-ref-1", + "instanceType": "node", + "lastUpdatedTime": 1751540600000, + "properties": { + "cdf_industrial_canvas": { + "ContainerReference/v2": { + "chartsId": "my-chart-id", + "containerReferenceType": "charts", + "height": 400, + "id": "chart-ref-1", + "label": "My Chart", + "maxHeight": None, + "maxWidth": None, + "resourceId": -1, + "resourceSubId": None, + "width": 600, + "x": 100, + "y": 100, + } + } + }, + "space": "IndustrialCanvasInstanceSpace", + "version": 1, + "existingVersion": 1, + }, + { + "createdTime": 1751540700000, + "externalId": "495af88f-fe1d-403d-91b1-76ef9f80f265_datagrid-ref-1", + "instanceType": "node", + "lastUpdatedTime": 1751540700000, + "properties": { + "cdf_industrial_canvas": { + "ContainerReference/v2": { + "chartsId": None, + "containerReferenceType": "dataGrid", + "height": 300, + "id": "datagrid-ref-1", + "label": "My Data Grid", + "maxHeight": None, + "maxWidth": None, + "resourceId": -1, + "resourceSubId": None, + "width": 800, + "x": 200, + "y": 200, + } + } + }, + "space": "IndustrialCanvasInstanceSpace", + "version": 1, + "existingVersion": 1, }, ], } diff --git a/tests/test_unit/test_cdf_tk/test_commands/test_migration_cmd/test_migrate_canvas.py b/tests/test_unit/test_cdf_tk/test_commands/test_migration_cmd/test_migrate_canvas.py index 509e3b3418..e7b25849a5 100644 --- a/tests/test_unit/test_cdf_tk/test_commands/test_migration_cmd/test_migrate_canvas.py +++ b/tests/test_unit/test_cdf_tk/test_commands/test_migration_cmd/test_migrate_canvas.py @@ -47,7 +47,11 @@ def test_migrate_canvas_happy_path( backup = client.canvas.industrial.create.call_args[0][0] assert isinstance(backup, IndustrialCanvasApply) - assert len(update.fdm_instance_container_references) == len(canvas.container_references) + # Only asset-centric container references should be migrated (not charts/dataGrid) + migratable_refs = [ + ref for ref in canvas.container_references if ref.container_reference_type not in {"charts", "dataGrid"} + ] + assert len(update.fdm_instance_container_references) == len(migratable_refs) assert len(backup.fdm_instance_container_references) == 0 def test_migrate_canvas_missing(self) -> None: diff --git a/tests/test_unit/test_cdf_tk/test_storageio/test_applications.py b/tests/test_unit/test_cdf_tk/test_storageio/test_applications.py index a1441e8ae0..d1a79473de 100644 --- a/tests/test_unit/test_cdf_tk/test_storageio/test_applications.py +++ b/tests/test_unit/test_cdf_tk/test_storageio/test_applications.py @@ -146,7 +146,12 @@ def test_download_iterable( class TestCanvasIO: def test_download_iterable(self, asset_centric_canvas: tuple[IndustrialCanvas, NodeList[InstanceSource]]) -> None: canvas, _ = asset_centric_canvas - ids = [container_ref.resource_id for container_ref in canvas.container_references or []] + # Exclude charts/dataGrid types which have resourceId=-1 by design + ids = [ + container_ref.resource_id + for container_ref in canvas.container_references or [] + if container_ref.container_reference_type not in {"charts", "dataGrid"} + ] assert len(ids) > 0, "Test canvas must have container references for this test to be valid." mapping = {id_: f"external_id_{no}" for no, id_ in enumerate(ids)} reverse = {v: k for k, v in mapping.items()} @@ -186,8 +191,10 @@ def lookup(external_id: str | list[str]) -> int | list[int]: json_str = json.dumps(json_format) internal_id_in_json = [id_ for id_ in ids if str(id_) in json_str] assert len(internal_id_in_json) == 0, "Internal IDs should not be present in the JSON export." + assert "existingVersion" not in json_str, "existingVersion should not be present in the JSON export." restored_canvases = io.json_chunk_to_data([("line 1", item) for item in json_format]) assert len(restored_canvases) == 1 restored_canvas = restored_canvases[0] - assert restored_canvas.item.dump() == canvas.as_write().dump() + # The restored canvas should match the original without existingVersion fields + assert restored_canvas.item.dump() == canvas.as_write().dump(keep_existing_version=False)