parent
cdcea7f9d6
commit
b354fe14bf
|
@ -2,6 +2,9 @@ from flask import Blueprint
|
|||
from flask_restful import Api
|
||||
|
||||
from controller.asset import views
|
||||
from controller.asset import instance
|
||||
from controller.asset import middleware
|
||||
from controller.asset import credential
|
||||
|
||||
# 当前app的蓝图,以app名为前缀
|
||||
asset = Blueprint('asset', __name__, url_prefix="/asset")
|
||||
|
@ -11,17 +14,19 @@ api = Api(asset)
|
|||
api.add_resource(views.HostViews, '/host/', endpoint="host")
|
||||
api.add_resource(views.HostDetailViews, '/host/<string:pk>/', endpoint="host-detail")
|
||||
|
||||
api.add_resource(views.MySQLInstanceViews, '/database/mysql/', endpoint="db-mysql")
|
||||
api.add_resource(views.MySQLInstanceDetail, '/database/mysql/<string:pk>/', endpoint="db-mysql-detail")
|
||||
# 数据实例中的库详情
|
||||
api.add_resource(views.DatabaseViews, '/database/<string:pk>/db/', endpoint="db-database")
|
||||
api.add_resource(views.DatabaseDetailViews, '/database/<string:pk>/db/<string:db>/', endpoint="db-database-detail")
|
||||
# 凭据相关视图
|
||||
api.add_resource(credential.CredentialViews, '/credential/', endpoint="cred-all")
|
||||
api.add_resource(credential.CredentialClassViews, '/credential/<string:class_name>/', endpoint="cred-cls-list")
|
||||
api.add_resource(credential.CredentialClassDetail,
|
||||
'/credential/<string:class_name>/<string:pk>/', endpoint="cred-cls-detail")
|
||||
|
||||
api.add_resource(views.RedisInstanceViews, '/database/redis/', endpoint="db-redis")
|
||||
api.add_resource(views.RedisInstanceDetail, '/database/redis/<string:pk>/', endpoint="db-redis-detail")
|
||||
# 中间件相关视图
|
||||
api.add_resource(middleware.MiddlewareViews, '/middleware/', endpoint="mdw-all")
|
||||
api.add_resource(middleware.MiddlewareClassViews, '/middleware/<string:class_name>/', endpoint="mdw-cls-list")
|
||||
api.add_resource(middleware.MiddlewareClassDetail,
|
||||
'/middleware/<string:class_name>/<string:pk>/', endpoint="mdw-cls-detail")
|
||||
|
||||
api.add_resource(views.NginxInstanceViews, '/middleware/nginx/', endpoint="middleware-nginx")
|
||||
api.add_resource(views.NginxInstanceDetail, '/middleware/nginx/<string:pk>/', endpoint="middleware-nginx-detail")
|
||||
|
||||
api.add_resource(views.CDNViews, '/cdn/', endpoint="cdn")
|
||||
api.add_resource(views.CDNDetail, '/cdn/<string:pk>/', endpoint="cdn-detail")
|
||||
# 实例相关视图
|
||||
api.add_resource(instance.InstanceViews, '/instance/', endpoint="ins-all")
|
||||
api.add_resource(instance.InstanceClassViews, '/instance/<string:class_name>/', endpoint="ins-cls-list")
|
||||
api.add_resource(instance.InstanceClassDetail, '/instance/<string:class_name>/<string:pk>/', endpoint="ins-cls-detail")
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
from flask_restful import marshal
|
||||
from common.serializer import marshal as marshal_fields
|
||||
from functools import reduce
|
||||
|
||||
|
||||
def merge_dict_reduce(x: dict, y: dict):
|
||||
"""合并字典"""
|
||||
x.update(y)
|
||||
return x
|
||||
|
||||
|
||||
def key_indexed_map(dataset: list, fields, key="id"):
|
||||
"""将数据集合转换为以指定列为 `key` 的数据,可以指定 `key` 为某个字段名
|
||||
如:{
|
||||
"xx01": {"id": "xx01", "field_1": "1", ...},
|
||||
"xx02": {"id": "xx02", "field_1": "1", ...},
|
||||
}
|
||||
"""
|
||||
def pk_indexed_map(obj):
|
||||
"""需要是个闭包,使用传入的参数"""
|
||||
idx = str(getattr(obj, key)) if hasattr(obj, key) else ""
|
||||
return {idx: marshal(obj, fields)}
|
||||
|
||||
return reduce(merge_dict_reduce, map(pk_indexed_map, dataset))
|
||||
|
||||
|
||||
def to_list(dataset: list, fields, only_exist=True, **kwargs):
|
||||
"""将数据集合转换为 `list` 对象,按 fields 字段描述转换"""
|
||||
if only_exist:
|
||||
return list(map(lambda x: marshal_fields(x, fields, only_exist=True, **kwargs), dataset))
|
||||
return list(map(lambda x: marshal(x, fields, **kwargs), dataset))
|
|
@ -0,0 +1,60 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
|
||||
def marshal(data, fields, envelope=None, only_exist=False, required_fields=None, **kwargs):
|
||||
"""Takes raw data (in the form of a dict, list, object) and a dict of
|
||||
fields to output and filters the data based on those fields.
|
||||
|
||||
:param data: the actual object(s) from which the fields are taken from
|
||||
:param fields: a dict of whose keys will make up the final serialized
|
||||
response output
|
||||
:param envelope: optional key that will be used to envelop the serialized
|
||||
response
|
||||
:param only_exist: 只渲染存在的 key
|
||||
:param required_fields: 必须渲染的 key,不会被 only_exist 排除
|
||||
|
||||
|
||||
>>> from flask_restful import fields, marshal
|
||||
>>> data = { 'a': 100, 'b': 'foo' }
|
||||
>>> mfields = { 'a': fields.Raw }
|
||||
|
||||
>>> marshal(data, mfields)
|
||||
OrderedDict([('a', 100)])
|
||||
|
||||
>>> marshal(data, mfields, envelope='data')
|
||||
OrderedDict([('data', OrderedDict([('a', 100)]))])
|
||||
|
||||
"""
|
||||
|
||||
if required_fields is None:
|
||||
required_fields = []
|
||||
|
||||
def make(cls):
|
||||
if isinstance(cls, type):
|
||||
return cls()
|
||||
return cls
|
||||
|
||||
if isinstance(data, (list, tuple)):
|
||||
return (OrderedDict([(envelope, [marshal(d, fields) for d in data])])
|
||||
if envelope else [marshal(d, fields) for d in data])
|
||||
|
||||
if not only_exist:
|
||||
items = ((k, marshal(data, v) if isinstance(v, dict)
|
||||
else make(v).output(k, data))
|
||||
for k, v in fields.items())
|
||||
else:
|
||||
def parse(item):
|
||||
k, v = item
|
||||
if isinstance(v, dict):
|
||||
return k, marshal(data, v)
|
||||
if hasattr(data, k) or k in required_fields:
|
||||
return k, make(v).output(k, data)
|
||||
return None, ""
|
||||
# items = ((k, marshal(data, v) if isinstance(v, dict)
|
||||
# else make(v).output(k, data))
|
||||
# for k, v in fields.items())
|
||||
items = ((k, v) for k, v in map(parse, fields.items()) if k is not None)
|
||||
print(items)
|
||||
return OrderedDict([(envelope, OrderedDict(items))]) if envelope else OrderedDict(items)
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ from typing import List, Dict
|
|||
from flask import request
|
||||
from flask_restful import abort, Resource, marshal, fields, reqparse
|
||||
|
||||
from common import formatter
|
||||
from common.utils import abort_response
|
||||
from common.crypto import quick_crypto
|
||||
|
||||
|
@ -60,9 +61,9 @@ class ModelViewBase(Resource):
|
|||
# method_decorators = [token_header_required, ]
|
||||
method_decorators = []
|
||||
|
||||
def get_object(self, pk):
|
||||
def get_object(self, pk, **kwargs):
|
||||
try:
|
||||
return self.model.objects(id=pk).first_or_404(message="resource not found")
|
||||
return self.model.objects(id=pk, **kwargs).first_or_404(message="resource not found")
|
||||
except:
|
||||
abort(404, msg=f"resource '{pk}' not found")
|
||||
|
||||
|
@ -117,7 +118,7 @@ class ModelViewBase(Resource):
|
|||
if errors:
|
||||
abort_response(400, 1001, msg=f"部分字段需要唯一,请检查重复!", errors=errors)
|
||||
|
||||
def validate_fields(self, args: dict, create=True) -> dict:
|
||||
def validate_fields(self, args: dict, create=True, **kwargs) -> dict:
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
|
@ -163,7 +164,7 @@ class ListMixin(ModelViewBase):
|
|||
# filter 过滤的字段参数
|
||||
filter_fields = []
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
self.queryset = self.model.objects
|
||||
|
||||
def filter_queryset(self):
|
||||
|
@ -197,21 +198,23 @@ class ListMixin(ModelViewBase):
|
|||
logger.exception(f"查询出错 {self.model} query_params={query_params}")
|
||||
return queryset
|
||||
|
||||
def get(self):
|
||||
def get(self, *args, **kwargs):
|
||||
"""获取列表数据"""
|
||||
# 过滤后的数据,
|
||||
|
||||
self.get_queryset()
|
||||
self.get_queryset(*args, **kwargs)
|
||||
|
||||
# TODO 匹配过滤参数
|
||||
self.queryset = self.filter_queryset()
|
||||
|
||||
if self.paging:
|
||||
limit = request.args.get("limit")
|
||||
if self.paging and limit != "0":
|
||||
return self.paginate_queryset(self. queryset)
|
||||
|
||||
# TODO 这里还未了解到比较妥的办法,似乎 `marshal` 返回的信息必须是 `dict`,返回铺平对象的 `[{obj}, {obj}]` 没找到方法,待研究
|
||||
# 不分页,需要取出对象,铺平
|
||||
return [marshal(obj, self.fields) for obj in self.queryset]
|
||||
# 转为对象列表
|
||||
if self.queryset.count() > 0:
|
||||
return formatter.to_list(self.queryset, self.fields, only_exist=True)
|
||||
# return marshal(self.queryset, self.fields, envelope="id")
|
||||
# return formatter.key_indexed_map(self.queryset, self.fields)
|
||||
return []
|
||||
|
||||
|
||||
class CreateMixin(ModelViewBase):
|
||||
|
@ -227,7 +230,7 @@ class CreateMixin(ModelViewBase):
|
|||
|
||||
# 解析参数
|
||||
args = self.request_parse.parse_args()
|
||||
validated_data = self.validate_fields(args, create=True)
|
||||
validated_data = self.validate_fields(args, create=True, **kwargs)
|
||||
|
||||
# 校验关联字段
|
||||
self.validate_relation_fields(validated_data)
|
||||
|
@ -319,6 +322,67 @@ class DetailViewSet(RetrieveMixin, UpdateMixin, DestroyMixin):
|
|||
"""带 `pk` 参数的视图集合"""
|
||||
|
||||
|
||||
class RetrieveClassMixin(ModelViewBase):
|
||||
|
||||
def get(self, class_name, pk):
|
||||
obj = self.get_object(pk, class_name=class_name)
|
||||
return marshal(obj, self.fields)
|
||||
|
||||
|
||||
class UpdateClassMixin(ModelViewBase):
|
||||
|
||||
def pre_update(self, obj, args: dict):
|
||||
"""更新前钩子"""
|
||||
data = {}
|
||||
for k, v in args.items():
|
||||
# 为 None 的不设置
|
||||
if v is not None:
|
||||
data[k] = v
|
||||
return data
|
||||
|
||||
def put(self, class_name, pk):
|
||||
# 获取对象
|
||||
obj = self.get_object(pk, class_name=class_name)
|
||||
|
||||
# 解析参数
|
||||
args = self.request_parse.parse_args()
|
||||
validated_data = self.validate_fields(args, create=False)
|
||||
# 校验关联字段
|
||||
self.validate_relation_fields(validated_data)
|
||||
validated_data = self.pre_update(obj, validated_data)
|
||||
|
||||
# 检查重复:检查提交的唯一字段值,是否有存在的对象,与当前对象id不同认为异常
|
||||
self.validate_uniq_fields(args, obj=obj)
|
||||
|
||||
# 更新对象、保存
|
||||
try:
|
||||
obj.update(**validated_data)
|
||||
obj.save()
|
||||
# 重新读取数据
|
||||
obj.reload()
|
||||
except Exception as e:
|
||||
logger.exception(f"{self.model} 保存对象失败!pk={pk} data={args}")
|
||||
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
||||
return
|
||||
|
||||
return marshal(obj, self.fields)
|
||||
|
||||
|
||||
class DestroyClassMixin(ModelViewBase):
|
||||
|
||||
def pre_destroy(self, obj):
|
||||
"""删除对象前的方法,可以在这拦截做些操作"""
|
||||
pass
|
||||
|
||||
def delete(self, class_name, pk):
|
||||
"""删除对象方法"""
|
||||
obj = self.get_object(pk, class_name=class_name)
|
||||
# 删除前钩子
|
||||
self.pre_destroy(obj)
|
||||
obj.delete()
|
||||
return {"id": pk}
|
||||
|
||||
|
||||
class EncryptRequiredCreateView(CreateMixin):
|
||||
"""创建对象时,加密字段的视图"""
|
||||
# 需要加密的字段
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
from flask import request
|
||||
from flask_restful import reqparse
|
||||
|
||||
from models.asset.credential import Credential
|
||||
from models.asset.credentialFields import CredentialFields, CredentialDetailFields
|
||||
from common.views import (ListCreateViewSet, DetailViewSet,
|
||||
RetrieveClassMixin, DestroyClassMixin, UpdateClassMixin)
|
||||
from common.utils import abort_response
|
||||
|
||||
|
||||
class CredentialParse:
|
||||
"""凭据信息的校验类"""
|
||||
request_parse = None
|
||||
# 给子类用的唯一字段,用于校验
|
||||
# uniq_fields = ("name", "host")
|
||||
|
||||
def init_parse(self):
|
||||
self.request_parse = reqparse.RequestParser()
|
||||
|
||||
self.request_parse.add_argument("class_name", required=False, type=str, location='json')
|
||||
|
||||
self.request_parse.add_argument("extra", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("data", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("tags", required=False, type=list, location='json')
|
||||
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
||||
|
||||
|
||||
class CredentialClassNameValidate:
|
||||
"""统一校验 class_name"""
|
||||
def validate_fields(self, args: dict, create=False, **kwargs) -> dict:
|
||||
class_name = kwargs.get("class_name")
|
||||
class_name_params = args.get("class_name")
|
||||
if class_name_params and class_name_params != class_name:
|
||||
return abort_response(400, 1400, msg="URL中的类别与内容中的类别字段有差异!")
|
||||
args["class_name"] = class_name
|
||||
return args
|
||||
|
||||
|
||||
class CredentialViews(CredentialParse, ListCreateViewSet):
|
||||
"""凭据列表、创建视图"""
|
||||
model = Credential
|
||||
fields = CredentialFields
|
||||
filter_fields = (("name", "icontains"), )
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
print(request.method)
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
|
||||
class CredentialDetail(CredentialParse, CredentialClassNameValidate, DetailViewSet):
|
||||
"""凭据详情"""
|
||||
model = Credential
|
||||
fields = CredentialDetailFields
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=False, type=str, location='json')
|
||||
|
||||
|
||||
class CredentialClassViews(CredentialParse, CredentialClassNameValidate, ListCreateViewSet):
|
||||
"""凭据列表,带 class_name 参数"""
|
||||
model = Credential
|
||||
fields = CredentialFields
|
||||
filter_fields = (("name", "icontains"),)
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
|
||||
class CredentialClassDetail(CredentialParse, CredentialClassNameValidate,
|
||||
RetrieveClassMixin, DestroyClassMixin, UpdateClassMixin):
|
||||
"""凭据详情,带 class_name 和 pk 参数"""
|
||||
model = Credential
|
||||
fields = CredentialDetailFields
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=False, type=str, location='json')
|
|
@ -0,0 +1,94 @@
|
|||
from flask_restful import reqparse
|
||||
|
||||
from models.asset.middleware import Middleware
|
||||
from models.asset.instance import Instance
|
||||
from models.asset.instanceFields import InstanceFields, InstanceDetailFields
|
||||
from common.views import (ListCreateViewSet, DetailViewSet,
|
||||
RetrieveClassMixin, DestroyClassMixin, UpdateClassMixin)
|
||||
from common.utils import abort_response
|
||||
|
||||
|
||||
class InstanceParse:
|
||||
"""
|
||||
中间件模型公共字段的校验类
|
||||
"""
|
||||
request_parse = None
|
||||
# 给子类用的唯一字段,用于校验
|
||||
uniq_fields = (("name", "class_name"),)
|
||||
|
||||
def init_parse(self):
|
||||
self.request_parse = reqparse.RequestParser()
|
||||
self.request_parse.add_argument("class_name", required=False, type=str, location='json')
|
||||
self.request_parse.add_argument("middleware_id", required=False, type=str, location='json')
|
||||
self.request_parse.add_argument("credentials", required=False, type=list, location='json')
|
||||
|
||||
self.request_parse.add_argument("extra", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("data", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("tags", required=False, type=list, location='json')
|
||||
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
||||
|
||||
|
||||
class InstanceClassNameValidate:
|
||||
"""统一校验 class_name和关联的中间件,若中间件 ID 设置值,class_name 需要相同"""
|
||||
def validate_fields(self, args: dict, create=False, **kwargs) -> dict:
|
||||
# 校验 class_name,非必填参数,但是在URL中有传递
|
||||
# 若URL中的参数与请求体中的参数不一致,将抛错(最好是请求体中不带,以URL中的为准)
|
||||
class_name = kwargs.get("class_name")
|
||||
class_name_params = args.get("class_name")
|
||||
if class_name_params and class_name_params != class_name:
|
||||
return abort_response(400, 1400, msg="URL中的类别与内容中的类别字段有差异!")
|
||||
args["class_name"] = class_name
|
||||
|
||||
# 校验 middleware_id 若设置值,检查 middleware 对象是否存在,且类型与 instance 一致
|
||||
middleware_id = args.get("middleware_id")
|
||||
if middleware_id:
|
||||
try:
|
||||
assert Middleware.objects(id=middleware_id, class_name=class_name).first()
|
||||
except:
|
||||
return abort_response(400, 1400, msg="参数中的同类型 middleware 对象不存在")
|
||||
return args
|
||||
|
||||
|
||||
class InstanceViews(InstanceParse, ListCreateViewSet):
|
||||
model = Instance
|
||||
fields = InstanceFields
|
||||
filter_fields = (("name", "icontains"), ("class_name", ""))
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
|
||||
class InstanceDetailViews(InstanceParse, DetailViewSet):
|
||||
model = Instance
|
||||
fields = InstanceDetailFields
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=False, type=str, location='json')
|
||||
|
||||
|
||||
class InstanceClassViews(InstanceParse, InstanceClassNameValidate, ListCreateViewSet):
|
||||
"""某类别的实例列表"""
|
||||
model = Instance
|
||||
fields = InstanceFields
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
self.queryset = self.model.objects(**kwargs)
|
||||
|
||||
|
||||
class InstanceClassDetail(InstanceParse, InstanceClassNameValidate, RetrieveClassMixin, UpdateClassMixin, DestroyClassMixin):
|
||||
"""某类别的实例详情"""
|
||||
model = Instance
|
||||
fields = InstanceDetailFields
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=False, type=str, location='json')
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
self.queryset = self.model.objects(**kwargs)
|
|
@ -0,0 +1,90 @@
|
|||
from flask_restful import reqparse
|
||||
|
||||
from models.asset.middleware import Middleware
|
||||
from models.asset.middlewareFields import MiddlewareFields
|
||||
from common.utils import abort_response
|
||||
from common.views import (ListCreateViewSet, ListMixin, DetailViewSet,
|
||||
RetrieveClassMixin, DestroyClassMixin, UpdateClassMixin)
|
||||
|
||||
|
||||
class MiddlewareParse:
|
||||
"""
|
||||
中间件模型公共字段的校验类
|
||||
"""
|
||||
request_parse = None
|
||||
# 给子类用的唯一字段,用于校验
|
||||
uniq_fields = ("name", "host")
|
||||
|
||||
def init_parse(self):
|
||||
self.request_parse = reqparse.RequestParser()
|
||||
|
||||
self.request_parse.add_argument("class_name", required=False, type=str, location='json')
|
||||
self.request_parse.add_argument("host", required=False, type=str, location='json')
|
||||
self.request_parse.add_argument("port", required=False, type=int, location='json')
|
||||
self.request_parse.add_argument("manage", required=False, type=str, location='json')
|
||||
|
||||
self.request_parse.add_argument("extra", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("data", required=False, type=dict, location='json')
|
||||
self.request_parse.add_argument("tags", required=False, type=list, location='json')
|
||||
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
||||
|
||||
|
||||
class MiddlewareClassNameValidate:
|
||||
"""统一校验 class_name"""
|
||||
def validate_fields(self, args: dict, create=False, **kwargs) -> dict:
|
||||
class_name = kwargs.get("class_name")
|
||||
class_name_params = args.get("class_name")
|
||||
if class_name_params and class_name_params != class_name:
|
||||
return abort_response(400, 1400, msg="URL中的类别与内容中的类别字段有差异!")
|
||||
args["class_name"] = class_name
|
||||
return args
|
||||
|
||||
|
||||
class MiddlewareViews(MiddlewareParse, ListCreateViewSet):
|
||||
model = Middleware
|
||||
fields = MiddlewareFields
|
||||
uniq_fields = ("name",)
|
||||
filter_fields = (("name", "icontains"), ("host", "icontains"), ("manage", "icontains"), ("class_name", ""))
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
|
||||
class MiddlewareDetail(MiddlewareParse, DetailViewSet):
|
||||
"""分类别的视图"""
|
||||
model = Middleware
|
||||
fields = MiddlewareFields
|
||||
uniq_fields = ("name",)
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
|
||||
class MiddlewareClassViews(MiddlewareParse, MiddlewareClassNameValidate, ListCreateViewSet):
|
||||
"""按分类的中间件视图,带 class_name 参数"""
|
||||
model = Middleware
|
||||
fields = MiddlewareFields
|
||||
uniq_fields = ("name",)
|
||||
filter_fields = (("name", "icontains"), ("host", "icontains"), ("manage", "icontains"))
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=True, type=str, location='json')
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
"""按分类查找"""
|
||||
self.queryset = self.model.objects(**kwargs)
|
||||
|
||||
|
||||
class MiddlewareClassDetail(MiddlewareParse, MiddlewareClassNameValidate,
|
||||
RetrieveClassMixin, DestroyClassMixin, UpdateClassMixin):
|
||||
"""分类别的详情视图,带 class_name 和 pk 参数"""
|
||||
model = Middleware
|
||||
fields = MiddlewareFields
|
||||
uniq_fields = ("name",)
|
||||
|
||||
def __init__(self):
|
||||
self.init_parse()
|
||||
self.request_parse.add_argument("name", required=False, type=str, location='json')
|
|
@ -0,0 +1,13 @@
|
|||
import mongoengine as mongo
|
||||
from common.document import DocumentBase
|
||||
|
||||
|
||||
class Credential(DocumentBase):
|
||||
"""凭据信息"""
|
||||
meta = {'allow_inheritance': True, 'collection': 'credential'}
|
||||
class_name = mongo.StringField(required=False, default="")
|
||||
name = mongo.StringField(required=True)
|
||||
extra = mongo.DictField(required=False)
|
||||
data = mongo.DictField(default=dict)
|
||||
tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表
|
||||
labels = mongo.DictField(default=dict)
|
|
@ -0,0 +1,15 @@
|
|||
from flask_restful import fields
|
||||
|
||||
|
||||
CredentialFields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"class_name": fields.String,
|
||||
"extra": fields.Raw,
|
||||
"credentials": fields.List(fields.String),
|
||||
"data": fields.Raw,
|
||||
"tags": fields.List(fields.String),
|
||||
"labels": fields.Raw,
|
||||
}
|
||||
|
||||
CredentialDetailFields = CredentialFields
|
|
@ -38,6 +38,18 @@ DatabaseServerFields = {
|
|||
"labels": fields.Raw,
|
||||
}
|
||||
|
||||
|
||||
databaseDetailFields = {
|
||||
"id": "",
|
||||
"name": fields.String,
|
||||
"domain": fields.String,
|
||||
"host": fields.String,
|
||||
"manage": fields.String,
|
||||
"data": fields.Raw,
|
||||
"tags": fields.List(fields.String),
|
||||
"labels": fields.Raw,
|
||||
}
|
||||
|
||||
DatabaseFields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import mongoengine as mongo
|
||||
|
||||
from common.document import DocumentBase
|
||||
from models.asset.middleware import Middleware
|
||||
|
||||
|
||||
class Instance(DocumentBase):
|
||||
""""""
|
||||
meta = {'allow_inheritance': True, 'collection': 'instance'}
|
||||
# 实例的分类名称
|
||||
class_name = mongo.StringField(required=False, default="")
|
||||
|
||||
# 实例的名称,可能是库名、仓库名等
|
||||
name = mongo.StringField(required=True)
|
||||
|
||||
# 关联的 middleware
|
||||
middleware_id = mongo.ObjectIdField(required=False)
|
||||
|
||||
# 对应可以有多个凭据
|
||||
credentials = mongo.ListField(mongo.ObjectIdField, default=list)
|
||||
# 按类型不同的信息存放到 extra 字典
|
||||
extra = mongo.DictField(default=dict)
|
||||
|
||||
data = mongo.DictField(default=dict)
|
||||
tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表
|
||||
labels = mongo.DictField(default=dict)
|
||||
|
||||
@property
|
||||
def middleware(self):
|
||||
if self.middleware_id:
|
||||
try:
|
||||
return Middleware.objects(id=self.middleware_id).first()
|
||||
except:
|
||||
pass
|
||||
return None
|
|
@ -0,0 +1,30 @@
|
|||
from flask_restful import fields
|
||||
|
||||
from models.asset.middlewareFields import MiddlewareFields
|
||||
|
||||
# 普通视图,只返回内部字段
|
||||
InstanceFields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"class_name": fields.String,
|
||||
"middleware_id": fields.String,
|
||||
|
||||
"extra": fields.Raw,
|
||||
"data": fields.Raw,
|
||||
"tags": fields.List(fields.String),
|
||||
"labels": fields.Raw,
|
||||
}
|
||||
|
||||
# 详情视图,返回中间件和凭据
|
||||
InstanceDetailFields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"class_name": fields.String,
|
||||
"middleware": fields.Nested(MiddlewareFields),
|
||||
"credentials": fields.List(fields.String),
|
||||
|
||||
"extra": fields.Raw,
|
||||
"data": fields.Raw,
|
||||
"tags": fields.List(fields.String),
|
||||
"labels": fields.Raw,
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import mongoengine as mongo
|
||||
|
||||
from common.document import DocumentBase
|
||||
|
||||
|
||||
class Middleware(DocumentBase):
|
||||
"""中间件"""
|
||||
meta = {'allow_inheritance': True, 'collection': 'mdw', 'strict': False}
|
||||
# 分类名称
|
||||
class_name = mongo.StringField(required=False, default="")
|
||||
|
||||
name = mongo.StringField(required=True)
|
||||
host = mongo.StringField(required=False, default="")
|
||||
port = mongo.IntField(default=0)
|
||||
# 管理者,预留
|
||||
manage = mongo.StringField(max_length=128, required=False, default="")
|
||||
extra = mongo.DictField(default=dict)
|
||||
data = mongo.DictField(default=dict)
|
||||
# 标记和标签
|
||||
tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表
|
||||
labels = mongo.DictField(default=dict)
|
||||
|
||||
|
||||
# class DBServer(Middleware):
|
||||
# """数据库服务器"""
|
||||
# meta = {'allow_inheritance': True, 'strict': False}
|
||||
# domain = mongo.StringField(required=False) # 域名连接的地址
|
||||
# credentials = mongo.ListField(mongo.ObjectIdField, default=list)
|
||||
|
||||
# 配置
|
||||
# storage = mongo.IntField(required=False, default=0)
|
||||
# memory = mongo.IntField(required=False, default=0)
|
||||
# core = mongo.IntField(required=False, default=0)
|
|
@ -0,0 +1,38 @@
|
|||
from flask_restful import fields
|
||||
|
||||
MiddlewareFields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"host": fields.String,
|
||||
"port": fields.Integer,
|
||||
"manage": fields.String,
|
||||
"class_name": fields.String,
|
||||
"extra": fields.Raw,
|
||||
"credentials": fields.List(fields.String),
|
||||
"data": fields.Raw,
|
||||
"tags": fields.List(fields.String),
|
||||
"labels": fields.Raw,
|
||||
}
|
||||
|
||||
# DatabaseFields = {
|
||||
# "id": fields.String,
|
||||
# "name": fields.String,
|
||||
# "username": fields.String,
|
||||
# "password": fields.String,
|
||||
# "data": fields.Raw,
|
||||
# "tags": fields.List(fields.String),
|
||||
# "labels": fields.Raw,
|
||||
# }
|
||||
#
|
||||
# DBServerFields = {
|
||||
# "domain": fields.String,
|
||||
# "port": fields.Integer,
|
||||
# "username": fields.String,
|
||||
# "password": fields.String,
|
||||
# "storage": fields.Integer,
|
||||
# "memory": fields.Integer,
|
||||
# "core": fields.Integer,
|
||||
# "databases": fields.List(fields.Nested(DatabaseFields))
|
||||
# }
|
||||
#
|
||||
# DBServerFields.update(MiddlewareFields)
|
|
@ -44,6 +44,7 @@ ChannelFields = {
|
|||
"id": fields.String,
|
||||
"project": fields.Nested(ProjectSimpleField),
|
||||
"name": fields.String,
|
||||
"fullname": fields.String,
|
||||
"spid": fields.String,
|
||||
"is_cross": fields.Boolean,
|
||||
"version": fields.Nested(VersionFields),
|
||||
|
|
|
@ -81,6 +81,13 @@ class Channel(DocumentBase):
|
|||
if isinstance(val, Project):
|
||||
self.project_id = str(val.id)
|
||||
|
||||
@property
|
||||
def fullname(self):
|
||||
project = ""
|
||||
if self.project:
|
||||
project = self.project.fullname
|
||||
return f"{self.spid} {project}"
|
||||
|
||||
@classmethod
|
||||
def filter_by_project(cls, name, fork, queryset=None):
|
||||
"""过滤项目的所有渠道"""
|
||||
|
|
Loading…
Reference in New Issue