Skip to content

Commit

Permalink
Merge branch 'ft_tenant' of https://github.com/Canway-shiisa/bk-user
Browse files Browse the repository at this point in the history
…into ft_tenant_create_ds_user

# Conflicts:
#	src/bk-user/bkuser/apis/web/urls.py
  • Loading branch information
Canway-shiisa committed Aug 23, 2023
2 parents 261ef73 + 608114a commit bc38f8a
Show file tree
Hide file tree
Showing 68 changed files with 2,057 additions and 671 deletions.
10 changes: 10 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
46 changes: 46 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import logging

from django.conf import settings
from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers

from bkuser.apps.tenant.models import Tenant

logger = logging.getLogger(__name__)


class TenantDepartmentOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="租户部门ID")
name = serializers.CharField(help_text="部门名称")
has_children = serializers.BooleanField(help_text="是否有子部门")


class TenantListOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="租户ID")
name = serializers.CharField(help_text="租户名称")
logo = serializers.SerializerMethodField(help_text="租户 Logo")
departments = serializers.SerializerMethodField(help_text="租户下每个数据源的根组织")

def get_logo(self, instance: Tenant) -> str:
return instance.logo or settings.DEFAULT_TENANT_LOGO

@swagger_serializer_method(serializer_or_field=TenantDepartmentOutputSLZ(many=True))
def get_departments(self, instance: Tenant):
departments = self.context["tenant_root_departments_map"].get(instance.id) or []
return [item.model_dump(include={"id", "name", "has_children"}) for item in departments]


class TenantDepartmentChildrenListOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="租户部门ID")
name = serializers.CharField(help_text="部门名称")
has_children = serializers.BooleanField(help_text="是否有子部门")
24 changes: 24 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from django.urls import path

from . import views

urlpatterns = [
# 租户
path("tenants/", views.TenantListApi.as_view(), name="organization.tenant.list"),
path("tenants/<str:id>/", views.TenantRetrieveUpdateApi.as_view(), name="organization.tenant.retrieve_update"),
path(
"departments/<int:id>/children/",
views.TenantDepartmentChildrenListApi.as_view(),
name="organization.children.list",
),
]
110 changes: 110 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import logging

from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status
from rest_framework.response import Response

from bkuser.apis.web.organization.serializers import TenantDepartmentChildrenListOutputSLZ, TenantListOutputSLZ
from bkuser.apis.web.tenant.serializers import TenantRetrieveOutputSLZ, TenantUpdateInputSLZ
from bkuser.apps.tenant.models import Tenant
from bkuser.biz.tenant import (
TenantDepartmentHandler,
TenantEditableBaseInfo,
TenantFeatureFlag,
TenantHandler,
)
from bkuser.common.error_codes import error_codes
from bkuser.common.views import ExcludePatchAPIViewMixin

logger = logging.getLogger(__name__)


class TenantListApi(generics.ListAPIView):
pagination_class = None
queryset = Tenant.objects.all()
serializer_class = TenantListOutputSLZ

def _get_tenant_id(self) -> str:
return self.request.user.get_property("tenant_id")

def get_serializer_context(self):
tenant_ids = list(self.queryset.values_list("id", flat=True))
tenant_root_departments_map = TenantDepartmentHandler.get_tenant_root_department_map_by_tenant_id(
tenant_ids, self._get_tenant_id()
)
return {"tenant_root_departments_map": tenant_root_departments_map}

@swagger_auto_schema(
operation_description="租户列表",
responses={status.HTTP_200_OK: TenantListOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)


class TenantRetrieveUpdateApi(ExcludePatchAPIViewMixin, generics.RetrieveUpdateAPIView):
queryset = Tenant.objects.all()
pagination_class = None
serializer_class = TenantRetrieveOutputSLZ
lookup_url_kwarg = "id"

def _get_tenant_id(self) -> str:
return self.request.user.get_property("tenant_id")

def get_serializer_context(self):
return {"current_tenant_id": self._get_tenant_id()}

@swagger_auto_schema(
operation_description="单个租户详情",
responses={status.HTTP_200_OK: TenantRetrieveOutputSLZ()},
)
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)

@swagger_auto_schema(
operation_description="更新租户",
request_body=TenantUpdateInputSLZ(),
responses={status.HTTP_200_OK: ""},
)
def put(self, request, *args, **kwargs):
slz = TenantUpdateInputSLZ(data=request.data)
slz.is_valid(raise_exception=True)
data = slz.validated_data

instance = self.get_object()
# NOTE 因协同数据源而展示的租户,非当前租户, 无权限做更新操作
if self._get_tenant_id() != instance.id:
raise error_codes.NO_PERMISSION

should_updated_info = TenantEditableBaseInfo(
name=data["name"], logo=data["logo"] or "", feature_flags=TenantFeatureFlag(**data["feature_flags"])
)

TenantHandler.update_with_managers(instance.id, should_updated_info, data["manager_ids"])
return Response()


class TenantDepartmentChildrenListApi(generics.ListAPIView):
pagination_class = None
serializer_class = TenantDepartmentChildrenListOutputSLZ

@swagger_auto_schema(
operation_description="租户部门的二级子部门列表",
responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
tenant_department_id = self.kwargs["id"]
# 拉取子部门信息列表
tenant_department_children = TenantDepartmentHandler.get_tenant_department_children_by_id(tenant_department_id)
data = [item.model_dump(include={"id", "name", "has_children"}) for item in tenant_department_children]
return Response(TenantDepartmentChildrenListOutputSLZ(data, many=True).data)
20 changes: 9 additions & 11 deletions src/bk-user/bkuser/apis/web/tenant/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from bkuser.apps.data_source.models import DataSourceUser
from bkuser.apps.tenant.models import Tenant, TenantUser
from bkuser.biz.data_source import DataSourceSimpleInfo
from bkuser.biz.tenant import TenantUserWithInheritedInfo
from bkuser.biz.tenant import TenantHandler, TenantUserWithInheritedInfo
from bkuser.biz.validators import validate_tenant_id


Expand Down Expand Up @@ -100,7 +100,7 @@ def get_data_sources(self, obj: Tenant) -> List[Dict]:

class TenantUpdateInputSLZ(serializers.Serializer):
name = serializers.CharField(help_text="租户名称")
logo = serializers.CharField(help_text="租户 Logo", required=False)
logo = serializers.CharField(help_text="租户 Logo", required=False, default=settings.DEFAULT_TENANT_LOGO)
manager_ids = serializers.ListField(child=serializers.CharField(), help_text="租户用户 ID 列表", allow_empty=False)
feature_flags = TenantFeatureFlagSLZ(help_text="租户特性集")

Expand All @@ -125,16 +125,14 @@ class TenantRetrieveOutputSLZ(serializers.Serializer):

@swagger_serializer_method(serializer_or_field=TenantRetrieveManagerOutputSLZ(many=True))
def get_managers(self, obj: Tenant) -> List[Dict]:
tenant_manager_map: Dict[str, List[TenantUserWithInheritedInfo]] = self.context["tenant_manager_map"]
managers = tenant_manager_map.get(obj.id) or []
# 根据当前登录的租户用户,获取租户ID
# NOTE 因协同数据源而展示的租户,不返回管理员
if obj.id != self.context["current_tenant_id"]:
return []
managers = TenantHandler.retrieve_tenant_managers(obj.id)
return [
{
"id": i.id,
**i.data_source_user.model_dump(
include={"username", "full_name", "email", "phone", "phone_country_code"}
),
}
for i in managers
{"id": manager.id, **manager.data_source_user.model_dump(include={"username", "full_name"})}
for manager in managers
]

def get_logo(self, obj: Tenant) -> str:
Expand Down
1 change: 1 addition & 0 deletions src/bk-user/bkuser/apis/web/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
# 租户
path("tenants/", include("bkuser.apis.web.tenant.urls")),
path("data-sources/", include("bkuser.apis.web.data_source.urls")),
path("tenant-organization/", include("bkuser.apis.web.organization.urls")),
]
29 changes: 28 additions & 1 deletion src/bk-user/bkuser/biz/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@

from pydantic import BaseModel

from bkuser.apps.data_source.models import DataSource
from bkuser.apps.data_source.models import DataSource, DataSourceDepartment, DataSourceDepartmentRelation


class DataSourceDepartmentInfoWithChildren(BaseModel):
id: int
name: str
children_ids: List[int]


class DataSourceSimpleInfo(BaseModel):
Expand All @@ -38,3 +44,24 @@ def get_data_source_map_by_owner(
data[i.owner_tenant_id].append(DataSourceSimpleInfo(id=i.id, name=i.name))

return data


class DataSourceDepartmentHandler:
@staticmethod
def get_department_info_map_by_id(department_ids: List[int]) -> Dict[int, DataSourceDepartmentInfoWithChildren]:
"""
获取部门基础信息
"""
departments = DataSourceDepartment.objects.filter(id__in=department_ids)
departments_map: Dict = {}
for item in departments:
departments_map[item.id] = DataSourceDepartmentInfoWithChildren(
id=item.id,
name=item.name,
children_ids=list(
DataSourceDepartmentRelation.objects.get(department=item)
.get_children()
.values_list("department_id", flat=True)
),
)
return departments_map
Loading

0 comments on commit bc38f8a

Please sign in to comment.