feat(api): My subscription supports CIType sorting

This commit is contained in:
pycook 2024-01-11 17:58:33 +08:00
parent 691051c254
commit f355ca4bcc
6 changed files with 142 additions and 31 deletions

View File

@ -11,6 +11,8 @@ from api.models.cmdb import Attribute
from api.models.cmdb import CI from api.models.cmdb import CI
from api.models.cmdb import CIType from api.models.cmdb import CIType
from api.models.cmdb import CITypeAttribute from api.models.cmdb import CITypeAttribute
from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView
from api.models.cmdb import RelationType from api.models.cmdb import RelationType
@ -228,8 +230,9 @@ class CITypeAttributeCache(object):
class CMDBCounterCache(object): class CMDBCounterCache(object):
KEY = 'CMDB::Counter' KEY = 'CMDB::Counter::dashboard'
KEY2 = 'CMDB::Counter2' KEY2 = 'CMDB::Counter::adc'
KEY3 = 'CMDB::Counter::sub'
@classmethod @classmethod
def get(cls): def get(cls):
@ -450,3 +453,29 @@ class CMDBCounterCache(object):
@classmethod @classmethod
def get_adc_counter(cls): def get_adc_counter(cls):
return cache.get(cls.KEY2) or cls.flush_adc_counter() return cache.get(cls.KEY2) or cls.flush_adc_counter()
@classmethod
def flush_sub_counter(cls):
result = dict(type_id2users=dict())
types = db.session.query(PreferenceShowAttributes.type_id,
PreferenceShowAttributes.uid, PreferenceShowAttributes.created_at).filter(
PreferenceShowAttributes.deleted.is_(False)).group_by(
PreferenceShowAttributes.uid, PreferenceShowAttributes.type_id)
for i in types:
result['type_id2users'].setdefault(i.type_id, []).append(i.uid)
types = PreferenceTreeView.get_by(to_dict=False)
for i in types:
result['type_id2users'].setdefault(i.type_id, [])
if i.uid not in result['type_id2users'][i.type_id]:
result['type_id2users'][i.type_id].append(i.uid)
cache.set(cls.KEY3, result, timeout=0)
return result
@classmethod
def get_sub_counter(cls):
return cache.get(cls.KEY3) or cls.flush_sub_counter()

View File

@ -1,7 +1,6 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import copy import copy
import toposort import toposort
from flask import abort from flask import abort
from flask import current_app from flask import current_app
@ -46,6 +45,7 @@ from api.models.cmdb import CITypeRelation
from api.models.cmdb import CITypeTrigger from api.models.cmdb import CITypeTrigger
from api.models.cmdb import CITypeUniqueConstraint from api.models.cmdb import CITypeUniqueConstraint
from api.models.cmdb import CustomDashboard from api.models.cmdb import CustomDashboard
from api.models.cmdb import PreferenceCITypeOrder
from api.models.cmdb import PreferenceRelationView from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceSearchOption from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceShowAttributes
@ -75,12 +75,13 @@ class CITypeManager(object):
return CIType.get_by_id(ci_type.id) return CIType.get_by_id(ci_type.id)
@staticmethod @staticmethod
def get_ci_types(type_name=None): def get_ci_types(type_name=None, like=True):
resources = None resources = None
if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'): if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'):
resources = set([i.get('name') for i in ACLManager().get_resources(ResourceTypeEnum.CI_TYPE)]) resources = set([i.get('name') for i in ACLManager().get_resources(ResourceTypeEnum.CI_TYPE)])
ci_types = CIType.get_by() if type_name is None else CIType.get_by_like(name=type_name) ci_types = CIType.get_by() if type_name is None else (
CIType.get_by_like(name=type_name) if like else CIType.get_by(name=type_name))
res = list() res = list()
for type_dict in ci_types: for type_dict in ci_types:
attr = AttributeCache.get(type_dict["unique_id"]) attr = AttributeCache.get(type_dict["unique_id"])
@ -222,7 +223,7 @@ class CITypeManager(object):
for table in [PreferenceTreeView, PreferenceShowAttributes, PreferenceSearchOption, CustomDashboard, for table in [PreferenceTreeView, PreferenceShowAttributes, PreferenceSearchOption, CustomDashboard,
CITypeGroupItem, CITypeAttributeGroup, CITypeAttribute, CITypeUniqueConstraint, CITypeTrigger, CITypeGroupItem, CITypeAttributeGroup, CITypeAttribute, CITypeUniqueConstraint, CITypeTrigger,
AutoDiscoveryCIType, CIFilterPerms]: AutoDiscoveryCIType, CIFilterPerms, PreferenceCITypeOrder]:
for item in table.get_by(type_id=type_id, to_dict=False): for item in table.get_by(type_id=type_id, to_dict=False):
item.soft_delete(commit=False) item.soft_delete(commit=False)
@ -1274,7 +1275,7 @@ class CITypeTemplateManager(object):
from api.lib.common_setting.upload_file import CommonFileCRUD from api.lib.common_setting.upload_file import CommonFileCRUD
tpt = dict( tpt = dict(
ci_types=CITypeManager.get_ci_types(type_name=ci_type.name), ci_types=CITypeManager.get_ci_types(type_name=ci_type.name, like=False),
ci_type_auto_discovery_rules=list(), ci_type_auto_discovery_rules=list(),
type2attributes=dict(), type2attributes=dict(),
type2attribute_group=dict(), type2attribute_group=dict(),

View File

@ -114,6 +114,8 @@ class CIFilterPermsCRUD(DBMixin):
obj.soft_delete() obj.soft_delete()
return obj
else: else:
if not kwargs.get('ci_filter') and not kwargs.get('attr_filter'): if not kwargs.get('ci_filter') and not kwargs.get('attr_filter'):
return return

View File

@ -2,7 +2,6 @@
import copy import copy
import six import six
import toposort import toposort
from flask import abort from flask import abort
@ -14,6 +13,7 @@ from api.lib.cmdb.attribute import AttributeManager
from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeAttributesCache from api.lib.cmdb.cache import CITypeAttributesCache
from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.cache import CMDBCounterCache
from api.lib.cmdb.const import ConstraintEnum from api.lib.cmdb.const import ConstraintEnum
from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import ResourceTypeEnum
@ -24,6 +24,7 @@ from api.lib.exception import AbortException
from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import ACLManager
from api.models.cmdb import CITypeAttribute from api.models.cmdb import CITypeAttribute
from api.models.cmdb import CITypeRelation from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceCITypeOrder
from api.models.cmdb import PreferenceRelationView from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceSearchOption from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceShowAttributes
@ -38,13 +39,22 @@ class PreferenceManager(object):
@staticmethod @staticmethod
def get_types(instance=False, tree=False): def get_types(instance=False, tree=False):
ci_type_order = sorted(PreferenceCITypeOrder.get_by(uid=current_user.uid, to_dict=False), key=lambda x: x.order)
types = db.session.query(PreferenceShowAttributes.type_id).filter( types = db.session.query(PreferenceShowAttributes.type_id).filter(
PreferenceShowAttributes.uid == current_user.uid).filter( PreferenceShowAttributes.uid == current_user.uid).filter(
PreferenceShowAttributes.deleted.is_(False)).group_by( PreferenceShowAttributes.deleted.is_(False)).group_by(
PreferenceShowAttributes.type_id).all() if instance else [] PreferenceShowAttributes.type_id).all() if instance else []
types = sorted(types, key=lambda x: {i.type_id: idx for idx, i in enumerate(
ci_type_order) if not i.is_tree}.get(x.type_id, 1))
tree_types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False) if tree else [] tree_types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False) if tree else []
type_ids = set([i.type_id for i in types + tree_types]) tree_types = sorted(tree_types, key=lambda x: {i.type_id: idx for idx, i in enumerate(
ci_type_order) if i.is_tree}.get(x.type_id, 1))
type_ids = [i.type_id for i in types + tree_types]
if types and tree_types:
type_ids = set(type_ids)
return [CITypeCache.get(type_id).to_dict() for type_id in type_ids] return [CITypeCache.get(type_id).to_dict() for type_id in type_ids]
@ -59,32 +69,36 @@ class PreferenceManager(object):
:param tree: :param tree:
:return: :return:
""" """
result = dict(self=dict(instance=[], tree=[], type_id2subs_time=dict()), result = dict(self=dict(instance=[], tree=[], type_id2subs_time=dict()))
type_id2users=dict())
result.update(CMDBCounterCache.get_sub_counter())
ci_type_order = sorted(PreferenceCITypeOrder.get_by(uid=current_user.uid, to_dict=False), key=lambda x: x.order)
if instance: if instance:
types = db.session.query(PreferenceShowAttributes.type_id, types = db.session.query(PreferenceShowAttributes.type_id,
PreferenceShowAttributes.uid, PreferenceShowAttributes.created_at).filter( PreferenceShowAttributes.uid, PreferenceShowAttributes.created_at).filter(
PreferenceShowAttributes.deleted.is_(False)).group_by( PreferenceShowAttributes.deleted.is_(False)).filter(
PreferenceShowAttributes.uid == current_user.uid).group_by(
PreferenceShowAttributes.uid, PreferenceShowAttributes.type_id) PreferenceShowAttributes.uid, PreferenceShowAttributes.type_id)
for i in types: for i in types:
if i.uid == current_user.uid:
result['self']['instance'].append(i.type_id) result['self']['instance'].append(i.type_id)
if str(i.created_at) > str(result['self']['type_id2subs_time'].get(i.type_id, "")): if str(i.created_at) > str(result['self']['type_id2subs_time'].get(i.type_id, "")):
result['self']['type_id2subs_time'][i.type_id] = i.created_at result['self']['type_id2subs_time'][i.type_id] = i.created_at
result['type_id2users'].setdefault(i.type_id, []).append(i.uid) instance_order = [i.type_id for i in ci_type_order if not i.is_tree]
if len(instance_order) == len(result['self']['instance']):
result['self']['instance'] = instance_order
if tree: if tree:
types = PreferenceTreeView.get_by(to_dict=False) types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False)
for i in types: for i in types:
if i.uid == current_user.uid:
result['self']['tree'].append(i.type_id) result['self']['tree'].append(i.type_id)
if str(i.created_at) > str(result['self']['type_id2subs_time'].get(i.type_id, "")): if str(i.created_at) > str(result['self']['type_id2subs_time'].get(i.type_id, "")):
result['self']['type_id2subs_time'][i.type_id] = i.created_at result['self']['type_id2subs_time'][i.type_id] = i.created_at
result['type_id2users'].setdefault(i.type_id, []) tree_order = [i.type_id for i in ci_type_order if i.is_tree]
if i.uid not in result['type_id2users'][i.type_id]: if len(tree_order) == len(result['self']['tree']):
result['type_id2users'][i.type_id].append(i.uid) result['self']['tree'] = tree_order
return result return result
@ -151,9 +165,22 @@ class PreferenceManager(object):
if i.attr_id not in attr_dict: if i.attr_id not in attr_dict:
i.soft_delete() i.soft_delete()
if not existed_all and attr_order:
cls.add_ci_type_order_item(type_id, is_tree=False)
elif not PreferenceShowAttributes.get_by(type_id=type_id, uid=current_user.uid, to_dict=False):
cls.delete_ci_type_order_item(type_id, is_tree=False)
@staticmethod @staticmethod
def get_tree_view(): def get_tree_view():
ci_type_order = sorted(PreferenceCITypeOrder.get_by(uid=current_user.uid, is_tree=True, to_dict=False),
key=lambda x: x.order)
res = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=True) res = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=True)
if ci_type_order:
res = sorted(res, key=lambda x: {ii.type_id: idx for idx, ii in enumerate(
ci_type_order)}.get(x['type_id'], 1))
for item in res: for item in res:
if item["levels"]: if item["levels"]:
ci_type = CITypeCache.get(item['type_id']).to_dict() ci_type = CITypeCache.get(item['type_id']).to_dict()
@ -172,8 +199,8 @@ class PreferenceManager(object):
return res return res
@staticmethod @classmethod
def create_or_update_tree_view(type_id, levels): def create_or_update_tree_view(cls, type_id, levels):
attrs = CITypeAttributesCache.get(type_id) attrs = CITypeAttributesCache.get(type_id)
for idx, i in enumerate(levels): for idx, i in enumerate(levels):
for attr in attrs: for attr in attrs:
@ -185,9 +212,12 @@ class PreferenceManager(object):
if existed is not None: if existed is not None:
if not levels: if not levels:
existed.soft_delete() existed.soft_delete()
cls.delete_ci_type_order_item(type_id, is_tree=True)
return existed return existed
return existed.update(levels=levels) return existed.update(levels=levels)
elif levels: elif levels:
cls.add_ci_type_order_item(type_id, is_tree=True)
return PreferenceTreeView.create(levels=levels, type_id=type_id, uid=current_user.uid) return PreferenceTreeView.create(levels=levels, type_id=type_id, uid=current_user.uid)
@staticmethod @staticmethod
@ -356,6 +386,9 @@ class PreferenceManager(object):
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False): for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
i.soft_delete() i.soft_delete()
for i in PreferenceCITypeOrder.get_by(type_id=type_id, uid=uid, to_dict=False):
i.soft_delete()
@staticmethod @staticmethod
def can_edit_relation(parent_id, child_id): def can_edit_relation(parent_id, child_id):
views = PreferenceRelationView.get_by(to_dict=False) views = PreferenceRelationView.get_by(to_dict=False)
@ -381,3 +414,36 @@ class PreferenceManager(object):
return False return False
return True return True
@staticmethod
def add_ci_type_order_item(type_id, is_tree=False):
max_order = PreferenceCITypeOrder.get_by(
uid=current_user.uid, is_tree=is_tree, only_query=True).order_by(PreferenceCITypeOrder.order.desc()).first()
order = (max_order and max_order.order + 1) or 1
PreferenceCITypeOrder.create(type_id=type_id, is_tree=is_tree, uid=current_user.uid, order=order)
@staticmethod
def delete_ci_type_order_item(type_id, is_tree=False):
existed = PreferenceCITypeOrder.get_by(uid=current_user.uid, type_id=type_id, is_tree=is_tree,
first=True, to_dict=False)
existed and existed.soft_delete()
@staticmethod
def upsert_ci_type_order(type_ids, is_tree=False):
for idx, type_id in enumerate(type_ids):
order = idx + 1
existed = PreferenceCITypeOrder.get_by(uid=current_user.uid, type_id=type_id, is_tree=is_tree,
to_dict=False, first=True)
if existed is not None:
existed.update(order=order, flush=True)
else:
PreferenceCITypeOrder.create(uid=current_user.uid, type_id=type_id, is_tree=is_tree, order=order,
flush=True)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.error("upsert citype order failed: {}".format(e))
return abort(400, ErrFormat.unknown_error)

View File

@ -465,16 +465,16 @@ class CITypeGrantView(APIView):
acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms, rebuild=False) acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms, rebuild=False)
resource = None
if 'ci_filter' in request.values or 'attr_filter' in request.values: if 'ci_filter' in request.values or 'attr_filter' in request.values:
CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values) resource = CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
else:
if not resource:
from api.tasks.acl import role_rebuild from api.tasks.acl import role_rebuild
from api.lib.perm.acl.const import ACL_QUEUE from api.lib.perm.acl.const import ACL_QUEUE
app_id = AppCache.get('cmdb').id app_id = AppCache.get('cmdb').id
current_app.logger.info((rid, app_id))
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE) role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
current_app.logger.info('done')
return self.jsonify(code=200) return self.jsonify(code=200)

View File

@ -2,6 +2,7 @@
from flask import abort from flask import abort
from flask import current_app
from flask import request from flask import request
from api.lib.cmdb.ci_type import CITypeManager from api.lib.cmdb.ci_type import CITypeManager
@ -187,3 +188,15 @@ class PreferenceRelationRevokeView(APIView):
acl.revoke_resource_from_role_by_rid(name, rid, ResourceTypeEnum.RELATION_VIEW, perms) acl.revoke_resource_from_role_by_rid(name, rid, ResourceTypeEnum.RELATION_VIEW, perms)
return self.jsonify(code=200) return self.jsonify(code=200)
class PreferenceCITypeOrderView(APIView):
url_prefix = ("/preference/ci_types/order",)
def post(self):
type_ids = request.values.get("type_ids")
is_tree = request.values.get("is_tree") in current_app.config.get('BOOL_TRUE')
PreferenceManager.upsert_ci_type_order(type_ids, is_tree)
return self.jsonify(type_ids=type_ids, is_tree=is_tree)