Skip to content

Commit

Permalink
Merge branch 'master' of github.com:cognitedata/cognite-sdk-python in…
Browse files Browse the repository at this point in the history
…to DOG-3974_dm_reference_in_diagram_detect
  • Loading branch information
Ola Liabøtrø committed Aug 30, 2024
2 parents 64b47cf + b201be9 commit 5d66596
Show file tree
Hide file tree
Showing 43 changed files with 3,714 additions and 2,280 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ scripts/tmp/
my_file.txt
.venv/
*.env
.envrc
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.7
rev: v0.6.2
hooks:
- id: ruff
args:
Expand Down
40 changes: 38 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,48 @@ Changes are grouped as follows
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [7.55.1] - 2024-08-29
### Fixed
- Missing exports for workflow triggers

## [7.55.0] - 2024-08-23
### Added
- Support for creating a session using a one-shot token in the `client.iam.session.create` method.
- Parameter `nonce` to the `client.functions.call()` and `client.workflow.executions.run()` methods to allow passing
a custom nonce instead of letting the SDK generate it from your current credentials.

## [7.54.19] - 2024-08-23
### Added
- [Feature Preview - beta] Support for `client.workflows.triggers`.

## [7.54.18] - 2024-08-26
### Added
- When retrieving datapoints, `instance_id` is now set on the objects (for time series created
through Data Modelling).

## [7.54.17] - 2024-08-22
### Added
- [Feature Preview] Added `ExtractorExtension` model of the Core Model.

## [7.54.16] - 2024-08-22
### Added
- Added new LocationFiltersAcl capability.

## [7.54.15] - 2024-08-21
### Fixed
- [Feature Preview] Updated the Core Model to latest version.

## [7.54.14] - 2024-08-19
### Fixed
- [Feature Preview - alpha] fix `files.upload_content`, `files.upload_content_bytes` and
`files.multipart_upload_content_session`

## [7.54.13] - 2024-08-13
### Added
- [Feature Preview - alpha] Support for `instanceId` in the `client.files.retrieve`, `client.files.retrieve_multiple`,
`client.files.update`, `client.files.retrieve_download_urls`, `client.files.download_bytes`, `client.files.download_to_path`,
`client.files.download`.
- [Feature Preview - alpha] Add three new methods for uploading content: `client.files.upload_content`,
`client.files.download`.
- [Feature Preview - alpha] Add three new methods for uploading content: `client.files.upload_content`,
`client.files.upload_content_bytes`, `client.files.multipart_upload_content_session`.

This is an experimental feature and may change without warning.
Expand Down
5 changes: 4 additions & 1 deletion cognite/client/_api/datapoint_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
NumericDatapoint,
StringDatapoint,
)
from cognite.client.data_classes.data_modeling.ids import NodeId
from cognite.client.data_classes.datapoints import (
_INT_AGGREGATES,
Aggregate,
Expand Down Expand Up @@ -462,15 +463,17 @@ def get_datapoints_from_proto(res: DataPointListItem) -> DatapointsAny:
return cast(DatapointsAny, [])


def get_ts_info_from_proto(res: DataPointListItem) -> dict[str, int | str | bool | None]:
def get_ts_info_from_proto(res: DataPointListItem) -> dict[str, int | str | bool | NodeId | None]:
# Note: When 'unit_external_id' is returned, regular 'unit' is ditched
instance_id = NodeId(res.instanceId.space, res.instanceId.externalId) if res.instanceId else None
return {
"id": res.id,
"external_id": res.externalId,
"is_string": res.isString,
"is_step": res.isStep,
"unit": res.unit,
"unit_external_id": res.unitExternalId,
"instance_id": instance_id,
}


Expand Down
21 changes: 10 additions & 11 deletions cognite/client/_api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
from typing import Any, BinaryIO, Iterator, Sequence, TextIO, cast, overload
from urllib.parse import urljoin, urlparse

from requests import Response

from cognite.client._api_client import APIClient
from cognite.client._constants import _RUNNING_IN_BROWSER, DEFAULT_LIMIT_READ
from cognite.client.data_classes import (
Expand Down Expand Up @@ -464,15 +462,15 @@ def upload_content(
path: str,
external_id: str | None = None,
instance_id: NodeId | None = None,
) -> FileMetadata | FileMetadataList:
) -> FileMetadata:
"""`Upload a file content <https://developer.cognite.com/api#tag/Files/operation/getUploadLink>`_
Args:
path (str): Path to the file you wish to upload. If path is a directory, this method will upload all files in that directory.
external_id (str | None): The external ID provided by the client. Must be unique within the project.
instance_id (NodeId | None): Instance ID of the file.
Returns:
FileMetadata | FileMetadataList: No description.
FileMetadata: No description.
"""
fh: bytes | BufferedReader
if os.path.isfile(path):
Expand Down Expand Up @@ -650,19 +648,20 @@ def upload_content_bytes(

try:
res = self._post(
url_path=f"{self._RESOURCE_PATH}/uploadlink", json=identifiers.as_dicts()[0], headers=headers
url_path=f"{self._RESOURCE_PATH}/uploadlink",
json={"items": identifiers.as_dicts()},
headers=headers,
)
except CogniteAPIError as e:
if e.code == 403:
raise CogniteAuthorizationError(message=e.message, code=e.code, x_request_id=e.x_request_id) from e
raise

file_metadata = self._upload_bytes(content, res)
file_metadata = self._upload_bytes(content, res.json()["items"][0])

return file_metadata

def _upload_bytes(self, content: bytes | TextIO | BinaryIO, res: Response) -> FileMetadata:
returned_file_metadata = res.json()
def _upload_bytes(self, content: bytes | TextIO | BinaryIO, returned_file_metadata: dict) -> FileMetadata:
upload_url = returned_file_metadata["uploadUrl"]
if urlparse(upload_url).netloc:
full_upload_url = upload_url
Expand Down Expand Up @@ -766,7 +765,7 @@ def upload_bytes(
raise CogniteAuthorizationError(message=msg, code=e.code, x_request_id=e.x_request_id) from e
raise

return self._upload_bytes(content, res)
return self._upload_bytes(content, res.json())

def multipart_upload_session(
self,
Expand Down Expand Up @@ -911,7 +910,7 @@ def multipart_upload_content_session(
try:
res = self._post(
url_path=f"{self._RESOURCE_PATH}/multiuploadlink",
json=identifiers.as_dicts()[0],
json={"items": identifiers.as_dicts()},
params={"parts": parts},
headers=headers,
)
Expand All @@ -920,7 +919,7 @@ def multipart_upload_content_session(
raise CogniteAuthorizationError(message=e.message, code=e.code, x_request_id=e.x_request_id) from e
raise

returned_file_metadata = res.json()
returned_file_metadata = res.json()["items"][0]
upload_urls = returned_file_metadata["uploadUrls"]
upload_id = returned_file_metadata["uploadId"]

Expand Down
7 changes: 6 additions & 1 deletion cognite/client/_api/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ def call(
external_id: str | None = None,
data: dict | None = None,
wait: bool = True,
nonce: str | None = None,
) -> FunctionCall:
"""`Call a function by its ID or external ID. <https://developer.cognite.com/api#tag/Function-calls/operation/postFunctionsCall>`_.
Expand All @@ -529,6 +530,10 @@ def call(
external_id (str | None): External ID
data (dict | None): Input data to the function (JSON serializable). This data is passed deserialized into the function through one of the arguments called data. **WARNING:** Secrets or other confidential information should not be passed via this argument. There is a dedicated `secrets` argument in FunctionsAPI.create() for this purpose.'
wait (bool): Wait until the function call is finished. Defaults to True.
nonce (str | None): Nonce retrieved from sessions API when creating a session. This will be used to bind the session before executing the function. If not provided, a new session will be created based on the client credentials.
Tip:
You can create a session via the Sessions API, using the client.iam.session.create() method.
Returns:
FunctionCall: A function call object.
Expand All @@ -550,7 +555,7 @@ def call(
"""
identifier = IdentifierSequence.load(ids=id, external_ids=external_id).as_singleton()[0]
id = _get_function_internal_id(self._cognite_client, identifier)
nonce = create_session_and_return_nonce(self._cognite_client, api_name="Functions API")
nonce = nonce or create_session_and_return_nonce(self._cognite_client, api_name="Functions API")

if data is None:
data = {}
Expand Down
51 changes: 45 additions & 6 deletions cognite/client/_api/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import warnings
from itertools import groupby
from operator import itemgetter
from typing import TYPE_CHECKING, Any, Dict, Iterable, Sequence, Union, overload
from typing import TYPE_CHECKING, Any, Dict, Iterable, Literal, Sequence, Union, overload

from typing_extensions import TypeAlias

Expand Down Expand Up @@ -34,7 +34,13 @@
SpaceIDScope,
UnknownAcl,
)
from cognite.client.data_classes.iam import GroupWrite, SecurityCategoryWrite, SessionStatus, TokenInspection
from cognite.client.data_classes.iam import (
GroupWrite,
SecurityCategoryWrite,
SessionStatus,
SessionType,
TokenInspection,
)
from cognite.client.utils._identifier import IdentifierSequence

if TYPE_CHECKING:
Expand Down Expand Up @@ -528,20 +534,53 @@ def __init__(self, config: ClientConfig, api_version: str | None, cognite_client
super().__init__(config, api_version, cognite_client)
self._LIST_LIMIT = 100

def create(self, client_credentials: ClientCredentials | None = None) -> CreatedSession:
def create(
self,
client_credentials: ClientCredentials | None = None,
session_type: SessionType | Literal["DEFAULT"] = "DEFAULT",
) -> CreatedSession:
"""`Create a session. <https://developer.cognite.com/api#tag/Sessions/operation/createSessions>`_
Args:
client_credentials (ClientCredentials | None): The client credentials to create the session. If set to None, a session will be created using the credentials used to instantiate -this- CogniteClient object. If that was done using a token, a session will be created using token exchange. Similarly, if the credentials were client credentials, a session will be created using client credentials. This method does not work when using client certificates (not supported server-side).
client_credentials (ClientCredentials | None): The client credentials to create the session. This is required
if session_type is set to 'CLIENT_CREDENTIALS'.
session_type (SessionType | Literal['DEFAULT']): The type of session to create. Can be
either 'CLIENT_CREDENTIALS', 'TOKEN_EXCHANGE', 'ONESHOT_TOKEN_EXCHANGE' or 'DEFAULT'.
Defaults to 'DEFAULT' which will use -this- CogniteClient object to create the session.
If this client was created using a token, 'TOKEN_EXCHANGE' will be used, and if
this client was created using client credentials, 'CLIENT_CREDENTIALS' will be used.
Session Types:
* **client_credentials**: Credentials for a session using client credentials from an identity provider.
* **token_exchange**: Credentials for a session using token exchange to reuse the user's credentials.
* **one_shot_token_exchange**: Credentials for a session using one-shot token exchange to reuse the user's credentials. One-shot sessions are short-lived sessions that are not refreshed and do not require support for token exchange from the identity provider.
Returns:
CreatedSession: The object with token inspection details.
"""
if client_credentials is None and isinstance((creds := self._config.credentials), OAuthClientCredentials):
if client_credentials is None and isinstance(creds := self._config.credentials, OAuthClientCredentials):
client_credentials = ClientCredentials(creds.client_id, creds.client_secret)

items = {"tokenExchange": True} if client_credentials is None else client_credentials.dump(camel_case=True)
session_type_up = session_type.upper()
if session_type_up == "DEFAULT": # For backwards compatibility after session_type was introduced
items = {"tokenExchange": True} if client_credentials is None else client_credentials.dump(camel_case=True)

elif session_type_up == "CLIENT_CREDENTIALS":
if client_credentials is None:
raise ValueError(
"For session_type='CLIENT_CREDENTIALS', either `client_credentials` must be provided OR "
"this client must be using OAuthClientCredentials"
)
items = client_credentials.dump(camel_case=True)

elif session_type_up == "TOKEN_EXCHANGE":
items = {"tokenExchange": True}

elif session_type_up == "ONESHOT_TOKEN_EXCHANGE":
items = {"oneshotTokenExchange": True}
else:
raise ValueError(f"Session type not understood: {session_type}")
return CreatedSession.load(self._post(self._RESOURCE_PATH, {"items": [items]}).json()["items"][0])

@overload
Expand Down
Loading

0 comments on commit 5d66596

Please sign in to comment.