cognite-toolkit 0.7.35__py3-none-any.whl → 0.7.36__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.
@@ -19,11 +19,13 @@ from cognite_toolkit._cdf_tk.commands._migrate.data_mapper import (
19
19
  AssetCentricMapper,
20
20
  CanvasMapper,
21
21
  ChartMapper,
22
+ ThreeDAssetMapper,
22
23
  ThreeDMapper,
23
24
  )
24
25
  from cognite_toolkit._cdf_tk.commands._migrate.migration_io import (
25
26
  AnnotationMigrationIO,
26
27
  AssetCentricMigrationIO,
28
+ ThreeDAssetMappingMigrationIO,
27
29
  ThreeDMigrationIO,
28
30
  )
29
31
  from cognite_toolkit._cdf_tk.commands._migrate.selectors import (
@@ -68,6 +70,7 @@ class MigrateApp(typer.Typer):
68
70
  self.command("canvas")(self.canvas)
69
71
  self.command("charts")(self.charts)
70
72
  self.command("3d")(self.three_d)
73
+ self.command("3d-mappings")(self.three_d_asset_mapping)
71
74
  # Uncomment when infield v2 config migration is ready
72
75
  # self.command("infield-configs")(self.infield_configs)
73
76
 
@@ -1054,6 +1057,104 @@ class MigrateApp(typer.Typer):
1054
1057
  )
1055
1058
  )
1056
1059
 
1060
+ @staticmethod
1061
+ def three_d_asset_mapping(
1062
+ ctx: typer.Context,
1063
+ model_id: Annotated[
1064
+ list[int] | None,
1065
+ typer.Argument(
1066
+ help="The IDs of the 3D model to migrate asset mappings for. If not provided, an interactive selection will be "
1067
+ "performed to select the."
1068
+ ),
1069
+ ] = None,
1070
+ object_3D_space: Annotated[
1071
+ str | None,
1072
+ typer.Option(
1073
+ "--object-3d-space",
1074
+ "-o",
1075
+ help="The instance space to ceate the 3D object nodes in.",
1076
+ ),
1077
+ ] = None,
1078
+ cad_node_space: Annotated[
1079
+ str | None,
1080
+ typer.Option(
1081
+ "--cad-node-space",
1082
+ "-c",
1083
+ help="The instance space to create the CAD node nodes in.",
1084
+ ),
1085
+ ] = None,
1086
+ log_dir: Annotated[
1087
+ Path,
1088
+ typer.Option(
1089
+ "--log-dir",
1090
+ "-l",
1091
+ help="Path to the directory where migration logs will be stored.",
1092
+ ),
1093
+ ] = Path(f"migration_logs_{TODAY}"),
1094
+ dry_run: Annotated[
1095
+ bool,
1096
+ typer.Option(
1097
+ "--dry-run",
1098
+ "-d",
1099
+ help="If set, the migration will not be executed, but only a report of what would be done is printed.",
1100
+ ),
1101
+ ] = False,
1102
+ verbose: Annotated[
1103
+ bool,
1104
+ typer.Option(
1105
+ "--verbose",
1106
+ "-v",
1107
+ help="Turn on to get more verbose output when running the command",
1108
+ ),
1109
+ ] = False,
1110
+ ) -> None:
1111
+ """Migrate 3D Model Asset Mappings from Asset-Centric to data modeling in CDF.
1112
+
1113
+ This command expects that the selected 3D model has already been migrated to data modeling.
1114
+ """
1115
+ client = EnvironmentVariables.create_from_environment().get_client()
1116
+ selected_ids: list[int]
1117
+ if model_id is not None:
1118
+ selected_ids = model_id
1119
+ else:
1120
+ # Interactive selection
1121
+ selected_models = ThreeDInteractiveSelect(client, "migrate").select_three_d_models("dm")
1122
+ selected_ids = [model.id for model in selected_models]
1123
+ space_selector = DataModelingSelect(client, "migrate")
1124
+ object_3D_space = space_selector.select_instance_space(
1125
+ multiselect=False,
1126
+ message="In which instance space do you want to create the 3D Object nodes?",
1127
+ include_empty=False,
1128
+ )
1129
+ cad_node_space = space_selector.select_instance_space(
1130
+ multiselect=False,
1131
+ message="In which instance space do you want to create the CAD Node nodes?",
1132
+ include_empty=False,
1133
+ )
1134
+ dry_run = questionary.confirm("Do you want to perform a dry run?", default=dry_run).ask()
1135
+ verbose = questionary.confirm("Do you want verbose output?", default=verbose).ask()
1136
+ if any(res is None for res in [dry_run, verbose]):
1137
+ raise typer.Abort()
1138
+
1139
+ if object_3D_space is None or cad_node_space is None:
1140
+ raise typer.BadParameter(
1141
+ "--object-3d-space and --cad-node-space are required when specifying IDs directly."
1142
+ )
1143
+
1144
+ cmd = MigrationCommand()
1145
+ cmd.run(
1146
+ lambda: cmd.migrate(
1147
+ selected=ThreeDModelIdSelector(ids=tuple(selected_ids)),
1148
+ data=ThreeDAssetMappingMigrationIO(
1149
+ client, object_3D_space=object_3D_space, cad_node_space=cad_node_space
1150
+ ),
1151
+ mapper=ThreeDAssetMapper(client),
1152
+ log_dir=log_dir,
1153
+ dry_run=dry_run,
1154
+ verbose=verbose,
1155
+ )
1156
+ )
1157
+
1057
1158
  @staticmethod
1058
1159
  def infield_configs(
1059
1160
  ctx: typer.Context,
@@ -4,7 +4,7 @@ from typing import Any, cast
4
4
  from pydantic import TypeAdapter
5
5
  from rich.console import Console
6
6
 
7
- from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse, QueryResponse
7
+ from cognite_toolkit._cdf_tk.client.data_classes.api_classes import QueryResponse
8
8
  from cognite_toolkit._cdf_tk.client.data_classes.infield import (
9
9
  DataExplorationConfig,
10
10
  InFieldCDMLocationConfig,
@@ -13,12 +13,11 @@ from cognite_toolkit._cdf_tk.client.data_classes.infield import (
13
13
  from cognite_toolkit._cdf_tk.client.data_classes.instance_api import (
14
14
  InstanceResponseItem,
15
15
  InstanceResult,
16
- NodeIdentifier,
16
+ TypedNodeIdentifier,
17
17
  )
18
18
  from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
19
19
  from cognite_toolkit._cdf_tk.utils.http_client import (
20
20
  HTTPClient,
21
- ItemsRequest,
22
21
  ItemsRequest2,
23
22
  RequestMessage2,
24
23
  )
@@ -56,7 +55,7 @@ class InfieldConfigAPI:
56
55
  responses.raise_for_status()
57
56
  return TypeAdapter(list[InstanceResult]).validate_python(responses.get_items())
58
57
 
59
- def retrieve(self, items: Sequence[NodeIdentifier]) -> list[InfieldLocationConfig]:
58
+ def retrieve(self, items: Sequence[TypedNodeIdentifier]) -> list[InfieldLocationConfig]:
60
59
  if len(items) > 100:
61
60
  raise ValueError("Cannot retrieve more than 100 InfieldLocationConfig items at once.")
62
61
  if not items:
@@ -73,7 +72,7 @@ class InfieldConfigAPI:
73
72
  parsed_response = QueryResponse[InstanceResponseItem].model_validate(success.body_json)
74
73
  return self._parse_retrieve_response(parsed_response)
75
74
 
76
- def delete(self, items: Sequence[InfieldLocationConfig]) -> list[NodeIdentifier]:
75
+ def delete(self, items: Sequence[InfieldLocationConfig]) -> list[TypedNodeIdentifier]:
77
76
  if len(items) > 500:
78
77
  raise ValueError("Cannot delete more than 500 InfieldLocationConfig items at once.")
79
78
 
@@ -83,18 +82,18 @@ class InfieldConfigAPI:
83
82
  else [item.as_id(), item.data_exploration_config.as_id()]
84
83
  for item in items
85
84
  )
86
- responses = self._http_client.request_with_retries(
87
- ItemsRequest(
85
+ responses = self._http_client.request_items_retries(
86
+ ItemsRequest2(
88
87
  endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
89
88
  method="POST",
90
89
  items=[identifier for sublist in identifiers for identifier in sublist],
91
90
  )
92
91
  )
93
92
  responses.raise_for_status()
94
- return PagedResponse[NodeIdentifier].model_validate(responses.get_first_body()).items
93
+ return TypeAdapter(list[TypedNodeIdentifier]).validate_python(responses.get_items())
95
94
 
96
95
  @classmethod
97
- def _retrieve_query(cls, items: Sequence[NodeIdentifier]) -> dict[str, Any]:
96
+ def _retrieve_query(cls, items: Sequence[TypedNodeIdentifier]) -> dict[str, Any]:
98
97
  return {
99
98
  "with": {
100
99
  cls.LOCATION_REF: {
@@ -184,7 +183,7 @@ class InFieldCDMConfigAPI:
184
183
  results.raise_for_status()
185
184
  return TypeAdapter(list[InstanceResult]).validate_python(results.get_items())
186
185
 
187
- def retrieve(self, items: Sequence[NodeIdentifier]) -> list[InFieldCDMLocationConfig]:
186
+ def retrieve(self, items: Sequence[TypedNodeIdentifier]) -> list[InFieldCDMLocationConfig]:
188
187
  if len(items) > 100:
189
188
  raise ValueError("Cannot retrieve more than 100 InFieldCDMLocationConfig items at once.")
190
189
  if not items:
@@ -200,23 +199,23 @@ class InFieldCDMConfigAPI:
200
199
  parsed_response = QueryResponse[InstanceResponseItem].model_validate(success.body_json)
201
200
  return self._parse_retrieve_response(parsed_response)
202
201
 
203
- def delete(self, items: Sequence[InFieldCDMLocationConfig]) -> list[NodeIdentifier]:
202
+ def delete(self, items: Sequence[InFieldCDMLocationConfig]) -> list[TypedNodeIdentifier]:
204
203
  if len(items) > 500:
205
204
  raise ValueError("Cannot delete more than 500 InFieldCDMLocationConfig items at once.")
206
205
 
207
- identifiers: Sequence = [item.as_id() for item in items]
208
- responses = self._http_client.request_with_retries(
209
- ItemsRequest(
206
+ identifiers = [item.as_id() for item in items]
207
+ responses = self._http_client.request_items_retries(
208
+ ItemsRequest2(
210
209
  endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
211
210
  method="POST",
212
- items=identifiers, # type: ignore[arg-type]
211
+ items=identifiers,
213
212
  )
214
213
  )
215
214
  responses.raise_for_status()
216
- return PagedResponse[NodeIdentifier].model_validate(responses.get_first_body()).items
215
+ return TypeAdapter(list[TypedNodeIdentifier]).validate_python(responses.get_items())
217
216
 
218
217
  @classmethod
219
- def _retrieve_query(cls, items: Sequence[NodeIdentifier]) -> dict[str, Any]:
218
+ def _retrieve_query(cls, items: Sequence[TypedNodeIdentifier]) -> dict[str, Any]:
220
219
  return {
221
220
  "with": {
222
221
  cls.LOCATION_REF: {
@@ -8,7 +8,6 @@ from cognite_toolkit._cdf_tk.client.data_classes.streams import StreamRequest, S
8
8
  from cognite_toolkit._cdf_tk.utils.http_client import (
9
9
  HTTPClient,
10
10
  ItemsRequest2,
11
- ParamRequest,
12
11
  RequestMessage2,
13
12
  )
14
13
 
@@ -46,13 +45,13 @@ class StreamsAPI:
46
45
  Args:
47
46
  external_id: External ID of the stream to delete.
48
47
  """
49
- responses = self._http_client.request_with_retries(
50
- ParamRequest(
48
+ response = self._http_client.request_single_retries(
49
+ RequestMessage2(
51
50
  endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/{external_id}"),
52
51
  method="DELETE",
53
52
  )
54
53
  )
55
- responses.raise_for_status()
54
+ _ = response.get_success_or_raise()
56
55
 
57
56
  def list(self) -> list[StreamResponse]:
58
57
  """List streams.
@@ -1,14 +1,23 @@
1
- from collections.abc import Sequence
1
+ from collections import defaultdict
2
+ from collections.abc import Iterable, Sequence
3
+ from typing import Any, TypeVar
2
4
 
5
+ from pydantic import TypeAdapter
3
6
  from rich.console import Console
4
7
 
5
- from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse
6
- from cognite_toolkit._cdf_tk.client.data_classes.three_d import ThreeDModelClassicRequest, ThreeDModelResponse
8
+ from cognite_toolkit._cdf_tk.client.data_classes.api_classes import InternalIdRequest, PagedResponse
9
+ from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
10
+ AssetMappingClassicRequest,
11
+ AssetMappingDMRequest,
12
+ AssetMappingResponse,
13
+ ThreeDModelClassicRequest,
14
+ ThreeDModelResponse,
15
+ )
16
+ from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
7
17
  from cognite_toolkit._cdf_tk.utils.http_client import (
8
18
  HTTPClient,
9
- ItemsRequest,
19
+ ItemsRequest2,
10
20
  RequestMessage2,
11
- SimpleBodyRequest,
12
21
  )
13
22
  from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
14
23
 
@@ -37,16 +46,15 @@ class ThreeDModelAPI:
37
46
  return []
38
47
  if len(models) > self.MAX_CLASSIC_MODELS_PER_CREATE_REQUEST:
39
48
  raise ValueError("Cannot create more than 1000 3D models in a single request.")
40
- responses = self._http_client.request_with_retries(
41
- ItemsRequest(
49
+ responses = self._http_client.request_items_retries(
50
+ ItemsRequest2(
42
51
  endpoint_url=self._config.create_api_url(self.ENDPOINT),
43
52
  method="POST",
44
- items=list(models),
53
+ items=models,
45
54
  )
46
55
  )
47
56
  responses.raise_for_status()
48
- body = responses.get_first_body()
49
- return PagedResponse[ThreeDModelResponse].model_validate(body).items
57
+ return TypeAdapter(list[ThreeDModelResponse]).validate_python(responses.get_items())
50
58
 
51
59
  def delete(self, ids: Sequence[int]) -> None:
52
60
  """Delete 3D models by their IDs.
@@ -58,11 +66,11 @@ class ThreeDModelAPI:
58
66
  return None
59
67
  if len(ids) > self.MAX_MODELS_PER_DELETE_REQUEST:
60
68
  raise ValueError("Cannot delete more than 1000 3D models in a single request.")
61
- responses = self._http_client.request_with_retries(
62
- SimpleBodyRequest(
69
+ responses = self._http_client.request_items_retries(
70
+ ItemsRequest2(
63
71
  endpoint_url=self._config.create_api_url(self.ENDPOINT + "/delete"),
64
72
  method="POST",
65
- body_content={"items": [{"id": id_} for id_ in ids]},
73
+ items=InternalIdRequest.from_ids(list(ids)),
66
74
  )
67
75
  )
68
76
  responses.raise_for_status()
@@ -126,6 +134,251 @@ class ThreeDModelAPI:
126
134
  return results
127
135
 
128
136
 
137
+ T_RequestMapping = TypeVar("T_RequestMapping", bound=AssetMappingClassicRequest | AssetMappingDMRequest)
138
+
139
+
140
+ class ThreeDAssetMappingAPI:
141
+ ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
142
+ CREATE_CLASSIC_MAX_MAPPINGS_PER_REQUEST = 1000
143
+ CREATE_DM_MAX_MAPPINGS_PER_REQUEST = 100
144
+ DELETE_CLASSIC_MAX_MAPPINGS_PER_REQUEST = 1000
145
+ DELETE_DM_MAX_MAPPINGS_PER_REQUEST = 100
146
+ LIST_REQUEST_MAX_LIMIT = 1000
147
+
148
+ def __init__(self, http_client: HTTPClient, console: Console) -> None:
149
+ self._http_client = http_client
150
+ self._console = console
151
+ self._config = http_client.config
152
+
153
+ def create(self, mappings: Sequence[AssetMappingClassicRequest]) -> list[AssetMappingResponse]:
154
+ """Create 3D asset mappings.
155
+
156
+ Args:
157
+ mappings (Sequence[AssetMappingClassicRequest]):
158
+ The 3D asset mapping(s) to create.
159
+
160
+ Returns:
161
+ list[AssetMappingResponse]: The created 3D asset mapping(s).
162
+ """
163
+ results: list[AssetMappingResponse] = []
164
+ for endpoint, model_id, revision_id, revision_mappings in self._chunk_mappings_by_endpoint(
165
+ mappings, self.CREATE_CLASSIC_MAX_MAPPINGS_PER_REQUEST
166
+ ):
167
+ responses = self._http_client.request_items_retries(
168
+ ItemsRequest2(
169
+ endpoint_url=self._config.create_api_url(endpoint),
170
+ method="POST",
171
+ items=revision_mappings,
172
+ )
173
+ )
174
+ responses.raise_for_status()
175
+ items = responses.get_items()
176
+ for item in items:
177
+ # We append modelId and revisionId to each item since the API does not return them
178
+ # this is needed to fully populate the AssetMappingResponse data class
179
+ item["modelId"] = model_id
180
+ item["revisionId"] = revision_id
181
+ results.extend(TypeAdapter(list[AssetMappingResponse]).validate_python(items))
182
+ return results
183
+
184
+ def create_dm(
185
+ self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str
186
+ ) -> list[AssetMappingResponse]:
187
+ """Create 3D asset mappings in Data Modeling format.
188
+
189
+ Args:
190
+ mappings (Sequence[AssetMappingDMRequest]):
191
+ The 3D asset mapping(s) to create
192
+ object_3d_space (str):
193
+ The instance space where the Cognite3DObject are located.
194
+ cad_node_space (str):
195
+ The instance space where the CogniteCADNode are located.
196
+ Returns:
197
+ list[AssetMappingResponse]: The created 3D asset mapping(s).
198
+ """
199
+ results: list[AssetMappingResponse] = []
200
+ for endpoint, model_id, revision_id, revision_mappings in self._chunk_mappings_by_endpoint(
201
+ mappings, self.CREATE_DM_MAX_MAPPINGS_PER_REQUEST
202
+ ):
203
+ responses = self._http_client.request_items_retries(
204
+ ItemsRequest2(
205
+ endpoint_url=self._config.create_api_url(endpoint),
206
+ method="POST",
207
+ items=revision_mappings,
208
+ extra_body_fields={
209
+ "dmsContextualizationConfig": {
210
+ "object3DSpace": object_3d_space,
211
+ "cadNodeSpace": cad_node_space,
212
+ }
213
+ },
214
+ )
215
+ )
216
+ responses.raise_for_status()
217
+ items = responses.get_items()
218
+ for item in items:
219
+ # We append modelId and revisionId to each item since the API does not return them
220
+ # this is needed to fully populate the AssetMappingResponse data class
221
+ item["modelId"] = model_id
222
+ item["revisionId"] = revision_id
223
+ results.extend(TypeAdapter(list[AssetMappingResponse]).validate_python(items))
224
+ return results
225
+
226
+ @classmethod
227
+ def _chunk_mappings_by_endpoint(
228
+ cls, mappings: Sequence[T_RequestMapping], chunk_size: int
229
+ ) -> Iterable[tuple[str, int, int, list[T_RequestMapping]]]:
230
+ chunked_mappings: dict[tuple[int, int], list[T_RequestMapping]] = defaultdict(list)
231
+ for mapping in mappings:
232
+ key = mapping.model_id, mapping.revision_id
233
+ chunked_mappings[key].append(mapping)
234
+ for (model_id, revision_id), revision_mappings in chunked_mappings.items():
235
+ endpoint = cls.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
236
+ for chunk in chunker_sequence(revision_mappings, chunk_size):
237
+ yield endpoint, model_id, revision_id, chunk
238
+
239
+ def delete(self, mappings: Sequence[AssetMappingClassicRequest]) -> None:
240
+ """Delete 3D asset mappings.
241
+
242
+ Args:
243
+ mappings (Sequence[AssetMappingClassicRequest]):
244
+ The 3D asset mapping(s) to delete.
245
+ """
246
+ for endpoint, *_, revision_mappings in self._chunk_mappings_by_endpoint(
247
+ mappings, self.DELETE_CLASSIC_MAX_MAPPINGS_PER_REQUEST
248
+ ):
249
+ responses = self._http_client.request_items_retries(
250
+ ItemsRequest2(
251
+ endpoint_url=self._config.create_api_url(f"{endpoint}/delete"),
252
+ method="DELETE",
253
+ items=revision_mappings,
254
+ )
255
+ )
256
+ responses.raise_for_status()
257
+ return None
258
+
259
+ def delete_dm(self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str) -> None:
260
+ """Delete 3D asset mappings in Data Modeling format.
261
+
262
+ Args:
263
+ mappings (Sequence[AssetMappingDMRequest]):
264
+ The 3D asset mapping(s) to delete.
265
+ object_3d_space (str):
266
+ The instance space where the Cognite3DObject are located.
267
+ cad_node_space (str):
268
+ The instance space where the CogniteCADNode are located.
269
+ """
270
+ for endpoint, *_, revision_mappings in self._chunk_mappings_by_endpoint(
271
+ mappings, self.DELETE_DM_MAX_MAPPINGS_PER_REQUEST
272
+ ):
273
+ responses = self._http_client.request_items_retries(
274
+ ItemsRequest2(
275
+ endpoint_url=self._config.create_api_url(f"{endpoint}/delete"),
276
+ method="DELETE",
277
+ items=revision_mappings,
278
+ extra_body_fields={
279
+ "dmsContextualizationConfig": {
280
+ "object3DSpace": object_3d_space,
281
+ "cadNodeSpace": cad_node_space,
282
+ }
283
+ },
284
+ )
285
+ )
286
+ responses.raise_for_status()
287
+ return None
288
+
289
+ def iterate(
290
+ self,
291
+ model_id: int,
292
+ revision_id: int,
293
+ asset_ids: list[int] | None = None,
294
+ asset_instance_ids: list[str] | None = None,
295
+ node_ids: list[int] | None = None,
296
+ tree_indexes: list[int] | None = None,
297
+ get_dms_instances: bool = False,
298
+ limit: int = 100,
299
+ cursor: str | None = None,
300
+ ) -> PagedResponse[AssetMappingResponse]:
301
+ if not (0 < limit <= self.LIST_REQUEST_MAX_LIMIT):
302
+ raise ValueError(f"Limit must be between 1 and {self.LIST_REQUEST_MAX_LIMIT}, got {limit}.")
303
+ if sum(param is not None for param in [asset_ids, asset_instance_ids, node_ids, tree_indexes]) > 1:
304
+ raise ValueError("Only one of asset_ids, asset_instance_ids, node_ids, or tree_indexes can be provided.")
305
+ body: dict[str, Any] = {
306
+ "getDmsInstances": get_dms_instances,
307
+ "limit": limit,
308
+ }
309
+ if asset_ids is not None:
310
+ if not (0 < len(asset_ids) <= 100):
311
+ raise ValueError("asset_ids must contain between 1 and 100 IDs.")
312
+ body["filter"] = {"assetIds": asset_ids}
313
+ elif asset_instance_ids is not None:
314
+ if not (0 < len(asset_instance_ids) <= 100):
315
+ raise ValueError("asset_instance_ids must contain between 1 and 100 IDs.")
316
+ body["filter"] = {"assetInstanceIds": asset_instance_ids}
317
+ elif node_ids is not None:
318
+ if not (0 < len(node_ids) <= 100):
319
+ raise ValueError("node_ids must contain between 1 and 100 IDs.")
320
+ body["filter"] = {"nodeIds": node_ids}
321
+ elif tree_indexes is not None:
322
+ if not (0 < len(tree_indexes) <= 100):
323
+ raise ValueError("tree_indexes must contain between 1 and 100 indexes.")
324
+ body["filter"] = {"treeIndexes": tree_indexes}
325
+ if cursor is not None:
326
+ body["cursor"] = cursor
327
+
328
+ endpoint = self.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
329
+ responses = self._http_client.request_single_retries(
330
+ RequestMessage2(
331
+ endpoint_url=self._config.create_api_url(f"{endpoint}/list"),
332
+ method="POST",
333
+ body_content=body,
334
+ )
335
+ )
336
+ success_response = responses.get_success_or_raise()
337
+ body_json = success_response.body_json
338
+ # Add modelId and revisionId to items since the API does not return them
339
+ for item in body_json.get("items", []):
340
+ item["modelId"] = model_id
341
+ item["revisionId"] = revision_id
342
+ return PagedResponse[AssetMappingResponse].model_validate(body_json)
343
+
344
+ def list(
345
+ self,
346
+ model_id: int,
347
+ revision_id: int,
348
+ asset_ids: list[int] | None = None,
349
+ asset_instance_ids: list[str] | None = None,
350
+ node_ids: list[int] | None = None,
351
+ tree_indexes: list[int] | None = None,
352
+ get_dms_instances: bool = False,
353
+ limit: int | None = 100,
354
+ ) -> list[AssetMappingResponse]:
355
+ results: list[AssetMappingResponse] = []
356
+ cursor: str | None = None
357
+ while True:
358
+ request_limit = (
359
+ self.LIST_REQUEST_MAX_LIMIT if limit is None else min(limit - len(results), self.LIST_REQUEST_MAX_LIMIT)
360
+ )
361
+ if request_limit <= 0:
362
+ break
363
+ page = self.iterate(
364
+ model_id=model_id,
365
+ revision_id=revision_id,
366
+ asset_ids=asset_ids,
367
+ asset_instance_ids=asset_instance_ids,
368
+ node_ids=node_ids,
369
+ tree_indexes=tree_indexes,
370
+ get_dms_instances=get_dms_instances,
371
+ limit=request_limit,
372
+ cursor=cursor,
373
+ )
374
+ results.extend(page.items)
375
+ if page.next_cursor is None:
376
+ break
377
+ cursor = page.next_cursor
378
+ return results
379
+
380
+
129
381
  class ThreeDAPI:
130
382
  def __init__(self, http_client: HTTPClient, console: Console) -> None:
131
383
  self.models = ThreeDModelAPI(http_client, console)
384
+ self.asset_mappings = ThreeDAssetMappingAPI(http_client, console)
@@ -2,6 +2,8 @@ from typing import Generic, TypeVar
2
2
 
3
3
  from pydantic import BaseModel, Field, JsonValue
4
4
 
5
+ from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import RequestResource
6
+
5
7
  T = TypeVar("T", bound=BaseModel)
6
8
 
7
9
 
@@ -15,3 +17,14 @@ class QueryResponse(BaseModel, Generic[T]):
15
17
  typing: dict[str, JsonValue] | None = None
16
18
  next_cursor: dict[str, str] = Field(alias="nextCursor")
17
19
  debug: dict[str, JsonValue] | None = None
20
+
21
+
22
+ class InternalIdRequest(RequestResource):
23
+ id: int
24
+
25
+ def as_id(self) -> int:
26
+ return self.id
27
+
28
+ @classmethod
29
+ def from_ids(cls, ids: list[int]) -> list["InternalIdRequest"]:
30
+ return [cls(id=id_) for id_ in ids]
@@ -3,7 +3,7 @@ from abc import ABC, abstractmethod
3
3
  from collections import UserList
4
4
  from typing import TYPE_CHECKING, Any, Generic, TypeVar
5
5
 
6
- from pydantic import BaseModel, ConfigDict
6
+ from pydantic import ConfigDict
7
7
  from pydantic.alias_generators import to_camel
8
8
 
9
9
  from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import BaseModelObject, RequestResource
@@ -31,21 +31,17 @@ class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
31
31
  return self.as_request_resource()
32
32
 
33
33
 
34
- class Identifier(BaseModel):
34
+ class Identifier(RequestResource, ABC):
35
35
  """Base class for all identifier classes."""
36
36
 
37
37
  model_config = ConfigDict(alias_generator=to_camel, extra="ignore", populate_by_name=True, frozen=True)
38
38
 
39
- def dump(self, include_type: bool = True) -> dict[str, Any]:
40
- """Dump the identifier to a dictionary.
39
+ def dump(self, camel_case: bool = True, include_type: bool = True) -> dict[str, Any]:
40
+ """Dump the resource to a dictionary.
41
41
 
42
- Args:
43
- include_type (bool): Whether to include the type of the identifier in the output.
44
-
45
- Returns:
46
- dict[str, Any]: The dumped identifier.
42
+ This is the default serialization method for request resources.
47
43
  """
48
- return self.model_dump(mode="json", by_alias=True, exclude_defaults=not include_type)
44
+ return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=not include_type)
49
45
 
50
46
  def as_id(self) -> Self:
51
47
  return self
@@ -15,19 +15,19 @@ class TypedInstanceIdentifier(Identifier):
15
15
  external_id: str
16
16
 
17
17
 
18
- class InstanceIdentifier(Identifier):
19
- space: str
20
- external_id: str
21
-
22
-
23
- class NodeIdentifier(TypedInstanceIdentifier):
18
+ class TypedNodeIdentifier(TypedInstanceIdentifier):
24
19
  instance_type: Literal["node"] = "node"
25
20
 
26
21
 
27
- class EdgeIdentifier(TypedInstanceIdentifier):
22
+ class TypedEdgeIdentifier(TypedInstanceIdentifier):
28
23
  instance_type: Literal["edge"] = "edge"
29
24
 
30
25
 
26
+ class InstanceIdentifier(Identifier):
27
+ space: str
28
+ external_id: str
29
+
30
+
31
31
  class InstanceResult(BaseModelObject):
32
32
  instance_type: InstanceType
33
33
  version: int
@@ -1,5 +1,8 @@
1
+ from collections.abc import Hashable
1
2
  from typing import Literal
2
3
 
4
+ from pydantic import Field
5
+
3
6
  from .base import BaseModelObject, RequestResource, ResponseResource
4
7
 
5
8
 
@@ -51,3 +54,59 @@ class ThreeDModelResponse(ResponseResource[ThreeDModelRequest]):
51
54
  return ThreeDModelClassicRequest._load(self.dump())
52
55
  else:
53
56
  return ThreeDModelDMSRequest._load(self.dump())
57
+
58
+
59
+ class AssetMappingDMRequest(RequestResource):
60
+ node_id: int
61
+ asset_instance_id: NodeReference
62
+ # These fields are part of the path request and not the body schema.
63
+ model_id: int = Field(exclude=True)
64
+ revision_id: int = Field(exclude=True)
65
+
66
+ def as_id(self) -> Hashable:
67
+ return (
68
+ self.model_id,
69
+ self.revision_id,
70
+ self.node_id,
71
+ self.asset_instance_id.space,
72
+ self.asset_instance_id.external_id,
73
+ )
74
+
75
+
76
+ class AssetMappingClassicRequest(RequestResource):
77
+ node_id: int
78
+ asset_id: int | None = None
79
+ asset_instance_id: NodeReference | None = None
80
+ # These fields are part of the path request and not the body schema.
81
+ model_id: int = Field(exclude=True)
82
+ revision_id: int = Field(exclude=True)
83
+
84
+ def as_id(self) -> Hashable:
85
+ if self.asset_id:
86
+ return self.model_id, self.revision_id, self.node_id, self.asset_id
87
+ elif self.asset_instance_id:
88
+ return (
89
+ self.model_id,
90
+ self.revision_id,
91
+ self.node_id,
92
+ self.asset_instance_id.space,
93
+ self.asset_instance_id.external_id,
94
+ )
95
+ else:
96
+ raise AttributeError("asset_id or asset_instance_id is required")
97
+
98
+
99
+ class AssetMappingResponse(ResponseResource[AssetMappingClassicRequest]):
100
+ node_id: int
101
+ asset_id: int | None = None
102
+ asset_instance_id: NodeReference | None = None
103
+ tree_index: int | None = None
104
+ subtree_size: int | None = None
105
+ # These fields are part of the path request and response, but they are included here for convenience.
106
+ model_id: int = Field(exclude=True)
107
+ revision_id: int = Field(exclude=True)
108
+
109
+ def as_request_resource(self) -> AssetMappingClassicRequest:
110
+ return AssetMappingClassicRequest.model_validate(
111
+ {**self.dump(), "modelId": self.model_id, "revisionId": self.revision_id}
112
+ )
@@ -29,7 +29,13 @@ from cognite_toolkit._cdf_tk.client.data_classes.legacy.canvas import (
29
29
  )
30
30
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.charts import Chart, ChartWrite
31
31
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import ResourceViewMappingApply
32
- from cognite_toolkit._cdf_tk.client.data_classes.three_d import RevisionStatus, ThreeDModelResponse
32
+ from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
33
+ AssetMappingDMRequest,
34
+ AssetMappingResponse,
35
+ NodeReference,
36
+ RevisionStatus,
37
+ ThreeDModelResponse,
38
+ )
33
39
  from cognite_toolkit._cdf_tk.commands._migrate.conversion import DirectRelationCache, asset_centric_to_dm
34
40
  from cognite_toolkit._cdf_tk.commands._migrate.data_classes import (
35
41
  Model,
@@ -469,3 +475,45 @@ class ThreeDMapper(DataMapper[ThreeDSelector, ThreeDModelResponse, ThreeDMigrati
469
475
  return "PointCloud"
470
476
  else:
471
477
  return None
478
+
479
+
480
+ class ThreeDAssetMapper(DataMapper[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
481
+ def __init__(self, client: ToolkitClient) -> None:
482
+ self.client = client
483
+
484
+ def map(
485
+ self, source: Sequence[AssetMappingResponse]
486
+ ) -> Sequence[tuple[AssetMappingDMRequest | None, MigrationIssue]]:
487
+ output: list[tuple[AssetMappingDMRequest | None, MigrationIssue]] = []
488
+ self._populate_cache(source)
489
+ for item in source:
490
+ mapped_item, issue = self._map_single_item(item)
491
+ output.append((mapped_item, issue))
492
+ return output
493
+
494
+ def _populate_cache(self, source: Sequence[AssetMappingResponse]) -> None:
495
+ asset_ids: set[int] = set()
496
+ for mapping in source:
497
+ if mapping.asset_id is not None:
498
+ asset_ids.add(mapping.asset_id)
499
+ self.client.migration.lookup.assets(list(asset_ids))
500
+
501
+ def _map_single_item(
502
+ self, item: AssetMappingResponse
503
+ ) -> tuple[AssetMappingDMRequest | None, ThreeDModelMigrationIssue]:
504
+ issue = ThreeDModelMigrationIssue(model_name=f"AssetMapping_{item.model_id}", model_id=item.model_id)
505
+ asset_instance_id = item.asset_instance_id
506
+ if item.asset_id and asset_instance_id is None:
507
+ asset_node_id = self.client.migration.lookup.assets(item.asset_id)
508
+ if asset_node_id is None:
509
+ issue.error_message.append(f"Missing asset instance for asset ID {item.asset_id!r}")
510
+ return None, issue
511
+ asset_instance_id = NodeReference(space=asset_node_id.space, externalId=asset_node_id.external_id)
512
+
513
+ if asset_instance_id is None:
514
+ issue.error_message.append("Neither assetInstanceId nor assetId provided for mapping.")
515
+ return None, issue
516
+ mapped_request = AssetMappingDMRequest(
517
+ modelId=item.model_id, revisionId=item.revision_id, nodeId=item.node_id, assetInstanceId=asset_instance_id
518
+ )
519
+ return mapped_request, issue
@@ -1,12 +1,16 @@
1
1
  from collections.abc import Iterable, Iterator, Mapping, Sequence
2
- from typing import ClassVar, cast
2
+ from typing import ClassVar, Literal, cast
3
3
 
4
4
  from cognite.client.data_classes import Annotation
5
5
  from cognite.client.data_classes.data_modeling import EdgeId, InstanceApply, NodeId
6
6
 
7
7
  from cognite_toolkit._cdf_tk.client import ToolkitClient
8
8
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.pending_instances_ids import PendingInstanceId
9
- from cognite_toolkit._cdf_tk.client.data_classes.three_d import ThreeDModelResponse
9
+ from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
10
+ AssetMappingDMRequest,
11
+ AssetMappingResponse,
12
+ ThreeDModelResponse,
13
+ )
10
14
  from cognite_toolkit._cdf_tk.commands._migrate.data_classes import ThreeDMigrationRequest
11
15
  from cognite_toolkit._cdf_tk.constants import MISSING_EXTERNAL_ID, MISSING_INSTANCE_SPACE
12
16
  from cognite_toolkit._cdf_tk.exceptions import ToolkitNotImplementedError, ToolkitValueError
@@ -14,6 +18,7 @@ from cognite_toolkit._cdf_tk.storageio import (
14
18
  AnnotationIO,
15
19
  HierarchyIO,
16
20
  InstanceIO,
21
+ T_Selector,
17
22
  UploadableStorageIO,
18
23
  )
19
24
  from cognite_toolkit._cdf_tk.storageio._base import Page, UploadItem
@@ -366,6 +371,14 @@ class AnnotationMigrationIO(
366
371
 
367
372
 
368
373
  class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse, ThreeDMigrationRequest]):
374
+ """IO class for downloading and migrating 3D models.
375
+
376
+ Args:
377
+ client: The ToolkitClient to use for CDF interactions.
378
+ data_model_type: The type of 3D data model to download. Either "classic" or "DM".
379
+
380
+ """
381
+
369
382
  KIND = "3DMigration"
370
383
  SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
371
384
  SUPPORTED_COMPRESSIONS = frozenset({".gz"})
@@ -375,9 +388,22 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
375
388
  UPLOAD_ENDPOINT = "/3d/migrate/models"
376
389
  REVISION_ENDPOINT = "/3d/migrate/revisions"
377
390
 
391
+ def __init__(self, client: ToolkitClient, data_model_type: Literal["classic", "data modeling"] = "classic") -> None:
392
+ super().__init__(client)
393
+ self.data_model_type = data_model_type
394
+
378
395
  def as_id(self, item: ThreeDModelResponse) -> str:
379
396
  return f"{item.name}_{item.id!s}"
380
397
 
398
+ def _is_selected(self, item: ThreeDModelResponse, included_models: set[int] | None) -> bool:
399
+ return self._is_correct_type(item) and (included_models is None or item.id in included_models)
400
+
401
+ def _is_correct_type(self, item: ThreeDModelResponse) -> bool:
402
+ if self.data_model_type == "classic":
403
+ return item.space is None
404
+ else:
405
+ return item.space is not None
406
+
381
407
  def stream_data(self, selector: ThreeDSelector, limit: int | None = None) -> Iterable[Page[ThreeDModelResponse]]:
382
408
  published: bool | None = None
383
409
  if isinstance(selector, ThreeDModelFilteredSelector):
@@ -392,12 +418,7 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
392
418
  response = self.client.tool.three_d.models.iterate(
393
419
  published=published, include_revision_info=True, limit=request_limit, cursor=cursor
394
420
  )
395
- # Only include asset-centric 3D models
396
- items = [
397
- item
398
- for item in response.items
399
- if item.space is None and (included_models is None or item.id in included_models)
400
- ]
421
+ items = [item for item in response.items if self._is_selected(item, included_models)]
401
422
  total += len(items)
402
423
  if items:
403
424
  yield Page(worker_id="main", items=items, next_cursor=response.next_cursor)
@@ -454,3 +475,93 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
454
475
  )
455
476
  results.extend(revision.as_item_responses(data.source_id))
456
477
  return results
478
+
479
+
480
+ class ThreeDAssetMappingMigrationIO(UploadableStorageIO[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
481
+ KIND = "3DMigrationAssetMapping"
482
+ SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
483
+ SUPPORTED_COMPRESSIONS = frozenset({".gz"})
484
+ SUPPORTED_READ_FORMATS = frozenset({".ndjson"})
485
+ DOWNLOAD_LIMIT = 1000
486
+ CHUNK_SIZE = 100
487
+ UPLOAD_ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
488
+
489
+ def __init__(self, client: ToolkitClient, object_3D_space: str, cad_node_space: str) -> None:
490
+ super().__init__(client)
491
+ self.object_3D_space = object_3D_space
492
+ self.cad_node_space = cad_node_space
493
+ # We can only migrate asset mappings for 3D models that are already migrated to data modeling.
494
+ self._3D_io = ThreeDMigrationIO(client, data_model_type="data modeling")
495
+
496
+ def as_id(self, item: AssetMappingResponse) -> str:
497
+ return f"AssetMapping_{item.model_id!s}_{item.revision_id!s}_{item.asset_id!s}"
498
+
499
+ def stream_data(self, selector: ThreeDSelector, limit: int | None = None) -> Iterable[Page[AssetMappingResponse]]:
500
+ total = 0
501
+ for three_d_page in self._3D_io.stream_data(selector, None):
502
+ for model in three_d_page.items:
503
+ if model.last_revision_info is None or model.last_revision_info.revision_id is None:
504
+ # No revisions, so no asset mappings to
505
+ continue
506
+ cursor: str | None = None
507
+ while True:
508
+ request_limit = (
509
+ min(self.DOWNLOAD_LIMIT, limit - total) if limit is not None else self.DOWNLOAD_LIMIT
510
+ )
511
+ if limit is not None and total >= limit:
512
+ return
513
+ response = self.client.tool.three_d.asset_mappings.iterate(
514
+ model_id=model.id,
515
+ revision_id=model.last_revision_info.revision_id,
516
+ cursor=cursor,
517
+ limit=request_limit,
518
+ )
519
+ items = response.items
520
+ total += len(items)
521
+ if items:
522
+ yield Page(worker_id="main", items=items, next_cursor=response.next_cursor)
523
+ if response.next_cursor is None:
524
+ break
525
+ cursor = response.next_cursor
526
+
527
+ def count(self, selector: ThreeDSelector) -> int | None:
528
+ # There is no efficient way to count 3D asset mappings in CDF.
529
+ return None
530
+
531
+ def upload_items(
532
+ self,
533
+ data_chunk: Sequence[UploadItem[AssetMappingDMRequest]],
534
+ http_client: HTTPClient,
535
+ selector: T_Selector | None = None,
536
+ ) -> Sequence[HTTPMessage]:
537
+ """Migrate 3D asset mappings by uploading them to the migrate/asset-mappings endpoint."""
538
+ if not data_chunk:
539
+ return []
540
+ # Assume all items in the chunk belong to the same model and revision, they should
541
+ # if the .stream_data method is used for downloading.
542
+ first = data_chunk[0]
543
+ model_id = first.item.model_id
544
+ revision_id = first.item.revision_id
545
+ endpoint = self.UPLOAD_ENDPOINT.format(modelId=model_id, revisionId=revision_id)
546
+ responses = http_client.request_with_retries(
547
+ ItemsRequest(
548
+ endpoint_url=self.client.config.create_api_url(endpoint),
549
+ method="POST",
550
+ items=list(data_chunk),
551
+ extra_body_fields={
552
+ "dmsContextualizationConfig": {
553
+ "object3DSpace": self.object_3D_space,
554
+ "cadNodeSpace": self.cad_node_space,
555
+ }
556
+ },
557
+ )
558
+ )
559
+ return responses
560
+
561
+ def json_to_resource(self, item_json: dict[str, JsonVal]) -> AssetMappingDMRequest:
562
+ raise NotImplementedError("Deserializing 3D Asset Mappings from JSON is not supported.")
563
+
564
+ def data_to_json_chunk(
565
+ self, data_chunk: Sequence[AssetMappingResponse], selector: ThreeDSelector | None = None
566
+ ) -> list[dict[str, JsonVal]]:
567
+ raise NotImplementedError("Serializing 3D Asset Mappings to JSON is not supported.")
@@ -16,6 +16,7 @@ from rich.console import Console
16
16
  from rich.panel import Panel
17
17
 
18
18
  from cognite_toolkit._cdf_tk.client import ToolkitClient
19
+ from cognite_toolkit._cdf_tk.client.data_classes.instance_api import TypedInstanceIdentifier
19
20
  from cognite_toolkit._cdf_tk.cruds import (
20
21
  AssetCRUD,
21
22
  ContainerCRUD,
@@ -59,10 +60,10 @@ from cognite_toolkit._cdf_tk.utils.aggregators import (
59
60
  TimeSeriesAggregator,
60
61
  )
61
62
  from cognite_toolkit._cdf_tk.utils.http_client import (
62
- FailedRequestItems,
63
- FailedResponseItems,
64
63
  HTTPClient,
65
64
  ItemsRequest,
65
+ ItemsRequest2,
66
+ ItemsSuccessResponse2,
66
67
  SuccessResponseItems,
67
68
  )
68
69
  from cognite_toolkit._cdf_tk.utils.producer_worker import ProducerWorkerExecutor
@@ -714,21 +715,18 @@ class PurgeCommand(ToolkitCommand):
714
715
  results.deleted += len(items)
715
716
  return
716
717
 
717
- responses = delete_client.request_with_retries(
718
- ItemsRequest(
719
- delete_client.config.create_api_url("/models/instances/delete"),
718
+ responses = delete_client.request_items_retries(
719
+ ItemsRequest2(
720
+ endpoint_url=delete_client.config.create_api_url("/models/instances/delete"),
720
721
  method="POST",
721
- # MyPy does not understand that InstanceId.load handles dict[str, JsonVal]
722
- items=[DeleteItem(item=item, as_id_fun=InstanceId.load) for item in items], # type: ignore[arg-type]
722
+ items=[TypedInstanceIdentifier._load(item) for item in items],
723
723
  )
724
724
  )
725
725
  for response in responses:
726
- if isinstance(response, SuccessResponseItems):
726
+ if isinstance(response, ItemsSuccessResponse2):
727
727
  results.deleted += len(response.ids)
728
- elif isinstance(response, FailedResponseItems | FailedRequestItems):
729
- results.failed += len(response.ids)
730
728
  else:
731
- results.failed += len(items)
729
+ results.failed += len(response.ids)
732
730
 
733
731
  @staticmethod
734
732
  def _unlink_timeseries(
@@ -13,7 +13,7 @@ from cognite_toolkit._cdf_tk.client.data_classes.infield import (
13
13
  InfieldLocationConfig,
14
14
  InfieldLocationConfigList,
15
15
  )
16
- from cognite_toolkit._cdf_tk.client.data_classes.instance_api import InstanceResult, NodeIdentifier
16
+ from cognite_toolkit._cdf_tk.client.data_classes.instance_api import InstanceResult, TypedNodeIdentifier
17
17
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.apm_config_v1 import (
18
18
  APMConfig,
19
19
  APMConfigList,
@@ -246,7 +246,7 @@ class InfieldV1CRUD(ResourceCRUD[str, APMConfigWrite, APMConfig]):
246
246
 
247
247
 
248
248
  @final
249
- class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConfig, InfieldLocationConfig]):
249
+ class InFieldLocationConfigCRUD(ResourceCRUD[TypedNodeIdentifier, InfieldLocationConfig, InfieldLocationConfig]):
250
250
  folder_name = "cdf_applications"
251
251
  resource_cls = InfieldLocationConfig
252
252
  resource_write_cls = InfieldLocationConfig
@@ -260,13 +260,13 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
260
260
  return "infield location configs"
261
261
 
262
262
  @classmethod
263
- def get_id(cls, item: InfieldLocationConfig | dict) -> NodeIdentifier:
263
+ def get_id(cls, item: InfieldLocationConfig | dict) -> TypedNodeIdentifier:
264
264
  if isinstance(item, dict):
265
- return NodeIdentifier(space=item["space"], external_id=item["externalId"])
266
- return NodeIdentifier(space=item.space, external_id=item.external_id)
265
+ return TypedNodeIdentifier(space=item["space"], external_id=item["externalId"])
266
+ return TypedNodeIdentifier(space=item.space, external_id=item.external_id)
267
267
 
268
268
  @classmethod
269
- def dump_id(cls, id: NodeIdentifier) -> dict[str, Any]:
269
+ def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
270
270
  return id.dump(include_type=False)
271
271
 
272
272
  @classmethod
@@ -308,13 +308,13 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
308
308
  # as we only want to count the infield location configs here.
309
309
  return [res for res in created if res.as_id() in config_ids]
310
310
 
311
- def retrieve(self, ids: SequenceNotStr[NodeIdentifier]) -> InfieldLocationConfigList:
311
+ def retrieve(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> InfieldLocationConfigList:
312
312
  return InfieldLocationConfigList(self.client.infield.config.retrieve(list(ids)))
313
313
 
314
314
  def update(self, items: Sequence[InfieldLocationConfig]) -> Sized:
315
315
  return self.create(items)
316
316
 
317
- def delete(self, ids: SequenceNotStr[NodeIdentifier]) -> int:
317
+ def delete(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> int:
318
318
  # We must retrieve the full resource to get hte DataExplorationConfig linked resource deleted as well.
319
319
  retrieved = self.retrieve(list(ids))
320
320
  # Then, we pass the entire resource to the delete method, which will delete both the InfieldLocationConfig
@@ -345,7 +345,9 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
345
345
 
346
346
 
347
347
  @final
348
- class InFieldCDMLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InFieldCDMLocationConfig, InFieldCDMLocationConfig]):
348
+ class InFieldCDMLocationConfigCRUD(
349
+ ResourceCRUD[TypedNodeIdentifier, InFieldCDMLocationConfig, InFieldCDMLocationConfig]
350
+ ):
349
351
  folder_name = "cdf_applications"
350
352
  resource_cls = InFieldCDMLocationConfig
351
353
  resource_write_cls = InFieldCDMLocationConfig
@@ -359,13 +361,13 @@ class InFieldCDMLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InFieldCDMLocati
359
361
  return "infield CDM location configs"
360
362
 
361
363
  @classmethod
362
- def get_id(cls, item: InFieldCDMLocationConfig | dict) -> NodeIdentifier:
364
+ def get_id(cls, item: InFieldCDMLocationConfig | dict) -> TypedNodeIdentifier:
363
365
  if isinstance(item, dict):
364
- return NodeIdentifier(space=item["space"], external_id=item["externalId"])
365
- return NodeIdentifier(space=item.space, external_id=item.external_id)
366
+ return TypedNodeIdentifier(space=item["space"], external_id=item["externalId"])
367
+ return TypedNodeIdentifier(space=item.space, external_id=item.external_id)
366
368
 
367
369
  @classmethod
368
- def dump_id(cls, id: NodeIdentifier) -> dict[str, Any]:
370
+ def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
369
371
  return id.dump(include_type=False)
370
372
 
371
373
  @classmethod
@@ -397,13 +399,13 @@ class InFieldCDMLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InFieldCDMLocati
397
399
  def create(self, items: Sequence[InFieldCDMLocationConfig]) -> list[InstanceResult]:
398
400
  return self.client.infield.cdm_config.apply(items)
399
401
 
400
- def retrieve(self, ids: SequenceNotStr[NodeIdentifier]) -> list[InFieldCDMLocationConfig]:
402
+ def retrieve(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> list[InFieldCDMLocationConfig]:
401
403
  return self.client.infield.cdm_config.retrieve(list(ids))
402
404
 
403
405
  def update(self, items: Sequence[InFieldCDMLocationConfig]) -> Sized:
404
406
  return self.create(items)
405
407
 
406
- def delete(self, ids: SequenceNotStr[NodeIdentifier]) -> int:
408
+ def delete(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> int:
407
409
  # We must retrieve the full resource to delete it.
408
410
  retrieved = self.retrieve(list(ids))
409
411
  _ = self.client.infield.cdm_config.delete(retrieved)
@@ -26,9 +26,7 @@ class HTTPResult2(BaseModel):
26
26
  if isinstance(self, SuccessResponse2):
27
27
  return self
28
28
  elif isinstance(self, FailedResponse2):
29
- raise ToolkitAPIError(
30
- f"Request failed with status code {self.status_code}: {self.error.code} - {self.error.message}"
31
- )
29
+ raise ToolkitAPIError(f"Request failed with status code {self.status_code}: {self.error.message}")
32
30
  elif isinstance(self, FailedRequest2):
33
31
  raise ToolkitAPIError(f"Request failed with error: {self.error}")
34
32
  else:
@@ -12,7 +12,7 @@ jobs:
12
12
  environment: dev
13
13
  name: Deploy
14
14
  container:
15
- image: cognite/toolkit:0.7.35
15
+ image: cognite/toolkit:0.7.36
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.35
13
+ image: cognite/toolkit:0.7.36
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.35"
7
+ version = "0.7.36"
8
8
 
9
9
 
10
10
  [plugins]
@@ -1 +1 @@
1
- __version__ = "0.7.35"
1
+ __version__ = "0.7.36"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite_toolkit
3
- Version: 0.7.35
3
+ Version: 0.7.36
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>
@@ -10,7 +10,7 @@ cognite_toolkit/_cdf_tk/apps/_download_app.py,sha256=s8-lkfvzGwQSuBcLEetlTTg7wVR
10
10
  cognite_toolkit/_cdf_tk/apps/_dump_app.py,sha256=X7uh9lQAE-U24z0FrNsechSOyczjVHW8BGjw3wFfwUU,37134
11
11
  cognite_toolkit/_cdf_tk/apps/_import_app.py,sha256=5n5AF40HJ0Q_3LENCknG0MxH4pMUS8OwsqvtCYj_t7w,2086
12
12
  cognite_toolkit/_cdf_tk/apps/_landing_app.py,sha256=YR9z83OY7PhhgBVC5gmRLgo9iTXoGoZfRhOU3gd_r2o,888
13
- cognite_toolkit/_cdf_tk/apps/_migrate_app.py,sha256=R7BsckVnPe59Z26mY06NsWSGjq21DwobPBJ3tMfqRhs,43707
13
+ cognite_toolkit/_cdf_tk/apps/_migrate_app.py,sha256=YUs5V5oJzLeWqFSTW2NfwTptJUC22kjZIQ30xUw-U7s,47665
14
14
  cognite_toolkit/_cdf_tk/apps/_modules_app.py,sha256=t0SPvulgbgkF_OO2E68mQ_ZUcJ6HoaurYe0IkmXie0o,7449
15
15
  cognite_toolkit/_cdf_tk/apps/_profile_app.py,sha256=vSRJW54bEvIul8_4rOqyOYA7ztXx7TFOvZRZWZTxMbg,7007
16
16
  cognite_toolkit/_cdf_tk/apps/_purge.py,sha256=KYI1wFy7yHFEM1qJnTYc4_8E2FVGu4QhPsWsxop1sZA,14242
@@ -31,7 +31,7 @@ cognite_toolkit/_cdf_tk/client/__init__.py,sha256=a6rQXDGfW2g7K5WwrOW5oakh1TdFlB
31
31
  cognite_toolkit/_cdf_tk/client/_constants.py,sha256=COUGcea37mDF2sf6MGqJXWmecTY_6aCImslxXrYW1I0,73
32
32
  cognite_toolkit/_cdf_tk/client/_toolkit_client.py,sha256=S2D0N3Cea39Bhl2NIu8jbQy8Xbq1RS1rldG238CvfQ4,3750
33
33
  cognite_toolkit/_cdf_tk/client/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- cognite_toolkit/_cdf_tk/client/api/infield.py,sha256=r_KeHEFs9slr6Zw3N6Ka3-_JBhU8yVW4KcjQ3u5OSFw,11071
34
+ cognite_toolkit/_cdf_tk/client/api/infield.py,sha256=B7sydnhE1cKhp9OJxkjmTAfRREWZTaD8EuSvscZoE8w,11039
35
35
  cognite_toolkit/_cdf_tk/client/api/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  cognite_toolkit/_cdf_tk/client/api/legacy/canvas.py,sha256=2TVKPlgtXviPxW9tOX_tv7I06K-SnYovg5c9sxBE3f4,8843
37
37
  cognite_toolkit/_cdf_tk/client/api/legacy/charts.py,sha256=YZ2jjHoKinRiqOBzosq6abvzStA4B6VezI1wSy3_U2k,4960
@@ -57,19 +57,19 @@ cognite_toolkit/_cdf_tk/client/api/lookup.py,sha256=c-cvtgfGGGYyk8ROcJu44qlo1ocq
57
57
  cognite_toolkit/_cdf_tk/client/api/migration.py,sha256=6q3ncT-_I2P7fWYzAcv_UG55VnrVRTSymwaf10aosJA,23102
58
58
  cognite_toolkit/_cdf_tk/client/api/project.py,sha256=5DDnyDF9WN8m8wNP3WhqX0gT6nDaG-2ekC1VSMMrdV8,1088
59
59
  cognite_toolkit/_cdf_tk/client/api/search.py,sha256=wl6MjmkELCWmkXf9yYM03LElLC5l0_DwwifsZc_tXSg,694
60
- cognite_toolkit/_cdf_tk/client/api/streams.py,sha256=qOUFHdpz75PSlfImIizVCtschLLHttR8AUV0Jw3DTRM,3055
61
- cognite_toolkit/_cdf_tk/client/api/three_d.py,sha256=zvzyeeAz8P2h0_yC-i-PAqe-kl8ocyOAZbQ3Y3_O3Wo,4949
60
+ cognite_toolkit/_cdf_tk/client/api/streams.py,sha256=uOD5UBsfkmMaiJ-UrtZI9I0O4fQlp2H627o0PJqzefY,3048
61
+ cognite_toolkit/_cdf_tk/client/api/three_d.py,sha256=C8lPVMceJnK1qJOvNNeQgbic0MIEY2up4oL-REZXN5I,15934
62
62
  cognite_toolkit/_cdf_tk/client/api/token.py,sha256=8SiA44Dwsx0j_X8lgIxl2rdNCQSdEiSfoD_4ybxMtFA,5131
63
63
  cognite_toolkit/_cdf_tk/client/api/verify.py,sha256=-x6z6lMaOZG91adi0m9NtJ4wIQgoZURbzluPALXM-ps,3730
64
64
  cognite_toolkit/_cdf_tk/client/api_client.py,sha256=CQdD_gfDqQkz5OYHrTnKvBvEvzHPdHDB1BkZPWRoahg,440
65
65
  cognite_toolkit/_cdf_tk/client/config.py,sha256=weMR43z-gqHMn-Jqvfmh_nJ0HbgEdyeCGtISuEf3OuY,4269
66
66
  cognite_toolkit/_cdf_tk/client/data_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
- cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py,sha256=X-aGFnYzSxXYDxlFP2rqJ8P10Um7bcRRUZxVVzblYBw,477
68
- cognite_toolkit/_cdf_tk/client/data_classes/base.py,sha256=VtqQv2xVV5J81Ls8Y-tvsITKnUCOfjhQwFijwqNIIuU,2303
67
+ cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py,sha256=_B0BRfLbhibwx-K4rfsq4IRq1V05KzUNCNN_2AlDhFg,801
68
+ cognite_toolkit/_cdf_tk/client/data_classes/base.py,sha256=0XsvidoFPAQ_j1cTcBJ0dLv2fP2Nwh7zQAW49hj-ZAk,2224
69
69
  cognite_toolkit/_cdf_tk/client/data_classes/capabilities.py,sha256=muqpAC2JLCFcEpRPzuh_3sS3o_q42WFyfsGzl-LfB_U,8773
70
70
  cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py,sha256=-dFfY53cos5DwASLU18aBfYF1VC6bfaUshC2HiGJ2uI,5571
71
71
  cognite_toolkit/_cdf_tk/client/data_classes/infield.py,sha256=xZDpHw190FgX2vK6zk_r8dUJA7J6UzdS8227VOu74ms,4298
72
- cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py,sha256=ajOCEzRf9OrwR3JyGKNvBkru_Nfpe4PxmcPc8mYB8PE,5065
72
+ cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py,sha256=3NPKpD0OLzac_tvxXm54NeYw9LO6RC8NEUolthwPD_Q,5075
73
73
  cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  cognite_toolkit/_cdf_tk/client/data_classes/legacy/apm_config_v1.py,sha256=0bPq7R0qvdf8SMFS06kX7TXHIClDcJNHwdTBweQB-GU,20150
75
75
  cognite_toolkit/_cdf_tk/client/data_classes/legacy/canvas.py,sha256=T5XLaUAFlh4NjWiJLGh4rjdS9BSeTUEsbhh9nbRd0s4,50728
@@ -91,7 +91,7 @@ cognite_toolkit/_cdf_tk/client/data_classes/legacy/search_config.py,sha256=Reo_r
91
91
  cognite_toolkit/_cdf_tk/client/data_classes/legacy/sequences.py,sha256=02d34fPcJ1H7U5ZnCCfOi36z5WJ4WnRfCWwkp99mW2E,6234
92
92
  cognite_toolkit/_cdf_tk/client/data_classes/legacy/streamlit_.py,sha256=nEk00FH3i-px2r6ql4kk1VVL4sytjUn0_sTkEdDSHVc,6746
93
93
  cognite_toolkit/_cdf_tk/client/data_classes/streams.py,sha256=DHSDrBax81fUzneIikn9hUMVgQVbdaiQ9aY-bRaTK38,2459
94
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py,sha256=9NvZNkbdkHlfzZqV_Kr43eHjpfyfOOxcL9ry7Y_zZ0I,1406
94
+ cognite_toolkit/_cdf_tk/client/data_classes/three_d.py,sha256=Tk8ujB_aN7rfpqbBNV2Ps2Onp_F7Ipt-EUItUIspTaE,3424
95
95
  cognite_toolkit/_cdf_tk/client/testing.py,sha256=xD1Pg7Cb5Th_o_XbyqyMSNGVlBEa-AlS54ezjRsQ4JA,7386
96
96
  cognite_toolkit/_cdf_tk/client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  cognite_toolkit/_cdf_tk/client/utils/_concurrency.py,sha256=3GtQbKDaosyKHEt-KzxKK9Yie4TvZPdoou2vUk6dUa8,2298
@@ -107,15 +107,15 @@ cognite_toolkit/_cdf_tk/commands/_migrate/command.py,sha256=l2P0Em05aEJvNZH4WkEI
107
107
  cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py,sha256=VxoFLS0Xi9uviICShxHHXbUU_wliqNZL3PxD0qveOSU,16810
108
108
  cognite_toolkit/_cdf_tk/commands/_migrate/creators.py,sha256=hQ9o_N2aY9nqHOKlwP7tI7mAYLCPkPLSJGe_QTwglWc,9612
109
109
  cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py,sha256=__o2JZ3GkD0uqtjjpumHJF8KLdqCFrbMZOoEQHAeOLo,11488
110
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py,sha256=59Ep9I3t0DxWfekh1LBwDJESEJETmejeOFRSutNadgs,22276
110
+ cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py,sha256=282o0zDiY5_zqMNTQApn5uaM73vgo_FuNnQo3sicq9Y,24366
111
111
  cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py,sha256=i1eUsNX6Dueol9STIEwyksBnBsWUk13O8qHIjW964pM,7860
112
112
  cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py,sha256=HRh-VlVWbOLjQfP968lReYtZFptjUEa4JbbZxiw5Qy8,5551
113
113
  cognite_toolkit/_cdf_tk/commands/_migrate/issues.py,sha256=uEMI5azCpHWAvEauRY9ZvsvNO0_aaUpdNP7_nM7oBC4,7547
114
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py,sha256=TCeu6zf2wIaAX1xwnwS4gGrfcaLKRuNbAEYwkulIU_s,21756
114
+ cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py,sha256=7gD7uKOy8kPa9u9rvRcOnZsuLGTD0WhU8szKGQ3GDoM,26782
115
115
  cognite_toolkit/_cdf_tk/commands/_migrate/prepare.py,sha256=RfqaNoso5CyBwc-p6ckwcYqBfZXKhdJgdGIyd0TATaI,2635
116
116
  cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py,sha256=N1H_-rBpPUD6pbrlcofn1uEK1bA694EUXEe1zIXeqyo,2489
117
117
  cognite_toolkit/_cdf_tk/commands/_profile.py,sha256=8E3U4WyJmGPeOXLEWtRaFpB05MFWgA5Y4vFKPay6OfY,43127
118
- cognite_toolkit/_cdf_tk/commands/_purge.py,sha256=HqN2T0OrHG53BfQrolYLi0kFa-_KuJjbZldFtQdMdnY,32914
118
+ cognite_toolkit/_cdf_tk/commands/_purge.py,sha256=MzBi-fTiNWFdAodOtraOWgtqG9rOSttKS49rCGL3XfQ,32764
119
119
  cognite_toolkit/_cdf_tk/commands/_questionary_style.py,sha256=h-w7fZKkGls3TrzIGBKjsZSGoXJJIYchgD1StfA40r8,806
120
120
  cognite_toolkit/_cdf_tk/commands/_upload.py,sha256=SBqevUZjNW_-hGXSI8YzwgQ7AgJH15M0S3avm7Wmqpw,13040
121
121
  cognite_toolkit/_cdf_tk/commands/_utils.py,sha256=UxMJW5QYKts4om5n6x2Tq2ihvfO9gWjhQKeqZNFTlKg,402
@@ -149,7 +149,7 @@ cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py,sha256=OniGxzx0XM
149
149
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py,sha256=U0ItuoNr1KEtoFQAiIe-K19_72ht9-kGndFVgF-iC10,9524
150
150
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py,sha256=4uTr3Mx89yaPsr23mcR7hD7h9XBpB4dUPCeNacI8mB0,64874
151
151
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py,sha256=nvMIDeAksJ_0iWvorK8k1BemSrTAUhll_JgCMNiUjZ4,17708
152
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py,sha256=X1u_uNvtui8EpmEbxcXBUuRI2B3X5VKK6Nt4KvuT6Ys,20669
152
+ cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py,sha256=4DL6koP_wwBqfgRveSAYbTVkcTJfjvrj0s2JXJ_p8yQ,20750
153
153
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py,sha256=YGsrp0_yLcpyUvVc_-EW7Rb6LTl_cKtWNmuJDbxbqhs,14862
154
154
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py,sha256=QHihJsyCc9EJImPQSIZDQsxDewCglBc6iSHXjLeutMs,28402
155
155
  cognite_toolkit/_cdf_tk/cruds/_resource_cruds/group_scoped.py,sha256=WEg8-CxMP64WfE_XXIlH114zM51K0uLaYa4atd992zI,1690
@@ -289,7 +289,7 @@ cognite_toolkit/_cdf_tk/utils/hashing.py,sha256=3NyNfljyYNTqAyAFBd6XlyWaj43jRzEN
289
289
  cognite_toolkit/_cdf_tk/utils/http_client/__init__.py,sha256=MG3rAi5IiLxqlCMyVvzyfsKXMb_3fVxwAZ7uyPXrMvs,1483
290
290
  cognite_toolkit/_cdf_tk/utils/http_client/_client.py,sha256=1nKMop_ORrGXybG-KDmC3rlcKs8cseLWSkPkMTFWRyw,22493
291
291
  cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py,sha256=tpU2pswtbGn5E45izm-B4qIc4RSyh5gGDBj8kA0Gs7o,14964
292
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py,sha256=Qqy7OEg8MGF9EksDUcn39fq6GpC_SrbWbyt-UGqD80Q,8811
292
+ cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py,sha256=kes6bUeHv8icMqcPbIXszl8GKtxpc5AIN4nUIHFoSV4,8761
293
293
  cognite_toolkit/_cdf_tk/utils/http_client/_exception.py,sha256=fC9oW6BN0HbUe2AkYABMP7Kj0-9dNYXVFBY5RQztq2c,126
294
294
  cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py,sha256=pu6oA-XpOeaOLdoeD_mGfJXC3BFGWfh5oGcRDpb6maw,1407
295
295
  cognite_toolkit/_cdf_tk/utils/interactive_select.py,sha256=XeTyxfcrbgxo5a7rIGXB0hF1rJ_vHYsat3Wl31XM-hI,37907
@@ -309,14 +309,14 @@ cognite_toolkit/_repo_files/.gitignore,sha256=ip9kf9tcC5OguF4YF4JFEApnKYw0nG0vPi
309
309
  cognite_toolkit/_repo_files/AzureDevOps/.devops/README.md,sha256=OLA0D7yCX2tACpzvkA0IfkgQ4_swSd-OlJ1tYcTBpsA,240
310
310
  cognite_toolkit/_repo_files/AzureDevOps/.devops/deploy-pipeline.yml,sha256=brULcs8joAeBC_w_aoWjDDUHs3JheLMIR9ajPUK96nc,693
311
311
  cognite_toolkit/_repo_files/AzureDevOps/.devops/dry-run-pipeline.yml,sha256=OBFDhFWK1mlT4Dc6mDUE2Es834l8sAlYG50-5RxRtHk,723
312
- cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml,sha256=oRTIMwg3E-WwV3a4BgMPLT_I_-45appBab-4dXfVk_M,667
313
- cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml,sha256=9xSaRlqZoEoogjFEry-yJhX1jBsJETzEqs5eu5aJXXk,2430
314
- cognite_toolkit/_resources/cdf.toml,sha256=X8Bwz7uEfAwKlp9XCv1bWkKYh2LAA1NIVpYCMsF9jPg,475
315
- cognite_toolkit/_version.py,sha256=T20GoRCjdih7BMGVy6MaNmk6gD2Y_Vzyc4VjBcPeFRk,23
312
+ cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml,sha256=ofqsT_RZbUsrih1RE9qrzOOQK72T9aM4HwIu5OOdM20,667
313
+ cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml,sha256=Lul-1240VOiishKOBBWDJua7KIEuJziNJXcpgz8mRgE,2430
314
+ cognite_toolkit/_resources/cdf.toml,sha256=DIpC1r6ql-BAasLk7nvhpPLpQeykwAU-XYG57VF8-iQ,475
315
+ cognite_toolkit/_version.py,sha256=s_fO2Pb37oajrH9N1MYPwjM5YVV5bZvqGHD_GGAAiec,23
316
316
  cognite_toolkit/config.dev.yaml,sha256=M33FiIKdS3XKif-9vXniQ444GTZ-bLXV8aFH86u9iUQ,332
317
317
  cognite_toolkit/demo/__init__.py,sha256=-m1JoUiwRhNCL18eJ6t7fZOL7RPfowhCuqhYFtLgrss,72
318
318
  cognite_toolkit/demo/_base.py,sha256=6xKBUQpXZXGQ3fJ5f7nj7oT0s2n7OTAGIa17ZlKHZ5U,8052
319
- cognite_toolkit-0.7.35.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
320
- cognite_toolkit-0.7.35.dist-info/entry_points.txt,sha256=EtZ17K2mUjh-AY0QNU1CPIB_aDSSOdmtNI_4Fj967mA,84
321
- cognite_toolkit-0.7.35.dist-info/METADATA,sha256=I_SSGGmlwY9k8AXGAOdcjMahvnbDAtrX8UJZlZS29I0,4507
322
- cognite_toolkit-0.7.35.dist-info/RECORD,,
319
+ cognite_toolkit-0.7.36.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
320
+ cognite_toolkit-0.7.36.dist-info/entry_points.txt,sha256=EtZ17K2mUjh-AY0QNU1CPIB_aDSSOdmtNI_4Fj967mA,84
321
+ cognite_toolkit-0.7.36.dist-info/METADATA,sha256=IVa9xEH1DSyQh4n7Ipr0HyVjKHPLoZUAgI0hole3qjo,4507
322
+ cognite_toolkit-0.7.36.dist-info/RECORD,,