Skip to content

Commit 3b7f7b3

Browse files
committed
feat(gooddata-pipelines): incremental workspace provisioning
1 parent 83b7f81 commit 3b7f7b3

File tree

6 files changed

+60
-19
lines changed

6 files changed

+60
-19
lines changed

gooddata-pipelines/gooddata_pipelines/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@
3434
from .provisioning.entities.users.permissions import PermissionProvisioner
3535
from .provisioning.entities.users.user_groups import UserGroupProvisioner
3636
from .provisioning.entities.users.users import UserProvisioner
37-
from .provisioning.entities.workspaces.models import WorkspaceFullLoad
37+
from .provisioning.entities.workspaces.models import (
38+
WorkspaceFullLoad,
39+
WorkspaceIncrementalLoad,
40+
)
3841
from .provisioning.entities.workspaces.workspace import WorkspaceProvisioner
3942

4043
__all__ = [
@@ -53,6 +56,7 @@
5356
"UserGroupFullLoad",
5457
"UserProvisioner",
5558
"UserGroupProvisioner",
59+
"WorkspaceIncrementalLoad",
5660
"PermissionProvisioner",
5761
"UserDataFilterProvisioner",
5862
"UserDataFilterFullLoad",

gooddata-pipelines/gooddata_pipelines/api/gooddata_api.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77

88
import requests
99

10-
# TODO: Limit the use of "typing.Any". Improve readability by using either models
11-
# or typed dicts.
12-
1310
TIMEOUT = 60
1411
REQUEST_PAGE_SIZE = 250
1512
API_VERSION = "v1"

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

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class WorkspaceProvisioner(
4040
WorkspaceIncrementalLoad
4141
)
4242

43+
upstream_group: list[CatalogWorkspace]
44+
4345
def __init__(self, *args: str, **kwargs: str) -> None:
4446
"""Creates an instance of the WorkspaceProvisioner.
4547
@@ -97,10 +99,11 @@ def _create_or_update_panther_workspaces(
9799
workspace_ids_to_update: set[str],
98100
child_to_parent_map: dict[str, str],
99101
workspace_id_to_wdf_map: dict[str, dict[str, list[str]]],
102+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
100103
) -> None:
101104
action: Literal["CREATE", "UPDATE"]
102105

103-
for source_workspace in self.source_group_full:
106+
for source_workspace in source_group:
104107
if source_workspace.workspace_id in workspace_ids_to_update:
105108
action = "UPDATE"
106109
elif source_workspace.workspace_id in workspace_ids_to_create:
@@ -205,8 +208,8 @@ def _provision_full_load(self) -> None:
205208
)
206209

207210
# Get upstream children of all parent workspaces.
208-
self.upstream_group: list[CatalogWorkspace] = (
209-
self._api.get_panther_children_workspaces(self.maps.parent_ids)
211+
self.upstream_group = self._api.get_panther_children_workspaces(
212+
self.maps.parent_ids
210213
)
211214

212215
# Set maps that require upstream data.
@@ -240,6 +243,7 @@ def _provision_full_load(self) -> None:
240243
self.ids_to_update,
241244
self.maps.child_to_parent_id_map,
242245
self.maps.workspace_id_to_wdf_map,
246+
self.source_group_full,
243247
)
244248

245249
# Check WDF settings of ignored workspaces.
@@ -265,5 +269,42 @@ def _provision_full_load(self) -> None:
265269

266270
def _provision_incremental_load(self) -> None:
267271
"""Incremental workspace provisioning."""
272+
# Set the maps based on the source data.
273+
self.maps = self.parser.set_maps_based_on_source(
274+
self.maps, self.source_group_incremental
275+
)
276+
277+
# Get upstream children of all parent workspaces.
278+
self.upstream_group = self._api.get_panther_children_workspaces(
279+
self.maps.parent_ids
280+
)
268281

269-
raise NotImplementedError("Not implemented yet.")
282+
# Set maps that require upstream data.
283+
self.maps = self.parser.set_maps_with_upstream_data(
284+
self.maps, self.source_group_incremental, self.upstream_group
285+
)
286+
287+
# Create an instance of WDF manager with the created maps.
288+
self.wdf_manager = WorkspaceDataFilterManager(self._api, self.maps)
289+
290+
# Iterate through the source data and sort workspace ID to groups
291+
ids_to_update: set[str] = set()
292+
ids_to_delete: set[str] = set()
293+
294+
for workspace in self.source_group_incremental:
295+
if workspace.is_active:
296+
ids_to_update.add(workspace.workspace_id)
297+
else:
298+
ids_to_delete.add(workspace.workspace_id)
299+
300+
self._create_or_update_panther_workspaces(
301+
set(),
302+
ids_to_update,
303+
self.maps.child_to_parent_id_map,
304+
self.maps.workspace_id_to_wdf_map,
305+
self.source_group_incremental,
306+
)
307+
308+
self.delete_panther_workspaces(
309+
ids_to_delete, self.maps.workspace_id_to_name_map
310+
)

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from gooddata_pipelines.provisioning.entities.workspaces.models import (
1010
WorkspaceDataMaps,
1111
WorkspaceFullLoad,
12+
WorkspaceIncrementalLoad,
1213
)
1314

1415

@@ -17,7 +18,7 @@ class WorkspaceDataParser:
1718

1819
@staticmethod
1920
def _get_id_to_name_map(
20-
source_group: list[WorkspaceFullLoad],
21+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
2122
upstream_group: list[CatalogWorkspace],
2223
) -> dict[str, str]:
2324
"""Creates a map of workspace IDs to their names for all known workspaces."""
@@ -33,7 +34,7 @@ def _get_id_to_name_map(
3334

3435
@staticmethod
3536
def _get_child_to_parent_map(
36-
source_group: list[WorkspaceFullLoad],
37+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
3738
) -> dict[str, str]:
3839
"""Creates a map of child workspace IDs to their parent workspace IDs."""
3940
child_to_parent_map: dict[str, str] = {
@@ -45,7 +46,8 @@ def _get_child_to_parent_map(
4546

4647
@staticmethod
4748
def _get_set_of_ids_from_source(
48-
source_group: list[WorkspaceFullLoad], column_name: str
49+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
50+
column_name: str,
4951
) -> set[str]:
5052
"""Creates a set of unique parent workspace IDs."""
5153
set_of_ids: set[str] = {
@@ -64,7 +66,8 @@ def get_set_of_upstream_workspace_ids(
6466
return set_of_ids
6567

6668
def _get_child_to_wdfs_map(
67-
self, source_group: list[WorkspaceFullLoad]
69+
self,
70+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
6871
) -> dict[str, dict[str, list[str]]]:
6972
"""Creates a map of child workspace IDs to their WDF IDs."""
7073
# TODO: Use objects or a more transparent data structure instead of this.
@@ -88,7 +91,7 @@ def _get_child_to_wdfs_map(
8891
def set_maps_based_on_source(
8992
self,
9093
map_object: WorkspaceDataMaps,
91-
source_group: list[WorkspaceFullLoad],
94+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
9295
) -> WorkspaceDataMaps:
9396
"""Creates maps which are dependent on the source group only."""
9497
map_object.child_to_parent_id_map = self._get_child_to_parent_map(
@@ -109,7 +112,7 @@ def set_maps_based_on_source(
109112
def set_maps_with_upstream_data(
110113
self,
111114
map_object: WorkspaceDataMaps,
112-
source_group: list[WorkspaceFullLoad],
115+
source_group: list[WorkspaceFullLoad] | list[WorkspaceIncrementalLoad],
113116
upstream_group: list[CatalogWorkspace],
114117
) -> WorkspaceDataMaps:
115118
"""Creates maps which are dependent on both the source group and upstream group."""

gooddata-pipelines/gooddata_pipelines/provisioning/provisioning.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Provisioning(Generic[TFullLoadSourceData, TIncrementalSourceData]):
2323
TProvisioning = TypeVar("TProvisioning", bound="Provisioning")
2424
source_group_full: list[TFullLoadSourceData]
2525
source_group_incremental: list[TIncrementalSourceData]
26+
2627
FULL_LOAD_TYPE: type[TFullLoadSourceData]
2728
INCREMENTAL_LOAD_TYPE: type[TIncrementalSourceData]
2829

gooddata-pipelines/tests/provisioning/entities/users/test_user_groups.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,8 @@
77
UserGroupIncrementalLoad,
88
)
99

10-
# TODO: Test business logic
11-
1210

1311
def test_missing_key_no_parent_groups() -> None:
14-
# NOTE: Type ignore because of the missing parent_user_groups key. In Python
15-
# 3.11, we can use NotRequired to make the key optional.
16-
1712
result = UserGroupIncrementalLoad(
1813
user_group_id="ug_2", user_group_name="Developers", is_active=True
1914
)

0 commit comments

Comments
 (0)