Merge pull request #2 from pycook/master

merge from main repo
This commit is contained in:
Bonsai 2020-02-04 10:56:03 +08:00 committed by GitHub
commit 90a97402ef
6 changed files with 254 additions and 30 deletions

1
.gitignore vendored
View File

@ -38,6 +38,7 @@ pip-log.txt
.tox .tox
nosetests.xml nosetests.xml
.pytest_cache .pytest_cache
cmdb-api/test-output
# Translations # Translations
*.mo *.mo

View File

@ -23,6 +23,7 @@ from api.models.cmdb import CITypeGroupItem
from api.models.cmdb import CITypeRelation from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView from api.models.cmdb import PreferenceTreeView
from api.tasks.cmdb import ci_type_attribute_order_rebuild
class CITypeManager(object): 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)) ci_type = CITypeCache.get(_type) or abort(404, "CIType <{0}> is not found".format(_type))
return ci_type.to_dict() 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 @classmethod
@kwargs_required("name") @kwargs_required("name")
def add(cls, **kwargs): def add(cls, **kwargs):
unique_key = kwargs.pop("unique_key", None) unique_key = kwargs.pop("unique_key", None)
unique_key = AttributeCache.get(unique_key) or abort(404, "Unique key is not defined") 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"] 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 kwargs["unique_id"] = unique_key.id
ci_type = CIType.create(**kwargs) ci_type = CIType.create(**kwargs)
@ -88,6 +105,9 @@ class CITypeManager(object):
ci_type = cls.check_is_existed(type_id) 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 = kwargs.pop("unique_key", None)
unique_key = AttributeCache.get(unique_key) unique_key = AttributeCache.get(unique_key)
if unique_key is not None: if unique_key is not None:
@ -305,6 +325,31 @@ class CITypeAttributeManager(object):
CITypeAttributesCache.clean(type_id) 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): class CITypeRelationManager(object):
""" """
@ -457,3 +502,86 @@ class CITypeAttributeGroupManager(object):
item.soft_delete() item.soft_delete()
return group_id 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)

View File

@ -11,6 +11,7 @@ from api.extensions import celery
from api.extensions import db from api.extensions import db
from api.extensions import es from api.extensions import es
from api.extensions import rd 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 CMDB_QUEUE
from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION 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) 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)) 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

View File

@ -164,6 +164,34 @@ class CITypeAttributeView(APIView):
return self.jsonify(attributes=attr_id_list) return self.jsonify(attributes=attr_id_list)
class CITypeAttributeTransferView(APIView):
url_prefix = "/ci_types/<int:type_id>/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/<int:type_id>/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): class CITypeAttributeGroupView(APIView):
url_prefix = ("/ci_types/<int:type_id>/attribute_groups", url_prefix = ("/ci_types/<int:type_id>/attribute_groups",
"/ci_types/attribute_groups/<int:group_id>") "/ci_types/attribute_groups/<int:group_id>")

View File

@ -122,3 +122,19 @@ export function deleteCITypeAttributesById (CITypeId, data) {
data: data data: data
}) })
} }
export function transferCITypeAttrIndex (CITypeId, data) {
return axios({
url: `/v0.1/ci_types/${CITypeId}/attributes/transfer`,
method: 'POST',
data: data
})
}
export function transferCITypeGroupIndex (CITypeId, data) {
return axios({
url: `/v0.1/ci_types/${CITypeId}/attribute_groups/transfer`,
method: 'POST',
data: data
})
}

View File

@ -86,8 +86,7 @@
v-model="CITypeGroup.attributes" v-model="CITypeGroup.attributes"
group="properties" group="properties"
@start="drag=true" @start="drag=true"
@end="handleEnd" @change="(e)=>{handleChange(e, CITypeGroup.id)}"
@change="handleChange"
:filter="'.filter-empty'" :filter="'.filter-empty'"
:animation="100" :animation="100"
tag="div" tag="div"
@ -164,8 +163,7 @@
v-model="otherGroupAttributes" v-model="otherGroupAttributes"
group="properties" group="properties"
@start="drag=true" @start="drag=true"
@end="handleEnd" @change="(e)=>{handleChange(e, -1)}"
@change="handleChange"
:animation="0" :animation="0"
style="min-height: 2rem; width: 100%; display: flex; flex-flow: wrap"> style="min-height: 2rem; width: 100%; display: flex; flex-flow: wrap">
@ -251,14 +249,14 @@
</template> </template>
<script> <script>
/* eslint-disable */
import { import {
deleteCITypeGroupById, deleteCITypeGroupById,
getCITypeGroupById, getCITypeGroupById,
createCITypeGroupById, createCITypeGroupById,
updateCITypeGroupById updateCITypeGroupById
} from '@/api/cmdb/CIType' } from '@/api/cmdb/CIType'
import { getCITypeAttributesById, updateCITypeAttributesById } from '@/api/cmdb/CITypeAttr' import { getCITypeAttributesById, updateCITypeAttributesById, transferCITypeAttrIndex, transferCITypeGroupIndex } from '@/api/cmdb/CITypeAttr'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
export default { export default {
@ -272,6 +270,7 @@ export default {
CITypeId: this.$route.params.CITypeId, CITypeId: this.$route.params.CITypeId,
CITypeName: this.$route.params.CITypeName, CITypeName: this.$route.params.CITypeName,
CITypeGroups: [], CITypeGroups: [],
addRemoveGroupFlag: {},
attributes: [], attributes: [],
otherGroupAttributes: [], otherGroupAttributes: [],
addGroupBtnVisible: true, addGroupBtnVisible: true,
@ -312,7 +311,7 @@ export default {
group.editable = false group.editable = false
group.originOrder = group.order group.originOrder = group.order
group.originName = group.name group.originName = group.name
group.attributes = group.attributes.sort((a, b) => a.order - b.order) // group.attributes = group.attributes.sort((a, b) => a.order - b.order)
}) })
this.otherGroupAttributes = this.attributes.filter(x => !inGroupAttrKeys.includes(x.id)).sort((a, b) => a.order - b.order) this.otherGroupAttributes = this.attributes.filter(x => !inGroupAttrKeys.includes(x.id)).sort((a, b) => a.order - b.order)
@ -322,7 +321,7 @@ export default {
attribute.originOrder = attribute.order attribute.originOrder = attribute.order
}) })
console.log('setOtherGroupAttributes', this.CITypeGroups, this.otherGroupAttributes) // console.log('setOtherGroupAttributes', this.CITypeGroups, this.otherGroupAttributes)
}, },
getCITypeGroupData () { getCITypeGroupData () {
const promises = [ const promises = [
@ -387,13 +386,18 @@ export default {
}, },
handleMoveGroup (beforeIndex, afterIndex) { handleMoveGroup (beforeIndex, afterIndex) {
const fromGroupId = this.CITypeGroups[beforeIndex].id
const toGroupId = this.CITypeGroups[afterIndex].id
transferCITypeGroupIndex(this.CITypeId, { from: fromGroupId, to: toGroupId }).then(res => {
this.$message.success('操作成功')
const beforeGroup = this.CITypeGroups[beforeIndex] const beforeGroup = this.CITypeGroups[beforeIndex]
this.CITypeGroups[beforeIndex] = this.CITypeGroups[afterIndex] this.CITypeGroups[beforeIndex] = this.CITypeGroups[afterIndex]
this.$set(this.CITypeGroups, beforeIndex, this.CITypeGroups[afterIndex]) this.$set(this.CITypeGroups, beforeIndex, this.CITypeGroups[afterIndex])
this.$set(this.CITypeGroups, afterIndex, beforeGroup) this.$set(this.CITypeGroups, afterIndex, beforeGroup)
}).catch(err => {
this.updatePropertyIndex() this.$httpError(err, '移动出错')
})
}, },
handleAddExistGroupAttr (index) { handleAddExistGroupAttr (index) {
const group = this.CITypeGroups[index] const group = this.CITypeGroups[index]
@ -456,24 +460,50 @@ export default {
}) })
.catch(err => this.requestFailed(err)) .catch(err => this.requestFailed(err))
}, },
handleChange (e) { handleChange (e, group) {
console.log(e) if (e.hasOwnProperty('moved') && e.moved.oldIndex !== e.moved.newIndex) {
if (e.hasOwnProperty('moved')) { if (group === -1) {
this.shouldUpdatePropertyIndex = e.moved.newIndex !== e.moved.oldIndex this.$message.error('更多属性不能进行排序, 如需排序需添加入其他分组中!')
} else { } else {
this.shouldUpdatePropertyIndex = true transferCITypeAttrIndex(this.CITypeId,
{
from: { attr_id: e.moved.element.id, group_id: group > -1 ? group : null },
to: { order: e.moved.newIndex, group_id: group > -1 ? group : null }
}).then(res => this.$message.success('保存成功')).catch(err => {
this.$httpError(err)
this.abortDraggable()
})
}
}
if (e.hasOwnProperty('added')) {
this.addRemoveGroupFlag = { to: { group_id: group > -1 ? group : null, order: e.added.newIndex }, inited: true }
}
if (e.hasOwnProperty('removed')) {
this.$nextTick(() => {
transferCITypeAttrIndex(this.CITypeId,
{
from: { attr_id: e.removed.element.id, group_id: group > -1 ? group : null },
to: { group_id: this.addRemoveGroupFlag.to.group_id, order: this.addRemoveGroupFlag.to.order }
}).then(res => this.$message.success('保存成功')).catch(err => {
this.$httpError(err)
this.abortDraggable()
}).finally(() => {
this.addRemoveGroupFlag = {}
})
})
} }
}, },
handleEnd (e) { abortDraggable () {
if (this.shouldUpdatePropertyIndex) { this.$nextTick(() => {
this.updatePropertyIndex() this.$router.push({name: 'ci_type'})
this.shouldUpdatePropertyIndex = false })
}
}, },
updatePropertyIndex () { updatePropertyIndex () {
const attributes = [] const attributes = [] // 全部属性
let attributeOrder = 0 let attributeOrder = 0 // 属性组
let groupOrder = 0 let groupOrder = 0 // 组排序
const promises = [ const promises = [
] ]