cognite-toolkit 0.7.39__py3-none-any.whl → 0.7.41__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +7 -1
  2. cognite_toolkit/_cdf_tk/client/api/assets.py +118 -0
  3. cognite_toolkit/_cdf_tk/client/api/events.py +97 -0
  4. cognite_toolkit/_cdf_tk/client/api/infield.py +3 -3
  5. cognite_toolkit/_cdf_tk/client/api/legacy/extended_functions.py +1 -1
  6. cognite_toolkit/_cdf_tk/client/api/project.py +1 -1
  7. cognite_toolkit/_cdf_tk/client/api/streams.py +2 -2
  8. cognite_toolkit/_cdf_tk/client/api/three_d.py +5 -4
  9. cognite_toolkit/_cdf_tk/client/api/timeseries.py +105 -0
  10. cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +9 -0
  11. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +220 -0
  12. cognite_toolkit/_cdf_tk/client/{data_classes/api_classes.py → cdf_client/responses.py} +10 -13
  13. cognite_toolkit/_cdf_tk/client/data_classes/agent.py +127 -0
  14. cognite_toolkit/_cdf_tk/client/data_classes/asset.py +54 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/base.py +117 -22
  16. cognite_toolkit/_cdf_tk/client/data_classes/event.py +43 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +56 -0
  18. cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +44 -0
  19. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +34 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/raw.py +43 -0
  21. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +3 -2
  22. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +29 -34
  23. cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +55 -0
  24. cognite_toolkit/_cdf_tk/{utils → client}/http_client/__init__.py +4 -4
  25. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_client.py +10 -10
  26. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes.py +2 -2
  27. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes2.py +10 -33
  28. cognite_toolkit/_cdf_tk/client/testing.py +6 -0
  29. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +1 -1
  30. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +6 -7
  31. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +6 -5
  32. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +6 -4
  33. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +10 -10
  34. cognite_toolkit/_cdf_tk/commands/_purge.py +7 -7
  35. cognite_toolkit/_cdf_tk/commands/_upload.py +1 -1
  36. cognite_toolkit/_cdf_tk/commands/pull.py +97 -2
  37. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +43 -47
  38. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +11 -4
  39. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +2 -1
  40. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -2
  41. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +2 -1
  42. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +1 -1
  43. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +2 -1
  44. cognite_toolkit/_cdf_tk/storageio/_applications.py +1 -1
  45. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +32 -29
  46. cognite_toolkit/_cdf_tk/storageio/_base.py +1 -1
  47. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +7 -7
  48. cognite_toolkit/_cdf_tk/storageio/_file_content.py +7 -7
  49. cognite_toolkit/_cdf_tk/storageio/_raw.py +1 -1
  50. cognite_toolkit/_cdf_tk/utils/useful_types.py +4 -7
  51. cognite_toolkit/_cdf_tk/utils/useful_types2.py +12 -0
  52. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  53. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  54. cognite_toolkit/_resources/cdf.toml +1 -1
  55. cognite_toolkit/_version.py +1 -1
  56. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/METADATA +1 -1
  57. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/RECORD +61 -48
  58. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/WHEEL +1 -1
  59. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_exception.py +0 -0
  60. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_tracker.py +0 -0
  61. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  import itertools
3
+ import json
3
4
  import re
4
5
  import sys
5
6
  import tempfile
@@ -28,6 +29,7 @@ from cognite_toolkit._cdf_tk.cruds import (
28
29
  HostedExtractorSourceCRUD,
29
30
  ResourceCRUD,
30
31
  StreamlitCRUD,
32
+ ViewCRUD,
31
33
  )
32
34
  from cognite_toolkit._cdf_tk.data_classes import (
33
35
  BuildEnvironment,
@@ -643,6 +645,44 @@ class PullCommand(ToolkitCommand):
643
645
  loader: ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse],
644
646
  source_file: Path,
645
647
  ) -> tuple[str, dict[Path, str]]:
648
+ """Convert resource data from CDF into YAML file content ready to be written to disk.
649
+
650
+ This method takes the raw CDF resource data and transforms it back into a properly
651
+ formatted YAML file that preserves:
652
+ - Template variables (e.g., {{ variable_name }}) instead of their resolved values
653
+ - YAML comments from the original source file
654
+ - The original key ordering in dictionaries
655
+
656
+ The transformation process:
657
+ 1. Replace all template variables with unique placeholders
658
+ 2. Load source YAML content while preserving comments
659
+ 3. Update the resource data with placeholder values where variables were used
660
+ 4. Dump the updated data back to YAML format
661
+ 5. Replace placeholders with the original template variable syntax
662
+ 6. Restore the YAML comments
663
+
664
+ Args:
665
+ source: The original YAML file content as a string.
666
+ to_write: A mapping from resource identifiers to their updated data dictionaries
667
+ pulled from CDF.
668
+ resources: The list of built resources containing build variables and metadata.
669
+ environment_variables: A mapping of environment variable names to their values,
670
+ used to resolve variables like ${VAR_NAME} in template values.
671
+ loader: The ResourceCRUD loader instance for this resource type.
672
+ source_file: The path to the source file being processed.
673
+
674
+ Returns:
675
+ A tuple containing:
676
+ - The final YAML content string ready to be written to disk.
677
+ - A dictionary mapping extra file paths to their content (for resources
678
+ that have additional files, like SQL queries for transformations).
679
+
680
+ Raises:
681
+ ValueError: If the loaded YAML structure doesn't match between the original
682
+ and placeholder versions.
683
+ ToolkitMissingResourceError: If a resource identifier is not found in the
684
+ to_write or resources mappings.
685
+ """
646
686
  # 1. Replace all variables with placeholders
647
687
  # 2. Load source and keep the comments
648
688
  # 3. Update the to_write dict with the placeholders
@@ -762,9 +802,22 @@ class PullCommand(ToolkitCommand):
762
802
 
763
803
 
764
804
  class ResourceReplacer:
765
- """Replaces values in a local resource directory with the updated values from CDF.
805
+ """Replaces values in a local resource dictionary with the updated values from CDF.
766
806
 
767
807
  The local resource dict order is maintained. In addition, placeholders are used for variables.
808
+
809
+ This class is responsible for merging CDF resource values back into local configuration files
810
+ while preserving:
811
+ - The original key ordering in dictionaries
812
+ - Template variable placeholders (e.g., {{ variable_name }})
813
+ - Comments and formatting where possible
814
+
815
+ Args:
816
+ value_by_placeholder: A mapping from placeholder strings to their corresponding
817
+ BuildVariable objects. Placeholders are temporary substitutes for template
818
+ variables during processing.
819
+ loader: The ResourceCRUD loader instance used to determine how to diff lists
820
+ and handle resource-specific logic.
768
821
  """
769
822
 
770
823
  def __init__(self, value_by_placeholder: dict[str, BuildVariable], loader: ResourceCRUD) -> None:
@@ -777,7 +830,49 @@ class ResourceReplacer:
777
830
  placeholder: dict[str, Any],
778
831
  to_write: dict[str, Any],
779
832
  ) -> dict[str, Any]:
780
- return self._replace_dict(current, placeholder, to_write, tuple())
833
+ """Replace values in a local resource dict with updated values from CDF.
834
+
835
+ Merges the CDF resource values into the local configuration while maintaining
836
+ the original dictionary key ordering and preserving template variable placeholders.
837
+
838
+ Args:
839
+ current: The current local resource dictionary with resolved variable values.
840
+ This represents the source file content after template variables have
841
+ been substituted with their actual values.
842
+ placeholder: The local resource dictionary with placeholder strings instead
843
+ of resolved values. Used to identify which values contain template
844
+ variables that should be preserved.
845
+ to_write: The resource dictionary from CDF containing the updated values
846
+ to merge into the local configuration.
847
+
848
+ Returns:
849
+ A new dictionary with CDF values merged in, maintaining the original key
850
+ order from `current`. Template variables are preserved as placeholders
851
+ (to be converted back to {{ variable }} syntax by the caller). New keys
852
+ from CDF are appended at the end, and removed keys are omitted.
853
+
854
+ Raises:
855
+ ToolkitValueError: If a list variable has changed and cannot be updated,
856
+ or if there's a type mismatch between local and CDF values.
857
+ """
858
+ has_stringified_view_filter = False
859
+ if isinstance(self._loader, ViewCRUD):
860
+ # view.filter are recursive nested dicts that are complex. To avoid issues with comparing
861
+ # lists inside the filters, we stringify them before processing such that they are compared
862
+ # as strings.
863
+ processed = []
864
+ for d in (current, placeholder, to_write):
865
+ if isinstance(d.get("filter"), dict):
866
+ d = d.copy()
867
+ d["filter"] = json.dumps(d["filter"])
868
+ has_stringified_view_filter = True
869
+ processed.append(d)
870
+ current, placeholder, to_write = processed
871
+ output = self._replace_dict(current, placeholder, to_write, tuple())
872
+ if has_stringified_view_filter and "filter" in output:
873
+ # Special case for ViewCRUD where the filter is stringified in CDF
874
+ output["filter"] = json.loads(output["filter"])
875
+ return output
781
876
 
782
877
  def _replace_dict(
783
878
  self,
@@ -6,10 +6,6 @@ from typing import Any, final
6
6
 
7
7
  import pandas as pd
8
8
  from cognite.client.data_classes import (
9
- AggregateResultItem,
10
- Asset,
11
- AssetList,
12
- AssetWrite,
13
9
  Event,
14
10
  EventList,
15
11
  EventWrite,
@@ -24,6 +20,12 @@ from cognite.client.utils.useful_types import SequenceNotStr
24
20
  from rich.console import Console
25
21
 
26
22
  from cognite_toolkit._cdf_tk.client import ToolkitClient
23
+ from cognite_toolkit._cdf_tk.client.data_classes.asset import (
24
+ AssetAggregateItem,
25
+ AssetRequest,
26
+ AssetResponse,
27
+ )
28
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId, InternalOrExternalId
27
29
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.sequences import (
28
30
  ToolkitSequenceRows,
29
31
  ToolkitSequenceRowsList,
@@ -44,10 +46,10 @@ _DEPRECATION_WARNING_ISSUED = False
44
46
 
45
47
 
46
48
  @final
47
- class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
49
+ class AssetCRUD(ResourceCRUD[ExternalId, AssetRequest, AssetResponse]):
48
50
  folder_name = "classic"
49
- resource_cls = Asset
50
- resource_write_cls = AssetWrite
51
+ resource_cls = AssetResponse
52
+ resource_write_cls = AssetRequest
51
53
  yaml_cls = AssetYAML
52
54
  kind = "Asset"
53
55
  dependencies = frozenset({DataSetsCRUD, LabelCRUD})
@@ -78,15 +80,15 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
78
80
  return "assets"
79
81
 
80
82
  @classmethod
81
- def get_id(cls, item: Asset | AssetWrite | dict) -> str:
83
+ def get_id(cls, item: AssetRequest | AssetResponse | dict) -> ExternalId:
82
84
  if isinstance(item, dict):
83
- return item["externalId"]
85
+ return ExternalId(external_id=item["externalId"])
84
86
  if not item.external_id:
85
87
  raise KeyError("Asset must have external_id")
86
- return item.external_id
88
+ return ExternalId(external_id=item.external_id)
87
89
 
88
90
  @classmethod
89
- def get_internal_id(cls, item: Asset | dict) -> int:
91
+ def get_internal_id(cls, item: AssetResponse | dict) -> int:
90
92
  if isinstance(item, dict):
91
93
  return item["id"]
92
94
  if not item.id:
@@ -94,12 +96,12 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
94
96
  return item.id
95
97
 
96
98
  @classmethod
97
- def dump_id(cls, id: str) -> dict[str, Any]:
98
- return {"externalId": id}
99
+ def dump_id(cls, id: ExternalId) -> dict[str, Any]:
100
+ return id.dump()
99
101
 
100
102
  @classmethod
101
103
  def get_required_capability(
102
- cls, items: collections.abc.Sequence[AssetWrite] | None, read_only: bool
104
+ cls, items: collections.abc.Sequence[AssetRequest] | None, read_only: bool
103
105
  ) -> Capability | list[Capability]:
104
106
  if not items and items is not None:
105
107
  return []
@@ -119,45 +121,39 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
119
121
 
120
122
  return capabilities.AssetsAcl(actions, scope)
121
123
 
122
- def create(self, items: Sequence[AssetWrite]) -> AssetList:
123
- return self.client.assets.create(items)
124
+ def create(self, items: collections.abc.Sequence[AssetRequest]) -> list[AssetResponse]:
125
+ return self.client.tool.assets.create(items)
124
126
 
125
- def retrieve(self, ids: SequenceNotStr[str]) -> AssetList:
126
- return self.client.assets.retrieve_multiple(external_ids=ids, ignore_unknown_ids=True)
127
+ def retrieve(self, ids: SequenceNotStr[ExternalId]) -> list[AssetResponse]:
128
+ return self.client.tool.assets.retrieve(list(ids), ignore_unknown_ids=True)
127
129
 
128
- def update(self, items: Sequence[AssetWrite]) -> AssetList:
129
- return self.client.assets.update(items, mode="replace")
130
+ def update(self, items: collections.abc.Sequence[AssetRequest]) -> list[AssetResponse]:
131
+ return self.client.tool.assets.update(items, mode="replace")
130
132
 
131
- def delete(self, ids: SequenceNotStr[str | int]) -> int:
133
+ def delete(self, ids: SequenceNotStr[InternalOrExternalId]) -> int:
132
134
  if not ids:
133
135
  return 0
134
- internal_ids, external_ids = self._split_ids(ids)
135
- try:
136
- self.client.assets.delete(id=internal_ids, external_id=external_ids)
137
- except CogniteNotFoundError as e:
138
- # Do a CogniteNotFoundError instead of passing 'ignore_unknown_ids=True' to the delete method
139
- # to obtain an accurate list of deleted assets.
140
- non_existing = set(e.failed or [])
141
- if existing := [id_ for id_ in ids if id_ not in non_existing]:
142
- internal_ids, external_ids = self._split_ids(existing)
143
- self.client.assets.delete(id=internal_ids, external_id=external_ids)
144
- return len(existing)
145
- else:
146
- return len(ids)
136
+ self.client.tool.assets.delete(list(ids), ignore_unknown_ids=True)
137
+ return len(ids)
147
138
 
148
139
  def _iterate(
149
140
  self,
150
141
  data_set_external_id: str | None = None,
151
142
  space: str | None = None,
152
143
  parent_ids: list[Hashable] | None = None,
153
- ) -> Iterable[Asset]:
154
- return iter(
155
- self.client.assets(
144
+ ) -> Iterable[AssetResponse]:
145
+ cursor: str | None = None
146
+ while True:
147
+ page = self.client.tool.assets.iterate(
148
+ limit=1000,
149
+ cursor=cursor,
156
150
  data_set_external_ids=[data_set_external_id] if data_set_external_id else None,
157
- # This is used in the purge command to delete the children before the parent.
158
- aggregated_properties=["depth", "child_count", "path"],
151
+ aggregated_properties=True,
159
152
  )
160
- )
153
+ yield from page.items
154
+ if not page.next_cursor or not page.items:
155
+ break
156
+ cursor = page.next_cursor
161
157
 
162
158
  @classmethod
163
159
  def get_dependent_items(cls, item: dict) -> Iterable[tuple[type[ResourceCRUD], Hashable]]:
@@ -202,7 +198,7 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
202
198
 
203
199
  return resources
204
200
 
205
- def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> AssetWrite:
201
+ def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> AssetRequest:
206
202
  # Unpack metadata keys from table formats (e.g. csv, parquet)
207
203
  metadata: dict = resource.get("metadata", {})
208
204
  for key, value in list(resource.items()):
@@ -219,10 +215,10 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
219
215
 
220
216
  if ds_external_id := resource.pop("dataSetExternalId", None):
221
217
  resource["dataSetId"] = self.client.lookup.data_sets.id(ds_external_id, is_dry_run)
222
- return AssetWrite._load(resource)
218
+ return AssetRequest.model_validate(resource)
223
219
 
224
- def dump_resource(self, resource: Asset, local: dict[str, Any] | None = None) -> dict[str, Any]:
225
- dumped = resource.as_write().dump()
220
+ def dump_resource(self, resource: AssetResponse, local: dict[str, Any] | None = None) -> dict[str, Any]:
221
+ dumped = resource.as_request_resource().dump()
226
222
  local = local or {}
227
223
  if data_set_id := dumped.pop("dataSetId", None):
228
224
  dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(data_set_id)
@@ -234,7 +230,7 @@ class AssetCRUD(ResourceCRUD[str, AssetWrite, Asset]):
234
230
  # This is only included when the asset is downloaded/migrated with aggregated properties
235
231
  aggregates = (
236
232
  resource.aggregates.dump()
237
- if isinstance(resource.aggregates, AggregateResultItem)
233
+ if isinstance(resource.aggregates, AssetAggregateItem)
238
234
  else resource.aggregates
239
235
  )
240
236
  if "path" in aggregates:
@@ -372,7 +368,7 @@ class SequenceCRUD(ResourceCRUD[str, SequenceWrite, CDFSequence]):
372
368
  if "dataSetExternalId" in item:
373
369
  yield DataSetsCRUD, item["dataSetExternalId"]
374
370
  if "assetExternalId" in item:
375
- yield AssetCRUD, item["assetExternalId"]
371
+ yield AssetCRUD, ExternalId(external_id=item["assetExternalId"])
376
372
 
377
373
 
378
374
  @final
@@ -596,7 +592,7 @@ class EventCRUD(ResourceCRUD[str, EventWrite, Event]):
596
592
  yield DataSetsCRUD, item["dataSetExternalId"]
597
593
  for asset_id in item.get("assetExternalIds", []):
598
594
  if isinstance(asset_id, str):
599
- yield AssetCRUD, asset_id
595
+ yield AssetCRUD, ExternalId(external_id=asset_id)
600
596
 
601
597
  def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> EventWrite:
602
598
  if ds_external_id := resource.get("dataSetExternalId", None):
@@ -8,6 +8,7 @@ from cognite.client.data_classes.data_modeling import NodeApplyResultList, NodeI
8
8
  from cognite.client.exceptions import CogniteAPIError
9
9
  from cognite.client.utils.useful_types import SequenceNotStr
10
10
 
11
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
11
12
  from cognite_toolkit._cdf_tk.client.data_classes.infield import (
12
13
  InFieldCDMLocationConfig,
13
14
  InfieldLocationConfig,
@@ -139,7 +140,7 @@ class InfieldV1CRUD(ResourceCRUD[str, APMConfigWrite, APMConfig]):
139
140
  yield SpaceCRUD, customer_data_space_id
140
141
  for config in cls._get_root_location_configurations(item) or []:
141
142
  if isinstance(asset_external_id := config.get("assetExternalId"), str):
142
- yield AssetCRUD, asset_external_id
143
+ yield AssetCRUD, ExternalId(external_id=asset_external_id)
143
144
  if isinstance(data_set_external_id := config.get("dataSetExternalId"), str):
144
145
  yield DataSetsCRUD, data_set_external_id
145
146
  if isinstance(app_data_instance_space := config.get("appDataInstanceSpace"), str):
@@ -162,7 +163,7 @@ class InfieldV1CRUD(ResourceCRUD[str, APMConfigWrite, APMConfig]):
162
163
  yield DataSetsCRUD, data_set_external_id
163
164
  for asset_external_id in filter_.get("assetSubtreeExternalIds", []):
164
165
  if isinstance(asset_external_id, str):
165
- yield AssetCRUD, asset_external_id
166
+ yield AssetCRUD, ExternalId(external_id=asset_external_id)
166
167
  if app_data_instance_space := filter_.get("appDataInstanceSpace"):
167
168
  if isinstance(app_data_instance_space, str):
168
169
  yield SpaceCRUD, app_data_instance_space
@@ -267,7 +268,10 @@ class InFieldLocationConfigCRUD(ResourceCRUD[TypedNodeIdentifier, InfieldLocatio
267
268
 
268
269
  @classmethod
269
270
  def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
270
- return id.dump(include_type=False)
271
+ return {
272
+ "space": id.space,
273
+ "externalId": id.external_id,
274
+ }
271
275
 
272
276
  @classmethod
273
277
  def get_required_capability(
@@ -368,7 +372,10 @@ class InFieldCDMLocationConfigCRUD(
368
372
 
369
373
  @classmethod
370
374
  def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
371
- return id.dump(include_type=False)
375
+ return {
376
+ "space": id.space,
377
+ "externalId": id.external_id,
378
+ }
372
379
 
373
380
  @classmethod
374
381
  def get_required_capability(
@@ -33,6 +33,7 @@ from cognite.client.utils._time import convert_data_modelling_timestamp
33
33
  from cognite.client.utils.useful_types import SequenceNotStr
34
34
  from rich import print
35
35
 
36
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
36
37
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.extendable_cognite_file import (
37
38
  ExtendableCogniteFile,
38
39
  ExtendableCogniteFileApply,
@@ -118,7 +119,7 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
118
119
  elif isinstance(label, str):
119
120
  yield LabelCRUD, label
120
121
  for asset_external_id in item.get("assetExternalIds", []):
121
- yield AssetCRUD, asset_external_id
122
+ yield AssetCRUD, ExternalId(external_id=asset_external_id)
122
123
 
123
124
  def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> FileMetadataWrite:
124
125
  if resource.get("dataSetExternalId") is not None:
@@ -7,6 +7,7 @@ from cognite.client.data_classes.capabilities import Capability, LocationFilters
7
7
  from cognite.client.data_classes.data_modeling import DataModelId, ViewId
8
8
  from cognite.client.utils.useful_types import SequenceNotStr
9
9
 
10
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
10
11
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.location_filters import (
11
12
  LocationFilter,
12
13
  LocationFilterList,
@@ -239,14 +240,14 @@ class LocationFilterCRUD(ResourceCRUD[str, LocationFilterWrite, LocationFilter])
239
240
  yield DataSetsCRUD, data_set_external_id
240
241
  for asset in asset_centric.get("assetSubtreeIds", []):
241
242
  if "externalId" in asset:
242
- yield AssetCRUD, asset["externalId"]
243
+ yield AssetCRUD, ExternalId(external_id=asset["externalId"])
243
244
  for subfilter_name in cls.subfilter_names:
244
245
  subfilter = asset_centric.get(subfilter_name, {})
245
246
  for data_set_external_id in subfilter.get("dataSetExternalIds", []):
246
247
  yield DataSetsCRUD, data_set_external_id
247
248
  for asset in subfilter.get("assetSubtreeIds", []):
248
249
  if "externalId" in asset:
249
- yield AssetCRUD, asset["externalId"]
250
+ yield AssetCRUD, ExternalId(external_id=asset["externalId"])
250
251
  for view in item.get("views", []):
251
252
  if in_dict(["space", "externalId", "version"], view):
252
253
  yield ViewCRUD, ViewId(view["space"], view["externalId"], view["version"])
@@ -11,6 +11,7 @@ from cognite.client.data_classes.capabilities import Capability
11
11
  from cognite.client.exceptions import CogniteAPIError, CogniteNotFoundError
12
12
  from cognite.client.utils.useful_types import SequenceNotStr
13
13
 
14
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
14
15
  from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceCRUD
15
16
  from cognite_toolkit._cdf_tk.resource_classes import RelationshipYAML
16
17
 
@@ -123,7 +124,7 @@ class RelationshipCRUD(ResourceCRUD[str, RelationshipWrite, Relationship]):
123
124
  if isinstance(id_value, str) and isinstance(type_value, str):
124
125
  type_value = type_value.strip().casefold()
125
126
  if type_value == "asset":
126
- yield AssetCRUD, id_value
127
+ yield AssetCRUD, ExternalId(external_id=id_value)
127
128
  elif type_value == "sequence":
128
129
  yield SequenceCRUD, id_value
129
130
  elif type_value == "timeseries":
@@ -9,9 +9,9 @@ from cognite_toolkit._cdf_tk.client.data_classes.streams import (
9
9
  StreamResponse,
10
10
  StreamResponseList,
11
11
  )
12
+ from cognite_toolkit._cdf_tk.client.http_client import ToolkitAPIError
12
13
  from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceCRUD
13
14
  from cognite_toolkit._cdf_tk.resource_classes import StreamYAML
14
- from cognite_toolkit._cdf_tk.utils.http_client import ToolkitAPIError
15
15
 
16
16
  from .datamodel import ContainerCRUD
17
17
 
@@ -23,6 +23,7 @@ from cognite.client.data_classes.datapoints_subscriptions import TimeSeriesIDLis
23
23
  from cognite.client.exceptions import CogniteAPIError, CogniteNotFoundError
24
24
  from cognite.client.utils.useful_types import SequenceNotStr
25
25
 
26
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
26
27
  from cognite_toolkit._cdf_tk.constants import MAX_TIMESTAMP_MS, MIN_TIMESTAMP_MS
27
28
  from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceContainerCRUD, ResourceCRUD
28
29
  from cognite_toolkit._cdf_tk.exceptions import (
@@ -97,7 +98,7 @@ class TimeSeriesCRUD(ResourceContainerCRUD[str, TimeSeriesWrite, TimeSeries]):
97
98
  for security_category in item["securityCategoryNames"]:
98
99
  yield SecurityCategoryCRUD, security_category
99
100
  if "assetExternalId" in item:
100
- yield AssetCRUD, item["assetExternalId"]
101
+ yield AssetCRUD, ExternalId(external_id=item["assetExternalId"])
101
102
 
102
103
  def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> TimeSeriesWrite:
103
104
  if ds_external_id := resource.pop("dataSetExternalId", None):
@@ -7,10 +7,10 @@ from cognite_toolkit._cdf_tk.client.data_classes.legacy.canvas import (
7
7
  IndustrialCanvasApply,
8
8
  )
9
9
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.charts import Chart, ChartList, ChartWrite
10
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, HTTPMessage, SimpleBodyRequest
10
11
  from cognite_toolkit._cdf_tk.exceptions import ToolkitNotImplementedError
11
12
  from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning, MediumSeverityWarning
12
13
  from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
13
- from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, HTTPMessage, SimpleBodyRequest
14
14
  from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
15
15
 
16
16
  from ._base import Page, UploadableStorageIO, UploadItem
@@ -4,10 +4,6 @@ from collections.abc import Iterable, Sequence
4
4
  from typing import Any, ClassVar, Generic
5
5
 
6
6
  from cognite.client.data_classes import (
7
- AggregateResultItem,
8
- Asset,
9
- AssetList,
10
- AssetWrite,
11
7
  Event,
12
8
  EventList,
13
9
  EventWrite,
@@ -21,6 +17,8 @@ from cognite.client.data_classes import (
21
17
  )
22
18
 
23
19
  from cognite_toolkit._cdf_tk.client import ToolkitClient
20
+ from cognite_toolkit._cdf_tk.client.data_classes.asset import AssetAggregateItem, AssetRequest, AssetResponse
21
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalId
24
22
  from cognite_toolkit._cdf_tk.cruds import (
25
23
  AssetCRUD,
26
24
  DataSetsCRUD,
@@ -42,10 +40,10 @@ from cognite_toolkit._cdf_tk.utils.cdf import metadata_key_counts
42
40
  from cognite_toolkit._cdf_tk.utils.fileio import FileReader, SchemaColumn
43
41
  from cognite_toolkit._cdf_tk.utils.fileio._readers import TableReader
44
42
  from cognite_toolkit._cdf_tk.utils.useful_types import (
45
- AssetCentricResource,
46
43
  AssetCentricType,
47
44
  JsonVal,
48
45
  )
46
+ from cognite_toolkit._cdf_tk.utils.useful_types2 import AssetCentricResource
49
47
 
50
48
  from ._base import (
51
49
  ConfigurableStorageIO,
@@ -112,12 +110,14 @@ class AssetCentricIO(
112
110
  return asset_subtree_external_ids, data_set_external_ids
113
111
 
114
112
  def _collect_dependencies(
115
- self, resources: AssetList | FileMetadataList | TimeSeriesList | EventList, selector: AssetCentricSelector
113
+ self,
114
+ resources: Sequence[AssetResponse] | FileMetadataList | TimeSeriesList | EventList,
115
+ selector: AssetCentricSelector,
116
116
  ) -> None:
117
117
  for resource in resources:
118
118
  if resource.data_set_id:
119
119
  self._downloaded_data_sets_by_selector[selector].add(resource.data_set_id)
120
- if isinstance(resource, Asset | FileMetadata):
120
+ if isinstance(resource, AssetResponse | FileMetadata):
121
121
  for label in resource.labels or []:
122
122
  if isinstance(label, str):
123
123
  self._downloaded_labels_by_selector[selector].add(label)
@@ -149,7 +149,7 @@ class AssetCentricIO(
149
149
  def create_internal_identifier(cls, internal_id: int, project: str) -> str:
150
150
  return f"INTERNAL_ID_project_{project}_{internal_id!s}"
151
151
 
152
- def _populate_data_set_id_cache(self, chunk: Sequence[Asset | FileMetadata | TimeSeries | Event]) -> None:
152
+ def _populate_data_set_id_cache(self, chunk: Sequence[AssetResponse | FileMetadata | TimeSeries | Event]) -> None:
153
153
  data_set_ids = {item.data_set_id for item in chunk if item.data_set_id is not None}
154
154
  self.client.lookup.data_sets.external_id(list(data_set_ids))
155
155
 
@@ -249,7 +249,7 @@ class UploadableAssetCentricIO(
249
249
  )
250
250
 
251
251
 
252
- class AssetIO(UploadableAssetCentricIO[Asset, AssetWrite]):
252
+ class AssetIO(UploadableAssetCentricIO[AssetResponse, AssetRequest]):
253
253
  KIND = "Assets"
254
254
  RESOURCE_TYPE = "asset"
255
255
  SUPPORTED_DOWNLOAD_FORMATS = frozenset({".parquet", ".csv", ".ndjson"})
@@ -261,7 +261,7 @@ class AssetIO(UploadableAssetCentricIO[Asset, AssetWrite]):
261
261
  super().__init__(client)
262
262
  self._crud = AssetCRUD.create_loader(self.client)
263
263
 
264
- def as_id(self, item: Asset) -> str:
264
+ def as_id(self, item: AssetResponse) -> str:
265
265
  return item.external_id if item.external_id is not None else self._create_identifier(item.id)
266
266
 
267
267
  def _get_aggregator(self) -> AssetCentricAggregator:
@@ -311,21 +311,25 @@ class AssetIO(UploadableAssetCentricIO[Asset, AssetWrite]):
311
311
 
312
312
  def stream_data(self, selector: AssetCentricSelector, limit: int | None = None) -> Iterable[Page]:
313
313
  asset_subtree_external_ids, data_set_external_ids = self._get_hierarchy_dataset_pair(selector)
314
- for asset_list in self.client.assets(
315
- chunk_size=self.CHUNK_SIZE,
316
- limit=limit,
317
- asset_subtree_external_ids=asset_subtree_external_ids,
318
- data_set_external_ids=data_set_external_ids,
319
- aggregated_properties=["child_count", "path", "depth"],
320
- # We cannot use partitions here as it is not thread safe. This spawn multiple threads
321
- # that are not shut down until all data is downloaded. We need to be able to abort.
322
- partitions=None,
323
- ):
324
- self._collect_dependencies(asset_list, selector)
325
- yield Page(worker_id="main", items=asset_list)
314
+ cursor: str | None = None
315
+ total_count = 0
316
+ while True:
317
+ page = self.client.tool.assets.iterate(
318
+ aggregated_properties=True,
319
+ data_set_external_ids=data_set_external_ids,
320
+ asset_subtree_external_ids=asset_subtree_external_ids,
321
+ limit=self.CHUNK_SIZE,
322
+ cursor=cursor,
323
+ )
324
+ self._collect_dependencies(page.items, selector)
325
+ yield Page(worker_id="main", items=page.items)
326
+ total_count += len(page.items)
327
+ if page.next_cursor is None or (limit is not None and total_count >= limit):
328
+ break
329
+ cursor = page.next_cursor
326
330
 
327
331
  def data_to_json_chunk(
328
- self, data_chunk: Sequence[Asset], selector: AssetCentricSelector | None = None
332
+ self, data_chunk: Sequence[AssetResponse], selector: AssetCentricSelector | None = None
329
333
  ) -> list[dict[str, JsonVal]]:
330
334
  # Ensure data sets are looked up to populate cache.
331
335
  # This is to avoid looking up each data set id individually in the .dump_resource call.
@@ -333,19 +337,18 @@ class AssetIO(UploadableAssetCentricIO[Asset, AssetWrite]):
333
337
  asset_ids = {
334
338
  segment["id"]
335
339
  for item in data_chunk
336
- if isinstance(item.aggregates, AggregateResultItem)
337
- for segment in item.aggregates.path or []
338
- if "id" in segment
340
+ if isinstance(item.aggregates, AssetAggregateItem)
341
+ for segment in item.aggregates.path
339
342
  }
340
343
  self.client.lookup.assets.external_id(list(asset_ids))
341
344
 
342
345
  return [self._crud.dump_resource(item) for item in data_chunk]
343
346
 
344
- def json_to_resource(self, item_json: dict[str, JsonVal]) -> AssetWrite:
347
+ def json_to_resource(self, item_json: dict[str, JsonVal]) -> AssetRequest:
345
348
  return self._crud.load_resource(item_json)
346
349
 
347
- def retrieve(self, ids: Sequence[int]) -> AssetList:
348
- return self.client.assets.retrieve_multiple(ids)
350
+ def retrieve(self, ids: Sequence[int]) -> list[AssetResponse]:
351
+ return self.client.tool.assets.retrieve(InternalId.from_ids(ids))
349
352
 
350
353
  @classmethod
351
354
  def read_chunks(
@@ -4,11 +4,11 @@ from dataclasses import dataclass
4
4
  from typing import ClassVar, Generic, Literal, TypeVar
5
5
 
6
6
  from cognite_toolkit._cdf_tk.client import ToolkitClient
7
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, HTTPMessage, ItemsRequest
7
8
  from cognite_toolkit._cdf_tk.exceptions import ToolkitNotImplementedError
8
9
  from cognite_toolkit._cdf_tk.protocols import T_ResourceRequest, T_ResourceResponse
9
10
  from cognite_toolkit._cdf_tk.utils.collection import chunker
10
11
  from cognite_toolkit._cdf_tk.utils.fileio import MultiFileReader, SchemaColumn
11
- from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, HTTPMessage, ItemsRequest
12
12
  from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
13
13
 
14
14
  from .selectors import DataSelector
@@ -15,6 +15,13 @@ from cognite.client.data_classes.filters import Exists
15
15
  from cognite.client.data_classes.time_series import TimeSeriesProperty
16
16
 
17
17
  from cognite_toolkit._cdf_tk.client import ToolkitClient
18
+ from cognite_toolkit._cdf_tk.client.http_client import (
19
+ DataBodyRequest,
20
+ HTTPClient,
21
+ HTTPMessage,
22
+ SimpleBodyRequest,
23
+ SuccessResponse,
24
+ )
18
25
  from cognite_toolkit._cdf_tk.exceptions import ToolkitNotImplementedError
19
26
  from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
20
27
  from cognite_toolkit._cdf_tk.utils import humanize_collection
@@ -26,13 +33,6 @@ from cognite_toolkit._cdf_tk.utils.dtype_conversion import (
26
33
  )
27
34
  from cognite_toolkit._cdf_tk.utils.fileio import SchemaColumn
28
35
  from cognite_toolkit._cdf_tk.utils.fileio._readers import MultiFileReader
29
- from cognite_toolkit._cdf_tk.utils.http_client import (
30
- DataBodyRequest,
31
- HTTPClient,
32
- HTTPMessage,
33
- SimpleBodyRequest,
34
- SuccessResponse,
35
- )
36
36
  from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
37
37
 
38
38
  from ._base import Page, TableStorageIO, TableUploadableStorageIO, UploadItem