gooddata-pipelines 1.48.1.dev4__py3-none-any.whl → 1.49.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.
- gooddata_pipelines/provisioning/entities/users/models/permissions.py +93 -72
- gooddata_pipelines/provisioning/entities/users/permissions.py +48 -20
- gooddata_pipelines/provisioning/provisioning.py +39 -20
- {gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/METADATA +3 -8
- {gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/RECORD +7 -7
- {gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/WHEEL +0 -0
- {gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,103 +1,101 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
|
-
from
|
|
2
|
+
from abc import abstractmethod
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Any, Iterator, TypeAlias
|
|
4
|
+
from typing import Any, Iterator, TypeAlias, TypeVar
|
|
5
5
|
|
|
6
|
+
import attrs
|
|
6
7
|
from gooddata_sdk.catalog.identifier import CatalogAssigneeIdentifier
|
|
7
8
|
from gooddata_sdk.catalog.permission.declarative_model.permission import (
|
|
8
9
|
CatalogDeclarativeSingleWorkspacePermission,
|
|
9
10
|
CatalogDeclarativeWorkspacePermissions,
|
|
10
11
|
)
|
|
12
|
+
from pydantic import BaseModel
|
|
11
13
|
|
|
12
14
|
from gooddata_pipelines.provisioning.utils.exceptions import BaseUserException
|
|
13
15
|
|
|
14
|
-
# TODO: refactor the full load and incremental load models to reuse as much as possible
|
|
15
|
-
# TODO: use pydantic models instead of dataclasses?
|
|
16
|
-
# TODO: make the validation logic more readable (as in PermissionIncrementalLoad)
|
|
17
|
-
|
|
18
16
|
TargetsPermissionDict: TypeAlias = dict[str, dict[str, bool]]
|
|
17
|
+
ConstructorType = TypeVar("ConstructorType", bound="ConstructorMixin")
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
class PermissionType(Enum):
|
|
20
|
+
class PermissionType(str, Enum):
|
|
21
|
+
# NOTE: Start using StrEnum with Python 3.11
|
|
22
22
|
user = "user"
|
|
23
23
|
user_group = "userGroup"
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
class ConstructorMixin:
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _get_id_and_type(
|
|
29
|
+
permission: dict[str, Any],
|
|
30
|
+
) -> tuple[str, PermissionType]:
|
|
31
|
+
user_id: str | None = permission.get("user_id")
|
|
32
|
+
user_group_id: str | None = permission.get("ug_id")
|
|
33
|
+
if user_id and user_group_id:
|
|
34
|
+
raise ValueError("Only one of user_id or ug_id must be present")
|
|
35
|
+
elif user_id:
|
|
36
|
+
return user_id, PermissionType.user
|
|
37
|
+
elif user_group_id:
|
|
38
|
+
return user_group_id, PermissionType.user_group
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError("Either user_id or ug_id must be present")
|
|
33
41
|
|
|
34
42
|
@classmethod
|
|
35
43
|
def from_list_of_dicts(
|
|
36
|
-
cls, data: list[dict[str, Any]]
|
|
37
|
-
) -> list[
|
|
38
|
-
"""Creates a list of
|
|
39
|
-
|
|
44
|
+
cls: type[ConstructorType], data: list[dict[str, Any]]
|
|
45
|
+
) -> list[ConstructorType]:
|
|
46
|
+
"""Creates a list of instances from list of dicts."""
|
|
47
|
+
# NOTE: We can use typing.Self for the return type in Python 3.11
|
|
40
48
|
permissions = []
|
|
41
49
|
for permission in data:
|
|
42
|
-
|
|
43
|
-
user_group_id: str | None = permission.get("ug_id")
|
|
44
|
-
|
|
45
|
-
if user_id is not None:
|
|
46
|
-
target_type = PermissionType.user
|
|
47
|
-
id = user_id
|
|
48
|
-
elif user_group_id is not None:
|
|
49
|
-
target_type = PermissionType.user_group
|
|
50
|
-
id = user_group_id
|
|
51
|
-
|
|
52
|
-
permissions.append(
|
|
53
|
-
PermissionIncrementalLoad(
|
|
54
|
-
permission=permission["ws_permissions"],
|
|
55
|
-
workspace_id=permission["ws_id"],
|
|
56
|
-
id=id,
|
|
57
|
-
type=target_type,
|
|
58
|
-
is_active=str(permission["is_active"]).lower() == "true",
|
|
59
|
-
)
|
|
60
|
-
)
|
|
50
|
+
permissions.append(cls.from_dict(permission))
|
|
61
51
|
return permissions
|
|
62
52
|
|
|
53
|
+
@classmethod
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def from_dict(cls, data: dict[str, Any]) -> Any:
|
|
56
|
+
"""Construction form a dictionary to be implemented by subclasses."""
|
|
57
|
+
pass
|
|
58
|
+
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
class PermissionFullLoad:
|
|
60
|
+
class PermissionIncrementalLoad(BaseModel, ConstructorMixin):
|
|
66
61
|
permission: str
|
|
67
62
|
workspace_id: str
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
id_: str
|
|
64
|
+
type_: PermissionType
|
|
65
|
+
is_active: bool
|
|
70
66
|
|
|
71
67
|
@classmethod
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
)
|
|
68
|
+
def from_dict(cls, data: dict[str, Any]) -> "PermissionIncrementalLoad":
|
|
69
|
+
"""Returns an instance of PermissionIncrementalLoad from a dictionary."""
|
|
70
|
+
id_, target_type = cls._get_id_and_type(data)
|
|
71
|
+
return cls(
|
|
72
|
+
permission=data["ws_permissions"],
|
|
73
|
+
workspace_id=data["ws_id"],
|
|
74
|
+
id_=id_,
|
|
75
|
+
type_=target_type,
|
|
76
|
+
is_active=data["is_active"],
|
|
77
|
+
)
|
|
83
78
|
|
|
84
|
-
if permission["user_id"]:
|
|
85
|
-
target_type = PermissionType.user
|
|
86
|
-
else:
|
|
87
|
-
target_type = PermissionType.user_group
|
|
88
|
-
|
|
89
|
-
permissions.append(
|
|
90
|
-
PermissionFullLoad(
|
|
91
|
-
permission=permission["ws_permissions"],
|
|
92
|
-
workspace_id=permission["ws_id"],
|
|
93
|
-
id=id,
|
|
94
|
-
type=target_type,
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
return permissions
|
|
98
79
|
|
|
80
|
+
class PermissionFullLoad(BaseModel, ConstructorMixin):
|
|
81
|
+
permission: str
|
|
82
|
+
workspace_id: str
|
|
83
|
+
id_: str
|
|
84
|
+
type_: PermissionType
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def from_dict(cls, data: dict[str, Any]) -> "PermissionFullLoad":
|
|
88
|
+
"""Returns an instance of PermissionFullLoad from a dictionary."""
|
|
89
|
+
id_, target_type = cls._get_id_and_type(data)
|
|
90
|
+
return cls(
|
|
91
|
+
permission=data["ws_permissions"],
|
|
92
|
+
workspace_id=data["ws_id"],
|
|
93
|
+
id_=id_,
|
|
94
|
+
type_=target_type,
|
|
95
|
+
)
|
|
99
96
|
|
|
100
|
-
|
|
97
|
+
|
|
98
|
+
@attrs.define
|
|
101
99
|
class PermissionDeclaration:
|
|
102
100
|
users: TargetsPermissionDict
|
|
103
101
|
user_groups: TargetsPermissionDict
|
|
@@ -192,7 +190,9 @@ class PermissionDeclaration:
|
|
|
192
190
|
permissions=permission_declarations
|
|
193
191
|
)
|
|
194
192
|
|
|
195
|
-
def
|
|
193
|
+
def add_incremental_permission(
|
|
194
|
+
self, permission: PermissionIncrementalLoad
|
|
195
|
+
) -> None:
|
|
196
196
|
"""
|
|
197
197
|
Adds WSPermission object into respective field within the instance.
|
|
198
198
|
Handles duplicate permissions and different combinations of input
|
|
@@ -200,15 +200,15 @@ class PermissionDeclaration:
|
|
|
200
200
|
"""
|
|
201
201
|
target_dict = (
|
|
202
202
|
self.users
|
|
203
|
-
if permission.
|
|
203
|
+
if permission.type_ == PermissionType.user
|
|
204
204
|
else self.user_groups
|
|
205
205
|
)
|
|
206
206
|
|
|
207
|
-
if permission.
|
|
208
|
-
target_dict[permission.
|
|
207
|
+
if permission.id_ not in target_dict:
|
|
208
|
+
target_dict[permission.id_] = {}
|
|
209
209
|
|
|
210
210
|
is_active = permission.is_active
|
|
211
|
-
target_permissions = target_dict[permission.
|
|
211
|
+
target_permissions = target_dict[permission.id_]
|
|
212
212
|
permission_value = permission.permission
|
|
213
213
|
|
|
214
214
|
if permission_value not in target_permissions:
|
|
@@ -225,6 +225,27 @@ class PermissionDeclaration:
|
|
|
225
225
|
)
|
|
226
226
|
target_permissions[permission_value] = is_active
|
|
227
227
|
|
|
228
|
+
def add_full_load_permission(self, permission: PermissionFullLoad) -> None:
|
|
229
|
+
"""
|
|
230
|
+
Adds WSPermission object into respective field within the instance.
|
|
231
|
+
Handles duplicate permissions and different combinations of input
|
|
232
|
+
and upstream is_active permission states.
|
|
233
|
+
"""
|
|
234
|
+
target_dict = (
|
|
235
|
+
self.users
|
|
236
|
+
if permission.type_ == PermissionType.user
|
|
237
|
+
else self.user_groups
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if permission.id_ not in target_dict:
|
|
241
|
+
target_dict[permission.id_] = {}
|
|
242
|
+
|
|
243
|
+
target_permissions = target_dict[permission.id_]
|
|
244
|
+
permission_value = permission.permission
|
|
245
|
+
|
|
246
|
+
if permission_value not in target_permissions:
|
|
247
|
+
target_permissions[permission_value] = True
|
|
248
|
+
|
|
228
249
|
def upsert(self, other: "PermissionDeclaration") -> None:
|
|
229
250
|
"""
|
|
230
251
|
Modifies the owner object by merging with the other.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
"""Module for provisioning user permissions in GoodData workspaces."""
|
|
4
4
|
|
|
5
|
+
from typing import TypeVar
|
|
6
|
+
|
|
5
7
|
from gooddata_pipelines.api.exceptions import GoodDataApiException
|
|
6
8
|
from gooddata_pipelines.provisioning.entities.users.models.permissions import (
|
|
7
9
|
PermissionDeclaration,
|
|
@@ -14,6 +16,11 @@ from gooddata_pipelines.provisioning.entities.users.models.permissions import (
|
|
|
14
16
|
from gooddata_pipelines.provisioning.provisioning import Provisioning
|
|
15
17
|
from gooddata_pipelines.provisioning.utils.exceptions import BaseUserException
|
|
16
18
|
|
|
19
|
+
# Type variable for permission models (PermissionIncrementalLoad or PermissionFullLoad)
|
|
20
|
+
PermissionModel = TypeVar(
|
|
21
|
+
"PermissionModel", PermissionIncrementalLoad, PermissionFullLoad
|
|
22
|
+
)
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
class PermissionProvisioner(
|
|
19
26
|
Provisioning[PermissionFullLoad, PermissionIncrementalLoad]
|
|
@@ -72,7 +79,7 @@ class PermissionProvisioner(
|
|
|
72
79
|
|
|
73
80
|
@staticmethod
|
|
74
81
|
def _construct_declarations(
|
|
75
|
-
permissions: list[PermissionIncrementalLoad],
|
|
82
|
+
permissions: list[PermissionIncrementalLoad] | list[PermissionFullLoad],
|
|
76
83
|
) -> WSPermissionsDeclarations:
|
|
77
84
|
"""Constructs workspace permission declarations from the input permissions."""
|
|
78
85
|
ws_dict: WSPermissionsDeclarations = {}
|
|
@@ -82,7 +89,12 @@ class PermissionProvisioner(
|
|
|
82
89
|
if ws_id not in ws_dict:
|
|
83
90
|
ws_dict[ws_id] = PermissionDeclaration({}, {})
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
if isinstance(permission, PermissionIncrementalLoad):
|
|
93
|
+
ws_dict[ws_id].add_incremental_permission(permission)
|
|
94
|
+
elif isinstance(permission, PermissionFullLoad):
|
|
95
|
+
ws_dict[ws_id].add_full_load_permission(permission)
|
|
96
|
+
else:
|
|
97
|
+
raise ValueError(f"Invalid permission type: {type(permission)}")
|
|
86
98
|
return ws_dict
|
|
87
99
|
|
|
88
100
|
def _check_user_group_exists(self, ug_id: str) -> None:
|
|
@@ -90,14 +102,14 @@ class PermissionProvisioner(
|
|
|
90
102
|
self._api._sdk.catalog_user.get_user_group(ug_id)
|
|
91
103
|
|
|
92
104
|
def _validate_permission(
|
|
93
|
-
self, permission: PermissionIncrementalLoad
|
|
105
|
+
self, permission: PermissionFullLoad | PermissionIncrementalLoad
|
|
94
106
|
) -> None:
|
|
95
107
|
"""Validates if the permission is correctly defined."""
|
|
96
|
-
if permission.
|
|
97
|
-
self._api.get_user(permission.
|
|
108
|
+
if permission.type_ == PermissionType.user:
|
|
109
|
+
self._api.get_user(permission.id_, error_message="User not found")
|
|
98
110
|
else:
|
|
99
111
|
self._api.get_user_group(
|
|
100
|
-
permission.
|
|
112
|
+
permission.id_, error_message="User group not found"
|
|
101
113
|
)
|
|
102
114
|
|
|
103
115
|
self._api.get_workspace(
|
|
@@ -105,10 +117,12 @@ class PermissionProvisioner(
|
|
|
105
117
|
)
|
|
106
118
|
|
|
107
119
|
def _filter_invalid_permissions(
|
|
108
|
-
self,
|
|
109
|
-
|
|
120
|
+
self,
|
|
121
|
+
permissions: list[PermissionModel],
|
|
122
|
+
) -> list[PermissionModel]:
|
|
110
123
|
"""Filters out invalid permissions from the input list."""
|
|
111
|
-
valid_permissions: list[
|
|
124
|
+
valid_permissions: list[PermissionModel] = []
|
|
125
|
+
|
|
112
126
|
for permission in permissions:
|
|
113
127
|
try:
|
|
114
128
|
self._validate_permission(permission)
|
|
@@ -121,13 +135,15 @@ class PermissionProvisioner(
|
|
|
121
135
|
valid_permissions.append(permission)
|
|
122
136
|
return valid_permissions
|
|
123
137
|
|
|
124
|
-
def
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
def _provision_incremental_load(self) -> None:
|
|
139
|
+
"""Provisiones permissions for a list of workspaces.
|
|
140
|
+
|
|
141
|
+
Modifies existing upstream workspace permission declarations for each
|
|
142
|
+
input workspace and skips rest of the workspaces.
|
|
129
143
|
"""
|
|
130
|
-
valid_permissions = self._filter_invalid_permissions(
|
|
144
|
+
valid_permissions = self._filter_invalid_permissions(
|
|
145
|
+
self.source_group_incremental
|
|
146
|
+
)
|
|
131
147
|
|
|
132
148
|
input_declarations = self._construct_declarations(valid_permissions)
|
|
133
149
|
|
|
@@ -145,9 +161,21 @@ class PermissionProvisioner(
|
|
|
145
161
|
self._api.put_declarative_permissions(ws_id, ws_permissions)
|
|
146
162
|
self.logger.info(f"Updated permissions for workspace {ws_id}")
|
|
147
163
|
|
|
148
|
-
def _provision_incremental_load(self) -> None:
|
|
149
|
-
"""Provision permissions based on the source group."""
|
|
150
|
-
self._manage_permissions(self.source_group_incremental)
|
|
151
|
-
|
|
152
164
|
def _provision_full_load(self) -> None:
|
|
153
|
-
|
|
165
|
+
"""Provisions permissions for selected of workspaces.
|
|
166
|
+
|
|
167
|
+
Modifies upstream workspace declarations for each input workspace and
|
|
168
|
+
skips non-existent workspace ids. Overwrites any existing configuration
|
|
169
|
+
of the workspace permissions.
|
|
170
|
+
"""
|
|
171
|
+
valid_permissions = self._filter_invalid_permissions(
|
|
172
|
+
self.source_group_full
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
input_declarations = self._construct_declarations(valid_permissions)
|
|
176
|
+
|
|
177
|
+
for ws_id, declaration in input_declarations.items():
|
|
178
|
+
ws_permissions = declaration.to_sdk_api()
|
|
179
|
+
|
|
180
|
+
self._api.put_declarative_permissions(ws_id, ws_permissions)
|
|
181
|
+
self.logger.info(f"Updated permissions for workspace {ws_id}")
|
|
@@ -36,6 +36,7 @@ class Provisioning(Generic[TFullLoadSourceData, TIncrementalSourceData]):
|
|
|
36
36
|
cls: Type[TProvisioning], host: str, token: str
|
|
37
37
|
) -> TProvisioning:
|
|
38
38
|
"""Creates a provisioner instance using provided host and token."""
|
|
39
|
+
cls._validate_credentials(host, token)
|
|
39
40
|
return cls(host=host, token=token)
|
|
40
41
|
|
|
41
42
|
@classmethod
|
|
@@ -48,6 +49,16 @@ class Provisioning(Generic[TFullLoadSourceData, TIncrementalSourceData]):
|
|
|
48
49
|
content = profile_content(profile, profiles_path)
|
|
49
50
|
return cls(**content)
|
|
50
51
|
|
|
52
|
+
@staticmethod
|
|
53
|
+
def _validate_credentials(host: str, token: str) -> None:
|
|
54
|
+
"""Validates the credentials."""
|
|
55
|
+
if (not host) and (not token):
|
|
56
|
+
raise ValueError("Host and token are required.")
|
|
57
|
+
if not host:
|
|
58
|
+
raise ValueError("Host is required.")
|
|
59
|
+
if not token:
|
|
60
|
+
raise ValueError("Token is required.")
|
|
61
|
+
|
|
51
62
|
@staticmethod
|
|
52
63
|
def _create_groups(
|
|
53
64
|
source_id: set[str], panther_id: set[str]
|
|
@@ -95,13 +106,9 @@ class Provisioning(Generic[TFullLoadSourceData, TIncrementalSourceData]):
|
|
|
95
106
|
|
|
96
107
|
try:
|
|
97
108
|
self._provision_full_load()
|
|
98
|
-
self.logger.info("Provisioning completed
|
|
109
|
+
self.logger.info("Provisioning completed.")
|
|
99
110
|
except Exception as e:
|
|
100
|
-
self.
|
|
101
|
-
self.logger.error(
|
|
102
|
-
f"Provisioning failed. Error: {self.fatal_exception} "
|
|
103
|
-
+ f"Context: {e.__dict__}"
|
|
104
|
-
)
|
|
111
|
+
self._handle_fatal_exception(e)
|
|
105
112
|
|
|
106
113
|
def incremental_load(
|
|
107
114
|
self, source_data: list[TIncrementalSourceData]
|
|
@@ -111,22 +118,34 @@ class Provisioning(Generic[TFullLoadSourceData, TIncrementalSourceData]):
|
|
|
111
118
|
Incremental provisioning is used to modify a subset of the upstream workspaces
|
|
112
119
|
based on the source data provided.
|
|
113
120
|
"""
|
|
121
|
+
# TODO: validate the data type of source group at runtime
|
|
114
122
|
self.source_group_incremental = source_data
|
|
115
123
|
|
|
116
124
|
try:
|
|
117
125
|
self._provision_incremental_load()
|
|
118
|
-
self.logger.info("Provisioning completed
|
|
126
|
+
self.logger.info("Provisioning completed.")
|
|
119
127
|
except Exception as e:
|
|
120
|
-
self.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
self._handle_fatal_exception(e)
|
|
129
|
+
|
|
130
|
+
def _handle_fatal_exception(self, e: Exception) -> None:
|
|
131
|
+
"""Handles fatal exceptions during provisioning.
|
|
132
|
+
|
|
133
|
+
Logs the exception content. Re-raises the exception if there is no
|
|
134
|
+
subscriber to the logger.
|
|
135
|
+
"""
|
|
136
|
+
self.fatal_exception = str(e)
|
|
137
|
+
|
|
138
|
+
if hasattr(e, "__dict__"):
|
|
139
|
+
exception_context = f"Context: {e.__dict__}"
|
|
140
|
+
else:
|
|
141
|
+
exception_context = ""
|
|
142
|
+
|
|
143
|
+
exception_message = (
|
|
144
|
+
f"Provisioning failed. Error: {self.fatal_exception}. "
|
|
145
|
+
+ exception_context
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
self.logger.error(exception_message)
|
|
149
|
+
|
|
150
|
+
if not self.logger.subscribers:
|
|
151
|
+
raise Exception(exception_message)
|
{gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/METADATA
RENAMED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gooddata-pipelines
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.49.1.dev1
|
|
4
|
+
Summary: GoodData Cloud lifecycle automation pipelines
|
|
4
5
|
Author-email: GoodData <support@gooddata.com>
|
|
5
6
|
License: MIT
|
|
6
7
|
License-File: LICENSE.txt
|
|
7
8
|
Requires-Python: >=3.10
|
|
8
9
|
Requires-Dist: boto3-stubs<2.0.0,>=1.39.3
|
|
9
10
|
Requires-Dist: boto3<2.0.0,>=1.39.3
|
|
10
|
-
Requires-Dist: gooddata-sdk~=1.
|
|
11
|
+
Requires-Dist: gooddata-sdk~=1.49.1.dev1
|
|
11
12
|
Requires-Dist: pydantic<3.0.0,>=2.11.3
|
|
12
13
|
Requires-Dist: requests<3.0.0,>=2.32.3
|
|
13
14
|
Requires-Dist: types-pyyaml<7.0.0,>=6.0.12.20250326
|
|
14
15
|
Requires-Dist: types-requests<3.0.0,>=2.32.0
|
|
15
|
-
Provides-Extra: dev
|
|
16
|
-
Requires-Dist: moto<6.0.0,>=5.1.6; extra == 'dev'
|
|
17
|
-
Requires-Dist: mypy<2.0.0,>=1.16.0; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest-mock<4.0.0,>=3.14.0; extra == 'dev'
|
|
19
|
-
Requires-Dist: pytest<9.0.0,>=8.3.5; extra == 'dev'
|
|
20
|
-
Requires-Dist: ruff<0.12.0,>=0.11.2; extra == 'dev'
|
|
21
16
|
Description-Content-Type: text/markdown
|
|
22
17
|
|
|
23
18
|
# GoodData Pipelines
|
{gooddata_pipelines-1.48.1.dev4.dist-info → gooddata_pipelines-1.49.1.dev1.dist-info}/RECORD
RENAMED
|
@@ -23,7 +23,7 @@ gooddata_pipelines/backup_and_restore/storage/s3_storage.py,sha256=iRtMtDq_C1b_J
|
|
|
23
23
|
gooddata_pipelines/logger/__init__.py,sha256=W-fJvMStnsDUY52AYFhx_LnS2cSCFNf3bB47Iew2j04,129
|
|
24
24
|
gooddata_pipelines/logger/logger.py,sha256=yIMdvqsmOSGQLI4U_tQwxX5E2q_FXUu0Ko7Hv39slFM,3549
|
|
25
25
|
gooddata_pipelines/provisioning/__init__.py,sha256=RZDEiv8nla4Jwa2TZXUdp1NSxg2_-lLqz4h7k2c4v5Y,854
|
|
26
|
-
gooddata_pipelines/provisioning/provisioning.py,sha256=
|
|
26
|
+
gooddata_pipelines/provisioning/provisioning.py,sha256=RSxp3bZgdJx3WfbkwQrV1W7dRlyngqfppCtCfVG7BbA,5357
|
|
27
27
|
gooddata_pipelines/provisioning/assets/wdf_setting.json,sha256=nxOLGZkEQiMdARcUDER5ygqr3Zu-MQlLlUyXVhPUq64,280
|
|
28
28
|
gooddata_pipelines/provisioning/entities/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
29
29
|
gooddata_pipelines/provisioning/entities/user_data_filters/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
@@ -31,11 +31,11 @@ gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py,
|
|
|
31
31
|
gooddata_pipelines/provisioning/entities/user_data_filters/models/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
32
32
|
gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py,sha256=y0q5E91AhxIkf_EHW0swCjNUkiiAOFXarAhvjUKVVKw,740
|
|
33
33
|
gooddata_pipelines/provisioning/entities/users/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
34
|
-
gooddata_pipelines/provisioning/entities/users/permissions.py,sha256=
|
|
34
|
+
gooddata_pipelines/provisioning/entities/users/permissions.py,sha256=dZUxzTscIpWPRvANUVocjAfpUSvJ7ImBiABBTIeguyE,6850
|
|
35
35
|
gooddata_pipelines/provisioning/entities/users/user_groups.py,sha256=Up36pwwlOFS_IdYetViZ7gUHfV2hIgXL4th_k9D31Eo,8266
|
|
36
36
|
gooddata_pipelines/provisioning/entities/users/users.py,sha256=1B1bMk8ysughCoCJs1aX0bI9iUIeAc1hIUyJ0hWyC5M,6503
|
|
37
37
|
gooddata_pipelines/provisioning/entities/users/models/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
38
|
-
gooddata_pipelines/provisioning/entities/users/models/permissions.py,sha256=
|
|
38
|
+
gooddata_pipelines/provisioning/entities/users/models/permissions.py,sha256=JCyItTqDdyDqB72O5f32IOh1sAiVK-DwFyXyllNU-v4,9279
|
|
39
39
|
gooddata_pipelines/provisioning/entities/users/models/user_groups.py,sha256=TjlP6oABK6UP7nMKNMlLk3M62eNf9e-3LdKI9-VFwi8,2007
|
|
40
40
|
gooddata_pipelines/provisioning/entities/users/models/users.py,sha256=rKtiRxtelLphw-_BbD-AM_-hPrpp0xqEr1jmuU_oJVg,3767
|
|
41
41
|
gooddata_pipelines/provisioning/entities/workspaces/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
@@ -48,7 +48,7 @@ gooddata_pipelines/provisioning/utils/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pj
|
|
|
48
48
|
gooddata_pipelines/provisioning/utils/context_objects.py,sha256=sM22hMsxE0XLI1TU0Vs-2kK0vf4YrB1musoAg__4bjc,936
|
|
49
49
|
gooddata_pipelines/provisioning/utils/exceptions.py,sha256=1WnAOlPhqOf0xRcvn70lxAlLb8Oo6m6WCYS4hj9uzDU,3630
|
|
50
50
|
gooddata_pipelines/provisioning/utils/utils.py,sha256=_Tk-mFgbIGpCixDCF9e-3ZYd-g5Jb3SJiLSH465k4jY,2846
|
|
51
|
-
gooddata_pipelines-1.
|
|
52
|
-
gooddata_pipelines-1.
|
|
53
|
-
gooddata_pipelines-1.
|
|
54
|
-
gooddata_pipelines-1.
|
|
51
|
+
gooddata_pipelines-1.49.1.dev1.dist-info/METADATA,sha256=_FSW5vT-giekFNSkPNpNZ5X5YEaPHOJql7kejY-50ew,3522
|
|
52
|
+
gooddata_pipelines-1.49.1.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
53
|
+
gooddata_pipelines-1.49.1.dev1.dist-info/licenses/LICENSE.txt,sha256=PNC7WXGIo6OKkNoPLRxlVrw6jaLcjSTUsSxy9Xcu9Jo,560365
|
|
54
|
+
gooddata_pipelines-1.49.1.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|