Skip to content

Commit c63d047

Browse files
committed
feat(gooddata-sdk): [AUTO] Add typed JsonApiParameter entity with NumberParameterDefinition
1 parent 4cb8139 commit c63d047

7 files changed

Lines changed: 608 additions & 0 deletions

File tree

packages/gooddata-sdk/src/gooddata_sdk/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@
264264
CatalogDependentEntitiesResponse,
265265
CatalogEntityIdentifier,
266266
)
267+
from gooddata_sdk.catalog.workspace.entity_model.parameter import (
268+
CatalogNumberParameterDefinition,
269+
CatalogWorkspaceParameter,
270+
CatalogWorkspaceParameterConstraints,
271+
)
267272
from gooddata_sdk.catalog.workspace.entity_model.user_data_filter import (
268273
CatalogUserDataFilter,
269274
CatalogUserDataFilterAttributes,

packages/gooddata-sdk/src/gooddata_sdk/catalog/catalog_service_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55

66
from gooddata_api_client import apis
7+
from gooddata_api_client.api.parameters_api import ParametersApi
78
from gooddata_api_client.model.json_api_organization_out_document import JsonApiOrganizationOutDocument
89

910
from gooddata_sdk.catalog.organization.entity_model.organization import CatalogOrganization
@@ -19,6 +20,7 @@ def __init__(self, api_client: GoodDataApiClient) -> None:
1920
self._layout_api: apis.LayoutApi = api_client.layout_api
2021
self._actions_api: apis.ActionsApi = api_client.actions_api
2122
self._user_management_api: apis.UserManagementApi = api_client.user_management_api
23+
self._parameters_api: ParametersApi = api_client.parameters_api
2224

2325
def get_organization(self) -> CatalogOrganization:
2426
# The generated client does work properly with redirecting APIs
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# (C) 2024 GoodData Corporation
2+
from __future__ import annotations
3+
4+
from typing import Any
5+
6+
import attrs
7+
from gooddata_api_client.model.json_api_parameter_in import JsonApiParameterIn
8+
from gooddata_api_client.model.json_api_parameter_in_document import JsonApiParameterInDocument
9+
from gooddata_api_client.model.json_api_parameter_patch import JsonApiParameterPatch
10+
from gooddata_api_client.model.json_api_parameter_patch_document import JsonApiParameterPatchDocument
11+
from gooddata_api_client.model.json_api_parameter_post_optional_id import JsonApiParameterPostOptionalId
12+
from gooddata_api_client.model.json_api_parameter_post_optional_id_document import (
13+
JsonApiParameterPostOptionalIdDocument,
14+
)
15+
from gooddata_api_client.model.number_constraints import NumberConstraints
16+
from gooddata_api_client.model.number_parameter_definition import NumberParameterDefinition
17+
18+
from gooddata_sdk.catalog.base import Base
19+
20+
21+
@attrs.define(kw_only=True)
22+
class CatalogWorkspaceParameterConstraints(Base):
23+
"""Constraints for a number parameter."""
24+
25+
min: float | None = None
26+
max: float | None = None
27+
28+
@staticmethod
29+
def client_class() -> type[NumberConstraints]:
30+
return NumberConstraints
31+
32+
def as_api_model(self) -> NumberConstraints:
33+
kwargs: dict[str, Any] = {}
34+
if self.min is not None:
35+
kwargs["min"] = self.min
36+
if self.max is not None:
37+
kwargs["max"] = self.max
38+
return NumberConstraints(_check_type=False, **kwargs)
39+
40+
41+
@attrs.define(kw_only=True)
42+
class CatalogNumberParameterDefinition(Base):
43+
"""Definition of a number-typed parameter."""
44+
45+
default_value: float
46+
type: str = "NUMBER"
47+
constraints: CatalogWorkspaceParameterConstraints | None = None
48+
49+
@staticmethod
50+
def client_class() -> type[NumberParameterDefinition]:
51+
return NumberParameterDefinition
52+
53+
def as_api_model(self) -> NumberParameterDefinition:
54+
kwargs: dict[str, Any] = {}
55+
if self.constraints is not None:
56+
kwargs["constraints"] = self.constraints.as_api_model()
57+
return NumberParameterDefinition(
58+
default_value=self.default_value,
59+
_check_type=False,
60+
**kwargs,
61+
)
62+
63+
64+
@attrs.define(kw_only=True)
65+
class CatalogWorkspaceParameter(Base):
66+
"""A workspace-scoped parameter entity."""
67+
68+
id: str | None = None
69+
definition: CatalogNumberParameterDefinition | None = None
70+
title: str | None = None
71+
description: str | None = None
72+
tags: list[str] = attrs.field(factory=list)
73+
74+
@staticmethod
75+
def client_class() -> type[JsonApiParameterIn]:
76+
return JsonApiParameterIn
77+
78+
@classmethod
79+
def init(
80+
cls,
81+
*,
82+
parameter_id: str,
83+
default_value: float,
84+
title: str | None = None,
85+
description: str | None = None,
86+
tags: list[str] | None = None,
87+
constraints: CatalogWorkspaceParameterConstraints | None = None,
88+
) -> CatalogWorkspaceParameter:
89+
definition = CatalogNumberParameterDefinition(
90+
default_value=default_value,
91+
constraints=constraints,
92+
)
93+
return cls(
94+
id=parameter_id,
95+
definition=definition,
96+
title=title,
97+
description=description,
98+
tags=tags or [],
99+
)
100+
101+
@classmethod
102+
def from_api(cls, entity: Any) -> CatalogWorkspaceParameter:
103+
"""Deserialize from an API model object or a snake_case dict.
104+
105+
When entity is an API model object (ModelNormal), its internal
106+
_data_store uses snake_case keys. When it is a plain dict produced
107+
by ``model.to_dict()``, the keys are also snake_case (the default).
108+
"""
109+
attrs_data = entity.get("attributes") or {}
110+
raw_definition = attrs_data.get("definition")
111+
definition: CatalogNumberParameterDefinition | None = None
112+
if raw_definition is not None:
113+
raw_constraints = raw_definition.get("constraints")
114+
constraints: CatalogWorkspaceParameterConstraints | None = None
115+
if raw_constraints is not None:
116+
constraints = CatalogWorkspaceParameterConstraints(
117+
min=raw_constraints.get("min"),
118+
max=raw_constraints.get("max"),
119+
)
120+
definition = CatalogNumberParameterDefinition(
121+
default_value=raw_definition.get("default_value", 0.0),
122+
type=raw_definition.get("type", "NUMBER"),
123+
constraints=constraints,
124+
)
125+
raw_tags = attrs_data.get("tags")
126+
return cls(
127+
id=entity.get("id"),
128+
definition=definition,
129+
title=attrs_data.get("title"),
130+
description=attrs_data.get("description"),
131+
tags=raw_tags if raw_tags is not None else [],
132+
)
133+
134+
def as_post_document(self) -> JsonApiParameterPostOptionalIdDocument:
135+
"""Serialize to document for POST (create)."""
136+
attributes = self._build_attributes()
137+
kwargs: dict[str, Any] = {}
138+
if self.id is not None:
139+
kwargs["id"] = self.id
140+
data = JsonApiParameterPostOptionalId(
141+
type="parameter",
142+
attributes=attributes,
143+
_check_type=False,
144+
**kwargs,
145+
)
146+
return JsonApiParameterPostOptionalIdDocument(data=data, _check_type=False)
147+
148+
def as_put_document(self) -> JsonApiParameterInDocument:
149+
"""Serialize to document for PUT (full update)."""
150+
attributes = self._build_attributes()
151+
data = JsonApiParameterIn(
152+
id=self.id,
153+
type="parameter",
154+
attributes=attributes,
155+
_check_type=False,
156+
)
157+
return JsonApiParameterInDocument(data=data, _check_type=False)
158+
159+
def as_patch_document(self) -> JsonApiParameterPatchDocument:
160+
"""Serialize to document for PATCH."""
161+
attributes = self._build_attributes()
162+
data = JsonApiParameterPatch(
163+
id=self.id,
164+
type="parameter",
165+
attributes=attributes,
166+
_check_type=False,
167+
)
168+
return JsonApiParameterPatchDocument(data=data, _check_type=False)
169+
170+
def _build_attributes(self) -> dict[str, Any]:
171+
attributes: dict[str, Any] = {}
172+
if self.definition is not None:
173+
definition_dict: dict[str, Any] = {
174+
"defaultValue": self.definition.default_value,
175+
"type": self.definition.type,
176+
}
177+
if self.definition.constraints is not None:
178+
constraints_dict: dict[str, Any] = {}
179+
if self.definition.constraints.min is not None:
180+
constraints_dict["min"] = self.definition.constraints.min
181+
if self.definition.constraints.max is not None:
182+
constraints_dict["max"] = self.definition.constraints.max
183+
if constraints_dict:
184+
definition_dict["constraints"] = constraints_dict
185+
attributes["definition"] = definition_dict
186+
if self.title is not None:
187+
attributes["title"] = self.title
188+
if self.description is not None:
189+
attributes["description"] = self.description
190+
if self.tags:
191+
attributes["tags"] = self.tags
192+
return attributes

packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/service.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
CatalogFilterView,
3535
CatalogFilterViewDocument,
3636
)
37+
from gooddata_sdk.catalog.workspace.entity_model.parameter import CatalogWorkspaceParameter
3738
from gooddata_sdk.catalog.workspace.entity_model.user_data_filter import (
3839
CatalogUserDataFilter,
3940
CatalogUserDataFilterDocument,
@@ -676,6 +677,97 @@ def provision_workspace_with_locales(
676677
self.put_declarative_workspace_data_filters(new_filters)
677678

678679
self.put_declarative_workspace(new_workspace.id, new_workspace_content)
680+
681+
# Parameters methods
682+
683+
def list_workspace_parameters(self, workspace_id: str) -> list[CatalogWorkspaceParameter]:
684+
"""Returns a list of all parameters defined in a workspace.
685+
686+
Args:
687+
workspace_id (str):
688+
Workspace identification string e.g. "demo"
689+
690+
Returns:
691+
list[CatalogWorkspaceParameter]:
692+
List of workspace parameters.
693+
"""
694+
get_parameters = functools.partial(
695+
self._parameters_api.get_all_entities_parameters,
696+
workspace_id,
697+
_check_return_type=False,
698+
)
699+
parameters = load_all_entities(get_parameters)
700+
return [CatalogWorkspaceParameter.from_api(p) for p in parameters.data]
701+
702+
def get_workspace_parameter(self, workspace_id: str, parameter_id: str) -> CatalogWorkspaceParameter:
703+
"""Get an individual workspace parameter.
704+
705+
Args:
706+
workspace_id (str):
707+
Workspace identification string e.g. "demo"
708+
parameter_id (str):
709+
Parameter identification string e.g. "my-param"
710+
711+
Returns:
712+
CatalogWorkspaceParameter:
713+
Catalog workspace parameter object.
714+
"""
715+
response = self._parameters_api.get_entity_parameters(workspace_id, parameter_id, _check_return_type=False)
716+
return CatalogWorkspaceParameter.from_api(response.data)
717+
718+
def create_or_update_workspace_parameter(
719+
self, workspace_id: str, parameter: CatalogWorkspaceParameter
720+
) -> CatalogWorkspaceParameter:
721+
"""Create a new workspace parameter or overwrite an existing one with the same id.
722+
723+
Args:
724+
workspace_id (str):
725+
Workspace identification string e.g. "demo"
726+
parameter (CatalogWorkspaceParameter):
727+
Catalog workspace parameter object to be created or updated.
728+
729+
Returns:
730+
CatalogWorkspaceParameter:
731+
The created or updated catalog workspace parameter.
732+
"""
733+
try:
734+
self.get_workspace_parameter(workspace_id, parameter.id)
735+
response = self._parameters_api.update_entity_parameters(
736+
workspace_id,
737+
parameter.id,
738+
parameter.as_put_document(),
739+
_check_return_type=False,
740+
)
741+
except NotFoundException:
742+
response = self._parameters_api.create_entity_parameters(
743+
workspace_id,
744+
parameter.as_post_document(),
745+
_check_return_type=False,
746+
)
747+
return CatalogWorkspaceParameter.from_api(response.data)
748+
749+
def delete_workspace_parameter(self, workspace_id: str, parameter_id: str) -> None:
750+
"""Delete a workspace parameter.
751+
752+
Args:
753+
workspace_id (str):
754+
Workspace identification string e.g. "demo"
755+
parameter_id (str):
756+
Parameter identification string e.g. "my-param"
757+
758+
Returns:
759+
None
760+
761+
Raises:
762+
ValueError:
763+
Parameter does not exist.
764+
"""
765+
try:
766+
self._parameters_api.delete_entity_parameters(workspace_id, parameter_id, _check_return_type=False)
767+
except NotFoundException:
768+
raise ValueError(
769+
f"Cannot delete parameter {parameter_id} from workspace {workspace_id}. This parameter does not exist."
770+
)
679771
# TODO - uncomment the copy after the fix is fully released
680772
# - list_workspace_settings is failing with 500 error too :-(
681773
# Copy settings from source workspace

packages/gooddata-sdk/src/gooddata_sdk/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import gooddata_api_client as api_client
99
import requests
1010
from gooddata_api_client import apis
11+
from gooddata_api_client.api.parameters_api import ParametersApi
1112

1213
from gooddata_sdk import __version__
1314
from gooddata_sdk.utils import HttpMethod
@@ -71,6 +72,7 @@ def __init__(
7172
self._actions_api = apis.ActionsApi(self._api_client)
7273
self._user_management_api = apis.UserManagementApi(self._api_client)
7374
self._appearance_api = apis.AppearanceApi(self._api_client)
75+
self._parameters_api = ParametersApi(self._api_client)
7476
self._executions_cancellable = executions_cancellable
7577

7678
def _do_post_request(
@@ -158,6 +160,10 @@ def user_management_api(self) -> apis.UserManagementApi:
158160
def appearance_api(self) -> apis.AppearanceApi:
159161
return self._appearance_api
160162

163+
@property
164+
def parameters_api(self) -> ParametersApi:
165+
return self._parameters_api
166+
161167
@property
162168
def executions_cancellable(self) -> bool:
163169
return self._executions_cancellable

0 commit comments

Comments
 (0)