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.
Files changed (72) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +7 -1
  2. cognite_toolkit/_cdf_tk/client/api/events.py +20 -2
  3. cognite_toolkit/_cdf_tk/client/api/filemetadata.py +145 -0
  4. cognite_toolkit/_cdf_tk/client/api/raw.py +174 -0
  5. cognite_toolkit/_cdf_tk/client/api/simulator_models.py +118 -0
  6. cognite_toolkit/_cdf_tk/client/api/simulators.py +8 -0
  7. cognite_toolkit/_cdf_tk/client/api/timeseries.py +20 -2
  8. cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +2 -1
  9. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +40 -6
  10. cognite_toolkit/_cdf_tk/client/data_classes/agent.py +6 -9
  11. cognite_toolkit/_cdf_tk/client/data_classes/annotation.py +79 -0
  12. cognite_toolkit/_cdf_tk/client/data_classes/asset.py +7 -14
  13. cognite_toolkit/_cdf_tk/client/data_classes/base.py +15 -5
  14. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/__init__.py +164 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_constraints.py +37 -0
  16. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_container.py +50 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_model.py +73 -0
  18. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_types.py +116 -0
  19. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_indexes.py +26 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_instance.py +143 -0
  21. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_references.py +86 -0
  22. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_space.py +26 -0
  23. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view.py +143 -0
  24. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view_property.py +152 -0
  25. cognite_toolkit/_cdf_tk/client/data_classes/dataset.py +35 -0
  26. cognite_toolkit/_cdf_tk/client/data_classes/event.py +12 -15
  27. cognite_toolkit/_cdf_tk/client/data_classes/extraction_pipeline.py +59 -0
  28. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +15 -19
  29. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_destination.py +34 -0
  30. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_job.py +134 -0
  31. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_mapping.py +72 -0
  32. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/__init__.py +63 -0
  33. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_auth.py +63 -0
  34. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_base.py +26 -0
  35. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_certificate.py +20 -0
  36. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_eventhub.py +31 -0
  37. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_kafka.py +53 -0
  38. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_mqtt.py +36 -0
  39. cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_rest.py +49 -0
  40. cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +8 -0
  41. cognite_toolkit/_cdf_tk/client/data_classes/label.py +27 -0
  42. cognite_toolkit/_cdf_tk/client/data_classes/raw.py +3 -2
  43. cognite_toolkit/_cdf_tk/client/data_classes/securitycategory.py +24 -0
  44. cognite_toolkit/_cdf_tk/client/data_classes/sequence.py +45 -0
  45. cognite_toolkit/_cdf_tk/client/data_classes/simulator_model.py +50 -0
  46. cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +15 -18
  47. cognite_toolkit/_cdf_tk/client/data_classes/transformation.py +140 -0
  48. cognite_toolkit/_cdf_tk/client/data_classes/workflow.py +27 -0
  49. cognite_toolkit/_cdf_tk/client/data_classes/workflow_trigger.py +63 -0
  50. cognite_toolkit/_cdf_tk/client/data_classes/workflow_version.py +155 -0
  51. cognite_toolkit/_cdf_tk/client/testing.py +6 -1
  52. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +10 -7
  53. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +4 -4
  54. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +7 -3
  55. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -1
  56. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +40 -39
  57. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +56 -59
  58. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -3
  59. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +48 -47
  60. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +2 -0
  61. cognite_toolkit/_cdf_tk/resource_classes/simulator_model.py +17 -0
  62. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +84 -71
  63. cognite_toolkit/_cdf_tk/storageio/_file_content.py +22 -19
  64. cognite_toolkit/_cdf_tk/utils/useful_types2.py +5 -3
  65. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  66. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  67. cognite_toolkit/_resources/cdf.toml +1 -1
  68. cognite_toolkit/_version.py +1 -1
  69. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/METADATA +11 -1
  70. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/RECORD +72 -34
  71. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.44.dist-info}/WHEEL +1 -1
  72. {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)