cognite-toolkit 0.6.89__py3-none-any.whl → 0.6.91__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.

Potentially problematic release.


This version of cognite-toolkit might be problematic. Click here for more details.

Files changed (34) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +5 -0
  2. cognite_toolkit/_cdf_tk/client/api/infield.py +156 -0
  3. cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +17 -0
  4. cognite_toolkit/_cdf_tk/client/data_classes/base.py +63 -0
  5. cognite_toolkit/_cdf_tk/client/data_classes/infield.py +102 -0
  6. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +157 -0
  7. cognite_toolkit/_cdf_tk/client/testing.py +3 -0
  8. cognite_toolkit/_cdf_tk/commands/_utils.py +1 -24
  9. cognite_toolkit/_cdf_tk/commands/build_cmd.py +1 -1
  10. cognite_toolkit/_cdf_tk/commands/clean.py +11 -5
  11. cognite_toolkit/_cdf_tk/commands/deploy.py +14 -10
  12. cognite_toolkit/_cdf_tk/commands/pull.py +19 -13
  13. cognite_toolkit/_cdf_tk/cruds/__init__.py +3 -0
  14. cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +28 -25
  15. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +10 -7
  16. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +2 -1
  17. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +111 -2
  18. cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -20
  19. cognite_toolkit/_cdf_tk/data_classes/_build_variables.py +120 -14
  20. cognite_toolkit/_cdf_tk/data_classes/_built_resources.py +1 -1
  21. cognite_toolkit/_cdf_tk/protocols.py +97 -0
  22. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +2 -0
  23. cognite_toolkit/_cdf_tk/resource_classes/agent.py +1 -0
  24. cognite_toolkit/_cdf_tk/resource_classes/infield_cdmv1.py +94 -0
  25. cognite_toolkit/_cdf_tk/utils/text.py +23 -0
  26. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  27. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  28. cognite_toolkit/_resources/cdf.toml +1 -1
  29. cognite_toolkit/_version.py +1 -1
  30. {cognite_toolkit-0.6.89.dist-info → cognite_toolkit-0.6.91.dist-info}/METADATA +1 -1
  31. {cognite_toolkit-0.6.89.dist-info → cognite_toolkit-0.6.91.dist-info}/RECORD +34 -27
  32. {cognite_toolkit-0.6.89.dist-info → cognite_toolkit-0.6.91.dist-info}/WHEEL +0 -0
  33. {cognite_toolkit-0.6.89.dist-info → cognite_toolkit-0.6.91.dist-info}/entry_points.txt +0 -0
  34. {cognite_toolkit-0.6.89.dist-info → cognite_toolkit-0.6.91.dist-info}/licenses/LICENSE +0 -0
@@ -3,6 +3,8 @@ from typing import cast
3
3
  from cognite.client import CogniteClient
4
4
  from rich.console import Console
5
5
 
6
+ from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient
7
+
6
8
  from .api.canvas import CanvasAPI
7
9
  from .api.charts import ChartsAPI
8
10
  from .api.dml import DMLAPI
@@ -11,6 +13,7 @@ from .api.extended_files import ExtendedFileMetadataAPI
11
13
  from .api.extended_functions import ExtendedFunctionsAPI
12
14
  from .api.extended_raw import ExtendedRawAPI
13
15
  from .api.extended_timeseries import ExtendedTimeSeriesAPI
16
+ from .api.infield import InfieldAPI
14
17
  from .api.lookup import LookUpGroup
15
18
  from .api.migration import MigrationAPI
16
19
  from .api.project import ProjectAPI
@@ -24,6 +27,7 @@ from .config import ToolkitClientConfig
24
27
  class ToolkitClient(CogniteClient):
25
28
  def __init__(self, config: ToolkitClientConfig | None = None, enable_set_pending_ids: bool = False) -> None:
26
29
  super().__init__(config=config)
30
+ http_client = HTTPClient(self.config)
27
31
  toolkit_config = ToolkitClientConfig.from_client_config(self.config)
28
32
  self.console = Console()
29
33
  self.search = SearchAPI(self._config, self._API_VERSION, self)
@@ -42,6 +46,7 @@ class ToolkitClient(CogniteClient):
42
46
  self.token = TokenAPI(self)
43
47
  self.charts = ChartsAPI(self._config, self._API_VERSION, self)
44
48
  self.project = ProjectAPI(config=toolkit_config, cognite_client=self)
49
+ self.infield = InfieldAPI(http_client, self.console)
45
50
 
46
51
  @property
47
52
  def config(self) -> ToolkitClientConfig:
@@ -0,0 +1,156 @@
1
+ from collections.abc import Sequence
2
+ from typing import Any, cast
3
+
4
+ from rich.console import Console
5
+
6
+ from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse, QueryResponse
7
+ from cognite_toolkit._cdf_tk.client.data_classes.infield import DataExplorationConfig, InfieldLocationConfig
8
+ from cognite_toolkit._cdf_tk.client.data_classes.instance_api import (
9
+ InstanceResponseItem,
10
+ InstanceResult,
11
+ NodeIdentifier,
12
+ )
13
+ from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
14
+ from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, ItemsRequest, SimpleBodyRequest
15
+
16
+
17
+ class InfieldConfigAPI:
18
+ ENDPOINT = "/models/instances"
19
+ LOCATION_REF = "locationConfig"
20
+ EXPLORATION_REF = "explorerConfig"
21
+ # We know that this key exists and it has alias set.
22
+ DATA_EXPLORATION_PROP_ID = cast(str, InfieldLocationConfig.model_fields["data_exploration_config"].alias)
23
+
24
+ def __init__(self, http_client: HTTPClient, console: Console) -> None:
25
+ self._http_client = http_client
26
+ self._console = console
27
+ self._config = http_client.config
28
+
29
+ def apply(self, items: Sequence[InfieldLocationConfig]) -> list[InstanceResult]:
30
+ if len(items) > 500:
31
+ raise ValueError("Cannot apply more than 500 InfieldLocationConfig items at once.")
32
+
33
+ request_items = (
34
+ [item.as_request_item()]
35
+ if item.data_exploration_config is None
36
+ else [item.as_request_item(), item.data_exploration_config.as_request_item()]
37
+ for item in items
38
+ )
39
+ responses = self._http_client.request_with_retries(
40
+ ItemsRequest(
41
+ endpoint_url=self._config.create_api_url(self.ENDPOINT),
42
+ method="POST",
43
+ items=[item for sublist in request_items for item in sublist],
44
+ )
45
+ )
46
+ responses.raise_for_status()
47
+ return PagedResponse[InstanceResult].model_validate(responses.get_first_body()).items
48
+
49
+ def retrieve(self, items: Sequence[NodeIdentifier]) -> list[InfieldLocationConfig]:
50
+ if len(items) > 100:
51
+ raise ValueError("Cannot retrieve more than 100 InfieldLocationConfig items at once.")
52
+ if not items:
53
+ return []
54
+ responses = self._http_client.request_with_retries(
55
+ SimpleBodyRequest(
56
+ # We use the query endpoint to be able to retrieve linked DataExplorationConfig items
57
+ endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/query"),
58
+ method="POST",
59
+ body_content=self._retrieve_query(items),
60
+ )
61
+ )
62
+ responses.raise_for_status()
63
+ parsed_response = QueryResponse[InstanceResponseItem].model_validate(responses.get_first_body())
64
+ return self._parse_retrieve_response(parsed_response)
65
+
66
+ def delete(self, items: Sequence[InfieldLocationConfig]) -> list[NodeIdentifier]:
67
+ if len(items) > 500:
68
+ raise ValueError("Cannot delete more than 500 InfieldLocationConfig items at once.")
69
+
70
+ identifiers = (
71
+ [item.as_id()]
72
+ if item.data_exploration_config is None
73
+ else [item.as_id(), item.data_exploration_config.as_id()]
74
+ for item in items
75
+ )
76
+ responses = self._http_client.request_with_retries(
77
+ ItemsRequest(
78
+ endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
79
+ method="POST",
80
+ items=[identifier for sublist in identifiers for identifier in sublist],
81
+ )
82
+ )
83
+ responses.raise_for_status()
84
+ return PagedResponse[NodeIdentifier].model_validate(responses.get_first_body()).items
85
+
86
+ @classmethod
87
+ def _retrieve_query(cls, items: Sequence[NodeIdentifier]) -> dict[str, Any]:
88
+ return {
89
+ "with": {
90
+ cls.LOCATION_REF: {
91
+ "limit": len(items),
92
+ "nodes": {
93
+ "filter": {
94
+ "instanceReferences": [
95
+ {"space": item.space, "externalId": item.external_id} for item in items
96
+ ]
97
+ },
98
+ },
99
+ },
100
+ cls.EXPLORATION_REF: {
101
+ "nodes": {
102
+ "from": "locationConfig",
103
+ "direction": "outwards",
104
+ "through": {
105
+ "source": InfieldLocationConfig.VIEW_ID.dump(),
106
+ "identifier": cls.DATA_EXPLORATION_PROP_ID,
107
+ },
108
+ }
109
+ },
110
+ },
111
+ "select": {
112
+ cls.LOCATION_REF: {
113
+ "sources": [{"source": InfieldLocationConfig.VIEW_ID.dump(), "properties": ["*"]}],
114
+ },
115
+ cls.EXPLORATION_REF: {
116
+ "sources": [{"source": DataExplorationConfig.VIEW_ID.dump(), "properties": ["*"]}],
117
+ },
118
+ },
119
+ }
120
+
121
+ def _parse_retrieve_response(
122
+ self, parsed_response: QueryResponse[InstanceResponseItem]
123
+ ) -> list[InfieldLocationConfig]:
124
+ data_exploration_results = (
125
+ DataExplorationConfig.model_validate(
126
+ item.get_properties_for_source(DataExplorationConfig.VIEW_ID, include_identifier=True)
127
+ )
128
+ for item in parsed_response.items[self.EXPLORATION_REF]
129
+ )
130
+ data_exploration_config_map = {(dec.space, dec.external_id): dec for dec in data_exploration_results}
131
+ result: list[InfieldLocationConfig] = []
132
+ for item in parsed_response.items[self.LOCATION_REF]:
133
+ properties = item.get_properties_for_source(InfieldLocationConfig.VIEW_ID, include_identifier=True)
134
+ data_exploration = properties.pop(self.DATA_EXPLORATION_PROP_ID, None)
135
+ if isinstance(data_exploration, dict):
136
+ space = data_exploration["space"]
137
+ external_id = data_exploration["externalId"]
138
+ if (space, external_id) not in data_exploration_config_map:
139
+ HighSeverityWarning(
140
+ f"{self.DATA_EXPLORATION_PROP_ID} with space '{space}' and externalId '{external_id}' referenced in InfieldLocationConfig '{properties['externalId']}' was not found in the retrieved results."
141
+ ).print_warning(console=self._console)
142
+ else:
143
+ # Pydantic allow already validated models to be assigned to fields
144
+ properties[self.DATA_EXPLORATION_PROP_ID] = data_exploration_config_map[(space, external_id)] # type: ignore[assignment,index]
145
+ else:
146
+ HighSeverityWarning(
147
+ f"InfieldLocationConfig '{properties['externalId']}' is missing a valid {self.DATA_EXPLORATION_PROP_ID} reference."
148
+ ).print_warning(console=self._console)
149
+ result.append(InfieldLocationConfig.model_validate(properties))
150
+ return result
151
+
152
+
153
+ class InfieldAPI:
154
+ def __init__(self, http_client: HTTPClient, console: Console) -> None:
155
+ self._http_client = http_client
156
+ self.config = InfieldConfigAPI(http_client, console)
@@ -0,0 +1,17 @@
1
+ from typing import Generic, TypeVar
2
+
3
+ from pydantic import BaseModel, Field, JsonValue
4
+
5
+ T = TypeVar("T", bound=BaseModel)
6
+
7
+
8
+ class PagedResponse(BaseModel, Generic[T]):
9
+ items: list[T]
10
+ next_cursor: str | None = Field(None, alias="nextCursor")
11
+
12
+
13
+ class QueryResponse(BaseModel, Generic[T]):
14
+ items: dict[str, list[T]]
15
+ typing: dict[str, JsonValue] | None = None
16
+ next_cursor: dict[str, str] = Field(alias="nextCursor")
17
+ debug: dict[str, JsonValue] | None = None
@@ -0,0 +1,63 @@
1
+ import sys
2
+ from abc import ABC, abstractmethod
3
+ from typing import Any, Generic, TypeVar
4
+
5
+ from pydantic import BaseModel, ConfigDict
6
+ from pydantic.alias_generators import to_camel
7
+
8
+ if sys.version_info >= (3, 11):
9
+ from typing import Self
10
+ else:
11
+ from typing_extensions import Self
12
+
13
+
14
+ class BaseModelObject(BaseModel):
15
+ """Base class for all object. This includes resources and nested objects."""
16
+
17
+ # We allow extra fields to support forward compatibility.
18
+ model_config = ConfigDict(alias_generator=to_camel, extra="allow")
19
+
20
+ def dump(self, camel_case: 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)
26
+
27
+ @classmethod
28
+ def _load(cls, resource: dict[str, Any]) -> "Self":
29
+ """Load method to match CogniteResource signature."""
30
+ return cls.model_validate(resource)
31
+
32
+
33
+ class RequestResource(BaseModelObject): ...
34
+
35
+
36
+ T_RequestResource = TypeVar("T_RequestResource", bound=RequestResource)
37
+
38
+
39
+ class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
40
+ @abstractmethod
41
+ def as_request_resource(self) -> T_RequestResource:
42
+ """Convert the response resource to a request resource."""
43
+ ...
44
+
45
+
46
+ class Identifier(BaseModel):
47
+ """Base class for all identifier classes."""
48
+
49
+ model_config = ConfigDict(alias_generator=to_camel, extra="ignore", populate_by_name=True, frozen=True)
50
+
51
+ def dump(self, include_type: bool = True) -> dict[str, Any]:
52
+ """Dump the identifier to a dictionary.
53
+
54
+ Args:
55
+ include_type (bool): Whether to include the type of the identifier in the output.
56
+
57
+ Returns:
58
+ dict[str, Any]: The dumped identifier.
59
+ """
60
+ return self.model_dump(mode="json", by_alias=True, exclude_defaults=not include_type)
61
+
62
+ def as_id(self) -> Self:
63
+ return self
@@ -0,0 +1,102 @@
1
+ import sys
2
+ from collections import UserList
3
+ from typing import Any, ClassVar, Literal
4
+
5
+ from cognite.client import CogniteClient
6
+ from pydantic import JsonValue, field_validator
7
+ from pydantic_core.core_schema import ValidationInfo
8
+
9
+ from cognite_toolkit._cdf_tk.protocols import ResourceRequestListProtocol, ResourceResponseListProtocol
10
+ from cognite_toolkit._cdf_tk.utils.text import sanitize_instance_external_id
11
+
12
+ from .base import ResponseResource
13
+ from .instance_api import InstanceRequestResource, ViewReference
14
+
15
+ if sys.version_info >= (3, 11):
16
+ from typing import Self
17
+ else:
18
+ from typing_extensions import Self
19
+
20
+ INFIELD_LOCATION_CONFIG_VIEW_ID = ViewReference(space="cdf_infield", external_id="InFieldLocationConfig", version="v1")
21
+ DATA_EXPLORATION_CONFIG_VIEW_ID = ViewReference(space="cdf_infield", external_id="DataExplorationConfig", version="v1")
22
+
23
+
24
+ class DataExplorationConfig(InstanceRequestResource):
25
+ """Data Exploration Configuration resource class."""
26
+
27
+ VIEW_ID: ClassVar[ViewReference] = DATA_EXPLORATION_CONFIG_VIEW_ID
28
+ instance_type: Literal["node"] = "node"
29
+
30
+ observations: dict[str, JsonValue] | None = None
31
+ activities: dict[str, JsonValue] | None = None
32
+ documents: dict[str, JsonValue] | None = None
33
+ notifications: dict[str, JsonValue] | None = None
34
+ assets: dict[str, JsonValue] | None = None
35
+
36
+
37
+ class InfieldLocationConfig(
38
+ ResponseResource["InfieldLocationConfig"],
39
+ InstanceRequestResource,
40
+ ):
41
+ """Infield Location Configuration resource class.
42
+
43
+ This class is used for both the response and request resource for Infield Location Configuration nodes.
44
+ """
45
+
46
+ VIEW_ID: ClassVar[ViewReference] = INFIELD_LOCATION_CONFIG_VIEW_ID
47
+ instance_type: Literal["node"] = "node"
48
+
49
+ root_location_external_id: str | None = None
50
+ feature_toggles: dict[str, JsonValue] | None = None
51
+ app_instance_space: str | None = None
52
+ access_management: dict[str, JsonValue] | None = None
53
+ data_filters: dict[str, JsonValue] | None = None
54
+ data_exploration_config: DataExplorationConfig | None = None
55
+
56
+ def as_request_resource(self) -> "InfieldLocationConfig":
57
+ return self
58
+
59
+ def as_write(self) -> Self:
60
+ return self
61
+
62
+ @field_validator("data_exploration_config", mode="before")
63
+ @classmethod
64
+ def generate_identifier_if_missing(cls, value: Any, info: ValidationInfo) -> Any:
65
+ """We do not require the user to specify the space and externalId for the data exploration config."""
66
+ if isinstance(value, dict):
67
+ if value.get("space") is None:
68
+ value["space"] = info.data["space"]
69
+ if value.get("externalId") is None:
70
+ external_id = info.data["external_id"]
71
+ candidate = f"{external_id}_data_exploration_config"
72
+ value["externalId"] = sanitize_instance_external_id(candidate)
73
+ return value
74
+
75
+
76
+ class InfieldLocationConfigList(
77
+ UserList[InfieldLocationConfig],
78
+ ResourceResponseListProtocol,
79
+ ResourceRequestListProtocol,
80
+ ):
81
+ """A list of InfieldLocationConfig objects."""
82
+
83
+ _RESOURCE = InfieldLocationConfig
84
+ data: list[InfieldLocationConfig]
85
+
86
+ def __init__(self, initlist: list[InfieldLocationConfig] | None = None, **_: Any) -> None:
87
+ super().__init__(initlist or [])
88
+
89
+ def dump(self, camel_case: bool = True) -> list[dict[str, Any]]:
90
+ """Serialize the list of InfieldLocationConfig objects to a list of dictionaries."""
91
+ return [item.dump(camel_case) for item in self.data]
92
+
93
+ @classmethod
94
+ def load(
95
+ cls, data: list[dict[str, Any]], cognite_client: CogniteClient | None = None
96
+ ) -> "InfieldLocationConfigList":
97
+ """Deserialize a list of dictionaries to an InfieldLocationConfigList."""
98
+ items = [InfieldLocationConfig.model_validate(item) for item in data]
99
+ return cls(items)
100
+
101
+ def as_write(self) -> Self:
102
+ return self
@@ -0,0 +1,157 @@
1
+ from typing import Any, ClassVar, Literal, TypeAlias
2
+
3
+ from pydantic import ConfigDict, JsonValue, model_serializer
4
+
5
+ from .base import BaseModelObject, Identifier, RequestResource
6
+
7
+ InstanceType: TypeAlias = Literal["node", "edge"]
8
+
9
+
10
+ class InstanceIdentifier(Identifier):
11
+ """Identifier for an Instance instance."""
12
+
13
+ instance_type: InstanceType
14
+ space: str
15
+ external_id: str
16
+
17
+
18
+ class NodeIdentifier(InstanceIdentifier):
19
+ instance_type: Literal["node"] = "node"
20
+
21
+
22
+ class EdgeIdentifier(InstanceIdentifier):
23
+ instance_type: Literal["edge"] = "edge"
24
+
25
+
26
+ class InstanceResult(BaseModelObject):
27
+ instance_type: InstanceType
28
+ version: int
29
+ was_modified: bool
30
+ space: str
31
+ external_id: str
32
+ created_time: int
33
+ last_updated_time: int
34
+
35
+ def as_id(self) -> InstanceIdentifier:
36
+ return InstanceIdentifier(
37
+ instance_type=self.instance_type,
38
+ space=self.space,
39
+ external_id=self.external_id,
40
+ )
41
+
42
+
43
+ class ViewReference(Identifier):
44
+ type: Literal["view"] = "view"
45
+ space: str
46
+ external_id: str
47
+ version: str
48
+
49
+
50
+ ######################################################
51
+ # The classes below are helper classes for making instances request/responses.
52
+ # By using these, we can avoid having to include the instances specific classes in the DTO classes
53
+ # that are instance. Instead, these classes can now only have the properties they need to define.
54
+ #######################################################
55
+
56
+
57
+ class InstanceRequestResource(RequestResource):
58
+ """This is a base class for resources that are Instances."""
59
+
60
+ VIEW_ID: ClassVar[ViewReference]
61
+ instance_type: InstanceType
62
+ space: str
63
+ external_id: str
64
+
65
+ def as_id(self) -> InstanceIdentifier:
66
+ return InstanceIdentifier(
67
+ instance_type=self.instance_type,
68
+ space=self.space,
69
+ external_id=self.external_id,
70
+ )
71
+
72
+ def as_request_item(self) -> "InstanceRequestItem":
73
+ return InstanceRequestItem(
74
+ instance_type=self.instance_type,
75
+ space=self.space,
76
+ external_id=self.external_id,
77
+ sources=[InstanceSource(source=self.VIEW_ID, resource=self)],
78
+ )
79
+
80
+
81
+ class InstanceSource(BaseModelObject):
82
+ source: ViewReference
83
+ resource: InstanceRequestResource
84
+
85
+ @model_serializer(mode="plain")
86
+ def serialize_resource(self) -> dict[str, Any]:
87
+ properties: dict[str, JsonValue] = {}
88
+ for field_id, field in type(self.resource).model_fields.items():
89
+ if field_id in InstanceRequestResource.model_fields:
90
+ # Skip space, external_id, instance_type
91
+ continue
92
+ key = field.alias or field_id
93
+ properties[key] = self._serialize_property(getattr(self.resource, field_id))
94
+
95
+ return {
96
+ "source": self.source.model_dump(by_alias=True),
97
+ "properties": properties,
98
+ }
99
+
100
+ @classmethod
101
+ def _serialize_property(cls, value: Any) -> JsonValue:
102
+ """Handles serialization of direct relations."""
103
+ if isinstance(value, InstanceRequestResource):
104
+ return {"space": value.space, "externalId": value.external_id}
105
+ elif isinstance(value, list):
106
+ return [cls._serialize_property(v) for v in value]
107
+ return value
108
+
109
+
110
+ class InstanceRequestItem(BaseModelObject):
111
+ model_config = ConfigDict(populate_by_name=True)
112
+ instance_type: InstanceType
113
+ space: str
114
+ external_id: str
115
+ existing_version: int | None = None
116
+ sources: list[InstanceSource] | None = None
117
+
118
+ def as_id(self) -> InstanceIdentifier:
119
+ return InstanceIdentifier(
120
+ instance_type=self.instance_type,
121
+ space=self.space,
122
+ external_id=self.external_id,
123
+ )
124
+
125
+
126
+ class InstanceResponseItem(BaseModelObject):
127
+ instance_type: InstanceType
128
+ space: str
129
+ external_id: str
130
+ version: int
131
+ type: InstanceIdentifier | None = None
132
+ created_time: int
133
+ last_updated_time: int
134
+ deleted_time: int | None = None
135
+ properties: dict[str, dict[str, dict[str, JsonValue]]] | None = None
136
+
137
+ def get_properties_for_source(
138
+ self, source: ViewReference, include_identifier: bool = False
139
+ ) -> dict[str, JsonValue]:
140
+ output: dict[str, JsonValue] = (
141
+ {"space": self.space, "externalId": self.external_id} if include_identifier else {}
142
+ )
143
+ if not self.properties:
144
+ return output
145
+ if source.space not in self.properties:
146
+ return output
147
+ space_properties = self.properties[source.space]
148
+ view_version = f"{source.external_id}/{source.version}"
149
+ output.update(space_properties.get(view_version, {}))
150
+ return output
151
+
152
+ def as_id(self) -> InstanceIdentifier:
153
+ return InstanceIdentifier(
154
+ instance_type=self.instance_type,
155
+ space=self.space,
156
+ external_id=self.external_id,
157
+ )
@@ -21,6 +21,7 @@ from .api.extended_files import ExtendedFileMetadataAPI
21
21
  from .api.extended_functions import ExtendedFunctionsAPI
22
22
  from .api.extended_raw import ExtendedRawAPI
23
23
  from .api.extended_timeseries import ExtendedTimeSeriesAPI
24
+ from .api.infield import InfieldAPI, InfieldConfigAPI
24
25
  from .api.location_filters import LocationFiltersAPI
25
26
  from .api.lookup import (
26
27
  AssetLookUpAPI,
@@ -73,6 +74,8 @@ class ToolkitClientMock(CogniteClientMock):
73
74
  self.functions = MagicMock(spec=ExtendedFunctionsAPI)
74
75
  self.functions.calls = MagicMock(spec_set=FunctionCallsAPI)
75
76
  self.functions.schedules = MagicMock(spec_set=FunctionSchedulesAPI)
77
+ self.infield = MagicMock(spec=InfieldAPI)
78
+ self.infield.config = MagicMock(spec_set=InfieldConfigAPI)
76
79
 
77
80
  self.project = MagicMock(spec_set=ProjectAPI)
78
81
 
@@ -1,10 +1,6 @@
1
- from cognite.client.data_classes._base import T_CogniteResourceList, T_WritableCogniteResource, T_WriteClass
2
1
  from cognite.client.utils.useful_types import SequenceNotStr
3
2
 
4
- from cognite_toolkit._cdf_tk.cruds import (
5
- ResourceCRUD,
6
- )
7
- from cognite_toolkit._cdf_tk.cruds._base_cruds import T_ID, T_WritableCogniteResourceList
3
+ from cognite_toolkit._cdf_tk.cruds._base_cruds import T_ID
8
4
 
9
5
 
10
6
  def _print_ids_or_length(resource_ids: SequenceNotStr[T_ID], limit: int = 10) -> str:
@@ -14,22 +10,3 @@ def _print_ids_or_length(resource_ids: SequenceNotStr[T_ID], limit: int = 10) ->
14
10
  return f"{resource_ids}"
15
11
  else:
16
12
  return f"{len(resource_ids)} items"
17
-
18
-
19
- def _remove_duplicates(
20
- loaded_resources: T_CogniteResourceList,
21
- loader: ResourceCRUD[
22
- T_ID, T_WriteClass, T_WritableCogniteResource, T_CogniteResourceList, T_WritableCogniteResourceList
23
- ],
24
- ) -> tuple[T_CogniteResourceList, list[T_ID]]:
25
- seen: set[T_ID] = set()
26
- output = loader.list_write_cls([])
27
- duplicates: list[T_ID] = []
28
- for item in loaded_resources:
29
- identifier = loader.get_id(item)
30
- if identifier not in seen:
31
- output.append(item)
32
- seen.add(identifier)
33
- else:
34
- duplicates.append(identifier)
35
- return output, duplicates
@@ -492,7 +492,7 @@ class BuildCommand(ToolkitCommand):
492
492
  # which is what we do in the deploy step to verify that the source file has not changed.
493
493
  source = SourceLocationEager(source_path, calculate_hash(source_path, shorten=True))
494
494
 
495
- content = variables.replace(content, source_path.suffix)
495
+ content = variables.replace(content, source_path)
496
496
 
497
497
  replace_warnings = self._check_variables_replaced(content, module_dir, source_path)
498
498
 
@@ -2,7 +2,6 @@ import traceback
2
2
  from graphlib import TopologicalSorter
3
3
  from pathlib import Path
4
4
 
5
- from cognite.client.data_classes._base import T_CogniteResourceList, T_WritableCogniteResource, T_WriteClass
6
5
  from cognite.client.exceptions import CogniteAPIError, CogniteNotFoundError
7
6
  from cognite.client.utils.useful_types import SequenceNotStr
8
7
  from rich import print
@@ -24,7 +23,14 @@ from cognite_toolkit._cdf_tk.cruds import (
24
23
  ResourceCRUD,
25
24
  ResourceWorker,
26
25
  )
27
- from cognite_toolkit._cdf_tk.cruds._base_cruds import T_ID, Loader, T_WritableCogniteResourceList
26
+ from cognite_toolkit._cdf_tk.cruds._base_cruds import (
27
+ T_ID,
28
+ Loader,
29
+ T_ResourceRequest,
30
+ T_ResourceRequestList,
31
+ T_ResourceResponse,
32
+ T_ResourceResponseList,
33
+ )
28
34
  from cognite_toolkit._cdf_tk.data_classes import (
29
35
  BuildEnvironment,
30
36
  DeployResults,
@@ -58,7 +64,7 @@ class CleanCommand(ToolkitCommand):
58
64
  def clean_resources(
59
65
  self,
60
66
  loader: ResourceCRUD[
61
- T_ID, T_WriteClass, T_WritableCogniteResource, T_CogniteResourceList, T_WritableCogniteResourceList
67
+ T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList
62
68
  ],
63
69
  env_vars: EnvironmentVariables,
64
70
  read_modules: list[ReadModule],
@@ -130,7 +136,7 @@ class CleanCommand(ToolkitCommand):
130
136
  return ResourceDeployResult(name=loader.display_name)
131
137
 
132
138
  def _delete_resources(
133
- self, loaded_resources: T_CogniteResourceList, loader: ResourceCRUD, dry_run: bool, verbose: bool
139
+ self, loaded_resources: T_ResourceResponseList, loader: ResourceCRUD, dry_run: bool, verbose: bool
134
140
  ) -> int:
135
141
  nr_of_deleted = 0
136
142
  resource_ids = loader.get_ids(loaded_resources)
@@ -155,7 +161,7 @@ class CleanCommand(ToolkitCommand):
155
161
  return nr_of_deleted
156
162
 
157
163
  def _drop_data(
158
- self, loaded_resources: T_CogniteResourceList, loader: ResourceContainerCRUD, dry_run: bool, verbose: bool
164
+ self, loaded_resources: T_ResourceResponseList, loader: ResourceContainerCRUD, dry_run: bool, verbose: bool
159
165
  ) -> int:
160
166
  nr_of_dropped = 0
161
167
  resource_ids = loader.get_ids(loaded_resources)