Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea
.vscode
.cursor
*.iml
.env
.env.test
Expand Down
2 changes: 1 addition & 1 deletion docs/assets/scss/variables/_variables.colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
$color-deep-purple: #1C0D3F;
$color-indigo: #1B127D;
$color-cobalt-blue: #2637EF;
$color-shocking-pink: #ED26B7;
$color-shocking-pink: #CF119C;
$color-violet: #8104CA;
$color-jade-green: #A3FFB0;
$color-emerald-green: #20CA8B;
Expand Down
5 changes: 5 additions & 0 deletions gooddata-pipelines/gooddata_pipelines/api/gooddata_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ def get_all_dashboards(self, workspace_id: str) -> requests.Response:
headers = {**self.headers, "X-GDC-VALIDATE-RELATIONS": "true"}
return self._get(endpoint, headers=headers)

def get_profile(self) -> requests.Response:
"""Returns organization and current user information."""
endpoint = "/profile"
return self._get(endpoint)

def _get(
self, endpoint: str, headers: dict[str, str] | None = None
) -> requests.Response:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
from typing import Any

from gooddata_sdk.catalog.user.entity_model.user import CatalogUser
from pydantic import BaseModel
from pydantic import BaseModel, Field


class UserProfile(BaseModel):
"""Minimal model of api/v1/profile response.

Does not contain all fields from the response.
"""

user_id: str = Field(alias="userId")


class BaseUser(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from gooddata_pipelines.provisioning.entities.users.models.users import (
UserFullLoad,
UserIncrementalLoad,
UserProfile,
)
from gooddata_pipelines.provisioning.provisioning import Provisioning
from gooddata_pipelines.provisioning.utils.context_objects import UserContext
Expand All @@ -30,13 +31,28 @@ class UserProvisioner(Provisioning[UserFullLoad, UserIncrementalLoad]):
source_group_incremental: list[UserIncrementalLoad]
source_group_full: list[UserFullLoad]

current_user_id: str

FULL_LOAD_TYPE: type[UserFullLoad] = UserFullLoad
INCREMENTAL_LOAD_TYPE: type[UserIncrementalLoad] = UserIncrementalLoad

def __init__(self, host: str, token: str) -> None:
super().__init__(host, token)
self.upstream_user_cache: dict[UserId, UserModel] = {}

def _get_current_user_id(self) -> str:
"""Gets the current user ID."""

profile_response = self._api.get_profile()

if not profile_response.ok:
raise Exception("Failed to get current user profile")

profile_json = profile_response.json()
profile = UserProfile.model_validate(profile_json)

return profile.user_id

def _try_get_user(
self, user: UserModel, model: type[UserModel]
) -> UserModel | None:
Expand Down Expand Up @@ -99,6 +115,14 @@ def _create_or_update_user(
for its existence and create it if needed.

"""

if user.user_id == self.current_user_id:
self.logger.warning(
f"Skipping creation/update of current user: {user.user_id}. "
+ "Current user should not be modified.",
)
return

user_context = UserContext(
user_id=user.user_id,
user_groups=user.user_groups,
Expand All @@ -118,6 +142,13 @@ def _create_or_update_user(

def _delete_user(self, user_id: str) -> None:
"""Deletes user from the project."""
if user_id == self.current_user_id:
self.logger.warning(
f"Skipping deletion of current user: {user_id}."
+ " Current user should not be deleted.",
)
return

try:
self._api._sdk.catalog_user.get_user(user_id)
except NotFoundException:
Expand All @@ -135,6 +166,9 @@ def _manage_user(self, user: UserIncrementalLoad) -> None:

def _provision_incremental_load(self) -> None:
"""Runs the incremental provisioning logic."""
# Set the current user ID
self.current_user_id = self._get_current_user_id()

for user in self.source_group_incremental:
# Attempt to process each user. On failure, log the error and continue
try:
Expand All @@ -146,6 +180,10 @@ def _provision_incremental_load(self) -> None:

def _provision_full_load(self) -> None:
"""Runs the full load provisioning logic."""

# Set the current user ID
self.current_user_id = self._get_current_user_id()

# Get all upstream users
catalog_upstream_users: list[CatalogUser] = self._api.list_users()

Expand Down
9 changes: 6 additions & 3 deletions gooddata-pipelines/tests/data/profiles.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# (C) 2025 GoodData Corporation
mock_profile:
host: http://localhost:3000
token: some_user_token
profiles:
mock_profile:
host: http://localhost:3000
token: $MOCK_TOKEN
default_profile: mock_profile
access: {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@
"email": "jack.cliff@example.com",
"authentication_id": "auth_4",
"user_groups": ["group_4", "group_5"]
},
{
"user_id": "protected_user_id",
"firstname": "Protected",
"lastname": "User",
"email": "protected.user@example.com",
"authentication_id": "auth_protected",
"user_groups": ["group_protected"]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"entitlements": [
{
"expiry": "2019-08-24",
"name": "CacheStrategy",
"value": "string"
}
],
"features": {
"live": {
"context": {
"earlyAccess": "string",
"earlyAccessValues": ["string"]
},
"configuration": {
"host": "string",
"key": "string"
}
}
},
"links": {
"organization": "string",
"self": "string",
"user": "string"
},
"name": "string",
"organizationId": "string",
"organizationName": "string",
"permissions": ["MANAGE"],
"telemetryConfig": {
"context": {
"deploymentId": "string",
"organizationHash": "string",
"userHash": "string"
},
"services": {
"amplitude": {
"aiProjectApiKey": "string",
"endpoint": "string",
"gdCommonApiKey": "string",
"reportingEndpoint": "string"
},
"matomo": {
"host": "string",
"reportingEndpoint": "string",
"siteId": 0
},
"openTelemetry": {
"host": "string"
}
}
},
"userId": "protected_user_id"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
{
"user_id": "user_1",
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@example.com",
"auth_id": "auth_1",
"user_groups": ["group_1", "group_2"]
},
{
"user_id": "user_2",
"firstname": "Jane",
"lastname": "Doe",
"email": "jane.doe@example.com",
"auth_id": "auth_2",
"user_groups": ["group_2", "group_3"]
},
{
"user_id": "user_3",
"firstname": "Jim",
"lastname": "Rock",
"email": "jim.rock@example.com",
"auth_id": "auth_3",
"user_groups": ["group_3", "group_4"]
},
{
"user_id": "protected_user_id",
"firstname": "Some",
"lastname": "New",
"email": "values.that@should.not",
"auth_id": "take",
"user_groups": ["effect"]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"user_id": "user_1",
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@example.com",
"auth_id": "auth_1",
"user_groups": ["group_1", "group_2"],
"is_active": true
},
{
"user_id": "user_2",
"firstname": "Jane",
"lastname": "Doe",
"email": "jane.doe@example.com",
"auth_id": "auth_2",
"user_groups": ["group_2", "group_3"],
"is_active": true
},
{
"user_id": "user_3",
"firstname": "Jim",
"lastname": "Rock",
"email": "jim.rock@example.com",
"auth_id": "auth_3",
"user_groups": ["group_3", "group_4"],
"is_active": true
},
{
"user_id": "user_4",
"firstname": "Jack",
"lastname": "Cliff",
"email": "jack.cliff@example.com",
"auth_id": "auth_4",
"user_groups": ["group_4", "group_5"],
"is_active": false
},
{
"user_id": "protected_user_id",
"firstname": "Some",
"lastname": "New",
"email": "values.that@should.not",
"auth_id": "take",
"user_groups": ["effect"],
"is_active": false
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"user_id": "user_1",
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@example.com",
"auth_id": "auth_1",
"user_groups": ["group_1", "group_2"],
"is_active": true
},
{
"user_id": "user_2",
"firstname": "Jane",
"lastname": "Doe",
"email": "jane.doe@example.com",
"auth_id": "auth_2",
"user_groups": ["group_2", "group_3"],
"is_active": true
},
{
"user_id": "user_3",
"firstname": "Jim",
"lastname": "Rock",
"email": "jim.rock@example.com",
"auth_id": "auth_3",
"user_groups": ["group_3", "group_4"],
"is_active": true
},
{
"user_id": "user_4",
"firstname": "Jack",
"lastname": "Cliff",
"email": "jack.cliff@example.com",
"auth_id": "auth_4",
"user_groups": ["group_4", "group_5"],
"is_active": false
},
{
"user_id": "protected_user_id",
"firstname": "Some",
"lastname": "New",
"email": "values.that@should.not",
"auth_id": "take",
"user_groups": ["effect"],
"is_active": true
}
]
Loading
Loading