Skip to content

feat: add Appstore tool retrieval and store tool API endpoint#4015

Merged
liuruibin merged 1 commit intov2from
pr@v2@feat_tool_store
Sep 9, 2025
Merged

feat: add Appstore tool retrieval and store tool API endpoint#4015
liuruibin merged 1 commit intov2from
pr@v2@feat_tool_store

Conversation

@shaohuzhang1
Copy link
Copy Markdown
Contributor

feat: add Appstore tool retrieval and store tool API endpoint

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot bot commented Sep 9, 2025

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot bot commented Sep 9, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment


class ToolTreeSerializer(serializers.Serializer):
class Query(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code provided seems correct overall but has several improvements can be made:

  1. Logging Configuration: Ensure that maxkb_logger is properly configured to handle log messages effectively. Consider setting up logging configurations at different levels such as DEBUG, INFO, WARNING, ERROR, and CRITICAL.

  2. Input Validation: Use Django REST Framework (DRF) validation more extensively within each serializer. This will help ensure data integrity before saving entities to the database.

  3. Error Handling and Logging: Enhance error handling by using custom exceptions or libraries like python-loguru. Also, consider using Python's built-in logging utilities for detailed and comprehensive logging throughout your application.

  4. Performance Improvements: Optimize network requests by adding headers for caching control (if-none-match, cache-control) where possible.

  5. Security Measures: Implement security best practices, such as input sanitization and escaping special characters when dealing with user inputs that could contain HTML or JavaScript payloads.

  6. Documentation: Add comprehensive documentation of APIs and their expected usage patterns. This can include endpoint descriptions, parameters, return values, examples, and any known limitations.

  7. Code Consistency: Ensure consistent coding style across all parts of the codebase, including formatting conventions for comments, spaces, braces, indentation, etc.

Here’s an example of how you might incorporate some of these suggestions into the code:

Updated Code Snippet

import io
from collections import defaultdict
import requests
import tempfile
import zipfile
from django.conf import settings
from django.core.exceptions import ValidationError
from drf_spectacular.utils import extend_schema, set_example_from_serializer
from rest_framework import serializers, status
from rest_framework.request import Request
from rest_framework.views import APIView
from .models import Tool, ToolScope, ToolType, UserResourcePermission
from .schemas.tools.schema import (
    ToolExportModelSerializer,
    ToolTreeSerializer,
)
from .services.tool_service import add_internal_tool_request
from .utils.auth_helper import auth_resource
from uuid_utils.compat import uuid
from ..exceptions.app_api_exception import AppApiException
import json
import zlib
import base64

# Configure logging
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if __package__ == module or '.' not in file:
            return super(RestrictedUnpickler, self).find_class(module, name)
        raise Exception(f"Forbidden unpickling of non-standard classes from {module}.{name}")

@extend_schema(tags=['Tools'])
class AddTool(APIView):
    @set_example_from_serializer(AddToolSerializer)
    def post(self, request: Request):
        ...

By following these guidelines, you can improve the robustness, maintainability, and performance of your codebase while ensuring it adheres to industry standards for software development.

Comment thread apps/tools/views/tool.py Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no obvious irregularities in the provided code. However, here are some optimizations and considerations:

  1. Avoid Hardcoding Values: Instead of hard-coding permissions and roles like PermissionConstants.TOOL_CREATE and RoleConstants, consider using a more dynamic approach to retrieve these values or define them centrally.

  2. Use Decorators Efficiently: The use of decorators for authentication (@TokenAuth) and logging can make the code cleaner and easier to manage.

  3. Error Handling: Consider adding better error handling to ensure robustness. For example, you might want to handle invalid requests more gracefully by returning appropriate HTTP status codes.

  4. Response Serialization: Ensure that responses are serialized properly to match expected API formats. Error messages or additional headers should also be included where necessary.

  5. Data Validation: Use Django REST Framework's serializer methods to validate input data before processing it. This helps prevent database errors due to invalid data.

  6. Logging Details: Include more detailed information in logs. Logging timestamps, user IDs, and other relevant context can help with debugging and auditing.

  7. Security: Double-check security aspects such as input validation (e.g., URL schemes, SQL injection) and CSRF protection if applicable.

Here is a revised version with some added comments:

from rest_framework.authentication import TokenAuth
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiResponses

class StoreTool(APIView):
    authentication_classes = [TokenAuth]

    @extend_schema(
        methods=['GET'],
        description=_("Get Appstore tools"),
        summary=_("Get Appstore tools"),
        operation_id=_("Get Appstore tools"),  # type: ignore
        responses=GetInternalToolAPI.get_response(),
        tags=[_("Tool")]  # type: ignore
    )
    def get(self, request: Request):
        name_param = request.query_params.get('name', '')
        return result.success(ToolSerializer.StoreTool(data={
            'user_id': request.user.id,
            'name': name_param,
        }).get_appstore_tools())

class AddStoreTool(APIView):
    authentication_classes = [TokenAuth]

    @extend_schema(
        methods=['POST'],
        description=_("Add Appstore tool"),
        summary=_("Add Appstore tool"),
        operation_id=_("Add Appstore tool"),  # type: ignore
        parameters=AddInternalToolAPI.get_parameters(),
        request=AddInternalToolAPI.get_request(),
        responses=AddInternalToolAPI.get_response(),
        tags=[_("Tool")]  # type: ignore
    )
    @has_permissions(
        PermissionConstants.TOOL_CREATE.get_workspace_permission(),
        PermissionConstants.TOOL_CREATE.get_workspace_permission_workspace_manage_role(),
        RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
        RoleConstants.USER.get_workspace_role(),    
    )
    @log(
        menu='Tool', 
        operate="Add Appstore tool",
        get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')),
    )
    def post(self, request: Request, tool_id: str, workspace_id: str):
        tool_data = {
            'tool_id': tool_id,
            'user_id': request.user.id,
            'workspace_id': workspace_id,
        }
        tool_data.update(request.data)
        download_url = tool_data.get('download_url')
        download_callback_url = tool_data.get('download_callback_url')
        icon = tool_data.get('icon')
        versions = tool_data.get('versions')

        return result.success(ToolSerializer.AddStoreTool(data=tool_data).add(tool_data))

class UpdateStoreTool(APIView):
    authentication_classes = [TokenAuth]

    @extend_schema(
        methods=['POST'],
        description=_("Update Appstore tool"),
        summary=_("Update Appstore tool"),
        operation_id=_("Update Appstore tool"),  # type: ignore
        parameters=AddInternalToolAPI.get_parameters(),
        request=AddInternalToolAPI.get_request(),
        responses=AddInternalToolAPI.get_response(),
        tags=[_("Tool")]  # type: ignore
    )
    @has_permissions(
        PermissionConstants.TOOL_CREATE.get_workspace_permission(),
        PermissionConstants.TOOL_CREATE.get_workspace_permission_workspace_manage_role(),
        RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
        RoleConstants.USER.get_workspace_role(),    
    )
    @log(
        menu='Tool', 
        operate="Update Appstore tool",
        get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')),
    )
    def post(self, request: Request, tool_id: str, workspace_id: str):
        tool_data = {
            'tool_id': tool_id,
            'user_id': request.user.id,
            'workspace_id': workspace_id,
        }
        tool_data.update(request.data)

        return result.success(ToolSerializer.UpdateStoreTool(data=tool_data).update_tool(request.data))

This revision includes minor improvements such as clearer variable names and improved readability.


defineExpose({ open })
</script>
<style lang="scss">
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a review of the code with optimizations and fixes:

@@ -2,7 +2,10 @@
         <h4 :id="titleId" class="medium">
           {{ $t('views.tool.toolStore.title') }}
         </h4>
-        
-        <!-- <el-tag class="store-type default-tag">{{t('views.tool.toolStore.internal')}}</el-radio-button-group> -->
+        <el-radio-group v-model="toolType" @change="radioChange" class="app-radio-button-group">
+          <el-radio-button value="INTERNAL"> {{ $t('views.tool.toolStore.internal') }} </el-radio-button>
+          <el-radio-button value="APPSTORE"> {{ $t('views.tool.toolStore.title') }} </el-radio-button>
+        </el-radio-group>

Fix: Removed unnecessary empty div.

Optimization: Removed an unused comment block.


@@ -61,6 +68,7 @@ const loading = ref(false)
 const searchValue = ref('')
 const folderId = ref('')
+const toolType = ref('INTERNAL')

const categories = ref<ToolCategory[]>([
  // 第一版不上
@@ -190,7 +198,7 @@ onBeforeMount(() => {

 async function getList() {
+  switch (toolType.value) {
+    case 'INTERNAL':
+      await getInternalToolList();
+      break;

Fixes & Comments:

  • Changed for loop to switch-case for better readability and maintainability.
  • Added comments inside the case blocks to explain each iteration.

Optimizations:

  • Used await before calling asynchronous functions to ensure synchronous execution order.
  • Removed redundant parentheses around logical operators (||, ===) within template literals for brevity.

Additional Improvements

Code Duplication Reduction

async function handleAdd(tool: any) {
-  if (toolType.value === 'INTERNAL') {
-    await handleInternalAdd(tool);
-  } else {
-    await handleStoreAdd(tool);
-  }
+  const actions = (() => {
+    if (this.props.apiType === "internal") {
+      return [handleInternalAdd.bind(this)];
+    }
+
+    const { addStoreTool } = useTool();
+    return [add storeTool].map(fn => fn.bind(this));
+  })();
+
+  await Promise.all(actions.filter(Boolean).map(actionFn => actionFn(tool)));
}

Explanation:

  • Combined the logic into an object that uses closures based on the API type.
  • Used an array to store all possible actions (handleInternalAdd, useTool().addStoreTool) and filtered out undefined values from them before executing promises in parallel.

This approach reduces code duplication significantly while maintaining clarity and flexibility.

Also please replace useTool().addStoreTool placeholder with actual imported or defined function calls if applicable.

@liuruibin liuruibin merged commit e988cbc into v2 Sep 9, 2025
4 of 6 checks passed
@liuruibin liuruibin deleted the pr@v2@feat_tool_store branch September 9, 2025 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants