diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a7777791d..a2c0056bc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ Changes are grouped as follows - `Fixed` for any bug fixes. - `Security` in case of vulnerabilities. +## [7.60.2] - 2024-09-14 +### Added +- [Feature Preview - alpha] Support for `client.hosted_extractors.destinations`. + ## [7.60.1] - 2024-09-13 ### Fixed - LocationFiltersACl.Scope.SpaceID changed to ID diff --git a/cognite/client/_api/hosted_extractors/__init__.py b/cognite/client/_api/hosted_extractors/__init__.py index 6c6a23a93a..81abaa4016 100644 --- a/cognite/client/_api/hosted_extractors/__init__.py +++ b/cognite/client/_api/hosted_extractors/__init__.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING +from cognite.client._api.hosted_extractors.destinations import DestinationsAPI from cognite.client._api.hosted_extractors.sources import SourcesAPI from cognite.client._api_client import APIClient @@ -14,3 +15,4 @@ class HostedExtractorsAPI(APIClient): def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: CogniteClient) -> None: super().__init__(config, api_version, cognite_client) self.sources = SourcesAPI(config, api_version, cognite_client) + self.destinations = DestinationsAPI(config, api_version, cognite_client) diff --git a/cognite/client/_api/hosted_extractors/destinations.py b/cognite/client/_api/hosted_extractors/destinations.py new file mode 100644 index 0000000000..f41b0ec9b9 --- /dev/null +++ b/cognite/client/_api/hosted_extractors/destinations.py @@ -0,0 +1,272 @@ +from __future__ import annotations + +from collections.abc import Iterator +from typing import TYPE_CHECKING, Any, Sequence, overload + +from cognite.client._api_client import APIClient +from cognite.client._constants import DEFAULT_LIMIT_READ +from cognite.client.data_classes.hosted_extractors.destinations import ( + Destination, + DestinationList, + DestinationUpdate, + DestinationWrite, +) +from cognite.client.utils._experimental import FeaturePreviewWarning +from cognite.client.utils._identifier import IdentifierSequence +from cognite.client.utils.useful_types import SequenceNotStr + +if TYPE_CHECKING: + from cognite.client import ClientConfig, CogniteClient + + +class DestinationsAPI(APIClient): + _RESOURCE_PATH = "/hostedextractors/destinations" + + def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: CogniteClient) -> None: + super().__init__(config, api_version, cognite_client) + self._warning = FeaturePreviewWarning( + api_maturity="beta", sdk_maturity="alpha", feature_name="Hosted Extractors" + ) + self._CREATE_LIMIT = 100 + self._LIST_LIMIT = 100 + self._RETRIEVE_LIMIT = 100 + self._DELETE_LIMIT = 100 + self._UPDATE_LIMIT = 100 + + @overload + def __call__( + self, + chunk_size: None = None, + limit: int | None = None, + ) -> Iterator[Destination]: ... + + @overload + def __call__( + self, + chunk_size: int, + limit: int | None = None, + ) -> Iterator[Destination]: ... + + def __call__( + self, + chunk_size: int | None = None, + limit: int | None = None, + ) -> Iterator[Destination] | Iterator[DestinationList]: + """Iterate over destinations + + Fetches Destination as they are iterated over, so you keep a limited number of destinations in memory. + + Args: + chunk_size (int | None): Number of Destinations to return in each chunk. Defaults to yielding one Destination a time. + limit (int | None): Maximum number of Destination to return. Defaults to returning all items. + + Returns: + Iterator[Destination] | Iterator[DestinationList]: yields Destination one by one if chunk_size is not specified, else DestinationList objects. + """ + self._warning.warn() + + return self._list_generator( + list_cls=DestinationList, + resource_cls=Destination, + method="GET", + chunk_size=chunk_size, + limit=limit, + headers={"cdf-version": "beta"}, + ) + + def __iter__(self) -> Iterator[Destination]: + """Iterate over destinations + + Fetches destinations as they are iterated over, so you keep a limited number of destinations in memory. + + Returns: + Iterator[Destination]: yields Destination one by one. + """ + return self() + + @overload + def retrieve(self, external_ids: str, ignore_unknown_ids: bool = False) -> Destination: ... + + @overload + def retrieve(self, external_ids: SequenceNotStr[str], ignore_unknown_ids: bool = False) -> DestinationList: ... + + def retrieve( + self, external_ids: str | SequenceNotStr[str], ignore_unknown_ids: bool = False + ) -> Destination | DestinationList: + """`Retrieve one or more destinations. `_ + + Args: + external_ids (str | SequenceNotStr[str]): The external ID provided by the client. Must be unique for the resource type. + ignore_unknown_ids (bool): Ignore external IDs that are not found + + + Returns: + Destination | DestinationList: Requested destinations + + Examples: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> res = client.hosted_extractors.destinations.retrieve('myDestination') + + Get multiple destinations by id: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> res = client.hosted_extractors.destinations.retrieve(["myDestination", "myDestination2"], ignore_unknown_ids=True) + + """ + self._warning.warn() + return self._retrieve_multiple( + list_cls=DestinationList, + resource_cls=Destination, + identifiers=IdentifierSequence.load(external_ids=external_ids), + ignore_unknown_ids=ignore_unknown_ids, + headers={"cdf-version": "beta"}, + ) + + def delete( + self, external_ids: str | SequenceNotStr[str], ignore_unknown_ids: bool = False, force: bool = False + ) -> None: + """`Delete one or more destsinations `_ + + Args: + external_ids (str | SequenceNotStr[str]): The external ID provided by the client. Must be unique for the resource type. + ignore_unknown_ids (bool): Ignore external IDs that are not found + force (bool): Delete any jobs associated with each item. + + Examples: + + Delete dests by id:: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> client.hosted_extractors.dests.delete(["myDest", "MyDest2"]) + """ + self._warning.warn() + extra_body_fields: dict[str, Any] = {} + if ignore_unknown_ids: + extra_body_fields["ignoreUnknownIds"] = True + if force: + extra_body_fields["force"] = True + + self._delete_multiple( + identifiers=IdentifierSequence.load(external_ids=external_ids), + wrap_ids=True, + returns_items=False, + headers={"cdf-version": "beta"}, + extra_body_fields=extra_body_fields or None, + ) + + @overload + def create(self, items: DestinationWrite) -> Destination: ... + + @overload + def create(self, items: Sequence[DestinationWrite]) -> DestinationList: ... + + def create(self, items: DestinationWrite | Sequence[DestinationWrite]) -> Destination | DestinationList: + """`Create one or more destinations. `_ + + Args: + items (DestinationWrite | Sequence[DestinationWrite]): Destination(s) to create. + + Returns: + Destination | DestinationList: Created destination(s) + + Examples: + + Create new destination: + + >>> from cognite.client import CogniteClient + >>> from cognite.client.data_classes.hosted_extractors import EventHubSourceWrite + >>> client = CogniteClient() + >>> source = EventHubSourceWrite('my_event_hub', 'http://myeventhub.com', "My EventHub", 'my_key', 'my_value') + >>> res = client.hosted_extractors.destinations.create(destination) + """ + self._warning.warn() + return self._create_multiple( + list_cls=DestinationList, + resource_cls=Destination, + items=items, + input_resource_cls=DestinationWrite, + headers={"cdf-version": "beta"}, + ) + + @overload + def update(self, items: DestinationWrite | DestinationUpdate) -> Destination: ... + + @overload + def update(self, items: Sequence[DestinationWrite | DestinationUpdate]) -> DestinationList: ... + + def update( + self, items: DestinationWrite | DestinationUpdate | Sequence[DestinationWrite | DestinationUpdate] + ) -> Destination | DestinationList: + """`Update one or more destinations. `_ + + Args: + items (DestinationWrite | DestinationUpdate | Sequence[DestinationWrite | DestinationUpdate]): Destination(s) to update. + + Returns: + Destination | DestinationList: Updated destination(s) + + Examples: + + Update destination: + + >>> from cognite.client import CogniteClient + >>> from cognite.client.data_classes.hosted_extractors import DestinationUpdate + >>> client = CogniteClient() + >>> destination = DestinationUpdate('my_dest').target_data_set_id.set(123) + >>> res = client.hosted_extractors.destinations.update(destination) + """ + self._warning.warn() + return self._update_multiple( + items=items, + list_cls=DestinationList, + resource_cls=Destination, + update_cls=DestinationUpdate, + headers={"cdf-version": "beta"}, + ) + + def list( + self, + limit: int | None = DEFAULT_LIMIT_READ, + ) -> DestinationList: + """`List destinations `_ + + Args: + limit (int | None): Maximum number of destinations to return. Defaults to 25. Set to -1, float("inf") or None to return all items. + + Returns: + DestinationList: List of requested destinations + + Examples: + + List destinations: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> destination_list = client.hosted_extractors.destinations.list(limit=5) + + Iterate over destinations:: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> for destination in client.hosted_extractors.destinations: + ... destination # do something with the destination + + Iterate over chunks of destinations to reduce memory load:: + + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> for destination_list in client.hosted_extractors.destinations(chunk_size=25): + ... destination_list # do something with the destinationss + """ + self._warning.warn() + return self._list( + list_cls=DestinationList, + resource_cls=Destination, + method="GET", + limit=limit, + headers={"cdf-version": "beta"}, + ) diff --git a/cognite/client/_version.py b/cognite/client/_version.py index 12a3586510..3b16dd706e 100644 --- a/cognite/client/_version.py +++ b/cognite/client/_version.py @@ -1,4 +1,4 @@ from __future__ import annotations -__version__ = "7.60.1" +__version__ = "7.60.2" __api_subversion__ = "20230101" diff --git a/cognite/client/data_classes/_base.py b/cognite/client/data_classes/_base.py index 02dfb3021d..618ff11623 100644 --- a/cognite/client/data_classes/_base.py +++ b/cognite/client/data_classes/_base.py @@ -520,6 +520,12 @@ def dump(self, camel_case: Literal[True] = True) -> dict[str, Any]: def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]: raise NotImplementedError + @classmethod + def _get_extra_identifying_properties(cls, item: CogniteResource | None = None) -> dict[str, Any]: + # This method is used to provide additional identifying properties for the update object. + # It is intended to be overridden by subclasses that need to provide additional identifying properties. + return {} + T_CogniteUpdate = TypeVar("T_CogniteUpdate", bound=CogniteUpdate) diff --git a/cognite/client/data_classes/hosted_extractors/__init__.py b/cognite/client/data_classes/hosted_extractors/__init__.py index abadfb1c4b..5d50c45c8a 100644 --- a/cognite/client/data_classes/hosted_extractors/__init__.py +++ b/cognite/client/data_classes/hosted_extractors/__init__.py @@ -1,5 +1,13 @@ from __future__ import annotations +from cognite.client.data_classes.hosted_extractors.destinations import ( + Destination, + DestinationList, + DestinationUpdate, + DestinationWrite, + DestinationWriteList, + SessionWrite, +) from cognite.client.data_classes.hosted_extractors.sources import ( EventHubSource, EventHubSourceUpdate, @@ -32,4 +40,10 @@ "MQTT3SourceUpdate", "MQTT5SourceUpdate", "EventHubSourceUpdate", + "Destination", + "DestinationList", + "DestinationWrite", + "DestinationWriteList", + "DestinationUpdate", + "SessionWrite", ] diff --git a/cognite/client/data_classes/hosted_extractors/destinations.py b/cognite/client/data_classes/hosted_extractors/destinations.py new file mode 100644 index 0000000000..f3216e3b8c --- /dev/null +++ b/cognite/client/data_classes/hosted_extractors/destinations.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +from abc import ABC +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, NoReturn + +from typing_extensions import Self + +from cognite.client.data_classes._base import ( + CogniteObject, + CognitePrimitiveUpdate, + CogniteResource, + CogniteResourceList, + CogniteUpdate, + ExternalIDTransformerMixin, + PropertySpec, + WriteableCogniteResource, + WriteableCogniteResourceList, +) + +if TYPE_CHECKING: + from cognite.client import CogniteClient + + +@dataclass +class SessionWrite(CogniteObject): + nonce: str + + @classmethod + def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self: + return cls(nonce=resource["nonce"]) + + +class _DestinationCore(WriteableCogniteResource["DestinationWrite"], ABC): + def __init__(self, external_id: str, target_data_set_id: int | None = None) -> None: + self.external_id = external_id + self.target_data_set_id = target_data_set_id + + +class DestinationWrite(_DestinationCore): + """A hosted extractor writes to a destination. + + The destination contains credentials for CDF, and additional information about where the data should land, + such as data set ID. Multiple jobs can share a single destination, + in which case requests will be combined, reducing the number of requests made to CDF APIs. + Metrics are still reported for each individual job. + + This is the write/request format of the destination. + + Args: + external_id (str): The external ID provided by the client. Must be unique for the resource type. + credentials (SessionWrite): Credentials for authenticating towards CDF using a CDF session. + target_data_set_id (int | None): Data set ID the created items are inserted into, if applicable. + + """ + + def __init__(self, external_id: str, credentials: SessionWrite, target_data_set_id: int | None = None) -> None: + super().__init__(external_id, target_data_set_id) + self.credentials = credentials + + @classmethod + def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> DestinationWrite: + return cls( + external_id=resource["externalId"], + credentials=SessionWrite._load(resource["credentials"]), + target_data_set_id=resource.get("targetDataSetId"), + ) + + def dump(self, camel_case: bool = True) -> dict[str, Any]: + output = super().dump(camel_case) + if isinstance(self.credentials, SessionWrite): + output["credentials"] = self.credentials.dump(camel_case) + + return output + + def as_write(self) -> DestinationWrite: + return self + + +class Destination(_DestinationCore): + """A hosted extractor writes to a destination. + + The destination contains credentials for CDF, and additional information about where the data should land, + such as data set ID. Multiple jobs can share a single destination, + in which case requests will be combined, reducing the number of requests made to CDF APIs. + Metrics are still reported for each individual job. + + This is the write/request format of the destination. + + Args: + external_id (str): The external ID provided by the client. Must be unique for the resource type. + created_time (int): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds. + last_updated_time (int): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds. + session_id (int | None): ID of the session tied to this destination. + target_data_set_id (int | None): Data set ID the created items are inserted into, if applicable. + + """ + + def __init__( + self, + external_id: str, + created_time: int, + last_updated_time: int, + session_id: int | None = None, + target_data_set_id: int | None = None, + ) -> None: + super().__init__(external_id, target_data_set_id) + self.created_time = created_time + self.last_updated_time = last_updated_time + self.session_id = session_id + + @classmethod + def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Destination: + return cls( + external_id=resource["externalId"], + created_time=resource["createdTime"], + last_updated_time=resource["lastUpdatedTime"], + session_id=resource.get("sessionId"), + target_data_set_id=resource.get("targetDataSetId"), + ) + + def as_write(self) -> NoReturn: + raise TypeError(f"{self.__class__.__name__} cannot be converted to a write object") + + +class DestinationUpdate(CogniteUpdate): + def __init__(self, external_id: str) -> None: + super().__init__(external_id=external_id) + + class _CredentialsUpdate(CognitePrimitiveUpdate): + def set(self, value: SessionWrite | None) -> DestinationUpdate: + return self._set(value.dump(camel_case=True) if isinstance(value, SessionWrite) else value) + + class _TargetDataSetIdUpdate(CognitePrimitiveUpdate): + def set(self, value: int | None) -> DestinationUpdate: + return self._set(value) + + @property + def credentials(self) -> DestinationUpdate._CredentialsUpdate: + return self._CredentialsUpdate(self, "credentials") + + @property + def target_data_set_id(self) -> DestinationUpdate._TargetDataSetIdUpdate: + return self._TargetDataSetIdUpdate(self, "targetDataSetId") + + @classmethod + def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]: + return [PropertySpec("credentials", is_nullable=True), PropertySpec("target_data_set_id", is_nullable=True)] + + +class DestinationWriteList(CogniteResourceList[DestinationWrite], ExternalIDTransformerMixin): + _RESOURCE = DestinationWrite + + +class DestinationList(WriteableCogniteResourceList[DestinationWrite, Destination], ExternalIDTransformerMixin): + _RESOURCE = Destination + + def as_write(self) -> NoReturn: + raise TypeError(f"{self.__class__.__name__} cannot be converted to a write object") diff --git a/cognite/client/testing.py b/cognite/client/testing.py index a4bfcb6f3b..2dfe641975 100644 --- a/cognite/client/testing.py +++ b/cognite/client/testing.py @@ -30,6 +30,7 @@ from cognite.client._api.functions import FunctionCallsAPI, FunctionsAPI, FunctionSchedulesAPI from cognite.client._api.geospatial import GeospatialAPI from cognite.client._api.hosted_extractors import HostedExtractorsAPI +from cognite.client._api.hosted_extractors.destinations import DestinationsAPI from cognite.client._api.hosted_extractors.sources import SourcesAPI from cognite.client._api.iam import IAMAPI, GroupsAPI, SecurityCategoriesAPI, SessionsAPI, TokenAPI from cognite.client._api.labels import LabelsAPI @@ -140,6 +141,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.hosted_extractors = MagicMock(spec=HostedExtractorsAPI) self.hosted_extractors.sources = MagicMock(spec_set=SourcesAPI) + self.hosted_extractors.destinations = MagicMock(spec_set=DestinationsAPI) self.templates = MagicMock(spec=TemplatesAPI) self.templates.groups = MagicMock(spec_set=TemplateGroupsAPI) diff --git a/docs/source/hosted_extractors.rst b/docs/source/hosted_extractors.rst index afc45101d5..da9ff70db4 100644 --- a/docs/source/hosted_extractors.rst +++ b/docs/source/hosted_extractors.rst @@ -22,3 +22,25 @@ Retrieve sources Update sources ^^^^^^^^^^^^^^^^^^ .. automethod:: cognite.client._api.hosted_extractors.SourcesAPI.update + +Destinations +------------- +Create new destinations +^^^^^^^^^^^^^^^^^^^^^^^ +.. automethod:: cognite.client._api.hosted_extractors.DestinationsAPI.create + +Delete destinations +^^^^^^^^^^^^^^^^^^^^^^ +.. automethod:: cognite.client._api.hosted_extractors.DestinationsAPI.delete + +List destinations +^^^^^^^^^^^^^^^^^^ +.. automethod:: cognite.client._api.hosted_extractors.DestinationsAPI.list + +Retrieve destinations +^^^^^^^^^^^^^^^^^^^^^^^ +.. automethod:: cognite.client._api.hosted_extractors.DestinationsAPI.retrieve + +Update destinations +^^^^^^^^^^^^^^^^^^^^ +.. automethod:: cognite.client._api.hosted_extractors.DestinationsAPI.update diff --git a/pyproject.toml b/pyproject.toml index f62e5fe418..2a9fd24348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "cognite-sdk" -version = "7.60.1" +version = "7.60.2" description = "Cognite Python SDK" readme = "README.md" documentation = "https://cognite-sdk-python.readthedocs-hosted.com" diff --git a/tests/tests_integration/test_api/test_hosted_extractors/test_destinations.py b/tests/tests_integration/test_api/test_hosted_extractors/test_destinations.py new file mode 100644 index 0000000000..f3a3da87b8 --- /dev/null +++ b/tests/tests_integration/test_api/test_hosted_extractors/test_destinations.py @@ -0,0 +1,114 @@ +from __future__ import annotations + +import pytest + +from cognite.client import CogniteClient +from cognite.client.data_classes import CreatedSession, DataSet, DataSetWrite +from cognite.client.data_classes.hosted_extractors import ( + Destination, + DestinationList, + DestinationUpdate, + DestinationWrite, + SessionWrite, +) +from cognite.client.exceptions import CogniteAPIError +from cognite.client.utils._text import random_string + + +@pytest.fixture +def fresh_session(cognite_client: CogniteClient) -> SessionWrite: + new_session = cognite_client.iam.sessions.create(session_type="ONESHOT_TOKEN_EXCHANGE") + yield SessionWrite(nonce=new_session.nonce) + cognite_client.iam.sessions.revoke(new_session.id) + + +@pytest.fixture(scope="session") +def a_data_set(cognite_client: CogniteClient) -> DataSet: + ds = DataSetWrite(external_id="test-dataset-hosted-extractor", name="test-dataset-hosted-extractor") + retrieved = cognite_client.data_sets.retrieve(external_id=ds.external_id) + if retrieved: + return retrieved + created = cognite_client.data_sets.create(ds) + return created + + +@pytest.fixture +def one_destination(cognite_client: CogniteClient, fresh_session: SessionWrite) -> DestinationList: + my_dest = DestinationWrite( + external_id=f"myNewDestination-{random_string(10)}", + credentials=fresh_session, + ) + retrieved = cognite_client.hosted_extractors.destinations.retrieve([my_dest.external_id], ignore_unknown_ids=True) + if retrieved: + yield retrieved + created = cognite_client.hosted_extractors.destinations.create(my_dest) + yield DestinationList([created]) + + cognite_client.hosted_extractors.destinations.delete(created.external_id, ignore_unknown_ids=True) + + +class TestDestinations: + def test_create_update_retrieve_delete( + self, cognite_client: CogniteClient, fresh_session: SessionWrite, a_data_set: DataSet + ) -> None: + my_dest = DestinationWrite( + external_id=f"myNewDestinationForTesting-{random_string(10)}", + credentials=fresh_session, + ) + created: Destination | None = None + try: + created = cognite_client.hosted_extractors.destinations.create(my_dest) + assert isinstance(created, Destination) + update = DestinationUpdate(external_id=my_dest.external_id).target_data_set_id.set(a_data_set.id) + updated = cognite_client.hosted_extractors.destinations.update(update) + assert updated.target_data_set_id == a_data_set.id + retrieved = cognite_client.hosted_extractors.destinations.retrieve(created.external_id) + assert retrieved is not None + assert retrieved.external_id == created.external_id + assert retrieved.target_data_set_id == a_data_set.id + + cognite_client.hosted_extractors.destinations.delete(created.external_id) + + with pytest.raises(CogniteAPIError): + cognite_client.hosted_extractors.destinations.retrieve(created.external_id) + + cognite_client.hosted_extractors.destinations.retrieve(created.external_id, ignore_unknown_ids=True) + + finally: + if created: + cognite_client.hosted_extractors.destinations.delete(created.external_id, ignore_unknown_ids=True) + + @pytest.mark.usefixtures("one_destination") + def test_list(self, cognite_client: CogniteClient) -> None: + res = cognite_client.hosted_extractors.destinations.list(limit=1) + assert len(res) == 1 + assert isinstance(res, DestinationList) + + def test_update_using_write_object( + self, cognite_client: CogniteClient, fresh_session: SessionWrite, a_data_set: DataSet + ) -> None: + my_dest = DestinationWrite( + external_id=f"toupdate-{random_string(10)}", + credentials=fresh_session, + ) + + created: Destination | None = None + new_session: CreatedSession | None = None + try: + new_session = cognite_client.iam.sessions.create(session_type="ONESHOT_TOKEN_EXCHANGE") + created = cognite_client.hosted_extractors.destinations.create(my_dest) + + update = DestinationWrite( + external_id=my_dest.external_id, + credentials=SessionWrite(nonce=new_session.nonce), + target_data_set_id=a_data_set.id, + ) + + updated = cognite_client.hosted_extractors.destinations.update(update) + + assert updated.target_data_set_id == a_data_set.id + finally: + if created: + cognite_client.hosted_extractors.destinations.delete(created.external_id, ignore_unknown_ids=True) + if new_session: + cognite_client.iam.sessions.revoke(new_session.id) diff --git a/tests/tests_unit/test_base.py b/tests/tests_unit/test_base.py index 317a82945d..ea54a53ee4 100644 --- a/tests/tests_unit/test_base.py +++ b/tests/tests_unit/test_base.py @@ -36,7 +36,7 @@ ) from cognite.client.data_classes.datapoints import DatapointsArray from cognite.client.data_classes.events import Event, EventList -from cognite.client.data_classes.hosted_extractors import Source, SourceList +from cognite.client.data_classes.hosted_extractors import Destination, DestinationList, Source, SourceList from cognite.client.exceptions import CogniteMissingClientError from cognite.client.testing import CogniteClientMock from cognite.client.utils import _json @@ -195,7 +195,8 @@ def test_dump_load_only_required( pytest.param(cls, id=f"{cls.__name__} in {cls.__module__}") # Hosted extractors does not support the as_write method for cls in all_concrete_subclasses(WriteableCogniteResource) - if not issubclass(cls, Source) + # Hosted extractors does not support the as_write method + if cls not in {Destination} and not issubclass(cls, Source) ], ) def test_writable_as_write( @@ -213,7 +214,7 @@ def test_writable_as_write( [ pytest.param(cls, id=f"{cls.__name__} in {cls.__module__}") for cls in all_concrete_subclasses(WriteableCogniteResourceList) - if cls not in [EdgeListWithCursor, NodeListWithCursor, SourceList] + if cls not in [EdgeListWithCursor, NodeListWithCursor, SourceList, DestinationList] ], ) def test_writable_list_as_write(