Added user spaces
This commit is contained in:
parent
9ddd85d36a
commit
75b4ec2ea1
@ -120,7 +120,7 @@ class QueryABC(ObjectType):
|
|||||||
skip = None
|
skip = None
|
||||||
|
|
||||||
if field.default_filter:
|
if field.default_filter:
|
||||||
filters.append(field.default_filter(*args, **kwargs))
|
filters.append(await field.default_filter(*args, **kwargs))
|
||||||
|
|
||||||
if field.filter_type and "filter" in kwargs:
|
if field.filter_type and "filter" in kwargs:
|
||||||
in_filters = kwargs["filter"]
|
in_filters = kwargs["filter"]
|
||||||
|
@ -11,3 +11,6 @@ class GroupFilter(DbModelFilterABC):
|
|||||||
|
|
||||||
self.add_field("name", StringFilter)
|
self.add_field("name", StringFilter)
|
||||||
self.add_field("description", StringFilter)
|
self.add_field("description", StringFilter)
|
||||||
|
|
||||||
|
self.add_field("isNull", bool)
|
||||||
|
self.add_field("isNotNull", bool)
|
||||||
|
@ -39,8 +39,8 @@ input StringFilter {
|
|||||||
startsWith: String
|
startsWith: String
|
||||||
endsWith: String
|
endsWith: String
|
||||||
|
|
||||||
isNull: String
|
isNull: Boolean
|
||||||
isNotNull: String
|
isNotNull: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input IntFilter {
|
input IntFilter {
|
||||||
@ -51,8 +51,8 @@ input IntFilter {
|
|||||||
less: Int
|
less: Int
|
||||||
lessOrEqual: Int
|
lessOrEqual: Int
|
||||||
|
|
||||||
isNull: Int
|
isNull: Boolean
|
||||||
isNotNull: Int
|
isNotNull: Boolean
|
||||||
in: [Int]
|
in: [Int]
|
||||||
notIn: [Int]
|
notIn: [Int]
|
||||||
}
|
}
|
||||||
@ -61,8 +61,8 @@ input BooleanFilter {
|
|||||||
equal: Boolean
|
equal: Boolean
|
||||||
notEqual: Int
|
notEqual: Int
|
||||||
|
|
||||||
isNull: Int
|
isNull: Boolean
|
||||||
isNotNull: Int
|
isNotNull: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input DateFilter {
|
input DateFilter {
|
||||||
@ -78,8 +78,8 @@ input DateFilter {
|
|||||||
contains: String
|
contains: String
|
||||||
notContains: String
|
notContains: String
|
||||||
|
|
||||||
isNull: String
|
isNull: Boolean
|
||||||
isNotNull: String
|
isNotNull: Boolean
|
||||||
|
|
||||||
in: [String]
|
in: [String]
|
||||||
notIn: [String]
|
notIn: [String]
|
||||||
|
@ -61,6 +61,9 @@ input GroupFilter {
|
|||||||
editor: IntFilter
|
editor: IntFilter
|
||||||
created: DateFilter
|
created: DateFilter
|
||||||
updated: DateFilter
|
updated: DateFilter
|
||||||
|
|
||||||
|
isNull: Boolean
|
||||||
|
isNotNull: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupMutation {
|
type GroupMutation {
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from api.route import Route
|
||||||
from api_graphql.abc.mutation_abc import MutationABC
|
from api_graphql.abc.mutation_abc import MutationABC
|
||||||
from api_graphql.input.group_create_input import GroupCreateInput
|
from api_graphql.input.group_create_input import GroupCreateInput
|
||||||
from api_graphql.input.group_update_input import GroupUpdateInput
|
from api_graphql.input.group_update_input import GroupUpdateInput
|
||||||
|
from core.configuration.feature_flags import FeatureFlags
|
||||||
|
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
from core.logger import APILogger
|
from core.logger import APILogger
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
@ -75,6 +78,11 @@ class GroupMutation(MutationABC):
|
|||||||
group = Group(
|
group = Group(
|
||||||
0,
|
0,
|
||||||
obj.name,
|
obj.name,
|
||||||
|
(
|
||||||
|
(await Route.get_user()).id
|
||||||
|
if await FeatureFlags.has_feature(FeatureFlagsEnum.per_user_setup)
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
gid = await groupDao.create(group)
|
gid = await groupDao.create(group)
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
from api.route import Route
|
||||||
from api_graphql.abc.mutation_abc import MutationABC
|
from api_graphql.abc.mutation_abc import MutationABC
|
||||||
from api_graphql.input.short_url_create_input import ShortUrlCreateInput
|
from api_graphql.input.short_url_create_input import ShortUrlCreateInput
|
||||||
from api_graphql.input.short_url_update_input import ShortUrlUpdateInput
|
from api_graphql.input.short_url_update_input import ShortUrlUpdateInput
|
||||||
|
from core.configuration.feature_flags import FeatureFlags
|
||||||
|
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
from core.logger import APILogger
|
from core.logger import APILogger
|
||||||
from data.schemas.public.domain_dao import domainDao
|
from data.schemas.public.domain_dao import domainDao
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
@ -57,6 +60,11 @@ class ShortUrlMutation(MutationABC):
|
|||||||
obj.group_id,
|
obj.group_id,
|
||||||
obj.domain_id,
|
obj.domain_id,
|
||||||
obj.loading_screen,
|
obj.loading_screen,
|
||||||
|
(
|
||||||
|
(await Route.get_user()).id
|
||||||
|
if await FeatureFlags.has_feature(FeatureFlagsEnum.per_user_setup)
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
nid = await shortUrlDao.create(short_url)
|
nid = await shortUrlDao.create(short_url)
|
||||||
return await shortUrlDao.get_by_id(nid)
|
return await shortUrlDao.get_by_id(nid)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from api_graphql.abc.db_history_model_query_abc import DbHistoryModelQueryABC
|
from api_graphql.abc.db_history_model_query_abc import DbHistoryModelQueryABC
|
||||||
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
||||||
from api_graphql.require_any_resolvers import group_by_assignment_resolver
|
from api_graphql.require_any_resolvers import by_assignment_resolver
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from data.schemas.public.group_role_assignment_dao import groupRoleAssignmentDao
|
from data.schemas.public.group_role_assignment_dao import groupRoleAssignmentDao
|
||||||
@ -21,7 +21,7 @@ class GroupHistoryQuery(DbHistoryModelQueryABC):
|
|||||||
[
|
[
|
||||||
Permissions.groups,
|
Permissions.groups,
|
||||||
],
|
],
|
||||||
[group_by_assignment_resolver],
|
[by_assignment_resolver],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.set_field(
|
self.set_field(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
||||||
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
||||||
from api_graphql.require_any_resolvers import group_by_assignment_resolver
|
from api_graphql.require_any_resolvers import by_assignment_resolver
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from data.schemas.public.group_role_assignment_dao import groupRoleAssignmentDao
|
from data.schemas.public.group_role_assignment_dao import groupRoleAssignmentDao
|
||||||
@ -21,7 +21,7 @@ class GroupQuery(DbModelQueryABC):
|
|||||||
[
|
[
|
||||||
Permissions.groups,
|
Permissions.groups,
|
||||||
],
|
],
|
||||||
[group_by_assignment_resolver],
|
[by_assignment_resolver],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.set_field("roles", self._get_roles)
|
self.set_field("roles", self._get_roles)
|
||||||
|
@ -11,7 +11,12 @@ from api_graphql.filter.permission_filter import PermissionFilter
|
|||||||
from api_graphql.filter.role_filter import RoleFilter
|
from api_graphql.filter.role_filter import RoleFilter
|
||||||
from api_graphql.filter.short_url_filter import ShortUrlFilter
|
from api_graphql.filter.short_url_filter import ShortUrlFilter
|
||||||
from api_graphql.filter.user_filter import UserFilter
|
from api_graphql.filter.user_filter import UserFilter
|
||||||
from api_graphql.require_any_resolvers import group_by_assignment_resolver
|
from api_graphql.require_any_resolvers import (
|
||||||
|
by_assignment_resolver,
|
||||||
|
by_user_setup_resolver,
|
||||||
|
)
|
||||||
|
from core.configuration.feature_flags import FeatureFlags
|
||||||
|
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
from data.schemas.administration.api_key import ApiKey
|
from data.schemas.administration.api_key import ApiKey
|
||||||
from data.schemas.administration.api_key_dao import apiKeyDao
|
from data.schemas.administration.api_key_dao import apiKeyDao
|
||||||
from data.schemas.administration.user import User
|
from data.schemas.administration.user import User
|
||||||
@ -109,7 +114,8 @@ class Query(QueryABC):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.field(
|
|
||||||
|
group_field = (
|
||||||
DaoFieldBuilder("groups")
|
DaoFieldBuilder("groups")
|
||||||
.with_dao(groupDao)
|
.with_dao(groupDao)
|
||||||
.with_filter(GroupFilter)
|
.with_filter(GroupFilter)
|
||||||
@ -120,15 +126,33 @@ class Query(QueryABC):
|
|||||||
Permissions.short_urls_create,
|
Permissions.short_urls_create,
|
||||||
Permissions.short_urls_update,
|
Permissions.short_urls_update,
|
||||||
],
|
],
|
||||||
[group_by_assignment_resolver],
|
[by_assignment_resolver, by_user_setup_resolver],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if FeatureFlags.get_default(FeatureFlagsEnum.per_user_setup):
|
||||||
|
group_field = group_field.with_default_filter(self._resolve_default_user_filter)
|
||||||
|
|
||||||
self.field(
|
self.field(
|
||||||
|
group_field
|
||||||
|
)
|
||||||
|
|
||||||
|
short_url_field = (
|
||||||
DaoFieldBuilder("shortUrls")
|
DaoFieldBuilder("shortUrls")
|
||||||
.with_dao(shortUrlDao)
|
.with_dao(shortUrlDao)
|
||||||
.with_filter(ShortUrlFilter)
|
.with_filter(ShortUrlFilter)
|
||||||
.with_sort(Sort[ShortUrl])
|
.with_sort(Sort[ShortUrl])
|
||||||
.with_require_any([Permissions.short_urls], [group_by_assignment_resolver])
|
.with_require_any(
|
||||||
|
[Permissions.short_urls],
|
||||||
|
[by_assignment_resolver, by_user_setup_resolver],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if FeatureFlags.get_default(FeatureFlagsEnum.per_user_setup):
|
||||||
|
short_url_field = short_url_field.with_default_filter(self._resolve_default_user_filter)
|
||||||
|
|
||||||
|
self.field(
|
||||||
|
short_url_field
|
||||||
)
|
)
|
||||||
|
|
||||||
self.field(
|
self.field(
|
||||||
@ -202,3 +226,7 @@ class Query(QueryABC):
|
|||||||
if "key" in kwargs:
|
if "key" in kwargs:
|
||||||
return [await featureFlagDao.find_by_key(kwargs["key"])]
|
return [await featureFlagDao.find_by_key(kwargs["key"])]
|
||||||
return await featureFlagDao.get_all()
|
return await featureFlagDao.get_all()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _resolve_default_user_filter(*args, **kwargs) -> dict:
|
||||||
|
return {"user": {"id": {"equal": (await Route.get_user()).id}}}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
from api_graphql.service.collection_result import CollectionResult
|
from api_graphql.service.collection_result import CollectionResult
|
||||||
from api_graphql.service.query_context import QueryContext
|
from api_graphql.service.query_context import QueryContext
|
||||||
|
from core.configuration.feature_flags import FeatureFlags
|
||||||
|
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from service.permission.permissions_enum import Permissions
|
from service.permission.permissions_enum import Permissions
|
||||||
|
|
||||||
|
|
||||||
async def group_by_assignment_resolver(ctx: QueryContext) -> bool:
|
async def by_assignment_resolver(ctx: QueryContext) -> bool:
|
||||||
if not isinstance(ctx.data, CollectionResult):
|
if not isinstance(ctx.data, CollectionResult):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -19,12 +21,19 @@ async def group_by_assignment_resolver(ctx: QueryContext) -> bool:
|
|||||||
and all(r.id in role_ids for r in roles)
|
and all(r.id in role_ids for r in roles)
|
||||||
]
|
]
|
||||||
|
|
||||||
ctx.data.nodes = [
|
return all(
|
||||||
node
|
(await node.group) is not None and (await node.group).id in filtered_groups
|
||||||
for node in ctx.data.nodes
|
for node in ctx.data.nodes
|
||||||
if (await node.group) is not None
|
)
|
||||||
and (await node.group).id in filtered_groups
|
|
||||||
]
|
|
||||||
return True
|
|
||||||
|
|
||||||
return True
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def by_user_setup_resolver(ctx: QueryContext) -> bool:
|
||||||
|
if not isinstance(ctx.data, CollectionResult):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not FeatureFlags.has_feature(FeatureFlagsEnum.per_user_setup):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return all(x.user_setup_id == ctx.user.id for x in ctx.data.nodes)
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
from core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
|
from core.environment import Environment
|
||||||
from data.schemas.system.feature_flag_dao import featureFlagDao
|
from data.schemas.system.feature_flag_dao import featureFlagDao
|
||||||
|
|
||||||
|
|
||||||
class FeatureFlags:
|
class FeatureFlags:
|
||||||
_flags = {
|
_flags = {
|
||||||
FeatureFlagsEnum.version_endpoint.value: True, # 15.01.2025
|
FeatureFlagsEnum.version_endpoint.value: True, # 15.01.2025
|
||||||
|
FeatureFlagsEnum.per_user_setup.value: Environment.get(
|
||||||
|
"PER_USER_SETUP", bool, False
|
||||||
|
), # 18.04.2025
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -15,6 +19,6 @@ class FeatureFlags:
|
|||||||
async def has_feature(key: FeatureFlagsEnum) -> bool:
|
async def has_feature(key: FeatureFlagsEnum) -> bool:
|
||||||
value = await featureFlagDao.find_by_key(key.value)
|
value = await featureFlagDao.find_by_key(key.value)
|
||||||
if value is None:
|
if value is None:
|
||||||
return False
|
return FeatureFlags.get_default(key)
|
||||||
|
|
||||||
return value.value
|
return value.value
|
||||||
|
@ -2,5 +2,5 @@ from enum import Enum
|
|||||||
|
|
||||||
|
|
||||||
class FeatureFlagsEnum(Enum):
|
class FeatureFlagsEnum(Enum):
|
||||||
# modules
|
|
||||||
version_endpoint = "VersionEndpoint"
|
version_endpoint = "VersionEndpoint"
|
||||||
|
per_user_setup = "PerUserSetup"
|
||||||
|
@ -18,6 +18,22 @@ T_DBM = TypeVar("T_DBM", bound=DbModelABC)
|
|||||||
|
|
||||||
class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
||||||
_external_fields: dict[str, ExternalDataTempTableBuilder] = {}
|
_external_fields: dict[str, ExternalDataTempTableBuilder] = {}
|
||||||
|
_operators = [
|
||||||
|
"equal",
|
||||||
|
"notEqual",
|
||||||
|
"greater",
|
||||||
|
"greaterOrEqual",
|
||||||
|
"less",
|
||||||
|
"lessOrEqual",
|
||||||
|
"isNull",
|
||||||
|
"isNotNull",
|
||||||
|
"contains",
|
||||||
|
"notContains",
|
||||||
|
"startsWith",
|
||||||
|
"endsWith",
|
||||||
|
"in",
|
||||||
|
"notIn",
|
||||||
|
]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, source: str, model_type: Type[T_DBM], table_name: str):
|
def __init__(self, source: str, model_type: Type[T_DBM], table_name: str):
|
||||||
@ -646,7 +662,9 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
|
|
||||||
if attr in self.__foreign_tables:
|
if attr in self.__foreign_tables:
|
||||||
foreign_table = self.__foreign_tables[attr]
|
foreign_table = self.__foreign_tables[attr]
|
||||||
cons, eftd = self._build_foreign_conditions(foreign_table, values)
|
cons, eftd = self._build_foreign_conditions(
|
||||||
|
attr, foreign_table, values
|
||||||
|
)
|
||||||
if eftd:
|
if eftd:
|
||||||
external_field_table_deps.extend(eftd)
|
external_field_table_deps.extend(eftd)
|
||||||
|
|
||||||
@ -670,6 +688,8 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
isinstance(values, dict) or isinstance(values, list)
|
isinstance(values, dict) or isinstance(values, list)
|
||||||
) and not attr in self.__foreign_tables:
|
) and not attr in self.__foreign_tables:
|
||||||
db_name = f"{self._table_name}.{self.__db_names[attr]}"
|
db_name = f"{self._table_name}.{self.__db_names[attr]}"
|
||||||
|
elif attr in self._operators:
|
||||||
|
db_name = f"{self._table_name}.{self.__db_names[attr]}"
|
||||||
else:
|
else:
|
||||||
db_name = self.__db_names[attr]
|
db_name = self.__db_names[attr]
|
||||||
|
|
||||||
@ -691,6 +711,8 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
self._get_value_validation_sql(db_name, value)
|
self._get_value_validation_sql(db_name, value)
|
||||||
)
|
)
|
||||||
f_conditions.append(f"({' OR '.join(sub_conditions)})")
|
f_conditions.append(f"({' OR '.join(sub_conditions)})")
|
||||||
|
elif attr in self._operators:
|
||||||
|
conditions.append(f"{self._build_condition(db_name, attr, values)}")
|
||||||
else:
|
else:
|
||||||
f_conditions.append(self._get_value_validation_sql(db_name, values))
|
f_conditions.append(self._get_value_validation_sql(db_name, values))
|
||||||
|
|
||||||
@ -711,10 +733,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def _build_foreign_conditions(
|
def _build_foreign_conditions(
|
||||||
self, table: str, values: dict
|
self, base_attr: str, table: str, values: dict
|
||||||
) -> (list[str], list[str]):
|
) -> (list[str], list[str]):
|
||||||
"""
|
"""
|
||||||
Build SQL conditions for foreign key references
|
Build SQL conditions for foreign key references
|
||||||
|
:param base_attr: Base attribute name
|
||||||
:param table: Foreign table name
|
:param table: Foreign table name
|
||||||
:param values: Filter values
|
:param values: Filter values
|
||||||
:return: List of conditions, List of external field tables
|
:return: List of conditions, List of external field tables
|
||||||
@ -728,7 +751,7 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
if attr in self.__foreign_tables:
|
if attr in self.__foreign_tables:
|
||||||
foreign_table = self.__foreign_tables[attr]
|
foreign_table = self.__foreign_tables[attr]
|
||||||
sub_conditions, eftd = self._build_foreign_conditions(
|
sub_conditions, eftd = self._build_foreign_conditions(
|
||||||
foreign_table, sub_values
|
attr, foreign_table, sub_values
|
||||||
)
|
)
|
||||||
if len(eftd) > 0:
|
if len(eftd) > 0:
|
||||||
external_field_table_deps.extend(eftd)
|
external_field_table_deps.extend(eftd)
|
||||||
@ -749,6 +772,8 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
]
|
]
|
||||||
db_name = f"{external_fields_table.table_name}.{attr}"
|
db_name = f"{external_fields_table.table_name}.{attr}"
|
||||||
external_field_table_deps.append(external_fields_table.table_name)
|
external_field_table_deps.append(external_fields_table.table_name)
|
||||||
|
elif attr in self._operators:
|
||||||
|
db_name = f"{self._table_name}.{self.__foreign_table_keys[base_attr]}"
|
||||||
else:
|
else:
|
||||||
db_name = f"{table}.{attr.lower().replace('_', '')}"
|
db_name = f"{table}.{attr.lower().replace('_', '')}"
|
||||||
|
|
||||||
@ -770,6 +795,8 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
self._get_value_validation_sql(db_name, value)
|
self._get_value_validation_sql(db_name, value)
|
||||||
)
|
)
|
||||||
conditions.append(f"({' OR '.join(sub_conditions)})")
|
conditions.append(f"({' OR '.join(sub_conditions)})")
|
||||||
|
elif attr in self._operators:
|
||||||
|
conditions.append(f"{self._build_condition(db_name, attr, sub_values)}")
|
||||||
else:
|
else:
|
||||||
conditions.append(self._get_value_validation_sql(db_name, sub_values))
|
conditions.append(self._get_value_validation_sql(db_name, sub_values))
|
||||||
|
|
||||||
@ -815,7 +842,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
|
|
||||||
def _get_value_validation_sql(self, field: str, value: Any):
|
def _get_value_validation_sql(self, field: str, value: Any):
|
||||||
value = self._get_value_sql(value)
|
value = self._get_value_sql(value)
|
||||||
field_selector = f"{self._table_name}.{field}"
|
field_selector = (
|
||||||
|
f"{self._table_name}.{field}"
|
||||||
|
if not field.startswith(self._table_name)
|
||||||
|
else field
|
||||||
|
)
|
||||||
if field in self.__foreign_tables:
|
if field in self.__foreign_tables:
|
||||||
field_selector = self.__db_names[field]
|
field_selector = self.__db_names[field]
|
||||||
|
|
||||||
@ -850,9 +881,9 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
elif operator == "lessOrEqual":
|
elif operator == "lessOrEqual":
|
||||||
return f"{db_name} <= {sql_value}"
|
return f"{db_name} <= {sql_value}"
|
||||||
elif operator == "isNull":
|
elif operator == "isNull":
|
||||||
return f"{db_name} IS NULL"
|
return f"{db_name} IS NULL" if sql_value else f"{db_name} IS NOT NULL"
|
||||||
elif operator == "isNotNull":
|
elif operator == "isNotNull":
|
||||||
return f"{db_name} IS NOT NULL"
|
return f"{db_name} IS NOT NULL" if sql_value else f"{db_name} IS NULL"
|
||||||
elif operator == "contains":
|
elif operator == "contains":
|
||||||
return f"{db_name} LIKE '%{value}%'"
|
return f"{db_name} LIKE '%{value}%'"
|
||||||
elif operator == "notContains":
|
elif operator == "notContains":
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from async_property import async_property
|
||||||
|
|
||||||
from core.database.abc.db_model_abc import DbModelABC
|
from core.database.abc.db_model_abc import DbModelABC
|
||||||
from core.typing import SerialId
|
from core.typing import SerialId
|
||||||
|
|
||||||
@ -10,6 +12,7 @@ class Group(DbModelABC):
|
|||||||
self,
|
self,
|
||||||
id: SerialId,
|
id: SerialId,
|
||||||
name: str,
|
name: str,
|
||||||
|
user_id: Optional[SerialId] = None,
|
||||||
deleted: bool = False,
|
deleted: bool = False,
|
||||||
editor_id: Optional[SerialId] = None,
|
editor_id: Optional[SerialId] = None,
|
||||||
created: Optional[datetime] = None,
|
created: Optional[datetime] = None,
|
||||||
@ -17,6 +20,7 @@ class Group(DbModelABC):
|
|||||||
):
|
):
|
||||||
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
|
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._user_id = user_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@ -25,3 +29,17 @@ class Group(DbModelABC):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, value: str):
|
def name(self, value: str):
|
||||||
self._name = value
|
self._name = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_id(self) -> Optional[SerialId]:
|
||||||
|
return self._user_id
|
||||||
|
|
||||||
|
@async_property
|
||||||
|
async def user(self):
|
||||||
|
if self._user_id is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
from data.schemas.administration.user_dao import userDao
|
||||||
|
|
||||||
|
user = await userDao.get_by_id(self.user_id)
|
||||||
|
return user
|
||||||
|
@ -11,6 +11,9 @@ class GroupDao(DbModelDaoABC[Group]):
|
|||||||
DbModelDaoABC.__init__(self, __name__, Group, "public.groups")
|
DbModelDaoABC.__init__(self, __name__, Group, "public.groups")
|
||||||
self.attribute(Group.name, str)
|
self.attribute(Group.name, str)
|
||||||
|
|
||||||
|
self.attribute(Group.user_id, int)
|
||||||
|
self.reference("user", "id", Group.user_id, "administration.users")
|
||||||
|
|
||||||
async def get_by_name(self, name: str) -> Group:
|
async def get_by_name(self, name: str) -> Group:
|
||||||
result = await self._db.select_map(
|
result = await self._db.select_map(
|
||||||
f"SELECT * FROM {self._table_name} WHERE Name = '{name}'"
|
f"SELECT * FROM {self._table_name} WHERE Name = '{name}'"
|
||||||
|
@ -18,6 +18,7 @@ class ShortUrl(DbModelABC):
|
|||||||
group_id: Optional[SerialId],
|
group_id: Optional[SerialId],
|
||||||
domain_id: Optional[SerialId],
|
domain_id: Optional[SerialId],
|
||||||
loading_screen: Optional[str] = None,
|
loading_screen: Optional[str] = None,
|
||||||
|
user_id: Optional[SerialId] = None,
|
||||||
deleted: bool = False,
|
deleted: bool = False,
|
||||||
editor_id: Optional[SerialId] = None,
|
editor_id: Optional[SerialId] = None,
|
||||||
created: Optional[datetime] = None,
|
created: Optional[datetime] = None,
|
||||||
@ -34,6 +35,8 @@ class ShortUrl(DbModelABC):
|
|||||||
loading_screen = False
|
loading_screen = False
|
||||||
self._loading_screen = loading_screen
|
self._loading_screen = loading_screen
|
||||||
|
|
||||||
|
self._user_id = user_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_url(self) -> str:
|
def short_url(self) -> str:
|
||||||
return self._short_url
|
return self._short_url
|
||||||
@ -106,6 +109,20 @@ class ShortUrl(DbModelABC):
|
|||||||
def loading_screen(self, value: Optional[str]):
|
def loading_screen(self, value: Optional[str]):
|
||||||
self._loading_screen = value
|
self._loading_screen = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_id(self) -> Optional[SerialId]:
|
||||||
|
return self._user_id
|
||||||
|
|
||||||
|
@async_property
|
||||||
|
async def user(self):
|
||||||
|
if self._user_id is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
from data.schemas.administration.user_dao import userDao
|
||||||
|
|
||||||
|
user = await userDao.get_by_id(self.user_id)
|
||||||
|
return user
|
||||||
|
|
||||||
def to_dto(self) -> dict:
|
def to_dto(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
|
@ -18,5 +18,8 @@ class ShortUrlDao(DbModelDaoABC[ShortUrl]):
|
|||||||
self.reference("domain", "id", ShortUrl.domain_id, "public.domains")
|
self.reference("domain", "id", ShortUrl.domain_id, "public.domains")
|
||||||
self.attribute(ShortUrl.loading_screen, bool)
|
self.attribute(ShortUrl.loading_screen, bool)
|
||||||
|
|
||||||
|
self.attribute(ShortUrl.user_id, int)
|
||||||
|
self.reference("user", "id", ShortUrl.user_id, "administration.users")
|
||||||
|
|
||||||
|
|
||||||
shortUrlDao = ShortUrlDao()
|
shortUrlDao = ShortUrlDao()
|
||||||
|
11
api/src/data/scripts/2025-04-18-12-15-user-spaces.sql
Normal file
11
api/src/data/scripts/2025-04-18-12-15-user-spaces.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ALTER TABLE public.groups
|
||||||
|
ADD COLUMN IF NOT EXISTS UserId INT NULL REFERENCES administration.users (Id);
|
||||||
|
|
||||||
|
ALTER TABLE public.groups_history
|
||||||
|
ADD COLUMN IF NOT EXISTS UserId INT NULL REFERENCES administration.users (Id);
|
||||||
|
|
||||||
|
ALTER TABLE public.short_urls
|
||||||
|
ADD COLUMN IF NOT EXISTS UserId INT NULL REFERENCES administration.users (Id);
|
||||||
|
|
||||||
|
ALTER TABLE public.short_urls_history
|
||||||
|
ADD COLUMN IF NOT EXISTS UserId INT NULL REFERENCES administration.users (Id);
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from '@angular/core';
|
||||||
import { merge, Observable } from 'rxjs';
|
import { forkJoin, merge, Observable } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
@ -48,13 +48,10 @@ export class ShortUrlsDataService
|
|||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined
|
||||||
): Observable<QueryResult<ShortUrl>> {
|
): Observable<QueryResult<ShortUrl>> {
|
||||||
return this.apollo
|
const query1 = this.apollo.query<{ shortUrls: QueryResult<ShortUrl> }>({
|
||||||
.query<{ shortUrls: QueryResult<ShortUrl> }>({
|
|
||||||
query: gql`
|
query: gql`
|
||||||
query getShortUrls($filter: [ShortUrlFilter], $sort: [ShortUrlSort]) {
|
query getShortUrls($filter: [ShortUrlFilter], $sort: [ShortUrlSort]) {
|
||||||
shortUrls(filter: $filter, sort: $sort) {
|
shortUrls(filter: $filter, sort: $sort) {
|
||||||
count
|
|
||||||
totalCount
|
|
||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
shortUrl
|
shortUrl
|
||||||
@ -70,28 +67,61 @@ export class ShortUrlsDataService
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
...DB_MODEL
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${DB_MODEL_FRAGMENT}
|
|
||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
filter: [{ group: { deleted: { equal: false } } }, ...(filter ?? [])],
|
filter: [{ group: { deleted: { equal: false } } }, ...(filter ?? [])],
|
||||||
sort: [{ id: SortOrder.DESC }, ...(sort ?? [])],
|
sort: [{ id: SortOrder.DESC }, ...(sort ?? [])],
|
||||||
skip: skip,
|
skip,
|
||||||
take: take,
|
take,
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const query2 = this.apollo.query<{ shortUrls: QueryResult<ShortUrl> }>({
|
||||||
|
query: gql`
|
||||||
|
query getShortUrls($filter: [ShortUrlFilter], $sort: [ShortUrlSort]) {
|
||||||
|
shortUrls(filter: $filter, sort: $sort) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
shortUrl
|
||||||
|
targetUrl
|
||||||
|
description
|
||||||
|
loadingScreen
|
||||||
|
visits
|
||||||
|
group {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
domain {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
filter: [{ group: { isNull: true } }, ...(filter ?? [])],
|
||||||
|
sort: [{ id: SortOrder.DESC }, ...(sort ?? [])],
|
||||||
|
skip,
|
||||||
|
take,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return forkJoin([query1, query2]).pipe(
|
||||||
|
map(([result1, result2]) => {
|
||||||
|
const nodes = [
|
||||||
|
...result1.data.shortUrls.nodes,
|
||||||
|
...result2.data.shortUrls.nodes,
|
||||||
|
];
|
||||||
|
const uniqueNodes = Array.from(
|
||||||
|
new Map(nodes.map(node => [node.id, node])).values()
|
||||||
|
);
|
||||||
|
return { ...result1.data.shortUrls, nodes: uniqueNodes };
|
||||||
})
|
})
|
||||||
.pipe(
|
);
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data.shortUrls));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<ShortUrl> {
|
loadById(id: number): Observable<ShortUrl> {
|
||||||
|
Loading…
Reference in New Issue
Block a user