From 11dc7a6013b3af592e2d81ebba5038d13cf42a12 Mon Sep 17 00:00:00 2001
From: pycook <pycook@126.com>
Date: Wed, 17 Apr 2024 17:50:46 +0800
Subject: [PATCH] feat(api): custom attribute display (#478)

---
 cmdb-api/api/lib/cmdb/ci_type.py              |  8 ++++-
 cmdb-api/api/lib/cmdb/preference.py           |  3 ++
 .../api/lib/cmdb/search/ci_relation/search.py | 36 +++++++++++++------
 cmdb-api/api/models/cmdb.py                   |  6 +++-
 4 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py
index cee1e7b..8284afd 100644
--- a/cmdb-api/api/lib/cmdb/ci_type.py
+++ b/cmdb-api/api/lib/cmdb/ci_type.py
@@ -92,6 +92,9 @@ class CITypeManager(object):
         for type_dict in ci_types:
             attr = AttributeCache.get(type_dict["unique_id"])
             type_dict["unique_key"] = attr and attr.name
+            if type_dict.get('show_id'):
+                attr = AttributeCache.get(type_dict["show_id"])
+                type_dict["show_name"] = attr and attr.name
             type_dict['parent_ids'] = CITypeInheritanceManager.get_parents(type_dict['id'])
             if resources is None or type_dict['name'] in resources:
                 res.append(type_dict)
@@ -192,7 +195,7 @@ class CITypeManager(object):
                 CITypeAttributeManager.update(type_id, [attr])
 
         ci_type2 = ci_type.to_dict()
-        new = ci_type.update(**kwargs)
+        new = ci_type.update(**kwargs, filter_none=False)
 
         CITypeCache.clean(type_id)
 
@@ -680,6 +683,9 @@ class CITypeAttributeManager(object):
 
                 CITypeAttributeCache.clean(type_id, attr_id)
 
+            if ci_type.show_id == attr_id:
+                ci_type.update(show_id=None, filter_none=False)
+
             CITypeHistoryManager.add(CITypeOperateType.DELETE_ATTRIBUTE, type_id, attr_id=attr.id,
                                      change=attr and attr.to_dict())
 
diff --git a/cmdb-api/api/lib/cmdb/preference.py b/cmdb-api/api/lib/cmdb/preference.py
index f5659d5..a82bc0a 100644
--- a/cmdb-api/api/lib/cmdb/preference.py
+++ b/cmdb-api/api/lib/cmdb/preference.py
@@ -297,6 +297,9 @@ class PreferenceManager(object):
 
         for type_id in id2type:
             id2type[type_id] = CITypeCache.get(type_id).to_dict()
+            id2type[type_id]['unique_name'] = AttributeCache.get(id2type[type_id]['unique_id']).name
+            id2type[type_id]['show_name'] = AttributeCache.get(
+                id2type[type_id]['show_id']).name if id2type[type_id]['show_id'] else None
 
         return result, id2type, sorted(name2id, key=lambda x: x[1])
 
diff --git a/cmdb-api/api/lib/cmdb/search/ci_relation/search.py b/cmdb-api/api/lib/cmdb/search/ci_relation/search.py
index b1a20ba..97f8ff6 100644
--- a/cmdb-api/api/lib/cmdb/search/ci_relation/search.py
+++ b/cmdb-api/api/lib/cmdb/search/ci_relation/search.py
@@ -21,6 +21,7 @@ from api.lib.cmdb.resp_format import ErrFormat
 from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
 from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
 from api.lib.cmdb.utils import TableMap
+from api.lib.cmdb.utils import ValueTypeMap
 from api.lib.perm.acl.acl import ACLManager
 from api.lib.perm.acl.acl import is_app_admin
 from api.models.cmdb import CI
@@ -340,10 +341,21 @@ class Search(object):
     def search_full(self, type_ids):
         def _get_id2name(_type_id):
             ci_type = CITypeCache.get(_type_id)
-            attr = AttributeCache.get(ci_type.show_id) or AttributeCache.get(ci_type.unique_id)
-            value_table = TableMap(attr=attr).table
 
-            return {i.ci_id: i.value for i in value_table.get_by(attr_id=attr.id, to_dict=False)}
+            attr = AttributeCache.get(ci_type.unique_id)
+            value_table = TableMap(attr=attr).table
+            serializer = ValueTypeMap.serialize[attr.value_type]
+            unique_value = {i.ci_id: serializer(i.value) for i in value_table.get_by(attr_id=attr.id, to_dict=False)}
+
+            attr = AttributeCache.get(ci_type.show_id)
+            if attr:
+                value_table = TableMap(attr=attr).table
+                serializer = ValueTypeMap.serialize[attr.value_type]
+                show_value = {i.ci_id: serializer(i.value) for i in value_table.get_by(attr_id=attr.id, to_dict=False)}
+            else:
+                show_value = unique_value
+
+            return show_value, unique_value
 
         self.level = int(self.level)
 
@@ -365,7 +377,8 @@ class Search(object):
             item = dict(id=int(i),
                         type_id=type_ids[0],
                         isLeaf=False,
-                        title=id2name.get(int(i)),
+                        title=id2name[0].get(int(i)),
+                        uniqueValue=id2name[1].get(int(i)),
                         children=[])
             result.append(item)
             id2children[str(i)] = item['children']
@@ -381,25 +394,26 @@ class Search(object):
                 key, prefix = [i for i in level_ids], REDIS_PREFIX_CI_RELATION2
             else:
                 key, prefix = [i.split(',')[-1] for i in level_ids], REDIS_PREFIX_CI_RELATION
-
             res = [json.loads(x).items() for x in [i or '{}' for i in rd.get(key, prefix) or []]]
             res = [[i for i in x if (not id_filter_limit or (key[idx] not in id_filter_limit or
                                                              int(i[0]) in id_filter_limit[key[idx]]) or
                                      int(i[0]) in id_filter_limit)] for idx, x in enumerate(res)]
-
             _level_ids = []
             type_id = type_ids[lv]
             id2name = _get_id2name(type_id)
-            for idx, _id in enumerate(level_ids):
+            for idx, node_path in enumerate(level_ids):
                 for child_id, _ in (res[idx] or []):
                     item = dict(id=int(child_id),
                                 type_id=type_id,
                                 isLeaf=True if lv == self.level - 1 else False,
-                                title=id2name.get(int(child_id)),
+                                title=id2name[0].get(int(child_id)),
+                                uniqueValue=id2name[1].get(int(child_id)),
                                 children=[])
-                    id2children[_id.split(',')[-1]].append(item)
-                    id2children[child_id] = item['children']
-                    _level_ids.append("{},{}".format(_id, child_id))
+                    id2children[node_path].append(item)
+
+                    _node_path = "{},{}".format(node_path, child_id)
+                    _level_ids.append(_node_path)
+                    id2children[_node_path] = item['children']
 
             level_ids = _level_ids
 
diff --git a/cmdb-api/api/models/cmdb.py b/cmdb-api/api/models/cmdb.py
index 193c94b..4ae31a9 100644
--- a/cmdb-api/api/models/cmdb.py
+++ b/cmdb-api/api/models/cmdb.py
@@ -46,13 +46,17 @@ class CIType(Model):
     name = db.Column(db.String(32), nullable=False)
     alias = db.Column(db.String(32), nullable=False)
     unique_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"), nullable=False)
+    show_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
     enabled = db.Column(db.Boolean, default=True, nullable=False)
     is_attached = db.Column(db.Boolean, default=False, nullable=False)
     icon = db.Column(db.Text)
     order = db.Column(db.SmallInteger, default=0, nullable=False)
     default_order_attr = db.Column(db.String(33))
 
-    unique_key = db.relationship("Attribute", backref="c_ci_types.unique_id")
+    unique_key = db.relationship("Attribute", backref="c_ci_types.unique_id",
+                                 primaryjoin="Attribute.id==CIType.unique_id", foreign_keys=[unique_id])
+    show_key = db.relationship("Attribute", backref="c_ci_types.show_id",
+                               primaryjoin="Attribute.id==CIType.show_id", foreign_keys=[show_id])
 
     uid = db.Column(db.Integer, index=True)