mirror of https://github.com/veops/cmdb.git
[fix] validate attribute is required
This commit is contained in:
parent
03bc27087d
commit
4e7e853173
|
@ -107,13 +107,13 @@ class RelationTypeCache(object):
|
|||
cache.delete(cls.PREFIX_ID.format(ct.id))
|
||||
|
||||
|
||||
class CITypeAttributeCache(object):
|
||||
class CITypeAttributesCache(object):
|
||||
"""
|
||||
key is type_id or type_name
|
||||
"""
|
||||
|
||||
PREFIX_ID = "CITypeAttribute::ID::{0}"
|
||||
PREFIX_NAME = "CITypeAttribute::Name::{0}"
|
||||
PREFIX_ID = "CITypeAttributes::TypeID::{0}"
|
||||
PREFIX_NAME = "CITypeAttributes::TypeName::{0}"
|
||||
|
||||
@classmethod
|
||||
def get(cls, key):
|
||||
|
@ -146,3 +146,30 @@ class CITypeAttributeCache(object):
|
|||
if attrs is not None and ci_type:
|
||||
cache.delete(cls.PREFIX_ID.format(ci_type.id))
|
||||
cache.delete(cls.PREFIX_NAME.format(ci_type.name))
|
||||
|
||||
|
||||
class CITypeAttributeCache(object):
|
||||
"""
|
||||
key is type_id & attr_id
|
||||
"""
|
||||
|
||||
PREFIX_ID = "CITypeAttribute::TypeID::{0}::AttrID::{1}"
|
||||
|
||||
@classmethod
|
||||
def get(cls, type_id, attr_id):
|
||||
|
||||
attr = cache.get(cls.PREFIX_ID.format(type_id, attr_id))
|
||||
attr = attr or cache.get(cls.PREFIX_ID.format(type_id, attr_id))
|
||||
if not attr:
|
||||
attr = CITypeAttribute.get_by(type_id=type_id, attr_id=attr_id, first=True, to_dict=False)
|
||||
if attr is not None:
|
||||
cls.set(type_id, attr_id, attr)
|
||||
return attr
|
||||
|
||||
@classmethod
|
||||
def set(cls, type_id, attr_id, attr):
|
||||
cache.set(cls.PREFIX_ID.format(type_id, attr_id), attr)
|
||||
|
||||
@classmethod
|
||||
def clean(cls, type_id, attr_id):
|
||||
cache.delete(cls.PREFIX_ID.format(type_id, attr_id))
|
||||
|
|
|
@ -190,7 +190,7 @@ class CIManager(object):
|
|||
value_manager = AttributeValueManager()
|
||||
for p, v in ci_dict.items():
|
||||
try:
|
||||
value_manager.create_or_update_attr_value(p, v, ci.id, _no_attribute_policy)
|
||||
value_manager.create_or_update_attr_value(p, v, ci, _no_attribute_policy)
|
||||
except BadRequest as e:
|
||||
if existed is None:
|
||||
cls.delete(ci.id)
|
||||
|
@ -201,11 +201,12 @@ class CIManager(object):
|
|||
return ci.id
|
||||
|
||||
def update(self, ci_id, **ci_dict):
|
||||
self.confirm_ci_existed(ci_id)
|
||||
ci = self.confirm_ci_existed(ci_id)
|
||||
|
||||
value_manager = AttributeValueManager()
|
||||
for p, v in ci_dict.items():
|
||||
try:
|
||||
value_manager.create_or_update_attr_value(p, v, ci_id)
|
||||
value_manager.create_or_update_attr_value(p, v, ci)
|
||||
except BadRequest as e:
|
||||
raise e
|
||||
|
||||
|
@ -213,9 +214,9 @@ class CIManager(object):
|
|||
|
||||
@staticmethod
|
||||
def update_unique_value(ci_id, unique_name, unique_value):
|
||||
CI.get_by_id(ci_id) or abort(404, "CI <{0}> is not found".format(ci_id))
|
||||
ci = CI.get_by_id(ci_id) or abort(404, "CI <{0}> is not found".format(ci_id))
|
||||
|
||||
AttributeValueManager().create_or_update_attr_value(unique_name, unique_value, ci_id)
|
||||
AttributeValueManager().create_or_update_attr_value(unique_name, unique_value, ci)
|
||||
|
||||
ci_cache.apply_async([ci_id], queue=CMDB_QUEUE)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from api.extensions import db
|
|||
from api.lib.cmdb.attribute import AttributeManager
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributesCache
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
from api.lib.decorator import kwargs_required
|
||||
from api.models.cmdb import CI
|
||||
|
@ -18,8 +19,8 @@ from api.models.cmdb import CITypeAttributeGroupItem
|
|||
from api.models.cmdb import CITypeGroup
|
||||
from api.models.cmdb import CITypeGroupItem
|
||||
from api.models.cmdb import CITypeRelation
|
||||
from api.models.cmdb import PreferenceTreeView
|
||||
from api.models.cmdb import PreferenceShowAttributes
|
||||
from api.models.cmdb import PreferenceTreeView
|
||||
|
||||
|
||||
class CITypeManager(object):
|
||||
|
@ -201,11 +202,11 @@ class CITypeAttributeManager(object):
|
|||
|
||||
@staticmethod
|
||||
def get_attr_names_by_type_id(type_id):
|
||||
return [AttributeCache.get(attr.attr_id).name for attr in CITypeAttributeCache.get(type_id)]
|
||||
return [AttributeCache.get(attr.attr_id).name for attr in CITypeAttributesCache.get(type_id)]
|
||||
|
||||
@staticmethod
|
||||
def get_attributes_by_type_id(type_id):
|
||||
attrs = CITypeAttributeCache.get(type_id)
|
||||
attrs = CITypeAttributesCache.get(type_id)
|
||||
result = list()
|
||||
for attr in sorted(attrs, key=lambda x: (x.order, x.id)):
|
||||
attr_dict = AttributeManager().get_attribute(attr.attr_id)
|
||||
|
@ -247,7 +248,7 @@ class CITypeAttributeManager(object):
|
|||
current_app.logger.debug(attr_id)
|
||||
CITypeAttribute.create(type_id=type_id, attr_id=attr_id, **kwargs)
|
||||
|
||||
CITypeAttributeCache.clean(type_id)
|
||||
CITypeAttributesCache.clean(type_id)
|
||||
|
||||
@classmethod
|
||||
def update(cls, type_id, attributes):
|
||||
|
@ -269,7 +270,9 @@ class CITypeAttributeManager(object):
|
|||
|
||||
existed.update(**attr)
|
||||
|
||||
CITypeAttributeCache.clean(type_id)
|
||||
CITypeAttributeCache.clean(type_id, existed.attr_id)
|
||||
|
||||
CITypeAttributesCache.clean(type_id)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, type_id, attr_ids=None):
|
||||
|
@ -289,7 +292,9 @@ class CITypeAttributeManager(object):
|
|||
if existed is not None:
|
||||
existed.soft_delete()
|
||||
|
||||
CITypeAttributeCache.clean(type_id)
|
||||
CITypeAttributeCache.clean(type_id, attr_id)
|
||||
|
||||
CITypeAttributesCache.clean(type_id)
|
||||
|
||||
|
||||
class CITypeRelationManager(object):
|
||||
|
|
|
@ -13,7 +13,7 @@ from flask import g
|
|||
from api.extensions import db
|
||||
from api.lib.cmdb.attribute import AttributeManager
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributesCache
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
from api.lib.cmdb.const import ResourceTypeEnum, RoleEnum, PermEnum
|
||||
from api.lib.exception import AbortException
|
||||
|
@ -100,7 +100,7 @@ class PreferenceManager(object):
|
|||
|
||||
@staticmethod
|
||||
def create_or_update_tree_view(type_id, levels):
|
||||
attrs = CITypeAttributeCache.get(type_id)
|
||||
attrs = CITypeAttributesCache.get(type_id)
|
||||
for idx, i in enumerate(levels):
|
||||
for attr in attrs:
|
||||
attr = AttributeCache.get(attr.attr_id)
|
||||
|
|
|
@ -34,7 +34,7 @@ class ValueTypeMap(object):
|
|||
ValueTypeEnum.TIME: lambda x: escape(x).encode('utf-8').decode('utf-8'),
|
||||
ValueTypeEnum.DATETIME: str2datetime,
|
||||
ValueTypeEnum.DATE: str2datetime,
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
|
||||
}
|
||||
|
||||
serialize = {
|
||||
|
@ -44,7 +44,7 @@ class ValueTypeMap(object):
|
|||
ValueTypeEnum.TIME: lambda x: x if isinstance(x, six.string_types) else str(x),
|
||||
ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d"),
|
||||
ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
|
||||
}
|
||||
|
||||
serialize2 = {
|
||||
|
@ -54,7 +54,7 @@ class ValueTypeMap(object):
|
|||
ValueTypeEnum.TIME: lambda x: x.decode() if not isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.DATE: lambda x: x.decode() if not isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.DATETIME: lambda x: x.decode() if not isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) else x,
|
||||
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
|
||||
}
|
||||
|
||||
choice = {
|
||||
|
|
|
@ -8,6 +8,7 @@ from flask import abort
|
|||
from api.extensions import db
|
||||
from api.lib.cmdb.attribute import AttributeManager
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||
from api.lib.cmdb.const import ExistPolicy
|
||||
from api.lib.cmdb.const import OperateType
|
||||
from api.lib.cmdb.history import AttributeHistoryManger
|
||||
|
@ -77,23 +78,31 @@ class AttributeValueManager(object):
|
|||
return abort(400, "attribute value <{0}> is invalid".format(value))
|
||||
|
||||
@staticmethod
|
||||
def __check_is_choice(attr_id, value_type, value):
|
||||
choice_values = AttributeManager.get_choice_values(attr_id, value_type)
|
||||
def __check_is_choice(attr, value_type, value):
|
||||
choice_values = AttributeManager.get_choice_values(attr.id, value_type)
|
||||
if value not in choice_values:
|
||||
return abort(400, "{0} does not existed in choice values".format(value))
|
||||
|
||||
@staticmethod
|
||||
def __check_is_unique(value_table, attr_id, ci_id, value):
|
||||
def __check_is_unique(value_table, attr, ci_id, value):
|
||||
existed = db.session.query(value_table.attr_id).filter(
|
||||
value_table.attr_id == attr_id).filter(value_table.deleted.is_(False)).filter(
|
||||
value_table.attr_id == attr.id).filter(value_table.deleted.is_(False)).filter(
|
||||
value_table.value == value).filter(value_table.ci_id != ci_id).first()
|
||||
existed and abort(400, "attribute <{0}> value {1} must be unique".format(attr_id, value))
|
||||
existed and abort(400, "attribute <{0}> value {1} must be unique".format(attr.alias, value))
|
||||
|
||||
def _validate(self, attr, value, value_table, ci_id):
|
||||
@staticmethod
|
||||
def __check_is_required(type_id, attr, value):
|
||||
type_attr = CITypeAttributeCache.get(type_id, attr.id)
|
||||
if type_attr and type_attr.is_required and not value and value != 0:
|
||||
return abort(400, "attribute <{0}> value is required".format(attr.alias))
|
||||
|
||||
def _validate(self, attr, value, value_table, ci):
|
||||
v = self.__deserialize_value(attr.value_type, value)
|
||||
|
||||
attr.is_choice and value and self.__check_is_choice(attr.id, attr.value_type, v)
|
||||
attr.is_unique and self.__check_is_unique(value_table, attr.id, ci_id, v)
|
||||
attr.is_choice and value and self.__check_is_choice(attr, attr.value_type, v)
|
||||
attr.is_unique and self.__check_is_unique(value_table, attr, ci.id, v)
|
||||
|
||||
self.__check_is_required(ci.type_id, attr, v)
|
||||
|
||||
return v
|
||||
|
||||
|
@ -101,12 +110,12 @@ class AttributeValueManager(object):
|
|||
def _write_change(ci_id, attr_id, operate_type, old, new):
|
||||
AttributeHistoryManger.add(ci_id, [(attr_id, operate_type, old, new)])
|
||||
|
||||
def create_or_update_attr_value(self, key, value, ci_id, _no_attribute_policy=ExistPolicy.IGNORE):
|
||||
def create_or_update_attr_value(self, key, value, ci, _no_attribute_policy=ExistPolicy.IGNORE):
|
||||
"""
|
||||
add or update attribute value, then write history
|
||||
:param key: id, name or alias
|
||||
:param value:
|
||||
:param ci_id:
|
||||
:param ci: instance object
|
||||
:param _no_attribute_policy: ignore or reject
|
||||
:return:
|
||||
"""
|
||||
|
@ -120,31 +129,34 @@ class AttributeValueManager(object):
|
|||
value_table = TableMap(attr_name=attr.name).table
|
||||
|
||||
if attr.is_list:
|
||||
value_list = [self._validate(attr, i, value_table, ci_id) for i in handle_arg_list(value)]
|
||||
value_list = [self._validate(attr, i, value_table, ci) for i in handle_arg_list(value)]
|
||||
if not value_list:
|
||||
self.__check_is_required(ci.type_id, attr, '')
|
||||
|
||||
existed_attrs = value_table.get_by(attr_id=attr.id,
|
||||
ci_id=ci_id,
|
||||
ci_id=ci.id,
|
||||
to_dict=False)
|
||||
existed_values = [i.value for i in existed_attrs]
|
||||
added = set(value_list) - set(existed_values)
|
||||
deleted = set(existed_values) - set(value_list)
|
||||
for v in added:
|
||||
value_table.create(ci_id=ci_id, attr_id=attr.id, value=v)
|
||||
self._write_change(ci_id, attr.id, OperateType.ADD, None, v)
|
||||
value_table.create(ci_id=ci.id, attr_id=attr.id, value=v)
|
||||
self._write_change(ci.id, attr.id, OperateType.ADD, None, v)
|
||||
|
||||
for v in deleted:
|
||||
existed_attr = existed_attrs[existed_values.index(v)]
|
||||
existed_attr.delete()
|
||||
self._write_change(ci_id, attr.id, OperateType.DELETE, v, None)
|
||||
self._write_change(ci.id, attr.id, OperateType.DELETE, v, None)
|
||||
else:
|
||||
value = self._validate(attr, value, value_table, ci_id)
|
||||
value = self._validate(attr, value, value_table, ci)
|
||||
existed_attr = value_table.get_by(attr_id=attr.id,
|
||||
ci_id=ci_id,
|
||||
ci_id=ci.id,
|
||||
first=True,
|
||||
to_dict=False)
|
||||
existed_value = existed_attr and existed_attr.value
|
||||
if existed_value is None:
|
||||
value_table.create(ci_id=ci_id, attr_id=attr.id, value=value)
|
||||
self._write_change(ci_id, attr.id, OperateType.ADD, None, value)
|
||||
value_table.create(ci_id=ci.id, attr_id=attr.id, value=value)
|
||||
self._write_change(ci.id, attr.id, OperateType.ADD, None, value)
|
||||
else:
|
||||
existed_attr.update(value=value)
|
||||
self._write_change(ci_id, attr.id, OperateType.UPDATE, existed_value, value)
|
||||
self._write_change(ci.id, attr.id, OperateType.UPDATE, existed_value, value)
|
||||
|
|
Loading…
Reference in New Issue