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