-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: Folder move #4311
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
feat: Folder move #4311
Conversation
|
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. DetailsInstructions 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. |
|
[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. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
| } | ||
| :deep(.overflow-inherit_node__children) { | ||
| .el-tree-node__children { | ||
| overflow: inherit !important; |
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.
This looks mostly correct and follows best practices. Here are some minor improvements:
-
Ensure
@node-contextmenuis also included for handling context menus when nodes are right-clicked. -
Consider using CSS variables consistently to avoid hardcoding colors directly in your styles. For example, you could use
--el-color-primary-light-9instead of a specific hex value. -
The documentation comment at the top might benefit from updating any outdated information or clarifying assumptions about current implementation details.
-
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() | ||
|
|
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 provided code is well-structured and mostly correct, but there are some areas for improvement:
Issues and Recommendations
-
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.
-
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 againstFOLDER_DEPTH. -
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. -
Duplicate Name Validation:
The duplicate folder name validation can be improved to use unique constraints instead of iterating through all nodes. -
Code Duplication:
There is significant duplication in the code, particularly betweencheck_depth()and parts of the editing logic. Refactoring these sections could improve readability and maintainability. -
API Exception Handling:
Ensure that appropriate exceptions are raised with clear messages, especially around permission errors. -
Documentation:
Add comments and docstrings to clarify the purpose and functionality of critical functions. -
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')) |
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 contains a few improvements and optimizations that can enhance its readability, maintainability, and functionality:
-
Variable Naming: Consistently use camelCase for variable names to improve consistency with Vue.js conventions.
-
Type Annotations: Add type annotations where appropriate to make the code more readable and maintainable.
-
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_folderto boolean to reduce confusion about whether it was originally defined. - Renamed variables like
folderObjtofolder_obj. - Updated the call to
putLarkKnowledgebased on assuming the correct method signature. - Removed unnecessary semicolons.
- Added error handling using
try-catchblocks. - Ensured clear separation of concerns by moving logic related to folder management outside main component logic when applicable.
feat: Folder move