[WIP] Added domain management
This commit is contained in:
parent
865e6465cf
commit
1001b6db5f
13
api/src/api_graphql/filter/domain_filter.py
Normal file
13
api/src/api_graphql/filter/domain_filter.py
Normal file
@ -0,0 +1,13 @@
|
||||
from api_graphql.abc.db_model_filter_abc import DbModelFilterABC
|
||||
from api_graphql.abc.filter.string_filter import StringFilter
|
||||
|
||||
|
||||
class DomainFilter(DbModelFilterABC):
|
||||
def __init__(
|
||||
self,
|
||||
obj: dict,
|
||||
):
|
||||
DbModelFilterABC.__init__(self, obj)
|
||||
|
||||
self.add_field("name", StringFilter)
|
||||
self.add_field("description", StringFilter)
|
53
api/src/api_graphql/graphql/domain.gql
Normal file
53
api/src/api_graphql/graphql/domain.gql
Normal file
@ -0,0 +1,53 @@
|
||||
type DomainResult {
|
||||
totalCount: Int
|
||||
count: Int
|
||||
nodes: [Domain]
|
||||
}
|
||||
|
||||
type Domain implements DbModel {
|
||||
id: ID
|
||||
name: String
|
||||
|
||||
shortUrls: [ShortUrl]
|
||||
|
||||
deleted: Boolean
|
||||
editor: User
|
||||
createdUtc: String
|
||||
updatedUtc: String
|
||||
}
|
||||
|
||||
input DomainSort {
|
||||
id: SortOrder
|
||||
name: SortOrder
|
||||
|
||||
deleted: SortOrder
|
||||
editorId: SortOrder
|
||||
createdUtc: SortOrder
|
||||
updatedUtc: SortOrder
|
||||
}
|
||||
|
||||
input DomainFilter {
|
||||
id: IntFilter
|
||||
name: StringFilter
|
||||
|
||||
deleted: BooleanFilter
|
||||
editor: IntFilter
|
||||
createdUtc: DateFilter
|
||||
updatedUtc: DateFilter
|
||||
}
|
||||
|
||||
type DomainMutation {
|
||||
create(input: DomainCreateInput!): Domain
|
||||
update(input: DomainUpdateInput!): Domain
|
||||
delete(id: ID!): Boolean
|
||||
restore(id: ID!): Boolean
|
||||
}
|
||||
|
||||
input DomainCreateInput {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input DomainUpdateInput {
|
||||
id: ID!
|
||||
name: String
|
||||
}
|
@ -5,5 +5,6 @@ type Mutation {
|
||||
role: RoleMutation
|
||||
|
||||
group: GroupMutation
|
||||
domain: DomainMutation
|
||||
shortUrl: ShortUrlMutation
|
||||
}
|
@ -11,6 +11,7 @@ type Query {
|
||||
userHasAnyPermission(permissions: [String]!): Boolean
|
||||
notExistingUsersFromKeycloak: KeycloakUserResult
|
||||
|
||||
domains(filter: [DomainFilter], sort: [DomainSort], skip: Int, take: Int): DomainResult
|
||||
groups(filter: [GroupFilter], sort: [GroupSort], skip: Int, take: Int): GroupResult
|
||||
shortUrls(filter: [ShortUrlFilter], sort: [ShortUrlSort], skip: Int, take: Int): ShortUrlResult
|
||||
}
|
@ -11,6 +11,7 @@ type ShortUrl implements DbModel {
|
||||
description: String
|
||||
visits: Int
|
||||
group: Group
|
||||
domain: Domain
|
||||
loadingScreen: Boolean
|
||||
|
||||
deleted: Boolean
|
||||
@ -55,6 +56,7 @@ input ShortUrlCreateInput {
|
||||
targetUrl: String!
|
||||
description: String
|
||||
groupId: ID
|
||||
domainId: ID
|
||||
loadingScreen: Boolean
|
||||
}
|
||||
|
||||
@ -64,5 +66,6 @@ input ShortUrlUpdateInput {
|
||||
targetUrl: String
|
||||
description: String
|
||||
groupId: ID
|
||||
domainId: ID
|
||||
loadingScreen: Boolean
|
||||
}
|
||||
|
13
api/src/api_graphql/input/domain_create_input.py
Normal file
13
api/src/api_graphql/input/domain_create_input.py
Normal file
@ -0,0 +1,13 @@
|
||||
from api_graphql.abc.input_abc import InputABC
|
||||
|
||||
|
||||
class DomainCreateInput(InputABC):
|
||||
|
||||
def __init__(self, src: dict):
|
||||
InputABC.__init__(self, src)
|
||||
|
||||
self._name = self.option("name", str, required=True)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
18
api/src/api_graphql/input/domain_update_input.py
Normal file
18
api/src/api_graphql/input/domain_update_input.py
Normal file
@ -0,0 +1,18 @@
|
||||
from api_graphql.abc.input_abc import InputABC
|
||||
|
||||
|
||||
class DomainUpdateInput(InputABC):
|
||||
|
||||
def __init__(self, src: dict):
|
||||
InputABC.__init__(self, src)
|
||||
|
||||
self._id = self.option("id", int, required=True)
|
||||
self._name = self.option("name", str)
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
@ -12,6 +12,7 @@ class ShortUrlCreateInput(InputABC):
|
||||
self._target_url = self.option("targetUrl", str, required=True)
|
||||
self._description = self.option("description", str)
|
||||
self._group_id = self.option("groupId", int)
|
||||
self._domain_id = self.option("domainId", int)
|
||||
self._loading_screen = self.option("loadingScreen", bool)
|
||||
|
||||
@property
|
||||
@ -30,6 +31,10 @@ class ShortUrlCreateInput(InputABC):
|
||||
def group_id(self) -> Optional[int]:
|
||||
return self._group_id
|
||||
|
||||
@property
|
||||
def domain_id(self) -> Optional[int]:
|
||||
return self._domain_id
|
||||
|
||||
@property
|
||||
def loading_screen(self) -> Optional[str]:
|
||||
return self._loading_screen
|
||||
|
@ -13,6 +13,7 @@ class ShortUrlUpdateInput(InputABC):
|
||||
self._target_url = self.option("targetUrl", str)
|
||||
self._description = self.option("description", str)
|
||||
self._group_id = self.option("groupId", int)
|
||||
self._domain_id = self.option("domainId", int)
|
||||
self._loading_screen = self.option("loadingScreen", bool)
|
||||
|
||||
@property
|
||||
@ -35,6 +36,10 @@ class ShortUrlUpdateInput(InputABC):
|
||||
def group_id(self) -> Optional[int]:
|
||||
return self._group_id
|
||||
|
||||
@property
|
||||
def domain_id(self) -> Optional[int]:
|
||||
return self._domain_id
|
||||
|
||||
@property
|
||||
def loading_screen(self) -> Optional[str]:
|
||||
return self._loading_screen
|
||||
|
@ -33,6 +33,16 @@ class Mutation(MutationABC):
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
self.add_mutation_type(
|
||||
"domain",
|
||||
"Domain",
|
||||
require_any_permission=[
|
||||
Permissions.domains_create,
|
||||
Permissions.domains_update,
|
||||
Permissions.domains_delete,
|
||||
],
|
||||
)
|
||||
self.add_mutation_type(
|
||||
"group",
|
||||
"Group",
|
||||
|
75
api/src/api_graphql/mutations/domain_mutation.py
Normal file
75
api/src/api_graphql/mutations/domain_mutation.py
Normal file
@ -0,0 +1,75 @@
|
||||
from api_graphql.abc.mutation_abc import MutationABC
|
||||
from api_graphql.input.domain_create_input import DomainCreateInput
|
||||
from api_graphql.input.domain_update_input import DomainUpdateInput
|
||||
from api_graphql.input.group_create_input import GroupCreateInput
|
||||
from api_graphql.input.group_update_input import GroupUpdateInput
|
||||
from core.logger import APILogger
|
||||
from data.schemas.public.domain_dao import domainDao
|
||||
from data.schemas.public.group import Group
|
||||
from service.permission.permissions_enum import Permissions
|
||||
|
||||
logger = APILogger(__name__)
|
||||
|
||||
|
||||
class DomainMutation(MutationABC):
|
||||
def __init__(self):
|
||||
MutationABC.__init__(self, "Domain")
|
||||
|
||||
self.mutation(
|
||||
"create",
|
||||
self.resolve_create,
|
||||
DomainCreateInput,
|
||||
require_any_permission=[Permissions.domains_create],
|
||||
)
|
||||
self.mutation(
|
||||
"update",
|
||||
self.resolve_update,
|
||||
DomainUpdateInput,
|
||||
require_any_permission=[Permissions.domains_update],
|
||||
)
|
||||
self.mutation(
|
||||
"delete",
|
||||
self.resolve_delete,
|
||||
require_any_permission=[Permissions.domains_delete],
|
||||
)
|
||||
self.mutation(
|
||||
"restore",
|
||||
self.resolve_restore,
|
||||
require_any_permission=[Permissions.domains_delete],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def resolve_create(obj: GroupCreateInput, *_):
|
||||
logger.debug(f"create domain: {obj.__dict__}")
|
||||
|
||||
domain = Group(
|
||||
0,
|
||||
obj.name,
|
||||
)
|
||||
nid = await domainDao.create(domain)
|
||||
return await domainDao.get_by_id(nid)
|
||||
|
||||
@staticmethod
|
||||
async def resolve_update(obj: GroupUpdateInput, *_):
|
||||
logger.debug(f"update domain: {input}")
|
||||
|
||||
if obj.name is not None:
|
||||
domain = await domainDao.get_by_id(obj.id)
|
||||
domain.name = obj.name
|
||||
await domainDao.update(domain)
|
||||
|
||||
return await domainDao.get_by_id(obj.id)
|
||||
|
||||
@staticmethod
|
||||
async def resolve_delete(*_, id: str):
|
||||
logger.debug(f"delete domain: {id}")
|
||||
domain = await domainDao.get_by_id(id)
|
||||
await domainDao.delete(domain)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
async def resolve_restore(*_, id: str):
|
||||
logger.debug(f"restore domain: {id}")
|
||||
domain = await domainDao.get_by_id(id)
|
||||
await domainDao.restore(domain)
|
||||
return True
|
@ -4,6 +4,7 @@ from api_graphql.abc.mutation_abc import MutationABC
|
||||
from api_graphql.input.short_url_create_input import ShortUrlCreateInput
|
||||
from api_graphql.input.short_url_update_input import ShortUrlUpdateInput
|
||||
from core.logger import APILogger
|
||||
from data.schemas.public.domain_dao import domainDao
|
||||
from data.schemas.public.group_dao import groupDao
|
||||
from data.schemas.public.short_url import ShortUrl
|
||||
from data.schemas.public.short_url_dao import shortUrlDao
|
||||
@ -49,6 +50,7 @@ class ShortUrlMutation(MutationABC):
|
||||
obj.target_url,
|
||||
obj.description,
|
||||
obj.group_id,
|
||||
obj.domain_id,
|
||||
obj.loading_screen,
|
||||
)
|
||||
nid = await shortUrlDao.create(short_url)
|
||||
@ -74,6 +76,16 @@ class ShortUrlMutation(MutationABC):
|
||||
if group_by_id is None:
|
||||
raise NotFound(f"Group with id {obj.group_id} does not exist")
|
||||
short_url.group_id = obj.group_id
|
||||
else:
|
||||
short_url.group_id = None
|
||||
|
||||
if obj.domain_id is not None:
|
||||
domain_by_id = await domainDao.find_by_id(obj.domain_id)
|
||||
if domain_by_id is None:
|
||||
raise NotFound(f"Domain with id {obj.domain_id} does not exist")
|
||||
short_url.domain_id = obj.domain_id
|
||||
else:
|
||||
short_url.domain_id = None
|
||||
|
||||
if obj.loading_screen is not None:
|
||||
short_url.loading_screen = obj.loading_screen
|
||||
|
17
api/src/api_graphql/queries/domain_query.py
Normal file
17
api/src/api_graphql/queries/domain_query.py
Normal file
@ -0,0 +1,17 @@
|
||||
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
||||
from data.schemas.public.domain import Domain
|
||||
from data.schemas.public.group import Group
|
||||
from data.schemas.public.short_url import ShortUrl
|
||||
from data.schemas.public.short_url_dao import shortUrlDao
|
||||
|
||||
|
||||
class DomainQuery(DbModelQueryABC):
|
||||
def __init__(self):
|
||||
DbModelQueryABC.__init__(self, "Domain")
|
||||
|
||||
self.set_field("name", lambda x, *_: x.name)
|
||||
self.set_field("shortUrls", self._get_urls)
|
||||
|
||||
@staticmethod
|
||||
async def _get_urls(domain: Domain, *_):
|
||||
return await shortUrlDao.find_by({ShortUrl.domain_id: domain.id})
|
@ -9,5 +9,6 @@ class ShortUrlQuery(DbModelQueryABC):
|
||||
self.set_field("targetUrl", lambda x, *_: x.target_url)
|
||||
self.set_field("description", lambda x, *_: x.description)
|
||||
self.set_field("group", lambda x, *_: x.group)
|
||||
self.set_field("domain", lambda x, *_: x.domain)
|
||||
self.set_field("visits", lambda x, *_: x.visit_count)
|
||||
self.set_field("loadingScreen", lambda x, *_: x.loading_screen)
|
||||
|
@ -5,6 +5,7 @@ from api_graphql.abc.sort_abc import Sort
|
||||
from api_graphql.field.dao_field_builder import DaoFieldBuilder
|
||||
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
||||
from api_graphql.filter.api_key_filter import ApiKeyFilter
|
||||
from api_graphql.filter.domain_filter import DomainFilter
|
||||
from api_graphql.filter.group_filter import GroupFilter
|
||||
from api_graphql.filter.permission_filter import PermissionFilter
|
||||
from api_graphql.filter.role_filter import RoleFilter
|
||||
@ -18,6 +19,8 @@ from data.schemas.permission.permission import Permission
|
||||
from data.schemas.permission.permission_dao import permissionDao
|
||||
from data.schemas.permission.role import Role
|
||||
from data.schemas.permission.role_dao import roleDao
|
||||
from data.schemas.public.domain import Domain
|
||||
from data.schemas.public.domain_dao import domainDao
|
||||
from data.schemas.public.group import Group
|
||||
from data.schemas.public.group_dao import groupDao
|
||||
from data.schemas.public.short_url import ShortUrl
|
||||
@ -80,6 +83,20 @@ class Query(QueryABC):
|
||||
.with_require_any_permission([Permissions.users_create])
|
||||
)
|
||||
|
||||
|
||||
self.field(
|
||||
DaoFieldBuilder("domains")
|
||||
.with_dao(domainDao)
|
||||
.with_filter(DomainFilter)
|
||||
.with_sort(Sort[Domain])
|
||||
.with_require_any_permission(
|
||||
[
|
||||
Permissions.domains,
|
||||
Permissions.short_urls_create,
|
||||
Permissions.short_urls_update,
|
||||
]
|
||||
)
|
||||
)
|
||||
self.field(
|
||||
DaoFieldBuilder("groups")
|
||||
.with_dao(groupDao)
|
||||
|
27
api/src/data/schemas/public/domain.py
Normal file
27
api/src/data/schemas/public/domain.py
Normal file
@ -0,0 +1,27 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from core.database.abc.db_model_abc import DbModelABC
|
||||
from core.typing import SerialId
|
||||
|
||||
|
||||
class Domain(DbModelABC):
|
||||
def __init__(
|
||||
self,
|
||||
id: SerialId,
|
||||
name: str,
|
||||
deleted: bool = False,
|
||||
editor_id: Optional[SerialId] = None,
|
||||
created: Optional[datetime] = None,
|
||||
updated: Optional[datetime] = None,
|
||||
):
|
||||
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value: str):
|
||||
self._name = value
|
22
api/src/data/schemas/public/domain_dao.py
Normal file
22
api/src/data/schemas/public/domain_dao.py
Normal file
@ -0,0 +1,22 @@
|
||||
from core.logger import DBLogger
|
||||
from data.schemas.public.domain import Domain
|
||||
from data.schemas.public.group import Group
|
||||
|
||||
logger = DBLogger(__name__)
|
||||
|
||||
from core.database.abc.db_model_dao_abc import DbModelDaoABC
|
||||
|
||||
|
||||
class DomainDao(DbModelDaoABC[Group]):
|
||||
def __init__(self):
|
||||
DbModelDaoABC.__init__(self, __name__, Group, "public.domains")
|
||||
self.attribute(Domain.name, str)
|
||||
|
||||
async def get_by_name(self, name: str) -> Group:
|
||||
result = await self._db.select_map(
|
||||
f"SELECT * FROM {self._table_name} WHERE Name = '{name}'"
|
||||
)
|
||||
return self.to_object(result[0])
|
||||
|
||||
|
||||
domainDao = DomainDao()
|
@ -16,6 +16,7 @@ class ShortUrl(DbModelABC):
|
||||
target_url: str,
|
||||
description: Optional[str],
|
||||
group_id: Optional[SerialId],
|
||||
domain_id: Optional[SerialId],
|
||||
loading_screen: Optional[str] = None,
|
||||
deleted: bool = False,
|
||||
editor_id: Optional[SerialId] = None,
|
||||
@ -27,6 +28,7 @@ class ShortUrl(DbModelABC):
|
||||
self._target_url = target_url
|
||||
self._description = description
|
||||
self._group_id = group_id
|
||||
self._domain_id = domain_id
|
||||
self._loading_screen = loading_screen
|
||||
|
||||
@property
|
||||
@ -70,6 +72,23 @@ class ShortUrl(DbModelABC):
|
||||
|
||||
return await groupDao.get_by_id(self._group_id)
|
||||
|
||||
@property
|
||||
def domain_id(self) -> SerialId:
|
||||
return self._domain_id
|
||||
|
||||
@domain_id.setter
|
||||
def domain_id(self, value: SerialId):
|
||||
self._domain_id = value
|
||||
|
||||
@async_property
|
||||
async def domain(self) -> Optional[Group]:
|
||||
if self._domain_id is None:
|
||||
return None
|
||||
|
||||
from data.schemas.public.domain_dao import domainDao
|
||||
|
||||
return await domainDao.get_by_id(self._domain_id)
|
||||
|
||||
@async_property
|
||||
async def visit_count(self) -> int:
|
||||
from data.schemas.public.short_url_visit_dao import shortUrlVisitDao
|
||||
|
@ -13,6 +13,7 @@ class ShortUrlDao(DbModelDaoABC[ShortUrl]):
|
||||
self.attribute(ShortUrl.target_url, str)
|
||||
self.attribute(ShortUrl.description, str)
|
||||
self.attribute(ShortUrl.group_id, int)
|
||||
self.attribute(ShortUrl.domain_id, int)
|
||||
self.attribute(ShortUrl.loading_screen, bool)
|
||||
|
||||
|
||||
|
32
api/src/data/scripts/2025-01-10-23-15-domains.sql
Normal file
32
api/src/data/scripts/2025-01-10-23-15-domains.sql
Normal file
@ -0,0 +1,32 @@
|
||||
CREATE
|
||||
SCHEMA IF NOT EXISTS public;
|
||||
|
||||
-- groups
|
||||
CREATE TABLE IF NOT EXISTS public.domains
|
||||
(
|
||||
Id SERIAL PRIMARY KEY,
|
||||
Name VARCHAR(255) NOT NULL,
|
||||
-- for history
|
||||
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
EditorId INT NULL REFERENCES administration.users (Id),
|
||||
CreatedUtc timestamptz NOT NULL DEFAULT NOW(),
|
||||
UpdatedUtc timestamptz NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.domains_history
|
||||
(
|
||||
LIKE public.domains
|
||||
);
|
||||
|
||||
CREATE TRIGGER domains_history_trigger
|
||||
BEFORE INSERT OR UPDATE OR DELETE
|
||||
ON public.domains
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.history_trigger_function();
|
||||
|
||||
ALTER TABLE public.short_urls
|
||||
ADD COLUMN domainId INT NULL REFERENCES public.domains (Id);
|
||||
|
||||
ALTER TABLE public.short_urls_history
|
||||
ADD COLUMN domainId INT NULL REFERENCES public.domains (Id);
|
||||
|
@ -31,6 +31,12 @@ class Permissions(Enum):
|
||||
"""
|
||||
Public
|
||||
"""
|
||||
# domains
|
||||
domains = "domains"
|
||||
domains_create = "domains.create"
|
||||
domains_update = "domains.update"
|
||||
domains_delete = "domains.delete"
|
||||
|
||||
# groups
|
||||
groups = "groups"
|
||||
groups_create = "groups.create"
|
||||
|
@ -1,30 +1,35 @@
|
||||
export enum PermissionsEnum {
|
||||
// Administration
|
||||
apiKeys = "api_keys",
|
||||
apiKeysCreate = "api_keys.create",
|
||||
apiKeysUpdate = "api_keys.update",
|
||||
apiKeysDelete = "api_keys.delete",
|
||||
apiKeys = 'api_keys',
|
||||
apiKeysCreate = 'api_keys.create',
|
||||
apiKeysUpdate = 'api_keys.update',
|
||||
apiKeysDelete = 'api_keys.delete',
|
||||
|
||||
// Users
|
||||
users = "users",
|
||||
usersCreate = "users.create",
|
||||
usersUpdate = "users.update",
|
||||
usersDelete = "users.delete",
|
||||
users = 'users',
|
||||
usersCreate = 'users.create',
|
||||
usersUpdate = 'users.update',
|
||||
usersDelete = 'users.delete',
|
||||
|
||||
// Permissions
|
||||
roles = "roles",
|
||||
rolesCreate = "roles.create",
|
||||
rolesUpdate = "roles.update",
|
||||
rolesDelete = "roles.delete",
|
||||
roles = 'roles',
|
||||
rolesCreate = 'roles.create',
|
||||
rolesUpdate = 'roles.update',
|
||||
rolesDelete = 'roles.delete',
|
||||
|
||||
// Public
|
||||
groups = "groups",
|
||||
groupsCreate = "groups.create",
|
||||
groupsUpdate = "groups.update",
|
||||
groupsDelete = "groups.delete",
|
||||
domains = 'domains',
|
||||
domainsCreate = 'domains.create',
|
||||
domainsUpdate = 'domains.update',
|
||||
domainsDelete = 'domains.delete',
|
||||
|
||||
shortUrls = "short_urls",
|
||||
shortUrlsCreate = "short_urls.create",
|
||||
shortUrlsUpdate = "short_urls.update",
|
||||
shortUrlsDelete = "short_urls.delete",
|
||||
groups = 'groups',
|
||||
groupsCreate = 'groups.create',
|
||||
groupsUpdate = 'groups.update',
|
||||
groupsDelete = 'groups.delete',
|
||||
|
||||
shortUrls = 'short_urls',
|
||||
shortUrlsCreate = 'short_urls.create',
|
||||
shortUrlsUpdate = 'short_urls.update',
|
||||
shortUrlsDelete = 'short_urls.delete',
|
||||
}
|
||||
|
14
web/src/app/model/entities/domain.ts
Normal file
14
web/src/app/model/entities/domain.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { DbModel } from 'src/app/model/entities/db-model';
|
||||
|
||||
export interface Domain extends DbModel {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DomainCreateInput {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DomainUpdateInput {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { DbModel } from 'src/app/model/entities/db-model';
|
||||
import { Group } from 'src/app/model/entities/group';
|
||||
import { Domain } from 'src/app/model/entities/domain';
|
||||
|
||||
export interface ShortUrl extends DbModel {
|
||||
shortUrl: string;
|
||||
@ -8,6 +9,7 @@ export interface ShortUrl extends DbModel {
|
||||
loadingScreen: boolean;
|
||||
visits: number;
|
||||
group?: Group;
|
||||
domain?: Domain;
|
||||
}
|
||||
|
||||
export interface ShortUrlDto {
|
||||
@ -22,6 +24,7 @@ export interface ShortUrlCreateInput {
|
||||
description: string;
|
||||
loadingScreen: boolean;
|
||||
groupId: number;
|
||||
domainId: number;
|
||||
}
|
||||
|
||||
export interface ShortUrlUpdateInput {
|
||||
@ -31,4 +34,5 @@ export interface ShortUrlUpdateInput {
|
||||
description: string;
|
||||
loadingScreen: boolean;
|
||||
groupId: number;
|
||||
domainId: number;
|
||||
}
|
||||
|
@ -6,6 +6,15 @@ import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'domains',
|
||||
loadChildren: () =>
|
||||
import('src/app/modules/admin/domains/domains.module').then(
|
||||
m => m.DomainsModule
|
||||
),
|
||||
canActivate: [PermissionGuard],
|
||||
data: { permissions: [PermissionsEnum.domains] },
|
||||
},
|
||||
{
|
||||
path: 'groups',
|
||||
loadChildren: () =>
|
||||
|
35
web/src/app/modules/admin/domains/domains.columns.ts
Normal file
35
web/src/app/modules/admin/domains/domains.columns.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Injectable, Provider } from '@angular/core';
|
||||
import {
|
||||
DB_MODEL_COLUMNS,
|
||||
ID_COLUMN,
|
||||
PageColumns,
|
||||
} from 'src/app/core/base/page.columns';
|
||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
||||
import { Domain } from 'src/app/model/entities/domain';
|
||||
|
||||
@Injectable()
|
||||
export class DomainsColumns extends PageColumns<Domain> {
|
||||
get(): TableColumn<Domain>[] {
|
||||
return [
|
||||
ID_COLUMN,
|
||||
{
|
||||
name: 'name',
|
||||
label: 'common.name',
|
||||
type: 'text',
|
||||
filterable: true,
|
||||
value: (row: Domain) => row.name,
|
||||
},
|
||||
...DB_MODEL_COLUMNS,
|
||||
];
|
||||
}
|
||||
|
||||
static provide(): Provider[] {
|
||||
return [
|
||||
{
|
||||
provide: PageColumns,
|
||||
useClass: DomainsColumns,
|
||||
},
|
||||
DomainsColumns,
|
||||
];
|
||||
}
|
||||
}
|
232
web/src/app/modules/admin/domains/domains.data.service.ts
Normal file
232
web/src/app/modules/admin/domains/domains.data.service.ts
Normal file
@ -0,0 +1,232 @@
|
||||
import { Injectable, Provider } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
Create,
|
||||
Delete,
|
||||
PageDataService,
|
||||
Restore,
|
||||
Update,
|
||||
} from 'src/app/core/base/page.data.service';
|
||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
||||
import { Apollo, gql } from 'apollo-angular';
|
||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
||||
import {
|
||||
Domain,
|
||||
DomainCreateInput,
|
||||
DomainUpdateInput,
|
||||
} from 'src/app/model/entities/domain';
|
||||
|
||||
@Injectable()
|
||||
export class DomainsDataService
|
||||
extends PageDataService<Domain>
|
||||
implements
|
||||
Create<Domain, DomainCreateInput>,
|
||||
Update<Domain, DomainUpdateInput>,
|
||||
Delete<Domain>,
|
||||
Restore<Domain>
|
||||
{
|
||||
constructor(
|
||||
private spinner: SpinnerService,
|
||||
private apollo: Apollo
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
load(
|
||||
filter?: Filter[] | undefined,
|
||||
sort?: Sort[] | undefined,
|
||||
skip?: number | undefined,
|
||||
take?: number | undefined
|
||||
): Observable<QueryResult<Domain>> {
|
||||
return this.apollo
|
||||
.query<{ domains: QueryResult<Domain> }>({
|
||||
query: gql`
|
||||
query getDomains(
|
||||
$filter: [DomainFilter]
|
||||
$sort: [DomainSort]
|
||||
$skip: Int
|
||||
$take: Int
|
||||
) {
|
||||
domains(filter: $filter, sort: $sort, skip: $skip, take: $take) {
|
||||
count
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${DB_MODEL_FRAGMENT}
|
||||
`,
|
||||
variables: {
|
||||
filter: filter,
|
||||
sort: sort,
|
||||
skip: skip,
|
||||
take: take,
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data.domains));
|
||||
}
|
||||
|
||||
loadById(id: number): Observable<Domain> {
|
||||
return this.apollo
|
||||
.query<{ domains: QueryResult<Domain> }>({
|
||||
query: gql`
|
||||
query getDomain($id: Int) {
|
||||
domain(filter: { id: { equal: $id } }) {
|
||||
id
|
||||
name
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
}
|
||||
|
||||
${DB_MODEL_FRAGMENT}
|
||||
`,
|
||||
variables: {
|
||||
id: id,
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data.domains.nodes[0]));
|
||||
}
|
||||
|
||||
create(object: DomainCreateInput): Observable<Domain | undefined> {
|
||||
return this.apollo
|
||||
.mutate<{ domain: { create: Domain } }>({
|
||||
mutation: gql`
|
||||
mutation createDomain($input: DomainCreateInput!) {
|
||||
domain {
|
||||
create(input: $input) {
|
||||
id
|
||||
name
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${DB_MODEL_FRAGMENT}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
name: object.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data?.domain.create));
|
||||
}
|
||||
|
||||
update(object: DomainUpdateInput): Observable<Domain | undefined> {
|
||||
return this.apollo
|
||||
.mutate<{ domain: { update: Domain } }>({
|
||||
mutation: gql`
|
||||
mutation updateDomain($input: DomainUpdateInput!) {
|
||||
domain {
|
||||
update(input: $input) {
|
||||
id
|
||||
name
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${DB_MODEL_FRAGMENT}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
id: object.id,
|
||||
name: object.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data?.domain.update));
|
||||
}
|
||||
|
||||
delete(object: Domain): Observable<boolean> {
|
||||
return this.apollo
|
||||
.mutate<{ domain: { delete: boolean } }>({
|
||||
mutation: gql`
|
||||
mutation deleteDomain($id: ID!) {
|
||||
domain {
|
||||
delete(id: $id)
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: object.id,
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data?.domain.delete ?? false));
|
||||
}
|
||||
|
||||
restore(object: Domain): Observable<boolean> {
|
||||
return this.apollo
|
||||
.mutate<{ domain: { restore: boolean } }>({
|
||||
mutation: gql`
|
||||
mutation restoreDomain($id: ID!) {
|
||||
domain {
|
||||
restore(id: $id)
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: object.id,
|
||||
},
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data?.domain.restore ?? false));
|
||||
}
|
||||
|
||||
static provide(): Provider[] {
|
||||
return [
|
||||
{
|
||||
provide: PageDataService,
|
||||
useClass: DomainsDataService,
|
||||
},
|
||||
DomainsDataService,
|
||||
];
|
||||
}
|
||||
}
|
43
web/src/app/modules/admin/domains/domains.module.ts
Normal file
43
web/src/app/modules/admin/domains/domains.module.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
||||
import { DomainsPage } from 'src/app/modules/admin/domains/domains.page';
|
||||
import { DomainFormPageComponent } from 'src/app/modules/admin/domains/form-page/domain-form-page.component';
|
||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
||||
import { DomainsColumns } from 'src/app/modules/admin/domains/domains.columns';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
title: 'Domains',
|
||||
component: DomainsPage,
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
component: DomainFormPageComponent,
|
||||
canActivate: [PermissionGuard],
|
||||
data: {
|
||||
permissions: [PermissionsEnum.apiKeysCreate],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:id',
|
||||
component: DomainFormPageComponent,
|
||||
canActivate: [PermissionGuard],
|
||||
data: {
|
||||
permissions: [PermissionsEnum.apiKeysUpdate],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [DomainsPage, DomainFormPageComponent],
|
||||
imports: [CommonModule, SharedModule, RouterModule.forChild(routes)],
|
||||
providers: [DomainsDataService.provide(), DomainsColumns.provide()],
|
||||
})
|
||||
export class DomainsModule {}
|
19
web/src/app/modules/admin/domains/domains.page.html
Normal file
19
web/src/app/modules/admin/domains/domains.page.html
Normal file
@ -0,0 +1,19 @@
|
||||
<app-table
|
||||
[rows]="result.nodes"
|
||||
[columns]="columns"
|
||||
[rowsPerPageOptions]="rowsPerPageOptions"
|
||||
[totalCount]="result.totalCount"
|
||||
[requireAnyPermissions]="requiredPermissions"
|
||||
countHeaderTranslation="domain.count_header"
|
||||
[loading]="loading"
|
||||
[(filter)]="filter"
|
||||
[(sort)]="sort"
|
||||
[(skip)]="skip"
|
||||
[(take)]="take"
|
||||
(load)="load()"
|
||||
[create]="true"
|
||||
[update]="true"
|
||||
(delete)="delete($event)"
|
||||
(restore)="restore($event)"></app-table>
|
||||
|
||||
<router-outlet></router-outlet>
|
0
web/src/app/modules/admin/domains/domains.page.scss
Normal file
0
web/src/app/modules/admin/domains/domains.page.scss
Normal file
51
web/src/app/modules/admin/domains/domains.page.spec.ts
Normal file
51
web/src/app/modules/admin/domains/domains.page.spec.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page";
|
||||
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||
import { TranslateModule } from "@ngx-translate/core";
|
||||
import { AuthService } from "src/app/service/auth.service";
|
||||
import { KeycloakService } from "keycloak-angular";
|
||||
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||
import { ToastService } from "src/app/service/toast.service";
|
||||
import { ConfirmationService, MessageService } from "primeng/api";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { of } from "rxjs";
|
||||
import { PageDataService } from "src/app/core/base/page.data.service";
|
||||
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||
|
||||
describe("ApiKeysComponent", () => {
|
||||
let component: ApiKeysPage;
|
||||
let fixture: ComponentFixture<ApiKeysPage>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ApiKeysPage],
|
||||
imports: [SharedModule, TranslateModule.forRoot()],
|
||||
providers: [
|
||||
AuthService,
|
||||
KeycloakService,
|
||||
ErrorHandlingService,
|
||||
ToastService,
|
||||
MessageService,
|
||||
ConfirmationService,
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {
|
||||
snapshot: { params: of({}) },
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: PageDataService,
|
||||
useClass: ApiKeysDataService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ApiKeysPage);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
72
web/src/app/modules/admin/domains/domains.page.ts
Normal file
72
web/src/app/modules/admin/domains/domains.page.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { PageBase } from 'src/app/core/base/page-base';
|
||||
import { ToastService } from 'src/app/service/toast.service';
|
||||
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
|
||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
||||
import { Group } from 'src/app/model/entities/group';
|
||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
||||
import { DomainsColumns } from 'src/app/modules/admin/domains/domains.columns';
|
||||
|
||||
@Component({
|
||||
selector: 'app-domains',
|
||||
templateUrl: './domains.page.html',
|
||||
styleUrl: './domains.page.scss',
|
||||
})
|
||||
export class DomainsPage extends PageBase<
|
||||
Group,
|
||||
DomainsDataService,
|
||||
DomainsColumns
|
||||
> {
|
||||
constructor(
|
||||
private toast: ToastService,
|
||||
private confirmation: ConfirmationDialogService
|
||||
) {
|
||||
super(true, {
|
||||
read: [PermissionsEnum.domains],
|
||||
create: [PermissionsEnum.domainsCreate],
|
||||
update: [PermissionsEnum.domainsUpdate],
|
||||
delete: [PermissionsEnum.domainsDelete],
|
||||
restore: [PermissionsEnum.domainsDelete],
|
||||
});
|
||||
}
|
||||
|
||||
load(): void {
|
||||
this.loading = true;
|
||||
this.dataService
|
||||
.load(this.filter, this.sort, this.skip, this.take)
|
||||
.subscribe(result => {
|
||||
this.result = result;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
delete(group: Group): void {
|
||||
this.confirmation.confirmDialog({
|
||||
header: 'dialog.delete.header',
|
||||
message: 'dialog.delete.message',
|
||||
accept: () => {
|
||||
this.loading = true;
|
||||
this.dataService.delete(group).subscribe(() => {
|
||||
this.toast.success('action.deleted');
|
||||
this.load();
|
||||
});
|
||||
},
|
||||
messageParams: { entity: group.name },
|
||||
});
|
||||
}
|
||||
|
||||
restore(group: Group): void {
|
||||
this.confirmation.confirmDialog({
|
||||
header: 'dialog.restore.header',
|
||||
message: 'dialog.restore.message',
|
||||
accept: () => {
|
||||
this.loading = true;
|
||||
this.dataService.restore(group).subscribe(() => {
|
||||
this.toast.success('action.restored');
|
||||
this.load();
|
||||
});
|
||||
},
|
||||
messageParams: { entity: group.name },
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<app-form-page
|
||||
*ngIf="node"
|
||||
[formGroup]="form"
|
||||
[isUpdate]="isUpdate"
|
||||
(onSave)="save()"
|
||||
(onClose)="close()">
|
||||
<ng-template formPageHeader let-isUpdate>
|
||||
<h2>
|
||||
{{ 'common.group' | translate }}
|
||||
{{
|
||||
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
||||
| translate
|
||||
}}
|
||||
</h2>
|
||||
</ng-template>
|
||||
|
||||
<ng-template formPageContent>
|
||||
<div class="form-page-input">
|
||||
<p class="label">{{ 'common.id' | translate }}</p>
|
||||
<input pInputText class="value" type="number" formControlName="id"/>
|
||||
</div>
|
||||
<div class="form-page-input">
|
||||
<p class="label">{{ 'common.name' | translate }}</p>
|
||||
<input
|
||||
pInputText
|
||||
class="value"
|
||||
type="text"
|
||||
formControlName="name"/>
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-form-page>
|
@ -0,0 +1,50 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component";
|
||||
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||
import { TranslateModule } from "@ngx-translate/core";
|
||||
import { AuthService } from "src/app/service/auth.service";
|
||||
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||
import { ToastService } from "src/app/service/toast.service";
|
||||
import { ConfirmationService, MessageService } from "primeng/api";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { of } from "rxjs";
|
||||
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
|
||||
describe("ApiKeyFormpageComponent", () => {
|
||||
let component: RoleFormPageComponent;
|
||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RoleFormPageComponent],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
SharedModule,
|
||||
TranslateModule.forRoot(),
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
ErrorHandlingService,
|
||||
ToastService,
|
||||
MessageService,
|
||||
ConfirmationService,
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {
|
||||
snapshot: { params: of({}) },
|
||||
},
|
||||
},
|
||||
ApiKeysDataService,
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RoleFormPageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,93 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { ToastService } from 'src/app/service/toast.service';
|
||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
||||
import {
|
||||
Domain,
|
||||
DomainCreateInput,
|
||||
DomainUpdateInput,
|
||||
} from 'src/app/model/entities/domain';
|
||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-domain-form-page',
|
||||
templateUrl: './domain-form-page.component.html',
|
||||
styleUrl: './domain-form-page.component.scss',
|
||||
})
|
||||
export class DomainFormPageComponent extends FormPageBase<
|
||||
Domain,
|
||||
DomainCreateInput,
|
||||
DomainUpdateInput,
|
||||
DomainsDataService
|
||||
> {
|
||||
constructor(private toast: ToastService) {
|
||||
super();
|
||||
if (!this.nodeId) {
|
||||
this.node = this.new();
|
||||
this.setForm(this.node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataService
|
||||
.load([{ id: { equal: this.nodeId } }])
|
||||
.subscribe(apiKey => {
|
||||
this.node = apiKey.nodes[0];
|
||||
this.setForm(this.node);
|
||||
});
|
||||
}
|
||||
|
||||
new(): Domain {
|
||||
return {} as Domain;
|
||||
}
|
||||
|
||||
buildForm() {
|
||||
this.form = new FormGroup({
|
||||
id: new FormControl<number | undefined>(undefined),
|
||||
name: new FormControl<string | undefined>(undefined, Validators.required),
|
||||
});
|
||||
this.form.controls['id'].disable();
|
||||
}
|
||||
|
||||
setForm(node?: Domain) {
|
||||
this.form.controls['id'].setValue(node?.id);
|
||||
this.form.controls['name'].setValue(node?.name);
|
||||
}
|
||||
|
||||
getCreateInput(): DomainCreateInput {
|
||||
return {
|
||||
name: this.form.controls['name'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['name'].value ?? undefined),
|
||||
};
|
||||
}
|
||||
|
||||
getUpdateInput(): DomainUpdateInput {
|
||||
if (!this.node?.id) {
|
||||
throw new Error('Node id is missing');
|
||||
}
|
||||
|
||||
return {
|
||||
id: this.form.controls['id'].value,
|
||||
name: this.form.controls['name'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['name'].value ?? undefined),
|
||||
};
|
||||
}
|
||||
|
||||
create(apiKey: DomainCreateInput): void {
|
||||
this.dataService.create(apiKey).subscribe(() => {
|
||||
this.spinner.hide();
|
||||
this.toast.success('action.created');
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
update(apiKey: DomainUpdateInput): void {
|
||||
this.dataService.update(apiKey).subscribe(() => {
|
||||
this.spinner.hide();
|
||||
this.toast.success('action.created');
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
(onClose)="close()">
|
||||
<ng-template formPageHeader let-isUpdate>
|
||||
<h2>
|
||||
{{ 'common.group' | translate }}
|
||||
{{ 'common.short_url' | translate }}
|
||||
{{
|
||||
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
||||
| translate
|
||||
@ -65,5 +65,20 @@
|
||||
></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-page-input">
|
||||
<p class="label">{{ 'common.domain' | translate }}</p>
|
||||
<div
|
||||
class="value">
|
||||
<p-dropdown
|
||||
[options]="domains"
|
||||
formControlName="domainId"
|
||||
[showClear]="true"
|
||||
[filter]="true"
|
||||
filterBy="name"
|
||||
optionLabel="name"
|
||||
optionValue="id"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-form-page>
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
} from 'src/app/model/entities/short-url';
|
||||
import { ShortUrlsDataService } from 'src/app/modules/admin/short-urls/short-urls.data.service';
|
||||
import { Group } from 'src/app/model/entities/group';
|
||||
import { Domain } from 'src/app/model/entities/domain';
|
||||
|
||||
@Component({
|
||||
selector: 'app-short-url-form-page',
|
||||
@ -22,12 +23,16 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
||||
ShortUrlsDataService
|
||||
> {
|
||||
groups: Group[] = [];
|
||||
domains: Domain[] = [];
|
||||
|
||||
constructor(private toast: ToastService) {
|
||||
super();
|
||||
this.dataService.getAllGroups().subscribe(groups => {
|
||||
this.groups = groups;
|
||||
});
|
||||
this.dataService.getAllDomains().subscribe(domains => {
|
||||
this.domains = domains;
|
||||
});
|
||||
|
||||
if (!this.nodeId) {
|
||||
this.node = this.new();
|
||||
@ -62,6 +67,7 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
||||
description: new FormControl<string | undefined>(undefined),
|
||||
loadingScreen: new FormControl<boolean | undefined>(undefined),
|
||||
groupId: new FormControl<number | undefined>(undefined),
|
||||
domainId: new FormControl<number | undefined>(undefined),
|
||||
});
|
||||
this.form.controls['id'].disable();
|
||||
}
|
||||
@ -86,6 +92,7 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
||||
this.form.controls['description'].setValue(node?.description);
|
||||
this.form.controls['loadingScreen'].setValue(node?.loadingScreen);
|
||||
this.form.controls['groupId'].setValue(node?.group?.id);
|
||||
this.form.controls['domainId'].setValue(node?.domain?.id);
|
||||
}
|
||||
|
||||
getCreateInput(): ShortUrlCreateInput {
|
||||
@ -103,6 +110,9 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
||||
groupId: this.form.controls['groupId'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['groupId'].value ?? undefined),
|
||||
domainId: this.form.controls['domainId'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['domainId'].value ?? undefined),
|
||||
};
|
||||
}
|
||||
|
||||
@ -128,6 +138,9 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
||||
groupId: this.form.controls['groupId'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['groupId'].value ?? undefined),
|
||||
domainId: this.form.controls['domainId'].pristine
|
||||
? undefined
|
||||
: (this.form.controls['domainId'].value ?? undefined),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
ShortUrlUpdateInput,
|
||||
} from 'src/app/model/entities/short-url';
|
||||
import { Group } from 'src/app/model/entities/group';
|
||||
import { Domain } from 'src/app/model/entities/domain';
|
||||
|
||||
@Injectable()
|
||||
export class ShortUrlsDataService
|
||||
@ -66,6 +67,10 @@ export class ShortUrlsDataService
|
||||
id
|
||||
name
|
||||
}
|
||||
domain {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
@ -106,6 +111,10 @@ export class ShortUrlsDataService
|
||||
id
|
||||
name
|
||||
}
|
||||
domain {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
...DB_MODEL
|
||||
}
|
||||
@ -150,6 +159,7 @@ export class ShortUrlsDataService
|
||||
description: object.description,
|
||||
loadingScreen: object.loadingScreen,
|
||||
groupId: object.groupId,
|
||||
domainId: object.domainId,
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -187,6 +197,7 @@ export class ShortUrlsDataService
|
||||
description: object.description,
|
||||
loadingScreen: object.loadingScreen,
|
||||
groupId: object.groupId,
|
||||
domainId: object.domainId,
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -268,6 +279,29 @@ export class ShortUrlsDataService
|
||||
.pipe(map(result => result.data.groups.nodes));
|
||||
}
|
||||
|
||||
getAllDomains() {
|
||||
return this.apollo
|
||||
.query<{ domains: QueryResult<Domain> }>({
|
||||
query: gql`
|
||||
query getGroups {
|
||||
domains {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
this.spinner.hide();
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
.pipe(map(result => result.data.domains.nodes));
|
||||
}
|
||||
|
||||
static provide(): Provider[] {
|
||||
return [
|
||||
{
|
||||
|
@ -22,6 +22,10 @@
|
||||
<div class="pi pi-{{ url.loadingScreen ? 'check-circle' : 'times-circle' }}"></div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid-container" *ngIf="url.domain">
|
||||
<span class="grid-label font-bold">{{ 'common.domain' | translate }}:</span>
|
||||
<span class="grid-value">{{ url.domain?.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex">
|
||||
|
@ -30,6 +30,14 @@ export class SidebarService {
|
||||
// trust me, you'll need this async
|
||||
async setElements() {
|
||||
const elements: MenuElement[] = [
|
||||
{
|
||||
label: 'common.domains',
|
||||
icon: 'pi pi-sitemap',
|
||||
routerLink: ['/admin/domains'],
|
||||
visible: await this.auth.hasAnyPermissionLazy([
|
||||
PermissionsEnum.domains,
|
||||
]),
|
||||
},
|
||||
{
|
||||
label: 'common.groups',
|
||||
icon: 'pi pi-tags',
|
||||
|
@ -25,6 +25,8 @@
|
||||
"created": "Erstellt",
|
||||
"deleted": "Gelöscht",
|
||||
"description": "Beschreibung",
|
||||
"domain": "Domain",
|
||||
"domains": "Domains",
|
||||
"download": "Herunterladen",
|
||||
"edited_at": "Bearbeitet am",
|
||||
"editor": "Bearbeiter",
|
||||
@ -58,6 +60,9 @@
|
||||
},
|
||||
"save": "Speichern"
|
||||
},
|
||||
"domain": {
|
||||
"count_header": "Domain(s)"
|
||||
},
|
||||
"error": {
|
||||
"404": "404 - Nicht gefunden",
|
||||
"create_failed": "Erstellung fehlgeschlagen",
|
||||
|
@ -25,6 +25,8 @@
|
||||
"created": "Created",
|
||||
"deleted": "Deleted",
|
||||
"description": "Description",
|
||||
"domain": "Domain",
|
||||
"domains": "Domains",
|
||||
"download": "Download",
|
||||
"edited_at": "Edited at",
|
||||
"editor": "Editor",
|
||||
@ -58,6 +60,9 @@
|
||||
},
|
||||
"save": "Save"
|
||||
},
|
||||
"domain": {
|
||||
"count_header": "Domain(s)"
|
||||
},
|
||||
"error": {
|
||||
"404": "404 - Not found",
|
||||
"create_failed": "Create failed",
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
background-color: $backgroundColor2;
|
||||
|
||||
|
||||
@layer utilities {
|
||||
.highlight {
|
||||
color: $textColorHighlight;
|
||||
@ -59,6 +58,10 @@
|
||||
.deleted {
|
||||
color: $accentColor !important;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid $accentColor;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
@ -67,6 +70,10 @@
|
||||
color: $headerColor;
|
||||
}
|
||||
|
||||
input, .p-checkbox-box, .p-dropdown {
|
||||
border: 1px solid $accentColor;
|
||||
}
|
||||
|
||||
.app {
|
||||
.component {
|
||||
background-color: $backgroundColor;
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
background-color: $backgroundColor2;
|
||||
|
||||
|
||||
@layer utilities {
|
||||
.highlight {
|
||||
color: $textColorHighlight;
|
||||
@ -59,6 +58,10 @@
|
||||
.deleted {
|
||||
color: $accentColor !important;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid $accentColor;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
@ -67,6 +70,10 @@
|
||||
color: $headerColor;
|
||||
}
|
||||
|
||||
input, .p-checkbox-box, .p-dropdown {
|
||||
border: 1px solid $accentColor;
|
||||
}
|
||||
|
||||
.app {
|
||||
.component {
|
||||
background-color: $backgroundColor;
|
||||
|
Loading…
Reference in New Issue
Block a user