diff --git a/app/commands/file.py b/app/commands/file.py index 319f2221..b40f284c 100644 --- a/app/commands/file.py +++ b/app/commands/file.py @@ -430,14 +430,12 @@ def file_download(**kwargs): else: item_res = [] for path in paths: - project_code, root_folder = path.strip('/').split('/')[:2] - target_path = '/'.join(path.split('/')[1::]) - # search the root to check for name folder or project folder - root_item = search_item(project_code, zone, root_folder).get('result', {}) - target_path = 'shared/' + target_path if root_item.get('type') == 'project_folder' else target_path + project_code, root_folder, object_path = path.strip('/').split('/', 2) + root_type = ItemType.get_type_from_keyword(root_folder) + object_path = os.path.join(root_type.get_prefix_by_type(), object_path) # search the target item and download to local - item = search_item(project_code, zone, target_path) + item = search_item(project_code, zone, object_path) if item.get('code') == 200 and item.get('result'): item_status = 'success' item_result = item.get('result') diff --git a/app/models/item.py b/app/models/item.py index b7c84b6c..0b8ba0a4 100644 --- a/app/models/item.py +++ b/app/models/item.py @@ -39,3 +39,8 @@ def get_prefix_by_type(self) -> str: } return prefix.get(self.value, '') + + +class ItemZone(str, Enum): + GREENROOM = 'greenroom' + CORE = 'core' diff --git a/app/services/file_manager/file_download/download_client.py b/app/services/file_manager/file_download/download_client.py index d9f0ca4c..3e5b7b4a 100644 --- a/app/services/file_manager/file_download/download_client.py +++ b/app/services/file_manager/file_download/download_client.py @@ -15,6 +15,7 @@ import app.services.output_manager.message_handler as mhandler from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig +from app.models.item import ItemZone from app.models.service_meta_class import MetaService from app.services.output_manager.error_handler import ECustomizedError from app.services.output_manager.error_handler import SrvErrorHandler @@ -53,7 +54,7 @@ def print_prepare_msg(self, message): click.secho(f"{message}{'.'*i}\r", fg='white', nl=False) def get_download_url(self, zone): - if zone == 'greenroom': + if zone == ItemZone.GREENROOM.value: url = self.appconfig.Connections.url_download_greenroom else: url = self.appconfig.Connections.url_download_core @@ -101,11 +102,11 @@ def prepare_download(self): file_path = download_info.get('file_path') pre_status = EFileStatus(response.get('status')) elif res.status_code == 403: - SrvErrorHandler.customized_handle(ECustomizedError.NO_FILE_PERMMISION, self.interactive) + SrvErrorHandler.customized_handle(ECustomizedError.NO_FILE_PERMMISION, if_exit=self.interactive) elif res.status_code == 400 and 'number of file must greater than 0' in res_json.get('error_msg'): - SrvErrorHandler.customized_handle(ECustomizedError.FOLDER_EMPTY, self.interactive) + SrvErrorHandler.customized_handle(ECustomizedError.FOLDER_EMPTY, if_exit=self.interactive) else: - SrvErrorHandler.customized_handle(ECustomizedError.DOWNLOAD_FAIL, self.interactive) + SrvErrorHandler.customized_handle(ECustomizedError.DOWNLOAD_FAIL, if_exit=self.interactive) return pre_status, file_path diff --git a/app/services/file_manager/file_download/model.py b/app/services/file_manager/file_download/model.py index b39745c2..fa990db5 100644 --- a/app/services/file_manager/file_download/model.py +++ b/app/services/file_manager/file_download/model.py @@ -5,11 +5,8 @@ from enum import Enum -class EFileStatus(Enum): +class EFileStatus(str, Enum): WAITING = 'WAITING' RUNNING = 'RUNNING' SUCCEED = 'SUCCEED' FAILED = 'FAILED' - - def __str__(self): - return '%s' % self.name diff --git a/tests/app/commands/test_file.py b/tests/app/commands/test_file.py index 8b558768..82241184 100644 --- a/tests/app/commands/test_file.py +++ b/tests/app/commands/test_file.py @@ -224,7 +224,7 @@ def test_file_download_success(requests_mock, mocker, cli_runner, parent_folder_ { 'code': 200, 'result': { - 'type': parent_folder_type, + 'type': parent_folder_type.value, 'name': 'test', 'id': 'id', }, @@ -244,13 +244,12 @@ def test_file_download_success(requests_mock, mocker, cli_runner, parent_folder_ return_value=None, ) - project_code, target_folder = 'testproject', 'test/test.txt' + project_code, target_folder = 'testproject', parent_folder_type.get_prefix_by_type() + 'test/test.txt' result = cli_runner.invoke(file_download, [f'{project_code}/{target_folder}', './']) outputs = result.output.split('\n') assert outputs[0] == '' - except_target_folder = 'test/test.txt' if parent_folder_type == 'name_folder' else 'shared/test/test.txt' - search_mock.assert_called_with(project_code, 'greenroom', except_target_folder) + search_mock.assert_called_with(project_code, 'greenroom', target_folder) download_mock.assert_called_once() diff --git a/tests/app/services/file_manager/file_download/test_file_download_client.py b/tests/app/services/file_manager/file_download/test_file_download_client.py new file mode 100644 index 00000000..64687b4c --- /dev/null +++ b/tests/app/services/file_manager/file_download/test_file_download_client.py @@ -0,0 +1,143 @@ +# Copyright (C) 2022-2024 Indoc Systems +# +# Contact Indoc Systems for any questions regarding the use of this source code. + +import click +import jwt +import pytest +from pytest_httpx import IteratorStream + +from app.configs.app_config import AppConfig +from app.models.item import ItemZone +from app.services.file_manager.file_download.download_client import SrvFileDownload +from app.services.file_manager.file_download.model import EFileStatus +from app.services.output_manager.error_handler import ECustomizedError +from app.services.output_manager.error_handler import customized_error_msg +from tests.conftest import decoded_token + + +@pytest.mark.parametrize('file_status', [EFileStatus.SUCCEED, EFileStatus.FAILED, EFileStatus.WAITING]) +def test_file_download_client_prepare_download_success(mocker, httpx_mock, file_status: EFileStatus): + test_file_path = 'test_file_path' + hash_token = jwt.encode({'file_path': test_file_path}, key='unittest', algorithm='HS256').decode('utf-8') + + mocker.patch( + 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', + return_value=decoded_token(), + ) + + download_client = SrvFileDownload(0, False) + download_client.file_geid = ['test'] + download_client.project_code = 'test_project' + + httpx_mock.add_response( + url=download_client.appconfig.Connections.url_v2_download_pre % (download_client.project_code), + method='POST', + status_code=200, + json={ + 'result': { + 'payload': { + 'hash_code': hash_token, + }, + 'status': file_status.value, + } + }, + ) + + pre_status, file_path = download_client.prepare_download() + assert pre_status == file_status + assert file_path == test_file_path + + +@pytest.mark.parametrize( + 'status_code', + [ + 403, + 400, + 500, + ], +) +def test_file_download_client_prepare_download_failed(mocker, httpx_mock, capfd, status_code: int): + mocker.patch( + 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', + return_value=decoded_token(), + ) + + download_client = SrvFileDownload(0, True) + download_client.file_geid = ['test'] + download_client.project_code = 'test_project' + + httpx_mock.add_response( + url=download_client.appconfig.Connections.url_v2_download_pre % (download_client.project_code), + method='POST', + status_code=status_code, + json={'error_msg': 'number of file must greater than 0'}, + ) + + try: + download_client.pre_download() + except SystemExit: + out, _ = capfd.readouterr() + + expect = { + 500: ECustomizedError.DOWNLOAD_FAIL, + 403: ECustomizedError.NO_FILE_PERMMISION, + 400: ECustomizedError.FOLDER_EMPTY, + } + assert out == customized_error_msg(expect.get(status_code)) + '\n' + else: + AssertionError('SystemExit not raised') + + +@pytest.mark.parametrize( + 'zone', + [ + ItemZone.GREENROOM.value, + ItemZone.CORE.value, + ], +) +def test_file_download_url_based_on_different_zones(zone: str): + download_client = SrvFileDownload(0, True) + download_client.file_geid = ['test'] + download_client.project_code = 'test_project' + + url = download_client.get_download_url(zone) + except_url = { + ItemZone.GREENROOM.value: AppConfig.Connections.url_download_greenroom, + ItemZone.CORE.value: AppConfig.Connections.url_download_core, + }.get(zone) + + assert url == except_url + + +@pytest.mark.parametrize( + 'total_size_presented', + [True, False], +) +def test_file_stream_download(mocker, httpx_mock, total_size_presented): + file_url = 'http://test.com' + file_content = b'123' + + mocker.patch( + 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', + return_value=decoded_token(), + ) + + httpx_mock.add_response( + url=file_url, + method='GET', + status_code=200, + content=IteratorStream([file_content]), + ) + + runner = click.testing.CliRunner() + with runner.isolated_filesystem(): + download_client = SrvFileDownload(0, True) + download_client.file_geid = ['test'] + download_client.project_code = 'test_project' + download_client.total_size = len(file_content) if total_size_presented else None + + download_client.download_file(file_url, 'test_file') + + with open('test_file', 'r') as f: + assert f.read() == '123'