Skip to content
Closed
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
5 changes: 5 additions & 0 deletions packages/gooddata-sdk/src/gooddata_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import logging

from gooddata_sdk._version import __version__
from gooddata_sdk.catalog.analytics_catalog.model.trending_objects import (
CatalogTrendingObjectItem,
CatalogTrendingObjectsResult,
)
from gooddata_sdk.catalog.appearance.entity_model.color_palette import (
CatalogColorPalette,
CatalogColorPaletteAttributes,
Expand All @@ -16,6 +20,7 @@
CatalogThemeAttributes,
)
from gooddata_sdk.catalog.appearance.service import CatalogAppearanceService
from gooddata_sdk.catalog.smart_functions.service import CatalogSmartFunctionsService
from gooddata_sdk.catalog.data_source.action_model.requests.ldm_request import (
CatalogGenerateLdmRequest,
CatalogPdmLdmRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# (C) 2024 GoodData Corporation
from gooddata_sdk.catalog.analytics_catalog.model.trending_objects import (
CatalogTrendingObjectItem,
CatalogTrendingObjectsResult,
)

__all__ = [
"CatalogTrendingObjectItem",
"CatalogTrendingObjectsResult",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# (C) 2024 GoodData Corporation
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# (C) 2024 GoodData Corporation
from __future__ import annotations

from datetime import datetime

import attrs


@attrs.define(kw_only=True)
class CatalogTrendingObjectItem:
"""Represents a trending object in the Analytics Catalog."""

id: str
tags: list[str]
title: str
type: str
usage_count: int
workspace_id: str
created_at: datetime | None = None
created_by: str | None = None
dataset_id: str | None = None
dataset_title: str | None = None
dataset_type: str | None = None
description: str | None = None
is_hidden: bool | None = None
is_hidden_from_kda: bool | None = None
metric_type: str | None = None
modified_at: datetime | None = None
modified_by: str | None = None
visualization_url: str | None = None

@classmethod
def from_api_model(cls, api_model: object) -> CatalogTrendingObjectItem:
return cls(
id=api_model.id,
tags=list(api_model.tags),
title=api_model.title,
type=api_model.type,
usage_count=api_model.usage_count,
workspace_id=api_model.workspace_id,
created_at=getattr(api_model, "created_at", None),
created_by=getattr(api_model, "created_by", None),
dataset_id=getattr(api_model, "dataset_id", None),
dataset_title=getattr(api_model, "dataset_title", None),
dataset_type=getattr(api_model, "dataset_type", None),
description=getattr(api_model, "description", None),
is_hidden=getattr(api_model, "is_hidden", None),
is_hidden_from_kda=getattr(api_model, "is_hidden_from_kda", None),
metric_type=getattr(api_model, "metric_type", None),
modified_at=getattr(api_model, "modified_at", None),
modified_by=getattr(api_model, "modified_by", None),
visualization_url=getattr(api_model, "visualization_url", None),
)


@attrs.define(kw_only=True)
class CatalogTrendingObjectsResult:
"""Represents the result of a trending objects query."""

objects: list[CatalogTrendingObjectItem] = attrs.field(factory=list)

@classmethod
def from_api_model(cls, api_model: object) -> CatalogTrendingObjectsResult:
return cls(
objects=[CatalogTrendingObjectItem.from_api_model(item) for item in api_model.objects],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# (C) 2024 GoodData Corporation
from gooddata_sdk.catalog.smart_functions.service import CatalogSmartFunctionsService

__all__ = [
"CatalogSmartFunctionsService",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# (C) 2024 GoodData Corporation
from __future__ import annotations

from gooddata_api_client import apis

from gooddata_sdk.catalog.analytics_catalog.model.trending_objects import (
CatalogTrendingObjectsResult,
)
from gooddata_sdk.client import GoodDataApiClient


class CatalogSmartFunctionsService:
"""Service for accessing AI smart functions in the Analytics Catalog."""

def __init__(self, api_client: GoodDataApiClient) -> None:
self._client = api_client
self._smart_functions_api: apis.SmartFunctionsApi = api_client.smart_functions_api

def get_trending_objects(self, workspace_id: str) -> CatalogTrendingObjectsResult:
"""Return trending objects for the given workspace.

Args:
workspace_id: Workspace identifier.

Returns:
CatalogTrendingObjectsResult containing a list of trending objects.
"""
result = self._smart_functions_api.trending_objects(
workspace_id=workspace_id,
_check_return_type=False,
)
return CatalogTrendingObjectsResult.from_api_model(result)
5 changes: 5 additions & 0 deletions packages/gooddata-sdk/src/gooddata_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def __init__(
self._actions_api = apis.ActionsApi(self._api_client)
self._user_management_api = apis.UserManagementApi(self._api_client)
self._appearance_api = apis.AppearanceApi(self._api_client)
self._smart_functions_api = apis.SmartFunctionsApi(self._api_client)
self._executions_cancellable = executions_cancellable

def _do_post_request(
Expand Down Expand Up @@ -158,6 +159,10 @@ def user_management_api(self) -> apis.UserManagementApi:
def appearance_api(self) -> apis.AppearanceApi:
return self._appearance_api

@property
def smart_functions_api(self) -> apis.SmartFunctionsApi:
return self._smart_functions_api

@property
def executions_cancellable(self) -> bool:
return self._executions_cancellable
6 changes: 6 additions & 0 deletions packages/gooddata-sdk/src/gooddata_sdk/sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path

from gooddata_sdk.catalog.appearance.service import CatalogAppearanceService
from gooddata_sdk.catalog.smart_functions.service import CatalogSmartFunctionsService
from gooddata_sdk.catalog.data_source.service import CatalogDataSourceService
from gooddata_sdk.catalog.export.service import ExportService
from gooddata_sdk.catalog.organization.service import CatalogOrganizationService
Expand Down Expand Up @@ -89,6 +90,7 @@ def __init__(self, client: GoodDataApiClient) -> None:
self._support = SupportService(self._client)
self._catalog_permission = CatalogPermissionService(self._client)
self._export = ExportService(self._client)
self._catalog_smart_functions = CatalogSmartFunctionsService(self._client)

@property
def catalog_appearance(self) -> CatalogAppearanceService:
Expand Down Expand Up @@ -138,6 +140,10 @@ def catalog_permission(self) -> CatalogPermissionService:
def export(self) -> ExportService:
return self._export

@property
def catalog_smart_functions(self) -> CatalogSmartFunctionsService:
return self._catalog_smart_functions

@property
def client(self) -> GoodDataApiClient:
return self._client
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# (C) 2024 GoodData Corporation
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# (C) 2024 GoodData Corporation
from __future__ import annotations

from pathlib import Path
from unittest.mock import MagicMock

import pytest
from gooddata_sdk import CatalogTrendingObjectItem, CatalogTrendingObjectsResult, GoodDataSdk
from tests_support.vcrpy_utils import get_vcr

gd_vcr = get_vcr()

_fixtures_dir = Path(__file__).parent / "fixtures"


def _make_mock_item(
id: str = "obj-1",
title: str = "Revenue Dashboard",
type: str = "dashboard",
usage_count: int = 42,
workspace_id: str = "ws-1",
tags: list | None = None,
) -> MagicMock:
item = MagicMock()
item.id = id
item.title = title
item.type = type
item.usage_count = usage_count
item.workspace_id = workspace_id
item.tags = tags or []
# optional fields not set
item.configure_mock(
**{
attr: None
for attr in [
"created_at",
"created_by",
"dataset_id",
"dataset_title",
"dataset_type",
"description",
"is_hidden",
"is_hidden_from_kda",
"metric_type",
"modified_at",
"modified_by",
"visualization_url",
]
}
)
return item


def test_catalog_trending_object_item_from_api_model():
"""Unit test: CatalogTrendingObjectItem.from_api_model converts required fields correctly."""
mock_item = _make_mock_item(
id="dash-1",
title="My Dashboard",
type="dashboard",
usage_count=10,
workspace_id="workspace-123",
tags=["finance", "monthly"],
)

result = CatalogTrendingObjectItem.from_api_model(mock_item)

assert result.id == "dash-1"
assert result.title == "My Dashboard"
assert result.type == "dashboard"
assert result.usage_count == 10
assert result.workspace_id == "workspace-123"
assert result.tags == ["finance", "monthly"]
assert result.created_at is None
assert result.description is None


def test_catalog_trending_objects_result_from_api_model():
"""Unit test: CatalogTrendingObjectsResult.from_api_model converts a list of items correctly."""
item1 = _make_mock_item(id="obj-1", title="Dashboard A", type="dashboard", usage_count=5, workspace_id="ws-1")
item2 = _make_mock_item(id="obj-2", title="Metric B", type="metric", usage_count=3, workspace_id="ws-1")

mock_result = MagicMock()
mock_result.objects = [item1, item2]

result = CatalogTrendingObjectsResult.from_api_model(mock_result)

assert len(result.objects) == 2
assert result.objects[0].id == "obj-1"
assert result.objects[1].id == "obj-2"
assert result.objects[0].type == "dashboard"
assert result.objects[1].type == "metric"


def test_catalog_trending_objects_result_empty():
"""Unit test: CatalogTrendingObjectsResult.from_api_model handles empty objects list."""
mock_result = MagicMock()
mock_result.objects = []

result = CatalogTrendingObjectsResult.from_api_model(mock_result)

assert result.objects == []


@gd_vcr.use_cassette(str(_fixtures_dir / "test_get_trending_objects.yaml"))
def test_get_trending_objects(test_config):
"""Integration test: get_trending_objects returns CatalogTrendingObjectsResult."""
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
workspace_id = test_config["workspace"]

result = sdk.catalog_smart_functions.get_trending_objects(workspace_id=workspace_id)

assert isinstance(result, CatalogTrendingObjectsResult)
assert isinstance(result.objects, list)
for item in result.objects:
assert isinstance(item, CatalogTrendingObjectItem)
assert item.id
assert item.workspace_id
assert item.usage_count >= 0
Loading