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.
- cognite_toolkit/_cdf_tk/client/_toolkit_client.py +7 -1
- cognite_toolkit/_cdf_tk/client/api/assets.py +118 -0
- cognite_toolkit/_cdf_tk/client/api/events.py +97 -0
- cognite_toolkit/_cdf_tk/client/api/infield.py +3 -3
- cognite_toolkit/_cdf_tk/client/api/legacy/extended_functions.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/project.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/streams.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/three_d.py +5 -4
- cognite_toolkit/_cdf_tk/client/api/timeseries.py +105 -0
- cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +9 -0
- cognite_toolkit/_cdf_tk/client/cdf_client/api.py +220 -0
- cognite_toolkit/_cdf_tk/client/{data_classes/api_classes.py → cdf_client/responses.py} +10 -13
- cognite_toolkit/_cdf_tk/client/data_classes/agent.py +127 -0
- cognite_toolkit/_cdf_tk/client/data_classes/asset.py +54 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +117 -22
- cognite_toolkit/_cdf_tk/client/data_classes/event.py +43 -0
- cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +56 -0
- cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +44 -0
- cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +34 -0
- cognite_toolkit/_cdf_tk/client/data_classes/raw.py +43 -0
- cognite_toolkit/_cdf_tk/client/data_classes/streams.py +3 -2
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +29 -34
- cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +55 -0
- cognite_toolkit/_cdf_tk/{utils → client}/http_client/__init__.py +4 -4
- cognite_toolkit/_cdf_tk/{utils → client}/http_client/_client.py +10 -10
- cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes.py +2 -2
- cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes2.py +10 -33
- cognite_toolkit/_cdf_tk/client/testing.py +6 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/command.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +6 -7
- cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +6 -5
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +6 -4
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +10 -10
- cognite_toolkit/_cdf_tk/commands/_purge.py +7 -7
- cognite_toolkit/_cdf_tk/commands/_upload.py +1 -1
- cognite_toolkit/_cdf_tk/commands/pull.py +97 -2
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +43 -47
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +11 -4
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +2 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -2
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +2 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +2 -1
- cognite_toolkit/_cdf_tk/storageio/_applications.py +1 -1
- cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +32 -29
- cognite_toolkit/_cdf_tk/storageio/_base.py +1 -1
- cognite_toolkit/_cdf_tk/storageio/_datapoints.py +7 -7
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +7 -7
- cognite_toolkit/_cdf_tk/storageio/_raw.py +1 -1
- cognite_toolkit/_cdf_tk/utils/useful_types.py +4 -7
- cognite_toolkit/_cdf_tk/utils/useful_types2.py +12 -0
- cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
- cognite_toolkit/_resources/cdf.toml +1 -1
- cognite_toolkit/_version.py +1 -1
- {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/METADATA +1 -1
- {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/RECORD +61 -48
- {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/WHEEL +1 -1
- /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_exception.py +0 -0
- /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_tracker.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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=
|
|
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,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
|