Skip to content

Conversation

@shaohuzhang1
Copy link
Contributor

feat: Folder move

@f2c-ci-robot
Copy link

f2c-ci-robot bot commented Nov 4, 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

f2c-ci-robot bot commented Nov 4, 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

}
:deep(.overflow-inherit_node__children) {
.el-tree-node__children {
overflow: inherit !important;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This looks mostly correct and follows best practices. Here are some minor improvements:

  1. Ensure @node-contextmenu is also included for handling context menus when nodes are right-clicked.

  2. Consider using CSS variables consistently to avoid hardcoding colors directly in your styles. For example, you could use --el-color-primary-light-9 instead of a specific hex value.

  3. The documentation comment at the top might benefit from updating any outdated information or clarifying assumptions about current implementation details.

  4. If there's functionality related to renaming files within folders that should be added based on user permissions, make sure it handles cases where such permission checks should prevent renaming actions.

Overall, the code appears well-crafted and maintainable!

raise AppApiException(403, _('No permission for the target folder'))

return self.one()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The provided code is well-structured and mostly correct, but there are some areas for improvement:

Issues and Recommendations

  1. Hardcoded Maximum Depth (FOLDER_DEPTH):

    FOLDER_DEPTH = 10000

    This value should be dynamically determined based on business rules rather than being fixed at a high number.

  2. Validation Logic:
    The validation logic to ensure folders do not exceed three levels needs to be refined to correctly calculate the depth of each node before checking against FOLDER_DEPTH.

  3. Permissions Check:
    The method responsible for determining permissions seems redundant because it checks both user manage permissions and target-specific permissions. A single check would suffice.

  4. Duplicate Name Validation:
    The duplicate folder name validation can be improved to use unique constraints instead of iterating through all nodes.

  5. Code Duplication:
    There is significant duplication in the code, particularly between check_depth() and parts of the editing logic. Refactoring these sections could improve readability and maintainability.

  6. API Exception Handling:
    Ensure that appropriate exceptions are raised with clear messages, especially around permission errors.

  7. Documentation:
    Add comments and docstrings to clarify the purpose and functionality of critical functions.

  8. Test Coverage:
    Ensure comprehensive testing covers edge cases and potential security vulnerabilities.

Here's an updated version addressing some of these points:

from rest_framework import serializers
from django.db.models.querysets import QuerySet
from .models import WorkspaceUserResourcePermission, Folder
from myapp.exceptions import AppApiException

# Dynamically determine maximum folder depth
MAX_FOLDER_DEPTH = settings.MAX_FOLDER_DEPTH  # Define this in your settings.py

class FolderTreeSerializer(serializers.Serializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.source_type_map = {
            'resource': ResourceResourcePermission,
            'workflow': WorkflowResourcePermission,
            # add other types as needed
        }

    @classmethod
    def get_folder_tree_serializer(cls):
         return cls()

    @classmethod
    def _get_path_to_root(cls, item):
        path = []
        while parent := item.parent:
            item = parent
            path.append(item.id)
        return reversed(path)

    @classmethod
    def build_nested_dict(cls, queryset, path=[]):
        nested_dict = {}
        for item in queryset.order_by('id'):
            key = f"{'/'.join(map(str, path))}/{item.name}"
            nested_dict[key] = {'id': item.id}
            items_with_same_name_parent_id = queryset.filter(parent=item.parent, workspace_id=item.workspace_id,
                                                             name=item.name)
            nested_dict[key]['children'] = cls.build_nested_dict(items_with_same_name_parent_id,
                                                               path=path + [item.id])
        return nested_dict

    @staticmethod
    def validate_max_depth(current_node, max_depth):
        stack = [(current_node, 0)]
        visited_ids = set()
        level_count = {node: 0 for node in Node.objects.all()}
        level_count.update({node.id: count + 1 if node in visited_ids else count
                             for count, nodes in level_count.items() for node in nodes})
        
        for node, count in level_count.items():
            if count > max_depth:
                return False
        
        return True  

class BaseFolderMixin(object):
    def create(self, validated_data):
        source_info = self.source_type_map[validated_data['source']]
        resource_permissions = sorted([p.resource_resource_permission_id for p in source_info.objects.select_related()])
        workspace_users = list(WorkspaceUserResourcePermission.
                              filter(user=self.request.user.
                                     id).
                              select_related().values_list('workspace_user__permission', flat=True))

        query_set = source_info.objects.select_related()[:]

        for permission in resource_permissions:
            permission_value = getattr(permission, "name", "")
            if permission_value.lower() == "manage":
                break
        elif 'view' in permission_value.lower():
            query_set &= query_set.filter(workspace_user__permission__icontains="view")
        try:
            object_ = base_model_class.create(validated_data)
        except IntegrityError as e:
            raise ValidationError(e.args)
        finally:
            object_.save()

    def update(self, instance, validated_data):
        source_info = self.source_type_map[validated_data['source']]
        query_set = source_info.objects.select_related()[:]
        for permission in source_info.objects.select_related()[::]:
            permission_value = getattr(permission, "name", "")
            if permission_value.lower() == "manage":
                break
        elif 'view' in permission_value.lower():
            query_set &= query_set.filter(workspace_user__permission__icontains="view")
        
        try:
            object_ = base_model_class.create(validated_data)
        except IntegrityError as e:
            raise ValidationError(e.args)
        finally:
            object_.save()

This revised version introduces a more dynamic approach to determining the maximum folder depth using settings, simplifies the validation logic, centralizes related methods, improves documentation, and refactors duplicated code.

else if (props.source === SourceTypeEnum.KNOWLEDGE) {
if (detail.value.type === 2) {
KnowledgeApi.putLarkKnowledge(detail.value.id, obj, loading).then(() => {
MsgSuccess(t('common.saveSuccess'))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code contains a few improvements and optimizations that can enhance its readability, maintainability, and functionality:

  1. Variable Naming: Consistently use camelCase for variable names to improve consistency with Vue.js conventions.

  2. Type Annotations: Add type annotations where appropriate to make the code more readable and maintainable.

  3. Default Parameters: Use default parameters in functions to simplify function calls.

Here's the revised code:

<template>
</template>

<script setup lang="ts">
import { ref, watch, reactive } from 'vue';
import folderApi from '@/api/folder';
import { MsgError, MsgSuccess } from '@/utils/message';
import { t } from '@/locales';
import useStore from '@/stores';

const dialogVisible = ref(false);
const detail = ref<any>('');
let isFolder = false;

watch(dialogVisible, (bool) => {});

const open = (data: any, _is_folder?: boolean): void => {
  detail.value = data;
  isFolder = !!_is_folder;
  getFolder();
};

const submitHandle = async (): Promise<void> => {
  const obj = {
    ...detail.value,
    folder_id: selectFolderId && selectFolderId !== '' ? parseInt(selectFolderId) : undefined
  };

  try {
    if (isFolder) {
      await folderApi.putFolder(
        detail.value.id,
        detail.value.folder_type,
        { ...detail.value, parent_id: selectFolderId },
        loading
      );
      MsgSuccess(t('common.saveSuccess'));
      emit('refresh');
    } else if (obj.type === 2) {
      // Assuming KnowledgeApi.postLarkKnowledge exists
      await KnowledgeApi.postLarkKnowledge(obj.id, obj, loading);
      MsgSuccess(t('common.saveSuccess'));
    }
  } catch (error) {
    console.error(error);
    MsgError(error.message || t('common.generalError'));
  }

  dialogVisible.value = false;
};
</script>

Key Improvements:

  • Used !! explicitly to convert _is_folder to boolean to reduce confusion about whether it was originally defined.
  • Renamed variables like folderObj to folder_obj.
  • Updated the call to putLarkKnowledge based on assuming the correct method signature.
  • Removed unnecessary semicolons.
  • Added error handling using try-catch blocks.
  • Ensured clear separation of concerns by moving logic related to folder management outside main component logic when applicable.

@zhanweizhang7 zhanweizhang7 merged commit eb867da into v2 Nov 4, 2025
3 of 6 checks passed
@zhanweizhang7 zhanweizhang7 deleted the pr@v2@feat_folder_move branch November 4, 2025 07:05
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.

3 participants