gooddata-pipelines 1.50.1.dev2__py3-none-any.whl → 1.52.0__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.

@@ -1,7 +1,6 @@
1
1
  # (C) 2025 GoodData Corporation
2
2
 
3
- from dataclasses import dataclass
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
- @dataclass
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
- @dataclass
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
- @dataclass(frozen=True)
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
- @dataclass(frozen=True)
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
- @dataclass(frozen=True)
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
- # TODO: consider using attrs instead of dataclasses for these models. Dataclasses
6
- # have different functionality per Python version (not package version).
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
- @dataclass
15
+ @attrs.define
18
16
  class WorkspaceUserDataFilters:
19
17
  workspace_id: str
20
- user_data_filters: list["UserDataFilterGroup"] = field(default_factory=list)
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 BaseModel, Field, ValidationInfo, field_validator
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
@@ -22,7 +22,7 @@ class WorkspaceDataMaps:
22
22
 
23
23
 
24
24
  class WorkspaceBase(BaseModel):
25
- model_config = ConfigDict(coerce_numbers_to_str=True)
25
+ model_config = ConfigDict(coerce_numbers_to_str=True, extra="forbid")
26
26
 
27
27
  parent_id: str
28
28
  workspace_id: str
@@ -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
- attrs: dict[str, str] = {}
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
- attrs.update(
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
- if value is None:
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
- attrs.update(overrides)
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
- return attrs
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.50.1.dev2
3
+ Version: 1.52.0
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.50.1.dev2
11
+ Requires-Dist: gooddata-sdk~=1.52.0
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
@@ -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=ex1tGwETdHDDBRJ_DGKZsZbH6uoRuOrbGbKOC976H5s,7940
12
- gooddata_pipelines/backup_and_restore/backup_manager.py,sha256=kWxhxe5K8_oK3tz2e1RBMpyHHv18_UA_QVlXQeb7UUk,15227
13
- gooddata_pipelines/backup_and_restore/constants.py,sha256=m8wAYhVGlRlfAgiC_54wJr6N8HDEAT7hIfrH1N2UrQY,884
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,19 +35,19 @@ 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=4Q3tbpTU4FPpCVE1t402mP3lJMrLFTDX4AgWG_pIIg0,8321
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=y0q5E91AhxIkf_EHW0swCjNUkiiAOFXarAhvjUKVVKw,740
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=buyNtDShvAJL4mFZSV-UqK_9JAL_2-AaIlGYCHibhHo,7244
47
- gooddata_pipelines/provisioning/entities/users/models/user_groups.py,sha256=Odp4yZoK2vC40jgh7FBKmaIINpwffl62uoaT8Xxr-14,1160
48
- gooddata_pipelines/provisioning/entities/users/models/users.py,sha256=hR5on68NEpw3KAPooR3Z1TRUzV5nbp0jrrOLUDW8P24,2424
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=-ehte9HLNos3l6yLip4mZU6wBcmY_Yzwq0t0m0fhwPI,2031
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
@@ -55,10 +55,10 @@ gooddata_pipelines/provisioning/entities/workspaces/workspace_data_validator.py,
55
55
  gooddata_pipelines/provisioning/utils/__init__.py,sha256=-BG28PGDbalLyZGQjpFG0pjdIvtf25ut0r8ZwZVbi4s,32
56
56
  gooddata_pipelines/provisioning/utils/context_objects.py,sha256=HJoeumH_gXwM6X-GO3HkC4w-6RYozz6-aqQOhDnu7no,879
57
57
  gooddata_pipelines/provisioning/utils/exceptions.py,sha256=1WnAOlPhqOf0xRcvn70lxAlLb8Oo6m6WCYS4hj9uzDU,3630
58
- gooddata_pipelines/provisioning/utils/utils.py,sha256=uF3k5hmoM5d6UoWWfPGCQgT_861zcU-ACyaQHHOOncY,2434
58
+ gooddata_pipelines/provisioning/utils/utils.py,sha256=u-nVVp6ykY4MZqRXBoPCKLrFlUunLF-cugF9SpSzL1E,2766
59
59
  gooddata_pipelines/utils/__init__.py,sha256=s9TtSjKqo1gSGWOVoGrXaGi1TsbRowjRDYKtjmKy7BY,155
60
60
  gooddata_pipelines/utils/rate_limiter.py,sha256=owbcEZhUxlTnE7rRHiWQ8XBC-vML2fVPbt41EeGEM7o,2002
61
- gooddata_pipelines-1.50.1.dev2.dist-info/METADATA,sha256=7ikqYG7cNfxQm5dBgWO1PcPvGqANtyYj6MoX2BQQsVA,3642
62
- gooddata_pipelines-1.50.1.dev2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
63
- gooddata_pipelines-1.50.1.dev2.dist-info/licenses/LICENSE.txt,sha256=PNC7WXGIo6OKkNoPLRxlVrw6jaLcjSTUsSxy9Xcu9Jo,560365
64
- gooddata_pipelines-1.50.1.dev2.dist-info/RECORD,,
61
+ gooddata_pipelines-1.52.0.dist-info/METADATA,sha256=-v_fmBYAqn4r8jQ-tBiujOS2bnEIaJj1gokj1fuSMxE,3632
62
+ gooddata_pipelines-1.52.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
63
+ gooddata_pipelines-1.52.0.dist-info/licenses/LICENSE.txt,sha256=PNC7WXGIo6OKkNoPLRxlVrw6jaLcjSTUsSxy9Xcu9Jo,560365
64
+ gooddata_pipelines-1.52.0.dist-info/RECORD,,