cognite-toolkit 0.7.42__py3-none-any.whl → 0.7.44__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/events.py +20 -2
- cognite_toolkit/_cdf_tk/client/api/filemetadata.py +145 -0
- cognite_toolkit/_cdf_tk/client/api/raw.py +174 -0
- cognite_toolkit/_cdf_tk/client/api/simulator_models.py +118 -0
- cognite_toolkit/_cdf_tk/client/api/simulators.py +8 -0
- cognite_toolkit/_cdf_tk/client/api/timeseries.py +20 -2
- cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +2 -1
- cognite_toolkit/_cdf_tk/client/cdf_client/api.py +40 -6
- cognite_toolkit/_cdf_tk/client/data_classes/agent.py +6 -9
- cognite_toolkit/_cdf_tk/client/data_classes/annotation.py +79 -0
- cognite_toolkit/_cdf_tk/client/data_classes/asset.py +7 -14
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +15 -5
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/__init__.py +164 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_constraints.py +37 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_container.py +50 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_model.py +73 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_types.py +116 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_indexes.py +26 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_instance.py +143 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_references.py +86 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_space.py +26 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view.py +143 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view_property.py +152 -0
- cognite_toolkit/_cdf_tk/client/data_classes/dataset.py +35 -0
- cognite_toolkit/_cdf_tk/client/data_classes/event.py +12 -15
- cognite_toolkit/_cdf_tk/client/data_classes/extraction_pipeline.py +59 -0
- cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +15 -19
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_destination.py +34 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_job.py +134 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_mapping.py +72 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/__init__.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_auth.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_base.py +26 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_certificate.py +20 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_eventhub.py +31 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_kafka.py +53 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_mqtt.py +36 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_rest.py +49 -0
- cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +8 -0
- cognite_toolkit/_cdf_tk/client/data_classes/label.py +27 -0
- cognite_toolkit/_cdf_tk/client/data_classes/raw.py +3 -2
- cognite_toolkit/_cdf_tk/client/data_classes/securitycategory.py +24 -0
- cognite_toolkit/_cdf_tk/client/data_classes/sequence.py +45 -0
- cognite_toolkit/_cdf_tk/client/data_classes/simulator_model.py +50 -0
- cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +15 -18
- cognite_toolkit/_cdf_tk/client/data_classes/transformation.py +140 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow.py +27 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow_trigger.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow_version.py +155 -0
- cognite_toolkit/_cdf_tk/client/testing.py +6 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +10 -7
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +4 -4
- cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +7 -3
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +40 -39
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +56 -59
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -3
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +48 -47
- cognite_toolkit/_cdf_tk/resource_classes/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/resource_classes/simulator_model.py +17 -0
- cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +84 -71
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +22 -19
- cognite_toolkit/_cdf_tk/utils/useful_types2.py +5 -3
- 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.42.dist-info → cognite_toolkit-0.7.44.dist-info}/METADATA +11 -1
- {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/RECORD +72 -34
- {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/WHEEL +1 -1
- {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Annotated, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field, TypeAdapter
|
|
5
|
+
|
|
6
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject
|
|
7
|
+
|
|
8
|
+
from ._references import ContainerReference, ViewReference
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PropertyTypeDefinition(BaseModelObject, ABC):
|
|
12
|
+
type: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ListablePropertyTypeDefinition(PropertyTypeDefinition, ABC):
|
|
16
|
+
list: bool | None = None
|
|
17
|
+
max_list_size: int | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TextProperty(ListablePropertyTypeDefinition):
|
|
21
|
+
type: Literal["text"] = "text"
|
|
22
|
+
max_text_size: int | None = None
|
|
23
|
+
collation: str | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Unit(BaseModelObject):
|
|
27
|
+
external_id: str
|
|
28
|
+
source_unit: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FloatProperty(ListablePropertyTypeDefinition, ABC):
|
|
32
|
+
unit: Unit | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Float32Property(FloatProperty):
|
|
36
|
+
type: Literal["float32"] = "float32"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Float64Property(FloatProperty):
|
|
40
|
+
type: Literal["float64"] = "float64"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class BooleanProperty(ListablePropertyTypeDefinition):
|
|
44
|
+
type: Literal["boolean"] = "boolean"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Int32Property(ListablePropertyTypeDefinition):
|
|
48
|
+
type: Literal["int32"] = "int32"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Int64Property(ListablePropertyTypeDefinition):
|
|
52
|
+
type: Literal["int64"] = "int64"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TimestampProperty(ListablePropertyTypeDefinition):
|
|
56
|
+
type: Literal["timestamp"] = "timestamp"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DateProperty(ListablePropertyTypeDefinition):
|
|
60
|
+
type: Literal["date"] = "date"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class JSONProperty(ListablePropertyTypeDefinition):
|
|
64
|
+
type: Literal["json"] = "json"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TimeseriesCDFExternalIdReference(ListablePropertyTypeDefinition):
|
|
68
|
+
type: Literal["timeseries"] = "timeseries"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class FileCDFExternalIdReference(ListablePropertyTypeDefinition):
|
|
72
|
+
type: Literal["file"] = "file"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SequenceCDFExternalIdReference(ListablePropertyTypeDefinition):
|
|
76
|
+
type: Literal["sequence"] = "sequence"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class DirectNodeRelation(ListablePropertyTypeDefinition):
|
|
80
|
+
type: Literal["direct"] = "direct"
|
|
81
|
+
container: ContainerReference | None = None
|
|
82
|
+
# This property is only available in the response object. It will be ignored in the request object.
|
|
83
|
+
# In the request object, use ViewCoreProperty.source instead.
|
|
84
|
+
source: ViewReference | None = Field(None, exclude=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class EnumValue(BaseModelObject):
|
|
88
|
+
name: str | None = None
|
|
89
|
+
description: str | None = None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class EnumProperty(PropertyTypeDefinition):
|
|
93
|
+
type: Literal["enum"] = "enum"
|
|
94
|
+
unknown_value: str | None = None
|
|
95
|
+
values: dict[str, EnumValue]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
DataType = Annotated[
|
|
99
|
+
TextProperty
|
|
100
|
+
| Float32Property
|
|
101
|
+
| Float64Property
|
|
102
|
+
| BooleanProperty
|
|
103
|
+
| Int32Property
|
|
104
|
+
| Int64Property
|
|
105
|
+
| TimestampProperty
|
|
106
|
+
| DateProperty
|
|
107
|
+
| JSONProperty
|
|
108
|
+
| TimeseriesCDFExternalIdReference
|
|
109
|
+
| FileCDFExternalIdReference
|
|
110
|
+
| SequenceCDFExternalIdReference
|
|
111
|
+
| EnumProperty
|
|
112
|
+
| DirectNodeRelation,
|
|
113
|
+
Field(discriminator="type"),
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
DataTypeAdapter: TypeAdapter[DataType] = TypeAdapter(DataType)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Annotated, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field, TypeAdapter
|
|
5
|
+
|
|
6
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IndexDefinition(BaseModelObject, ABC):
|
|
10
|
+
index_type: str
|
|
11
|
+
properties: list[str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BtreeIndex(IndexDefinition):
|
|
15
|
+
index_type: Literal["btree"] = "btree"
|
|
16
|
+
by_space: bool | None = None
|
|
17
|
+
cursorable: bool | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InvertedIndex(IndexDefinition):
|
|
21
|
+
index_type: Literal["inverted"] = "inverted"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Index = Annotated[BtreeIndex | InvertedIndex, Field(discriminator="index_type")]
|
|
25
|
+
|
|
26
|
+
IndexAdapter: TypeAdapter[Index] = TypeAdapter(Index)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Any, Generic, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import JsonValue, field_serializer, field_validator
|
|
5
|
+
|
|
6
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import (
|
|
7
|
+
BaseModelObject,
|
|
8
|
+
RequestResource,
|
|
9
|
+
ResponseResource,
|
|
10
|
+
T_RequestResource,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from ._references import ContainerReference, EdgeReference, NodeReference, ViewReference
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InstanceDefinition(BaseModelObject, ABC):
|
|
17
|
+
"""Base class for node and edge instances."""
|
|
18
|
+
|
|
19
|
+
instance_type: str # "node" | "edge"
|
|
20
|
+
space: str
|
|
21
|
+
external_id: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InstanceSource(BaseModelObject):
|
|
25
|
+
source: ViewReference | ContainerReference
|
|
26
|
+
properties: dict[str, JsonValue] | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class InstanceRequestDefinition(InstanceDefinition, RequestResource, ABC):
|
|
30
|
+
existing_version: int | None = None
|
|
31
|
+
sources: list[InstanceSource]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class InstanceResponseDefinition(InstanceDefinition, ResponseResource, Generic[T_RequestResource], ABC):
|
|
35
|
+
version: int
|
|
36
|
+
created_time: int
|
|
37
|
+
last_updated_time: int
|
|
38
|
+
deleted_time: int | None = None
|
|
39
|
+
properties: dict[ViewReference | ContainerReference, dict[str, JsonValue]] | None = None
|
|
40
|
+
|
|
41
|
+
@field_validator("properties", mode="before")
|
|
42
|
+
def parse_reference(cls, value: Any) -> Any:
|
|
43
|
+
if not isinstance(value, dict):
|
|
44
|
+
return value
|
|
45
|
+
parsed: dict[ViewReference | ContainerReference, dict[str, Any]] = {}
|
|
46
|
+
for space, inner_dict in value.items():
|
|
47
|
+
if not isinstance(inner_dict, dict) or not isinstance(space, str):
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Invalid properties format expected dict[str, dict[...]], got: dict[{type(space).__name__}, {type(inner_dict).__name__}]"
|
|
50
|
+
)
|
|
51
|
+
for view_or_container_identifier, prop in inner_dict.items():
|
|
52
|
+
if not isinstance(view_or_container_identifier, str):
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"Invalid properties format expected dict[str, dict[str, ...]]], "
|
|
55
|
+
f"got: dict[{type(space).__name__}, "
|
|
56
|
+
f"dict[{type(view_or_container_identifier).__name__}, ...]]"
|
|
57
|
+
)
|
|
58
|
+
source_ref: ViewReference | ContainerReference
|
|
59
|
+
if "/" in view_or_container_identifier:
|
|
60
|
+
external_id, version = view_or_container_identifier.split("/", 1)
|
|
61
|
+
source_ref = ViewReference(space=space, external_id=external_id, version=version)
|
|
62
|
+
else:
|
|
63
|
+
source_ref = ContainerReference(space=space, external_id=view_or_container_identifier)
|
|
64
|
+
parsed[source_ref] = prop
|
|
65
|
+
return parsed
|
|
66
|
+
|
|
67
|
+
@field_serializer("properties", mode="plain")
|
|
68
|
+
def serialize_properties(self, value: dict[ViewReference | ContainerReference, dict[str, Any]] | None) -> Any:
|
|
69
|
+
if value is None:
|
|
70
|
+
return None
|
|
71
|
+
serialized: dict[str, dict[str, Any]] = {}
|
|
72
|
+
for source_ref, props in value.items():
|
|
73
|
+
space = source_ref.space
|
|
74
|
+
if space not in serialized:
|
|
75
|
+
serialized[space] = {}
|
|
76
|
+
if isinstance(source_ref, ViewReference):
|
|
77
|
+
identifier = f"{source_ref.external_id}/{source_ref.version}"
|
|
78
|
+
else:
|
|
79
|
+
identifier = source_ref.external_id
|
|
80
|
+
serialized[space][identifier] = props
|
|
81
|
+
return serialized
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class NodeRequest(InstanceRequestDefinition):
|
|
85
|
+
"""A node request resource."""
|
|
86
|
+
|
|
87
|
+
instance_type: Literal["node"] = "node"
|
|
88
|
+
type: NodeReference | None = None
|
|
89
|
+
|
|
90
|
+
def as_id(self) -> NodeReference:
|
|
91
|
+
return NodeReference(space=self.space, external_id=self.external_id)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class EdgeRequest(InstanceRequestDefinition):
|
|
95
|
+
"""An edge request resource."""
|
|
96
|
+
|
|
97
|
+
instance_type: Literal["edge"] = "edge"
|
|
98
|
+
type: NodeReference
|
|
99
|
+
start_node: NodeReference
|
|
100
|
+
end_node: NodeReference
|
|
101
|
+
|
|
102
|
+
def as_id(self) -> EdgeReference:
|
|
103
|
+
return EdgeReference(space=self.space, external_id=self.external_id)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class NodeResponse(InstanceResponseDefinition[NodeRequest]):
|
|
107
|
+
"""A node response from the API."""
|
|
108
|
+
|
|
109
|
+
instance_type: Literal["node"] = "node"
|
|
110
|
+
type: NodeReference | None = None
|
|
111
|
+
|
|
112
|
+
def as_id(self) -> NodeReference:
|
|
113
|
+
return NodeReference(space=self.space, external_id=self.external_id)
|
|
114
|
+
|
|
115
|
+
def as_request_resource(self) -> NodeRequest:
|
|
116
|
+
dumped = self.dump()
|
|
117
|
+
if self.properties:
|
|
118
|
+
dumped["sources"] = [
|
|
119
|
+
InstanceSource(source=source_ref, properties=props) for source_ref, props in self.properties.items()
|
|
120
|
+
]
|
|
121
|
+
dumped["existingVersion"] = dumped.pop("version", None)
|
|
122
|
+
return NodeRequest.model_validate(dumped, extra="ignore")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class EdgeResponse(InstanceResponseDefinition[EdgeRequest]):
|
|
126
|
+
"""An edge response from the API."""
|
|
127
|
+
|
|
128
|
+
instance_type: Literal["edge"] = "edge"
|
|
129
|
+
type: NodeReference
|
|
130
|
+
start_node: NodeReference
|
|
131
|
+
end_node: NodeReference
|
|
132
|
+
|
|
133
|
+
def as_id(self) -> EdgeReference:
|
|
134
|
+
return EdgeReference(space=self.space, external_id=self.external_id)
|
|
135
|
+
|
|
136
|
+
def as_request_resource(self) -> EdgeRequest:
|
|
137
|
+
dumped = self.dump()
|
|
138
|
+
if self.properties:
|
|
139
|
+
dumped["sources"] = [
|
|
140
|
+
InstanceSource(source=source_ref, properties=props) for source_ref, props in self.properties.items()
|
|
141
|
+
]
|
|
142
|
+
dumped["existingVersion"] = dumped.pop("version", None)
|
|
143
|
+
return EdgeRequest.model_validate(dumped, extra="ignore")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.identifiers import Identifier
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SpaceReference(Identifier):
|
|
9
|
+
space: str
|
|
10
|
+
|
|
11
|
+
def __str__(self) -> str:
|
|
12
|
+
return self.space
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ContainerReference(Identifier):
|
|
16
|
+
type: Literal["container"] = Field("container", exclude=True)
|
|
17
|
+
space: str
|
|
18
|
+
external_id: str
|
|
19
|
+
|
|
20
|
+
def __str__(self) -> str:
|
|
21
|
+
return f"{self.space}:{self.external_id}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ViewReference(Identifier):
|
|
25
|
+
type: Literal["view"] = Field("view", exclude=True)
|
|
26
|
+
space: str
|
|
27
|
+
external_id: str
|
|
28
|
+
version: str
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
return f"{self.space}:{self.external_id}(version={self.version})"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DataModelReference(Identifier):
|
|
35
|
+
space: str
|
|
36
|
+
external_id: str
|
|
37
|
+
version: str
|
|
38
|
+
|
|
39
|
+
def __str__(self) -> str:
|
|
40
|
+
return f"{self.space}:{self.external_id}(version={self.version})"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class NodeReference(Identifier):
|
|
44
|
+
space: str
|
|
45
|
+
external_id: str
|
|
46
|
+
|
|
47
|
+
def __str__(self) -> str:
|
|
48
|
+
return f"{self.space}:{self.external_id}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class EdgeReference(Identifier):
|
|
52
|
+
space: str
|
|
53
|
+
external_id: str
|
|
54
|
+
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
return f"{self.space}:{self.external_id}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ContainerDirectReference(Identifier):
|
|
60
|
+
source: ContainerReference
|
|
61
|
+
identifier: str
|
|
62
|
+
|
|
63
|
+
def __str__(self) -> str:
|
|
64
|
+
return f"{self.source!s}.{self.identifier}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ViewDirectReference(Identifier):
|
|
68
|
+
source: ViewReference
|
|
69
|
+
identifier: str
|
|
70
|
+
|
|
71
|
+
def __str__(self) -> str:
|
|
72
|
+
return f"{self.source!s}.{self.identifier}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ContainerIndexReference(ContainerReference):
|
|
76
|
+
identifier: str
|
|
77
|
+
|
|
78
|
+
def __str__(self) -> str:
|
|
79
|
+
return f"{self.space}:{self.external_id}(index={self.identifier})"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ContainerConstraintReference(ContainerReference):
|
|
83
|
+
identifier: str
|
|
84
|
+
|
|
85
|
+
def __str__(self) -> str:
|
|
86
|
+
return f"{self.space}:{self.external_id}(constraint={self.identifier})"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestResource, ResponseResource
|
|
4
|
+
|
|
5
|
+
from ._references import SpaceReference
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Space(BaseModelObject, ABC):
|
|
9
|
+
space: str
|
|
10
|
+
name: str | None = None
|
|
11
|
+
description: str | None = None
|
|
12
|
+
|
|
13
|
+
def as_id(self) -> SpaceReference:
|
|
14
|
+
return SpaceReference(space=self.space)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SpaceRequest(Space, RequestResource): ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SpaceResponse(Space, ResponseResource[SpaceRequest]):
|
|
21
|
+
created_time: int
|
|
22
|
+
last_updated_time: int
|
|
23
|
+
is_global: bool
|
|
24
|
+
|
|
25
|
+
def as_request_resource(self) -> SpaceRequest:
|
|
26
|
+
return SpaceRequest.model_validate(self.model_dump(by_alias=True), extra="ignore")
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Any, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field, JsonValue, field_serializer, model_validator
|
|
5
|
+
from pydantic_core.core_schema import FieldSerializationInfo
|
|
6
|
+
|
|
7
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestResource, ResponseResource
|
|
8
|
+
|
|
9
|
+
from ._data_types import DirectNodeRelation
|
|
10
|
+
from ._references import ContainerReference, ViewReference
|
|
11
|
+
from ._view_property import (
|
|
12
|
+
MultiReverseDirectRelationPropertyResponse,
|
|
13
|
+
SingleReverseDirectRelationPropertyResponse,
|
|
14
|
+
ViewCorePropertyRequest,
|
|
15
|
+
ViewCorePropertyResponse,
|
|
16
|
+
ViewRequestProperty,
|
|
17
|
+
ViewResponseProperty,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class View(BaseModelObject, ABC):
|
|
22
|
+
space: str
|
|
23
|
+
external_id: str
|
|
24
|
+
version: str
|
|
25
|
+
name: str | None = None
|
|
26
|
+
description: str | None = None
|
|
27
|
+
filter: JsonValue | None
|
|
28
|
+
implements: list[ViewReference] | None = None
|
|
29
|
+
|
|
30
|
+
def as_id(self) -> ViewReference:
|
|
31
|
+
return ViewReference(space=self.space, external_id=self.external_id, version=self.version)
|
|
32
|
+
|
|
33
|
+
@model_validator(mode="before")
|
|
34
|
+
def set_connection_type_on_primary_properties(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
35
|
+
if "properties" not in data:
|
|
36
|
+
return data
|
|
37
|
+
properties = data["properties"]
|
|
38
|
+
if not isinstance(properties, dict):
|
|
39
|
+
return data
|
|
40
|
+
# We assume all properties without connectionType are core properties.
|
|
41
|
+
# The reason we set connectionType is to make it easy for pydantic to discriminate the union.
|
|
42
|
+
# This also leads to better error messages, as if there is a union and pydantic does not know which
|
|
43
|
+
# type to pick it will give errors from all type in the union.
|
|
44
|
+
new_properties: dict[str, Any] = {}
|
|
45
|
+
for prop_id, prop in properties.items():
|
|
46
|
+
if isinstance(prop, dict) and "connectionType" not in prop:
|
|
47
|
+
prop_copy = prop.copy()
|
|
48
|
+
prop_copy["connectionType"] = "primary_property"
|
|
49
|
+
new_properties[prop_id] = prop_copy
|
|
50
|
+
else:
|
|
51
|
+
new_properties[prop_id] = prop
|
|
52
|
+
if new_properties:
|
|
53
|
+
new_data = data.copy()
|
|
54
|
+
new_data["properties"] = new_properties
|
|
55
|
+
return new_data
|
|
56
|
+
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
@field_serializer("implements", mode="plain")
|
|
60
|
+
@classmethod
|
|
61
|
+
def serialize_implements(
|
|
62
|
+
cls, implements: list[ViewReference] | None, info: FieldSerializationInfo
|
|
63
|
+
) -> list[dict[str, Any]] | None:
|
|
64
|
+
if implements is None:
|
|
65
|
+
return None
|
|
66
|
+
output: list[dict[str, Any]] = []
|
|
67
|
+
for view in implements:
|
|
68
|
+
dumped = view.model_dump(**vars(info))
|
|
69
|
+
dumped["type"] = "view"
|
|
70
|
+
output.append(dumped)
|
|
71
|
+
return output
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ViewRequest(View, RequestResource):
|
|
75
|
+
properties: dict[str, ViewRequestProperty] = Field(
|
|
76
|
+
description="View with included properties and expected edges, indexed by a unique space-local identifier."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def used_containers(self) -> set[ContainerReference]:
|
|
81
|
+
"""Get all containers referenced by this view."""
|
|
82
|
+
return {prop.container for prop in self.properties.values() if isinstance(prop, ViewCorePropertyRequest)}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ViewResponse(View, ResponseResource[ViewRequest]):
|
|
86
|
+
properties: dict[str, ViewResponseProperty]
|
|
87
|
+
|
|
88
|
+
created_time: int
|
|
89
|
+
last_updated_time: int
|
|
90
|
+
writable: bool
|
|
91
|
+
queryable: bool
|
|
92
|
+
used_for: Literal["node", "edge", "all"]
|
|
93
|
+
is_global: bool
|
|
94
|
+
mapped_containers: list[ContainerReference]
|
|
95
|
+
|
|
96
|
+
def as_request_resource(self) -> ViewRequest:
|
|
97
|
+
dumped = self.model_dump(by_alias=True, exclude={"properties"})
|
|
98
|
+
properties: dict[str, Any] = {}
|
|
99
|
+
for key, value in self.properties.items():
|
|
100
|
+
if isinstance(value, ViewCorePropertyResponse) and isinstance(value.type, DirectNodeRelation):
|
|
101
|
+
# Special case. In the request the source of DirectNodeRelation is set on the Property object,
|
|
102
|
+
# while in the response it is set on the DirectNodeRelation object.
|
|
103
|
+
request_object = value.as_request().model_dump(by_alias=True)
|
|
104
|
+
request_object["source"] = value.type.source.model_dump(by_alias=True) if value.type.source else None
|
|
105
|
+
properties[key] = request_object
|
|
106
|
+
elif isinstance(
|
|
107
|
+
value,
|
|
108
|
+
ViewCorePropertyResponse
|
|
109
|
+
| SingleReverseDirectRelationPropertyResponse
|
|
110
|
+
| MultiReverseDirectRelationPropertyResponse,
|
|
111
|
+
):
|
|
112
|
+
properties[key] = value.as_request().model_dump(by_alias=True)
|
|
113
|
+
else:
|
|
114
|
+
properties[key] = value.model_dump(by_alias=True)
|
|
115
|
+
|
|
116
|
+
dumped["properties"] = properties
|
|
117
|
+
return ViewRequest.model_validate(dumped, extra="ignore")
|
|
118
|
+
|
|
119
|
+
@field_serializer("mapped_containers", mode="plain")
|
|
120
|
+
@classmethod
|
|
121
|
+
def serialize_mapped_containers(
|
|
122
|
+
cls, mapped_containers: list[ContainerReference], info: FieldSerializationInfo
|
|
123
|
+
) -> list[dict[str, Any]]:
|
|
124
|
+
return [container.model_dump(**vars(info)) | {"type": "container"} for container in mapped_containers]
|
|
125
|
+
|
|
126
|
+
@field_serializer("properties", mode="plain")
|
|
127
|
+
@classmethod
|
|
128
|
+
def serialize_properties_special_handling_direct_relation_with_source(
|
|
129
|
+
cls, properties: dict[str, ViewResponseProperty], info: FieldSerializationInfo
|
|
130
|
+
) -> dict[str, dict[str, Any]]:
|
|
131
|
+
output: dict[str, dict[str, Any]] = {}
|
|
132
|
+
for prop_id, prop in properties.items():
|
|
133
|
+
output[prop_id] = prop.model_dump(**vars(info))
|
|
134
|
+
if (
|
|
135
|
+
isinstance(prop, ViewCorePropertyResponse)
|
|
136
|
+
and isinstance(prop.type, DirectNodeRelation)
|
|
137
|
+
and prop.type.source
|
|
138
|
+
):
|
|
139
|
+
# We manually include the source as this is excluded by default. The reason why this is excluded
|
|
140
|
+
# is that the DirectNodeRelation is used for both request and response, and in the request the source
|
|
141
|
+
# does not exist on the DirectNodeRelation, but on the Property object.
|
|
142
|
+
output[prop_id]["type"]["source"] = prop.type.source.model_dump(**vars(info)) | {"type": "view"}
|
|
143
|
+
return output
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Annotated, Any, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field, JsonValue, TypeAdapter, field_serializer
|
|
5
|
+
from pydantic_core.core_schema import FieldSerializationInfo
|
|
6
|
+
|
|
7
|
+
from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject
|
|
8
|
+
|
|
9
|
+
from ._data_types import DataType
|
|
10
|
+
from ._references import ContainerDirectReference, ContainerReference, NodeReference, ViewDirectReference, ViewReference
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ViewPropertyDefinition(BaseModelObject, ABC):
|
|
14
|
+
connection_type: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ViewCoreProperty(ViewPropertyDefinition, ABC):
|
|
18
|
+
# Core properties do not have connection type in the API, but we add it here such that
|
|
19
|
+
# we can use it as a discriminator in unions. The exclude=True ensures that it is not
|
|
20
|
+
# sent to the API.
|
|
21
|
+
connection_type: Literal["primary_property"] = Field(default="primary_property", exclude=True)
|
|
22
|
+
name: str | None = None
|
|
23
|
+
description: str | None = None
|
|
24
|
+
container: ContainerReference
|
|
25
|
+
container_property_identifier: str
|
|
26
|
+
|
|
27
|
+
@field_serializer("container", mode="plain")
|
|
28
|
+
@classmethod
|
|
29
|
+
def serialize_container(cls, container: ContainerReference, info: FieldSerializationInfo) -> dict[str, Any]:
|
|
30
|
+
return {**container.model_dump(**vars(info)), "type": "container"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ViewCorePropertyRequest(ViewCoreProperty):
|
|
34
|
+
source: ViewReference | None = None
|
|
35
|
+
|
|
36
|
+
@field_serializer("source", mode="plain")
|
|
37
|
+
@classmethod
|
|
38
|
+
def serialize_source(cls, source: ViewReference | None, info: FieldSerializationInfo) -> dict[str, Any] | None:
|
|
39
|
+
if source is None:
|
|
40
|
+
return None
|
|
41
|
+
return {**source.model_dump(**vars(info)), "type": "view"}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ConstraintOrIndexState(BaseModelObject):
|
|
45
|
+
nullability: Literal["current", "pending", "failed"] | None = None
|
|
46
|
+
max_list_size: Literal["current", "pending", "failed"] | None = None
|
|
47
|
+
max_text_size: Literal["current", "pending", "failed"] | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ViewCorePropertyResponse(ViewCoreProperty):
|
|
51
|
+
immutable: bool | None = None
|
|
52
|
+
nullable: bool | None = None
|
|
53
|
+
auto_increment: bool | None = None
|
|
54
|
+
default_value: str | int | bool | dict[str, JsonValue] | None = None
|
|
55
|
+
constraint_state: ConstraintOrIndexState
|
|
56
|
+
type: DataType
|
|
57
|
+
|
|
58
|
+
def as_request(self) -> ViewCorePropertyRequest:
|
|
59
|
+
return ViewCorePropertyRequest.model_validate(self.model_dump(by_alias=True))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ConnectionPropertyDefinition(ViewPropertyDefinition, ABC):
|
|
63
|
+
name: str | None = None
|
|
64
|
+
description: str | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class EdgeProperty(ConnectionPropertyDefinition, ABC):
|
|
68
|
+
source: ViewReference
|
|
69
|
+
type: NodeReference
|
|
70
|
+
edge_source: ViewReference | None = None
|
|
71
|
+
direction: Literal["outwards", "inwards"] = "outwards"
|
|
72
|
+
|
|
73
|
+
@field_serializer("source", "edge_source", mode="plain")
|
|
74
|
+
@classmethod
|
|
75
|
+
def serialize_source(cls, source: ViewReference | None, info: FieldSerializationInfo) -> dict[str, Any] | None:
|
|
76
|
+
if source is None:
|
|
77
|
+
return None
|
|
78
|
+
return {**source.model_dump(**vars(info)), "type": "view"}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class SingleEdgeProperty(EdgeProperty):
|
|
82
|
+
connection_type: Literal["single_edge_connection"] = "single_edge_connection"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class MultiEdgeProperty(EdgeProperty):
|
|
86
|
+
connection_type: Literal["multi_edge_connection"] = "multi_edge_connection"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ReverseDirectRelationProperty(ConnectionPropertyDefinition, ABC):
|
|
90
|
+
source: ViewReference
|
|
91
|
+
through: ContainerDirectReference | ViewDirectReference
|
|
92
|
+
|
|
93
|
+
@field_serializer("source", mode="plain")
|
|
94
|
+
@classmethod
|
|
95
|
+
def serialize_source(cls, source: ViewReference, info: FieldSerializationInfo) -> dict[str, Any]:
|
|
96
|
+
return {**source.model_dump(**vars(info)), "type": "view"}
|
|
97
|
+
|
|
98
|
+
@field_serializer("through", mode="plain")
|
|
99
|
+
@classmethod
|
|
100
|
+
def serialize_through(
|
|
101
|
+
cls, through: ContainerDirectReference | ViewDirectReference, info: FieldSerializationInfo
|
|
102
|
+
) -> dict[str, Any]:
|
|
103
|
+
output = through.model_dump(**vars(info))
|
|
104
|
+
if isinstance(through, ContainerDirectReference):
|
|
105
|
+
output["source"]["type"] = "container"
|
|
106
|
+
else:
|
|
107
|
+
output["source"]["type"] = "view"
|
|
108
|
+
return output
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class SingleReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
|
|
112
|
+
connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class MultiReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
|
|
116
|
+
connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class SingleReverseDirectRelationPropertyResponse(ReverseDirectRelationProperty):
|
|
120
|
+
connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
|
|
121
|
+
targets_list: bool
|
|
122
|
+
|
|
123
|
+
def as_request(self) -> SingleReverseDirectRelationPropertyRequest:
|
|
124
|
+
return SingleReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class MultiReverseDirectRelationPropertyResponse(ReverseDirectRelationProperty):
|
|
128
|
+
connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
|
|
129
|
+
targets_list: bool
|
|
130
|
+
|
|
131
|
+
def as_request(self) -> MultiReverseDirectRelationPropertyRequest:
|
|
132
|
+
return MultiReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
ViewRequestProperty = Annotated[
|
|
136
|
+
SingleEdgeProperty
|
|
137
|
+
| MultiEdgeProperty
|
|
138
|
+
| SingleReverseDirectRelationPropertyRequest
|
|
139
|
+
| MultiReverseDirectRelationPropertyRequest
|
|
140
|
+
| ViewCorePropertyRequest,
|
|
141
|
+
Field(discriminator="connection_type"),
|
|
142
|
+
]
|
|
143
|
+
ViewResponseProperty = Annotated[
|
|
144
|
+
SingleEdgeProperty
|
|
145
|
+
| MultiEdgeProperty
|
|
146
|
+
| SingleReverseDirectRelationPropertyResponse
|
|
147
|
+
| MultiReverseDirectRelationPropertyResponse
|
|
148
|
+
| ViewCorePropertyResponse,
|
|
149
|
+
Field(discriminator="connection_type"),
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
ViewRequestPropertyAdapter: TypeAdapter[ViewRequestProperty] = TypeAdapter(ViewRequestProperty)
|