Skip to content
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

PILOT 4808: CLI differentiates the item path of move/rename between Project Folders and Name Folders #137

Merged
merged 22 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion app/commands/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
2 changes: 1 addition & 1 deletion app/models/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
12 changes: 7 additions & 5 deletions app/services/file_manager/file_metadata/file_metadata_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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', [])
Expand Down
5 changes: 0 additions & 5 deletions app/services/file_manager/file_move/file_move_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion app/services/project_manager/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"]

Expand Down
9 changes: 6 additions & 3 deletions tests/app/commands/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down Expand Up @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -41,24 +52,34 @@ 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
assert res_attributes == {attri_template_name: attributes.get(attri_template_uid)}
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(
Expand All @@ -76,84 +97,44 @@ 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
assert res_attributes == {}
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(),
)

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')
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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,
}
},
)
Expand Down
Loading