gooddata-pipelines 1.47.1.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gooddata-pipelines might be problematic. Click here for more details.

Files changed (54) hide show
  1. gooddata_pipelines/__init__.py +59 -0
  2. gooddata_pipelines/_version.py +7 -0
  3. gooddata_pipelines/api/__init__.py +5 -0
  4. gooddata_pipelines/api/exceptions.py +41 -0
  5. gooddata_pipelines/api/gooddata_api.py +309 -0
  6. gooddata_pipelines/api/gooddata_api_wrapper.py +36 -0
  7. gooddata_pipelines/api/gooddata_sdk.py +374 -0
  8. gooddata_pipelines/api/utils.py +43 -0
  9. gooddata_pipelines/backup_and_restore/__init__.py +1 -0
  10. gooddata_pipelines/backup_and_restore/backup_input_processor.py +195 -0
  11. gooddata_pipelines/backup_and_restore/backup_manager.py +430 -0
  12. gooddata_pipelines/backup_and_restore/constants.py +42 -0
  13. gooddata_pipelines/backup_and_restore/csv_reader.py +41 -0
  14. gooddata_pipelines/backup_and_restore/models/__init__.py +1 -0
  15. gooddata_pipelines/backup_and_restore/models/input_type.py +11 -0
  16. gooddata_pipelines/backup_and_restore/models/storage.py +58 -0
  17. gooddata_pipelines/backup_and_restore/models/workspace_response.py +51 -0
  18. gooddata_pipelines/backup_and_restore/storage/__init__.py +1 -0
  19. gooddata_pipelines/backup_and_restore/storage/base_storage.py +18 -0
  20. gooddata_pipelines/backup_and_restore/storage/local_storage.py +37 -0
  21. gooddata_pipelines/backup_and_restore/storage/s3_storage.py +71 -0
  22. gooddata_pipelines/logger/__init__.py +8 -0
  23. gooddata_pipelines/logger/logger.py +115 -0
  24. gooddata_pipelines/provisioning/__init__.py +31 -0
  25. gooddata_pipelines/provisioning/assets/wdf_setting.json +14 -0
  26. gooddata_pipelines/provisioning/entities/__init__.py +1 -0
  27. gooddata_pipelines/provisioning/entities/user_data_filters/__init__.py +1 -0
  28. gooddata_pipelines/provisioning/entities/user_data_filters/models/__init__.py +1 -0
  29. gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py +32 -0
  30. gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py +221 -0
  31. gooddata_pipelines/provisioning/entities/users/__init__.py +1 -0
  32. gooddata_pipelines/provisioning/entities/users/models/__init__.py +1 -0
  33. gooddata_pipelines/provisioning/entities/users/models/permissions.py +242 -0
  34. gooddata_pipelines/provisioning/entities/users/models/user_groups.py +64 -0
  35. gooddata_pipelines/provisioning/entities/users/models/users.py +114 -0
  36. gooddata_pipelines/provisioning/entities/users/permissions.py +153 -0
  37. gooddata_pipelines/provisioning/entities/users/user_groups.py +212 -0
  38. gooddata_pipelines/provisioning/entities/users/users.py +179 -0
  39. gooddata_pipelines/provisioning/entities/workspaces/__init__.py +1 -0
  40. gooddata_pipelines/provisioning/entities/workspaces/models.py +78 -0
  41. gooddata_pipelines/provisioning/entities/workspaces/workspace.py +263 -0
  42. gooddata_pipelines/provisioning/entities/workspaces/workspace_data_filters.py +286 -0
  43. gooddata_pipelines/provisioning/entities/workspaces/workspace_data_parser.py +123 -0
  44. gooddata_pipelines/provisioning/entities/workspaces/workspace_data_validator.py +188 -0
  45. gooddata_pipelines/provisioning/provisioning.py +132 -0
  46. gooddata_pipelines/provisioning/utils/__init__.py +1 -0
  47. gooddata_pipelines/provisioning/utils/context_objects.py +32 -0
  48. gooddata_pipelines/provisioning/utils/exceptions.py +95 -0
  49. gooddata_pipelines/provisioning/utils/utils.py +80 -0
  50. gooddata_pipelines/py.typed +0 -0
  51. gooddata_pipelines-1.47.1.dev1.dist-info/METADATA +85 -0
  52. gooddata_pipelines-1.47.1.dev1.dist-info/RECORD +54 -0
  53. gooddata_pipelines-1.47.1.dev1.dist-info/WHEEL +4 -0
  54. gooddata_pipelines-1.47.1.dev1.dist-info/licenses/LICENSE.txt +1 -277
@@ -0,0 +1,59 @@
1
+ # (C) 2025 GoodData Corporation
2
+
3
+ from ._version import __version__
4
+
5
+ # -------- Backup and Restore --------
6
+ from .backup_and_restore.backup_manager import BackupManager
7
+ from .backup_and_restore.models.storage import (
8
+ BackupRestoreConfig,
9
+ StorageType,
10
+ )
11
+ from .backup_and_restore.storage.local_storage import LocalStorage
12
+ from .backup_and_restore.storage.s3_storage import S3Storage
13
+
14
+ # -------- Provisioning --------
15
+ from .provisioning.entities.user_data_filters.models.udf_models import (
16
+ UserDataFilterFullLoad,
17
+ )
18
+ from .provisioning.entities.user_data_filters.user_data_filters import (
19
+ UserDataFilterProvisioner,
20
+ )
21
+ from .provisioning.entities.users.models.permissions import (
22
+ PermissionFullLoad,
23
+ PermissionIncrementalLoad,
24
+ )
25
+ from .provisioning.entities.users.models.user_groups import (
26
+ UserGroupFullLoad,
27
+ UserGroupIncrementalLoad,
28
+ )
29
+ from .provisioning.entities.users.models.users import (
30
+ UserFullLoad,
31
+ UserIncrementalLoad,
32
+ )
33
+ from .provisioning.entities.users.permissions import PermissionProvisioner
34
+ from .provisioning.entities.users.user_groups import UserGroupProvisioner
35
+ from .provisioning.entities.users.users import UserProvisioner
36
+ from .provisioning.entities.workspaces.models import WorkspaceFullLoad
37
+ from .provisioning.entities.workspaces.workspace import WorkspaceProvisioner
38
+
39
+ __all__ = [
40
+ "BackupManager",
41
+ "BackupRestoreConfig",
42
+ "StorageType",
43
+ "LocalStorage",
44
+ "S3Storage",
45
+ "WorkspaceFullLoad",
46
+ "WorkspaceProvisioner",
47
+ "UserIncrementalLoad",
48
+ "UserGroupIncrementalLoad",
49
+ "PermissionFullLoad",
50
+ "PermissionIncrementalLoad",
51
+ "UserFullLoad",
52
+ "UserGroupFullLoad",
53
+ "UserProvisioner",
54
+ "UserGroupProvisioner",
55
+ "PermissionProvisioner",
56
+ "UserDataFilterProvisioner",
57
+ "UserDataFilterFullLoad",
58
+ "__version__",
59
+ ]
@@ -0,0 +1,7 @@
1
+ # (C) 2025 GoodData Corporation
2
+ from importlib import metadata
3
+
4
+ try:
5
+ __version__ = metadata.version("gooddata-pipelines")
6
+ except metadata.PackageNotFoundError:
7
+ __version__ = "unknown-version"
@@ -0,0 +1,5 @@
1
+ # (C) 2025 GoodData Corporation
2
+
3
+ from .gooddata_api_wrapper import GoodDataApi
4
+
5
+ __all__ = ["GoodDataApi"]
@@ -0,0 +1,41 @@
1
+ # (C) 2025 GoodData Corporation
2
+
3
+ """Exception class for Panther operations.
4
+
5
+ This module defines the internally used `PantherException` class, which is used
6
+ to handle exceptions that occur during operations related to the Panther SDK or
7
+ GoodData Cloud API.
8
+ """
9
+
10
+
11
+ class GoodDataApiException(Exception):
12
+ """Exception raised during Panther operations.
13
+
14
+ This exception is used to indicate errors that occur during operations
15
+ related to interactions with the GoodData Python SDK or GoodData Cloud API.
16
+ It can include additional context provided through keyword arguments.
17
+ """
18
+
19
+ def __init__(self, message: str, **kwargs: str) -> None:
20
+ """Raise a PantherException with a message and optional context.
21
+
22
+ Args:
23
+ message (str): The error message describing the exception.
24
+ **kwargs: Additional context for the exception, such as HTTP status,
25
+ API endpoint, and HTTP method or any other relevant information.
26
+ """
27
+
28
+ super().__init__(message)
29
+ self.error_message: str = message
30
+
31
+ # Set default values for attributes.
32
+ # TODO: Consider if the defaults for these are still needed
33
+ # - the values were necessary for log schema implementations, which
34
+ # are not used anymore.
35
+ self.http_status: str = "500 Internal Server Error"
36
+ self.api_endpoint: str = "NA"
37
+ self.http_method: str = "NA"
38
+
39
+ # Set attributes from kwargs.
40
+ for key, value in kwargs.items():
41
+ setattr(self, key, value)
@@ -0,0 +1,309 @@
1
+ # (C) 2025 GoodData Corporation
2
+
3
+ """Interaction with GoodData Cloud via the direct API calls."""
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+ import requests
9
+
10
+ # TODO: Limit the use of "typing.Any". Improve readability by using either models
11
+ # or typed dicts.
12
+
13
+ TIMEOUT = 60
14
+ REQUEST_PAGE_SIZE = 250
15
+ API_VERSION = "v1"
16
+
17
+
18
+ class ApiMethods:
19
+ headers: dict[str, str]
20
+ base_url: str
21
+
22
+ @staticmethod
23
+ def _get_base_url(domain: str) -> str:
24
+ """Returns the root endpoint for the GoodData Cloud API.
25
+
26
+ Method ensures that the URL starts with "https://" and does not
27
+ end with a trailing slash.
28
+
29
+ Args:
30
+ domain (str): The domain of the GoodData Cloud instance.
31
+ Returns:
32
+ str: The base URL for the GoodData Cloud API.
33
+ """
34
+ # Remove trailing slash if present.
35
+ if domain[-1] == "/":
36
+ domain = domain[:-1]
37
+
38
+ if not domain.startswith("https://") and not domain.startswith(
39
+ "http://"
40
+ ):
41
+ domain = f"https://{domain}"
42
+
43
+ if domain.startswith("http://") and not domain.startswith("https://"):
44
+ domain = domain.replace("http://", "https://")
45
+
46
+ return f"{domain}/api/{API_VERSION}"
47
+
48
+ def _get_url(self, endpoint: str) -> str:
49
+ """Returns the full URL for a given API endpoint.
50
+
51
+ Args:
52
+ endpoint (str): The API endpoint to be appended to the base URL.
53
+ Returns:
54
+ str: The full URL for the API endpoint.
55
+ """
56
+ return f"{self.base_url}{endpoint}"
57
+
58
+ def get_custom_application_setting(
59
+ self, workspace_id: str, setting_id: str
60
+ ) -> requests.Response:
61
+ """Gets a custom application setting.
62
+
63
+ Args:
64
+ workspace_id (str): The ID of the workspace.
65
+ setting_id (str): The ID of the custom application setting.
66
+ Returns:
67
+ requests.Response: The response from the server containing the
68
+ custom application setting.
69
+ """
70
+ url = f"/entities/workspaces/{workspace_id}/customApplicationSettings/{setting_id}"
71
+ return self._get(url)
72
+
73
+ def put_custom_application_setting(
74
+ self, workspace_id: str, setting_id: str, data: dict[str, Any]
75
+ ) -> requests.Response:
76
+ url = f"/entities/workspaces/{workspace_id}/customApplicationSettings/{setting_id}"
77
+ return self._put(url, data, self.headers)
78
+
79
+ def post_custom_application_setting(
80
+ self, workspace_id: str, data: dict[str, Any]
81
+ ) -> requests.Response:
82
+ """Creates a custom application setting for a given workspace.
83
+
84
+ Args:
85
+ workspace_id (str): The ID of the workspace.
86
+ data (dict[str, Any]): The data for the custom application setting.
87
+ Returns:
88
+ requests.Response: The response from the server containing the
89
+ created custom application setting.
90
+ """
91
+ url = f"/entities/workspaces/{workspace_id}/customApplicationSettings/"
92
+ return self._post(url, data, self.headers)
93
+
94
+ def get_all_workspace_data_filters(
95
+ self, workspace_id: str
96
+ ) -> requests.Response:
97
+ """Gets all workspace data filters for a given workspace.
98
+
99
+ Args:
100
+ workspace_id (str): The ID of the workspace.
101
+ Returns:
102
+ requests.Response: The response from the server containing all
103
+ workspace data filters.
104
+ """
105
+ url = f"/entities/workspaces/{workspace_id}/workspaceDataFilters"
106
+ return self._get(url)
107
+
108
+ def get_workspace_data_filter_settings(
109
+ self, workspace_id: str
110
+ ) -> requests.Response:
111
+ """Gets all workspace data filter settings for a given workspace.
112
+
113
+ Args:
114
+ workspace_id (str): The ID of the workspace.
115
+ Returns:
116
+ requests.Response: The response from the server containing all
117
+ workspace data filter settings.
118
+ """
119
+ url = f"/entities/workspaces/{workspace_id}/workspaceDataFilterSettings?include=workspaceDataFilters"
120
+ return self._get(url)
121
+
122
+ def get_workspace_data_filter_setting(
123
+ self, workspace_id: str, wdf_id: str
124
+ ) -> requests.Response:
125
+ """Gets a specific workspace data filter setting.
126
+
127
+ Args:
128
+ workspace_id (str): The ID of the workspace.
129
+ wdf_id (str): The ID of the workspace data filter setting.
130
+ Returns:
131
+ requests.Response: The response from the server containing the
132
+ workspace data filter setting.
133
+ """
134
+ url = f"/entities/workspaces/{workspace_id}/workspaceDataFilterSettings/{wdf_id}"
135
+ return self._get(url)
136
+
137
+ def put_workspace_data_filter_setting(
138
+ self,
139
+ workspace_id: str,
140
+ wdf_setting: dict[str, Any],
141
+ ) -> requests.Response:
142
+ """Updates a workspace data filter setting.
143
+
144
+ Args:
145
+ workspace_id (str): The ID of the workspace.
146
+ wdf_setting (dict[str, Any]): The workspace data filter setting to
147
+ update.
148
+ Returns:
149
+ requests.Response: The response from the server containing the
150
+ updated workspace data filter setting.
151
+ """
152
+ wdf_setting_id = wdf_setting["data"]["id"]
153
+ endpoint = f"/entities/workspaces/{workspace_id}/workspaceDataFilterSettings/{wdf_setting_id}"
154
+ return self._put(
155
+ endpoint,
156
+ wdf_setting,
157
+ self.headers,
158
+ )
159
+
160
+ def post_workspace_data_filter_setting(
161
+ self,
162
+ workspace_id: str,
163
+ wdf_setting: dict[str, Any],
164
+ ) -> requests.Response:
165
+ """Creates a workspace data filter setting for a given workspace.
166
+
167
+ Args:
168
+ workspace_id (str): The ID of the workspace.
169
+ wdf_setting (dict[str, Any]): The workspace data filter setting to
170
+ create.
171
+ Returns:
172
+ requests.Response: The response from the server containing the
173
+ created workspace data filter setting.
174
+ """
175
+ endpoint = (
176
+ f"/entities/workspaces/{workspace_id}/workspaceDataFilterSettings/"
177
+ )
178
+ return self._post(
179
+ endpoint,
180
+ wdf_setting,
181
+ self.headers,
182
+ )
183
+
184
+ def delete_workspace_data_filter_setting(
185
+ self,
186
+ workspace_id: str,
187
+ wdf_setting_id: str,
188
+ ) -> requests.Response:
189
+ """Deletes a workspace data filter setting.
190
+
191
+ Args:
192
+ workspace_id (str): The ID of the workspace.
193
+ wdf_setting_id (str): The ID of the workspace data filter setting
194
+ to delete.
195
+ Returns:
196
+ requests.Response: The response from the server confirming the
197
+ deletion of the workspace data filter setting.
198
+ """
199
+ endpoint = f"/entities/workspaces/{workspace_id}/workspaceDataFilterSettings/{wdf_setting_id}"
200
+ return self._delete(
201
+ endpoint,
202
+ )
203
+
204
+ def post_workspace_data_filter(
205
+ self, workspace_id: str, data: dict[str, Any]
206
+ ) -> requests.Response:
207
+ """Creates a workspace data filter for a given workspace.
208
+
209
+ Args:
210
+ workspace_id (str): The ID of the workspace.
211
+ data (dict[str, Any]): The data for the workspace data filter.
212
+ Returns:
213
+ requests.Response: The response from the server containing the
214
+ created workspace data filter.
215
+ """
216
+ endpoint = f"/entities/workspaces/{workspace_id}/workspaceDataFilters"
217
+ return self._post(endpoint, data, self.headers)
218
+
219
+ def get_user_data_filters(self, workspace_id: str) -> requests.Response:
220
+ """Gets the user data filters for a given workspace."""
221
+ endpoint = f"/layout/workspaces/{workspace_id}/userDataFilters"
222
+ return self._get(endpoint)
223
+
224
+ def get_automations(self, workspace_id: str) -> requests.Response:
225
+ """Gets the automations for a given workspace."""
226
+ endpoint = (
227
+ f"/entities/workspaces/{workspace_id}/automations?include=ALL"
228
+ )
229
+ return self._get(endpoint)
230
+
231
+ def _get(
232
+ self, endpoint: str, headers: dict[str, str] | None = None
233
+ ) -> requests.Response:
234
+ """Sends a GET request to the server.
235
+
236
+ Args:
237
+ endpoint (str): The API endpoint to send the GET request to.
238
+ headers (dict[str, str] | None): Headers to include in the request.
239
+ If no headers are provided, the default headers will be used.
240
+ Returns:
241
+ requests.Response: The response from the server.
242
+ """
243
+ url = self._get_url(endpoint)
244
+ request_headers = headers if headers else self.headers
245
+
246
+ return requests.get(url, headers=request_headers, timeout=TIMEOUT)
247
+
248
+ def _post(
249
+ self,
250
+ endpoint: str,
251
+ data: Any,
252
+ headers: dict | None = None,
253
+ ) -> requests.Response:
254
+ """Sends a POST request to the server with a given JSON object.
255
+
256
+ Args:
257
+ endpoint (str): The API endpoint to send the POST request to.
258
+ data (Any): The JSON data to send in the request body.
259
+ headers (dict | None): Headers to include in the request.
260
+ If no headers are provided, the default headers will be used.
261
+ Returns:
262
+ requests.Response: The response from the server.
263
+ """
264
+ url = self._get_url(endpoint)
265
+ request_headers = headers if headers else self.headers
266
+ data_json = json.dumps(data)
267
+
268
+ return requests.post(
269
+ url, data=data_json, headers=request_headers, timeout=TIMEOUT
270
+ )
271
+
272
+ def _put(
273
+ self,
274
+ endpoint: str,
275
+ data: Any,
276
+ headers: dict | None = None,
277
+ ) -> requests.Response:
278
+ """Sends a PUT request to the server with a given JSON object.
279
+
280
+ Args:
281
+ endpoint (str): The API endpoint to send the PUT request to.
282
+ data (Any): The JSON data to send in the request body.
283
+ headers (dict | None): Headers to include in the request.
284
+ If no headers are provided, the default headers will be used.
285
+ Returns:
286
+ requests.Response: The response from the server.
287
+ """
288
+ url = self._get_url(endpoint)
289
+ request_headers = headers if headers else self.headers
290
+ data_json = json.dumps(data)
291
+
292
+ return requests.put(
293
+ url, data=data_json, headers=request_headers, timeout=TIMEOUT
294
+ )
295
+
296
+ def _delete(
297
+ self,
298
+ endpoint: str,
299
+ ) -> requests.Response:
300
+ """Sends a DELETE request to the server.
301
+
302
+ Args:
303
+ endpoint (str): The API endpoint to send the DELETE request to.
304
+ Returns:
305
+ requests.Response: The response from the server.
306
+ """
307
+ url = self._get_url(endpoint)
308
+
309
+ return requests.delete(url, headers=self.headers, timeout=TIMEOUT)
@@ -0,0 +1,36 @@
1
+ # (C) 2025 GoodData Corporation
2
+
3
+ """Wrapper for interaction with GoodData Cloud."""
4
+
5
+ from gooddata_sdk.sdk import GoodDataSdk
6
+
7
+ from gooddata_pipelines.api.gooddata_api import ApiMethods
8
+ from gooddata_pipelines.api.gooddata_sdk import SdkMethods
9
+
10
+
11
+ class GoodDataApi(SdkMethods, ApiMethods):
12
+ """Wrapper class for the GoodData Cloud API.
13
+
14
+ This class combines interactions with the GoodData Python SDK and direct API
15
+ calls.
16
+ """
17
+
18
+ def __init__(self, host: str, token: str) -> None:
19
+ """Initialize the GoodDataApi with host and token.
20
+
21
+ Args:
22
+ host (str): The GoodData Cloud host URL.
23
+ token (str): The authentication token for the GoodData Cloud API.
24
+ """
25
+ self._domain: str = host
26
+ self._token: str = token
27
+
28
+ # Initialize the GoodData SDK
29
+ self._sdk = GoodDataSdk.create(self._domain, self._token)
30
+
31
+ # Set up utils for direct API interaction
32
+ self.base_url = self._get_base_url(self._domain)
33
+ self.headers: dict = {
34
+ "Authorization": f"Bearer {self._token}",
35
+ "Content-Type": "application/vnd.gooddata.api+json",
36
+ }