[WIP] User space handling
Some checks failed
Test API before pr merge / test-lint (pull_request) Failing after 9s
Test before pr merge / test-lint (pull_request) Successful in 39s
Test before pr merge / test-translation-lint (pull_request) Successful in 34s
Test before pr merge / test-before-merge (pull_request) Successful in 1m36s

This commit is contained in:
Sven Heidemann 2025-04-20 12:34:28 +02:00
parent d190d2f218
commit 0ed3cb846d
5 changed files with 61 additions and 16 deletions

View File

@ -1,7 +1,9 @@
from api.route import Route
from api_graphql.abc.mutation_abc import MutationABC
from api_graphql.field.mutation_field_builder import MutationFieldBuilder
from api_graphql.input.user_setting_input import UserSettingInput
from core.logger import APILogger
from core.string import first_to_lower
from data.schemas.public.user_setting import UserSetting
from data.schemas.public.user_setting_dao import userSettingsDao
from data.schemas.system.setting_dao import settingsDao
@ -13,13 +15,20 @@ logger = APILogger(__name__)
class UserSettingMutation(MutationABC):
def __init__(self):
MutationABC.__init__(self, "UserSetting")
self.mutation(
"change",
self.resolve_change,
UserSettingInput,
require_any_permission=[Permissions.settings_update],
self.field(
MutationFieldBuilder("change")
.with_resolver(self.resolve_change)
.with_change_broadcast(
f"{first_to_lower(self.name.replace("Mutation", ""))}Change"
)
.with_input(UserSettingInput, "input")
.with_require_any([], [self._x])
)
@staticmethod
async def _x(ctx):
return ctx.data.user_id == (await Route.get_user()).id
@staticmethod
async def resolve_change(obj: UserSettingInput, *_):
logger.debug(f"create new setting: {input}")

View File

@ -1,3 +1,5 @@
from typing import Union
from core.configuration.feature_flags_enum import FeatureFlagsEnum
from core.environment import Environment
from data.schemas.system.feature_flag_dao import featureFlagDao
@ -6,19 +8,31 @@ from data.schemas.system.feature_flag_dao import featureFlagDao
class FeatureFlags:
_flags = {
FeatureFlagsEnum.version_endpoint.value: True, # 15.01.2025
FeatureFlagsEnum.technical_demo_banner.value: False, # 18.04.2025
FeatureFlagsEnum.per_user_setup.value: Environment.get(
"PER_USER_SETUP", bool, False
), # 18.04.2025
}
_overwrite_flags = [
FeatureFlagsEnum.per_user_setup.value,
]
@staticmethod
def overwrite_flag(key: str):
return key in FeatureFlags._overwrite_flags
@staticmethod
def get_default(key: FeatureFlagsEnum) -> bool:
return FeatureFlags._flags[key.value]
@staticmethod
async def has_feature(key: FeatureFlagsEnum) -> bool:
value = await featureFlagDao.find_by_key(key.value)
if value is None:
return FeatureFlags.get_default(key)
async def has_feature(key: Union[str, FeatureFlagsEnum]) -> bool:
key_value = key.value if isinstance(key, FeatureFlagsEnum) else key
return value.value
value = await featureFlagDao.find_by_key(key_value)
return (
value.value
if value
else FeatureFlags.get_default(FeatureFlagsEnum(key_value))
)

View File

@ -3,4 +3,5 @@ from enum import Enum
class FeatureFlagsEnum(Enum):
version_endpoint = "VersionEndpoint"
technical_demo_banner = "TechnicalDemoBanner"
per_user_setup = "PerUserSetup"

View File

@ -21,6 +21,7 @@ class FeatureFlagsSeeder(DataSeederABC):
x.value: FeatureFlags.get_default(x) for x in FeatureFlagsEnum
}
# Create new feature flags
to_create = [
FeatureFlag(0, x, possible_feature_flags[x])
for x in possible_feature_flags.keys()
@ -31,6 +32,19 @@ class FeatureFlagsSeeder(DataSeederABC):
to_create_dicts = {x.key: x.value for x in to_create}
logger.debug(f"Created feature flags: {to_create_dicts}")
# Update existing feature flags if they can be overwritten and have a different value
to_update = [
FeatureFlag(x.id, x.key, possible_feature_flags[x.key])
for x in feature_flags
if FeatureFlags.overwrite_flag(x.key)
and x.value != possible_feature_flags[x.key]
]
if len(to_update) > 0:
await featureFlagDao.update_many(to_update)
to_update_dicts = {x.key: x.value for x in to_update}
logger.debug(f"Updated feature flags: {to_update_dicts}")
# Delete feature flags that are no longer defined
to_delete = [
x for x in feature_flags if x.key not in possible_feature_flags.keys()
]

View File

@ -3,6 +3,7 @@ import { BehaviorSubject } from 'rxjs';
import { MenuElement } from 'src/app/model/view/menu-element';
import { AuthService } from 'src/app/service/auth.service';
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
import { FeatureFlagService } from 'src/app/service/feature-flag.service';
@Injectable({
providedIn: 'root',
@ -11,7 +12,10 @@ export class SidebarService {
visible$ = new BehaviorSubject<boolean>(true);
elements$ = new BehaviorSubject<MenuElement[]>([]);
constructor(private auth: AuthService) {
constructor(
private auth: AuthService,
private featureFlags: FeatureFlagService
) {
this.auth.user$.subscribe(async () => {
await this.setElements();
});
@ -40,16 +44,19 @@ export class SidebarService {
label: 'common.groups',
icon: 'pi pi-tags',
routerLink: ['/admin/groups'],
visible: await this.auth.hasAnyPermissionLazy([PermissionsEnum.groups]),
visible:
(await this.auth.hasAnyPermissionLazy([PermissionsEnum.groups])) ||
(await this.featureFlags.get('PerUserSetup')),
},
{
label: 'common.urls',
icon: 'pi pi-tag',
routerLink: ['/admin/urls'],
visible: await this.auth.hasAnyPermissionLazy([
PermissionsEnum.shortUrls,
PermissionsEnum.shortUrlsByAssignment,
]),
visible:
(await this.auth.hasAnyPermissionLazy([
PermissionsEnum.shortUrls,
PermissionsEnum.shortUrlsByAssignment,
])) || (await this.featureFlags.get('PerUserSetup')),
},
await this.sectionAdmin(),
];