Formatting
This commit is contained in:
parent
86bd5fb545
commit
7de31f5d32
@ -36,15 +36,15 @@ class Route(RouteUserExtension):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _get_auth_type(
|
async def _get_auth_type(
|
||||||
cls, request: Request, auth_header: str
|
cls, request: Request, auth_header: str
|
||||||
) -> Optional[Union[User, ApiKey]]:
|
) -> Optional[Union[User, ApiKey]]:
|
||||||
if auth_header.startswith("Bearer "):
|
if auth_header.startswith("Bearer "):
|
||||||
return await cls.get_user()
|
return await cls.get_user()
|
||||||
elif auth_header.startswith("API-Key "):
|
elif auth_header.startswith("API-Key "):
|
||||||
return await cls.get_api_key(request)
|
return await cls.get_api_key(request)
|
||||||
elif (
|
elif (
|
||||||
auth_header.startswith("DEV-User ")
|
auth_header.startswith("DEV-User ")
|
||||||
and Environment.get_environment() == "development"
|
and Environment.get_environment() == "development"
|
||||||
):
|
):
|
||||||
return await cls.get_dev_user()
|
return await cls.get_dev_user()
|
||||||
return None
|
return None
|
||||||
@ -66,7 +66,7 @@ class Route(RouteUserExtension):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_authenticated_user_or_api_key_or_default(
|
async def get_authenticated_user_or_api_key_or_default(
|
||||||
cls,
|
cls,
|
||||||
) -> Optional[Union[User, ApiKey]]:
|
) -> Optional[Union[User, ApiKey]]:
|
||||||
request = get_request()
|
request = get_request()
|
||||||
if request is None:
|
if request is None:
|
||||||
@ -93,8 +93,8 @@ class Route(RouteUserExtension):
|
|||||||
elif auth_header.startswith("API-Key "):
|
elif auth_header.startswith("API-Key "):
|
||||||
return await cls._verify_api_key(request)
|
return await cls._verify_api_key(request)
|
||||||
elif (
|
elif (
|
||||||
auth_header.startswith("DEV-User ")
|
auth_header.startswith("DEV-User ")
|
||||||
and Environment.get_environment() == "development"
|
and Environment.get_environment() == "development"
|
||||||
):
|
):
|
||||||
user = await cls.get_dev_user()
|
user = await cls.get_dev_user()
|
||||||
return user is not None
|
return user is not None
|
||||||
@ -102,10 +102,10 @@ class Route(RouteUserExtension):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def authorize(
|
def authorize(
|
||||||
cls,
|
cls,
|
||||||
f: Callable = None,
|
f: Callable = None,
|
||||||
skip_in_dev=False,
|
skip_in_dev=False,
|
||||||
by_api_key=False,
|
by_api_key=False,
|
||||||
):
|
):
|
||||||
if f is None:
|
if f is None:
|
||||||
return functools.partial(
|
return functools.partial(
|
||||||
|
@ -13,11 +13,11 @@ class MutationABC(QueryABC):
|
|||||||
QueryABC.__init__(self, f"{name}Mutation")
|
QueryABC.__init__(self, f"{name}Mutation")
|
||||||
|
|
||||||
def add_mutation_type(
|
def add_mutation_type(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
mutation_name: str,
|
mutation_name: str,
|
||||||
require_any_permission=None,
|
require_any_permission=None,
|
||||||
public: bool = False,
|
public: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add mutation type (sub mutation) to the mutation object
|
Add mutation type (sub mutation) to the mutation object
|
||||||
|
@ -64,19 +64,19 @@ class QueryABC(ObjectType):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _require_any(
|
async def _require_any(
|
||||||
cls,
|
cls,
|
||||||
data: Any,
|
data: Any,
|
||||||
permissions: TRequireAnyPermissions,
|
permissions: TRequireAnyPermissions,
|
||||||
resolvers: TRequireAnyResolvers,
|
resolvers: TRequireAnyResolvers,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
info = args[0]
|
info = args[0]
|
||||||
|
|
||||||
if len(permissions) > 0:
|
if len(permissions) > 0:
|
||||||
user = await Route.get_authenticated_user_or_api_key_or_default()
|
user = await Route.get_authenticated_user_or_api_key_or_default()
|
||||||
if user is not None and all(
|
if user is not None and all(
|
||||||
[await user.has_permission(x) for x in permissions]
|
[await user.has_permission(x) for x in permissions]
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -97,13 +97,13 @@ class QueryABC(ObjectType):
|
|||||||
raise AccessDenied()
|
raise AccessDenied()
|
||||||
|
|
||||||
def field(
|
def field(
|
||||||
self,
|
self,
|
||||||
builder: Union[
|
builder: Union[
|
||||||
DaoFieldBuilder,
|
DaoFieldBuilder,
|
||||||
CollectionFieldBuilder,
|
CollectionFieldBuilder,
|
||||||
ResolverFieldBuilder,
|
ResolverFieldBuilder,
|
||||||
MutationFieldBuilder,
|
MutationFieldBuilder,
|
||||||
],
|
],
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add a field to the query
|
Add a field to the query
|
||||||
@ -199,7 +199,7 @@ class QueryABC(ObjectType):
|
|||||||
elif isinstance(field, MutationField):
|
elif isinstance(field, MutationField):
|
||||||
|
|
||||||
async def input_wrapper(
|
async def input_wrapper(
|
||||||
mutation: QueryABC, info: GraphQLResolveInfo, **kwargs
|
mutation: QueryABC, info: GraphQLResolveInfo, **kwargs
|
||||||
):
|
):
|
||||||
if field.input_type is None:
|
if field.input_type is None:
|
||||||
return await resolver_wrapper(mutation, info, **kwargs)
|
return await resolver_wrapper(mutation, info, **kwargs)
|
||||||
@ -230,9 +230,9 @@ class QueryABC(ObjectType):
|
|||||||
await self._authorize()
|
await self._authorize()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
field.require_any is None
|
field.require_any is None
|
||||||
and not field.public
|
and not field.public
|
||||||
and field.require_any_permission
|
and field.require_any_permission
|
||||||
):
|
):
|
||||||
await self._require_any_permission(field.require_any_permission)
|
await self._require_any_permission(field.require_any_permission)
|
||||||
|
|
||||||
@ -252,13 +252,13 @@ class QueryABC(ObjectType):
|
|||||||
|
|
||||||
@deprecated("Use field(FieldBuilder()) instead")
|
@deprecated("Use field(FieldBuilder()) instead")
|
||||||
def mutation(
|
def mutation(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
f: Callable,
|
f: Callable,
|
||||||
input_type: Type[InputABC] = None,
|
input_type: Type[InputABC] = None,
|
||||||
input_key: str = "input",
|
input_key: str = "input",
|
||||||
require_any_permission: list[Permissions] = None,
|
require_any_permission: list[Permissions] = None,
|
||||||
public: bool = False,
|
public: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Adds a mutation to the query
|
Adds a mutation to the query
|
||||||
@ -284,13 +284,13 @@ class QueryABC(ObjectType):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _resolve_collection(
|
def _resolve_collection(
|
||||||
cls,
|
cls,
|
||||||
collection: list,
|
collection: list,
|
||||||
*_,
|
*_,
|
||||||
filters: list[CollectionFilterABC] = None,
|
filters: list[CollectionFilterABC] = None,
|
||||||
sort: list[Sort] = None,
|
sort: list[Sort] = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
) -> CollectionResult:
|
) -> CollectionResult:
|
||||||
total_count = len(collection)
|
total_count = len(collection)
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ class QueryABC(ObjectType):
|
|||||||
return attr
|
return attr
|
||||||
|
|
||||||
for s in reversed(
|
for s in reversed(
|
||||||
sort
|
sort
|
||||||
): # Apply sorting in reverse order to make first primary "orderBy" and other secondary "thenBy"
|
): # Apply sorting in reverse order to make first primary "orderBy" and other secondary "thenBy"
|
||||||
attrs = [a for a in dir(s) if not a.startswith("_")]
|
attrs = [a for a in dir(s) if not a.startswith("_")]
|
||||||
for k in attrs:
|
for k in attrs:
|
||||||
|
@ -16,9 +16,12 @@ class GroupQuery(DbModelQueryABC):
|
|||||||
self.field(
|
self.field(
|
||||||
ResolverFieldBuilder("shortUrls")
|
ResolverFieldBuilder("shortUrls")
|
||||||
.with_resolver(self._get_urls)
|
.with_resolver(self._get_urls)
|
||||||
.with_require_any([
|
.with_require_any(
|
||||||
Permissions.groups,
|
[
|
||||||
], [group_by_assignment_resolver])
|
Permissions.groups,
|
||||||
|
],
|
||||||
|
[group_by_assignment_resolver],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.set_field("roles", self._get_roles)
|
self.set_field("roles", self._get_roles)
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ class Query(QueryABC):
|
|||||||
Permissions.short_urls_create,
|
Permissions.short_urls_create,
|
||||||
Permissions.short_urls_update,
|
Permissions.short_urls_update,
|
||||||
],
|
],
|
||||||
[group_by_assignment_resolver]
|
[group_by_assignment_resolver],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.field(
|
self.field(
|
||||||
|
@ -12,11 +12,19 @@ async def group_by_assignment_resolver(ctx: QueryContext) -> bool:
|
|||||||
groups = [await x.group for x in ctx.data.nodes]
|
groups = [await x.group for x in ctx.data.nodes]
|
||||||
role_ids = {x.id for x in await ctx.user.roles}
|
role_ids = {x.id for x in await ctx.user.roles}
|
||||||
filtered_groups = [
|
filtered_groups = [
|
||||||
g.id for g in groups if
|
g.id
|
||||||
g is not None and (roles := await groupDao.get_roles(g.id)) and all(r.id in role_ids for r in roles)
|
for g in groups
|
||||||
|
if g is not None
|
||||||
|
and (roles := await groupDao.get_roles(g.id))
|
||||||
|
and all(r.id in role_ids for r in roles)
|
||||||
]
|
]
|
||||||
|
|
||||||
ctx.data.nodes = [node for node in ctx.data.nodes if (await node.group) is not None and (await node.group).id in filtered_groups]
|
ctx.data.nodes = [
|
||||||
|
node
|
||||||
|
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 True
|
return True
|
||||||
|
@ -9,7 +9,7 @@ TRequireAnyResolvers = list[
|
|||||||
Union[
|
Union[
|
||||||
Callable[[QueryContext], bool],
|
Callable[[QueryContext], bool],
|
||||||
Awaitable[[QueryContext], bool],
|
Awaitable[[QueryContext], bool],
|
||||||
Callable[[QueryContext], Coroutine[Any, Any, bool]]
|
Callable[[QueryContext], Coroutine[Any, Any, bool]],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
TRequireAny = tuple[TRequireAnyPermissions, TRequireAnyResolvers]
|
TRequireAny = tuple[TRequireAnyPermissions, TRequireAnyResolvers]
|
||||||
|
@ -42,12 +42,12 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return self._table_name
|
return self._table_name
|
||||||
|
|
||||||
def attribute(
|
def attribute(
|
||||||
self,
|
self,
|
||||||
attr_name: Attribute,
|
attr_name: Attribute,
|
||||||
attr_type: type,
|
attr_type: type,
|
||||||
db_name: str = None,
|
db_name: str = None,
|
||||||
ignore=False,
|
ignore=False,
|
||||||
primary_key=False,
|
primary_key=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add an attribute for db and object mapping to the data access object
|
Add an attribute for db and object mapping to the data access object
|
||||||
@ -77,11 +77,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
self.__date_attributes.add(db_name)
|
self.__date_attributes.add(db_name)
|
||||||
|
|
||||||
def reference(
|
def reference(
|
||||||
self,
|
self,
|
||||||
attr: Attribute,
|
attr: Attribute,
|
||||||
primary_attr: Attribute,
|
primary_attr: Attribute,
|
||||||
foreign_attr: Attribute,
|
foreign_attr: Attribute,
|
||||||
table_name: str,
|
table_name: str,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add a reference to another table for the given attribute
|
Add a reference to another table for the given attribute
|
||||||
@ -164,11 +164,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return self.to_object(result[0])
|
return self.to_object(result[0])
|
||||||
|
|
||||||
async def get_by(
|
async def get_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> list[T_DBM]:
|
) -> list[T_DBM]:
|
||||||
"""
|
"""
|
||||||
Get all objects by the given filters
|
Get all objects by the given filters
|
||||||
@ -189,11 +189,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return [self.to_object(x) for x in result]
|
return [self.to_object(x) for x in result]
|
||||||
|
|
||||||
async def get_single_by(
|
async def get_single_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> T_DBM:
|
) -> T_DBM:
|
||||||
"""
|
"""
|
||||||
Get a single object by the given filters
|
Get a single object by the given filters
|
||||||
@ -214,11 +214,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
async def find_by(
|
async def find_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> list[Optional[T_DBM]]:
|
) -> list[Optional[T_DBM]]:
|
||||||
"""
|
"""
|
||||||
Find all objects by the given filters
|
Find all objects by the given filters
|
||||||
@ -238,11 +238,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return [self.to_object(x) for x in result]
|
return [self.to_object(x) for x in result]
|
||||||
|
|
||||||
async def find_single_by(
|
async def find_single_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> Optional[T_DBM]:
|
) -> Optional[T_DBM]:
|
||||||
"""
|
"""
|
||||||
Find a single object by the given filters
|
Find a single object by the given filters
|
||||||
@ -342,7 +342,7 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
await self._db.execute(query)
|
await self._db.execute(query)
|
||||||
|
|
||||||
async def _build_delete_statement(
|
async def _build_delete_statement(
|
||||||
self, obj: T_DBM, hard_delete: bool = False
|
self, obj: T_DBM, hard_delete: bool = False
|
||||||
) -> str:
|
) -> str:
|
||||||
if hard_delete:
|
if hard_delete:
|
||||||
return f"""
|
return f"""
|
||||||
@ -458,11 +458,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return cast_type(value)
|
return cast_type(value)
|
||||||
|
|
||||||
def _build_conditional_query(
|
def _build_conditional_query(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
query = f"SELECT {self._table_name}.* FROM {self._table_name}"
|
query = f"SELECT {self._table_name}.* FROM {self._table_name}"
|
||||||
|
|
||||||
@ -506,7 +506,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
" OR ".join(
|
" OR ".join(
|
||||||
self._build_fuzzy_conditions(
|
self._build_fuzzy_conditions(
|
||||||
[
|
[
|
||||||
self.__db_names[x] if x in self.__db_names else self.__db_names[camel_to_snake(x)]
|
(
|
||||||
|
self.__db_names[x]
|
||||||
|
if x in self.__db_names
|
||||||
|
else self.__db_names[camel_to_snake(x)]
|
||||||
|
)
|
||||||
for x in get_value(values, "fields", list[str])
|
for x in get_value(values, "fields", list[str])
|
||||||
],
|
],
|
||||||
get_value(values, "term", str),
|
get_value(values, "term", str),
|
||||||
@ -546,7 +550,7 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return " AND ".join(conditions)
|
return " AND ".join(conditions)
|
||||||
|
|
||||||
def _build_fuzzy_conditions(
|
def _build_fuzzy_conditions(
|
||||||
self, fields: list[str], term: str, threshold: int = 10
|
self, fields: list[str], term: str, threshold: int = 10
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
conditions = []
|
conditions = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
|
@ -4,11 +4,11 @@ from core.typing import T
|
|||||||
|
|
||||||
|
|
||||||
def get_value(
|
def get_value(
|
||||||
source: dict,
|
source: dict,
|
||||||
key: str,
|
key: str,
|
||||||
cast_type: Type[T],
|
cast_type: Type[T],
|
||||||
default: Optional[T] = None,
|
default: Optional[T] = None,
|
||||||
list_delimiter: str = ",",
|
list_delimiter: str = ",",
|
||||||
) -> Optional[T]:
|
) -> Optional[T]:
|
||||||
"""
|
"""
|
||||||
Get value from source dictionary and cast it to a specified type.
|
Get value from source dictionary and cast it to a specified type.
|
||||||
@ -26,8 +26,8 @@ def get_value(
|
|||||||
|
|
||||||
value = source[key]
|
value = source[key]
|
||||||
if isinstance(
|
if isinstance(
|
||||||
value,
|
value,
|
||||||
cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__,
|
cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__,
|
||||||
):
|
):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -36,10 +36,15 @@ def get_value(
|
|||||||
return value.lower() in ["true", "1"]
|
return value.lower() in ["true", "1"]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__
|
cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__
|
||||||
) == list:
|
) == list:
|
||||||
if not (value.startswith("[") and value.endswith("]")) and list_delimiter not in value:
|
if (
|
||||||
raise ValueError("List values must be enclosed in square brackets or use a delimiter.")
|
not (value.startswith("[") and value.endswith("]"))
|
||||||
|
and list_delimiter not in value
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
"List values must be enclosed in square brackets or use a delimiter."
|
||||||
|
)
|
||||||
|
|
||||||
if value.startswith("[") and value.endswith("]"):
|
if value.startswith("[") and value.endswith("]"):
|
||||||
value = value[1:-1]
|
value = value[1:-1]
|
||||||
|
@ -4,5 +4,6 @@ import re
|
|||||||
def first_to_lower(s: str) -> str:
|
def first_to_lower(s: str) -> str:
|
||||||
return s[0].lower() + s[1:] if s else s
|
return s[0].lower() + s[1:] if s else s
|
||||||
|
|
||||||
|
|
||||||
def camel_to_snake(s: str) -> str:
|
def camel_to_snake(s: str) -> str:
|
||||||
return re.sub(r'(?<!^)(?=[A-Z])', '_', s).lower()
|
return re.sub(r"(?<!^)(?=[A-Z])", "_", s).lower()
|
||||||
|
@ -8,7 +8,6 @@ import { ShortUrlsDataService } from 'src/app/modules/admin/short-urls/short-url
|
|||||||
import { ShortUrlsColumns } from 'src/app/modules/admin/short-urls/short-urls.columns';
|
import { ShortUrlsColumns } from 'src/app/modules/admin/short-urls/short-urls.columns';
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from 'src/app/service/auth.service';
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
||||||
import { SettingsService } from 'src/app/service/settings.service';
|
|
||||||
import QrCodeWithLogo from 'qrcode-with-logos';
|
import QrCodeWithLogo from 'qrcode-with-logos';
|
||||||
import { FileUpload, FileUploadHandlerEvent } from 'primeng/fileupload';
|
import { FileUpload, FileUploadHandlerEvent } from 'primeng/fileupload';
|
||||||
import { ConfigService } from 'src/app/service/config.service';
|
import { ConfigService } from 'src/app/service/config.service';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from 'src/app/model/auth/user';
|
||||||
import { BehaviorSubject, concatWith, firstValueFrom, Observable } from 'rxjs';
|
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from 'apollo-angular';
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
Loading…
Reference in New Issue
Block a user