diff --git a/cmdb-api/api/lib/cmdb/attribute.py b/cmdb-api/api/lib/cmdb/attribute.py index 2384d5c..1380f14 100644 --- a/cmdb-api/api/lib/cmdb/attribute.py +++ b/cmdb-api/api/lib/cmdb/attribute.py @@ -60,22 +60,33 @@ class AttributeManager(object): return [] @staticmethod - def _get_choice_values_from_other_ci(choice_other): + def _get_choice_values_from_other(choice_other): from api.lib.cmdb.search import SearchError from api.lib.cmdb.search.ci import search - type_ids = choice_other.get('type_ids') - attr_id = choice_other.get('attr_id') - other_filter = choice_other.get('filter') or '' + if choice_other.get('type_ids'): + type_ids = choice_other.get('type_ids') + attr_id = choice_other.get('attr_id') + other_filter = choice_other.get('filter') or '' - query = "_type:({}),{}".format(";".join(map(str, type_ids)), other_filter) - s = search(query, fl=[str(attr_id)], facet=[str(attr_id)], count=1) - try: - _, _, _, _, _, facet = s.search() - return [[i[0], {}] for i in (list(facet.values()) or [[]])[0]] - except SearchError as e: - current_app.logger.error("get choice values from other ci failed: {}".format(e)) - return [] + query = "_type:({}),{}".format(";".join(map(str, type_ids)), other_filter) + s = search(query, fl=[str(attr_id)], facet=[str(attr_id)], count=1) + try: + _, _, _, _, _, facet = s.search() + return [[i[0], {}] for i in (list(facet.values()) or [[]])[0]] + except SearchError as e: + current_app.logger.error("get choice values from other ci failed: {}".format(e)) + return [] + + elif choice_other.get('script'): + try: + x = compile(choice_other['script'], '', "exec") + exec(x) + res = locals()['ChoiceValue']().values() or [] + return [[i, {}] for i in res] + except Exception as e: + current_app.logger.error("get choice values from script: {}".format(e)) + return [] @classmethod def get_choice_values(cls, attr_id, value_type, choice_web_hook, choice_other, @@ -87,7 +98,7 @@ class AttributeManager(object): return [] elif choice_other: if choice_other_parse and isinstance(choice_other, dict): - return cls._get_choice_values_from_other_ci(choice_other) + return cls._get_choice_values_from_other(choice_other) else: return [] @@ -96,7 +107,8 @@ class AttributeManager(object): return [] choice_values = choice_table.get_by(fl=["value", "option"], attr_id=attr_id) - return [[choice_value['value'], choice_value['option']] for choice_value in choice_values] + return [[ValueTypeMap.serialize[value_type](choice_value['value']), choice_value['option']] + for choice_value in choice_values] @staticmethod def add_choice_values(_id, value_type, choice_values): @@ -218,10 +230,15 @@ class AttributeManager(object): if name in BUILTIN_KEYWORDS: return abort(400, ErrFormat.attribute_name_cannot_be_builtin) - if kwargs.get('choice_other'): - if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or - not kwargs['choice_other'].get('attr_id')): - return abort(400, ErrFormat.attribute_choice_other_invalid) + while kwargs.get('choice_other'): + if isinstance(kwargs['choice_other'], dict): + if kwargs['choice_other'].get('script'): + break + + if kwargs['choice_other'].get('type_ids') and kwargs['choice_other'].get('attr_id'): + break + + return abort(400, ErrFormat.attribute_choice_other_invalid) alias = kwargs.pop("alias", "") alias = name if not alias else alias @@ -232,6 +249,8 @@ class AttributeManager(object): kwargs.get('is_computed') and cls.can_create_computed_attribute() + kwargs.get('choice_other') and kwargs['choice_other'].get('script') and cls.can_create_computed_attribute() + attr = Attribute.create(flush=True, name=name, alias=alias, @@ -337,10 +356,15 @@ class AttributeManager(object): self._change_index(attr, attr.is_index, kwargs['is_index']) - if kwargs.get('choice_other'): - if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or - not kwargs['choice_other'].get('attr_id')): - return abort(400, ErrFormat.attribute_choice_other_invalid) + while kwargs.get('choice_other'): + if isinstance(kwargs['choice_other'], dict): + if kwargs['choice_other'].get('script'): + break + + if kwargs['choice_other'].get('type_ids') and kwargs['choice_other'].get('attr_id'): + break + + return abort(400, ErrFormat.attribute_choice_other_invalid) existed2 = attr.to_dict() if not existed2['choice_web_hook'] and not existed2.get('choice_other') and existed2['is_choice']: diff --git a/cmdb-api/api/lib/cmdb/search/ci/db/search.py b/cmdb-api/api/lib/cmdb/search/ci/db/search.py index 0e26453..fee7251 100644 --- a/cmdb-api/api/lib/cmdb/search/ci/db/search.py +++ b/cmdb-api/api/lib/cmdb/search/ci/db/search.py @@ -28,6 +28,7 @@ from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_TYPE from api.lib.cmdb.search.ci.db.query_sql import QUERY_UNION_CI_ATTRIBUTE_IS_NULL 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.lib.utils import handle_arg_list @@ -524,15 +525,15 @@ class Search(object): if k: table_name = TableMap(attr=attr).table_name query_sql = FACET_QUERY.format(table_name, self.query_sql, attr.id) - # current_app.logger.warning(query_sql) result = db.session.execute(query_sql).fetchall() facet[k] = result facet_result = dict() for k, v in facet.items(): if not k.startswith('_'): - a = getattr(AttributeCache.get(k), self.ret_key) - facet_result[a] = [(f[0], f[1], a) for f in v] + attr = AttributeCache.get(k) + a = getattr(attr, self.ret_key) + facet_result[a] = [(ValueTypeMap.serialize[attr.value_type](f[0]), f[1], a) for f in v] return facet_result diff --git a/cmdb-api/api/lib/cmdb/utils.py b/cmdb-api/api/lib/cmdb/utils.py index c6d4630..e529670 100644 --- a/cmdb-api/api/lib/cmdb/utils.py +++ b/cmdb-api/api/lib/cmdb/utils.py @@ -12,7 +12,7 @@ import api.models.cmdb as model from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.const import ValueTypeEnum -TIME_RE = re.compile(r"^(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$") +TIME_RE = re.compile(r"^20|21|22|23|[0-1]\d:[0-5]\d:[0-5]\d$") def string2int(x): @@ -21,7 +21,7 @@ def string2int(x): def str2datetime(x): try: - return datetime.datetime.strptime(x, "%Y-%m-%d") + return datetime.datetime.strptime(x, "%Y-%m-%d").date() except ValueError: pass @@ -44,8 +44,8 @@ class ValueTypeMap(object): ValueTypeEnum.FLOAT: float, ValueTypeEnum.TEXT: 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.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S"), + ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d") if not isinstance(x, six.string_types) else x, + ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S") if not isinstance(x, six.string_types) else x, ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x, } @@ -64,6 +64,8 @@ class ValueTypeMap(object): ValueTypeEnum.FLOAT: model.FloatChoice, ValueTypeEnum.TEXT: model.TextChoice, ValueTypeEnum.TIME: model.TextChoice, + ValueTypeEnum.DATE: model.TextChoice, + ValueTypeEnum.DATETIME: model.TextChoice, } table = { @@ -97,7 +99,7 @@ class ValueTypeMap(object): ValueTypeEnum.DATE: 'text', ValueTypeEnum.TIME: 'text', ValueTypeEnum.FLOAT: 'float', - ValueTypeEnum.JSON: 'object' + ValueTypeEnum.JSON: 'object', }