From 7d1c1020e5d3b2f8548d6d6cab343957ed840418 Mon Sep 17 00:00:00 2001 From: nannan00 <17491932+nannan00@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:33:39 +0800 Subject: [PATCH] feat(open api): apply policy support custom ticket (#2685) (#2686) * feat(open api): apply policy support custom ticket (#2686 * fix: mypy (#2688) --- saas/.pre-commit-config.yaml | 4 +- saas/__init__.py | 10 -- saas/backend/api/application/serializers.py | 96 +++++++++++++++++++ saas/backend/api/application/urls.py | 5 + saas/backend/api/application/views.py | 42 ++++++++ saas/backend/biz/action.py | 2 +- saas/backend/biz/application.py | 32 ++++++- saas/backend/biz/policy.py | 8 +- saas/backend/biz/related_policy.py | 2 +- saas/backend/biz/subject_template.py | 10 +- saas/backend/component/iam.py | 6 +- .../plugins/application_ticket/base.py | 7 +- .../plugins/application_ticket/itsm/itsm.py | 25 +++-- saas/backend/service/application.py | 17 +++- saas/backend/service/group.py | 1 + saas/backend/service/models/subject.py | 2 +- saas/backend/trans/application.py | 2 +- saas/backend/trans/open_application.py | 62 +++++++++++- saas/pyproject.toml | 6 ++ saas/tests/biz/policy_tests.py | 16 ++-- saas/tests/service/models/policy_tests.py | 2 +- 21 files changed, 309 insertions(+), 48 deletions(-) delete mode 100644 saas/__init__.py diff --git a/saas/.pre-commit-config.yaml b/saas/.pre-commit-config.yaml index ed88182fc..1279fd612 100644 --- a/saas/.pre-commit-config.yaml +++ b/saas/.pre-commit-config.yaml @@ -38,9 +38,9 @@ repos: entry: mypy --config-file=saas/pyproject.toml saas - id: import-linter name: import-linter - language: python + language: system pass_filenames: false - entry: cd saas && lint-imports --config=./.importlinter && cd .. + entry: bash -c "cd saas && lint-imports --config=.importlinter" - id: pytest name: pytest language: python diff --git a/saas/__init__.py b/saas/__init__.py deleted file mode 100644 index 5d3baadf7..000000000 --- a/saas/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) 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. -""" diff --git a/saas/backend/api/application/serializers.py b/saas/backend/api/application/serializers.py index 0f3ca718d..4c668b92f 100644 --- a/saas/backend/api/application/serializers.py +++ b/saas/backend/api/application/serializers.py @@ -137,3 +137,99 @@ class ApprovalBotRoleCallbackSLZ(serializers.Serializer): expired_at_before = serializers.IntegerField(label="过期时间") expired_at_after = serializers.IntegerField(label="过期时间") month = serializers.IntegerField(label="续期月数") + + +class ASResourceTypeWithCustomTicketSLZ(serializers.Serializer): + """ + 接入系统申请操作的资源类型 + """ + + system = serializers.CharField(label="系统ID") + type = serializers.CharField(label="资源类型") + instance = serializers.ListField( + label="资源拓扑", child=ASInstanceSLZ(label="资源实例"), required=False, allow_empty=True, default=list + ) + attributes = serializers.ListField( + label="属性", default=list, required=False, child=AttributeSLZ(label="属性"), allow_empty=True + ) + + +class ASActionWithCustomTicketSLZ(serializers.Serializer): + id = serializers.CharField(label="操作ID") + related_resource_types = serializers.ListField( + label="关联资源类型", child=ASResourceTypeWithCustomTicketSLZ(label="资源类型"), allow_empty=True, default=list + ) + + ticket_content = serializers.DictField(label="单条权限的审批单内容", required=False, allow_empty=True, default=dict) + + +class ASApplicationCustomPolicyWithCustomTicketSLZ(serializers.Serializer): + """接入系统自定义权限申请单据创建""" + + applicant = serializers.CharField(label="申请者的用户名", max_length=32) + reason = serializers.CharField(label="申请理由", max_length=255) + expired_at = serializers.IntegerField( + label="过期时间", required=False, default=0, min_value=0, max_value=PERMANENT_SECONDS + ) + + ticket_title_prefix = serializers.CharField(label="审批单标题前缀", required=False, allow_blank=True, default="") + ticket_content_template = serializers.DictField(label="审批单内容模板", required=False, allow_empty=True, default=dict) + + system = serializers.CharField(label="系统ID") + actions = serializers.ListField(label="申请操作", child=ASActionWithCustomTicketSLZ(label="操作"), allow_empty=False) + + def validate_expired_at(self, value): + """ + 验证过期时间 + """ + if 0 < value <= (time.time()): + raise serializers.ValidationError("greater than now timestamp") + return value + + def validate(self, data): + # 自定义 ITSM 单据展示内容 + content_template = data["ticket_content_template"] + if content_template: + # 必须满足 ITSM 的单据数据结构 + if "schemes" not in content_template or "form_data" not in content_template: + raise serializers.ValidationError( + {"ticket_content_template": ["ticket_content_template 中必须包含 schemes 和 form_data "]} + ) + + if not isinstance(content_template["form_data"], list) or len(content_template["form_data"]) == 0: + raise serializers.ValidationError( + {"ticket_content_template": ["ticket_content_template 中必须包含 form_data,且 form_data 必须为非空数组"]} + ) + + # IAM 所需的策略 Form (索引) + policy_forms = [ + i + for i in content_template["form_data"] + if isinstance(i, dict) + and i.get("scheme") == "policy_table_scheme" + and isinstance(i.get("value"), list) + ] + if len(policy_forms) != 1: + raise serializers.ValidationError( + { + "ticket_content_template": [ + "ticket_content_template['form_data'] 必须" + "包含 IAM 指定 scheme 为 iam_policy_table_scheme 且 value 为列表的项," + ] + }, + ) + # 必须每条权限都有配置单据所需渲染内容 + empty_ticket_content_actions = [ + str(ind + 1) for ind, a in enumerate(data["actions"]) if not a["ticket_content"] + ] + if len(empty_ticket_content_actions) > 0: + raise serializers.ValidationError( + { + "actions": [ + f"当 ticket_content_template 不为空时,所有权限的 ticket_content 都必须非空,当前请求中," + f"第 {','.join(empty_ticket_content_actions)} 条权限的 ticket_content 为空" + ] + } + ) + + return data diff --git a/saas/backend/api/application/urls.py b/saas/backend/api/application/urls.py index 669049210..a65458e62 100644 --- a/saas/backend/api/application/urls.py +++ b/saas/backend/api/application/urls.py @@ -18,4 +18,9 @@ path("approval_bot/user/", views.ApprovalBotUserCallbackView.as_view(), name="open.approval_bot_user"), path("approval_bot/role/", views.ApprovalBotRoleCallbackView.as_view(), name="open.approval_bot_role"), path("/", views.ApplicationDetailView.as_view({"get": "retrieve"}), name="open.application_detail"), + path( + "policies/with-custom-ticket/", + views.ApplicationCustomPolicyWithCustomTicketView.as_view(), + name="open.application_policy_with_custom_ticket", + ), ] diff --git a/saas/backend/api/application/views.py b/saas/backend/api/application/views.py index dceb6bb1b..ff9d76ac9 100644 --- a/saas/backend/api/application/views.py +++ b/saas/backend/api/application/views.py @@ -45,6 +45,7 @@ AccessSystemApplicationUrlSLZ, ApprovalBotRoleCallbackSLZ, ApprovalBotUserCallbackSLZ, + ASApplicationCustomPolicyWithCustomTicketSLZ, ) @@ -283,3 +284,44 @@ def post(self, request): ) return Response({}) + + +class ApplicationCustomPolicyWithCustomTicketView(views.APIView): + """ + 创建自定义权限申请单 - 允许单据自定义审批内容 + """ + + authentication_classes = [ESBAuthentication] + permission_classes = [IsAuthenticated] + + access_system_application_trans = AccessSystemApplicationTrans() + application_biz = ApplicationBiz() + + @swagger_auto_schema( + operation_description="创建自定义权限申请单-允许单据自定义审批内容", + request_body=ASApplicationCustomPolicyWithCustomTicketSLZ(), + responses={status.HTTP_200_OK: AccessSystemApplicationCustomPolicyResultSLZ(label="申请单信息", many=True)}, + tags=["open"], + ) + def post(self, request): + serializer = ASApplicationCustomPolicyWithCustomTicketSLZ(data=request.data) + serializer.is_valid(raise_exception=True) + + data = serializer.validated_data + username = data["applicant"] + + # 将Dict数据转换为创建单据所需的数据结构 + ( + application_data, + policy_ticket_contents, + ) = self.access_system_application_trans.from_grant_policy_with_custom_ticket_application(username, data) + # 创建单据 + applications = self.application_biz.create_for_policy( + ApplicationType.GRANT_ACTION.value, + application_data, + data["ticket_content_template"] or None, + policy_ticket_contents, + data["ticket_title_prefix"], + ) + + return Response(AccessSystemApplicationCustomPolicyResultSLZ(applications, many=True).data) diff --git a/saas/backend/biz/action.py b/saas/backend/biz/action.py index 718dd5a95..bb64c66dc 100644 --- a/saas/backend/biz/action.py +++ b/saas/backend/biz/action.py @@ -195,7 +195,7 @@ def list_by_subject(self, system_id: str, role, subject: Subject, hidden: bool = action_list = ActionBeanList(actions) if hidden: - action_list = ActionList(action_list.list_not_hidden()) + action_list = ActionBeanList(action_list.list_not_hidden()) policies = self.policy_svc.list_by_subject(system_id, subject) action_expired_at = {policy.action_id: policy.expired_at for policy in policies} diff --git a/saas/backend/biz/application.py b/saas/backend/biz/application.py index 3ebcd45f8..f79d5d771 100644 --- a/saas/backend/biz/application.py +++ b/saas/backend/biz/application.py @@ -489,7 +489,12 @@ def _gen_application_system(self, system_id: str) -> ApplicationSystem: return parse_obj_as(ApplicationSystem, system) def create_for_policy( - self, application_type: ApplicationType, data: ActionApplicationDataBean + self, + application_type: ApplicationType, + data: ActionApplicationDataBean, + content_template: Optional[Dict[str, Any]] = None, + policy_contents: Optional[List[Tuple[PolicyBean, Any]]] = None, + title_prefix: str = "", ) -> List[Application]: """自定义权限""" # 1. 提前查询部分信息 @@ -540,12 +545,31 @@ def create_for_policy( applicants=data.applicants, ), ) - new_data_list.append((application_data, process)) + + # 组装外部传入的 itsm 单据数据 + content: Optional[Dict[str, Any]] = None + if content_template and policy_contents: + content = deepcopy(content_template) + policy_form_value = [c for p, c in policy_contents if policy_list.contains_policy(p)] + for c in content["form_data"]: + if ( + isinstance(c, dict) + and c.get("scheme") == "policy_table_scheme" + and isinstance(c.get("value"), list) + ): + c["value"] = policy_form_value + + new_data_list.append((application_data, process, content)) # 8. 循环创建申请单 applications = [] - for _data, _process in new_data_list: - application = self.svc.create_for_policy(_data, _process) + for _data, _process, _content in new_data_list: + application = self.svc.create_for_policy( + _data, + _process, + approval_content=_content, + approval_title_prefix=title_prefix, + ) applications.append(application) return applications diff --git a/saas/backend/biz/policy.py b/saas/backend/biz/policy.py index f4b0b483d..91e7e393f 100644 --- a/saas/backend/biz/policy.py +++ b/saas/backend/biz/policy.py @@ -1279,6 +1279,12 @@ def sub(self, policy_list: "PolicyBeanList") -> "PolicyBeanList": pass return PolicyBeanList(self.system_id, subtraction) + def contains_policy(self, policy: PolicyBean): + """是否包含策略""" + p = self.get(policy.action_id) + + return p and p.has_resource_group_list(policy.resource_groups) + def to_svc_policies(self): return parse_obj_as(List[Policy], self.policies) @@ -1471,7 +1477,7 @@ def list_expired(self, subject: Subject, expired_at: int) -> List[ExpiredPolicy] # 查询saas policy id all_action_id = {p.action_id for p in backend_policies} - action_id_dict = self.svc.get_action_id_dict(subject, all_action_id) + action_id_dict = self.svc.get_action_id_dict(subject, list(all_action_id)) # 取策略详情 system_ids = defaultdict(list) diff --git a/saas/backend/biz/related_policy.py b/saas/backend/biz/related_policy.py index 601cfa013..05520f23d 100644 --- a/saas/backend/biz/related_policy.py +++ b/saas/backend/biz/related_policy.py @@ -321,7 +321,7 @@ def _check_path_by_instance_selection( 不同类型的实例视图匹配使用前缀匹配 """ - path = list(path.copy()) + path = list(path.copy()) # type: ignore for selection in instance_selections: # 资源类型不同时, 截取视图长度的拓扑 if len(path) > len(selection.resource_type_chain): diff --git a/saas/backend/biz/subject_template.py b/saas/backend/biz/subject_template.py index 0e6308f81..9e57147ce 100644 --- a/saas/backend/biz/subject_template.py +++ b/saas/backend/biz/subject_template.py @@ -272,7 +272,7 @@ def get_subject_template_group_count( params = [subject.type, subject.id] if id: where_conditions.append("a.id = %s") - params.append(id) + params.append(id) # type: ignore if name: where_conditions.append("a.name LIKE %s") params.append("%" + name + "%") @@ -283,7 +283,7 @@ def get_subject_template_group_count( where_conditions.append("a.hidden = 0") if group_ids: where_conditions.append("a.id IN %s") - params.append(tuple(group_ids)) + params.append(tuple(group_ids)) # type: ignore if system_id: where_conditions.append("a.source_system_id = %s") params.append(system_id) @@ -397,7 +397,7 @@ def list_paging_subject_template_group( params = [subject.type, subject.id] if id: where_conditions.append("a.id = %s") - params.append(id) + params.append(id) # type: ignore if name: where_conditions.append("a.name LIKE %s") params.append("%" + name + "%") @@ -408,12 +408,12 @@ def list_paging_subject_template_group( where_conditions.append("a.hidden = 0") if group_ids: where_conditions.append("a.id IN %s") - params.append(tuple(group_ids)) + params.append(tuple(group_ids)) # type: ignore if system_id: where_conditions.append("a.source_system_id = %s") params.append(system_id) - params.extend([limit, offset]) + params.extend([limit, offset]) # type: ignore sql_query = """ SELECT diff --git a/saas/backend/component/iam.py b/saas/backend/component/iam.py index bac7ebbc3..6ba901d88 100644 --- a/saas/backend/component/iam.py +++ b/saas/backend/component/iam.py @@ -8,7 +8,7 @@ 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 typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union from django.conf import settings @@ -478,7 +478,9 @@ def list_exist_subjects_before_expired_at(subjects: List[Dict], expired_at: int) return _call_iam_api(http_post, url_path, data=data) -def list_group_subject_before_expired_at(expired_at: int, limit: int = 10, offset: int = 0) -> List: +def list_group_subject_before_expired_at( + expired_at: int, limit: int = 10, offset: int = 0 +) -> Dict[str, Union[List, int]]: """ 查询已过期的关系 """ diff --git a/saas/backend/plugins/application_ticket/base.py b/saas/backend/plugins/application_ticket/base.py index 71a7181fb..d49628020 100644 --- a/saas/backend/plugins/application_ticket/base.py +++ b/saas/backend/plugins/application_ticket/base.py @@ -37,7 +37,12 @@ def get_ticket(self, sn: str) -> ApplicationTicket: @abc.abstractmethod def create_for_policy( - self, data: GrantActionApplicationData, process: ApprovalProcessWithNodeProcessor, callback_url: str + self, + data: GrantActionApplicationData, + process: ApprovalProcessWithNodeProcessor, + callback_url: str, + approval_title_prefix: str = "", + approval_content: Optional[Dict] = None, ) -> str: """创建 - 申请或续期自定义权限单据""" pass diff --git a/saas/backend/plugins/application_ticket/itsm/itsm.py b/saas/backend/plugins/application_ticket/itsm/itsm.py index 3feabe23e..2f094ce8e 100644 --- a/saas/backend/plugins/application_ticket/itsm/itsm.py +++ b/saas/backend/plugins/application_ticket/itsm/itsm.py @@ -75,15 +75,28 @@ def _generate_ticket_common_params( } def create_for_policy( - self, data: GrantActionApplicationData, process: ApprovalProcessWithNodeProcessor, callback_url: str + self, + data: GrantActionApplicationData, + process: ApprovalProcessWithNodeProcessor, + callback_url: str, + approval_title_prefix: str = "", + approval_content: Optional[Dict] = None, ) -> str: """创建 - 申请或续期自定义权限单据""" params = self._generate_ticket_common_params(data, process, callback_url) - params["title"] = f"申请{data.content.system.name}{len(data.content.policies)}个操作权限" - params["content"] = { - "schemes": FORM_SCHEMES, - "form_data": [ActionTable.from_application(data.content).dict()], - } # 真正生成申请内容的核心入口点 + + if approval_title_prefix: + params["title"] = f"{approval_title_prefix} {len(data.content.policies)} 个操作权限" + else: + params["title"] = f"申请{data.content.system.name}{len(data.content.policies)}个操作权限" + + if approval_content: + params["content"] = approval_content + else: + params["content"] = { + "schemes": FORM_SCHEMES, + "form_data": [ActionTable.from_application(data.content).dict()], + } # 真正生成申请内容的核心入口点 # 如果审批流程中包含资源审批人, 并且资源审批人不为空 # 增加 has_instance_approver 字段, 用于itsm审批流程走分支 diff --git a/saas/backend/service/application.py b/saas/backend/service/application.py index c9c35885d..379f68227 100644 --- a/saas/backend/service/application.py +++ b/saas/backend/service/application.py @@ -83,10 +83,23 @@ def _create( return application def create_for_policy( - self, data: GrantActionApplicationData, process: ApprovalProcessWithNodeProcessor + self, + data: GrantActionApplicationData, + process: ApprovalProcessWithNodeProcessor, + approval_title_prefix: str = "", + approval_content: Optional[Dict] = None, ) -> Application: """创建或续期自定义权限申请单""" - return self._create(data, lambda callback_url: self.provider.create_for_policy(data, process, callback_url)) + return self._create( + data, + lambda callback_url: self.provider.create_for_policy( + data, + process, + callback_url, + approval_title_prefix=approval_title_prefix, + approval_content=approval_content, + ), + ) def create_for_group( self, diff --git a/saas/backend/service/group.py b/saas/backend/service/group.py index 06b2d97c1..9739b6085 100644 --- a/saas/backend/service/group.py +++ b/saas/backend/service/group.py @@ -377,6 +377,7 @@ def list_group_subject_before_expired_at(self, expired_at: int) -> Generator[Gro limit, offset = 1000, 0 while True: iam_data = iam.list_group_subject_before_expired_at(expired_at=expired_at, limit=limit, offset=offset) + assert isinstance(iam_data["results"], list) if len(iam_data["results"]) == 0: break diff --git a/saas/backend/service/models/subject.py b/saas/backend/service/models/subject.py index 3ae27fad6..23e5f0374 100644 --- a/saas/backend/service/models/subject.py +++ b/saas/backend/service/models/subject.py @@ -38,7 +38,7 @@ def from_department_id(cls, department_id: Union[int, str]) -> "Subject": return cls(type=SubjectType.DEPARTMENT.value, id=str(department_id)) @classmethod - def from_usernames(cls, usernames: str) -> List["Subject"]: + def from_usernames(cls, usernames: List[str]) -> List["Subject"]: return [cls.from_username(username) for username in usernames] diff --git a/saas/backend/trans/application.py b/saas/backend/trans/application.py index cf5a26368..228887728 100644 --- a/saas/backend/trans/application.py +++ b/saas/backend/trans/application.py @@ -242,7 +242,7 @@ def from_grant_temporary_policy_application(self, applicant: str, data: Dict) -> return application_data - def _check_temporary_policy(self, applicant: str, system_id: str, policy_list: PolicyBeanList) -> PolicyBeanList: + def _check_temporary_policy(self, applicant: str, system_id: str, policy_list: PolicyBeanList): """检查临时权限""" # NOTE: 临时权限的申请过期时间限制由前端处理 diff --git a/saas/backend/trans/open_application.py b/saas/backend/trans/open_application.py index 9cfd31d35..ca8b69a9b 100644 --- a/saas/backend/trans/open_application.py +++ b/saas/backend/trans/open_application.py @@ -8,13 +8,14 @@ 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 typing import Dict, List +from copy import deepcopy +from typing import Any, Dict, List, Optional, Tuple from pydantic.tools import parse_obj_as from backend.apps.organization.models import User as UserModel from backend.biz.application import ActionApplicationDataBean -from backend.biz.policy import PolicyBeanList +from backend.biz.policy import PolicyBean, PolicyBeanList from backend.service.constants import SubjectType from backend.service.models import Applicant from backend.trans.application import ApplicationDataTrans @@ -107,3 +108,60 @@ def from_grant_policy_application(self, applicant: str, data: Dict) -> ActionApp ) return application_data + + def from_grant_policy_with_custom_ticket_application( + self, applicant: str, data: Dict + ) -> Tuple[ActionApplicationDataBean, Optional[List[Tuple[PolicyBean, Any]]]]: + """来着带自定义审批内容的自定义权限申请的数据转换""" + + # 1. 将多条策略按 Action 合并为标准的策略数据 + policy_list = PolicyBeanList(system_id=data["system"], policies=[]) + policy_ticket_contents = [] + for action in data["actions"]: + one_policy_data = { + "system": data["system"], + "actions": [ + { + "id": action["id"], + "related_resource_types": [ + { + "system": rrt["system"], + "type": rrt["type"], + # 实际上是将单个实例转换为标准的多实例结构 + "instances": [rrt["instance"]] if "instance" in rrt else [], + "attributes": rrt.get("attributes", []), + } + for rrt in action["related_resource_types"] + ], + } + ], + } + if "expired_at" in data: + one_policy_data["expired_at"] = data["expired_at"] + + # 转换数据结构 + one_policy_list = self.to_policy_list(one_policy_data) + + # 每条原始策略(未合并) 对应的审批单据内容 + if data["ticket_content_template"]: + # Note: 这里必须使用 deepcopy, 且需要 one_policy_list 被添加到 policy_list 前, + # 否则其实 one_policy_list 会跟随着 policy_list 变化,获取的数据就不正确 + policy_ticket_contents.append((deepcopy(one_policy_list.policies[0]), action["ticket_content"])) + + # 添加 + policy_list.add(one_policy_list) + + # 2. 只对新增的策略进行申请,所以需要移除掉已有的权限 + application_policy_list = self._gen_need_apply_policy_list(applicant, data["system"], policy_list) + + # 3. 转换为ApplicationBiz创建申请单所需数据结构 + user = UserModel.objects.get(username=applicant) + + application_data = ActionApplicationDataBean( + applicant=applicant, + policy_list=application_policy_list, + applicants=[Applicant(type=SubjectType.USER.value, id=user.username, display_name=user.display_name)], + reason=data["reason"], + ) + + return application_data, policy_ticket_contents or None diff --git a/saas/pyproject.toml b/saas/pyproject.toml index fd4e841bc..7b6768b4a 100644 --- a/saas/pyproject.toml +++ b/saas/pyproject.toml @@ -42,6 +42,12 @@ follow_imports="skip" strict_optional=true pretty=true show_error_codes=true +exclude='''(?x)( + ^.*/config/.*| + .*/settings\.py| + .*/tests/unittest_settings\.py| + .*/backend/tracing/.* +)''' [[tool.mypy.overrides]] module = [ diff --git a/saas/tests/biz/policy_tests.py b/saas/tests/biz/policy_tests.py index 05b3ebdf0..29672e865 100644 --- a/saas/tests/biz/policy_tests.py +++ b/saas/tests/biz/policy_tests.py @@ -137,7 +137,7 @@ def test_display(self, path_node_bean_list: PathNodeBeanList): def test_match_selection_one_node(self, path_node_bean_list: PathNodeBeanList): path_node_bean_list.__root__.pop() - assert path_node_bean_list.match_selection("system_id", "type", None) + assert path_node_bean_list.match_selection("system_id", "type", None) # type: ignore @pytest.mark.parametrize( "instance_selection, expected", @@ -326,8 +326,8 @@ def instance_bean_list(instance_bean: InstanceBean): class TestInstanceBeanList: def test_get(self, instance_bean_list: InstanceBeanList): - assert instance_bean_list.get("type").type == "type" - assert instance_bean_list.get("test") is None + assert instance_bean_list.get("type").type == "type" # type: ignore + assert instance_bean_list.get("test") is None # type: ignore def test_add(self, instance_bean_list: InstanceBeanList): instance_bean_list1 = InstanceBeanList([instance_bean_list.instances.pop()]) @@ -421,7 +421,7 @@ def test_sub(self, condition_bean_list: ConditionBeanList): assert condition_bean_list.is_empty def test_remove_by_ids(self, condition_bean_list: ConditionBeanList): - condition_bean_list.remove_by_ids(condition_bean_list.conditions[0].id) + condition_bean_list.remove_by_ids([condition_bean_list.conditions[0].id]) assert condition_bean_list.is_empty @@ -1036,7 +1036,7 @@ class TestPolicyBeanListMixin: def test_check_instance_selection( self, policy_bean_list_mixin: PolicyBeanListMixin, instance_selection, raise_exception ): - policy_bean_list_mixin.action_svc.new_action_list = Mock( + policy_bean_list_mixin.action_svc.new_action_list = Mock( # type: ignore return_value=ActionList( [ Action( @@ -1095,7 +1095,7 @@ def test_check_instance_selection( def test_check_resource_name( self, policy_bean_list_mixin: PolicyBeanListMixin, resource_name_dict, raise_exception ): - policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock(return_value=resource_name_dict) + policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock(return_value=resource_name_dict) # type: ignore try: policy_bean_list_mixin.check_resource_name() assert not raise_exception @@ -1106,7 +1106,7 @@ def test_check_instance_count_limit(self, policy_bean_list_mixin: PolicyBeanList assert policy_bean_list_mixin.check_instance_count_limit() is None def test_get_renamed_resources(self, policy_bean_list_mixin: PolicyBeanListMixin): - policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock( + policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock( # type: ignore return_value=ResourceNodeAttributeDictBean( data={ ResourceNodeBean(id="id", system_id="system_id", type="type"): "name", @@ -1150,5 +1150,5 @@ def test_get_renamed_resources(self, policy_bean_list_mixin: PolicyBeanListMixin ], ) def test_auto_update_resource_name(self, policy_bean_list_mixin: PolicyBeanListMixin, resource_name_dict, result): - policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock(return_value=resource_name_dict) + policy_bean_list_mixin.resource_biz.fetch_resource_name = Mock(return_value=resource_name_dict) # type: ignore assert bool(policy_bean_list_mixin.auto_update_resource_name()) == result diff --git a/saas/tests/service/models/policy_tests.py b/saas/tests/service/models/policy_tests.py index ddafa5b01..7e845eb80 100644 --- a/saas/tests/service/models/policy_tests.py +++ b/saas/tests/service/models/policy_tests.py @@ -82,7 +82,7 @@ class TestPathNodeList: def test_match_selection(self, path_node_list: PathNodeList, instance_selection: InstanceSelection): copied_path_node_list = deepcopy(path_node_list) copied_path_node_list.pop(1) - assert copied_path_node_list.match_selection("system_id", "type", None) + assert copied_path_node_list.match_selection("system_id", "type", instance_selection) assert path_node_list.match_selection("system_id", "type", instance_selection) def test_ignore_path(self, path_node_list: PathNodeList, instance_selection: InstanceSelection):