Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cognite/client/_api/transformations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ async def list(
data_set_external_ids: str | list[str] | None = None,
tags: TagsFilter | None = None,
limit: int | None = DEFAULT_LIMIT_READ,
data_domain_external_ids: str | list[str] | None = None,
) -> TransformationList:
"""`List all transformations <https://api-docs.cognite.com/20230101/tag/Transformations/operation/filterTransformations>`_.

Expand All @@ -317,6 +318,7 @@ async def list(
data_set_external_ids (str | list[str] | None): Return only transformations in the specified data sets with these external id(s).
tags (TagsFilter | None): Return only the resource matching the specified tags constraints. It only supports ContainsAny as of now.
limit (int | None): Limits the number of results to be returned. To retrieve all results use limit=-1, default limit is 25.
data_domain_external_ids (str | list[str] | None): Return only transformations belonging to one of the specified data domain(s).

Returns:
TransformationList: List of transformations
Expand All @@ -331,6 +333,9 @@ async def list(
>>> transformations_list = client.transformations.list()
"""
ds_ids = IdentifierSequence.load(data_set_ids, data_set_external_ids, id_name="data_set").as_dicts()
domain_external_ids = (
[data_domain_external_ids] if isinstance(data_domain_external_ids, str) else data_domain_external_ids
) or None

filter = TransformationFilter(
include_public=include_public,
Expand All @@ -344,6 +349,7 @@ async def list(
last_updated_time=last_updated_time,
tags=tags,
data_set_ids=ds_ids or None,
data_domain_external_ids=domain_external_ids,
).dump(camel_case=True)

return await self._list(
Expand Down
3 changes: 3 additions & 0 deletions cognite/client/_sync_api/transformations/__init__.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions cognite/client/data_classes/transformations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ class Transformation(WriteableCogniteResourceWithClientRef["TransformationWrite"
source_session (SessionDetails | None): Details for the session used to read from the source project.
destination_session (SessionDetails | None): Details for the session used to write to the destination project.
tags (list[str] | None): No description.
data_domain_external_id (str | None): The external ID of the data domain this transformation belongs to.
"""

def __init__(
Expand Down Expand Up @@ -245,6 +246,7 @@ def __init__(
source_session: SessionDetails | None = None,
destination_session: SessionDetails | None = None,
tags: list[str] | None = None,
data_domain_external_id: str | None = None,
) -> None:
self.external_id = external_id
self.name = name
Expand All @@ -254,6 +256,7 @@ def __init__(
self.source_nonce = source_nonce
self.destination_nonce = destination_nonce
self.data_set_id = data_set_id
self.data_domain_external_id = data_domain_external_id
self.id = id
self.query = query
self.destination = destination
Expand Down Expand Up @@ -300,6 +303,7 @@ def as_write(self) -> TransformationWrite:
source_oidc_credentials=self.source_oidc_credentials,
destination_oidc_credentials=self.destination_oidc_credentials,
data_set_id=self.data_set_id,
data_domain_external_id=self.data_domain_external_id,
source_nonce=self.source_nonce,
destination_nonce=self.destination_nonce,
tags=self.tags,
Expand Down Expand Up @@ -334,6 +338,7 @@ def copy(self) -> Transformation:
blocked=self.blocked,
schedule=self.schedule,
data_set_id=self.data_set_id,
data_domain_external_id=self.data_domain_external_id,
source_nonce=self.source_nonce,
destination_nonce=self.destination_nonce,
source_session=self.source_session,
Expand Down Expand Up @@ -412,6 +417,7 @@ def _load(cls, resource: dict) -> Transformation:
blocked=TransformationBlockedInfo._load_if(resource.get("blocked")),
schedule=TransformationSchedule._load_if(resource.get("schedule")),
data_set_id=resource.get("dataSetId"),
data_domain_external_id=resource.get("dataDomainExternalId"),
source_nonce=NonceCredentials._load_if(resource.get("sourceNonce")),
destination_nonce=NonceCredentials._load_if(resource.get("destinationNonce")),
source_session=SessionDetails._load_if(resource.get("sourceSession")),
Expand Down Expand Up @@ -440,6 +446,7 @@ class TransformationWrite(WriteableCogniteResource["TransformationWrite"], _Tran
source_nonce (NonceCredentials | None): Single use credentials to bind to a CDF session for reading.
destination_nonce (NonceCredentials | None): Single use credentials to bind to a CDF session for writing.
tags (list[str] | None): No description.
data_domain_external_id (str | None): The external ID of the data domain this transformation belongs to.
"""

def __init__(
Expand All @@ -457,6 +464,7 @@ def __init__(
source_nonce: NonceCredentials | None = None,
destination_nonce: NonceCredentials | None = None,
tags: list[str] | None = None,
data_domain_external_id: str | None = None,
) -> None:
self.external_id = external_id
self.name = name
Expand All @@ -466,6 +474,7 @@ def __init__(
self.source_nonce = source_nonce
self.destination_nonce = destination_nonce
self.data_set_id = data_set_id
self.data_domain_external_id = data_domain_external_id
self.query = query
self.destination = destination
self.conflict_mode = conflict_mode
Expand All @@ -485,6 +494,7 @@ def _load(cls, resource: dict) -> TransformationWrite:
source_oidc_credentials=OidcCredentials._load_if(resource.get("sourceOidcCredentials")),
destination_oidc_credentials=OidcCredentials._load_if(resource.get("destinationOidcCredentials")),
data_set_id=resource.get("dataSetId"),
data_domain_external_id=resource.get("dataDomainExternalId"),
source_nonce=NonceCredentials._load_if(resource.get("sourceNonce")),
destination_nonce=NonceCredentials._load_if(resource.get("destinationNonce")),
tags=resource.get("tags"),
Expand All @@ -505,6 +515,7 @@ def copy(self) -> TransformationWrite:
self.source_nonce,
self.destination_nonce,
self.tags,
self.data_domain_external_id,
Comment thread
ankuagarwal marked this conversation as resolved.
Resolved
)
Comment thread
MoonWorm marked this conversation as resolved.
Resolved

def dump(self, camel_case: bool = True) -> dict[str, Any]:
Expand Down Expand Up @@ -598,6 +609,10 @@ def ignore_null_fields(self) -> _PrimitiveTransformationUpdate:
def data_set_id(self) -> _PrimitiveTransformationUpdate:
return TransformationUpdate._PrimitiveTransformationUpdate(self, "dataSetId")

@property
def data_domain_external_id(self) -> _PrimitiveTransformationUpdate:
return TransformationUpdate._PrimitiveTransformationUpdate(self, "dataDomainExternalId")

@property
def tags(self) -> _ListTransformationUpdate:
return TransformationUpdate._ListTransformationUpdate(self, "tags")
Expand Down Expand Up @@ -626,6 +641,7 @@ def _get_update_properties(cls, item: CogniteResource | None = None) -> list[Pro
PropertySpec("is_public", is_nullable=False),
PropertySpec("ignore_null_fields", is_nullable=False),
PropertySpec("data_set_id"),
PropertySpec("data_domain_external_id"),
PropertySpec("tags", is_list=True),
]

Expand Down Expand Up @@ -683,6 +699,7 @@ class TransformationFilter(CogniteFilter):
last_updated_time (dict[str, Any] | TimestampRange | None): Range between two timestamps
data_set_ids (list[dict[str, Any]] | None): Return only transformations in the specified data sets with these ids, e.g. [{"id": 1}, {"externalId": "foo"}].
tags (TagsFilter | None): Return only the resource matching the specified tags constraints. It only supports ContainsAny as of now.
data_domain_external_ids (list[str] | None): Return only transformations belonging to one of the specified data domains, e.g. ["my-domain"].
"""

def __init__(
Expand All @@ -698,6 +715,7 @@ def __init__(
last_updated_time: dict[str, Any] | TimestampRange | None = None,
data_set_ids: list[dict[str, Any]] | None = None,
tags: TagsFilter | None = None,
data_domain_external_ids: list[str] | None = None,
) -> None:
self.include_public = include_public
self.name_regex = name_regex
Expand All @@ -710,6 +728,7 @@ def __init__(
self.last_updated_time = last_updated_time
self.data_set_ids = data_set_ids
self.tags = tags
self.data_domain_external_ids = data_domain_external_ids

def dump(self, camel_case: bool = True) -> dict[str, Any]:
obj = super().dump(camel_case=camel_case)
Expand Down
36 changes: 36 additions & 0 deletions tests/tests_unit/test_api/test_transformations.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
from __future__ import annotations

import re
from typing import TYPE_CHECKING

import pytest
from pytest_httpx2 import HTTPXMock
Comment thread
ankuagarwal marked this conversation as resolved.
Resolved
Comment thread
MoonWorm marked this conversation as resolved.
Resolved

from cognite.client.credentials import OAuthClientCredentials
from cognite.client.data_classes.iam import ClientCredentials
from cognite.client.data_classes.transformations.common import OidcCredentials
from tests.utils import get_url, jsgz_load

if TYPE_CHECKING:
from cognite.client import AsyncCogniteClient, CogniteClient


@pytest.fixture
Expand Down Expand Up @@ -49,3 +57,31 @@ def test_oidc_credentials_as_client_credentials(oidc_credentials: OidcCredential

assert isinstance(client_creds, ClientCredentials)
assert client_creds == ClientCredentials("id", "secret")


@pytest.fixture
def mock_transformations_list_response(httpx2_mock: HTTPXMock, async_client: AsyncCogniteClient) -> HTTPXMock:
url_pattern = re.compile(re.escape(get_url(async_client.transformations)) + r"/transformations/filter(?:\?.+)?$")
httpx2_mock.add_response(method="POST", url=url_pattern, status_code=200, json={"items": []})
return httpx2_mock
Comment thread
ankuagarwal marked this conversation as resolved.
Resolved
Comment thread
MoonWorm marked this conversation as resolved.
Resolved


@pytest.mark.parametrize(
"data_domain_external_ids, expected",
[
pytest.param(None, None, id="omitted when not given"),
pytest.param([], None, id="omitted when empty list given"),
pytest.param("my-domain", ["my-domain"], id="single string is wrapped in list"),
pytest.param(["domain-a", "domain-b"], ["domain-a", "domain-b"], id="list of strings passed through"),
],
)
def test_list_data_domain_external_ids_filter(
cognite_client: CogniteClient,
mock_transformations_list_response: HTTPXMock,
data_domain_external_ids: str | list[str] | None,
expected: list[str] | None,
) -> None:
cognite_client.transformations.list(data_domain_external_ids=data_domain_external_ids)

sent_filter = jsgz_load(mock_transformations_list_response.get_requests()[0].content)["filter"]
assert sent_filter.get("dataDomainExternalIds") == expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

import pytest

from cognite.client.data_classes.transformations import (
Transformation,
TransformationUpdate,
TransformationWrite,
)
from tests.utils import FakeCogniteResourceGenerator


@pytest.mark.dsl # requirement when using FakeCogniteResourceGenerator
@pytest.mark.parametrize("transformation_cls", (Transformation, TransformationWrite))
def test_copy_preserves_all_fields(transformation_cls: type[Transformation] | type[TransformationWrite]) -> None:
transformation = FakeCogniteResourceGenerator().create_instance(transformation_cls)

copied = transformation.copy()

assert copied is not transformation
assert type(copied) is transformation_cls
assert copied.dump() == transformation.dump()


@pytest.mark.parametrize(
"value, expected",
[
pytest.param("my-domain", {"set": "my-domain"}, id="set value"),
pytest.param(None, {"setNull": True}, id="set to None clears the value"),
],
)
def test_update_data_domain_external_id(value: str | None, expected: dict[str, object]) -> None:
update = TransformationUpdate(external_id="my-transformation").data_domain_external_id.set(value)

assert update.dump()["update"] == {"dataDomainExternalId": expected}
Loading