gooddata-pipelines 1.51.0__py3-none-any.whl → 1.52.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/__init__.py +6 -0
- gooddata_pipelines/backup_and_restore/backup_input_processor.py +2 -3
- gooddata_pipelines/backup_and_restore/backup_manager.py +2 -2
- gooddata_pipelines/backup_and_restore/constants.py +4 -4
- gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py +8 -10
- gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py +2 -2
- gooddata_pipelines/provisioning/entities/users/models/permissions.py +3 -1
- gooddata_pipelines/provisioning/entities/users/models/user_groups.py +9 -1
- gooddata_pipelines/provisioning/entities/users/models/users.py +3 -1
- gooddata_pipelines/provisioning/entities/workspaces/models.py +1 -1
- gooddata_pipelines/provisioning/generic/__init__.py +1 -0
- gooddata_pipelines/provisioning/generic/config.py +118 -0
- gooddata_pipelines/provisioning/generic/provision.py +49 -0
- gooddata_pipelines/provisioning/utils/utils.py +24 -15
- {gooddata_pipelines-1.51.0.dist-info → gooddata_pipelines-1.52.1.dev1.dist-info}/METADATA +14 -10
- {gooddata_pipelines-1.51.0.dist-info → gooddata_pipelines-1.52.1.dev1.dist-info}/RECORD +18 -15
- {gooddata_pipelines-1.51.0.dist-info → gooddata_pipelines-1.52.1.dev1.dist-info}/WHEEL +0 -0
- {gooddata_pipelines-1.51.0.dist-info → gooddata_pipelines-1.52.1.dev1.dist-info}/licenses/LICENSE.txt +0 -0
gooddata_pipelines/__init__.py
CHANGED
|
@@ -51,6 +51,10 @@ from .provisioning.entities.workspaces.models import (
|
|
|
51
51
|
)
|
|
52
52
|
from .provisioning.entities.workspaces.workspace import WorkspaceProvisioner
|
|
53
53
|
|
|
54
|
+
# -------- Generic Provisioning --------
|
|
55
|
+
from .provisioning.generic.config import WorkflowType
|
|
56
|
+
from .provisioning.generic.provision import provision
|
|
57
|
+
|
|
54
58
|
__all__ = [
|
|
55
59
|
"BackupManager",
|
|
56
60
|
"BackupRestoreConfig",
|
|
@@ -79,5 +83,7 @@ __all__ = [
|
|
|
79
83
|
"CustomFieldDefinition",
|
|
80
84
|
"ColumnDataType",
|
|
81
85
|
"CustomFieldType",
|
|
86
|
+
"provision",
|
|
87
|
+
"WorkflowType",
|
|
82
88
|
"__version__",
|
|
83
89
|
]
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import attrs
|
|
5
4
|
import requests
|
|
6
5
|
|
|
7
6
|
from gooddata_pipelines.api import GoodDataApi
|
|
@@ -46,7 +45,7 @@ class BackupInputProcessor:
|
|
|
46
45
|
)
|
|
47
46
|
self.all_workspaces_endpoint = f"{self.base_workspace_endpoint}?page=0&size={self.page_size}&sort=name,asc&metaInclude=page"
|
|
48
47
|
|
|
49
|
-
@
|
|
48
|
+
@attrs.define
|
|
50
49
|
class _ProcessDataOutput:
|
|
51
50
|
workspace_ids: list[str]
|
|
52
51
|
sub_parents: list[str] | None = None
|
|
@@ -6,10 +6,10 @@ import shutil
|
|
|
6
6
|
import tempfile
|
|
7
7
|
import time
|
|
8
8
|
import traceback
|
|
9
|
-
from dataclasses import dataclass
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
from typing import Any, Type
|
|
12
11
|
|
|
12
|
+
import attrs
|
|
13
13
|
import requests
|
|
14
14
|
import yaml
|
|
15
15
|
from gooddata_sdk.utils import PROFILES_FILE_PATH, profile_content
|
|
@@ -40,7 +40,7 @@ from gooddata_pipelines.logger import LogObserver
|
|
|
40
40
|
from gooddata_pipelines.utils.rate_limiter import RateLimiter
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
@
|
|
43
|
+
@attrs.define
|
|
44
44
|
class BackupBatch:
|
|
45
45
|
list_of_ids: list[str]
|
|
46
46
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
2
|
import datetime
|
|
3
|
-
from dataclasses import dataclass
|
|
4
3
|
|
|
4
|
+
import attrs
|
|
5
5
|
from gooddata_sdk._version import __version__ as sdk_version
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
@
|
|
8
|
+
@attrs.frozen
|
|
9
9
|
class DirNames:
|
|
10
10
|
"""
|
|
11
11
|
Folder names used in the SDK backup process:
|
|
@@ -21,14 +21,14 @@ class DirNames:
|
|
|
21
21
|
UDF = "user_data_filters"
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
@
|
|
24
|
+
@attrs.frozen
|
|
25
25
|
class ApiDefaults:
|
|
26
26
|
DEFAULT_PAGE_SIZE = 100
|
|
27
27
|
DEFAULT_BATCH_SIZE = 100
|
|
28
28
|
DEFAULT_API_CALLS_PER_SECOND = 1.0
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@
|
|
31
|
+
@attrs.frozen
|
|
32
32
|
class BackupSettings(ApiDefaults):
|
|
33
33
|
MAX_RETRIES = 3
|
|
34
34
|
RETRY_DELAY = 5 # seconds
|
|
@@ -2,31 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
"""This module defines data models for user data filters in a GoodData workspace."""
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import attrs
|
|
6
|
+
from pydantic import BaseModel, ConfigDict
|
|
7
7
|
|
|
8
|
-
from dataclasses import dataclass, field
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
@dataclass
|
|
9
|
+
@attrs.define
|
|
12
10
|
class UserDataFilterGroup:
|
|
13
11
|
udf_id: str
|
|
14
12
|
udf_values: list[str]
|
|
15
13
|
|
|
16
14
|
|
|
17
|
-
@
|
|
15
|
+
@attrs.define
|
|
18
16
|
class WorkspaceUserDataFilters:
|
|
19
17
|
workspace_id: str
|
|
20
|
-
user_data_filters: list["UserDataFilterGroup"] = field(
|
|
18
|
+
user_data_filters: list["UserDataFilterGroup"] = attrs.field(factory=list)
|
|
19
|
+
|
|
21
20
|
|
|
21
|
+
class UserDataFilterFullLoad(BaseModel):
|
|
22
|
+
model_config = ConfigDict(extra="forbid")
|
|
22
23
|
|
|
23
|
-
@dataclass
|
|
24
|
-
class UserDataFilterFullLoad:
|
|
25
24
|
workspace_id: str
|
|
26
25
|
udf_id: str
|
|
27
26
|
udf_value: str
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
@dataclass
|
|
31
29
|
class UserDataFilterIncrementalLoad(UserDataFilterFullLoad):
|
|
32
30
|
is_active: bool
|
|
@@ -50,6 +50,8 @@ class UserDataFilterProvisioner(
|
|
|
50
50
|
ldm_column_name: str = ""
|
|
51
51
|
maql_column_name: str = ""
|
|
52
52
|
|
|
53
|
+
FULL_LOAD_TYPE = UserDataFilterFullLoad
|
|
54
|
+
|
|
53
55
|
def set_ldm_column_name(self, ldm_column_name: str) -> None:
|
|
54
56
|
"""Set the LDM column name for user data filters.
|
|
55
57
|
|
|
@@ -214,8 +216,6 @@ class UserDataFilterProvisioner(
|
|
|
214
216
|
)
|
|
215
217
|
self._create_user_data_filters(grouped_db_user_data_filters)
|
|
216
218
|
|
|
217
|
-
self.logger.info("User data filters provisioning completed")
|
|
218
|
-
|
|
219
219
|
def _provision_incremental_load(self) -> None:
|
|
220
220
|
"""Provision user data filters in GoodData workspaces."""
|
|
221
221
|
raise NotImplementedError("Not implemented yet.")
|
|
@@ -9,7 +9,7 @@ from gooddata_sdk.catalog.permission.declarative_model.permission import (
|
|
|
9
9
|
CatalogDeclarativeSingleWorkspacePermission,
|
|
10
10
|
CatalogDeclarativeWorkspacePermissions,
|
|
11
11
|
)
|
|
12
|
-
from pydantic import BaseModel
|
|
12
|
+
from pydantic import BaseModel, ConfigDict
|
|
13
13
|
|
|
14
14
|
from gooddata_pipelines.provisioning.utils.exceptions import BaseUserException
|
|
15
15
|
|
|
@@ -23,6 +23,8 @@ class EntityType(str, Enum):
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class BasePermission(BaseModel):
|
|
26
|
+
model_config = ConfigDict(extra="forbid")
|
|
27
|
+
|
|
26
28
|
permission: str
|
|
27
29
|
workspace_id: str
|
|
28
30
|
entity_id: str
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import (
|
|
4
|
+
BaseModel,
|
|
5
|
+
ConfigDict,
|
|
6
|
+
Field,
|
|
7
|
+
ValidationInfo,
|
|
8
|
+
field_validator,
|
|
9
|
+
)
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
class UserGroupBase(BaseModel):
|
|
13
|
+
model_config = ConfigDict(extra="forbid")
|
|
14
|
+
|
|
7
15
|
user_group_id: str
|
|
8
16
|
user_group_name: str
|
|
9
17
|
parent_user_groups: list[str] = Field(default_factory=list)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
from gooddata_sdk.catalog.user.entity_model.user import CatalogUser
|
|
6
|
-
from pydantic import BaseModel, Field
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class UserProfile(BaseModel):
|
|
@@ -18,6 +18,8 @@ class UserProfile(BaseModel):
|
|
|
18
18
|
class BaseUser(BaseModel):
|
|
19
19
|
"""Base class containing shared user fields and functionality."""
|
|
20
20
|
|
|
21
|
+
model_config = ConfigDict(extra="forbid")
|
|
22
|
+
|
|
21
23
|
user_id: str
|
|
22
24
|
firstname: str | None
|
|
23
25
|
lastname: str | None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Type, TypeAlias
|
|
5
|
+
|
|
6
|
+
import attrs
|
|
7
|
+
|
|
8
|
+
from gooddata_pipelines.provisioning.entities.users.models.permissions import (
|
|
9
|
+
PermissionFullLoad,
|
|
10
|
+
PermissionIncrementalLoad,
|
|
11
|
+
)
|
|
12
|
+
from gooddata_pipelines.provisioning.entities.users.models.user_groups import (
|
|
13
|
+
UserGroupFullLoad,
|
|
14
|
+
UserGroupIncrementalLoad,
|
|
15
|
+
)
|
|
16
|
+
from gooddata_pipelines.provisioning.entities.users.models.users import (
|
|
17
|
+
UserFullLoad,
|
|
18
|
+
UserIncrementalLoad,
|
|
19
|
+
)
|
|
20
|
+
from gooddata_pipelines.provisioning.entities.users.permissions import (
|
|
21
|
+
PermissionProvisioner,
|
|
22
|
+
)
|
|
23
|
+
from gooddata_pipelines.provisioning.entities.users.user_groups import (
|
|
24
|
+
UserGroupProvisioner,
|
|
25
|
+
)
|
|
26
|
+
from gooddata_pipelines.provisioning.entities.users.users import UserProvisioner
|
|
27
|
+
from gooddata_pipelines.provisioning.entities.workspaces.models import (
|
|
28
|
+
WorkspaceFullLoad,
|
|
29
|
+
WorkspaceIncrementalLoad,
|
|
30
|
+
)
|
|
31
|
+
from gooddata_pipelines.provisioning.entities.workspaces.workspace import (
|
|
32
|
+
WorkspaceProvisioner,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
ValidationModel: TypeAlias = (
|
|
36
|
+
PermissionFullLoad
|
|
37
|
+
| PermissionIncrementalLoad
|
|
38
|
+
| UserFullLoad
|
|
39
|
+
| UserIncrementalLoad
|
|
40
|
+
| UserGroupFullLoad
|
|
41
|
+
| UserGroupIncrementalLoad
|
|
42
|
+
| WorkspaceFullLoad
|
|
43
|
+
| WorkspaceIncrementalLoad
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
Provisioner: TypeAlias = (
|
|
47
|
+
PermissionProvisioner
|
|
48
|
+
| UserProvisioner
|
|
49
|
+
| UserGroupProvisioner
|
|
50
|
+
| WorkspaceProvisioner
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class LoadType(str, Enum):
|
|
55
|
+
FULL = "full"
|
|
56
|
+
INCREMENTAL = "incremental"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class WorkflowType(str, Enum):
|
|
60
|
+
WORKSPACE_FULL_LOAD = "workspace_full_load"
|
|
61
|
+
WORKSPACE_INCREMENTAL_LOAD = "workspace_incremental_load"
|
|
62
|
+
USER_FULL_LOAD = "user_full_load"
|
|
63
|
+
USER_INCREMENTAL_LOAD = "user_incremental_load"
|
|
64
|
+
USER_GROUP_FULL_LOAD = "user_group_full_load"
|
|
65
|
+
USER_GROUP_INCREMENTAL_LOAD = "user_group_incremental_load"
|
|
66
|
+
PERMISSION_FULL_LOAD = "permission_full_load"
|
|
67
|
+
PERMISSION_INCREMENTAL_LOAD = "permission_incremental_load"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@attrs.define
|
|
71
|
+
class ProvisioningConfig:
|
|
72
|
+
validation_model: Type[ValidationModel]
|
|
73
|
+
provisioner_class: Type[Provisioner]
|
|
74
|
+
load_type: LoadType
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
PROVISIONING_CONFIG = {
|
|
78
|
+
WorkflowType.WORKSPACE_FULL_LOAD: ProvisioningConfig(
|
|
79
|
+
validation_model=WorkspaceFullLoad,
|
|
80
|
+
provisioner_class=WorkspaceProvisioner,
|
|
81
|
+
load_type=LoadType.FULL,
|
|
82
|
+
),
|
|
83
|
+
WorkflowType.WORKSPACE_INCREMENTAL_LOAD: ProvisioningConfig(
|
|
84
|
+
validation_model=WorkspaceIncrementalLoad,
|
|
85
|
+
provisioner_class=WorkspaceProvisioner,
|
|
86
|
+
load_type=LoadType.INCREMENTAL,
|
|
87
|
+
),
|
|
88
|
+
WorkflowType.USER_FULL_LOAD: ProvisioningConfig(
|
|
89
|
+
validation_model=UserFullLoad,
|
|
90
|
+
provisioner_class=UserProvisioner,
|
|
91
|
+
load_type=LoadType.FULL,
|
|
92
|
+
),
|
|
93
|
+
WorkflowType.USER_INCREMENTAL_LOAD: ProvisioningConfig(
|
|
94
|
+
validation_model=UserIncrementalLoad,
|
|
95
|
+
provisioner_class=UserProvisioner,
|
|
96
|
+
load_type=LoadType.INCREMENTAL,
|
|
97
|
+
),
|
|
98
|
+
WorkflowType.USER_GROUP_FULL_LOAD: ProvisioningConfig(
|
|
99
|
+
validation_model=UserGroupFullLoad,
|
|
100
|
+
provisioner_class=UserGroupProvisioner,
|
|
101
|
+
load_type=LoadType.FULL,
|
|
102
|
+
),
|
|
103
|
+
WorkflowType.USER_GROUP_INCREMENTAL_LOAD: ProvisioningConfig(
|
|
104
|
+
validation_model=UserGroupIncrementalLoad,
|
|
105
|
+
provisioner_class=UserGroupProvisioner,
|
|
106
|
+
load_type=LoadType.INCREMENTAL,
|
|
107
|
+
),
|
|
108
|
+
WorkflowType.PERMISSION_FULL_LOAD: ProvisioningConfig(
|
|
109
|
+
validation_model=PermissionFullLoad,
|
|
110
|
+
provisioner_class=PermissionProvisioner,
|
|
111
|
+
load_type=LoadType.FULL,
|
|
112
|
+
),
|
|
113
|
+
WorkflowType.PERMISSION_INCREMENTAL_LOAD: ProvisioningConfig(
|
|
114
|
+
validation_model=PermissionIncrementalLoad,
|
|
115
|
+
provisioner_class=PermissionProvisioner,
|
|
116
|
+
load_type=LoadType.INCREMENTAL,
|
|
117
|
+
),
|
|
118
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from gooddata_pipelines.logger import LoggerLike
|
|
6
|
+
from gooddata_pipelines.provisioning.generic.config import (
|
|
7
|
+
PROVISIONING_CONFIG,
|
|
8
|
+
LoadType,
|
|
9
|
+
WorkflowType,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def provision(
|
|
14
|
+
data: list[dict[str, Any]],
|
|
15
|
+
workflow_type: WorkflowType,
|
|
16
|
+
host: str,
|
|
17
|
+
token: str,
|
|
18
|
+
logger: LoggerLike | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Generic provisioning function accepting raw data and workflow type.
|
|
21
|
+
|
|
22
|
+
The function will validate data based on the selected workflow type and run
|
|
23
|
+
the corresponding provisioning in full or incremental mode.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
data: List of dictionaries containing the data to be provisioned.
|
|
27
|
+
workflow_type: The type of workflow to run.
|
|
28
|
+
host: The host of the GoodData platform.
|
|
29
|
+
token: The token for the GoodData platform.
|
|
30
|
+
logger: The logger to use for logging.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
if workflow_type not in PROVISIONING_CONFIG:
|
|
34
|
+
raise ValueError(f"Invalid workflow type: {workflow_type}")
|
|
35
|
+
|
|
36
|
+
config = PROVISIONING_CONFIG[workflow_type]
|
|
37
|
+
|
|
38
|
+
provisioner = config.provisioner_class.create(host, token)
|
|
39
|
+
validated_data: list = [config.validation_model(**item) for item in data]
|
|
40
|
+
|
|
41
|
+
if logger:
|
|
42
|
+
provisioner.logger.subscribe(logger)
|
|
43
|
+
|
|
44
|
+
if config.load_type == LoadType.FULL:
|
|
45
|
+
provisioner.full_load(validated_data)
|
|
46
|
+
elif config.load_type == LoadType.INCREMENTAL:
|
|
47
|
+
provisioner.incremental_load(validated_data)
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(f"Invalid load type: {config.load_type}")
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
"""Module for utilities used in GoodData Pipelines provisioning."""
|
|
4
4
|
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
5
7
|
import attrs
|
|
6
8
|
from requests import Response
|
|
7
9
|
|
|
@@ -11,9 +13,8 @@ class AttributesMixin:
|
|
|
11
13
|
Mixin class to provide a method for getting attributes of an object which may or may not exist.
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
|
-
@staticmethod
|
|
15
16
|
def get_attrs(
|
|
16
|
-
*objects: object, overrides: dict[str, str] | None = None
|
|
17
|
+
self, *objects: object, overrides: dict[str, str] | None = None
|
|
17
18
|
) -> dict[str, str]:
|
|
18
19
|
"""
|
|
19
20
|
Returns a dictionary of attributes from the given objects.
|
|
@@ -27,11 +28,11 @@ class AttributesMixin:
|
|
|
27
28
|
"""
|
|
28
29
|
# TODO: This might not work great with nested objects, values which are lists of objects etc.
|
|
29
30
|
# If we care about parsing the logs back from the string, we should consider some other approach
|
|
30
|
-
|
|
31
|
+
attributes: dict[str, str] = {}
|
|
31
32
|
for context_object in objects:
|
|
32
33
|
if isinstance(context_object, Response):
|
|
33
34
|
# for request.Response objects, keys need to be renamed to match the log schema
|
|
34
|
-
|
|
35
|
+
attributes.update(
|
|
35
36
|
{
|
|
36
37
|
"http_status": str(context_object.status_code),
|
|
37
38
|
"http_method": getattr(
|
|
@@ -42,23 +43,31 @@ class AttributesMixin:
|
|
|
42
43
|
),
|
|
43
44
|
}
|
|
44
45
|
)
|
|
46
|
+
elif attrs.has(type(context_object)):
|
|
47
|
+
for key, value in attrs.asdict(
|
|
48
|
+
cast(attrs.AttrsInstance, context_object)
|
|
49
|
+
).items():
|
|
50
|
+
self._add_to_dict(attributes, key, value)
|
|
45
51
|
else:
|
|
46
52
|
# Generic handling for other objects
|
|
47
53
|
for key, value in context_object.__dict__.items():
|
|
48
|
-
|
|
49
|
-
continue
|
|
50
|
-
|
|
51
|
-
if isinstance(value, list):
|
|
52
|
-
attrs[key] = ", ".join(
|
|
53
|
-
str(list_item) for list_item in value
|
|
54
|
-
)
|
|
55
|
-
else:
|
|
56
|
-
attrs[key] = str(value)
|
|
54
|
+
self._add_to_dict(attributes, key, value)
|
|
57
55
|
|
|
58
56
|
if overrides:
|
|
59
|
-
|
|
57
|
+
attributes.update(overrides)
|
|
58
|
+
|
|
59
|
+
return attributes
|
|
60
|
+
|
|
61
|
+
def _add_to_dict(
|
|
62
|
+
self, attributes: dict[str, str], key: str, value: Any
|
|
63
|
+
) -> None:
|
|
64
|
+
if value is None:
|
|
65
|
+
return
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
if isinstance(value, list):
|
|
68
|
+
attributes[key] = ", ".join(str(list_item) for list_item in value)
|
|
69
|
+
else:
|
|
70
|
+
attributes[key] = str(value)
|
|
62
71
|
|
|
63
72
|
|
|
64
73
|
@attrs.define
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gooddata-pipelines
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.52.1.dev1
|
|
4
4
|
Summary: GoodData Cloud lifecycle automation pipelines
|
|
5
5
|
Author-email: GoodData <support@gooddata.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ License-File: LICENSE.txt
|
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Requires-Dist: boto3-stubs<2.0.0,>=1.39.3
|
|
10
10
|
Requires-Dist: boto3<2.0.0,>=1.39.3
|
|
11
|
-
Requires-Dist: gooddata-sdk~=1.
|
|
11
|
+
Requires-Dist: gooddata-sdk~=1.52.1.dev1
|
|
12
12
|
Requires-Dist: pydantic<3.0.0,>=2.11.3
|
|
13
13
|
Requires-Dist: requests<3.0.0,>=2.32.3
|
|
14
14
|
Requires-Dist: types-pyyaml<7.0.0,>=6.0.12.20250326
|
|
@@ -45,33 +45,37 @@ The provisioning module exposes _Provisioner_ classes reflecting the different e
|
|
|
45
45
|
|
|
46
46
|
```python
|
|
47
47
|
import os
|
|
48
|
+
import logging
|
|
49
|
+
|
|
48
50
|
from csv import DictReader
|
|
49
51
|
from pathlib import Path
|
|
50
52
|
|
|
51
53
|
# Import the Entity Provisioner class and corresponding model from gooddata_pipelines library
|
|
52
54
|
from gooddata_pipelines import UserFullLoad, UserProvisioner
|
|
53
|
-
from gooddata_pipelines.logger.logger import LogObserver
|
|
54
|
-
|
|
55
|
-
# Optionally, subscribe a standard Python logger to the LogObserver
|
|
56
|
-
import logging
|
|
57
|
-
logger = logging.getLogger(__name__)
|
|
58
|
-
LogObserver().subscribe(logger)
|
|
59
55
|
|
|
60
56
|
# Create the Provisioner instance - you can also create the instance from a GDC yaml profile
|
|
61
57
|
provisioner = UserProvisioner(
|
|
62
58
|
host=os.environ["GDC_HOSTNAME"], token=os.environ["GDC_AUTH_TOKEN"]
|
|
63
59
|
)
|
|
64
60
|
|
|
61
|
+
# Optional: set up logging and subscribe to logs emitted by the provisioner
|
|
62
|
+
logging.basicConfig(level=logging.INFO)
|
|
63
|
+
logger = logging.getLogger(__name__)
|
|
64
|
+
provisioner.logger.subscribe(logger)
|
|
65
|
+
|
|
65
66
|
# Load your data from your data source
|
|
66
67
|
source_data_path: Path = Path("path/to/some.csv")
|
|
67
68
|
source_data_reader = DictReader(source_data_path.read_text().splitlines())
|
|
68
69
|
source_data = [row for row in source_data_reader]
|
|
69
70
|
|
|
70
|
-
# Validate your input data
|
|
71
|
+
# Validate your input data
|
|
71
72
|
full_load_data: list[UserFullLoad] = UserFullLoad.from_list_of_dicts(
|
|
72
73
|
source_data
|
|
73
74
|
)
|
|
75
|
+
|
|
76
|
+
# Run the provisioning
|
|
74
77
|
provisioner.full_load(full_load_data)
|
|
78
|
+
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
## Bugs & Requests
|
|
@@ -81,5 +85,5 @@ or request features.
|
|
|
81
85
|
|
|
82
86
|
## Changelog
|
|
83
87
|
|
|
84
|
-
See
|
|
88
|
+
See [Github releases](https://github.com/gooddata/gooddata-python-sdk/releases) for released versions
|
|
85
89
|
and a list of changes.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
gooddata_pipelines/__init__.py,sha256=
|
|
1
|
+
gooddata_pipelines/__init__.py,sha256=dGbyckzT8MrxL5SljSOj2jo5DFXZ2HYn0kBbAe0vO7s,2602
|
|
2
2
|
gooddata_pipelines/_version.py,sha256=Zi8Ht5ofjFeSYGG5USixQtJNB1po6okh0Rez8VyAsFM,200
|
|
3
3
|
gooddata_pipelines/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
gooddata_pipelines/api/__init__.py,sha256=0WaBI2XMdkkZgnUsQ9kqipNzh2l2zamZvUt_qjp8xCk,106
|
|
@@ -8,9 +8,9 @@ gooddata_pipelines/api/gooddata_api_wrapper.py,sha256=t7dFrXJ6X4yXS9XDthOmvd2Cyz
|
|
|
8
8
|
gooddata_pipelines/api/gooddata_sdk.py,sha256=wd5O4e9BQLWUawt6odrs5a51nqFGthBkvqh9WOiW36Q,13734
|
|
9
9
|
gooddata_pipelines/api/utils.py,sha256=3QY_aYH17I9THoCINE3l-n5oj52k-gNeT1wv6Z_VxN8,1433
|
|
10
10
|
gooddata_pipelines/backup_and_restore/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
11
|
-
gooddata_pipelines/backup_and_restore/backup_input_processor.py,sha256=
|
|
12
|
-
gooddata_pipelines/backup_and_restore/backup_manager.py,sha256=
|
|
13
|
-
gooddata_pipelines/backup_and_restore/constants.py,sha256=
|
|
11
|
+
gooddata_pipelines/backup_and_restore/backup_input_processor.py,sha256=iUB-bvYMsOc6FGiHsIzHPWkz-TGM8VontudlVWzmBDI,7921
|
|
12
|
+
gooddata_pipelines/backup_and_restore/backup_manager.py,sha256=ubYgePX72pg4hCMPXCeX8_T6QHHhQS8fMbHx__nbfso,15209
|
|
13
|
+
gooddata_pipelines/backup_and_restore/constants.py,sha256=SpCll6C-QcWYYVhcYZOyOGG7ELP6M87ioarbUr1nN9I,833
|
|
14
14
|
gooddata_pipelines/backup_and_restore/csv_reader.py,sha256=0Kw7mJT7REj3Gjqfsc6YT9MbhcqfCGNB_SKBwzTI1rk,1268
|
|
15
15
|
gooddata_pipelines/backup_and_restore/models/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
16
16
|
gooddata_pipelines/backup_and_restore/models/input_type.py,sha256=CBKJigKdmZ-NJD9MSfNhq89bo86W0AqCMMoyonbd1QA,239
|
|
@@ -35,30 +35,33 @@ gooddata_pipelines/provisioning/provisioning.py,sha256=UUHClT0q6O1XDAgiR2M23eFgt
|
|
|
35
35
|
gooddata_pipelines/provisioning/assets/wdf_setting.json,sha256=nxOLGZkEQiMdARcUDER5ygqr3Zu-MQlLlUyXVhPUq64,280
|
|
36
36
|
gooddata_pipelines/provisioning/entities/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
37
37
|
gooddata_pipelines/provisioning/entities/user_data_filters/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
38
|
-
gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py,sha256=
|
|
38
|
+
gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py,sha256=aUtlhSyAvHUkCbF85EqbzWuHNXcXWjUx0FWmu5zEdho,8296
|
|
39
39
|
gooddata_pipelines/provisioning/entities/user_data_filters/models/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
40
|
-
gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py,sha256=
|
|
40
|
+
gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py,sha256=iu-uj4sbquTDRc_6ZQ7GEByA-dZ2P1sZGinXf7dNxOI,639
|
|
41
41
|
gooddata_pipelines/provisioning/entities/users/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
42
42
|
gooddata_pipelines/provisioning/entities/users/permissions.py,sha256=2k3oPI7WyABcD2TMmLPsMUDrAjnKM7Vw56kz_RWhcmI,7135
|
|
43
43
|
gooddata_pipelines/provisioning/entities/users/user_groups.py,sha256=-2Nca01ZMjXmnAGDUuKP5G7mqFyn4MnsgZsnS2oy7vg,8511
|
|
44
44
|
gooddata_pipelines/provisioning/entities/users/users.py,sha256=BPTbE0-lvwkgoTVwLUbMqmlq7L597nwRCSK5FaM8F4I,7730
|
|
45
45
|
gooddata_pipelines/provisioning/entities/users/models/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
46
|
-
gooddata_pipelines/provisioning/entities/users/models/permissions.py,sha256=
|
|
47
|
-
gooddata_pipelines/provisioning/entities/users/models/user_groups.py,sha256=
|
|
48
|
-
gooddata_pipelines/provisioning/entities/users/models/users.py,sha256=
|
|
46
|
+
gooddata_pipelines/provisioning/entities/users/models/permissions.py,sha256=90Th5QZgDB2ha9D4CvZpSOLKAykPVD7GDFvMVe6x-fE,7303
|
|
47
|
+
gooddata_pipelines/provisioning/entities/users/models/user_groups.py,sha256=AIgCf3UQij6sLrJxdhzfGyEHuXD33DiOWnBAZpjBEjA,1244
|
|
48
|
+
gooddata_pipelines/provisioning/entities/users/models/users.py,sha256=QE_2ACS5XtoWm01Aa1AK9JI2ELEjsOfU5jcuxP07GpA,2483
|
|
49
49
|
gooddata_pipelines/provisioning/entities/workspaces/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
50
|
-
gooddata_pipelines/provisioning/entities/workspaces/models.py,sha256
|
|
50
|
+
gooddata_pipelines/provisioning/entities/workspaces/models.py,sha256=HekbLKyTerzX5h-5PV4qHQb8WteOm88nxkTU3QQZHYw,2047
|
|
51
51
|
gooddata_pipelines/provisioning/entities/workspaces/workspace.py,sha256=jngaEKNlMfhjRr4rQ2ECQDoh0gk7KaZTMuTazPLECnM,11505
|
|
52
52
|
gooddata_pipelines/provisioning/entities/workspaces/workspace_data_filters.py,sha256=0dNcK7tkp40XulCj7EPoB4zVeyQbRx2Tt4yAfgLrm50,10736
|
|
53
53
|
gooddata_pipelines/provisioning/entities/workspaces/workspace_data_parser.py,sha256=akiN8F9x-6xo7KXLJ40iOlmBImEKqWlGYlN3lpF4jQs,4562
|
|
54
54
|
gooddata_pipelines/provisioning/entities/workspaces/workspace_data_validator.py,sha256=t6RWNsrDpebyOgB4c_ctqrkio72jBHqsXqk-ntBTkA4,7225
|
|
55
|
+
gooddata_pipelines/provisioning/generic/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
56
|
+
gooddata_pipelines/provisioning/generic/config.py,sha256=RMIa7UXiXRsodsHwmg_9w74m0WLDnEF6cce0OiDZWpQ,3689
|
|
57
|
+
gooddata_pipelines/provisioning/generic/provision.py,sha256=TgBFbOroG9nHVhHQkqAmpYt2gDiaVkbMNyAVbazCwRg,1541
|
|
55
58
|
gooddata_pipelines/provisioning/utils/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
|
|
56
59
|
gooddata_pipelines/provisioning/utils/context_objects.py,sha256=HJoeumH_gXwM6X-GO3HkC4w-6RYozz6-aqQOhDnu7no,879
|
|
57
60
|
gooddata_pipelines/provisioning/utils/exceptions.py,sha256=1WnAOlPhqOf0xRcvn70lxAlLb8Oo6m6WCYS4hj9uzDU,3630
|
|
58
|
-
gooddata_pipelines/provisioning/utils/utils.py,sha256=
|
|
61
|
+
gooddata_pipelines/provisioning/utils/utils.py,sha256=u-nVVp6ykY4MZqRXBoPCKLrFlUunLF-cugF9SpSzL1E,2766
|
|
59
62
|
gooddata_pipelines/utils/__init__.py,sha256=s9TtSjKqo1gSGWOVoGrXaGi1TsbRowjRDYKtjmKy7BY,155
|
|
60
63
|
gooddata_pipelines/utils/rate_limiter.py,sha256=owbcEZhUxlTnE7rRHiWQ8XBC-vML2fVPbt41EeGEM7o,2002
|
|
61
|
-
gooddata_pipelines-1.
|
|
62
|
-
gooddata_pipelines-1.
|
|
63
|
-
gooddata_pipelines-1.
|
|
64
|
-
gooddata_pipelines-1.
|
|
64
|
+
gooddata_pipelines-1.52.1.dev1.dist-info/METADATA,sha256=Bq0BXaTIq36gNxCeBc63ijIms2O87QuZsDGDKg8T82o,3658
|
|
65
|
+
gooddata_pipelines-1.52.1.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
66
|
+
gooddata_pipelines-1.52.1.dev1.dist-info/licenses/LICENSE.txt,sha256=PNC7WXGIo6OKkNoPLRxlVrw6jaLcjSTUsSxy9Xcu9Jo,560365
|
|
67
|
+
gooddata_pipelines-1.52.1.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|