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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +7 -1
  2. cognite_toolkit/_cdf_tk/client/api/assets.py +118 -0
  3. cognite_toolkit/_cdf_tk/client/api/events.py +97 -0
  4. cognite_toolkit/_cdf_tk/client/api/infield.py +3 -3
  5. cognite_toolkit/_cdf_tk/client/api/legacy/extended_functions.py +1 -1
  6. cognite_toolkit/_cdf_tk/client/api/project.py +1 -1
  7. cognite_toolkit/_cdf_tk/client/api/streams.py +2 -2
  8. cognite_toolkit/_cdf_tk/client/api/three_d.py +5 -4
  9. cognite_toolkit/_cdf_tk/client/api/timeseries.py +105 -0
  10. cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +9 -0
  11. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +220 -0
  12. cognite_toolkit/_cdf_tk/client/{data_classes/api_classes.py → cdf_client/responses.py} +10 -13
  13. cognite_toolkit/_cdf_tk/client/data_classes/agent.py +127 -0
  14. cognite_toolkit/_cdf_tk/client/data_classes/asset.py +54 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/base.py +117 -22
  16. cognite_toolkit/_cdf_tk/client/data_classes/event.py +43 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +56 -0
  18. cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +44 -0
  19. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +34 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/raw.py +43 -0
  21. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +3 -2
  22. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +29 -34
  23. cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +55 -0
  24. cognite_toolkit/_cdf_tk/{utils → client}/http_client/__init__.py +4 -4
  25. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_client.py +10 -10
  26. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes.py +2 -2
  27. cognite_toolkit/_cdf_tk/{utils → client}/http_client/_data_classes2.py +10 -33
  28. cognite_toolkit/_cdf_tk/client/testing.py +6 -0
  29. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +1 -1
  30. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +6 -7
  31. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +6 -5
  32. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +6 -4
  33. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +10 -10
  34. cognite_toolkit/_cdf_tk/commands/_purge.py +7 -7
  35. cognite_toolkit/_cdf_tk/commands/_upload.py +1 -1
  36. cognite_toolkit/_cdf_tk/commands/pull.py +97 -2
  37. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +43 -47
  38. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +11 -4
  39. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +2 -1
  40. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -2
  41. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +2 -1
  42. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +1 -1
  43. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +2 -1
  44. cognite_toolkit/_cdf_tk/storageio/_applications.py +1 -1
  45. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +32 -29
  46. cognite_toolkit/_cdf_tk/storageio/_base.py +1 -1
  47. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +7 -7
  48. cognite_toolkit/_cdf_tk/storageio/_file_content.py +7 -7
  49. cognite_toolkit/_cdf_tk/storageio/_raw.py +1 -1
  50. cognite_toolkit/_cdf_tk/utils/useful_types.py +4 -7
  51. cognite_toolkit/_cdf_tk/utils/useful_types2.py +12 -0
  52. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  53. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  54. cognite_toolkit/_resources/cdf.toml +1 -1
  55. cognite_toolkit/_version.py +1 -1
  56. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/METADATA +1 -1
  57. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/RECORD +61 -48
  58. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/WHEEL +1 -1
  59. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_exception.py +0 -0
  60. /cognite_toolkit/_cdf_tk/{utils → client}/http_client/_tracker.py +0 -0
  61. {cognite_toolkit-0.7.39.dist-info → cognite_toolkit-0.7.41.dist-info}/entry_points.txt +0 -0
@@ -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 TYPE_CHECKING, Any, Generic, TypeVar
5
+ from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, get_args, get_origin
5
6
 
6
- from pydantic import ConfigDict
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
- T_RequestResource = TypeVar("T_RequestResource", bound=RequestResource)
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
- class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
24
- @abstractmethod
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
- def as_write(self) -> T_RequestResource:
30
- """Alias for as_request_resource to match protocol signature."""
31
- return self.as_request_resource()
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(RequestResource, ABC):
35
- """Base class for all identifier classes."""
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, include_type: bool = True) -> dict[str, Any]:
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=not include_type)
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) -> str:
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):