-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat: Batch delete and move tools #4973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1173,6 +1173,65 @@ def process(): | |
|
|
||
| return to_stream_response_simple(process()) | ||
|
|
||
| class ToolBatchOperateSerializer(serializers.Serializer): | ||
| workspace_id = serializers.CharField(required=True, label=_('workspace id')) | ||
|
|
||
| def is_valid(self, *, raise_exception=False): | ||
| super().is_valid(raise_exception=True) | ||
|
|
||
| @transaction.atomic | ||
| def batch_delete(self, instance: Dict, with_valid=True): | ||
| from knowledge.serializers.common import BatchSerializer | ||
| from trigger.handler.simple_tools import deploy | ||
| from trigger.serializers.trigger import TriggerModelSerializer | ||
|
|
||
| if with_valid: | ||
| BatchSerializer(data=instance).is_valid(model=Tool, raise_exception=True) | ||
| self.is_valid(raise_exception=True) | ||
| id_list = instance.get('id_list') | ||
| workspace_id = self.data.get('workspace_id') | ||
|
|
||
| tool_query_set = QuerySet(Tool).filter(id__in=id_list, workspace_id=workspace_id) | ||
|
|
||
| for tool in tool_query_set: | ||
| if tool.template_id is None and tool.icon != '': | ||
| QuerySet(File).filter(id=tool.icon.split('/')[-1]).delete() | ||
| if tool.tool_type == ToolType.SKILL: | ||
| QuerySet(File).filter(id=tool.code).delete() | ||
|
|
||
| QuerySet(WorkspaceUserResourcePermission).filter(target__in=id_list).delete() | ||
| QuerySet(ResourceMapping).filter(target_id__in=id_list).delete() | ||
| QuerySet(ToolRecord).filter(tool_id__in=id_list).delete() | ||
|
|
||
| trigger_ids = list( | ||
| QuerySet(TriggerTask).filter( | ||
| source_type="TOOL", source_id__in=id_list | ||
| ).values('trigger_id').distinct() | ||
| ) | ||
|
|
||
| QuerySet(TriggerTask).filter(source_type="TOOL", source_id__in=id_list).delete() | ||
| for trigger_id in trigger_ids: | ||
| trigger = Trigger.objects.filter(id=trigger_id['trigger_id']).first() | ||
| if trigger and trigger.is_active: | ||
| deploy(TriggerModelSerializer(trigger).data, **{}) | ||
|
|
||
| tool_query_set.delete() | ||
| return True | ||
|
|
||
| def batch_move(self, instance: Dict, with_valid=True): | ||
| from knowledge.serializers.common import BatchMoveSerializer | ||
| if with_valid: | ||
| BatchMoveSerializer(data=instance).is_valid(model=Tool, raise_exception=True) | ||
| self.is_valid(raise_exception=True) | ||
| id_list = instance.get('id_list') | ||
| folder_id = instance.get('folder_id') | ||
| workspace_id = self.data.get('workspace_id') | ||
|
|
||
| QuerySet(Tool).filter(id__in=id_list, workspace_id=workspace_id).update(folder_id=folder_id) | ||
| return True | ||
|
|
||
|
|
||
|
|
||
|
|
||
| class ToolTreeSerializer(serializers.Serializer): | ||
| class Query(serializers.Serializer): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided code seems to be implementing batch operations for tools, including deletion and moving. Here's a review of the code:
Overall, the code is mostly functional with room for improvement in handling edge cases and optimizing performance based on specific requirements and context (e.g., recovery options, user feedback). |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,14 +6,15 @@ | |
| from rest_framework.views import APIView | ||
|
|
||
| from common.auth import TokenAuth | ||
| from common.auth.authentication import has_permissions | ||
| from common.auth.authentication import has_permissions, check_batch_permissions | ||
| from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants | ||
| from common.log.log import log | ||
| from common.result import result | ||
| from common import result | ||
| from tools.api.tool import ToolCreateAPI, ToolEditAPI, ToolReadAPI, ToolDeleteAPI, ToolTreeReadAPI, ToolDebugApi, \ | ||
| ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI, GetInternalToolAPI, AddInternalToolAPI | ||
| ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI, GetInternalToolAPI, AddInternalToolAPI, \ | ||
| ToolBatchOperateAPI | ||
| from tools.models import ToolScope, Tool | ||
| from tools.serializers.tool import ToolSerializer, ToolTreeSerializer | ||
| from tools.serializers.tool import ToolSerializer, ToolTreeSerializer, ToolBatchOperateSerializer | ||
|
|
||
|
|
||
| def get_tool_operation_object(tool_id): | ||
|
|
@@ -24,6 +25,15 @@ def get_tool_operation_object(tool_id): | |
| } | ||
| return {} | ||
|
|
||
| def get_tool_operation_object_batch(tool_id_list): | ||
| tool_model_list = QuerySet(model=Tool).filter(id__in=tool_id_list) | ||
| if tool_model_list is not None: | ||
| return { | ||
| "name": f'[{",".join([t.name for t in tool_model_list])}]', | ||
| 'tool_list': [{'name': t.name} for t in tool_model_list] | ||
| } | ||
| return {} | ||
|
|
||
|
|
||
| class ToolView(APIView): | ||
| authentication_classes = [TokenAuth] | ||
|
|
@@ -186,6 +196,83 @@ def delete(self, request: Request, workspace_id: str, tool_id: str): | |
| data={'id': tool_id, 'workspace_id': workspace_id} | ||
| ).delete()) | ||
|
|
||
| class BatchDelete(APIView): | ||
| authentication_classes = [TokenAuth] | ||
|
|
||
| @extend_schema( | ||
| methods=['PUT'], | ||
| description=_("Batch delete tools"), | ||
| summary=_("Batch delete tools"), | ||
| operation_id=_("Batch delete tools"), | ||
| parameters=ToolBatchOperateAPI.get_parameters(), | ||
| request=ToolBatchOperateAPI.get_request(), | ||
| responses=result.DefaultResultSerializer, | ||
| tags=[_('Tool')] | ||
| ) | ||
| @has_permissions(PermissionConstants.TOOL_BATCH_DELETE.get_workspace_permission(), | ||
| RoleConstants.USER.get_workspace_role(), | ||
| RoleConstants.WORKSPACE_MANAGE.get_workspace_role() | ||
| ) | ||
| def put(self, request: Request, workspace_id: str): | ||
| id_list = request.data.get('id_list', []) | ||
| permitted_ids = check_batch_permissions( | ||
| request, id_list, 'tool_id', | ||
| (PermissionConstants.TOOL_DELETE.get_workspace_tool_permission(), | ||
| PermissionConstants.TOOL_DELETE.get_workspace_permission_workspace_manage_role(), | ||
| ViewPermission([RoleConstants.USER.get_workspace_role()], | ||
| [PermissionConstants.TOOL.get_workspace_tool_permission()], | ||
| CompareConstants.AND), | ||
| RoleConstants.WORKSPACE_MANAGE.get_workspace_role()), workspace_id=workspace_id | ||
| ) | ||
|
|
||
| @log(menu='Tool', operate='Batch delete tools', | ||
| get_operation_object=lambda r, k: get_tool_operation_object_batch(permitted_ids)) | ||
| def inner(view, r, **kwargs): | ||
| return ToolBatchOperateSerializer( | ||
| data={'workspace_id': workspace_id} | ||
| ).batch_delete({'id_list': permitted_ids}) | ||
|
|
||
| return result.success(inner(self, request, workspace_id=workspace_id)) | ||
|
|
||
| class BatchMove(APIView): | ||
| authentication_classes = [TokenAuth] | ||
|
|
||
| @extend_schema( | ||
| methods=['PUT'], | ||
| description=_("Batch move tools"), | ||
| summary=_("Batch move tools"), | ||
| operation_id=_("Batch move tools"), | ||
| parameters=ToolBatchOperateAPI.get_parameters(), | ||
| request=ToolBatchOperateAPI.get_move_request(), | ||
| responses=result.DefaultResultSerializer, | ||
| tags=[_('Tool')] | ||
| ) | ||
| @has_permissions(PermissionConstants.TOOL_BATCH_MOVE.get_workspace_permission(), | ||
| RoleConstants.USER.get_workspace_role(), | ||
| RoleConstants.WORKSPACE_MANAGE.get_workspace_role() | ||
| ) | ||
| def put(self, request: Request, workspace_id: str): | ||
| id_list = request.data.get('id_list', []) | ||
| permitted_ids = check_batch_permissions( | ||
| request, id_list, 'tool_id', | ||
| (PermissionConstants.TOOL_EDIT.get_workspace_tool_permission(), | ||
| PermissionConstants.TOOL_EDIT.get_workspace_permission_workspace_manage_role(), | ||
| ViewPermission([RoleConstants.USER.get_workspace_role()], | ||
| [PermissionConstants.TOOL.get_workspace_tool_permission()], | ||
| CompareConstants.AND), | ||
| RoleConstants.WORKSPACE_MANAGE.get_workspace_role()), | ||
| workspace_id=workspace_id | ||
| ) | ||
|
|
||
| @log(menu='Tool', operate='Batch move tools', | ||
| get_operation_object=lambda r, k: get_tool_operation_object_batch(permitted_ids)) | ||
| def inner(view, r, **kwargs): | ||
| return ToolBatchOperateSerializer( | ||
| data={'workspace_id': workspace_id} | ||
| ).batch_move({'id_list': permitted_ids, 'folder_id': request.data.get('folder_id')}) | ||
|
|
||
| return result.success(inner(self, request, workspace_id=workspace_id)) | ||
|
|
||
| class Page(APIView): | ||
| authentication_classes = [TokenAuth] | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided Python code is largely well-structured with clean imports, functions, and classes. However, there are several areas where improvements can be made for clarity, maintainability, and potentially better performance: Improvements
Here’s an example of how you might refactor some parts of the code based on these suggestions: # Import necessary functionalities
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db.models import QuerySet
class ToolBatchOperateSerializer:
# Serializer implementation details...
class ToolsBatchOperationsViewMixin(APIView):
# Common logic for batch delete and move methods...
def check_and_perform_operations(self, action_type, request_data, workspace_id=None):
ids_list = request_data.get('id_list', [])
permitted_ids = self.check_batch_permissions(ids_list, 'tool_id')
@log(menu=self.__class__.__name__, operate=f'{action_type.capitalize()} tools',
get_operation_object=lambda r, k: get_tool_operation_object_batch(permitted_ids))
def perform_inner():
serializer_cls = getattr(self, f'{action_type}_serializer', None)
if serializer_cls:
return serializer_cls(data={'workspace_id': workspace_id}).process(action=action_type)
return {"error": "Invalid operation type"}
return ResultHandler().success(perform_inner)(self.request)
@staticmethod
def get_tool_operation_objects(obj_list):
tool_names = list(map(lambda obj: item['name'], obj_list)) # Adjust this part based on actual serialization output
return {'name': '[' + ','.join(tool_names) + ']', 'tool_list': obj_list}
class BatchDeleteView(ToolsBatchOperationsViewMixin):
authentication_classes = [IsAuthenticated]
serializer_class = ToolDeleteSerializer
def post(self, *args, **kwargs):
return super().check_and_perform_operations("delete", kwargs["data"], kwargs["workspace_id"])
class BatchMoveView(ToolsBatchOperationsViewMixin):
authentication_classes = [IsAuthenticated]
serializer_class = ToolMoveSerializer
def post(self, *args, **kwargs):
return super().check_and_perform_operations("move", kwargs["data"], kwargs["workspace_id"])This refactored version improves readability, reusability, and maintainability while maintaining the same functionality. Make sure to adjust the serializers according to your application's structure and requirements. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code you provided appears to be a set of changes for an API in Python using Django REST Framework (DRF) framework. Overall, it looks relatively clean and consistent with typical DRF practices:
Key Points:
Imports: The necessary imports at the top are correctly formatted and aligned.
Functionality:
get_parametersmethod in bothAPIMixinandToolBatchOperateAPIhave been updated to include new parameters such asworkspace_id, which is appropriate if these methods are supposed to handle operations with workspace context.get_requestandget_move_requestmethods now point toBatchSerializerandBatchMoveSerializer, respectively, indicating that this API handles batch data related operations on tools in different ways.General Structure: The code follows a standard structure where static methods (
@staticmethod) define API-specific behavior like request serializers and parameter configurations.Comments: There are inline comments explaining each part, which helps maintain readability.
Potential Improvements:
Error Handling: Ensure that error handling is properly implemented throughout the application. Missing error responses can lead to poor user experience.
Validation: Validate input data thoroughly before processing. This can prevent invalid requests from being processed improperly.
Security Measures: Implement security best practices such as authentication and authorization checks to ensure only authorized users can interact with the APIs.
Logging: Add logging around sensitive operations to help debug issues and track API usage.
Documentation: Ensure thorough documentation is available for developers, especially around parameters, expected response formats, and general operation descriptions.
Performance Considerations: Review performance implications of certain operations, especially those involving large datasets or complex computations.
In general, your code setup is well-structured with clear separation of concerns. If there are specific areas you need further refinement or want additional features included, feel free to specify!