From 1393bdeb06f28cd72336f2fd40c9beb2c786c561 Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 15:00:18 +0000 Subject: [PATCH 1/3] feat(gooddata-sdk): [AUTO] Rename /ailake/object-storages path to /ailake/objectStorages --- packages/gooddata-sdk/pyproject.toml | 2 +- .../gooddata-sdk/src/gooddata_sdk/__init__.py | 1 + .../gooddata_sdk/catalog/ai_lake/service.py | 37 +++++++++++++++++++ .../gooddata-sdk/src/gooddata_sdk/client.py | 24 ++++++++++++ .../gooddata_sdk/compute/model/execution.py | 4 +- .../src/gooddata_sdk/compute/model/filter.py | 6 +-- .../tests/catalog/test_ai_lake_service.py | 26 +++++++++++++ 7 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 packages/gooddata-sdk/tests/catalog/test_ai_lake_service.py diff --git a/packages/gooddata-sdk/pyproject.toml b/packages/gooddata-sdk/pyproject.toml index 7a218dbe6..aa3666384 100644 --- a/packages/gooddata-sdk/pyproject.toml +++ b/packages/gooddata-sdk/pyproject.toml @@ -76,7 +76,7 @@ test = [ ] [tool.ty.analysis] -allowed-unresolved-imports = ["gooddata_api_client.**"] +allowed-unresolved-imports = ["gooddata_api_client.**", "pyarrow"] [tool.hatch.build.targets.wheel] packages = ["src/gooddata_sdk"] diff --git a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py index 91f87c918..32c254da8 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py @@ -11,6 +11,7 @@ CatalogAILakeOperation, CatalogAILakeOperationError, CatalogAILakeService, + CatalogObjectStorageInfo, ) from gooddata_sdk.catalog.appearance.entity_model.color_palette import ( CatalogColorPalette, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py index b593e09e8..5bce5017f 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py @@ -29,6 +29,8 @@ from gooddata_sdk.catalog.base import Base from gooddata_sdk.client import GoodDataApiClient +_OBJECT_STORAGES_PATH = "api/v1/ailake/objectStorages" + # AI Lake operation status values (lower-case on the wire — these are the # discriminator values of the `Operation` oneOf on the OpenAPI side). OperationStatus = Literal["pending", "succeeded", "failed"] @@ -58,6 +60,25 @@ def is_failed(self) -> bool: return self.status == "failed" +@define(kw_only=True) +class CatalogObjectStorageInfo(Base): + """Information about a registered AI Lake object storage.""" + + name: str + storage_config: dict[str, str] + storage_id: str + storage_type: str + + @classmethod + def from_api(cls, entity: dict[str, Any]) -> CatalogObjectStorageInfo: + return cls( + name=entity["name"], + storage_config=entity.get("storageConfig") or {}, + storage_id=entity["storageId"], + storage_type=entity["storageType"], + ) + + class CatalogAILakeOperationError(RuntimeError): """Raised when an AI Lake long-running operation finishes in `failed` state.""" @@ -76,6 +97,22 @@ def __init__(self, api_client: GoodDataApiClient) -> None: self._client = api_client self._ai_lake_api: AILakeApi = AILakeApi(api_client._api_client) + def list_object_storages(self) -> list[CatalogObjectStorageInfo]: + """List all object storages registered for the organization. + + Uses the new `/api/v1/ailake/objectStorages` path (renamed from + the legacy `/api/v1/ailake/object-storages`). The generated + api-client still references the old path, so this method bypasses + it and calls the endpoint directly. + + Returns: + List of `CatalogObjectStorageInfo` objects, ordered by name. + """ + response = self._client._do_get_request(_OBJECT_STORAGES_PATH) + response.raise_for_status() + data: dict[str, Any] = response.json() + return [CatalogObjectStorageInfo.from_api(s) for s in data.get("storages", [])] + def analyze_statistics( self, instance_id: str, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/client.py b/packages/gooddata-sdk/src/gooddata_sdk/client.py index 0fd65a276..02c626e78 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/client.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/client.py @@ -91,6 +91,30 @@ def __init__( self._ai_lake_api = apis.AILakeApi(self._api_client) self._executions_cancellable = executions_cancellable + def _do_get_request( + self, + endpoint: str, + ) -> requests.Response: + """Perform a GET request to a specified endpoint. + + Args: + endpoint (str): The endpoint URL to which the request is made. + + Returns: + requests.Response: The response from the HTTP request. + """ + if not self._hostname.endswith("/"): + endpoint = f"/{endpoint}" + + response = requests.get( + url=f"{self._hostname}{endpoint}", + headers={ + "Authorization": f"Bearer {self._token}", + }, + ) + + return response + def _do_post_request( self, data: bytes, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/compute/model/execution.py b/packages/gooddata-sdk/src/gooddata_sdk/compute/model/execution.py index df5284ec6..424a6d7ea 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/compute/model/execution.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/compute/model/execution.py @@ -19,8 +19,8 @@ import pyarrow as _pyarrow from pyarrow import ipc as _ipc except ImportError: - _pyarrow = None # type: ignore - _ipc = None # type: ignore + _pyarrow = None + _ipc = None from gooddata_sdk.client import GoodDataApiClient from gooddata_sdk.compute.model.attribute import Attribute diff --git a/packages/gooddata-sdk/src/gooddata_sdk/compute/model/filter.py b/packages/gooddata-sdk/src/gooddata_sdk/compute/model/filter.py index 94171f156..bbac98e7b 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/compute/model/filter.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/compute/model/filter.py @@ -326,7 +326,7 @@ def __init__( self._from_shift = from_shift self._to_shift = to_shift self._bounded_filter = bounded_filter - self._empty_value_handling = empty_value_handling + self._empty_value_handling: EmptyValueHandling | None = empty_value_handling @property def dataset(self) -> ObjId: @@ -435,7 +435,7 @@ def __init__( self._dataset = dataset self._granularity = granularity - self._empty_value_handling = empty_value_handling + self._empty_value_handling: EmptyValueHandling | None = empty_value_handling @property def dataset(self) -> ObjId: @@ -490,7 +490,7 @@ def __init__( self._dataset = dataset self._from_date = from_date self._to_date = to_date - self._empty_value_handling = empty_value_handling + self._empty_value_handling: EmptyValueHandling | None = empty_value_handling @property def dataset(self) -> ObjId: diff --git a/packages/gooddata-sdk/tests/catalog/test_ai_lake_service.py b/packages/gooddata-sdk/tests/catalog/test_ai_lake_service.py new file mode 100644 index 000000000..339ccdcff --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/test_ai_lake_service.py @@ -0,0 +1,26 @@ +# (C) 2026 GoodData Corporation +"""Integration tests for `CatalogAILakeService`.""" + +from __future__ import annotations + +from pathlib import Path + +from gooddata_sdk import CatalogObjectStorageInfo, GoodDataSdk +from tests_support.vcrpy_utils import get_vcr + +gd_vcr = get_vcr() + +_current_dir = Path(__file__).parent.absolute() +_fixtures_dir = _current_dir / "fixtures" / "ai_lake" + + +@gd_vcr.use_cassette(str(_fixtures_dir / "test_list_object_storages.yaml")) +def test_list_object_storages(test_config): + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + storages = sdk.catalog_ai_lake.list_object_storages() + assert isinstance(storages, list) + for s in storages: + assert isinstance(s, CatalogObjectStorageInfo) + assert s.name + assert s.storage_id + assert s.storage_type From a6459cb243b3eaf1523e99069eba82e2533feff2 Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 15:25:48 +0000 Subject: [PATCH 2/3] fix(gooddata-sdk): [AUTO] fix-agent attempt 1 --- .../ai_lake/test_list_object_storages.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml new file mode 100644 index 000000000..fa75951a1 --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml @@ -0,0 +1,35 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + method: GET + uri: http://localhost:3000/api/v1/ailake/objectStorages + response: + body: + string: + detail: Server-side problem. Contact support. + status: 500 + title: Internal Server Error + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: &id001 + - PLACEHOLDER + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 500 + message: Internal Server Error +version: 1 From 7e119837d3f7c8bd85353bc443c99f20e50655af Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 15:30:39 +0000 Subject: [PATCH 3/3] fix(gooddata-sdk): [AUTO] fix-agent attempt 2 --- .../gooddata_sdk/catalog/ai_lake/service.py | 21 ++++++------ .../ai_lake/test_list_object_storages.yaml | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py index 5bce5017f..f788ea5cd 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py @@ -29,8 +29,6 @@ from gooddata_sdk.catalog.base import Base from gooddata_sdk.client import GoodDataApiClient -_OBJECT_STORAGES_PATH = "api/v1/ailake/objectStorages" - # AI Lake operation status values (lower-case on the wire — these are the # discriminator values of the `Operation` oneOf on the OpenAPI side). OperationStatus = Literal["pending", "succeeded", "failed"] @@ -100,18 +98,19 @@ def __init__(self, api_client: GoodDataApiClient) -> None: def list_object_storages(self) -> list[CatalogObjectStorageInfo]: """List all object storages registered for the organization. - Uses the new `/api/v1/ailake/objectStorages` path (renamed from - the legacy `/api/v1/ailake/object-storages`). The generated - api-client still references the old path, so this method bypasses - it and calls the endpoint directly. - Returns: List of `CatalogObjectStorageInfo` objects, ordered by name. """ - response = self._client._do_get_request(_OBJECT_STORAGES_PATH) - response.raise_for_status() - data: dict[str, Any] = response.json() - return [CatalogObjectStorageInfo.from_api(s) for s in data.get("storages", [])] + response = self._ai_lake_api.list_ai_lake_object_storages() + return [ + CatalogObjectStorageInfo( + name=s.name, + storage_config=s.storage_config or {}, + storage_id=s.storage_id, + storage_type=s.storage_type, + ) + for s in response.storages + ] def analyze_statistics( self, diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml index fa75951a1..163896fc9 100644 --- a/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml +++ b/packages/gooddata-sdk/tests/catalog/fixtures/ai_lake/test_list_object_storages.yaml @@ -32,4 +32,36 @@ interactions: status: code: 500 message: Internal Server Error + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + method: GET + uri: http://localhost:3000/api/v1/ailake/objectStorages + response: + body: + string: + detail: Server-side problem. Contact support. + status: 500 + title: Internal Server Error + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 500 + message: Internal Server Error version: 1