From 356cf581353c149d75710a5e04ae30d0b4f0bdd3 Mon Sep 17 00:00:00 2001 From: pycook Date: Sun, 19 Jan 2020 17:59:32 +0800 Subject: [PATCH 1/6] /ci_types//attributes/transfer and /ci_types//attribute_groups/transfer --- .gitignore | 1 + cmdb-api/api/lib/cmdb/ci_type.py | 132 ++++++++++++++++++++++++++++- cmdb-api/api/tasks/cmdb.py | 21 +++++ cmdb-api/api/views/cmdb/ci_type.py | 28 ++++++ 4 files changed, 180 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 01ccfa2..a53b01e 100755 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ pip-log.txt .tox nosetests.xml .pytest_cache +cmdb-api/test-output # Translations *.mo diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index 8c7934d..bf936d4 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -23,6 +23,7 @@ from api.models.cmdb import CITypeGroupItem from api.models.cmdb import CITypeRelation from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceTreeView +from api.tasks.cmdb import ci_type_attribute_order_rebuild class CITypeManager(object): @@ -55,16 +56,32 @@ class CITypeManager(object): ci_type = CITypeCache.get(_type) or abort(404, "CIType <{0}> is not found".format(_type)) return ci_type.to_dict() + @staticmethod + def _validate_unique(type_id=None, name=None, alias=None): + if name is not None: + ci_type = CIType.get_by(name=name, first=True, to_dict=False) + elif alias is not None: + ci_type = CIType.get_by(alias=alias, first=True, to_dict=False) + else: + return + + if type_id is not None and ci_type.id != type_id: + return abort(400, "CIType <{0}> is already existed".format(name or alias)) + + if type_id is None and ci_type is not None: + return abort(400, "CIType <{0}> is already existed".format(name or alias)) + @classmethod @kwargs_required("name") def add(cls, **kwargs): unique_key = kwargs.pop("unique_key", None) unique_key = AttributeCache.get(unique_key) or abort(404, "Unique key is not defined") - CIType.get_by(name=kwargs['name']) and abort(404, "CIType <{0}> is already existed".format(kwargs.get("name"))) - kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"] + cls._validate_unique(name=kwargs['name']) + cls._validate_unique(alias=kwargs['alias']) + kwargs["unique_id"] = unique_key.id ci_type = CIType.create(**kwargs) @@ -88,6 +105,9 @@ class CITypeManager(object): ci_type = cls.check_is_existed(type_id) + cls._validate_unique(type_id=type_id, name=kwargs.get('name')) + cls._validate_unique(type_id=type_id, alias=kwargs.get('alias')) + unique_key = kwargs.pop("unique_key", None) unique_key = AttributeCache.get(unique_key) if unique_key is not None: @@ -305,6 +325,31 @@ class CITypeAttributeManager(object): CITypeAttributesCache.clean(type_id) + @classmethod + def transfer(cls, type_id, _from, _to): + current_app.logger.info("[{0}] {1} -> {2}".format(type_id, _from, _to)) + attr_id = _from.get('attr_id') + from_group_id = _from.get('group_id') + to_group_id = _to.get('group_id') + order = _to.get('order') + + if from_group_id != to_group_id: + if from_group_id is not None: + CITypeAttributeGroupManager.delete_item(from_group_id, attr_id) + + if to_group_id is not None: + CITypeAttributeGroupManager.add_item(to_group_id, attr_id, order) + + elif from_group_id: + CITypeAttributeGroupManager.update_item(from_group_id, attr_id, order) + + else: # other attribute transfer + return abort(400, "invalid operation!!!") + + CITypeAttributesCache.clean(type_id) + + ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE) + class CITypeRelationManager(object): """ @@ -455,3 +500,86 @@ class CITypeAttributeGroupManager(object): item.soft_delete() return group_id + + @classmethod + def add_item(cls, group_id, attr_id, order): + db.session.remove() + + existed = CITypeAttributeGroupItem.get_by(group_id=group_id, + attr_id=attr_id, + first=True, + to_dict=False) + if existed is not None: + existed.update(order=order) + else: + CITypeAttributeGroupItem.create(group_id=group_id, attr_id=attr_id, order=order) + + gt_items = db.session.query(CITypeAttributeGroupItem).filter( + CITypeAttributeGroupItem.deleted.is_(False)).filter(CITypeAttributeGroupItem.order > order) + for _item in gt_items: + _order = _item.order + _item.update(order=_order + 1) + + @classmethod + def update_item(cls, group_id, attr_id, order): + db.session.remove() + + existed = CITypeAttributeGroupItem.get_by(group_id=group_id, + attr_id=attr_id, + first=True, + to_dict=False) + existed or abort(404, "Group<{0}> - Attribute<{1}> is not found".format(group_id, attr_id)) + + if existed.order > order: # forward, +1 + items = db.session.query(CITypeAttributeGroupItem).filter( + CITypeAttributeGroupItem.deleted.is_(False)).filter( + CITypeAttributeGroupItem.order >= order).filter( + CITypeAttributeGroupItem.order < existed.order) + for item in items: + item.update(order=item.order + 1) + + elif existed.order < order: # backward, -1 + items = db.session.query(CITypeAttributeGroupItem).filter( + CITypeAttributeGroupItem.deleted.is_(False)).filter( + CITypeAttributeGroupItem.order > existed.order).filter( + CITypeAttributeGroupItem.order <= order) + for item in items: + item.update(order=item.order - 1) + + existed.update(order=order) + + @classmethod + def delete_item(cls, group_id, attr_id): + db.session.remove() + + item = CITypeAttributeGroupItem.get_by(group_id=group_id, + attr_id=attr_id, + first=True, + to_dict=False) + + if item is not None: + item.soft_delete() + order = item.order + gt_items = db.session.query(CITypeAttributeGroupItem).filter( + CITypeAttributeGroupItem.deleted.is_(False)).filter(CITypeAttributeGroupItem.order > order) + for _item in gt_items: + _order = _item.order + _item.update(order=_order - 1) + + @classmethod + def transfer(cls, type_id, _from, _to): + current_app.logger.info("CIType[{0}] {1} -> {2}".format(type_id, _from, _to)) + from_group = CITypeAttributeGroup.get_by_id(_from) + from_group or abort(404, "Group <{0}> is not found".format(_from)) + + to_group = CITypeAttributeGroup.get_by_id(_to) + to_group or abort(404, "Group <{0}> is not found".format(_to)) + + from_order, to_order = from_group.order, to_group.order + + from_group.update(order=to_order) + to_group.update(order=from_order) + + CITypeAttributesCache.clean(type_id) + + ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE) diff --git a/cmdb-api/api/tasks/cmdb.py b/cmdb-api/api/tasks/cmdb.py index 36853eb..7560b5e 100644 --- a/cmdb-api/api/tasks/cmdb.py +++ b/cmdb-api/api/tasks/cmdb.py @@ -11,6 +11,7 @@ from api.extensions import celery from api.extensions import db from api.extensions import es from api.extensions import rd +from api.lib.cmdb.cache import CITypeAttributeCache from api.lib.cmdb.const import CMDB_QUEUE from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION @@ -71,3 +72,23 @@ def ci_relation_delete(parent_id, child_id): rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id)) + + +@celery.task(name="cmdb.ci_type_attribute_order_rebuild", queue=CMDB_QUEUE) +def ci_type_attribute_order_rebuild(type_id): + current_app.logger.info('rebuild attribute order') + db.session.remove() + + from api.lib.cmdb.ci_type import CITypeAttributeGroupManager + + attrs = CITypeAttributeCache.get(type_id) + id2attr = {attr.attr_id: attr for attr in attrs} + + res = CITypeAttributeGroupManager.get_by_type_id(type_id, True) + order = 0 + for group in res: + for _attr in group.get('attributes'): + if order != id2attr.get(_attr['id']) and id2attr.get(_attr['id']): + id2attr.get(_attr['id']).update(order=order) + + order += 1 diff --git a/cmdb-api/api/views/cmdb/ci_type.py b/cmdb-api/api/views/cmdb/ci_type.py index 554655d..50fc386 100644 --- a/cmdb-api/api/views/cmdb/ci_type.py +++ b/cmdb-api/api/views/cmdb/ci_type.py @@ -164,6 +164,34 @@ class CITypeAttributeView(APIView): return self.jsonify(attributes=attr_id_list) +class CITypeAttributeTransferView(APIView): + url_prefix = "/ci_types//attributes/transfer" + + @args_required('from') + @args_required('to') + def post(self, type_id): + _from = request.values.get('from') # {'attr_id': xx, 'group_id': xx} + _to = request.values.get('to') # {'group_id': xx, 'order': xxx} + + CITypeAttributeManager.transfer(type_id, _from, _to) + + return self.jsonify(code=200) + + +class CITypeAttributeGroupTransferView(APIView): + url_prefix = "/ci_types//attribute_groups/transfer" + + @args_required('from') + @args_required('to') + def post(self, type_id): + _from = request.values.get('from') # group_id + _to = request.values.get('to') # group_id + + CITypeAttributeGroupManager.transfer(type_id, _from, _to) + + return self.jsonify(code=200) + + class CITypeAttributeGroupView(APIView): url_prefix = ("/ci_types//attribute_groups", "/ci_types/attribute_groups/") From 00f4eeb90d9d663dda304435eeb6bbd1c393ef10 Mon Sep 17 00:00:00 2001 From: penzai Date: Tue, 4 Feb 2020 10:53:07 +0800 Subject: [PATCH 2/6] fix: update attribute group without name params will fail. #tests/test_cmdb_ci_type.py::test_update_attribute_group_ci_type --- cmdb-api/api/lib/cmdb/ci_type.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index 8c7934d..500cd67 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -441,6 +441,8 @@ class CITypeAttributeGroupManager(object): return abort(400, "Group <{0}> duplicate".format(name)) if name is not None: group.update(name=name) + else: + name = group.name cls.create_or_update(group.type_id, name, attr_order, group_order) From 362122952bb950b9f44b662a09a77b15222839d3 Mon Sep 17 00:00:00 2001 From: penzai Date: Tue, 4 Feb 2020 10:54:16 +0800 Subject: [PATCH 3/6] test: add ci_type test cases --- cmdb-api/tests/conftest.py | 4 +- cmdb-api/tests/sample.py | 49 ++++++++ cmdb-api/tests/test_cmdb_attribute.py | 35 +++++- cmdb-api/tests/test_cmdb_ci.py | 7 -- cmdb-api/tests/test_cmdb_ci_type.py | 166 ++++++++++++++++++++++++++ 5 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 cmdb-api/tests/sample.py diff --git a/cmdb-api/tests/conftest.py b/cmdb-api/tests/conftest.py index fee9d8f..d2f0c04 100644 --- a/cmdb-api/tests/conftest.py +++ b/cmdb-api/tests/conftest.py @@ -21,7 +21,7 @@ class CMDBTestClient(FlaskClient): headers.setdefault("User-Agent", "py.test") kwargs["headers"] = headers - json_data = kwargs.pop("json") + json_data = kwargs.pop("json", None) if json_data is not None: kwargs["data"] = json.dumps(json_data) if not kwargs.get("content_type"): @@ -49,6 +49,7 @@ def app(): _app.config['SECRET_KEY'] = CMDBTestClient.TEST_APP_SECRET _app.test_client_class = CMDBTestClient _app.response_class = CMDBTestResponse + ctx = _app.test_request_context() ctx.push() yield _app @@ -99,7 +100,6 @@ def clean_db(): db.session.commit() if not User.get_by(email="test@xx.com"): - print("hello world xxxxx") u = User.create( flush=True, username="test", diff --git a/cmdb-api/tests/sample.py b/cmdb-api/tests/sample.py new file mode 100644 index 0000000..3bdc563 --- /dev/null +++ b/cmdb-api/tests/sample.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +"""provide some sample data in database""" +import uuid +import random + +from api.models.cmdb import Attribute, CIType, CITypeAttributeGroup, CITypeAttribute + + +def init_attributes(num=1): + attrs = [] + for i in range(num): + attrs.append(Attribute.create( + name=uuid.uuid4().hex[:8], + alias=uuid.uuid4().hex[:8], + value_type=str(random.randint(0, 100) % 7) + )) + return attrs + + +def init_ci_types(num=1): + attrs = init_attributes(num) + + ci_types = [] + for i in range(num): + ci_type = CIType.create( + name=uuid.uuid4().hex[:8], + alias=uuid.uuid4().hex[:8], + unique_id=attrs[i].id + ) + CITypeAttribute.create( + type_id=ci_type.id, + attr_id=attrs[i].id, + ) + ci_types.append(ci_type) + + return ci_types + + +def init_attribute_groups(num=1): + ci_types = init_ci_types(num) + + ags = [] + for i in range(num): + ags.append(CITypeAttributeGroup.create( + name=uuid.uuid4().hex[:8], + type_id=ci_types[i].id, + order=i + )) + return ags diff --git a/cmdb-api/tests/test_cmdb_attribute.py b/cmdb-api/tests/test_cmdb_attribute.py index fc754d9..eac93e0 100644 --- a/cmdb-api/tests/test_cmdb_attribute.py +++ b/cmdb-api/tests/test_cmdb_attribute.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from api.models.cmdb import Attribute +from tests.sample import init_attributes + def test_create_attribute(session, client): url = "/api/v0.1/attributes" @@ -16,7 +18,7 @@ def test_create_attribute(session, client): assert resp.status_code == 200 assert resp.json["attr_id"] - # check there is a ci_types in database + # check there is a attribute in database attr_id = resp.json["attr_id"] attr_ins = Attribute.get_by_id(attr_id) assert attr_ins.id == attr_id @@ -24,3 +26,34 @@ def test_create_attribute(session, client): assert attr_ins.alias == "区域" +def test_update_attribute(session, client): + attr_ins = init_attributes(1)[0] + + url = "/api/v0.1/attributes/" + str(attr_ins.id) + payload = { + "name": "update", + } + + resp = client.put(url, json=payload) + + # check resp status code and content + assert resp.status_code == 200 + assert resp.json["attr_id"] == attr_ins.id + + # check attribute updated in database + attr_ins = Attribute.get_by_id(attr_ins.id) + assert attr_ins.name == "update" + + +def test_delete_attribute(session, client): + attr_ins = init_attributes(1)[0] + url = "/api/v0.1/attributes/" + str(attr_ins.id) + + resp = client.delete(url) + + assert resp.status_code == 200 + # attr should be soft delete + attr_ins = Attribute.get_by_id(attr_ins.id) + assert attr_ins.deleted is True + assert attr_ins.deleted_at + diff --git a/cmdb-api/tests/test_cmdb_ci.py b/cmdb-api/tests/test_cmdb_ci.py index 78a93f1..faaaf79 100644 --- a/cmdb-api/tests/test_cmdb_ci.py +++ b/cmdb-api/tests/test_cmdb_ci.py @@ -1,10 +1,3 @@ # -*- coding: utf-8 -*- -class TestCI: - - def test_ci_search_only_type_query(self, app): - with app.test_client() as c: - rv = c.get('/api/v0.1/ci/s?q=_type:server', json={}) - json_data = rv.get_json() - assert type(json_data.get("result")) is list diff --git a/cmdb-api/tests/test_cmdb_ci_type.py b/cmdb-api/tests/test_cmdb_ci_type.py index 40a96af..fb7c23b 100644 --- a/cmdb-api/tests/test_cmdb_ci_type.py +++ b/cmdb-api/tests/test_cmdb_ci_type.py @@ -1 +1,167 @@ # -*- coding: utf-8 -*- +from api.models.cmdb import ( + CIType, CITypeAttribute, + Attribute, CITypeAttributeGroup, + CITypeAttributeGroupItem) + +from tests.sample import ( + init_attributes, init_ci_types, + init_attribute_groups) + + +def test_create_ci_type(session, client): + attr = init_attributes(1)[0] + + url = "/api/v0.1/ci_types" + payload = { + "name": "test", + "alias": "测试", + "unique_key": attr.id + } + + resp = client.post(url, json=payload) + + # check resp status code and content + assert resp.status_code == 200 + assert resp.json["type_id"] + + # check there is a attribute in database + type_id = resp.json["type_id"] + ci_type_ins = CIType.get_by_id(type_id) + assert ci_type_ins.id == type_id + assert ci_type_ins.name == "test" + assert ci_type_ins.alias == "测试" + assert ci_type_ins.unique_id == attr.id + + +def test_update_ci_type(session, client): + ci_type_ins = init_ci_types(1)[0] + + url = "/api/v0.1/ci_types/" + str(ci_type_ins.id) + payload = { + "name": "update", + } + + resp = client.put(url, json=payload) + + # check resp status code and content + assert resp.status_code == 200 + assert resp.json["type_id"] == ci_type_ins.id + + # check ci_type updated in database + ci_type_ins = CIType.get_by_id(ci_type_ins.id) + assert ci_type_ins.name == "update" + + +def test_delete_ci_type(session, client): + ci_type_ins = init_ci_types(1)[0] + url = "/api/v0.1/ci_types/" + str(ci_type_ins.id) + + resp = client.delete(url) + + assert resp.status_code == 200 + # attr should be soft delete + ci_type_ins = CIType.get_by_id(ci_type_ins.id) + assert ci_type_ins.deleted is True + assert ci_type_ins.deleted_at + + +def test_bind_attributes_ci_type(session, client): + attrs = init_attributes(3) + ci_type = init_ci_types(1)[0] + + url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.id) + payload = { + "attr_id": [str(x.id) for x in attrs] + } + + resp = client.post(url, json=payload) + + # check resp status code and content + assert resp.status_code == 200 + assert len(resp.json["attributes"]) == len(attrs) + + # check ci_type has 4 attributes + ci_type_attribute_ids = [x.attr_id for x in CITypeAttribute.query.filter_by(type_id=ci_type.id).all()] + for attr in attrs: + assert attr.id in ci_type_attribute_ids + + +def test_get_attributes_ci_type(session, client): + ci_type = init_ci_types(1)[0] + url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.name) + + resp = client.get(url) + + assert resp.status_code == 200 + assert len(resp.json["attributes"]) == 1 + + +def test_update_attributes_ci_type(session, client): + ci_type = init_ci_types(1)[0] + attr = Attribute.query.first() + url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.id) + + payload = { + "attributes": [ + {"attr_id": attr.id, "default_show": False, "is_required": True} + ] + } + resp = client.put(url, json=payload) + assert resp.status_code == 200 + + ci_type_attr_ins = CITypeAttribute.query.filter_by(type_id=ci_type.id).first() + assert ci_type_attr_ins + assert ci_type_attr_ins.is_required is True + assert ci_type_attr_ins.default_show is False + + +def test_create_attribute_group_ci_type(session, client): + ci_type = init_ci_types(1)[0] + + url = "/api/v0.1/ci_types/{}/attribute_groups".format(ci_type.id) + payload = { + "name": "A", + "order": 100, + } + + resp = client.post(url, json=payload) + + # check resp status code and content + assert resp.status_code == 200 + assert resp.json["group_id"] + + ins = CITypeAttributeGroup.query.filter_by(type_id=ci_type.id).first() + assert ins + assert ins.id == resp.json["group_id"] + assert ins.name == "A" + assert ins.order == 100 + + +def test_update_attribute_group_ci_type(session, client): + attribute_groups = init_attribute_groups(1)[0] + + url = "/api/v0.1/ci_types/attribute_groups/{}".format(attribute_groups.id) + payload = { + "attributes": [x.id for x in Attribute.query.all()] + } + + resp = client.put(url, json=payload) + assert resp.status_code == 200 + assert resp.json["group_id"] + + ag_items = CITypeAttributeGroupItem.query.filter_by(group_id=attribute_groups.id).all() + for a in Attribute.query.all(): + assert a.id in [x.attr_id for x in ag_items] + + +def test_delete_attribute_group_ci_type(session, client): + attribute_groups = init_attribute_groups(1)[0] + + url = "/api/v0.1/ci_types/attribute_groups/{}".format(attribute_groups.id) + resp = client.delete(url) + + assert resp.status_code == 200 + attribute_group_ins = CITypeAttributeGroup.query.filter_by(id=attribute_groups.id).first() + assert attribute_group_ins.deleted is True + assert attribute_group_ins.deleted_at From 87deba2e460e1b7ce19261c865340db543815e7e Mon Sep 17 00:00:00 2001 From: pycook Date: Thu, 6 Feb 2020 09:59:24 +0800 Subject: [PATCH 4/6] fix jwt decode --- cmdb-api/api/lib/cmdb/ci_type.py | 7 ++++++- cmdb-api/api/lib/database.py | 3 +++ cmdb-api/api/lib/perm/auth.py | 2 +- cmdb-api/api/models/cmdb.py | 16 ++++++++-------- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index bc50a7f..6a6885a 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -40,7 +40,9 @@ class CITypeManager(object): @staticmethod def check_is_existed(key): - return CITypeCache.get(key) or abort(404, "CIType <{0}> is not existed".format(key)) + ci_type = CITypeCache.get(key) or abort(404, "CIType <{0}> is not existed".format(key)) + + return CIType.get_by_id(ci_type.id) @staticmethod def get_ci_types(type_name=None): @@ -65,6 +67,9 @@ class CITypeManager(object): else: return + if not ci_type: + return + if type_id is not None and ci_type.id != type_id: return abort(400, "CIType <{0}> is already existed".format(name or alias)) diff --git a/cmdb-api/api/lib/database.py b/cmdb-api/api/lib/database.py index af89b6f..71a94fc 100644 --- a/cmdb-api/api/lib/database.py +++ b/cmdb-api/api/lib/database.py @@ -25,6 +25,9 @@ class FormatMixin(object): class CRUDMixin(FormatMixin): + def __init__(self, **kwargs): + super(CRUDMixin, self).__init__(**kwargs) + @classmethod def create(cls, flush=False, **kwargs): return cls(**kwargs).save(flush=flush) diff --git a/cmdb-api/api/lib/perm/auth.py b/cmdb-api/api/lib/perm/auth.py index 22d82d8..5c5d25f 100644 --- a/cmdb-api/api/lib/perm/auth.py +++ b/cmdb-api/api/lib/perm/auth.py @@ -47,7 +47,7 @@ def _auth_with_token(): try: token = auth_headers - data = jwt.decode(token, current_app.config['SECRET_KEY']) + data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256']) user = User.query.filter_by(email=data['sub']).first() if not user: return False diff --git a/cmdb-api/api/models/cmdb.py b/cmdb-api/api/models/cmdb.py index 31a22dc..0c0a953 100644 --- a/cmdb-api/api/models/cmdb.py +++ b/cmdb-api/api/models/cmdb.py @@ -15,13 +15,13 @@ from api.lib.database import Model class RelationType(Model): __tablename__ = "c_relation_types" - name = db.Column(db.String(16), index=True) + name = db.Column(db.String(16), index=True, nullable=False) class CITypeGroup(Model): __tablename__ = "c_ci_type_groups" - name = db.Column(db.String(32)) + name = db.Column(db.String(32), nullable=False) class CITypeGroupItem(Model): @@ -35,12 +35,12 @@ class CITypeGroupItem(Model): class CIType(Model): __tablename__ = "c_ci_types" - name = db.Column(db.String(32)) - alias = db.Column(db.String(32)) + 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) enabled = db.Column(db.Boolean, default=True, nullable=False) is_attached = db.Column(db.Boolean, default=False, nullable=False) - icon_url = db.Column(db.String(256)) + icon_url = db.Column(db.String(256), default='', nullable=False) order = db.Column(db.SmallInteger, default=0, nullable=False) unique_key = db.relationship("Attribute", backref="c_ci_types.unique_id") @@ -89,7 +89,7 @@ class CITypeAttribute(Model): class CITypeAttributeGroup(Model): __tablename__ = "c_ci_type_attribute_groups" - name = db.Column(db.String(64)) + name = db.Column(db.String(64), nullable=False) type_id = db.Column(db.Integer, db.ForeignKey("c_ci_types.id"), nullable=False) order = db.Column(db.SmallInteger, default=0) @@ -266,8 +266,8 @@ class OperationRecord(Model): __tablename__ = "c_records" uid = db.Column(db.Integer, index=True, nullable=False) - origin = db.Column(db.String(32)) - ticket_id = db.Column(db.String(32)) + origin = db.Column(db.String(32), nullable=False) + ticket_id = db.Column(db.String(32), nullable=False) reason = db.Column(db.Text) From 780c51c36402386ea5d2c03576409f9a45071f14 Mon Sep 17 00:00:00 2001 From: pycook Date: Sat, 8 Feb 2020 17:36:54 +0800 Subject: [PATCH 5/6] Define display fields --- cmdb-ui/src/views/cmdb/ci/index.vue | 258 ++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 75 deletions(-) diff --git a/cmdb-ui/src/views/cmdb/ci/index.vue b/cmdb-ui/src/views/cmdb/ci/index.vue index 82aa7e1..4c51396 100644 --- a/cmdb-ui/src/views/cmdb/ci/index.vue +++ b/cmdb-ui/src/views/cmdb/ci/index.vue @@ -1,78 +1,126 @@