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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +7 -1
  2. cognite_toolkit/_cdf_tk/client/api/assets.py +118 -0
  3. cognite_toolkit/_cdf_tk/client/api/events.py +97 -0
  4. cognite_toolkit/_cdf_tk/client/api/infield.py +3 -3
  5. cognite_toolkit/_cdf_tk/client/api/legacy/extended_functions.py +1 -1
  6. cognite_toolkit/_cdf_tk/client/api/project.py +1 -1
  7. cognite_toolkit/_cdf_tk/client/api/streams.py +2 -2
  8. cognite_toolkit/_cdf_tk/client/api/three_d.py +5 -4
  9. cognite_toolkit/_cdf_tk/client/api/timeseries.py +105 -0
  10. cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +9 -0
  11. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +220 -0
  12. cognite_toolkit/_cdf_tk/client/{data_classes/api_classes.py → cdf_client/responses.py} +10 -13
  13. cognite_toolkit/_cdf_tk/client/data_classes/agent.py +127 -0
  14. cognite_toolkit/_cdf_tk/client/data_classes/asset.py +54 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/base.py +117 -22
  16. cognite_toolkit/_cdf_tk/client/data_classes/event.py +43 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +56 -0
  18. cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +44 -0
  19. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +34 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/raw.py +43 -0
  21. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +3 -2
  22. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +29 -34
  23. cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +55 -0
  24. cognite_toolkit/_cdf_tk/{utils → client}/http_client/__init__.py +4 -4
  25. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_client.py +10 -10
  26. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes.py +2 -2
  27. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes2.py +10 -33
  28. cognite_toolkit/_cdf_tk/client/testing.py +6 -0
  29. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +1 -1
  30. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +6 -7
  31. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +6 -5
  32. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +6 -4
  33. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +10 -10
  34. cognite_toolkit/_cdf_tk/commands/_purge.py +7 -7
  35. cognite_toolkit/_cdf_tk/commands/_upload.py +1 -1
  36. cognite_toolkit/_cdf_tk/commands/pull.py +97 -2
  37. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +43 -47
  38. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +11 -4
  39. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +2 -1
  40. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -2
  41. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +2 -1
  42. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +1 -1
  43. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +2 -1
  44. cognite_toolkit/_cdf_tk/storageio/_applications.py +1 -1
  45. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +32 -29
  46. cognite_toolkit/_cdf_tk/storageio/_base.py +1 -1
  47. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +7 -7
  48. cognite_toolkit/_cdf_tk/storageio/_file_content.py +7 -7
  49. cognite_toolkit/_cdf_tk/storageio/_raw.py +1 -1
  50. cognite_toolkit/_cdf_tk/utils/useful_types.py +4 -7
  51. cognite_toolkit/_cdf_tk/utils/useful_types2.py +12 -0
  52. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  53. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  54. cognite_toolkit/_resources/cdf.toml +1 -1
  55. cognite_toolkit/_version.py +1 -1
  56. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/METADATA +1 -1
  57. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/RECORD +61 -48
  58. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/WHEEL +1 -1
  59. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_exception.py +0 -0
  60. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_tracker.py +0 -0
  61. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/entry_points.txt +0 -0
@@ -12,8 +12,10 @@ from cognite_toolkit._cdf_tk.client.api.legacy.extended_functions import Extende
12
12
  from cognite_toolkit._cdf_tk.client.api.legacy.extended_raw import ExtendedRawAPI
13
13
  from cognite_toolkit._cdf_tk.client.api.legacy.extended_timeseries import ExtendedTimeSeriesAPI
14
14
  from cognite_toolkit._cdf_tk.client.api.legacy.robotics import RoboticsAPI
15
- from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient
15
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient
16
16
 
17
+ from .api.assets import AssetsAPI
18
+ from .api.events import EventsAPI
17
19
  from .api.infield import InfieldAPI
18
20
  from .api.lookup import LookUpGroup
19
21
  from .api.migration import MigrationAPI
@@ -21,6 +23,7 @@ from .api.project import ProjectAPI
21
23
  from .api.search import SearchAPI
22
24
  from .api.streams import StreamsAPI
23
25
  from .api.three_d import ThreeDAPI
26
+ from .api.timeseries import TimeSeriesAPI
24
27
  from .api.token import TokenAPI
25
28
  from .api.verify import VerifyAPI
26
29
  from .config import ToolkitClientConfig
@@ -32,6 +35,9 @@ class ToolAPI:
32
35
  def __init__(self, http_client: HTTPClient, console: Console) -> None:
33
36
  self.http_client = http_client
34
37
  self.three_d = ThreeDAPI(http_client, console)
38
+ self.assets = AssetsAPI(http_client)
39
+ self.time_series = TimeSeriesAPI(http_client)
40
+ self.events = EventsAPI(http_client)
35
41
 
36
42
 
37
43
  class ToolkitClient(CogniteClient):
@@ -0,0 +1,118 @@
1
+ from collections.abc import Sequence
2
+ from typing import Any, Literal
3
+
4
+ from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse, ResponseItems
5
+ from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
6
+ from cognite_toolkit._cdf_tk.client.data_classes.asset import AssetRequest, AssetResponse
7
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalOrExternalId
8
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, SuccessResponse2
9
+
10
+
11
+ class AssetsAPI(CDFResourceAPI[InternalOrExternalId, AssetRequest, AssetResponse]):
12
+ def __init__(self, http_client: HTTPClient) -> None:
13
+ super().__init__(
14
+ http_client=http_client,
15
+ method_endpoint_map={
16
+ "create": Endpoint(method="POST", path="/assets", item_limit=1000, concurrency_max_workers=1),
17
+ "retrieve": Endpoint(method="POST", path="/assets/byids", item_limit=1000, concurrency_max_workers=1),
18
+ "update": Endpoint(method="POST", path="/assets/update", item_limit=1000, concurrency_max_workers=1),
19
+ "delete": Endpoint(method="POST", path="/assets/delete", item_limit=1000, concurrency_max_workers=1),
20
+ "list": Endpoint(method="POST", path="/assets/list", item_limit=1000),
21
+ },
22
+ )
23
+
24
+ def _page_response(self, response: SuccessResponse2) -> PagedResponse[AssetResponse]:
25
+ return PagedResponse[AssetResponse].model_validate_json(response.body)
26
+
27
+ def _reference_response(self, response: SuccessResponse2) -> ResponseItems[InternalOrExternalId]:
28
+ return ResponseItems[InternalOrExternalId].model_validate_json(response.body)
29
+
30
+ def create(self, items: Sequence[AssetRequest]) -> list[AssetResponse]:
31
+ """Create assets in CDF.
32
+
33
+ Args:
34
+ items: List of AssetRequest objects to create.
35
+ Returns:
36
+ List of created AssetResponse objects.
37
+ """
38
+ return self._request_item_response(items, "create")
39
+
40
+ def retrieve(self, items: Sequence[InternalOrExternalId], ignore_unknown_ids: bool = False) -> list[AssetResponse]:
41
+ """Retrieve assets from CDF.
42
+
43
+ Args:
44
+ items: List of InternalOrExternalId objects to retrieve.
45
+ ignore_unknown_ids: Whether to ignore unknown IDs.
46
+ Returns:
47
+ List of retrieved AssetResponse objects.
48
+ """
49
+ return self._request_item_response(
50
+ items, method="retrieve", extra_body={"ignoreUnknownIds": ignore_unknown_ids}
51
+ )
52
+
53
+ def update(
54
+ self, items: Sequence[AssetRequest], mode: Literal["patch", "replace"] = "replace"
55
+ ) -> list[AssetResponse]:
56
+ """Update assets in CDF.
57
+
58
+ Args:
59
+ items: List of AssetRequest objects to update.
60
+ mode: Update mode, either "patch" or "replace".
61
+
62
+ Returns:
63
+ List of updated AssetResponse objects.
64
+ """
65
+ return self._update(items, mode=mode)
66
+
67
+ def delete(
68
+ self, items: Sequence[InternalOrExternalId], recursive: bool = False, ignore_unknown_ids: bool = False
69
+ ) -> None:
70
+ """Delete assets from CDF.
71
+
72
+ Args:
73
+ items: List of InternalOrExternalId objects to delete.
74
+ recursive: Whether to delete assets recursively.
75
+ ignore_unknown_ids: Whether to ignore unknown IDs.
76
+ """
77
+ self._request_no_response(
78
+ items, "delete", extra_body={"recursive": recursive, "ignoreUnknownIds": ignore_unknown_ids}
79
+ )
80
+
81
+ def iterate(
82
+ self,
83
+ aggregated_properties: bool = False,
84
+ data_set_external_ids: list[str] | None = None,
85
+ asset_subtree_external_ids: list[str] | None = None,
86
+ limit: int = 100,
87
+ cursor: str | None = None,
88
+ ) -> PagedResponse[AssetResponse]:
89
+ """Iterate over all assets in CDF.
90
+
91
+ Returns:
92
+ PagedResponse of AssetResponse objects.
93
+ """
94
+ filter_: dict[str, Any] = {}
95
+ if asset_subtree_external_ids:
96
+ filter_["assetSubtreeExternalIds"] = [{"externalId": ext_id} for ext_id in asset_subtree_external_ids]
97
+ if data_set_external_ids:
98
+ filter_["dataSetIds"] = [{"externalId": ds_id} for ds_id in data_set_external_ids]
99
+
100
+ return self._iterate(
101
+ cursor=cursor,
102
+ limit=limit,
103
+ body={
104
+ "aggregatedProperties": ["childCount", "path", "depth"] if aggregated_properties else [],
105
+ "filter": filter_ or None,
106
+ },
107
+ )
108
+
109
+ def list(
110
+ self,
111
+ limit: int | None = 100,
112
+ ) -> list[AssetResponse]:
113
+ """List all asset references in CDF.
114
+
115
+ Returns:
116
+ ResponseItems of T_Identifier objects.
117
+ """
118
+ return self._list(limit=limit)
@@ -0,0 +1,97 @@
1
+ from collections.abc import Sequence
2
+ from typing import Literal
3
+
4
+ from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse, ResponseItems
5
+ from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
6
+ from cognite_toolkit._cdf_tk.client.data_classes.event import EventRequest, EventResponse
7
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalOrExternalId
8
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, SuccessResponse2
9
+
10
+
11
+ class EventsAPI(CDFResourceAPI[InternalOrExternalId, EventRequest, EventResponse]):
12
+ def __init__(self, http_client: HTTPClient) -> None:
13
+ super().__init__(
14
+ http_client=http_client,
15
+ method_endpoint_map={
16
+ "create": Endpoint(method="POST", path="/events", item_limit=1000, concurrency_max_workers=1),
17
+ "retrieve": Endpoint(method="POST", path="/events/byids", item_limit=1000, concurrency_max_workers=1),
18
+ "update": Endpoint(method="POST", path="/events/update", item_limit=1000, concurrency_max_workers=1),
19
+ "delete": Endpoint(method="POST", path="/events/delete", item_limit=1000, concurrency_max_workers=1),
20
+ "list": Endpoint(method="POST", path="/events/list", item_limit=1000),
21
+ },
22
+ )
23
+
24
+ def _page_response(self, response: SuccessResponse2) -> PagedResponse[EventResponse]:
25
+ return PagedResponse[EventResponse].model_validate_json(response.body)
26
+
27
+ def _reference_response(self, response: SuccessResponse2) -> ResponseItems[InternalOrExternalId]:
28
+ return ResponseItems[InternalOrExternalId].model_validate_json(response.body)
29
+
30
+ def create(self, items: Sequence[EventRequest]) -> list[EventResponse]:
31
+ """Create events in CDF.
32
+
33
+ Args:
34
+ items: List of EventRequest objects to create.
35
+ Returns:
36
+ List of created EventResponse objects.
37
+ """
38
+ return self._request_item_response(items, "create")
39
+
40
+ def retrieve(self, items: Sequence[InternalOrExternalId], ignore_unknown_ids: bool = False) -> list[EventResponse]:
41
+ """Retrieve events from CDF.
42
+
43
+ Args:
44
+ items: List of InternalOrExternalId objects to retrieve.
45
+ ignore_unknown_ids: Whether to ignore unknown IDs.
46
+ Returns:
47
+ List of retrieved EventResponse objects.
48
+ """
49
+ return self._request_item_response(
50
+ items, method="retrieve", extra_body={"ignoreUnknownIds": ignore_unknown_ids}
51
+ )
52
+
53
+ def update(
54
+ self, items: Sequence[EventRequest], mode: Literal["patch", "replace"] = "replace"
55
+ ) -> list[EventResponse]:
56
+ """Update events in CDF.
57
+
58
+ Args:
59
+ items: List of EventRequest objects to update.
60
+ mode: Update mode, either "patch" or "replace".
61
+
62
+ Returns:
63
+ List of updated EventResponse objects.
64
+ """
65
+ return self._update(items, mode=mode)
66
+
67
+ def delete(self, items: Sequence[InternalOrExternalId], ignore_unknown_ids: bool = False) -> None:
68
+ """Delete events from CDF.
69
+
70
+ Args:
71
+ items: List of InternalOrExternalId objects to delete.
72
+ ignore_unknown_ids: Whether to ignore unknown IDs.
73
+ """
74
+ self._request_no_response(items, "delete", extra_body={"ignoreUnknownIds": ignore_unknown_ids})
75
+
76
+ def iterate(
77
+ self,
78
+ limit: int = 100,
79
+ cursor: str | None = None,
80
+ ) -> PagedResponse[EventResponse]:
81
+ """Iterate over all events in CDF.
82
+
83
+ Returns:
84
+ PagedResponse of EventResponse objects.
85
+ """
86
+ return self._iterate(cursor=cursor, limit=limit)
87
+
88
+ def list(
89
+ self,
90
+ limit: int | None = 100,
91
+ ) -> list[EventResponse]:
92
+ """List all events in CDF.
93
+
94
+ Returns:
95
+ List of EventResponse objects.
96
+ """
97
+ return self._list(limit=limit)
@@ -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 QueryResponse
7
+ from cognite_toolkit._cdf_tk.client.cdf_client.responses import QueryResponse
8
8
  from cognite_toolkit._cdf_tk.client.data_classes.infield import (
9
9
  DataExplorationConfig,
10
10
  InFieldCDMLocationConfig,
@@ -15,12 +15,12 @@ from cognite_toolkit._cdf_tk.client.data_classes.instance_api import (
15
15
  InstanceResult,
16
16
  TypedNodeIdentifier,
17
17
  )
18
- from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
19
- from cognite_toolkit._cdf_tk.utils.http_client import (
18
+ from cognite_toolkit._cdf_tk.client.http_client import (
20
19
  HTTPClient,
21
20
  ItemsRequest2,
22
21
  RequestMessage2,
23
22
  )
23
+ from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
24
24
 
25
25
 
26
26
  class InfieldConfigAPI:
@@ -6,8 +6,8 @@ from cognite.client.utils.useful_types import SequenceNotStr
6
6
  from rich.console import Console
7
7
 
8
8
  from cognite_toolkit._cdf_tk.client.config import ToolkitClientConfig
9
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, RequestMessage2
9
10
  from cognite_toolkit._cdf_tk.utils.collection import chunker
10
- from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, RequestMessage2
11
11
  from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
12
12
 
13
13
 
@@ -2,7 +2,7 @@ from cognite.client import CogniteClient
2
2
 
3
3
  from cognite_toolkit._cdf_tk.client.config import ToolkitClientConfig
4
4
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.project import ProjectStatusList
5
- from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, RequestMessage2
5
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, RequestMessage2
6
6
 
7
7
 
8
8
  class ProjectAPI:
@@ -3,9 +3,9 @@ from collections.abc import Sequence
3
3
  from pydantic import TypeAdapter
4
4
  from rich.console import Console
5
5
 
6
- from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse
6
+ from cognite_toolkit._cdf_tk.client.cdf_client.responses import PagedResponse
7
7
  from cognite_toolkit._cdf_tk.client.data_classes.streams import StreamRequest, StreamResponse
8
- from cognite_toolkit._cdf_tk.utils.http_client import (
8
+ from cognite_toolkit._cdf_tk.client.http_client import (
9
9
  HTTPClient,
10
10
  ItemsRequest2,
11
11
  RequestMessage2,
@@ -5,7 +5,8 @@ from typing import Any, TypeVar
5
5
  from pydantic import TypeAdapter
6
6
  from rich.console import Console
7
7
 
8
- from cognite_toolkit._cdf_tk.client.data_classes.api_classes import InternalIdRequest, PagedResponse
8
+ from cognite_toolkit._cdf_tk.client.cdf_client.responses import PagedResponse
9
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalId
9
10
  from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
10
11
  AssetMappingClassicRequest,
11
12
  AssetMappingDMRequest,
@@ -13,12 +14,12 @@ from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
13
14
  ThreeDModelClassicRequest,
14
15
  ThreeDModelResponse,
15
16
  )
16
- from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
17
- from cognite_toolkit._cdf_tk.utils.http_client import (
17
+ from cognite_toolkit._cdf_tk.client.http_client import (
18
18
  HTTPClient,
19
19
  ItemsRequest2,
20
20
  RequestMessage2,
21
21
  )
22
+ from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
22
23
  from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
23
24
 
24
25
 
@@ -70,7 +71,7 @@ class ThreeDModelAPI:
70
71
  ItemsRequest2(
71
72
  endpoint_url=self._config.create_api_url(self.ENDPOINT + "/delete"),
72
73
  method="POST",
73
- items=InternalIdRequest.from_ids(list(ids)),
74
+ items=InternalId.from_ids(list(ids)),
74
75
  )
75
76
  )
76
77
  responses.raise_for_status()
@@ -0,0 +1,105 @@
1
+ from collections.abc import Sequence
2
+ from typing import Literal
3
+
4
+ from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse, ResponseItems
5
+ from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
6
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalOrExternalId
7
+ from cognite_toolkit._cdf_tk.client.data_classes.timeseries import TimeSeriesRequest, TimeSeriesResponse
8
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, SuccessResponse2
9
+
10
+
11
+ class TimeSeriesAPI(CDFResourceAPI[InternalOrExternalId, TimeSeriesRequest, TimeSeriesResponse]):
12
+ def __init__(self, http_client: HTTPClient) -> None:
13
+ super().__init__(
14
+ http_client=http_client,
15
+ method_endpoint_map={
16
+ "create": Endpoint(method="POST", path="/timeseries", item_limit=1000, concurrency_max_workers=1),
17
+ "retrieve": Endpoint(
18
+ method="POST", path="/timeseries/byids", item_limit=1000, concurrency_max_workers=1
19
+ ),
20
+ "update": Endpoint(
21
+ method="POST", path="/timeseries/update", item_limit=1000, concurrency_max_workers=1
22
+ ),
23
+ "delete": Endpoint(
24
+ method="POST", path="/timeseries/delete", item_limit=1000, concurrency_max_workers=1
25
+ ),
26
+ "list": Endpoint(method="POST", path="/timeseries/list", item_limit=1000),
27
+ },
28
+ )
29
+
30
+ def _page_response(self, response: SuccessResponse2) -> PagedResponse[TimeSeriesResponse]:
31
+ return PagedResponse[TimeSeriesResponse].model_validate_json(response.body)
32
+
33
+ def _reference_response(self, response: SuccessResponse2) -> ResponseItems[InternalOrExternalId]:
34
+ return ResponseItems[InternalOrExternalId].model_validate_json(response.body)
35
+
36
+ def create(self, items: Sequence[TimeSeriesRequest]) -> list[TimeSeriesResponse]:
37
+ """Create time series in CDF.
38
+
39
+ Args:
40
+ items: List of TimeSeriesRequest objects to create.
41
+ Returns:
42
+ List of created TimeSeriesResponse objects.
43
+ """
44
+ return self._request_item_response(items, "create")
45
+
46
+ def retrieve(
47
+ self, items: Sequence[InternalOrExternalId], ignore_unknown_ids: bool = False
48
+ ) -> list[TimeSeriesResponse]:
49
+ """Retrieve time series from CDF.
50
+
51
+ Args:
52
+ items: List of InternalOrExternalId objects to retrieve.
53
+ ignore_unknown_ids: Whether to ignore unknown IDs.
54
+ Returns:
55
+ List of retrieved TimeSeriesResponse objects.
56
+ """
57
+ return self._request_item_response(
58
+ items, method="retrieve", extra_body={"ignoreUnknownIds": ignore_unknown_ids}
59
+ )
60
+
61
+ def update(
62
+ self, items: Sequence[TimeSeriesRequest], mode: Literal["patch", "replace"] = "replace"
63
+ ) -> list[TimeSeriesResponse]:
64
+ """Update time series in CDF.
65
+
66
+ Args:
67
+ items: List of TimeSeriesRequest objects to update.
68
+ mode: Update mode, either "patch" or "replace".
69
+
70
+ Returns:
71
+ List of updated TimeSeriesResponse objects.
72
+ """
73
+ return self._update(items, mode=mode)
74
+
75
+ def delete(self, items: Sequence[InternalOrExternalId], ignore_unknown_ids: bool = False) -> None:
76
+ """Delete time series from CDF.
77
+
78
+ Args:
79
+ items: List of InternalOrExternalId objects to delete.
80
+ ignore_unknown_ids: Whether to ignore unknown IDs.
81
+ """
82
+ self._request_no_response(items, "delete", extra_body={"ignoreUnknownIds": ignore_unknown_ids})
83
+
84
+ def iterate(
85
+ self,
86
+ limit: int = 100,
87
+ cursor: str | None = None,
88
+ ) -> PagedResponse[TimeSeriesResponse]:
89
+ """Iterate over all time series in CDF.
90
+
91
+ Returns:
92
+ PagedResponse of TimeSeriesResponse objects.
93
+ """
94
+ return self._iterate(cursor=cursor, limit=limit)
95
+
96
+ def list(
97
+ self,
98
+ limit: int | None = 100,
99
+ ) -> list[TimeSeriesResponse]:
100
+ """List all time series in CDF.
101
+
102
+ Returns:
103
+ List of TimeSeriesResponse objects.
104
+ """
105
+ return self._list(limit=limit)
@@ -0,0 +1,9 @@
1
+ from .api import CDFResourceAPI
2
+ from .responses import PagedResponse, QueryResponse, ResponseItems
3
+
4
+ __all__ = [
5
+ "CDFResourceAPI",
6
+ "PagedResponse",
7
+ "QueryResponse",
8
+ "ResponseItems",
9
+ ]
@@ -0,0 +1,220 @@
1
+ """Base classes for resource clients.
2
+
3
+ This module provides the base infrastructure for resource-specific clients
4
+ that handle CRUD operations for CDF Data Modeling API resources.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from collections.abc import Callable, Iterable, Sequence
9
+ from dataclasses import dataclass
10
+ from functools import partial
11
+ from typing import Any, Generic, Literal, TypeAlias, TypeVar
12
+
13
+ from pydantic import BaseModel, JsonValue
14
+
15
+ from cognite_toolkit._cdf_tk.client.data_classes.base import (
16
+ RequestUpdateable,
17
+ T_Identifier,
18
+ T_RequestResource,
19
+ T_ResponseResource,
20
+ )
21
+ from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, RequestMessage2, SuccessResponse2
22
+ from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
23
+
24
+ from .responses import PagedResponse, ResponseItems
25
+
26
+ _T_BaseModel = TypeVar("_T_BaseModel", bound=BaseModel)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class Endpoint:
31
+ method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
32
+ path: str
33
+ item_limit: int
34
+ concurrency_max_workers: int = 1
35
+
36
+
37
+ APIMethod: TypeAlias = Literal["create", "retrieve", "update", "delete", "list"]
38
+
39
+
40
+ class CDFResourceAPI(Generic[T_Identifier, T_RequestResource, T_ResponseResource], ABC):
41
+ """Generic resource API for CDF APIs
42
+
43
+ This class provides the logic for working with CDF resources,
44
+ including creating, retrieving, deleting, and listing resources.
45
+ """
46
+
47
+ def __init__(self, http_client: HTTPClient, method_endpoint_map: dict[APIMethod, Endpoint]) -> None:
48
+ """Initialize the resource API.
49
+
50
+ Args:
51
+ http_client: The HTTP client to use for API requests.
52
+ method_endpoint_map: A mapping of endpoint suffixes to their properties.
53
+ """
54
+ self._http_client = http_client
55
+ self._method_endpoint_map = method_endpoint_map
56
+
57
+ @classmethod
58
+ def _serialize_items(cls, items: Sequence[BaseModel]) -> list[dict[str, JsonValue]]:
59
+ """Serialize reference objects to JSON-compatible dicts."""
60
+ return [item.model_dump(mode="json", by_alias=True) for item in items]
61
+
62
+ @classmethod
63
+ def _serialize_updates(
64
+ cls, items: Sequence[RequestUpdateable], mode: Literal["patch", "replace"]
65
+ ) -> list[dict[str, JsonValue]]:
66
+ """Serialize updateable objects to JSON-compatible dicts."""
67
+ return [item.as_update(mode=mode) for item in items]
68
+
69
+ @abstractmethod
70
+ def _page_response(self, response: SuccessResponse2) -> PagedResponse[T_ResponseResource]:
71
+ """Parse a single item response."""
72
+ raise NotImplementedError()
73
+
74
+ @abstractmethod
75
+ def _reference_response(self, response: SuccessResponse2) -> ResponseItems[T_Identifier]:
76
+ """Parse a reference response."""
77
+ raise NotImplementedError()
78
+
79
+ def _make_url(self, path: str = "") -> str:
80
+ """Create the full URL for this resource endpoint."""
81
+ return self._http_client.config.create_api_url(path)
82
+
83
+ def _update(
84
+ self, items: Sequence[RequestUpdateable], mode: Literal["patch", "replace"]
85
+ ) -> list[T_ResponseResource]:
86
+ """Update resources in chunks."""
87
+ response_items: list[T_ResponseResource] = []
88
+ for response in self._chunk_requests(
89
+ items, "update", serialization=partial(self._serialize_updates, mode=mode)
90
+ ):
91
+ response_items.extend(self._page_response(response).items)
92
+ return response_items
93
+
94
+ def _request_item_response(
95
+ self,
96
+ items: Sequence[BaseModel],
97
+ method: APIMethod,
98
+ params: dict[str, Any] | None = None,
99
+ extra_body: dict[str, Any] | None = None,
100
+ ) -> list[T_ResponseResource]:
101
+ response_items: list[T_ResponseResource] = []
102
+ for response in self._chunk_requests(items, method, self._serialize_items, params, extra_body):
103
+ response_items.extend(self._page_response(response).items)
104
+ return response_items
105
+
106
+ def _request_reference_response(
107
+ self,
108
+ items: Sequence[BaseModel],
109
+ method: APIMethod,
110
+ params: dict[str, Any] | None = None,
111
+ extra_body: dict[str, Any] | None = None,
112
+ ) -> list[T_Identifier]:
113
+ all_ids: list[T_Identifier] = []
114
+ for response in self._chunk_requests(items, method, self._serialize_items, params, extra_body):
115
+ all_ids.extend(self._reference_response(response).items)
116
+ return all_ids
117
+
118
+ def _request_no_response(
119
+ self,
120
+ items: Sequence[BaseModel],
121
+ method: APIMethod,
122
+ params: dict[str, Any] | None = None,
123
+ extra_body: dict[str, Any] | None = None,
124
+ ) -> None:
125
+ list(self._chunk_requests(items, method, self._serialize_items, params, extra_body))
126
+ return None
127
+
128
+ def _chunk_requests(
129
+ self,
130
+ items: Sequence[_T_BaseModel],
131
+ method: APIMethod,
132
+ serialization: Callable[[Sequence[_T_BaseModel]], list[dict[str, JsonValue]]],
133
+ params: dict[str, Any] | None = None,
134
+ extra_body: dict[str, Any] | None = None,
135
+ ) -> Iterable[SuccessResponse2]:
136
+ # Filter out None
137
+ request_params = self._filter_out_none_values(params)
138
+ endpoint = self._method_endpoint_map[method]
139
+
140
+ for chunk in chunker_sequence(items, endpoint.item_limit):
141
+ request = RequestMessage2(
142
+ endpoint_url=f"{self._make_url(endpoint.path)}",
143
+ method=endpoint.method,
144
+ body_content={"items": serialization(chunk), **(extra_body or {})}, # type: ignore[dict-item]
145
+ parameters=request_params,
146
+ )
147
+ response = self._http_client.request_single_retries(request)
148
+ yield response.get_success_or_raise()
149
+
150
+ @classmethod
151
+ def _filter_out_none_values(cls, params: dict[str, Any] | None) -> dict[str, Any] | None:
152
+ request_params: dict[str, Any] | None = None
153
+ if params:
154
+ request_params = {k: v for k, v in params.items() if v is not None}
155
+ return request_params
156
+
157
+ def _iterate(
158
+ self,
159
+ limit: int,
160
+ cursor: str | None = None,
161
+ params: dict[str, Any] | None = None,
162
+ body: dict[str, Any] | None = None,
163
+ ) -> PagedResponse[T_ResponseResource]:
164
+ """Fetch a single page of resources.
165
+
166
+ Args:
167
+ params: Query parameters for the request. Supported parameters depend on
168
+ the resource type but typically include:
169
+ - cursor: Cursor for pagination
170
+ - limit: Maximum number of items (defaults to list limit)
171
+ - space: Filter by space
172
+ - includeGlobal: Whether to include global resources
173
+ body : Body content for the request, if applicable.
174
+ limit: Maximum number of items to return in the page.
175
+ cursor: Cursor for pagination.
176
+ Returns:
177
+ A Page containing the items and the cursor for the next page.
178
+ """
179
+ endpoint = self._method_endpoint_map["list"]
180
+ if not (0 < limit <= endpoint.item_limit):
181
+ raise ValueError(f"Limit must be between 1 and {endpoint.item_limit}, got {limit}.")
182
+
183
+ request_params = self._filter_out_none_values(params) or {}
184
+ body = self._filter_out_none_values(body) or {}
185
+ if endpoint.method == "GET":
186
+ request_params["limit"] = limit
187
+ if cursor is not None:
188
+ request_params["cursor"] = cursor
189
+ elif endpoint.method in {"POST", "PUT", "PATCH"}:
190
+ body["limit"] = limit
191
+ if cursor is not None:
192
+ body["cursor"] = cursor
193
+ else:
194
+ raise NotImplementedError(f"Unsupported method {endpoint.method} for pagination.")
195
+
196
+ request = RequestMessage2(
197
+ endpoint_url=self._make_url(endpoint.path),
198
+ method=endpoint.method,
199
+ parameters=request_params,
200
+ body_content=body,
201
+ )
202
+ result = self._http_client.request_single_retries(request)
203
+ response = result.get_success_or_raise()
204
+ return self._page_response(response)
205
+
206
+ def _list(self, limit: int | None = None, params: dict[str, Any] | None = None) -> list[T_ResponseResource]:
207
+ """List all resources, handling pagination automatically."""
208
+ all_items: list[T_ResponseResource] = []
209
+ next_cursor: str | None = None
210
+ total = 0
211
+ endpoint = self._method_endpoint_map["list"]
212
+ while True:
213
+ page_limit = endpoint.item_limit if limit is None else min(limit - total, endpoint.item_limit)
214
+ page = self._iterate(limit=page_limit, cursor=next_cursor, params=params)
215
+ all_items.extend(page.items)
216
+ total += len(page.items)
217
+ if page.next_cursor is None or (limit is not None and total >= limit):
218
+ break
219
+ next_cursor = page.next_cursor
220
+ return all_items