diff --git a/app/commands/file.py b/app/commands/file.py index e83baa27..319f2221 100644 --- a/app/commands/file.py +++ b/app/commands/file.py @@ -12,6 +12,7 @@ import app.services.output_manager.help_page as file_help import app.services.output_manager.message_handler as message_handler from app.configs.app_config import AppConfig +from app.models.item import ItemType from app.services.file_manager.file_download.download_client import SrvFileDownload from app.services.file_manager.file_list import SrvFileList from app.services.file_manager.file_manifests import SrvFileManifests @@ -544,8 +545,21 @@ def file_move(**kwargs): zone = kwargs.get('zone') skip_confirm = kwargs.get('yes') + if len(src_item_path.split('/')) == 1 and len(dest_item_path.split('/')) == 1: + raise Exception('Invalid path') + + # tranlate keyword to correct object path + src_keyword, src_path = src_item_path.split('/', 1) + dest_keyword, dest_path = dest_item_path.split('/', 1) + + src_type = ItemType.get_type_from_keyword(src_keyword) + dest_type = ItemType.get_type_from_keyword(dest_keyword) + + src_path = src_type.get_prefix_by_type() + src_path + dest_path = dest_type.get_prefix_by_type() + dest_path + zone = get_zone(zone) if zone else AppConfig.Env.green_zone.lower() - file_meta_client = FileMoveClient(zone, project_code, src_item_path, dest_item_path, skip_confirm=skip_confirm) + file_meta_client = FileMoveClient(zone, project_code, src_path, dest_path, skip_confirm=skip_confirm) file_meta_client.move_file() message_handler.SrvOutPutHandler.move_action_success(src_item_path, dest_item_path) diff --git a/app/models/item.py b/app/models/item.py index db1068d8..b7c84b6c 100644 --- a/app/models/item.py +++ b/app/models/item.py @@ -27,7 +27,7 @@ def get_type_from_keyword(self, keyword: str): 'users': self.NAMEFOLDER, } - return alternative_mapping.get(keyword, '') + return alternative_mapping.get(keyword, self.FOLDER) def get_prefix_by_type(self) -> str: """Get the prefix for the folder type.""" diff --git a/app/services/file_manager/file_metadata/file_metadata_client.py b/app/services/file_manager/file_metadata/file_metadata_client.py index ad3833a3..fad9cc02 100644 --- a/app/services/file_manager/file_metadata/file_metadata_client.py +++ b/app/services/file_manager/file_metadata/file_metadata_client.py @@ -19,6 +19,7 @@ import app.services.logger_services.log_functions as logger import app.services.output_manager.message_handler as message_handler +from app.models.item import ItemType from app.services.output_manager.error_handler import ECustomizedError from app.services.output_manager.error_handler import customized_error_msg from app.utils.aggregated import get_attribute_template_by_id @@ -109,14 +110,15 @@ def download_file_metadata(self) -> List[Dict[str, Any]]: """ project_code, object_path = self.file_path.split('/', 1) + root_folder, object_path = object_path.split('/', 1) + root_type = ItemType.get_type_from_keyword(root_folder) + object_path = join(root_type.get_prefix_by_type(), object_path) item_res = search_item(project_code, self.zone, object_path) - # double check if the file is in shared folder if item_res.get('code') == 404: - item_res = search_item(project_code, self.zone, f'shared/{object_path}') - if item_res.get('code') == 404: - logger.error(f'Cannot find item {self.file_path} at {self.zone}.') - exit(1) + logger.error(f'Cannot find item {self.file_path} at {self.zone}.') + exit(1) + # filter out item metadata item_res = item_res.get('result', {}) extra_info = item_res.pop('extended', {}).get('extra') tags = extra_info.get('tags', []) diff --git a/app/services/file_manager/file_move/file_move_client.py b/app/services/file_manager/file_move/file_move_client.py index 17478598..2c998428 100644 --- a/app/services/file_manager/file_move/file_move_client.py +++ b/app/services/file_manager/file_move/file_move_client.py @@ -60,11 +60,6 @@ def create_object_path_if_not_exist(self, folder_path: str) -> dict: """ path_list = folder_path.split('/') - # first get the root folder to check if it is name folder - # or project folder - root_item = search_item(self.project_code, self.zone, path_list[0]).get('result') - if root_item.get('type') == 'project_folder': - path_list[0] = '/'.join([root_item.get('parent_path'), path_list[0]]) # first check every folder in path exist or not # the loop start with index 1 since we assume cli will not diff --git a/app/services/project_manager/project.py b/app/services/project_manager/project.py index ec98c503..ef832bc0 100644 --- a/app/services/project_manager/project.py +++ b/app/services/project_manager/project.py @@ -48,4 +48,4 @@ def list_projects(self, page, page_size, order, order_by): else: SrvErrorHandler.default_handle(response.content, True) except Exception: - SrvErrorHandler.default_handle(response.content, True) + SrvErrorHandler.default_handle('Error when listing projects', True) diff --git a/pyproject.toml b/pyproject.toml index c5a8b4c7..d2b215b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "app" -version = "3.0.0" +version = "3.0.1" description = "This service is designed to support pilot platform" authors = ["Indoc Systems"] diff --git a/tests/app/commands/test_file.py b/tests/app/commands/test_file.py index 2331a535..8b558768 100644 --- a/tests/app/commands/test_file.py +++ b/tests/app/commands/test_file.py @@ -208,7 +208,7 @@ def test_empty_file_list_with_pagination(requests_mock, mocker, cli_runner): questionary.select.return_value.ask.return_value = 'exit' result = cli_runner.invoke(file_list, ['testproject/admin', '-z', 'greenroom']) outputs = result.output.split('\n') - assert outputs[0] == '' + assert outputs[0] == ' ' @pytest.mark.parametrize('parent_folder_type', [ItemType.NAMEFOLDER.value, ItemType.SHAREDFOLDER.value]) @@ -365,7 +365,10 @@ def test_file_move_success(mocker, cli_runner): return_value=None, ) - result = cli_runner.invoke(file_move, ['test_project', 'src_item_path', 'dest_item_path']) + src_path = 'src_item_path/test' + dest_path = 'dest_item_path/test' + result = cli_runner.invoke(file_move, ['test_project', src_path, dest_path]) + outputs = result.output.split('\n') - assert outputs[0] == 'Successfully moved src_item_path to dest_item_path' + assert outputs[0] == f'Successfully moved {src_path} to {dest_path}' file_move_mock.assert_called_once() diff --git a/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py b/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py index dafeeaac..05b72110 100644 --- a/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py +++ b/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py @@ -2,19 +2,30 @@ # # Contact Indoc Systems for any questions regarding the use of this source code. +import pytest + from app.configs.app_config import AppConfig +from app.models.item import ItemType from app.services.file_manager.file_metadata.file_metadata_client import FileMetaClient from tests.conftest import decoded_token -def test_file_metadata_client_get_detail_success(mocker, httpx_mock): +@pytest.mark.parametrize( + 'root_folder', + [ItemType.NAMEFOLDER, ItemType.SHAREDFOLDER], +) +def test_file_metadata_client_get_detail_success(mocker, httpx_mock, root_folder: ItemType): + file_name = 'test_file.txt' + project_code = 'project_code' item_info = { 'id': 'test', 'parent_id': 'test_parent', - 'parent_path': '', - 'name': 'admin', + 'parent_path': root_folder.get_prefix_by_type(), + 'name': file_name, 'zone': 0, 'status': 'ACTIVE', + 'container_code': project_code, + 'container_type': 'project', } tags = ['test'] attri_template_uid = 'template_uid' @@ -41,9 +52,11 @@ def test_file_metadata_client_get_detail_success(mocker, httpx_mock): return_value=None, ) - file_meta_client = FileMetaClient('zone', 'project_code/object_path', 'general', 'attr', 'tag') - assert file_meta_client.project_code == 'project_code' - assert file_meta_client.object_path == 'object_path' + file_meta_client = FileMetaClient( + 'zone', f'{project_code}/{root_folder.get_prefix_by_type()}/{file_name}', 'general', 'attr', 'tag' + ) + assert file_meta_client.project_code == project_code + assert file_meta_client.object_path == f'{root_folder.get_prefix_by_type()}/{file_name}' item_info, res_attributes, tags = file_meta_client.download_file_metadata() assert item_info == item_info @@ -51,14 +64,22 @@ def test_file_metadata_client_get_detail_success(mocker, httpx_mock): assert tags == tags -def test_file_metadata_client_get_detail_success_with_no_tag_attributes(mocker, httpx_mock): +@pytest.mark.parametrize( + 'root_folder', + [ItemType.NAMEFOLDER, ItemType.SHAREDFOLDER], +) +def test_file_metadata_client_get_detail_success_with_no_tag_attributes(mocker, root_folder: ItemType): + file_name = 'test_file.txt' + project_code = 'project_code' item_info = { 'id': 'test', 'parent_id': 'test_parent', - 'parent_path': '', - 'name': 'admin', + 'parent_path': root_folder.get_prefix_by_type(), + 'name': file_name, 'zone': 0, 'status': 'ACTIVE', + 'container_code': project_code, + 'container_type': 'project', } mocker.patch( @@ -76,9 +97,11 @@ def test_file_metadata_client_get_detail_success_with_no_tag_attributes(mocker, return_value=None, ) - file_meta_client = FileMetaClient('zone', 'project_code/object_path', 'general', 'attr', 'tag') - assert file_meta_client.project_code == 'project_code' - assert file_meta_client.object_path == 'object_path' + file_meta_client = FileMetaClient( + 'zone', f'{project_code}/{root_folder.get_prefix_by_type()}/{file_name}', 'general', 'attr', 'tag' + ) + assert file_meta_client.project_code == project_code + assert file_meta_client.object_path == f'{root_folder.get_prefix_by_type()}/{file_name}' item_info, res_attributes, tags = file_meta_client.download_file_metadata() assert item_info == item_info @@ -86,20 +109,11 @@ def test_file_metadata_client_get_detail_success_with_no_tag_attributes(mocker, assert tags == tags -def test_metadata_download_from_project_folder(mocker, httpx_mock): - item_info = { - 'id': 'test', - 'parent_id': 'test_parent', - 'parent_path': 'shared/path', - 'name': 'admin', - 'zone': 0, - 'status': 'ACTIVE', - } - tags = ['test'] - attri_template_uid = 'template_uid' - attri_template_name = 'template_name' - attributes = {attri_template_uid: {'attr_1': 'value'}} - +@pytest.mark.parametrize( + 'root_folder', + [ItemType.NAMEFOLDER, ItemType.SHAREDFOLDER], +) +def test_metadata_download_fail_when_file_doesnot_exist(mocker, capfd, root_folder: ItemType): mocker.patch( 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', return_value=decoded_token(), @@ -107,53 +121,20 @@ def test_metadata_download_from_project_folder(mocker, httpx_mock): search_mock = mocker.patch( 'app.services.file_manager.file_metadata.file_metadata_client.search_item', - ) - search_mock.side_effect = [ - {'result': {}, 'code': 404}, - {'result': {**item_info, 'extended': {'extra': {'tags': tags, 'attributes': attributes}}}}, - ] - httpx_mock.add_response( - url=AppConfig.Connections.url_portal + f'/v1/data/manifest/{attri_template_uid}', - method='GET', - json={'result': {'id': attri_template_uid, 'name': attri_template_name}}, + return_value={'result': {}, 'code': 404}, ) - mocker.patch( - 'app.services.file_manager.file_metadata.file_metadata_client.FileMetaClient.save_file_metadata', - return_value=None, + file_meta_client = FileMetaClient( + 'zone', f'project_code/{root_folder.get_prefix_by_type()}/file_name', 'general', 'attr', 'tag' ) - file_meta_client = FileMetaClient('zone', 'project_code/object_path', 'general', 'attr', 'tag') - assert file_meta_client.project_code == 'project_code' - assert file_meta_client.object_path == 'object_path' - - item_info, res_attributes, tags = file_meta_client.download_file_metadata() - assert item_info == item_info - assert res_attributes == {attri_template_name: attributes.get(attri_template_uid)} - assert tags == tags - assert search_mock.call_count == 2 - - -def test_metadata_download_fail_when_file_doesnot_exist(mocker, capfd): - mocker.patch( - 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', - return_value=decoded_token(), - ) - - search_mock = mocker.patch( - 'app.services.file_manager.file_metadata.file_metadata_client.search_item', - ) - search_mock.side_effect = [{'result': {}, 'code': 404}, {'result': {}, 'code': 404}] - - file_meta_client = FileMetaClient('zone', 'project_code/object_path', 'general', 'attr', 'tag') - try: file_meta_client.download_file_metadata() except SystemExit: - assert search_mock.call_count == 2 + assert search_mock.call_count == 1 out, _ = capfd.readouterr() - expect = 'Cannot find item project_code/object_path at zone.\n' + expect = f'Cannot find item project_code/{root_folder.get_prefix_by_type()}/file_name at zone.\n' assert out == expect else: AssertionError('SystemExit not raised') diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index 34e56d96..907d51b9 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -132,7 +132,7 @@ def test_assemble_path_at_non_existing_folder(mocker): def test_assemble_path_at_project_folder(mocker): local_file_path = './test/file.txt' - target_folder = 'project_folder' + target_folder = ItemType.SHAREDFOLDER.value project_code = 'test_project' zone = 0 @@ -143,9 +143,9 @@ def test_assemble_path_at_project_folder(mocker): 'id': 'test', 'parent_id': 'test_parent', 'parent_path': '', - 'name': 'project_folder', + 'name': target_folder, 'zone': 0, - 'type': 'project_folder', + 'type': target_folder, } }, )