feat: implement CRUD operations for tools with API views

This commit is contained in:
CaptainB 2025-04-18 16:04:58 +08:00 committed by 刘瑞斌
parent f89e18578f
commit 0b8f5d7597
7 changed files with 162 additions and 22 deletions

View File

@ -133,6 +133,12 @@ class PermissionConstants(Enum):
TOOL_CREATE = Permission(group=Group.TOOL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN, TOOL_CREATE = Permission(group=Group.TOOL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
RoleConstants.USER]) RoleConstants.USER])
TOOL_EDIT = Permission(group=Group.TOOL, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN,
RoleConstants.USER])
TOOL_READ = Permission(group=Group.TOOL, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
RoleConstants.USER])
TOOL_DELETE = Permission(group=Group.TOOL, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN,
RoleConstants.USER])
def get_workspace_application_permission(self): def get_workspace_application_permission(self):
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate, return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,

View File

@ -1,4 +1,6 @@
# coding=utf-8 # coding=utf-8
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from common.mixins.api_mixin import APIMixin from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer from common.result import ResultSerializer
@ -11,6 +13,18 @@ class ToolCreateResponse(ResultSerializer):
class ToolCreateAPI(APIMixin): class ToolCreateAPI(APIMixin):
@staticmethod
def get_parameters():
return [
OpenApiParameter(
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
required=True,
)
]
@staticmethod @staticmethod
def get_request(): def get_request():
return ToolCreateRequest return ToolCreateRequest
@ -18,3 +32,39 @@ class ToolCreateAPI(APIMixin):
@staticmethod @staticmethod
def get_response(): def get_response():
return ToolCreateResponse return ToolCreateResponse
class ToolReadAPI(APIMixin):
@staticmethod
def get_parameters():
return [
OpenApiParameter(
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
required=True,
),
OpenApiParameter(
name="tool_id",
description="工具id",
type=OpenApiTypes.STR,
location='path',
required=True,
)
]
@staticmethod
def get_response():
return ToolCreateResponse
class ToolEditAPI(ToolReadAPI):
@staticmethod
def get_request():
return ToolCreateRequest
class ToolDeleteAPI(ToolReadAPI):
pass

View File

@ -66,13 +66,13 @@ class Migration(migrations.Migration):
models.CharField(choices=[('INTERNAL', '内置'), ('PUBLIC', '公开')], default='PUBLIC', max_length=20, models.CharField(choices=[('INTERNAL', '内置'), ('PUBLIC', '公开')], default='PUBLIC', max_length=20,
verbose_name='函数类型')), verbose_name='函数类型')),
('template_id', models.UUIDField(default=None, null=True, verbose_name='模版id')), ('template_id', models.UUIDField(default=None, null=True, verbose_name='模版id')),
('workspace_id', models.CharField(default='default', max_length=64, verbose_name='工作空间id')), ('workspace_id', models.CharField(default='default', max_length=64, verbose_name='工作空间id', db_index=True)),
('init_params', models.CharField(max_length=102400, null=True, verbose_name='初始化参数')), ('init_params', models.CharField(max_length=102400, null=True, verbose_name='初始化参数')),
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')), ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')), ('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user', ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user',
verbose_name='用户id')), verbose_name='用户id')),
('module_id', ('module',
models.ForeignKey(default='root', on_delete=django.db.models.deletion.CASCADE, to='tools.toolmodule', models.ForeignKey(default='root', on_delete=django.db.models.deletion.CASCADE, to='tools.toolmodule',
verbose_name='模块id')), verbose_name='模块id')),
], ],

View File

@ -30,8 +30,8 @@ class Tool(models.Model):
tool_type = models.CharField(max_length=20, verbose_name='函数类型', choices=ToolType.choices, tool_type = models.CharField(max_length=20, verbose_name='函数类型', choices=ToolType.choices,
default=ToolType.PUBLIC) default=ToolType.PUBLIC)
template_id = models.UUIDField(max_length=128, verbose_name="模版id", null=True, default=None) template_id = models.UUIDField(max_length=128, verbose_name="模版id", null=True, default=None)
module_id = models.ForeignKey(ToolModule, on_delete=models.CASCADE, verbose_name="模块id", default='root') module = models.ForeignKey(ToolModule, on_delete=models.CASCADE, verbose_name="模块id", default='root')
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default") workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
init_params = models.CharField(max_length=102400, verbose_name="初始化参数", null=True) init_params = models.CharField(max_length=102400, verbose_name="初始化参数", null=True)
create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True) create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True)
update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, null=True) update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, null=True)

View File

@ -3,6 +3,7 @@ import re
import uuid_utils.compat as uuid import uuid_utils.compat as uuid
from django.core import validators from django.core import validators
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
@ -30,6 +31,17 @@ class ToolInputField(serializers.Serializer):
]) ])
class InitField(serializers.Serializer):
field = serializers.CharField(required=True, label=_('field name'))
label = serializers.CharField(required=True, label=_('field label'))
required = serializers.BooleanField(required=True, label=_('required'))
input_type = serializers.CharField(required=True, label=_('input type'))
default_value = serializers.CharField(required=False, allow_null=True, allow_blank=True)
show_default_value = serializers.BooleanField(required=False, default=False)
props_info = serializers.DictField(required=False, default=dict)
attrs = serializers.DictField(required=False, default=dict)
class ToolCreateRequest(serializers.Serializer): class ToolCreateRequest(serializers.Serializer):
name = serializers.CharField(required=True, label=_('tool name')) name = serializers.CharField(required=True, label=_('tool name'))
@ -38,9 +50,10 @@ class ToolCreateRequest(serializers.Serializer):
code = serializers.CharField(required=True, label=_('tool content')) code = serializers.CharField(required=True, label=_('tool content'))
input_field_list = serializers.ListField(child=ToolInputField(), required=False, label=_('input field list')) input_field_list = serializers.ListField(child=ToolInputField(), required=False, default=list,
label=_('input field list'))
init_field_list = serializers.ListField(required=False, default=list, label=_('init field list')) init_field_list = serializers.ListField(child=InitField(), required=False, default=list, label=_('init field list'))
is_active = serializers.BooleanField(required=False, label=_('Is active')) is_active = serializers.BooleanField(required=False, label=_('Is active'))
@ -50,6 +63,7 @@ class ToolCreateRequest(serializers.Serializer):
class ToolSerializer(serializers.Serializer): class ToolSerializer(serializers.Serializer):
class Create(serializers.Serializer): class Create(serializers.Serializer):
user_id = serializers.UUIDField(required=True, label=_('user id')) user_id = serializers.UUIDField(required=True, label=_('user id'))
workspace_id = serializers.UUIDField(required=True, label=_('workspace id'))
def insert(self, instance, with_valid=True): def insert(self, instance, with_valid=True):
if with_valid: if with_valid:
@ -66,3 +80,32 @@ class ToolSerializer(serializers.Serializer):
is_active=False) is_active=False)
tool.save() tool.save()
return ToolModelSerializer(tool).data return ToolModelSerializer(tool).data
class Operate(serializers.Serializer):
id = serializers.UUIDField(required=True, label=_('tool id'))
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
def edit(self, instance, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
ToolCreateRequest(data=instance).is_valid(raise_exception=True)
if not QuerySet(Tool).filter(id=self.data.get('id')).exists():
raise serializers.ValidationError(_('Tool not found'))
edit_field_list = ['name', 'desc', 'code', 'icon', 'input_field_list', 'init_field_list', 'init_params',
'is_active']
edit_dict = {field: instance.get(field) for field in edit_field_list if (
field in instance and instance.get(field) is not None)}
QuerySet(Tool).filter(id=self.data.get('id')).update(**edit_dict)
return self.one()
def delete(self):
self.is_valid(raise_exception=True)
QuerySet(Tool).filter(id=self.data.get('id')).delete()
def one(self):
self.is_valid(raise_exception=True)
tool = QuerySet(Tool).filter(id=self.data.get('id')).first()
return ToolModelSerializer(tool).data

View File

@ -4,5 +4,6 @@ from . import views
app_name = "tool" app_name = "tool"
urlpatterns = [ urlpatterns = [
path('workspace/<str:workspace_id>/tool/create', views.ToolCreateView.as_view()), path('workspace/<str:workspace_id>/tool', views.ToolView.Create.as_view()),
path('workspace/<str:workspace_id>/tool/<str:tool_id>', views.ToolView.Operate.as_view()),
] ]

View File

@ -7,22 +7,62 @@ from common.auth import TokenAuth
from common.auth.authentication import has_permissions from common.auth.authentication import has_permissions
from common.constants.permission_constants import PermissionConstants from common.constants.permission_constants import PermissionConstants
from common.result import result from common.result import result
from tools.api.tool import ToolCreateAPI from tools.api.tool import ToolCreateAPI, ToolEditAPI, ToolReadAPI, ToolDeleteAPI
from tools.serializers.tool import ToolSerializer from tools.serializers.tool import ToolSerializer
class ToolCreateView(APIView): class ToolView(APIView):
authentication_classes = [TokenAuth] class Create(APIView):
authentication_classes = [TokenAuth]
@extend_schema(methods=['POST'], @extend_schema(methods=['POST'],
description=_('Create tool'), description=_('Create tool'),
operation_id=_('Create tool'), operation_id=_('Create tool'),
request=ToolCreateAPI.get_request(), parameters=ToolCreateAPI.get_parameters(),
responses=ToolCreateAPI.get_response(), request=ToolCreateAPI.get_request(),
tags=[_('Tool')]) responses=ToolCreateAPI.get_response(),
@has_permissions(PermissionConstants.TOOL_CREATE) tags=[_('Tool')])
# @log(menu='Tool', operate="Create tool", @has_permissions(PermissionConstants.TOOL_CREATE.get_workspace_permission())
# get_operation_object=lambda r, k: r.data.get('name')) def post(self, request: Request, workspace_id: str):
def post(self, request: Request, workspace_id: str): return result.success(ToolSerializer.Create(
print(workspace_id) data={'user_id': request.user.id, 'workspace_id': workspace_id}
return result.success(ToolSerializer.Create(data={'user_id': request.user.id}).insert(request.data)) ).insert(request.data))
class Operate(APIView):
authentication_classes = [TokenAuth]
@extend_schema(methods=['PUT'],
description=_('Update tool'),
operation_id=_('Update tool'),
parameters=ToolEditAPI.get_parameters(),
request=ToolEditAPI.get_request(),
responses=ToolEditAPI.get_response(),
tags=[_('Tool')])
@has_permissions(PermissionConstants.TOOL_EDIT.get_workspace_permission())
def put(self, request: Request, workspace_id: str, tool_id: str):
return result.success(ToolSerializer.Operate(
data={'id': tool_id, 'workspace_id': workspace_id}
).edit(request.data))
@extend_schema(methods=['GET'],
description=_('Update tool'),
operation_id=_('Update tool'),
parameters=ToolReadAPI.get_parameters(),
responses=ToolReadAPI.get_response(),
tags=[_('Tool')])
@has_permissions(PermissionConstants.TOOL_READ.get_workspace_permission())
def get(self, request: Request, workspace_id: str, tool_id: str):
return result.success(ToolSerializer.Operate(
data={'id': tool_id, 'workspace_id': workspace_id}
).one())
@extend_schema(methods=['DELETE'],
description=_('Delete tool'),
operation_id=_('Delete tool'),
parameters=ToolDeleteAPI.get_parameters(),
tags=[_('Tool')])
@has_permissions(PermissionConstants.TOOL_DELETE.get_workspace_permission())
def delete(self, request: Request, workspace_id: str, tool_id: str):
return result.success(ToolSerializer.Operate(
data={'id': tool_id, 'workspace_id': workspace_id}
).delete())