Skip to content

Commit

Permalink
[CDF-22563] Update Resources mode (#1935)
Browse files Browse the repository at this point in the history
  • Loading branch information
doctrino authored Sep 19, 2024
1 parent 7089a29 commit 18fca40
Show file tree
Hide file tree
Showing 22 changed files with 529 additions and 64 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Changes are grouped as follows
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [7.62.0] - 2024-09-19
### Added
- All `update` methods now accept a new parameter `mode` that controls how non-update objects should be
interpreted. For example, should we do a partial update or a full replacement.

## [7.61.1] - 2024-09-19
### Added
- [Feature Preview - alpha] Support for `client.hosted_extractors.jobs`.
Expand Down
21 changes: 18 additions & 3 deletions cognite/client/_api/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,42 @@ def _convert_resource_to_patch_object(
return annotation_update.dump()

@overload
def update(self, item: Annotation | AnnotationWrite | AnnotationUpdate) -> Annotation: ...
def update(
self,
item: Annotation | AnnotationWrite | AnnotationUpdate,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Annotation: ...

@overload
def update(self, item: Sequence[Annotation | AnnotationWrite | AnnotationUpdate]) -> AnnotationList: ...
def update(
self,
item: Sequence[Annotation | AnnotationWrite | AnnotationUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> AnnotationList: ...

def update(
self,
item: Annotation
| AnnotationWrite
| AnnotationUpdate
| Sequence[Annotation | AnnotationWrite | AnnotationUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Annotation | AnnotationList:
"""`Update annotations <https://developer.cognite.com/api#tag/Annotations/operation/annotationsUpdate>`_
Args:
item (Annotation | AnnotationWrite | AnnotationUpdate | Sequence[Annotation | AnnotationWrite | AnnotationUpdate]): Annotation or list of annotations to update (or patch or list of patches to apply)
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (Annotation or -Write). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
Annotation | AnnotationList: No description."""
return self._update_multiple(
list_cls=AnnotationList, resource_cls=Annotation, update_cls=AnnotationUpdate, items=item
list_cls=AnnotationList, resource_cls=Annotation, update_cls=AnnotationUpdate, items=item, mode=mode
)

def delete(self, id: int | Sequence[int]) -> None:
Expand Down
27 changes: 22 additions & 5 deletions cognite/client/_api/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,20 +755,35 @@ def delete(
)

@overload
def update(self, item: Sequence[Asset | AssetWrite | AssetUpdate]) -> AssetList: ...
def update(
self,
item: Sequence[Asset | AssetWrite | AssetUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> AssetList: ...

@overload
def update(self, item: Asset | AssetWrite | AssetUpdate) -> Asset: ...
def update(
self,
item: Asset | AssetWrite | AssetUpdate,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Asset: ...

def update(
self, item: Asset | AssetWrite | AssetUpdate | Sequence[Asset | AssetWrite | AssetUpdate]
self,
item: Asset | AssetWrite | AssetUpdate | Sequence[Asset | AssetWrite | AssetUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Asset | AssetList:
"""`Update one or more assets <https://developer.cognite.com/api#tag/Assets/operation/updateAssets>`_
Labels can be added, removed or replaced (set). Note that set operation deletes all the existing labels and adds the new specified labels.
Args:
item (Asset | AssetWrite | AssetUpdate | Sequence[Asset | AssetWrite | AssetUpdate]): Asset(s) to update
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (Asset or -Write). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
Asset | AssetList: Updated asset(s)
Expand Down Expand Up @@ -820,7 +835,9 @@ def update(
>>> my_update = AssetUpdate(id=1).labels.set("PUMP")
>>> res = client.assets.update(my_update)
"""
return self._update_multiple(list_cls=AssetList, resource_cls=Asset, update_cls=AssetUpdate, items=item)
return self._update_multiple(
list_cls=AssetList, resource_cls=Asset, update_cls=AssetUpdate, items=item, mode=mode
)

@overload
def upsert(self, item: Sequence[Asset | AssetWrite], mode: Literal["patch", "replace"] = "patch") -> AssetList: ...
Expand Down
28 changes: 23 additions & 5 deletions cognite/client/_api/data_sets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Iterator, Sequence, overload
from typing import TYPE_CHECKING, Any, Iterator, Literal, Sequence, overload

from cognite.client._api_client import APIClient
from cognite.client._constants import DEFAULT_LIMIT_READ
Expand Down Expand Up @@ -213,18 +213,34 @@ def aggregate(self, filter: DataSetFilter | dict[str, Any] | None = None) -> lis
return self._aggregate(filter=filter, cls=CountAggregate)

@overload
def update(self, item: DataSet | DataSetWrite | DataSetUpdate) -> DataSet: ...
def update(
self,
item: DataSet | DataSetWrite | DataSetUpdate,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> DataSet: ...

@overload
def update(self, item: Sequence[DataSet | DataSetWrite | DataSetUpdate]) -> DataSetList: ...
def update(
self,
item: Sequence[DataSet | DataSetWrite | DataSetUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> DataSetList: ...

def update(
self, item: DataSet | DataSetWrite | DataSetUpdate | Sequence[DataSet | DataSetWrite | DataSetUpdate]
self,
item: DataSet | DataSetWrite | DataSetUpdate | Sequence[DataSet | DataSetWrite | DataSetUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> DataSet | DataSetList:
"""`Update one or more data sets <https://developer.cognite.com/api#tag/Data-sets/operation/updateDataSets>`_
Args:
item (DataSet | DataSetWrite | DataSetUpdate | Sequence[DataSet | DataSetWrite | DataSetUpdate]): Data set(s) to update
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (DataSet or -Write). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
DataSet | DataSetList: Updated data set(s)
Expand All @@ -247,7 +263,9 @@ def update(
>>> my_update = DataSetUpdate(id=1).description.set("New description").metadata.remove(["key"])
>>> res = client.data_sets.update(my_update)
"""
return self._update_multiple(list_cls=DataSetList, resource_cls=DataSet, update_cls=DataSetUpdate, items=item)
return self._update_multiple(
list_cls=DataSetList, resource_cls=DataSet, update_cls=DataSetUpdate, items=item, mode=mode
)

def list(
self,
Expand Down
15 changes: 13 additions & 2 deletions cognite/client/_api/datapoints_subscriptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Iterator, cast, overload
from typing import TYPE_CHECKING, Iterator, Literal, cast, overload

from cognite.client._api_client import APIClient
from cognite.client._constants import DEFAULT_LIMIT_READ
Expand Down Expand Up @@ -192,14 +192,24 @@ def list_member_time_series(self, external_id: str, limit: int | None = DEFAULT_
other_params={"externalId": external_id},
)

def update(self, update: DataPointSubscriptionUpdate | DataPointSubscriptionWrite) -> DatapointSubscription:
def update(
self,
update: DataPointSubscriptionUpdate | DataPointSubscriptionWrite,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> DatapointSubscription:
"""`Update a subscriptions <https://api-docs.cognite.com/20230101/tag/Data-point-subscriptions/operation/updateSubscriptions>`_
Update a subscription. Note that Fields that are not included in the request are not changed.
Furthermore, the subscription partition cannot be changed.
Args:
update (DataPointSubscriptionUpdate | DataPointSubscriptionWrite): The subscription update.
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (DataPointSubscriptionWrite). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing.
Returns:
DatapointSubscription: Updated subscription.
Expand Down Expand Up @@ -229,6 +239,7 @@ def update(self, update: DataPointSubscriptionUpdate | DataPointSubscriptionWrit
list_cls=DatapointSubscriptionList,
resource_cls=DatapointSubscription,
update_cls=DataPointSubscriptionUpdate,
mode=mode,
)

def iterate_data(
Expand Down
10 changes: 9 additions & 1 deletion cognite/client/_api/entity_matching.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any, Sequence, TypeVar
from typing import Any, Literal, Sequence, TypeVar

from cognite.client._api_client import APIClient
from cognite.client._constants import DEFAULT_LIMIT_READ
Expand Down Expand Up @@ -89,11 +89,18 @@ def update(
item: EntityMatchingModel
| EntityMatchingModelUpdate
| Sequence[EntityMatchingModel | EntityMatchingModelUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> EntityMatchingModelList | EntityMatchingModel:
"""`Update model <https://developer.cognite.com/api#tag/Entity-matching/operation/entityMatchingUpdate>`_
Args:
item (EntityMatchingModel | EntityMatchingModelUpdate | Sequence[EntityMatchingModel | EntityMatchingModelUpdate]): Model(s) to update
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (EntityMatchingModel). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
EntityMatchingModelList | EntityMatchingModel: No description.
Expand All @@ -109,6 +116,7 @@ def update(
resource_cls=EntityMatchingModel,
update_cls=EntityMatchingModelUpdate,
items=item,
mode=mode,
)

def list(
Expand Down
26 changes: 22 additions & 4 deletions cognite/client/_api/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,18 +555,34 @@ def delete(
)

@overload
def update(self, item: Sequence[Event | EventWrite | EventUpdate]) -> EventList: ...
def update(
self,
item: Sequence[Event | EventWrite | EventUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> EventList: ...

@overload
def update(self, item: Event | EventWrite | EventUpdate) -> Event: ...
def update(
self,
item: Event | EventWrite | EventUpdate,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Event: ...

def update(
self, item: Event | EventWrite | EventUpdate | Sequence[Event | EventWrite | EventUpdate]
self,
item: Event | EventWrite | EventUpdate | Sequence[Event | EventWrite | EventUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> Event | EventList:
"""`Update one or more events <https://developer.cognite.com/api#tag/Events/operation/updateEvents>`_
Args:
item (Event | EventWrite | EventUpdate | Sequence[Event | EventWrite | EventUpdate]): Event(s) to update
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (Event or -Write). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
Event | EventList: Updated event(s)
Expand All @@ -589,7 +605,9 @@ def update(
>>> my_update = EventUpdate(id=1).description.set("New description").metadata.add({"key": "value"})
>>> res = client.events.update(my_update)
"""
return self._update_multiple(list_cls=EventList, resource_cls=Event, update_cls=EventUpdate, items=item)
return self._update_multiple(
list_cls=EventList, resource_cls=Event, update_cls=EventUpdate, items=item, mode=mode
)

def search(
self,
Expand Down
8 changes: 8 additions & 0 deletions cognite/client/_api/extractionpipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,18 @@ def update(
| ExtractionPipelineWrite
| ExtractionPipelineUpdate
| Sequence[ExtractionPipeline | ExtractionPipelineWrite | ExtractionPipelineUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> ExtractionPipeline | ExtractionPipelineList:
"""`Update one or more extraction pipelines <https://developer.cognite.com/api#tag/Extraction-Pipelines/operation/updateExtPipes>`_
Args:
item (ExtractionPipeline | ExtractionPipelineWrite | ExtractionPipelineUpdate | Sequence[ExtractionPipeline | ExtractionPipelineWrite | ExtractionPipelineUpdate]): Extraction pipeline(s) to update
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update
object is given (ExtractionPipeline or -Write). If you use 'replace_ignore_null', only the fields
you have set will be used to replace existing (default). Using 'replace' will additionally
clear all the fields that are not specified by you. Last option, 'patch', will update only
the fields you have set and for container-like fields such as metadata or labels, add the
values to the existing. For more details, see :ref:`appendix-update`.
Returns:
ExtractionPipeline | ExtractionPipelineList: Updated extraction pipeline(s)
Expand All @@ -270,6 +277,7 @@ def update(
resource_cls=ExtractionPipeline,
update_cls=ExtractionPipelineUpdate,
items=item,
mode=mode,
)


Expand Down
17 changes: 14 additions & 3 deletions cognite/client/_api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from collections import defaultdict
from io import BufferedReader
from pathlib import Path
from typing import Any, BinaryIO, Iterator, Sequence, TextIO, cast, overload
from typing import Any, BinaryIO, Iterator, Literal, Sequence, TextIO, cast, overload
from urllib.parse import urljoin, urlparse

from cognite.client._api_client import APIClient
Expand Down Expand Up @@ -360,23 +360,33 @@ def delete(
)

@overload
def update(self, item: FileMetadata | FileMetadataWrite | FileMetadataUpdate) -> FileMetadata: ...
def update(
self,
item: FileMetadata | FileMetadataWrite | FileMetadataUpdate,
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> FileMetadata: ...

@overload
def update(self, item: Sequence[FileMetadata | FileMetadataWrite | FileMetadataUpdate]) -> FileMetadataList: ...
def update(
self,
item: Sequence[FileMetadata | FileMetadataWrite | FileMetadataUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> FileMetadataList: ...

def update(
self,
item: FileMetadata
| FileMetadataWrite
| FileMetadataUpdate
| Sequence[FileMetadata | FileMetadataWrite | FileMetadataUpdate],
mode: Literal["replace_ignore_null", "patch", "replace"] = "replace_ignore_null",
) -> FileMetadata | FileMetadataList:
"""`Update files <https://developer.cognite.com/api#tag/Files/operation/updateFiles>`_
Currently, a full replacement of labels on a file is not supported (only partial add/remove updates). See the example below on how to perform partial labels update.
Args:
item (FileMetadata | FileMetadataWrite | FileMetadataUpdate | Sequence[FileMetadata | FileMetadataWrite | FileMetadataUpdate]): file(s) to update.
mode (Literal["replace_ignore_null", "patch", "replace"]): How to update data when a non-update object is given (FilesMetadata or -Write). If you use 'replace_ignore_null', only the fields you have set will be used to replace existing (default). Using 'replace' will additionally clear all the fields that are not specified by you. Last option, 'patch', will update only the fields you have set and for container-like fields such as metadata or labels, add the values to the existing. For more details, see :ref:`appendix-update`.
Returns:
FileMetadata | FileMetadataList: The updated files.
Expand Down Expand Up @@ -429,6 +439,7 @@ def update(
resource_path=self._RESOURCE_PATH,
items=item,
headers=headers,
mode=mode,
)

def search(
Expand Down
Loading

0 comments on commit 18fca40

Please sign in to comment.