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
|
@@ -2,11 +2,19 @@ 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
|
-
|
|
7
5
|
T = TypeVar("T", bound=BaseModel)
|
|
8
6
|
|
|
9
7
|
|
|
8
|
+
class ResponseItems(BaseModel, Generic[T]):
|
|
9
|
+
"""A page of reference items from a paginated API response.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
items: The list of reference items in this page.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
items: list[T]
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
class PagedResponse(BaseModel, Generic[T]):
|
|
11
19
|
items: list[T]
|
|
12
20
|
next_cursor: str | None = Field(None, alias="nextCursor")
|
|
@@ -17,14 +25,3 @@ class QueryResponse(BaseModel, Generic[T]):
|
|
|
17
25
|
typing: dict[str, JsonValue] | None = None
|
|
18
26
|
next_cursor: dict[str, str] = Field(alias="nextCursor")
|
|
19
27
|
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]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import Annotated, Any, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BeforeValidator, Field
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestResource, ResponseResource
|
|
6
|
+
from tests.test_unit.test_cdf_tk.test_tk_warnings.test_warnings_metatest import get_all_subclasses
|
|
7
|
+
|
|
8
|
+
from .identifiers import ExternalId
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AgentToolDefinition(BaseModelObject):
|
|
12
|
+
type: str
|
|
13
|
+
name: str
|
|
14
|
+
description: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AskDocument(AgentToolDefinition):
|
|
18
|
+
type: Literal["askDocument"] = "askDocument"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ExamineDataSemantically(AgentToolDefinition):
|
|
22
|
+
type: Literal["examineDataSemantically"] = "examineDataSemantically"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AgentDataModel(BaseModelObject):
|
|
26
|
+
space: str
|
|
27
|
+
external_id: str
|
|
28
|
+
version: str
|
|
29
|
+
view_external_ids: list[str] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentInstanceSpacesDefinition(BaseModelObject):
|
|
33
|
+
type: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AllInstanceSpaces(AgentInstanceSpacesDefinition):
|
|
37
|
+
type: Literal["all"] = "all"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ManualInstanceSpaces(AgentInstanceSpacesDefinition):
|
|
41
|
+
type: Literal["manual"] = "manual"
|
|
42
|
+
spaces: list[str]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
AgentInstanceSpaces = Annotated[
|
|
46
|
+
AllInstanceSpaces | ManualInstanceSpaces,
|
|
47
|
+
Field(discriminator="type"),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class QueryKnowledgeGraphConfig(BaseModelObject):
|
|
52
|
+
data_models: list[AgentDataModel]
|
|
53
|
+
instance_spaces: AgentInstanceSpaces | None = None
|
|
54
|
+
# This is deviating from the API documentation, but the Atlas team has confirmed that "v2" is the default
|
|
55
|
+
version: Literal["v1", "v2"] = "v2"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class QueryKnowledgeGraph(AgentToolDefinition):
|
|
59
|
+
type: Literal["queryKnowledgeGraph"] = "queryKnowledgeGraph"
|
|
60
|
+
configuration: QueryKnowledgeGraphConfig
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class QueryTimeSeriesDatapoints(AgentToolDefinition):
|
|
64
|
+
type: Literal["queryTimeSeriesDatapoints"] = "queryTimeSeriesDatapoints"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SummarizeDocument(AgentToolDefinition):
|
|
68
|
+
type: Literal["summarizeDocument"] = "summarizeDocument"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class UnknownAgentTool(AgentToolDefinition):
|
|
72
|
+
"""Fallback for unknown tool types."""
|
|
73
|
+
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
KNOWN_TOOLS = {tool.type: tool for tool in get_all_subclasses(AgentToolDefinition) if hasattr(tool, "type")}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _handle_unknown_tool(value: Any) -> Any:
|
|
81
|
+
if isinstance(value, dict):
|
|
82
|
+
tool_type = value.get("type")
|
|
83
|
+
if tool_type not in KNOWN_TOOLS:
|
|
84
|
+
return UnknownAgentTool(**value)
|
|
85
|
+
else:
|
|
86
|
+
return KNOWN_TOOLS[tool_type].model_validate(value)
|
|
87
|
+
return value
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
AgentTool = Annotated[
|
|
91
|
+
AskDocument
|
|
92
|
+
| QueryKnowledgeGraph
|
|
93
|
+
| QueryTimeSeriesDatapoints
|
|
94
|
+
| SummarizeDocument
|
|
95
|
+
| ExamineDataSemantically
|
|
96
|
+
| UnknownAgentTool,
|
|
97
|
+
BeforeValidator(_handle_unknown_tool),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AgentRequest(RequestResource):
|
|
102
|
+
external_id: str
|
|
103
|
+
name: str
|
|
104
|
+
description: str | None = None
|
|
105
|
+
instructions: str | None = None
|
|
106
|
+
model: str = "azure/gpt-4o-mini"
|
|
107
|
+
tools: list[AgentTool] | None = None
|
|
108
|
+
runtime_version: str | None = None
|
|
109
|
+
|
|
110
|
+
def as_id(self) -> ExternalId:
|
|
111
|
+
return ExternalId(external_id=self.external_id)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class AgentResponse(ResponseResource[AgentRequest]):
|
|
115
|
+
created_time: int
|
|
116
|
+
last_updated_time: int
|
|
117
|
+
owner_id: str
|
|
118
|
+
external_id: str
|
|
119
|
+
name: str
|
|
120
|
+
description: str | None = None
|
|
121
|
+
instructions: str | None = None
|
|
122
|
+
model: str = "azure/gpt-4o-mini"
|
|
123
|
+
tools: list[AgentTool] | None = None
|
|
124
|
+
runtime_version: str
|
|
125
|
+
|
|
126
|
+
def as_request_resource(self) -> AgentRequest:
|
|
127
|
+
return AgentRequest.model_validate(self.dump(), extra="ignore")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import ClassVar, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import JsonValue
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestUpdateable, ResponseResource
|
|
6
|
+
|
|
7
|
+
from .identifiers import ExternalId
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AssetRequest(RequestUpdateable):
|
|
11
|
+
container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "labels"})
|
|
12
|
+
non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"parent_id", "parent_external_id"})
|
|
13
|
+
external_id: str | None = None
|
|
14
|
+
name: str
|
|
15
|
+
parent_id: int | None = None
|
|
16
|
+
parent_external_id: str | None = None
|
|
17
|
+
description: str | None = None
|
|
18
|
+
metadata: dict[str, str] | None = None
|
|
19
|
+
data_set_id: int | None = None
|
|
20
|
+
source: str | None = None
|
|
21
|
+
labels: list[dict[Literal["externalId"], str]] | None = None
|
|
22
|
+
geo_location: dict[str, JsonValue] | None = None
|
|
23
|
+
|
|
24
|
+
def as_id(self) -> ExternalId:
|
|
25
|
+
if self.external_id is None:
|
|
26
|
+
raise ValueError("Cannot convert AssetRequest to ExternalId when external_id is None")
|
|
27
|
+
return ExternalId(external_id=self.external_id)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AssetAggregateItem(BaseModelObject):
|
|
31
|
+
child_count: int
|
|
32
|
+
depth: int
|
|
33
|
+
path: list[dict[Literal["id"], int]]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AssetResponse(ResponseResource[AssetRequest]):
|
|
37
|
+
created_time: int
|
|
38
|
+
last_updated_time: int
|
|
39
|
+
root_id: int
|
|
40
|
+
aggregates: AssetAggregateItem | None = None
|
|
41
|
+
id: int
|
|
42
|
+
external_id: str | None = None
|
|
43
|
+
name: str
|
|
44
|
+
parent_id: int | None = None
|
|
45
|
+
parent_external_id: str | None = None
|
|
46
|
+
description: str | None = None
|
|
47
|
+
metadata: dict[str, str] | None = None
|
|
48
|
+
data_set_id: int | None = None
|
|
49
|
+
source: str | None = None
|
|
50
|
+
labels: list[dict[Literal["externalId"], str]] | None = None
|
|
51
|
+
geo_location: dict[str, JsonValue] | None = None
|
|
52
|
+
|
|
53
|
+
def as_request_resource(self) -> AssetRequest:
|
|
54
|
+
return AssetRequest.model_validate(self.dump(), extra="ignore")
|
|
@@ -1,55 +1,150 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
import types
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
3
4
|
from collections import UserList
|
|
4
|
-
from typing import
|
|
5
|
+
from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, get_args, get_origin
|
|
5
6
|
|
|
6
|
-
from
|
|
7
|
+
from cognite.client import CogniteClient
|
|
8
|
+
from pydantic import BaseModel, ConfigDict
|
|
7
9
|
from pydantic.alias_generators import to_camel
|
|
8
10
|
|
|
9
|
-
from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import BaseModelObject, RequestResource
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from cognite.client import CogniteClient
|
|
13
|
-
|
|
14
11
|
if sys.version_info >= (3, 11):
|
|
15
12
|
from typing import Self
|
|
16
13
|
else:
|
|
17
14
|
from typing_extensions import Self
|
|
18
15
|
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
class BaseModelObject(BaseModel):
|
|
18
|
+
"""Base class for all object. This includes resources and nested objects."""
|
|
21
19
|
|
|
20
|
+
# We allow extra fields to support forward compatibility.
|
|
21
|
+
model_config = ConfigDict(alias_generator=to_camel, extra="allow")
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def as_request_resource(self) -> T_RequestResource:
|
|
26
|
-
"""Convert the response resource to a request resource."""
|
|
27
|
-
...
|
|
23
|
+
def dump(self, camel_case: bool = True) -> dict[str, Any]:
|
|
24
|
+
"""Dump the resource to a dictionary.
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
"""
|
|
31
|
-
return self.
|
|
26
|
+
This is the default serialization method for request resources.
|
|
27
|
+
"""
|
|
28
|
+
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=True)
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def _load(cls, resource: dict[str, Any]) -> Self:
|
|
32
|
+
"""Load method to match CogniteResource signature."""
|
|
33
|
+
return cls.model_validate(resource)
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
class Identifier(
|
|
35
|
-
"""Base class for all identifier
|
|
36
|
+
class Identifier(BaseModelObject):
|
|
37
|
+
"""Base class for all identifier objects typically
|
|
38
|
+
{"externalId": "..."}, {"id": ...}, {"space": "...", "externalId: }."""
|
|
36
39
|
|
|
37
40
|
model_config = ConfigDict(alias_generator=to_camel, extra="ignore", populate_by_name=True, frozen=True)
|
|
38
41
|
|
|
39
|
-
def dump(self, camel_case: bool = True
|
|
42
|
+
def dump(self, camel_case: bool = True) -> dict[str, Any]:
|
|
40
43
|
"""Dump the resource to a dictionary.
|
|
41
44
|
|
|
42
45
|
This is the default serialization method for request resources.
|
|
43
46
|
"""
|
|
44
|
-
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=
|
|
47
|
+
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=True)
|
|
48
|
+
|
|
49
|
+
def __str__(self) -> str:
|
|
50
|
+
raise NotImplementedError()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
T_Identifier = TypeVar("T_Identifier", bound=Identifier)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class RequestResource(BaseModelObject, ABC):
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def as_id(self) -> Identifier:
|
|
59
|
+
raise NotImplementedError()
|
|
60
|
+
|
|
61
|
+
def __str__(self) -> str:
|
|
62
|
+
return str(self.as_id())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
T_RequestResource = TypeVar("T_RequestResource", bound=RequestResource)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RequestUpdateable(RequestResource, ABC):
|
|
69
|
+
container_fields: ClassVar[frozenset[str]] = frozenset()
|
|
70
|
+
non_nullable_fields: ClassVar[frozenset[str]] = frozenset()
|
|
71
|
+
|
|
72
|
+
def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
|
|
73
|
+
"""Convert the request resource to an update item."""
|
|
74
|
+
update_item = self.as_id().dump(camel_case=True)
|
|
75
|
+
update: dict[str, Any] = {}
|
|
76
|
+
field_by_name = {info.alias or field_id: (field_id, info) for field_id, info in type(self).model_fields.items()}
|
|
77
|
+
# When mode is "patch", we only include fields that are set
|
|
78
|
+
exclude_unset = mode == "patch"
|
|
79
|
+
for key, value in self.model_dump(mode="json", by_alias=True, exclude_unset=exclude_unset).items():
|
|
80
|
+
if key in update_item:
|
|
81
|
+
# Skip identifier fields
|
|
82
|
+
continue
|
|
83
|
+
if key not in field_by_name:
|
|
84
|
+
# Skip unknown fields
|
|
85
|
+
continue
|
|
86
|
+
field_id, info = field_by_name[key]
|
|
87
|
+
if field_id in self.container_fields:
|
|
88
|
+
if mode == "patch":
|
|
89
|
+
update[key] = {"add": value}
|
|
90
|
+
elif mode == "replace":
|
|
91
|
+
if value is None:
|
|
92
|
+
origin = _get_annotation_origin(info.annotation)
|
|
93
|
+
if origin is list:
|
|
94
|
+
update[key] = {"set": []}
|
|
95
|
+
elif origin is dict:
|
|
96
|
+
update[key] = {"set": {}}
|
|
97
|
+
else:
|
|
98
|
+
raise NotImplementedError(
|
|
99
|
+
f'Cannot replace container field "{key}" with None when its type is unknown.'
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
update[key] = {"set": value}
|
|
103
|
+
else:
|
|
104
|
+
raise NotImplementedError(f'Update mode "{mode}" is not supported for container fields.')
|
|
105
|
+
elif value is None:
|
|
106
|
+
if field_id not in self.non_nullable_fields:
|
|
107
|
+
update[key] = {"setNull": True}
|
|
108
|
+
else:
|
|
109
|
+
update[key] = {"set": value}
|
|
110
|
+
update_item["update"] = update
|
|
111
|
+
return update_item
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _get_annotation_origin(field_type: Any) -> Any:
|
|
115
|
+
origin = get_origin(field_type)
|
|
116
|
+
args = get_args(field_type)
|
|
117
|
+
|
|
118
|
+
# Check for Union type (both typing.Union and | syntax from Python 3.10+)
|
|
119
|
+
is_union = origin is Union or isinstance(field_type, getattr(types, "UnionType", ()))
|
|
120
|
+
|
|
121
|
+
if is_union:
|
|
122
|
+
# Handle Optional[T] by filtering out NoneType
|
|
123
|
+
none_types = (type(None), types.NoneType)
|
|
124
|
+
non_none_args = [arg for arg in args if arg not in none_types]
|
|
125
|
+
if len(non_none_args) == 1:
|
|
126
|
+
field_type = non_none_args[0]
|
|
127
|
+
origin = get_origin(field_type) or field_type
|
|
128
|
+
return origin
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def as_request_resource(self) -> T_RequestResource:
|
|
134
|
+
"""Convert the response resource to a request resource."""
|
|
135
|
+
raise NotImplementedError()
|
|
136
|
+
|
|
137
|
+
def as_write(self) -> T_RequestResource:
|
|
138
|
+
"""Alias for as_request_resource to match protocol signature."""
|
|
139
|
+
return self.as_request_resource()
|
|
45
140
|
|
|
46
|
-
def as_id(self) -> Self:
|
|
47
|
-
return self
|
|
48
141
|
|
|
142
|
+
T_ResponseResource = TypeVar("T_ResponseResource", bound=ResponseResource)
|
|
49
143
|
|
|
50
144
|
T_Resource = TypeVar("T_Resource", bound=RequestResource | ResponseResource)
|
|
51
145
|
|
|
52
146
|
|
|
147
|
+
# Todo: Delete this class and use list[T_Resource] directly
|
|
53
148
|
class BaseResourceList(UserList[T_Resource]):
|
|
54
149
|
"""Base class for resource lists."""
|
|
55
150
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import ClassVar
|
|
2
|
+
|
|
3
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
|
|
4
|
+
|
|
5
|
+
from .identifiers import ExternalId, InternalOrExternalId
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventRequest(RequestUpdateable):
|
|
9
|
+
container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "asset_ids"})
|
|
10
|
+
external_id: str | None = None
|
|
11
|
+
data_set_id: int | None = None
|
|
12
|
+
start_time: int | None = None
|
|
13
|
+
end_time: int | None = None
|
|
14
|
+
type: str | None = None
|
|
15
|
+
subtype: str | None = None
|
|
16
|
+
description: str | None = None
|
|
17
|
+
metadata: dict[str, str] | None = None
|
|
18
|
+
asset_ids: list[int] | None = None
|
|
19
|
+
source: str | None = None
|
|
20
|
+
|
|
21
|
+
def as_id(self) -> InternalOrExternalId:
|
|
22
|
+
if self.external_id is None:
|
|
23
|
+
raise ValueError("Cannot convert EventRequest to ExternalId when external_id is None")
|
|
24
|
+
return ExternalId(external_id=self.external_id)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class EventResponse(ResponseResource[EventRequest]):
|
|
28
|
+
external_id: str | None = None
|
|
29
|
+
data_set_id: int | None = None
|
|
30
|
+
start_time: int | None = None
|
|
31
|
+
end_time: int | None = None
|
|
32
|
+
type: str | None = None
|
|
33
|
+
subtype: str | None = None
|
|
34
|
+
description: str | None = None
|
|
35
|
+
metadata: dict[str, str] | None = None
|
|
36
|
+
asset_ids: list[int] | None = None
|
|
37
|
+
source: str | None = None
|
|
38
|
+
id: int
|
|
39
|
+
created_time: int
|
|
40
|
+
last_updated_time: int
|
|
41
|
+
|
|
42
|
+
def as_request_resource(self) -> EventRequest:
|
|
43
|
+
return EventRequest.model_validate(self.dump())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import ClassVar, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import JsonValue
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
|
|
6
|
+
|
|
7
|
+
from .identifiers import ExternalId
|
|
8
|
+
from .instance_api import NodeReference
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FileMetadataRequest(RequestUpdateable):
|
|
12
|
+
container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "labels", "asset_ids", "security_categories"})
|
|
13
|
+
non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"asset_ids", "security_categories"})
|
|
14
|
+
external_id: str | None = None
|
|
15
|
+
name: str
|
|
16
|
+
directory: str | None = None
|
|
17
|
+
source: str | None = None
|
|
18
|
+
mime_type: str | None = None
|
|
19
|
+
metadata: dict[str, str] | None = None
|
|
20
|
+
asset_ids: list[int] | None = None
|
|
21
|
+
data_set_id: int | None = None
|
|
22
|
+
labels: list[dict[Literal["externalId"], str]] | None = None
|
|
23
|
+
geo_location: JsonValue | None = None
|
|
24
|
+
source_created_time: int | None = None
|
|
25
|
+
source_modified_time: int | None = None
|
|
26
|
+
security_categories: list[int] | None = None
|
|
27
|
+
|
|
28
|
+
def as_id(self) -> ExternalId:
|
|
29
|
+
if self.external_id is None:
|
|
30
|
+
raise ValueError("Cannot convert FileMetadataRequest to ExternalId when external_id is None")
|
|
31
|
+
return ExternalId(external_id=self.external_id)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FileMetadataResponse(ResponseResource[FileMetadataRequest]):
|
|
35
|
+
created_time: int
|
|
36
|
+
last_updated_time: int
|
|
37
|
+
uploaded_time: int | None = None
|
|
38
|
+
uploaded: bool
|
|
39
|
+
id: int
|
|
40
|
+
external_id: str | None = None
|
|
41
|
+
name: str
|
|
42
|
+
directory: str | None = None
|
|
43
|
+
instance_id: NodeReference | None = None
|
|
44
|
+
source: str | None = None
|
|
45
|
+
mime_type: str | None = None
|
|
46
|
+
metadata: dict[str, str] | None = None
|
|
47
|
+
asset_ids: list[int] | None = None
|
|
48
|
+
data_set_id: int | None = None
|
|
49
|
+
labels: list[dict[Literal["externalId"], str]] | None = None
|
|
50
|
+
geo_location: JsonValue | None = None
|
|
51
|
+
source_created_time: int | None = None
|
|
52
|
+
source_modified_time: int | None = None
|
|
53
|
+
security_categories: list[int] | None = None
|
|
54
|
+
|
|
55
|
+
def as_request_resource(self) -> FileMetadataRequest:
|
|
56
|
+
return FileMetadataRequest.model_validate(self.dump(), extra="ignore")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Annotated, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import Identifier
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InternalOrExternalIdDefinition(Identifier):
|
|
10
|
+
type: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InternalId(InternalOrExternalIdDefinition):
|
|
14
|
+
type: Literal["id"] = Field("id", exclude=True)
|
|
15
|
+
id: int
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_ids(cls, ids: Sequence[int]) -> list["InternalId"]:
|
|
19
|
+
return [cls(id=id_) for id_ in ids]
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
return f"id={self.id}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ExternalId(InternalOrExternalIdDefinition):
|
|
26
|
+
type: Literal["externalId"] = Field("externalId", exclude=True)
|
|
27
|
+
external_id: str
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_external_ids(cls, external_ids: list[str]) -> list["ExternalId"]:
|
|
31
|
+
return [cls(external_id=ext_id) for ext_id in external_ids]
|
|
32
|
+
|
|
33
|
+
def __str__(self) -> str:
|
|
34
|
+
return f"externalId='{self.external_id}'"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
InternalOrExternalId = Annotated[InternalId | ExternalId, Field(discriminator="type")]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class NameId(Identifier):
|
|
41
|
+
name: str
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
return f"name='{self.name}'"
|
|
@@ -14,19 +14,38 @@ class TypedInstanceIdentifier(Identifier):
|
|
|
14
14
|
space: str
|
|
15
15
|
external_id: str
|
|
16
16
|
|
|
17
|
+
def __str__(self) -> str:
|
|
18
|
+
return f"Instance({self.instance_type}, {self.space}, {self.external_id})"
|
|
19
|
+
|
|
20
|
+
def dump(self, camel_case: bool = True, include_type: bool = True) -> dict[str, Any]:
|
|
21
|
+
"""Dump the resource to a dictionary.
|
|
22
|
+
|
|
23
|
+
This is the default serialization method for request resources.
|
|
24
|
+
"""
|
|
25
|
+
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=not include_type)
|
|
26
|
+
|
|
17
27
|
|
|
18
28
|
class TypedNodeIdentifier(TypedInstanceIdentifier):
|
|
19
29
|
instance_type: Literal["node"] = "node"
|
|
20
30
|
|
|
31
|
+
def __str__(self) -> str:
|
|
32
|
+
return f"Node({self.space}, {self.external_id})"
|
|
33
|
+
|
|
21
34
|
|
|
22
35
|
class TypedEdgeIdentifier(TypedInstanceIdentifier):
|
|
23
36
|
instance_type: Literal["edge"] = "edge"
|
|
24
37
|
|
|
38
|
+
def __str__(self) -> str:
|
|
39
|
+
return f"Edge({self.space}, {self.external_id})"
|
|
40
|
+
|
|
25
41
|
|
|
26
42
|
class InstanceIdentifier(Identifier):
|
|
27
43
|
space: str
|
|
28
44
|
external_id: str
|
|
29
45
|
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return f"Instance({self.space}, {self.external_id})"
|
|
48
|
+
|
|
30
49
|
|
|
31
50
|
class InstanceResult(BaseModelObject):
|
|
32
51
|
instance_type: InstanceType
|
|
@@ -51,6 +70,16 @@ class ViewReference(Identifier):
|
|
|
51
70
|
external_id: str
|
|
52
71
|
version: str
|
|
53
72
|
|
|
73
|
+
def __str__(self) -> str:
|
|
74
|
+
return f"View({self.space}, {self.external_id}, v{self.version})"
|
|
75
|
+
|
|
76
|
+
def dump(self, camel_case: bool = True, include_type: bool = True) -> dict[str, Any]:
|
|
77
|
+
"""Dump the resource to a dictionary.
|
|
78
|
+
|
|
79
|
+
This is the default serialization method for request resources.
|
|
80
|
+
"""
|
|
81
|
+
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=not include_type)
|
|
82
|
+
|
|
54
83
|
|
|
55
84
|
######################################################
|
|
56
85
|
# The classes below are helper classes for making instances request/responses.
|
|
@@ -160,3 +189,8 @@ class InstanceResponseItem(BaseModelObject):
|
|
|
160
189
|
space=self.space,
|
|
161
190
|
external_id=self.external_id,
|
|
162
191
|
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class NodeReference(BaseModelObject):
|
|
195
|
+
space: str
|
|
196
|
+
external_id: str
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import (
|
|
6
|
+
Identifier,
|
|
7
|
+
RequestResource,
|
|
8
|
+
ResponseResource,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
if sys.version_info >= (3, 11):
|
|
12
|
+
from typing import Self
|
|
13
|
+
else:
|
|
14
|
+
from typing_extensions import Self
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RAWDatabase(RequestResource, Identifier, ResponseResource["RAWDatabase"]):
|
|
18
|
+
name: str
|
|
19
|
+
|
|
20
|
+
def as_id(self) -> Self:
|
|
21
|
+
return self
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return f"name='{self.name}'"
|
|
25
|
+
|
|
26
|
+
def as_request_resource(self) -> "RAWDatabase":
|
|
27
|
+
return type(self).model_validate(self.dump(), extra="ignore")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RAWTable(RequestResource, Identifier, ResponseResource["RAWTable"]):
|
|
31
|
+
# This is a query parameter, so we exclude it from serialization
|
|
32
|
+
db_name: str = Field(exclude=True)
|
|
33
|
+
name: str
|
|
34
|
+
|
|
35
|
+
def as_id(self) -> Self:
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
def __str__(self) -> str:
|
|
39
|
+
return f"dbName='{self.db_name}', tableName='{self.name}'"
|
|
40
|
+
|
|
41
|
+
def as_request_resource(self) -> "RAWTable":
|
|
42
|
+
dumped = {**self.dump(), "dbName": self.db_name}
|
|
43
|
+
return type(self).model_validate(dumped, extra="ignore")
|
|
@@ -7,6 +7,7 @@ from cognite_toolkit._cdf_tk.protocols import (
|
|
|
7
7
|
)
|
|
8
8
|
|
|
9
9
|
from .base import BaseModelObject, BaseResourceList, RequestResource, ResponseResource
|
|
10
|
+
from .identifiers import ExternalId
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class StreamRequest(RequestResource):
|
|
@@ -15,8 +16,8 @@ class StreamRequest(RequestResource):
|
|
|
15
16
|
external_id: str
|
|
16
17
|
settings: dict[Literal["template"], dict[Literal["name"], StreamTemplateName]]
|
|
17
18
|
|
|
18
|
-
def as_id(self) ->
|
|
19
|
-
return self.external_id
|
|
19
|
+
def as_id(self) -> ExternalId:
|
|
20
|
+
return ExternalId(external_id=self.external_id)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class StreamRequestList(BaseResourceList[StreamRequest], ResourceRequestListProtocol):
|