Skip to content

Commit 5e28fd3

Browse files
authored
Merge pull request #1167 from janmatzek/jmat-SVS-1259-forbid-extra-fields-when-validating-provisioning-input
feat(gooddata-pipelines): forbid extra fields when validating provisioning input
2 parents 511b8ca + 6af7911 commit 5e28fd3

File tree

13 files changed

+165
-47
lines changed

13 files changed

+165
-47
lines changed

gooddata-pipelines/TODO.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@ A list of outstanding tasks, features, or technical debt to be addressed in this
1010

1111
- [ ] Integrate with GoodDataApiClient
1212
- [ ] Consider replacing the SdkMethods wrapper with direct calls to the SDK methods
13-
- [ ] Consider using orjson library instead of json to load test data
1413
- [ ] Cleanup custom exceptions
1514
- [ ] Improve test coverage. Write missing unit tests for legacy code (e.g., user data filters)
1615

1716
## Documentation
1817

1918
- [ ] Improve package README
20-
- [ ] Workspace provisioning
21-
- [ ] User provisioning
22-
- [ ] User group provisioning
23-
- [ ] Permission provisioning
2419
- [ ] User data filter provisioning

gooddata-pipelines/gooddata_pipelines/backup_and_restore/backup_input_processor.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# (C) 2025 GoodData Corporation
22

3-
from dataclasses import dataclass
4-
3+
import attrs
54
import requests
65

76
from gooddata_pipelines.api import GoodDataApi
@@ -46,7 +45,7 @@ def set_endpoints(self) -> None:
4645
)
4746
self.all_workspaces_endpoint = f"{self.base_workspace_endpoint}?page=0&size={self.page_size}&sort=name,asc&metaInclude=page"
4847

49-
@dataclass
48+
@attrs.define
5049
class _ProcessDataOutput:
5150
workspace_ids: list[str]
5251
sub_parents: list[str] | None = None

gooddata-pipelines/gooddata_pipelines/backup_and_restore/backup_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import tempfile
77
import time
88
import traceback
9-
from dataclasses import dataclass
109
from pathlib import Path
1110
from typing import Any, Type
1211

12+
import attrs
1313
import requests
1414
import yaml
1515
from gooddata_sdk.utils import PROFILES_FILE_PATH, profile_content
@@ -40,7 +40,7 @@
4040
from gooddata_pipelines.utils.rate_limiter import RateLimiter
4141

4242

43-
@dataclass
43+
@attrs.define
4444
class BackupBatch:
4545
list_of_ids: list[str]
4646

gooddata-pipelines/gooddata_pipelines/backup_and_restore/constants.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# (C) 2025 GoodData Corporation
22
import datetime
3-
from dataclasses import dataclass
43

4+
import attrs
55
from gooddata_sdk._version import __version__ as sdk_version
66

77

8-
@dataclass(frozen=True)
8+
@attrs.frozen
99
class DirNames:
1010
"""
1111
Folder names used in the SDK backup process:
@@ -21,14 +21,14 @@ class DirNames:
2121
UDF = "user_data_filters"
2222

2323

24-
@dataclass(frozen=True)
24+
@attrs.frozen
2525
class ApiDefaults:
2626
DEFAULT_PAGE_SIZE = 100
2727
DEFAULT_BATCH_SIZE = 100
2828
DEFAULT_API_CALLS_PER_SECOND = 1.0
2929

3030

31-
@dataclass(frozen=True)
31+
@attrs.frozen
3232
class BackupSettings(ApiDefaults):
3333
MAX_RETRIES = 3
3434
RETRY_DELAY = 5 # seconds

gooddata-pipelines/gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,29 @@
22

33
"""This module defines data models for user data filters in a GoodData workspace."""
44

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
77

8-
from dataclasses import dataclass, field
98

10-
11-
@dataclass
9+
@attrs.define
1210
class UserDataFilterGroup:
1311
udf_id: str
1412
udf_values: list[str]
1513

1614

17-
@dataclass
15+
@attrs.define
1816
class WorkspaceUserDataFilters:
1917
workspace_id: str
20-
user_data_filters: list["UserDataFilterGroup"] = field(default_factory=list)
18+
user_data_filters: list["UserDataFilterGroup"] = attrs.field(factory=list)
19+
2120

21+
class UserDataFilterFullLoad(BaseModel):
22+
model_config = ConfigDict(extra="forbid")
2223

23-
@dataclass
24-
class UserDataFilterFullLoad:
2524
workspace_id: str
2625
udf_id: str
2726
udf_value: str
2827

2928

30-
@dataclass
3129
class UserDataFilterIncrementalLoad(UserDataFilterFullLoad):
3230
is_active: bool

gooddata-pipelines/gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class UserDataFilterProvisioner(
5050
ldm_column_name: str = ""
5151
maql_column_name: str = ""
5252

53+
FULL_LOAD_TYPE = UserDataFilterFullLoad
54+
5355
def set_ldm_column_name(self, ldm_column_name: str) -> None:
5456
"""Set the LDM column name for user data filters.
5557
@@ -214,8 +216,6 @@ def _provision_full_load(self) -> None:
214216
)
215217
self._create_user_data_filters(grouped_db_user_data_filters)
216218

217-
self.logger.info("User data filters provisioning completed")
218-
219219
def _provision_incremental_load(self) -> None:
220220
"""Provision user data filters in GoodData workspaces."""
221221
raise NotImplementedError("Not implemented yet.")

gooddata-pipelines/gooddata_pipelines/provisioning/entities/users/models/permissions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
CatalogDeclarativeSingleWorkspacePermission,
1010
CatalogDeclarativeWorkspacePermissions,
1111
)
12-
from pydantic import BaseModel
12+
from pydantic import BaseModel, ConfigDict
1313

1414
from gooddata_pipelines.provisioning.utils.exceptions import BaseUserException
1515

@@ -23,6 +23,8 @@ class EntityType(str, Enum):
2323

2424

2525
class BasePermission(BaseModel):
26+
model_config = ConfigDict(extra="forbid")
27+
2628
permission: str
2729
workspace_id: str
2830
entity_id: str

gooddata-pipelines/gooddata_pipelines/provisioning/entities/users/models/user_groups.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# (C) 2025 GoodData Corporation
22

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+
)
410

511

612
class UserGroupBase(BaseModel):
13+
model_config = ConfigDict(extra="forbid")
14+
715
user_group_id: str
816
user_group_name: str
917
parent_user_groups: list[str] = Field(default_factory=list)

gooddata-pipelines/gooddata_pipelines/provisioning/entities/users/models/users.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any
44

55
from gooddata_sdk.catalog.user.entity_model.user import CatalogUser
6-
from pydantic import BaseModel, Field
6+
from pydantic import BaseModel, ConfigDict, Field
77

88

99
class UserProfile(BaseModel):
@@ -18,6 +18,8 @@ class UserProfile(BaseModel):
1818
class BaseUser(BaseModel):
1919
"""Base class containing shared user fields and functionality."""
2020

21+
model_config = ConfigDict(extra="forbid")
22+
2123
user_id: str
2224
firstname: str | None
2325
lastname: str | None

gooddata-pipelines/gooddata_pipelines/provisioning/entities/workspaces/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class WorkspaceDataMaps:
2222

2323

2424
class WorkspaceBase(BaseModel):
25-
model_config = ConfigDict(coerce_numbers_to_str=True)
25+
model_config = ConfigDict(coerce_numbers_to_str=True, extra="forbid")
2626

2727
parent_id: str
2828
workspace_id: str

0 commit comments

Comments
 (0)