cognite-toolkit 0.7.67__py3-none-any.whl → 0.7.68__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 (29) hide show
  1. cognite_toolkit/_cdf_tk/client/api/instances.py +7 -3
  2. cognite_toolkit/_cdf_tk/client/api/security_categories.py +5 -5
  3. cognite_toolkit/_cdf_tk/client/api/transformations.py +4 -21
  4. cognite_toolkit/_cdf_tk/client/api/workflow_versions.py +2 -2
  5. cognite_toolkit/_cdf_tk/client/api/workflows.py +2 -2
  6. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_instance.py +19 -1
  7. cognite_toolkit/_cdf_tk/client/resource_classes/extraction_pipeline.py +7 -1
  8. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_job.py +19 -5
  9. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_mapping.py +2 -1
  10. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_auth.py +1 -1
  11. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_base.py +2 -0
  12. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_eventhub.py +3 -2
  13. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_kafka.py +2 -1
  14. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_mqtt.py +2 -1
  15. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_rest.py +2 -1
  16. cognite_toolkit/_cdf_tk/client/resource_classes/identifiers.py +23 -2
  17. cognite_toolkit/_cdf_tk/client/resource_classes/securitycategory.py +13 -4
  18. cognite_toolkit/_cdf_tk/client/resource_classes/sequence.py +44 -2
  19. cognite_toolkit/_cdf_tk/client/resource_classes/transformation.py +4 -3
  20. cognite_toolkit/_cdf_tk/client/testing.py +10 -0
  21. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +160 -125
  22. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  23. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  24. cognite_toolkit/_resources/cdf.toml +1 -1
  25. cognite_toolkit/_version.py +1 -1
  26. {cognite_toolkit-0.7.67.dist-info → cognite_toolkit-0.7.68.dist-info}/METADATA +1 -1
  27. {cognite_toolkit-0.7.67.dist-info → cognite_toolkit-0.7.68.dist-info}/RECORD +29 -29
  28. {cognite_toolkit-0.7.67.dist-info → cognite_toolkit-0.7.68.dist-info}/WHEEL +0 -0
  29. {cognite_toolkit-0.7.67.dist-info → cognite_toolkit-0.7.68.dist-info}/entry_points.txt +0 -0
@@ -10,6 +10,7 @@ from cognite_toolkit._cdf_tk.client.resource_classes.data_modeling import (
10
10
  InstanceResponse,
11
11
  ViewReference,
12
12
  )
13
+ from cognite_toolkit._cdf_tk.client.resource_classes.data_modeling._instance import InstanceSlimDefinition
13
14
  from cognite_toolkit._cdf_tk.client.resource_classes.instance_api import TypedInstanceIdentifier
14
15
 
15
16
 
@@ -33,15 +34,18 @@ class InstancesAPI(CDFResourceAPI[TypedInstanceIdentifier, InstanceRequest, Inst
33
34
  def _validate_response(self, response: SuccessResponse) -> ResponseItems[TypedInstanceIdentifier]:
34
35
  return ResponseItems[TypedInstanceIdentifier].model_validate_json(response.body)
35
36
 
36
- def create(self, items: Sequence[InstanceRequest]) -> list[InstanceResponse]:
37
+ def create(self, items: Sequence[InstanceRequest]) -> list[InstanceSlimDefinition]:
37
38
  """Create instances in CDF.
38
39
 
39
40
  Args:
40
41
  items: List of InstanceRequest objects to create.
41
42
  Returns:
42
- List of created InstanceResponse objects.
43
+ List of created InstanceSlimDefinition objects.
43
44
  """
44
- return self._request_item_response(items, "upsert")
45
+ response_items: list[InstanceSlimDefinition] = []
46
+ for response in self._chunk_requests(items, "upsert", self._serialize_items):
47
+ response_items.extend(PagedResponse[InstanceSlimDefinition].model_validate_json(response.body).items)
48
+ return response_items
45
49
 
46
50
  def retrieve(
47
51
  self, items: Sequence[TypedInstanceIdentifier], source: ViewReference | None = None
@@ -4,14 +4,14 @@ from typing import Literal
4
4
  from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse, ResponseItems
5
5
  from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
6
6
  from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, ItemsSuccessResponse, SuccessResponse
7
- from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import InternalId
7
+ from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import InternalIdUnwrapped
8
8
  from cognite_toolkit._cdf_tk.client.resource_classes.securitycategory import (
9
9
  SecurityCategoryRequest,
10
10
  SecurityCategoryResponse,
11
11
  )
12
12
 
13
13
 
14
- class SecurityCategoriesAPI(CDFResourceAPI[InternalId, SecurityCategoryRequest, SecurityCategoryResponse]):
14
+ class SecurityCategoriesAPI(CDFResourceAPI[InternalIdUnwrapped, SecurityCategoryRequest, SecurityCategoryResponse]):
15
15
  def __init__(self, http_client: HTTPClient) -> None:
16
16
  super().__init__(
17
17
  http_client=http_client,
@@ -27,8 +27,8 @@ class SecurityCategoriesAPI(CDFResourceAPI[InternalId, SecurityCategoryRequest,
27
27
  ) -> PagedResponse[SecurityCategoryResponse]:
28
28
  return PagedResponse[SecurityCategoryResponse].model_validate_json(response.body)
29
29
 
30
- def _reference_response(self, response: SuccessResponse) -> ResponseItems[InternalId]:
31
- return ResponseItems[InternalId].model_validate_json(response.body)
30
+ def _reference_response(self, response: SuccessResponse) -> ResponseItems[InternalIdUnwrapped]:
31
+ return ResponseItems[InternalIdUnwrapped].model_validate_json(response.body)
32
32
 
33
33
  def create(self, items: Sequence[SecurityCategoryRequest]) -> list[SecurityCategoryResponse]:
34
34
  """Create security categories in CDF.
@@ -40,7 +40,7 @@ class SecurityCategoriesAPI(CDFResourceAPI[InternalId, SecurityCategoryRequest,
40
40
  """
41
41
  return self._request_item_response(items, "create")
42
42
 
43
- def delete(self, items: Sequence[InternalId]) -> None:
43
+ def delete(self, items: Sequence[InternalIdUnwrapped]) -> None:
44
44
  """Delete security categories from CDF.
45
45
 
46
46
  Args:
@@ -1,10 +1,9 @@
1
1
  from collections.abc import Iterable, Sequence
2
- from typing import Any, Literal
2
+ from typing import Literal
3
3
 
4
4
  from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse, ResponseItems
5
5
  from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
6
6
  from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, ItemsSuccessResponse, SuccessResponse
7
- from cognite_toolkit._cdf_tk.client.request_classes.filters import ClassicFilter
8
7
  from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import InternalOrExternalId
9
8
  from cognite_toolkit._cdf_tk.client.resource_classes.transformation import TransformationRequest, TransformationResponse
10
9
 
@@ -83,55 +82,39 @@ class TransformationsAPI(CDFResourceAPI[InternalOrExternalId, TransformationRequ
83
82
 
84
83
  def paginate(
85
84
  self,
86
- filter: ClassicFilter | None = None,
87
- is_public: bool | None = None,
88
85
  limit: int = 100,
89
86
  cursor: str | None = None,
90
87
  ) -> PagedResponse[TransformationResponse]:
91
88
  """Iterate over all transformations in CDF.
92
89
 
93
90
  Args:
94
- filter: Filter by data set IDs.
95
- is_public: Filter by public status.
96
91
  limit: Maximum number of items to return.
97
92
  cursor: Cursor for pagination.
98
93
 
99
94
  Returns:
100
95
  PagedResponse of TransformationResponse objects.
101
96
  """
102
- filter_: dict[str, Any] = filter.dump() if filter else {}
103
- if is_public is not None:
104
- filter_["isPublic"] = is_public
105
-
106
97
  return self._paginate(
107
98
  cursor=cursor,
108
99
  limit=limit,
109
- body={"filter": filter_ or None},
100
+ body={"filter": {}},
110
101
  )
111
102
 
112
103
  def iterate(
113
104
  self,
114
- filter: ClassicFilter | None = None,
115
- is_public: bool | None = None,
116
105
  limit: int = 100,
117
106
  ) -> Iterable[list[TransformationResponse]]:
118
107
  """Iterate over all transformations in CDF.
119
108
 
120
109
  Args:
121
- filter: Filter by data set IDs.
122
- is_public: Filter by public status.
123
110
  limit: Maximum number of items to return per page.
124
111
 
125
112
  Returns:
126
113
  Iterable of lists of TransformationResponse objects.
127
114
  """
128
- filter_: dict[str, Any] = filter.dump() if filter else {}
129
- if is_public is not None:
130
- filter_["isPublic"] = is_public
131
-
132
115
  return self._iterate(
133
116
  limit=limit,
134
- body={"filter": filter_ or None},
117
+ body={"filter": {}},
135
118
  )
136
119
 
137
120
  def list(
@@ -143,4 +126,4 @@ class TransformationsAPI(CDFResourceAPI[InternalOrExternalId, TransformationRequ
143
126
  Returns:
144
127
  List of TransformationResponse objects.
145
128
  """
146
- return self._list(limit=limit)
129
+ return self._list(limit=limit, body={"filter": {}})
@@ -65,8 +65,8 @@ class WorkflowVersionsAPI(CDFResourceAPI[WorkflowVersionId, WorkflowVersionReque
65
65
  result: list[WorkflowVersionResponse] = []
66
66
  for item in items:
67
67
  endpoint = f"/workflows/{item.workflow_external_id}/versions/{item.version}"
68
- retrieved = self._request_item_response([item], "retrieve", endpoint=endpoint)
69
- result.extend(retrieved)
68
+ for response in self._chunk_requests([item], "retrieve", self._serialize_items, endpoint_path=endpoint):
69
+ result.append(WorkflowVersionResponse.model_validate_json(response.body))
70
70
  return result
71
71
 
72
72
  def delete(self, items: Sequence[WorkflowVersionId]) -> None:
@@ -65,8 +65,8 @@ class WorkflowsAPI(CDFResourceAPI[ExternalId, WorkflowRequest, WorkflowResponse]
65
65
  result: list[WorkflowResponse] = []
66
66
  for item in items:
67
67
  endpoint = f"/workflows/{item.external_id}"
68
- retrieved = self._request_item_response([item], "retrieve", endpoint=endpoint)
69
- result.extend(retrieved)
68
+ for response in self._chunk_requests([item], "retrieve", self._serialize_items, endpoint_path=endpoint):
69
+ result.append(WorkflowResponse.model_validate_json(response.body))
70
70
  return result
71
71
 
72
72
  def delete(self, items: Sequence[ExternalId]) -> None:
@@ -29,7 +29,7 @@ class InstanceSource(BaseModelObject):
29
29
 
30
30
  class InstanceRequestDefinition(InstanceDefinition, RequestResource, ABC):
31
31
  existing_version: int | None = None
32
- sources: list[InstanceSource]
32
+ sources: list[InstanceSource] | None = None
33
33
 
34
34
 
35
35
  class InstanceResponseDefinition(InstanceDefinition, ResponseResource, Generic[T_RequestResource], ABC):
@@ -144,6 +144,24 @@ class EdgeResponse(InstanceResponseDefinition[EdgeRequest]):
144
144
  return EdgeRequest.model_validate(dumped, extra="ignore")
145
145
 
146
146
 
147
+ class InstanceSlimDefinition(BaseModelObject):
148
+ """Slim version of instance definition for listing instances."""
149
+
150
+ instance_type: Literal["node", "edge"]
151
+ version: int
152
+ was_modified: bool
153
+ space: str
154
+ external_id: str
155
+ created_time: int
156
+ last_updated_time: int
157
+
158
+ def as_id(self) -> TypedNodeIdentifier | TypedEdgeIdentifier:
159
+ if self.instance_type == "node":
160
+ return TypedNodeIdentifier(space=self.space, external_id=self.external_id)
161
+ else:
162
+ return TypedEdgeIdentifier(space=self.space, external_id=self.external_id)
163
+
164
+
147
165
  InstanceRequest: TypeAlias = Annotated[
148
166
  NodeRequest | EdgeRequest,
149
167
  Field(discriminator="instance_type"),
@@ -1,4 +1,4 @@
1
- from typing import ClassVar
1
+ from typing import Any, ClassVar, Literal
2
2
 
3
3
  from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
@@ -49,6 +49,12 @@ class ExtractionPipelineRequest(ExtractionPipeline, UpdatableRequestResource):
49
49
  {"documentation", "source", "notification_config", "schedule", "description"}
50
50
  )
51
51
 
52
+ def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
53
+ update = super().as_update(mode)
54
+ # createdBy cannot be updated.
55
+ update["update"].pop("createdBy", None)
56
+ return update
57
+
52
58
 
53
59
  class ExtractionPipelineResponse(ExtractionPipeline, ResponseResource[ExtractionPipelineRequest]):
54
60
  id: int
@@ -1,6 +1,6 @@
1
- from typing import Annotated, Literal
1
+ from typing import Annotated, Any, Literal
2
2
 
3
- from pydantic import Field, JsonValue
3
+ from pydantic import Field, JsonValue, field_validator
4
4
 
5
5
  from cognite_toolkit._cdf_tk.client._resource_base import (
6
6
  BaseModelObject,
@@ -116,13 +116,27 @@ class HostedExtractorJob(BaseModelObject):
116
116
  destination_id: str
117
117
  source_id: str
118
118
  format: JobFormat
119
- config: MQTTConfig | KafkaConfig | RestConfig
119
+ config: MQTTConfig | KafkaConfig | RestConfig | None = None
120
120
 
121
121
  def as_id(self) -> ExternalId:
122
122
  return ExternalId(external_id=self.external_id)
123
123
 
124
-
125
- class HostedExtractorJobRequest(HostedExtractorJob, UpdatableRequestResource): ...
124
+ @field_validator("config", mode="before")
125
+ @classmethod
126
+ def empty_dict_as_none(cls, value: Any) -> Any:
127
+ if value == {}:
128
+ return None
129
+ return value
130
+
131
+
132
+ class HostedExtractorJobRequest(HostedExtractorJob, UpdatableRequestResource):
133
+ def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
134
+ update_item = super().as_update(mode=mode)
135
+ exclude_unset = mode == "patch"
136
+ update_item["update"]["format"] = {
137
+ "set": self.format.model_dump(mode="json", exclude_none=True, by_alias=True, exclude_unset=exclude_unset)
138
+ }
139
+ return update_item
126
140
 
127
141
 
128
142
  class HostedExtractorJobResponse(HostedExtractorJob, ResponseResource[HostedExtractorJobRequest]):
@@ -1,4 +1,4 @@
1
- from typing import Annotated, Literal
1
+ from typing import Annotated, ClassVar, Literal
2
2
 
3
3
  from pydantic import Field
4
4
 
@@ -60,6 +60,7 @@ class HostedExtractorMapping(BaseModelObject):
60
60
 
61
61
 
62
62
  class HostedExtractorMappingRequest(HostedExtractorMapping, UpdatableRequestResource):
63
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"input"})
63
64
  input: MappingInput | None = None
64
65
 
65
66
 
@@ -26,7 +26,7 @@ class BasicAuthenticationResponse(BasicAuthentication, AuthenticationResponseDef
26
26
 
27
27
 
28
28
  class ClientCredentialAuthentication(BaseModelObject):
29
- type: Literal["clientCredential"] = "clientCredential"
29
+ type: Literal["clientCredentials"] = "clientCredentials"
30
30
  client_id: str
31
31
  token_url: str
32
32
  scopes: str
@@ -16,6 +16,8 @@ class SourceRequestDefinition(UpdatableRequestResource):
16
16
 
17
17
  def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
18
18
  output = super().as_update(mode)
19
+ # Move type from update to top-level, as the API expects it there
20
+ output["update"].pop("type")
19
21
  output["type"] = self.type
20
22
  return output
21
23
 
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import ClassVar, Literal
2
2
 
3
3
  from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
@@ -10,13 +10,14 @@ from ._base import SourceRequestDefinition, SourceResponseDefinition
10
10
 
11
11
 
12
12
  class EventHubSource(BaseModelObject):
13
- type: Literal["eventHub"] = "eventHub"
13
+ type: Literal["eventhub"] = "eventhub"
14
14
  host: str
15
15
  event_hub_name: str
16
16
  consumer_group: str | None = None
17
17
 
18
18
 
19
19
  class EventHubSourceRequest(EventHubSource, SourceRequestDefinition):
20
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"authentication"})
20
21
  authentication: BasicAuthenticationRequest | None = None
21
22
 
22
23
 
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import ClassVar, Literal
2
2
 
3
3
  from pydantic import Field
4
4
 
@@ -31,6 +31,7 @@ class KafkaSource(BaseModelObject):
31
31
 
32
32
 
33
33
  class KafkaSourceRequest(KafkaSource, SourceRequestDefinition):
34
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"use_tls"})
34
35
  authentication: (
35
36
  BasicAuthenticationRequest | ClientCredentialAuthenticationRequest | ScramShaAuthenticationRequest | None
36
37
  ) = Field(None, discriminator="type")
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import ClassVar, Literal
2
2
 
3
3
  from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
@@ -18,6 +18,7 @@ class MQTTSource(BaseModelObject):
18
18
 
19
19
 
20
20
  class MQTTSourceRequest(MQTTSource, SourceRequestDefinition):
21
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"use_tls"})
21
22
  authentication: BasicAuthenticationRequest | None = None
22
23
  ca_certificate: CACertificateRequest | None = None
23
24
  auth_certificate: AuthCertificateRequest | None = None
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import ClassVar, Literal
2
2
 
3
3
  from pydantic import Field
4
4
 
@@ -26,6 +26,7 @@ class RESTSource(BaseModelObject):
26
26
 
27
27
 
28
28
  class RESTSourceRequest(RESTSource, SourceRequestDefinition):
29
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"scheme", "port"})
29
30
  scheme: Literal["https", "http"] | None = None
30
31
  authentication: (
31
32
  BasicAuthenticationRequest | HTTPBasicAuthenticationRequest | ClientCredentialAuthenticationRequest | None
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Sequence
2
- from typing import Annotated, Literal
2
+ from typing import Annotated, Any, Literal
3
3
 
4
- from pydantic import Field
4
+ from pydantic import Field, model_serializer, model_validator
5
5
 
6
6
  from cognite_toolkit._cdf_tk.client._resource_base import Identifier
7
7
 
@@ -10,6 +10,24 @@ class InternalOrExternalIdDefinition(Identifier):
10
10
  type: str
11
11
 
12
12
 
13
+ class InternalIdUnwrapped(Identifier):
14
+ id: int
15
+
16
+ def __str__(self) -> str:
17
+ return f"id={self.id}"
18
+
19
+ @model_serializer
20
+ def serialize(self) -> int:
21
+ return self.id
22
+
23
+ @model_validator(mode="before")
24
+ @classmethod
25
+ def deserialize(cls, value: Any) -> Any:
26
+ if isinstance(value, int):
27
+ return {"id": value}
28
+ return value
29
+
30
+
13
31
  class InternalId(InternalOrExternalIdDefinition):
14
32
  type: Literal["id"] = Field("id", exclude=True)
15
33
  id: int
@@ -21,6 +39,9 @@ class InternalId(InternalOrExternalIdDefinition):
21
39
  def __str__(self) -> str:
22
40
  return f"id={self.id}"
23
41
 
42
+ def as_unwrapped(self) -> InternalIdUnwrapped:
43
+ return InternalIdUnwrapped(id=self.id)
44
+
24
45
 
25
46
  class ExternalId(InternalOrExternalIdDefinition):
26
47
  type: Literal["externalId"] = Field("externalId", exclude=True)
@@ -1,20 +1,26 @@
1
+ from pydantic import Field
2
+
1
3
  from cognite_toolkit._cdf_tk.client._resource_base import (
2
4
  BaseModelObject,
3
5
  RequestResource,
4
6
  ResponseResource,
5
7
  )
6
8
 
7
- from .identifiers import NameId
9
+ from .identifiers import InternalIdUnwrapped
8
10
 
9
11
 
10
12
  class SecurityCategory(BaseModelObject):
11
13
  name: str
12
14
 
13
- def as_id(self) -> NameId:
14
- return NameId(name=self.name)
15
15
 
16
+ class SecurityCategoryRequest(SecurityCategory, RequestResource):
17
+ # This is not part of the request payload.
18
+ id: int | None = Field(None, exclude=True)
16
19
 
17
- class SecurityCategoryRequest(SecurityCategory, RequestResource): ...
20
+ def as_id(self) -> InternalIdUnwrapped:
21
+ if self.id is None:
22
+ raise ValueError("Cannot create identifier from SecurityCategoryRequest without id set")
23
+ return InternalIdUnwrapped(id=self.id)
18
24
 
19
25
 
20
26
  class SecurityCategoryResponse(SecurityCategory, ResponseResource[SecurityCategoryRequest]):
@@ -22,3 +28,6 @@ class SecurityCategoryResponse(SecurityCategory, ResponseResource[SecurityCatego
22
28
 
23
29
  def as_request_resource(self) -> SecurityCategoryRequest:
24
30
  return SecurityCategoryRequest.model_validate(self.dump(), extra="ignore")
31
+
32
+ def as_id(self) -> InternalIdUnwrapped:
33
+ return InternalIdUnwrapped(id=self.id)
@@ -1,7 +1,9 @@
1
- from typing import ClassVar, Literal
1
+ from collections import defaultdict
2
+ from typing import Any, ClassVar, Literal
2
3
 
3
4
  from cognite_toolkit._cdf_tk.client._resource_base import (
4
5
  BaseModelObject,
6
+ Identifier,
5
7
  ResponseResource,
6
8
  UpdatableRequestResource,
7
9
  )
@@ -17,6 +19,19 @@ class SequenceColumn(BaseModelObject):
17
19
  value_type: Literal["STRING", "DOUBLE", "LONG"] | None = None
18
20
 
19
21
 
22
+ class SequenceColumnRequest(SequenceColumn, UpdatableRequestResource):
23
+ def as_id(self) -> Identifier:
24
+ return ExternalId(external_id=self.external_id)
25
+
26
+
27
+ class SequenceColumnResponse(SequenceColumn, ResponseResource[SequenceColumnRequest]):
28
+ created_time: int
29
+ last_updated_time: int
30
+
31
+ def as_request_resource(self) -> SequenceColumnRequest:
32
+ return SequenceColumnRequest.model_validate(self.dump(), extra="ignore")
33
+
34
+
20
35
  class Sequence(BaseModelObject):
21
36
  external_id: str | None = None
22
37
  name: str | None = None
@@ -24,7 +39,6 @@ class Sequence(BaseModelObject):
24
39
  asset_id: int | None = None
25
40
  data_set_id: int | None = None
26
41
  metadata: dict[str, str] | None = None
27
- columns: list[SequenceColumn]
28
42
 
29
43
  def as_id(self) -> ExternalId:
30
44
  if self.external_id is None:
@@ -34,12 +48,40 @@ class Sequence(BaseModelObject):
34
48
 
35
49
  class SequenceRequest(Sequence, UpdatableRequestResource):
36
50
  container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "columns"})
51
+ columns: list[SequenceColumnRequest]
52
+
53
+ def as_update(
54
+ self, mode: Literal["patch", "replace"], last_columns: list[SequenceColumn] | None = None
55
+ ) -> dict[str, Any]:
56
+ output = super().as_update(mode)
57
+ output["update"].pop("columns", None)
58
+ exclude_unset = mode == "patch"
59
+ existing_ids = {col.external_id for col in last_columns} if last_columns is not None else set()
60
+ columns: dict[str, list[Any]] = defaultdict(list)
61
+ for col in self.columns:
62
+ col_dumped = col.model_dump(mode="json", by_alias=True, exclude_none=True)
63
+ if col_dumped == {"externalId": col.external_id}:
64
+ continue # No changes to this column
65
+
66
+ if col.external_id in existing_ids:
67
+ columns["modify"].append(col.as_update(mode=mode))
68
+ else:
69
+ columns["add"].append(col.model_dump(mode="json", by_alias=True, exclude_unset=exclude_unset))
70
+ if mode == "replace":
71
+ current_columns_ids = {col.external_id for col in self.columns}
72
+ for last_col in last_columns or []:
73
+ if last_col.external_id not in current_columns_ids:
74
+ columns["remove"].append({"externalId": last_col.external_id})
75
+ if columns:
76
+ output["update"]["columns"] = dict(columns)
77
+ return output
37
78
 
38
79
 
39
80
  class SequenceResponse(Sequence, ResponseResource[SequenceRequest]):
40
81
  id: int
41
82
  created_time: int
42
83
  last_updated_time: int
84
+ columns: list[SequenceColumnResponse]
43
85
 
44
86
  def as_request_resource(self) -> SequenceRequest:
45
87
  return SequenceRequest.model_validate(self.dump(), extra="ignore")
@@ -68,7 +68,7 @@ class ViewDataSource(DestinationDefinition):
68
68
 
69
69
 
70
70
  class RawDataSource(DestinationDefinition):
71
- type: Literal["raw_tables"] = "raw_tables"
71
+ type: Literal["raw"] = "raw"
72
72
  database: str
73
73
  table: str
74
74
 
@@ -108,7 +108,7 @@ class Transformation(BaseModelObject):
108
108
 
109
109
  class TransformationRequest(Transformation, UpdatableRequestResource):
110
110
  container_fields: ClassVar[frozenset[str]] = frozenset({"tags"})
111
- non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_public", "query", "destination"})
111
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_public", "query", "destination", "conflict_mode"})
112
112
  query: str | None = None
113
113
  conflict_mode: str | None = None
114
114
  destination: Destination | None = None
@@ -126,7 +126,8 @@ class TransformationResponse(Transformation, ResponseResource[TransformationRequ
126
126
  conflict_mode: str
127
127
  destination: Destination
128
128
  blocked: BlockedInfo | None = None
129
- owner: str
129
+ # In the API spec, owner is documented as a string, but the response is actually {"user": '<uuid>'}
130
+ owner: str | dict[str, str]
130
131
  owner_is_current_user: bool
131
132
  has_source_oidc_credentials: bool
132
133
  has_destination_oidc_credentials: bool
@@ -13,6 +13,7 @@ from cognite.client.testing import CogniteClientMock
13
13
  from rich.console import Console
14
14
 
15
15
  from cognite_toolkit._cdf_tk.client._toolkit_client import ToolkitClient
16
+ from cognite_toolkit._cdf_tk.client.api.hosted_extractors import HostedExtractorsAPI
16
17
  from cognite_toolkit._cdf_tk.client.api.legacy.canvas import CanvasAPI, IndustrialCanvasAPI
17
18
  from cognite_toolkit._cdf_tk.client.api.legacy.charts import ChartsAPI
18
19
  from cognite_toolkit._cdf_tk.client.api.legacy.dml import DMLAPI
@@ -38,6 +39,10 @@ from .api.datasets import DataSetsAPI
38
39
  from .api.events import EventsAPI
39
40
  from .api.extraction_pipelines import ExtractionPipelinesAPI
40
41
  from .api.filemetadata import FileMetadataAPI
42
+ from .api.hosted_extractor_destinations import HostedExtractorDestinationsAPI
43
+ from .api.hosted_extractor_jobs import HostedExtractorJobsAPI
44
+ from .api.hosted_extractor_mappings import HostedExtractorMappingsAPI
45
+ from .api.hosted_extractor_sources import HostedExtractorSourcesAPI
41
46
  from .api.infield import InfieldAPI, InFieldCDMConfigAPI, InfieldConfigAPI
42
47
  from .api.labels import LabelsAPI
43
48
  from .api.lookup import (
@@ -160,6 +165,11 @@ class ToolkitClientMock(CogniteClientMock):
160
165
  self.tool.simulators.models = MagicMock(spec_set=SimulatorModelsAPI)
161
166
  self.tool.datasets = MagicMock(spec_set=DataSetsAPI)
162
167
  self.tool.extraction_pipelines = MagicMock(spec_set=ExtractionPipelinesAPI)
168
+ self.tool.hosted_extractors = MagicMock(spec=HostedExtractorsAPI)
169
+ self.tool.hosted_extractors.sources = MagicMock(spec_set=HostedExtractorSourcesAPI)
170
+ self.tool.hosted_extractors.jobs = MagicMock(spec_set=HostedExtractorJobsAPI)
171
+ self.tool.hosted_extractors.destinations = MagicMock(spec_set=HostedExtractorDestinationsAPI)
172
+ self.tool.hosted_extractors.mappings = MagicMock(spec_set=HostedExtractorMappingsAPI)
163
173
  self.tool.labels = MagicMock(spec_set=LabelsAPI)
164
174
  self.tool.security_categories = MagicMock(spec_set=SecurityCategoriesAPI)
165
175
  self.tool.sequences = MagicMock(spec_set=SequencesAPI)