cognite-toolkit 0.7.43__py3-none-any.whl → 0.7.45__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 (57) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +6 -0
  2. cognite_toolkit/_cdf_tk/client/api/assets.py +2 -2
  3. cognite_toolkit/_cdf_tk/client/api/events.py +2 -2
  4. cognite_toolkit/_cdf_tk/client/api/filemetadata.py +150 -0
  5. cognite_toolkit/_cdf_tk/client/api/raw.py +174 -0
  6. cognite_toolkit/_cdf_tk/client/api/simulator_models.py +128 -0
  7. cognite_toolkit/_cdf_tk/client/api/simulators.py +8 -0
  8. cognite_toolkit/_cdf_tk/client/api/timeseries.py +2 -2
  9. cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +2 -1
  10. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +114 -8
  11. cognite_toolkit/_cdf_tk/client/data_classes/annotation.py +79 -0
  12. cognite_toolkit/_cdf_tk/client/data_classes/base.py +13 -3
  13. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/__init__.py +16 -0
  14. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_instance.py +143 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_references.py +8 -0
  16. cognite_toolkit/_cdf_tk/client/data_classes/dataset.py +35 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/extraction_pipeline.py +59 -0
  18. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +7 -1
  19. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_destination.py +34 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_job.py +134 -0
  21. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_mapping.py +72 -0
  22. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/__init__.py +63 -0
  23. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_auth.py +63 -0
  24. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_base.py +26 -0
  25. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_certificate.py +20 -0
  26. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_eventhub.py +31 -0
  27. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_kafka.py +53 -0
  28. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_mqtt.py +36 -0
  29. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_rest.py +49 -0
  30. cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +8 -0
  31. cognite_toolkit/_cdf_tk/client/data_classes/label.py +27 -0
  32. cognite_toolkit/_cdf_tk/client/data_classes/raw.py +3 -2
  33. cognite_toolkit/_cdf_tk/client/data_classes/securitycategory.py +24 -0
  34. cognite_toolkit/_cdf_tk/client/data_classes/sequence.py +45 -0
  35. cognite_toolkit/_cdf_tk/client/data_classes/transformation.py +140 -0
  36. cognite_toolkit/_cdf_tk/client/data_classes/workflow.py +27 -0
  37. cognite_toolkit/_cdf_tk/client/data_classes/workflow_trigger.py +63 -0
  38. cognite_toolkit/_cdf_tk/client/data_classes/workflow_version.py +155 -0
  39. cognite_toolkit/_cdf_tk/client/testing.py +5 -0
  40. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +4 -3
  41. cognite_toolkit/_cdf_tk/cruds/__init__.py +5 -1
  42. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +7 -3
  43. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +2 -0
  44. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +56 -59
  45. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +1 -1
  46. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/simulators.py +119 -0
  47. cognite_toolkit/_cdf_tk/feature_flags.py +4 -0
  48. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +34 -29
  49. cognite_toolkit/_cdf_tk/storageio/_file_content.py +22 -19
  50. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  51. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  52. cognite_toolkit/_resources/cdf.toml +1 -1
  53. cognite_toolkit/_version.py +1 -1
  54. {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/METADATA +11 -1
  55. {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/RECORD +57 -30
  56. {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/WHEEL +1 -1
  57. {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/entry_points.txt +0 -0
@@ -15,13 +15,8 @@
15
15
 
16
16
  from collections.abc import Hashable, Iterable, Sequence
17
17
  from datetime import date, datetime
18
- from typing import Any, cast, final
18
+ from typing import Any, final
19
19
 
20
- from cognite.client.data_classes import (
21
- FileMetadata,
22
- FileMetadataList,
23
- FileMetadataWrite,
24
- )
25
20
  from cognite.client.data_classes.capabilities import (
26
21
  Capability,
27
22
  DataModelInstancesAcl,
@@ -31,9 +26,9 @@ from cognite.client.data_classes.data_modeling import NodeApplyResultList, NodeI
31
26
  from cognite.client.exceptions import CogniteAPIError
32
27
  from cognite.client.utils._time import convert_data_modelling_timestamp
33
28
  from cognite.client.utils.useful_types import SequenceNotStr
34
- from rich import print
35
29
 
36
- from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId
30
+ from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataRequest, FileMetadataResponse
31
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId, InternalOrExternalId
37
32
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.extendable_cognite_file import (
38
33
  ExtendableCogniteFile,
39
34
  ExtendableCogniteFileApply,
@@ -56,11 +51,11 @@ from .datamodel import SpaceCRUD, ViewCRUD
56
51
 
57
52
 
58
53
  @final
59
- class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadata]):
54
+ class FileMetadataCRUD(ResourceContainerCRUD[ExternalId, FileMetadataRequest, FileMetadataResponse]):
60
55
  item_name = "file contents"
61
56
  folder_name = "files"
62
- resource_cls = FileMetadata
63
- resource_write_cls = FileMetadataWrite
57
+ resource_cls = FileMetadataResponse
58
+ resource_write_cls = FileMetadataRequest
64
59
  yaml_cls = FileMetadataYAML
65
60
  kind = "FileMetadata"
66
61
  dependencies = frozenset({DataSetsCRUD, GroupAllScopedCRUD, LabelCRUD, AssetCRUD})
@@ -73,7 +68,7 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
73
68
 
74
69
  @classmethod
75
70
  def get_required_capability(
76
- cls, items: Sequence[FileMetadataWrite] | None, read_only: bool
71
+ cls, items: Sequence[FileMetadataRequest] | None, read_only: bool
77
72
  ) -> Capability | list[Capability]:
78
73
  if not items and items is not None:
79
74
  return []
@@ -88,22 +83,22 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
88
83
  return FilesAcl(actions, scope)
89
84
 
90
85
  @classmethod
91
- def get_id(cls, item: FileMetadata | FileMetadataWrite | dict) -> str:
86
+ def get_id(cls, item: FileMetadataRequest | FileMetadataResponse | dict) -> ExternalId:
92
87
  if isinstance(item, dict):
93
- return item["externalId"]
88
+ return ExternalId(external_id=item["externalId"])
94
89
  if item.external_id is None:
95
90
  raise ToolkitRequiredValueError("FileMetadata must have external_id set.")
96
- return item.external_id
91
+ return ExternalId(external_id=item.external_id)
97
92
 
98
93
  @classmethod
99
- def get_internal_id(cls, item: FileMetadata | dict) -> int:
94
+ def get_internal_id(cls, item: FileMetadataResponse | dict) -> int:
100
95
  if isinstance(item, dict):
101
96
  return item["id"]
102
97
  return item.id
103
98
 
104
99
  @classmethod
105
- def dump_id(cls, id: str) -> dict[str, Any]:
106
- return {"externalId": id}
100
+ def dump_id(cls, id: ExternalId) -> dict[str, Any]:
101
+ return id.dump()
107
102
 
108
103
  @classmethod
109
104
  def get_dependent_items(cls, item: dict) -> Iterable[tuple[type[ResourceCRUD], Hashable]]:
@@ -121,69 +116,71 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
121
116
  for asset_external_id in item.get("assetExternalIds", []):
122
117
  yield AssetCRUD, ExternalId(external_id=asset_external_id)
123
118
 
124
- def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> FileMetadataWrite:
125
- if resource.get("dataSetExternalId") is not None:
126
- ds_external_id = resource.pop("dataSetExternalId")
119
+ def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> FileMetadataRequest:
120
+ if ds_external_id := resource.pop("dataSetExternalId", None):
127
121
  resource["dataSetId"] = self.client.lookup.data_sets.id(ds_external_id, is_dry_run)
128
122
  if security_categories_names := resource.pop("securityCategoryNames", []):
129
- security_categories = self.client.lookup.security_categories.id(security_categories_names, is_dry_run)
130
- resource["securityCategories"] = security_categories
131
- if "assetExternalIds" in resource:
132
- resource["assetIds"] = self.client.lookup.assets.id(resource["assetExternalIds"], is_dry_run)
133
- return FileMetadataWrite._load(resource)
134
-
135
- def dump_resource(self, resource: FileMetadata, local: dict[str, Any] | None = None) -> dict[str, Any]:
136
- dumped = resource.as_write().dump()
137
- if ds_id := dumped.pop("dataSetId", None):
138
- dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(ds_id)
123
+ resource["securityCategories"] = self.client.lookup.security_categories.id(
124
+ security_categories_names, is_dry_run
125
+ )
126
+ if asset_external_ids := resource.pop("assetExternalIds", None):
127
+ resource["assetIds"] = self.client.lookup.assets.id(asset_external_ids, is_dry_run)
128
+ return FileMetadataRequest.model_validate(resource)
129
+
130
+ def dump_resource(self, resource: FileMetadataResponse, local: dict[str, Any] | None = None) -> dict[str, Any]:
131
+ dumped = resource.as_request_resource().dump()
132
+ if data_set_id := dumped.pop("dataSetId", None):
133
+ dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(data_set_id)
139
134
  if security_categories := dumped.pop("securityCategories", []):
140
135
  dumped["securityCategoryNames"] = self.client.lookup.security_categories.external_id(security_categories)
141
136
  if asset_ids := dumped.pop("assetIds", []):
142
137
  dumped["assetExternalIds"] = self.client.lookup.assets.external_id(asset_ids)
143
138
  return dumped
144
139
 
145
- def create(self, items: Sequence[FileMetadataWrite]) -> FileMetadataList:
146
- created = FileMetadataList([])
147
- for meta in items:
148
- try:
149
- created.append(self.client.files.create(meta))
150
- except CogniteAPIError as e:
151
- if e.code == 409:
152
- print(f" [bold yellow]WARNING:[/] File {meta.external_id} already exists, skipping upload.")
153
- return created
140
+ def create(self, items: Sequence[FileMetadataRequest]) -> list[FileMetadataResponse]:
141
+ return self.client.tool.filemetadata.create(items, overwrite=True)
154
142
 
155
- def retrieve(self, ids: SequenceNotStr[str]) -> FileMetadataList:
156
- return self.client.files.retrieve_multiple(external_ids=ids, ignore_unknown_ids=True)
143
+ def retrieve(self, ids: SequenceNotStr[ExternalId]) -> list[FileMetadataResponse]:
144
+ return self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True)
157
145
 
158
- def update(self, items: Sequence[FileMetadataWrite]) -> FileMetadataList:
159
- return self.client.files.update(items, mode="replace")
146
+ def update(self, items: Sequence[FileMetadataRequest]) -> list[FileMetadataResponse]:
147
+ return self.client.tool.filemetadata.update(items, mode="replace")
160
148
 
161
- def delete(self, ids: str | int | SequenceNotStr[str | int] | None) -> int:
162
- internal_ids, external_ids = self._split_ids(ids)
163
- self.client.files.delete(id=internal_ids, external_id=external_ids)
164
- return len(cast(SequenceNotStr[str], ids))
149
+ def delete(self, ids: SequenceNotStr[InternalOrExternalId]) -> int:
150
+ if not ids:
151
+ return 0
152
+ self.client.tool.filemetadata.delete(list(ids), ignore_unknown_ids=True)
153
+ return len(ids)
165
154
 
166
155
  def _iterate(
167
156
  self,
168
157
  data_set_external_id: str | None = None,
169
158
  space: str | None = None,
170
159
  parent_ids: list[Hashable] | None = None,
171
- ) -> Iterable[FileMetadata]:
172
- return iter(self.client.files(data_set_external_ids=[data_set_external_id] if data_set_external_id else None))
173
-
174
- def count(self, ids: SequenceNotStr[str]) -> int:
160
+ ) -> Iterable[FileMetadataResponse]:
161
+ cursor: str | None = None
162
+ while True:
163
+ page = self.client.tool.filemetadata.iterate(
164
+ data_set_external_ids=[data_set_external_id] if data_set_external_id else None,
165
+ limit=1000,
166
+ cursor=cursor,
167
+ )
168
+ yield from page.items
169
+ if not page.next_cursor or not page.items:
170
+ break
171
+ cursor = page.next_cursor
172
+
173
+ def count(self, ids: SequenceNotStr[ExternalId]) -> int:
175
174
  return sum(
176
- 1
177
- for meta in self.client.files.retrieve_multiple(external_ids=list(ids), ignore_unknown_ids=True)
178
- if meta.uploaded
175
+ 1 for meta in self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True) if meta.uploaded
179
176
  )
180
177
 
181
- def drop_data(self, ids: SequenceNotStr[str]) -> int:
182
- existing = self.client.files.retrieve_multiple(external_ids=list(ids), ignore_unknown_ids=True)
178
+ def drop_data(self, ids: SequenceNotStr[ExternalId]) -> int:
179
+ existing = self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True)
183
180
  # File and FileMetadata is tightly coupled, so we need to delete the metadata and recreate it
184
181
  # without the source set to delete the file.
185
- deleted_files = self.delete(existing.as_external_ids())
186
- self.create(existing.as_write())
182
+ deleted_files = self.delete([meta.as_id() for meta in existing])
183
+ self.create([meta.as_request_resource() for meta in existing])
187
184
  return deleted_files
188
185
 
189
186
 
@@ -130,7 +130,7 @@ class RelationshipCRUD(ResourceCRUD[str, RelationshipWrite, Relationship]):
130
130
  elif type_value == "timeseries":
131
131
  yield TimeSeriesCRUD, ExternalId(external_id=id_value)
132
132
  elif type_value == "file":
133
- yield FileMetadataCRUD, id_value
133
+ yield FileMetadataCRUD, ExternalId(external_id=id_value)
134
134
  elif type_value == "event":
135
135
  yield EventCRUD, ExternalId(external_id=id_value)
136
136
 
@@ -0,0 +1,119 @@
1
+ from collections.abc import Hashable, Iterable, Sequence
2
+ from typing import Any, final
3
+
4
+ from cognite.client.data_classes.capabilities import Capability
5
+ from cognite.client.utils.useful_types import SequenceNotStr
6
+
7
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId, InternalOrExternalId
8
+ from cognite_toolkit._cdf_tk.client.data_classes.simulator_model import SimulatorModelRequest, SimulatorModelResponse
9
+ from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceCRUD
10
+ from cognite_toolkit._cdf_tk.resource_classes import SimulatorModelYAML
11
+
12
+ from .data_organization import DataSetsCRUD
13
+
14
+
15
+ @final
16
+ class SimulatorModelCRUD(ResourceCRUD[ExternalId, SimulatorModelRequest, SimulatorModelResponse]):
17
+ folder_name = "simulators"
18
+ resource_cls = SimulatorModelResponse
19
+ resource_write_cls = SimulatorModelRequest
20
+ yaml_cls = SimulatorModelYAML
21
+ kind = "SimulatorModel"
22
+ dependencies = frozenset({DataSetsCRUD})
23
+ _doc_url = "Simulator-Models/operation/create_simulator_model_simulators_models_post"
24
+
25
+ @property
26
+ def display_name(self) -> str:
27
+ return "simulator models"
28
+
29
+ @classmethod
30
+ def get_id(cls, item: SimulatorModelRequest | SimulatorModelResponse | dict) -> ExternalId:
31
+ if isinstance(item, dict):
32
+ return ExternalId(external_id=item["externalId"])
33
+ if not item.external_id:
34
+ raise KeyError("SimulatorModel must have external_id")
35
+ return ExternalId(external_id=item.external_id)
36
+
37
+ @classmethod
38
+ def get_internal_id(cls, item: SimulatorModelResponse | dict) -> int:
39
+ if isinstance(item, dict):
40
+ return item["id"]
41
+ if not item.id:
42
+ raise KeyError("SimulatorModel must have id")
43
+ return item.id
44
+
45
+ @classmethod
46
+ def dump_id(cls, id: ExternalId) -> dict[str, Any]:
47
+ return id.dump()
48
+
49
+ @classmethod
50
+ def get_required_capability(
51
+ cls, items: Sequence[SimulatorModelRequest] | None, read_only: bool
52
+ ) -> Capability | list[Capability]:
53
+ # Simulator ACLs is not yet implemented in the PySDK, which means
54
+ # that we cannot check for specific capabilities.
55
+ return []
56
+
57
+ def create(self, items: Sequence[SimulatorModelRequest]) -> list[SimulatorModelResponse]:
58
+ return self.client.tool.simulators.models.create(items)
59
+
60
+ def retrieve(self, ids: SequenceNotStr[ExternalId]) -> list[SimulatorModelResponse]:
61
+ return self.client.tool.simulators.models.retrieve(list(ids), ignore_unknown_ids=True)
62
+
63
+ def update(self, items: Sequence[SimulatorModelRequest]) -> list[SimulatorModelResponse]:
64
+ return self.client.tool.simulators.models.update(items, mode="replace")
65
+
66
+ def delete(self, ids: SequenceNotStr[ExternalId | InternalOrExternalId]) -> int:
67
+ if not ids:
68
+ return 0
69
+ self.client.tool.simulators.models.delete(list(ids))
70
+ return len(ids)
71
+
72
+ def _iterate(
73
+ self,
74
+ data_set_external_id: str | None = None,
75
+ space: str | None = None,
76
+ parent_ids: list[Hashable] | None = None,
77
+ ) -> Iterable[SimulatorModelResponse]:
78
+ # Note: The SimulatorModelsAPI doesn't support data_set_external_id filtering directly,
79
+ # so we iterate and filter in memory if needed.
80
+ cursor: str | None = None
81
+ data_set_id: int | None = None
82
+ if data_set_external_id:
83
+ data_set_id = self.client.lookup.data_sets.id(data_set_external_id, is_dry_run=False)
84
+ while True:
85
+ page = self.client.tool.simulators.models.iterate(
86
+ limit=1000,
87
+ cursor=cursor,
88
+ )
89
+ if data_set_id:
90
+ # Filter by data_set_external_id in memory
91
+ for item in page.items:
92
+ if item.data_set_id == data_set_id:
93
+ yield item
94
+ else:
95
+ yield from page.items
96
+ if not page.next_cursor or not page.items:
97
+ break
98
+ cursor = page.next_cursor
99
+
100
+ @classmethod
101
+ def get_dependent_items(cls, item: dict) -> Iterable[tuple[type[ResourceCRUD], Hashable]]:
102
+ """Returns all items that this item requires.
103
+
104
+ For example, a SimulatorModel requires a DataSet, so this method would return the
105
+ DataSetsCRUD and identifier of that dataset.
106
+ """
107
+ if "dataSetExternalId" in item:
108
+ yield DataSetsCRUD, item["dataSetExternalId"]
109
+
110
+ def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> SimulatorModelRequest:
111
+ if ds_external_id := resource.pop("dataSetExternalId", None):
112
+ resource["dataSetId"] = self.client.lookup.data_sets.id(ds_external_id, is_dry_run)
113
+ return SimulatorModelRequest.model_validate(resource)
114
+
115
+ def dump_resource(self, resource: SimulatorModelResponse, local: dict[str, Any] | None = None) -> dict[str, Any]:
116
+ dumped = resource.as_request_resource().dump()
117
+ if data_set_id := dumped.pop("dataSetId", None):
118
+ dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(data_set_id)
119
+ return dumped
@@ -61,6 +61,10 @@ class Flags(Enum):
61
61
  visible=True,
62
62
  description="Enables extended upload to support uploading individual files",
63
63
  )
64
+ SIMULATORS = FlagMetadata(
65
+ visible=True,
66
+ description="Enables the support for simulator model resources",
67
+ )
64
68
 
65
69
  def is_enabled(self) -> bool:
66
70
  return FeatureFlag.is_enabled(self)
@@ -3,16 +3,12 @@ from collections import defaultdict
3
3
  from collections.abc import Iterable, Sequence
4
4
  from typing import Any, ClassVar, Generic
5
5
 
6
- from cognite.client.data_classes import (
7
- FileMetadata,
8
- FileMetadataList,
9
- Label,
10
- LabelDefinition,
11
- )
6
+ from cognite.client.data_classes import Label, LabelDefinition
12
7
 
13
8
  from cognite_toolkit._cdf_tk.client import ToolkitClient
14
9
  from cognite_toolkit._cdf_tk.client.data_classes.asset import AssetAggregateItem, AssetRequest, AssetResponse
15
10
  from cognite_toolkit._cdf_tk.client.data_classes.event import EventRequest, EventResponse
11
+ from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataResponse
16
12
  from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalId
17
13
  from cognite_toolkit._cdf_tk.client.data_classes.timeseries import TimeSeriesRequest, TimeSeriesResponse
18
14
  from cognite_toolkit._cdf_tk.cruds import (
@@ -107,13 +103,16 @@ class AssetCentricIO(
107
103
 
108
104
  def _collect_dependencies(
109
105
  self,
110
- resources: Sequence[AssetResponse] | FileMetadataList | Sequence[TimeSeriesResponse] | Sequence[EventResponse],
106
+ resources: Sequence[AssetResponse]
107
+ | Sequence[FileMetadataResponse]
108
+ | Sequence[TimeSeriesResponse]
109
+ | Sequence[EventResponse],
111
110
  selector: AssetCentricSelector,
112
111
  ) -> None:
113
112
  for resource in resources:
114
113
  if resource.data_set_id:
115
114
  self._downloaded_data_sets_by_selector[selector].add(resource.data_set_id)
116
- if isinstance(resource, AssetResponse | FileMetadata):
115
+ if isinstance(resource, AssetResponse | FileMetadataResponse):
117
116
  for label in resource.labels or []:
118
117
  if isinstance(label, str):
119
118
  self._downloaded_labels_by_selector[selector].add(label)
@@ -146,18 +145,18 @@ class AssetCentricIO(
146
145
  return f"INTERNAL_ID_project_{project}_{internal_id!s}"
147
146
 
148
147
  def _populate_data_set_id_cache(
149
- self, chunk: Sequence[AssetResponse | FileMetadata | TimeSeriesResponse | EventResponse]
148
+ self, chunk: Sequence[AssetResponse | FileMetadataResponse | TimeSeriesResponse | EventResponse]
150
149
  ) -> None:
151
150
  data_set_ids = {item.data_set_id for item in chunk if item.data_set_id is not None}
152
151
  self.client.lookup.data_sets.external_id(list(data_set_ids))
153
152
 
154
- def _populate_security_category_cache(self, chunk: Sequence[FileMetadata | TimeSeriesResponse]) -> None:
153
+ def _populate_security_category_cache(self, chunk: Sequence[FileMetadataResponse | TimeSeriesResponse]) -> None:
155
154
  security_category_ids: set[int] = set()
156
155
  for item in chunk:
157
156
  security_category_ids.update(item.security_categories or [])
158
157
  self.client.lookup.security_categories.external_id(list(security_category_ids))
159
158
 
160
- def _populate_asset_id_cache(self, chunk: Sequence[FileMetadata | EventResponse]) -> None:
159
+ def _populate_asset_id_cache(self, chunk: Sequence[FileMetadataResponse | EventResponse]) -> None:
161
160
  asset_ids: set[int] = set()
162
161
  for item in chunk:
163
162
  asset_ids.update(item.asset_ids or [])
@@ -379,7 +378,7 @@ class AssetIO(UploadableAssetCentricIO[AssetResponse, AssetRequest]):
379
378
  current_depth += 1
380
379
 
381
380
 
382
- class FileMetadataIO(AssetCentricIO[FileMetadata]):
381
+ class FileMetadataIO(AssetCentricIO[FileMetadataResponse]):
383
382
  KIND = "FileMetadata"
384
383
  RESOURCE_TYPE = "file"
385
384
  SUPPORTED_DOWNLOAD_FORMATS = frozenset({".parquet", ".csv", ".ndjson"})
@@ -391,7 +390,7 @@ class FileMetadataIO(AssetCentricIO[FileMetadata]):
391
390
  super().__init__(client)
392
391
  self._crud = FileMetadataCRUD.create_loader(self.client)
393
392
 
394
- def as_id(self, item: FileMetadata) -> str:
393
+ def as_id(self, item: FileMetadataResponse) -> str:
395
394
  return item.external_id if item.external_id is not None else self._create_identifier(item.id)
396
395
 
397
396
  def _get_aggregator(self) -> AssetCentricAggregator:
@@ -434,25 +433,31 @@ class FileMetadataIO(AssetCentricIO[FileMetadata]):
434
433
  ]
435
434
  return file_schema + metadata_schema
436
435
 
437
- def stream_data(self, selector: AssetCentricSelector, limit: int | None = None) -> Iterable[Page[FileMetadata]]:
436
+ def stream_data(
437
+ self, selector: AssetCentricSelector, limit: int | None = None
438
+ ) -> Iterable[Page[FileMetadataResponse]]:
438
439
  asset_subtree_external_ids, data_set_external_ids = self._get_hierarchy_dataset_pair(selector)
439
- for file_list in self.client.files(
440
- chunk_size=self.CHUNK_SIZE,
441
- limit=limit,
442
- asset_subtree_external_ids=asset_subtree_external_ids,
443
- data_set_external_ids=data_set_external_ids,
444
- # We cannot use partitions here as it is not thread safe. This spawn multiple threads
445
- # that are not shut down until all data is downloaded. We need to be able to abort.
446
- partitions=None,
447
- ):
448
- self._collect_dependencies(file_list, selector)
449
- yield Page(worker_id="main", items=file_list)
450
-
451
- def retrieve(self, ids: Sequence[int]) -> FileMetadataList:
452
- return self.client.files.retrieve_multiple(ids)
440
+ cursor: str | None = None
441
+ total_count = 0
442
+ while True:
443
+ page = self.client.tool.filemetadata.iterate(
444
+ data_set_external_ids=data_set_external_ids,
445
+ asset_subtree_external_ids=asset_subtree_external_ids,
446
+ limit=self.CHUNK_SIZE,
447
+ cursor=cursor,
448
+ )
449
+ self._collect_dependencies(page.items, selector)
450
+ yield Page(worker_id="main", items=page.items)
451
+ total_count += len(page.items)
452
+ if page.next_cursor is None or (limit is not None and total_count >= limit):
453
+ break
454
+ cursor = page.next_cursor
455
+
456
+ def retrieve(self, ids: Sequence[int]) -> list[FileMetadataResponse]:
457
+ return self.client.tool.filemetadata.retrieve(InternalId.from_ids(ids))
453
458
 
454
459
  def data_to_json_chunk(
455
- self, data_chunk: Sequence[FileMetadata], selector: AssetCentricSelector | None = None
460
+ self, data_chunk: Sequence[FileMetadataResponse], selector: AssetCentricSelector | None = None
456
461
  ) -> list[dict[str, JsonVal]]:
457
462
  # Ensure data sets/assets/security-categories are looked up to populate cache.
458
463
  # This is to avoid looking up each data set id individually in the .dump_resource call
@@ -6,10 +6,11 @@ from pathlib import Path
6
6
  from typing import cast
7
7
 
8
8
  import httpx
9
- from cognite.client.data_classes import FileMetadata, FileMetadataWrite
10
- from cognite.client.data_classes.data_modeling import NodeId, ViewId
9
+ from cognite.client.data_classes.data_modeling import ViewId
11
10
 
12
11
  from cognite_toolkit._cdf_tk.client import ToolkitClient
12
+ from cognite_toolkit._cdf_tk.client.data_classes.data_modeling import NodeReference
13
+ from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataRequest, FileMetadataResponse
13
14
  from cognite_toolkit._cdf_tk.client.http_client import (
14
15
  DataBodyRequest,
15
16
  ErrorDetails,
@@ -45,21 +46,24 @@ COGNITE_FILE_VIEW = ViewId("cdf_cdm", "CogniteFile", "v1")
45
46
 
46
47
 
47
48
  @dataclass
48
- class UploadFileContentItem(UploadItem[FileMetadataWrite]):
49
+ class UploadFileContentItem(UploadItem[FileMetadataRequest]):
49
50
  file_path: Path
50
51
  mime_type: str
51
52
 
53
+ def dump(self) -> JsonVal:
54
+ return self.item.dump(camel_case=True, exclude_extra=True)
55
+
52
56
 
53
57
  @dataclass
54
58
  class MetadataWithFilePath(ResourceResponseProtocol):
55
- metadata: FileMetadata
59
+ metadata: FileMetadataResponse
56
60
  file_path: Path
57
61
 
58
- def as_write(self) -> FileMetadataWrite:
59
- return self.metadata.as_write()
62
+ def as_write(self) -> FileMetadataRequest:
63
+ return self.metadata.as_request_resource()
60
64
 
61
65
 
62
- class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePath, FileMetadataWrite]):
66
+ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePath, FileMetadataRequest]):
63
67
  SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
64
68
  SUPPORTED_COMPRESSIONS = frozenset({".gz"})
65
69
  CHUNK_SIZE = 10
@@ -116,7 +120,7 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
116
120
  )
117
121
  yield Page(items=downloaded_files, worker_id="Main")
118
122
 
119
- def _retrieve_metadata(self, identifiers: Sequence[FileIdentifier]) -> Sequence[FileMetadata] | None:
123
+ def _retrieve_metadata(self, identifiers: Sequence[FileIdentifier]) -> Sequence[FileMetadataResponse] | None:
120
124
  config = self.client.config
121
125
  responses = self.client.http_client.request_with_retries(
122
126
  message=SimpleBodyRequest(
@@ -137,12 +141,11 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
137
141
  items_data = body.get("items", [])
138
142
  if not isinstance(items_data, list):
139
143
  return None
140
- # MyPy does not understand that JsonVal is valid dict[Any, Any]
141
- return [FileMetadata._load(item) for item in items_data] # type: ignore[arg-type]
144
+ return [FileMetadataResponse.model_validate(item) for item in items_data]
142
145
 
143
146
  @staticmethod
144
- def _as_metadata_map(metadata: Sequence[FileMetadata]) -> dict[FileIdentifier, FileMetadata]:
145
- identifiers_map: dict[FileIdentifier, FileMetadata] = {}
147
+ def _as_metadata_map(metadata: Sequence[FileMetadataResponse]) -> dict[FileIdentifier, FileMetadataResponse]:
148
+ identifiers_map: dict[FileIdentifier, FileMetadataResponse] = {}
146
149
  for item in metadata:
147
150
  if item.id is not None:
148
151
  identifiers_map[FileInternalID(internal_id=item.id)] = item
@@ -158,9 +161,9 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
158
161
  ] = item
159
162
  return identifiers_map
160
163
 
161
- def _create_filepath(self, meta: FileMetadata, selector: FileIdentifierSelector) -> Path:
164
+ def _create_filepath(self, meta: FileMetadataResponse, selector: FileIdentifierSelector) -> Path:
162
165
  # We now that metadata always have name set
163
- filename = Path(sanitize_filename(cast(str, meta.name)))
166
+ filename = Path(sanitize_filename(meta.name))
164
167
  if len(filename.suffix) == 0 and meta.mime_type:
165
168
  if mime_ext := mimetypes.guess_extension(meta.mime_type):
166
169
  filename = filename.with_suffix(mime_ext)
@@ -245,12 +248,12 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
245
248
  )
246
249
  return result
247
250
 
248
- def json_to_resource(self, item_json: dict[str, JsonVal]) -> FileMetadataWrite:
251
+ def json_to_resource(self, item_json: dict[str, JsonVal]) -> FileMetadataRequest:
249
252
  return self._crud.load_resource(item_json)
250
253
 
251
254
  def upload_items(
252
255
  self,
253
- data_chunk: Sequence[UploadItem[FileMetadataWrite]],
256
+ data_chunk: Sequence[UploadItem[FileMetadataRequest]],
254
257
  http_client: HTTPClient,
255
258
  selector: FileContentSelector | None = None,
256
259
  ) -> Sequence[HTTPMessage]:
@@ -320,12 +323,12 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
320
323
 
321
324
  """
322
325
  # We know that instance_id is always set for data modeling uploads
323
- instance_id = cast(NodeId, item.item.instance_id)
326
+ instance_id = cast(NodeReference, item.item.instance_id)
324
327
  responses = http_client.request_with_retries(
325
328
  message=SimpleBodyRequest(
326
329
  endpoint_url=http_client.config.create_api_url("/files/uploadlink"),
327
330
  method="POST",
328
- body_content={"items": [{"instanceId": instance_id.dump(include_instance_type=False)}]}, # type: ignore[dict-item]
331
+ body_content={"items": [{"instanceId": instance_id.dump()}]},
329
332
  )
330
333
  )
331
334
  # We know there is only one response since we only requested one upload link
@@ -340,7 +343,7 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
340
343
 
341
344
  @classmethod
342
345
  def _create_cognite_file_node(
343
- cls, instance_id: NodeId, http_client: HTTPClient, upload_id: str, results: MutableSequence[HTTPMessage]
346
+ cls, instance_id: NodeReference, http_client: HTTPClient, upload_id: str, results: MutableSequence[HTTPMessage]
344
347
  ) -> bool:
345
348
  node_creation = http_client.request_with_retries(
346
349
  message=SimpleBodyRequest(
@@ -12,7 +12,7 @@ jobs:
12
12
  environment: dev
13
13
  name: Deploy
14
14
  container:
15
- image: cognite/toolkit:0.7.43
15
+ image: cognite/toolkit:0.7.45
16
16
  env:
17
17
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
18
18
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -10,7 +10,7 @@ jobs:
10
10
  environment: dev
11
11
  name: Deploy Dry Run
12
12
  container:
13
- image: cognite/toolkit:0.7.43
13
+ image: cognite/toolkit:0.7.45
14
14
  env:
15
15
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
16
16
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -4,7 +4,7 @@ default_env = "<DEFAULT_ENV_PLACEHOLDER>"
4
4
  [modules]
5
5
  # This is the version of the modules. It should not be changed manually.
6
6
  # It will be updated by the 'cdf modules upgrade' command.
7
- version = "0.7.43"
7
+ version = "0.7.45"
8
8
 
9
9
 
10
10
  [plugins]
@@ -1 +1 @@
1
- __version__ = "0.7.43"
1
+ __version__ = "0.7.45"
@@ -1,10 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite_toolkit
3
- Version: 0.7.43
3
+ Version: 0.7.45
4
4
  Summary: Official Cognite Data Fusion tool for project templates and configuration deployment
5
5
  Author: Cognite AS
6
6
  Author-email: Cognite AS <support@cognite.com>
7
7
  License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Build Tools
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Operating System :: OS Independent
8
18
  Requires-Dist: python-dotenv>=1.0.0
9
19
  Requires-Dist: cognite-sdk>=7.87.0,<8.0.0
10
20
  Requires-Dist: httpx>=0.28.1