Dev api 240111 (#377)

* feat(api): My subscription supports CIType sorting

* feat(api): db change
This commit is contained in:
pycook 2024-01-11 18:01:37 +08:00 committed by GitHub
parent 691051c254
commit 6e3f9478b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 151 additions and 32 deletions

View File

@ -11,6 +11,8 @@ from api.models.cmdb import Attribute
from api.models.cmdb import CI
from api.models.cmdb import CIType
from api.models.cmdb import CITypeAttribute
from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView
from api.models.cmdb import RelationType
@ -228,8 +230,9 @@ class CITypeAttributeCache(object):
class CMDBCounterCache(object):
KEY = 'CMDB::Counter'
KEY2 = 'CMDB::Counter2'
KEY = 'CMDB::Counter::dashboard'
KEY2 = 'CMDB::Counter::adc'
KEY3 = 'CMDB::Counter::sub'
@classmethod
def get(cls):
@ -450,3 +453,29 @@ class CMDBCounterCache(object):
@classmethod
def get_adc_counter(cls):
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 -*-
import copy
import toposort
from flask import abort
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 CITypeUniqueConstraint
from api.models.cmdb import CustomDashboard
from api.models.cmdb import PreferenceCITypeOrder
from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes
@ -75,12 +75,13 @@ class CITypeManager(object):
return CIType.get_by_id(ci_type.id)
@staticmethod
def get_ci_types(type_name=None):
def get_ci_types(type_name=None, like=True):
resources = None
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)])
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()
for type_dict in ci_types:
attr = AttributeCache.get(type_dict["unique_id"])
@ -222,7 +223,7 @@ class CITypeManager(object):
for table in [PreferenceTreeView, PreferenceShowAttributes, PreferenceSearchOption, CustomDashboard,
CITypeGroupItem, CITypeAttributeGroup, CITypeAttribute, CITypeUniqueConstraint, CITypeTrigger,
AutoDiscoveryCIType, CIFilterPerms]:
AutoDiscoveryCIType, CIFilterPerms, PreferenceCITypeOrder]:
for item in table.get_by(type_id=type_id, to_dict=False):
item.soft_delete(commit=False)
@ -1274,7 +1275,7 @@ class CITypeTemplateManager(object):
from api.lib.common_setting.upload_file import CommonFileCRUD
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(),
type2attributes=dict(),
type2attribute_group=dict(),

View File

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

View File

@ -2,7 +2,6 @@
import copy
import six
import toposort
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 CITypeAttributesCache
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 PermEnum
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.models.cmdb import CITypeAttribute
from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceCITypeOrder
from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes
@ -38,13 +39,22 @@ class PreferenceManager(object):
@staticmethod
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(
PreferenceShowAttributes.uid == current_user.uid).filter(
PreferenceShowAttributes.deleted.is_(False)).group_by(
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 []
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]
@ -59,32 +69,36 @@ class PreferenceManager(object):
:param tree:
:return:
"""
result = dict(self=dict(instance=[], tree=[], type_id2subs_time=dict()),
type_id2users=dict())
result = dict(self=dict(instance=[], tree=[], type_id2subs_time=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:
types = db.session.query(PreferenceShowAttributes.type_id,
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)
for i in types:
if i.uid == current_user.uid:
result['self']['instance'].append(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']['instance'].append(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['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:
types = PreferenceTreeView.get_by(to_dict=False)
types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False)
for i in types:
if i.uid == current_user.uid:
result['self']['tree'].append(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']['tree'].append(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['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)
tree_order = [i.type_id for i in ci_type_order if i.is_tree]
if len(tree_order) == len(result['self']['tree']):
result['self']['tree'] = tree_order
return result
@ -151,9 +165,22 @@ class PreferenceManager(object):
if i.attr_id not in attr_dict:
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
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)
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:
if item["levels"]:
ci_type = CITypeCache.get(item['type_id']).to_dict()
@ -172,8 +199,8 @@ class PreferenceManager(object):
return res
@staticmethod
def create_or_update_tree_view(type_id, levels):
@classmethod
def create_or_update_tree_view(cls, type_id, levels):
attrs = CITypeAttributesCache.get(type_id)
for idx, i in enumerate(levels):
for attr in attrs:
@ -185,9 +212,12 @@ class PreferenceManager(object):
if existed is not None:
if not levels:
existed.soft_delete()
cls.delete_ci_type_order_item(type_id, is_tree=True)
return existed
return existed.update(levels=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)
@staticmethod
@ -356,6 +386,9 @@ class PreferenceManager(object):
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
i.soft_delete()
for i in PreferenceCITypeOrder.get_by(type_id=type_id, uid=uid, to_dict=False):
i.soft_delete()
@staticmethod
def can_edit_relation(parent_id, child_id):
views = PreferenceRelationView.get_by(to_dict=False)
@ -381,3 +414,36 @@ class PreferenceManager(object):
return False
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

@ -2,7 +2,6 @@
import datetime
from sqlalchemy.dialects.mysql import DOUBLE
from api.extensions import db
@ -464,6 +463,15 @@ class PreferenceSearchOption(Model):
option = db.Column(db.JSON)
class PreferenceCITypeOrder(Model):
__tablename__ = "c_pcto"
uid = db.Column(db.Integer, index=True, nullable=False)
type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'))
order = db.Column(db.SmallInteger, default=0)
is_tree = db.Column(db.Boolean, default=False) # True is tree view, False is resource view
# custom
class CustomDashboard(Model):
__tablename__ = "c_c_d"

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)
resource = None
if 'ci_filter' in request.values or 'attr_filter' in request.values:
CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
else:
resource = CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
if not resource:
from api.tasks.acl import role_rebuild
from api.lib.perm.acl.const import ACL_QUEUE
app_id = AppCache.get('cmdb').id
current_app.logger.info((rid, app_id))
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
current_app.logger.info('done')
return self.jsonify(code=200)

View File

@ -2,6 +2,7 @@
from flask import abort
from flask import current_app
from flask import request
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)
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)