Skip to content

Commit

Permalink
minor: resolve #1207 conversations
Browse files Browse the repository at this point in the history
  • Loading branch information
narasux committed Sep 4, 2023
1 parent 8d38773 commit e8b8213
Show file tree
Hide file tree
Showing 16 changed files with 626 additions and 406 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,4 @@ pre_commit_hooks
# local settings
cliff.toml
.codecc
.idea
.idea
188 changes: 28 additions & 160 deletions src/bk-user/bkuser/apis/web/data_source/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,15 @@
import logging
from typing import Any, Dict, List

from django.conf import settings
from django.utils.translation import gettext_lazy as _
from drf_yasg.utils import swagger_serializer_method
from pydantic import ValidationError as PDValidationError
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from bkuser.apps.data_source.constants import DataSourcePluginEnum, FieldMappingOperation
from bkuser.apps.data_source.models import (
DataSource,
DataSourceDepartment,
DataSourceDepartmentUserRelation,
DataSourcePlugin,
DataSourceUser,
)
from bkuser.apps.data_source.models import DataSource, DataSourcePlugin
from bkuser.apps.data_source.plugins.constants import DATA_SOURCE_PLUGIN_CONFIG_CLASS_MAP
from bkuser.biz.validators import validate_data_source_user_username
from bkuser.common.validators import validate_phone_with_country_code
from bkuser.utils.pydantic import stringify_pydantic_error

logger = logging.getLogger(__name__)
Expand All @@ -43,7 +34,7 @@ class DataSourceSearchOutputSLZ(serializers.Serializer):
name = serializers.CharField(help_text="数据源名称")
owner_tenant_id = serializers.CharField(help_text="数据源所属租户 ID")
plugin_name = serializers.SerializerMethodField(help_text="数据源插件名称")
collaborative_companies = serializers.SerializerMethodField(help_text="协作公司")
cooperation_tenants = serializers.SerializerMethodField(help_text="协作公司")
status = serializers.CharField(help_text="数据源状态")
updater = serializers.CharField(help_text="更新者")
updated_at = serializers.SerializerMethodField(help_text="更新时间")
Expand All @@ -58,7 +49,7 @@ def get_plugin_name(self, obj: DataSource) -> str:
allow_empty=True,
)
)
def get_collaborative_companies(self, obj: DataSource) -> List[str]:
def get_cooperation_tenants(self, obj: DataSource) -> List[str]:
# TODO 目前未支持数据源跨租户协作,因此该数据均为空
return []

Expand All @@ -67,7 +58,10 @@ def get_updated_at(self, obj: DataSource) -> str:


class DataSourceFieldMappingSLZ(serializers.Serializer):
"""单个数据源字段映射"""
"""
单个数据源字段映射
FIXME (su) 动态字段实现后,需要检查:target_field 需是租户定义的,source_field 需是插件允许的
"""

source_field = serializers.CharField(help_text="数据源原始字段")
mapping_operation = serializers.ChoiceField(help_text="映射关系", choices=FieldMappingOperation.get_choices())
Expand All @@ -83,6 +77,12 @@ class DataSourceCreateInputSLZ(serializers.Serializer):
help_text="用户字段映射", child=DataSourceFieldMappingSLZ(), allow_empty=True, required=False, default=list
)

def validate_name(self, name: str) -> str:
if DataSource.objects.filter(name=name).exists():
raise ValidationError(_("同名数据源已存在"))

return name

def validate_plugin_id(self, plugin_id: str) -> str:
if not DataSourcePlugin.objects.filter(id=plugin_id).exists():
raise ValidationError(_("数据源插件不存在"))
Expand Down Expand Up @@ -159,158 +159,26 @@ def validate_field_mapping(self, field_mapping: List[Dict]) -> List[Dict]:
return field_mapping


class UserSearchInputSLZ(serializers.Serializer):
username = serializers.CharField(required=False, help_text="用户名", allow_blank=True)


class DataSourceSearchDepartmentsOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="部门ID")
name = serializers.CharField(help_text="部门名称")


class UserSearchOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="用户ID")
username = serializers.CharField(help_text="用户名")
full_name = serializers.CharField(help_text="全名")
phone = serializers.CharField(help_text="手机号")
email = serializers.CharField(help_text="邮箱")
departments = serializers.SerializerMethodField(help_text="用户部门")

# FIXME:考虑抽象一个函数 获取数据后传递到context
@swagger_serializer_method(serializer_or_field=DataSourceSearchDepartmentsOutputSLZ(many=True))
def get_departments(self, obj: DataSourceUser):
return [
{"id": department_user_relation.department.id, "name": department_user_relation.department.name}
for department_user_relation in DataSourceDepartmentUserRelation.objects.filter(user=obj)
]


class UserCreateInputSLZ(serializers.Serializer):
username = serializers.CharField(help_text="用户名", validators=[validate_data_source_user_username])
full_name = serializers.CharField(help_text="姓名")
email = serializers.EmailField(help_text="邮箱")
phone_country_code = serializers.CharField(
help_text="手机号国际区号", required=False, default=settings.DEFAULT_PHONE_COUNTRY_CODE
)
phone = serializers.CharField(help_text="手机号")
logo = serializers.CharField(help_text="用户 Logo", required=False)
department_ids = serializers.ListField(help_text="部门ID列表", child=serializers.IntegerField(), default=[])
leader_ids = serializers.ListField(help_text="上级ID列表", child=serializers.IntegerField(), default=[])

def validate(self, data):
validate_phone_with_country_code(phone=data["phone"], country_code=data["phone_country_code"])
return data

def validate_department_ids(self, department_ids):
diff_department_ids = set(department_ids) - set(
DataSourceDepartment.objects.filter(
id__in=department_ids, data_source=self.context["data_source"]
).values_list("id", flat=True)
)
if diff_department_ids:
raise serializers.ValidationError(_("传递了错误的部门信息: {}").format(diff_department_ids))
return department_ids

def validate_leader_ids(self, leader_ids):
diff_leader_ids = set(leader_ids) - set(
DataSourceUser.objects.filter(id__in=leader_ids, data_source=self.context["data_source"]).values_list(
"id", flat=True
)
)
if diff_leader_ids:
raise serializers.ValidationError(_("传递了错误的上级信息: {}").format(diff_leader_ids))
return leader_ids


class UserCreateOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="数据源用户ID")


class LeaderSearchInputSLZ(serializers.Serializer):
keyword = serializers.CharField(help_text="搜索关键字", required=False)


class LeaderSearchOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="上级ID")
username = serializers.CharField(help_text="上级名称")


class DepartmentSearchInputSLZ(serializers.Serializer):
name = serializers.CharField(required=False, help_text="部门名称", allow_blank=True)
class DataSourceSwitchStatusOutputSLZ(serializers.Serializer):
status = serializers.CharField(help_text="数据源状态")


class DepartmentSearchOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="部门ID")
name = serializers.CharField(help_text="部门名称")
class RawDataSourceUserSLZ(serializers.Serializer):
id = serializers.CharField(help_text="用户 ID")
properties = serializers.JSONField(help_text="用户属性")
leaders = serializers.ListField(help_text="用户 leader ID 列表", child=serializers.CharField())
departments = serializers.ListField(help_text="用户部门 ID 列表", child=serializers.CharField())


class UserDepartmentOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="部门ID")
class RawDataSourceDepartmentSLZ(serializers.Serializer):
id = serializers.CharField(help_text="部门 ID")
name = serializers.CharField(help_text="部门名称")
parent = serializers.CharField(help_text="父部门 ID")


class UserLeaderOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="上级ID")
username = serializers.CharField(help_text="上级用户名")


class UserRetrieveOutputSLZ(serializers.Serializer):
username = serializers.CharField(help_text="用户名")
full_name = serializers.CharField(help_text="全名")
email = serializers.CharField(help_text="邮箱")
phone_country_code = serializers.CharField(help_text="手机区号")
phone = serializers.CharField(help_text="手机号")
logo = serializers.SerializerMethodField(help_text="用户Logo")

departments = serializers.SerializerMethodField(help_text="部门信息")
leaders = serializers.SerializerMethodField(help_text="上级信息")

def get_logo(self, obj: DataSourceUser) -> str:
return obj.logo or settings.DEFAULT_DATA_SOURCE_USER_LOGO
class DataSourceTestConnectionOutputSLZ(serializers.Serializer):
"""数据源连通性测试"""

@swagger_serializer_method(serializer_or_field=UserDepartmentOutputSLZ(many=True))
def get_departments(self, obj: DataSourceUser) -> List[Dict]:
user_departments_map = self.context["user_departments_map"]
departments = user_departments_map.get(obj.id, [])
return [{"id": dept.id, "name": dept.name} for dept in departments]

@swagger_serializer_method(serializer_or_field=UserLeaderOutputSLZ(many=True))
def get_leaders(self, obj: DataSourceUser) -> List[Dict]:
user_leaders_map = self.context["user_leaders_map"]
leaders = user_leaders_map.get(obj.id, [])
return [{"id": leader.id, "username": leader.username} for leader in leaders]


class UserUpdateInputSLZ(serializers.Serializer):
full_name = serializers.CharField(help_text="姓名")
email = serializers.CharField(help_text="邮箱")
phone_country_code = serializers.CharField(help_text="手机国际区号")
phone = serializers.CharField(help_text="手机号")
logo = serializers.CharField(help_text="用户 Logo", allow_blank=True)

department_ids = serializers.ListField(help_text="部门ID列表", child=serializers.IntegerField())
leader_ids = serializers.ListField(help_text="上级ID列表", child=serializers.IntegerField())

def validate(self, data):
validate_phone_with_country_code(phone=data["phone"], country_code=data["phone_country_code"])
return data

def validate_department_ids(self, department_ids):
diff_department_ids = set(department_ids) - set(
DataSourceDepartment.objects.filter(
id__in=department_ids, data_source=self.context["data_source"]
).values_list("id", flat=True)
)
if diff_department_ids:
raise serializers.ValidationError(_("传递了错误的部门信息: {}").format(diff_department_ids))
return department_ids

def validate_leader_ids(self, leader_ids):
diff_leader_ids = set(leader_ids) - set(
DataSourceUser.objects.filter(id__in=leader_ids, data_source=self.context["data_source"]).values_list(
"id", flat=True
)
)
if diff_leader_ids:
raise serializers.ValidationError(_("传递了错误的上级信息: {}").format(diff_leader_ids))
return leader_ids
error_message = serializers.CharField(help_text="错误信息")
user = serializers.CharField(help_text="用户")
department = serializers.CharField(help_text="部门")
35 changes: 22 additions & 13 deletions src/bk-user/bkuser/apis/web/data_source/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,28 @@
path("", views.DataSourceListCreateApi.as_view(), name="data_source.list_create"),
# 数据源更新/获取
path("<int:id>/", views.DataSourceRetrieveUpdateApi.as_view(), name="data_source.retrieve_update"),
# 通用数据源连通性测试
# 数据源启/停
path(
"<int:id>/connectivity/",
views.DataSourceConnectivityApi.as_view(),
name="data_source.connectivity_test",
"<int:id>/operations/switch_status/",
views.DataSourceSwitchStatusApi.as_view(),
name="data_source.switch_status",
),
# 数据源用户
path("<int:id>/users/", views.DataSourceUserListCreateApi.as_view(), name="data_source_user.list_create"),
# 本地数据源用户导入导出
path("<int:id>/users/io/", views.DataSourceUserImportExportApi.as_view(), name="data_source_user.import_export"),
# 数据源用户 Leader
path("<int:id>/leaders/", views.DataSourceLeadersListApi.as_view(), name="data_source_leaders.list"),
# 数据源部门
path("<int:id>/departments/", views.DataSourceDepartmentsListApi.as_view(), name="data_source_departments.list"),
path("user/<int:id>/", views.DataSourceUserRetrieveUpdateApi.as_view(), name="data_source_user.retrieve_update"),
# 连通性测试
path(
"<int:id>/operations/test_connection/",
views.DataSourceTestConnectionApi.as_view(),
name="data_source.test_connection",
),
# 获取用户信息导入模板
path(
"<int:id>/operations/download_template/",
views.DataSourceTemplateApi.as_view(),
name="data_source.download_template",
),
# 导出数据源用户数据
path("<int:id>/operations/export/", views.DataSourceExportApi.as_view(), name="data_source.export_data"),
# 数据源导入
path("<int:id>/operations/import/", views.DataSourceImportApi.as_view(), name="data_source.import_from_excel"),
# 手动触发数据源同步
path("<int:id>/operations/sync/", views.DataSourceSyncApi.as_view(), name="data_source.sync"),
]
Loading

0 comments on commit e8b8213

Please sign in to comment.