Create config files automatically and UX style updates (#3514)
This commit is contained in:
@@ -76,6 +76,7 @@
|
||||
</qresource>
|
||||
<qresource prefix="/Application">
|
||||
<file>res/o3de_editor.ico</file>
|
||||
<file alias="o3de_application_reverse.svg">res/o3de_application_reverse.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/Icons">
|
||||
<file alias="Eye.svg">res/Eye.svg</file>
|
||||
@@ -152,6 +153,7 @@
|
||||
<file alias="error_report_error.svg">res/error_report_error.svg</file>
|
||||
<file alias="error_report_warning.svg">res/error_report_warning.svg</file>
|
||||
<file alias="error_report_comment.svg">res/error_report_comment.svg</file>
|
||||
<file alias="error_report_helper.svg">res/error_report_helper.svg</file>
|
||||
<file>particles_tree_00.png</file>
|
||||
<file>particles_tree_01.png</file>
|
||||
<file>particles_tree_02.png</file>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="21px" height="21px" viewBox="0 0 21 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icons / Notification / Helpers</title>
|
||||
<g id="primary-use-cases" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Screen-1-Copy-61" transform="translate(-620.000000, -295.000000)">
|
||||
<g id="Modals-/-Scrollable-Modal" transform="translate(594.114148, 244.000000)">
|
||||
<g id="Group" transform="translate(24.000000, 49.500000)">
|
||||
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
|
||||
<path d="M12,2 C17.5228475,2 22,6.4771525 22,12 C22,17.5228475 17.5228475,22 12,22 C6.4771525,22 2,17.5228475 2,12 C2,6.4771525 6.4771525,2 12,2 Z M12.4748737,17 L10.4748737,17 L10.4748737,20 L12.4748737,20 L12.4748737,17 Z M8.47487373,5 L5.5,8.06066017 L6.91421356,9.47487373 L9.324,7 L14.9,7 L16,7.888 L16,11.306 L15.501,12 L10.4748737,12 L10.4748737,15 L12.4748737,15 L12.474,14 L16.4748737,14 L17.998,12 L18,12 L18,7 L15.4748737,5 L8.47487373,5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="21px" viewBox="0 0 24 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>o3de icon</title>
|
||||
<g id="o3de-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Mono-Reversed" transform="translate(0.000000, 0.000000)" fill="#000000" fill-rule="nonzero">
|
||||
<path d="M21.1541405,17.6979529 C19.3400003,19.4999226 16.9380429,20.4923077 14.3913014,20.4923077 L14.390648,20.4923077 L14.3450938,20.4923077 C11.7797759,20.4803636 9.3640962,19.4651168 7.54323489,17.6334736 C5.11112576,15.1866169 4.27677437,11.7429896 5.03495316,8.61000159 L8.51135509,8.61000159 C8.23037526,9.33504454 8.08409772,10.1140225 8.08885851,10.9163286 C8.09866013,12.5958715 8.76171652,14.1683842 9.95574077,15.3439429 C11.1512586,16.5210879 12.7239055,17.1693342 14.3843003,17.1693342 L14.4020365,17.1692408 C16.0735398,17.163922 17.6458133,16.505038 18.8316229,15.3138961 C20.0308746,14.109317 20.6859031,12.5198213 20.6761947,10.8382256 C20.6663931,9.15513684 19.9919482,7.57151988 18.7770139,6.37907152 C17.5971786,5.2210557 16.046842,4.58577998 14.4049303,4.58577998 C14.3803796,4.58577998 14.3559222,4.5859666 14.3311848,4.58624655 C13.4169268,4.59604442 12.5319804,4.80254636 11.7229265,5.18298395 L11.7229265,1.63624556 C12.5795883,1.3889658 13.4737762,1.25879399 14.3867273,1.25879399 L14.4338684,1.25879399 C17.005814,1.27120464 19.4248544,2.29568939 21.2452489,4.14366241 C23.038479,5.96410807 24.0166808,8.37475921 23.9997847,10.9315387 C23.9828886,13.4891581 22.9722947,15.8920641 21.1541405,17.6979529 L21.1541405,17.6979529 Z M1.11066379,1.9110527 L3.02244681,1.9110527 L3.02244681,0 L1.11066379,0 L1.11066379,1.9110527 Z M0,7.6534488 L2.99948301,7.6534488 L2.99948301,4.65511162 L0,4.65511162 L0,7.6534488 Z M4.90426487,6.43272657 L9.52867009,6.43272657 L9.52867009,1.8100879 L4.90426487,1.8100879 L4.90426487,6.43272657 Z" id="Fill-2"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -68,7 +68,7 @@ namespace AWSCore
|
||||
},
|
||||
"AccountIdString": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{12}$"
|
||||
"pattern": "^[0-9]{12}$|EMPTY"
|
||||
},
|
||||
"NonEmptyString": {
|
||||
"type": "string",
|
||||
|
||||
@@ -66,6 +66,13 @@ R"({
|
||||
"Region": "123",
|
||||
"Version": "123"
|
||||
})";
|
||||
static constexpr const char TEST_TEMPLATE_RESOURCE_MAPPING_CONFIG_FILE[] =
|
||||
R"({
|
||||
"AWSResourceMappings": {},
|
||||
"AccountId": "EMPTY",
|
||||
"Region": "us-west-2",
|
||||
"Version": "1.0.0"
|
||||
})";
|
||||
|
||||
class AWSResourceMappingManagerTest
|
||||
: public AWSCoreFixture
|
||||
@@ -190,6 +197,21 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseTemplateConfigFile_ConfigDataIsNotEmpty)
|
||||
{
|
||||
CreateTestConfigFile(TEST_TEMPLATE_RESOURCE_MAPPING_CONFIG_FILE);
|
||||
m_resourceMappingManager->ActivateManager();
|
||||
|
||||
AZStd::string actualAccountId;
|
||||
AZStd::string actualRegion;
|
||||
AWSResourceMappingRequestBus::BroadcastResult(actualAccountId, &AWSResourceMappingRequests::GetDefaultAccountId);
|
||||
AWSResourceMappingRequestBus::BroadcastResult(actualRegion, &AWSResourceMappingRequests::GetDefaultRegion);
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 0);
|
||||
EXPECT_FALSE(actualAccountId.empty());
|
||||
EXPECT_FALSE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_ConfigDataIsNotEmptyWithMultithreadCalls)
|
||||
{
|
||||
CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE);
|
||||
|
||||
@@ -68,12 +68,17 @@ class ViewEditController(QObject):
|
||||
# convert model resources into json dict
|
||||
json_dict: Dict[str, any] = \
|
||||
json_utils.convert_resources_to_json_dict(self._proxy_model.get_resources(), self._config_file_json_source)
|
||||
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
if json_dict.get(json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME) == \
|
||||
json_utils.RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE:
|
||||
json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = configuration.account_id
|
||||
|
||||
if json_dict == self._config_file_json_source:
|
||||
# skip because no difference found against existing json file
|
||||
return True
|
||||
|
||||
# try to write in memory json content into json file
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
try:
|
||||
config_file_full_path: str = file_utils.join_path(configuration.config_directory, config_file_name)
|
||||
json_utils.write_into_json_file(config_file_full_path, json_dict)
|
||||
@@ -103,31 +108,6 @@ class ViewEditController(QObject):
|
||||
for resource in resources:
|
||||
self._proxy_model.load_resource(resource)
|
||||
|
||||
def _create_new_config_file(self) -> None:
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
self._set_default_region(configuration)
|
||||
|
||||
try:
|
||||
new_config_file_path: str = file_utils.join_path(
|
||||
configuration.config_directory, constants.RESOURCE_MAPPING_DEFAULT_CONFIG_FILE_NAME)
|
||||
json_utils.create_empty_resource_mapping_file(
|
||||
new_config_file_path, configuration.account_id, configuration.region)
|
||||
except IOError as e:
|
||||
logger.exception(e)
|
||||
self.set_notification_frame_text_sender.emit(str(e))
|
||||
return
|
||||
|
||||
self._rescan_config_directory()
|
||||
|
||||
def _set_default_region(self, configuration: Configuration):
|
||||
default_region = configuration.region
|
||||
if not default_region or default_region == 'aws-global':
|
||||
self.set_notification_frame_text_sender.emit(
|
||||
notification_label_text.VIEW_EDIT_PAGE_CREATE_NEW_CONFIG_FILE_NO_DEFAULT_REGION_MESSAGE)
|
||||
logger.warning(notification_label_text.VIEW_EDIT_PAGE_CREATE_NEW_CONFIG_FILE_NO_DEFAULT_REGION_MESSAGE)
|
||||
|
||||
configuration.region = constants.RESOURCE_MAPPING_DEFAULT_CONFIG_FILE_REGION
|
||||
|
||||
def _delete_table_row(self) -> None:
|
||||
indices: List[QModelIndex] = self._table_view.selectedIndexes()
|
||||
self._proxy_model.remove_resources(indices)
|
||||
@@ -246,14 +226,13 @@ class ViewEditController(QObject):
|
||||
self._view_edit_page.save_changes_button.clicked.connect(self._save_changes)
|
||||
self._view_edit_page.search_filter_input.returnPressed.connect(self._filter_based_on_search_text)
|
||||
self._view_edit_page.cancel_button.clicked.connect(self._cancel)
|
||||
self._view_edit_page.create_new_button.clicked.connect(self._create_new_config_file)
|
||||
self._view_edit_page.rescan_button.clicked.connect(self._rescan_config_directory)
|
||||
|
||||
def _setup_page_start_state(self) -> None:
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
self._view_edit_page.set_current_main_view_index(ViewEditPageConstants.NOTIFICATION_PAGE_INDEX)
|
||||
self._view_edit_page.set_config_files(configuration.config_files)
|
||||
self._view_edit_page.set_config_location(configuration.config_directory)
|
||||
self._view_edit_page.set_config_files(configuration.config_files)
|
||||
|
||||
def _switch_to_import_resources_page(self) -> None:
|
||||
if self._view_edit_page.import_resources_combobox.currentIndex() == -1:
|
||||
@@ -297,5 +276,5 @@ class ViewEditController(QObject):
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Setting view edit page starting state and bind interactions with its corresponding behavior"""
|
||||
self._setup_page_start_state()
|
||||
self._setup_page_interactions_behavior()
|
||||
self._setup_page_start_state()
|
||||
|
||||
@@ -44,7 +44,7 @@ class ViewManager(object):
|
||||
def __init__(self) -> None:
|
||||
if ViewManager.__instance is None:
|
||||
self._main_window: QMainWindow = QMainWindow()
|
||||
self._main_window.setWindowIcon(QIcon(":/Application/res/o3de_editor.ico"))
|
||||
self._main_window.setWindowIcon(QIcon(":/Application/o3de_application_reverse.svg"))
|
||||
self._main_window.setWindowTitle("Resource Mapping")
|
||||
self._main_window.setGeometry(0, 0,
|
||||
view_size_constants.TOOL_APPLICATION_MAIN_WINDOW_WIDTH,
|
||||
|
||||
@@ -5,11 +5,11 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
ERROR_PAGE_TOOL_SETUP_ERROR_TITLE: str = "AWS credentials are missing or invalid"
|
||||
|
||||
ERROR_PAGE_TOOL_SETUP_ERROR_MESSAGE: str = \
|
||||
"AWS credentials are missing or invalid. See " \
|
||||
"<a href=\"https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-core/configuring-credentials/\">"\
|
||||
"<span style=\"color:#4A90E2;\">documentation</span></a> for details." \
|
||||
"<br>Check log file under Gems/AWSCore/Code/Tool/ResourceMappingTool for further information."
|
||||
"Use our <a href=\"https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-core/configuring-credentials/\">"\
|
||||
"<span style=\"color:#4A90E2;\">documentation</span></a> to setup your AWS credentials. "
|
||||
|
||||
VIEW_EDIT_PAGE_SAVING_FAILED_WITH_INVALID_ROW_ERROR_MESSAGE: str = \
|
||||
"Row {} have errors. Please correct errors or delete the row to proceed."
|
||||
|
||||
@@ -13,6 +13,7 @@ ERROR_PAGE_OK_TEXT: str = "OK"
|
||||
|
||||
VIEW_EDIT_PAGE_CONFIG_FILE_TEXT: str = "Config File"
|
||||
VIEW_EDIT_PAGE_CONFIG_LOCATION_TEXT: str = "Config Location:"
|
||||
VIEW_EDIT_PAGE_INVALID_CONFIG_LOCATION_TEXT: str = "Invalid Location"
|
||||
VIEW_EDIT_PAGE_ADD_ROW_TEXT: str = "Add Row"
|
||||
VIEW_EDIT_PAGE_DELETE_ROW_TEXT: str = "Delete Row"
|
||||
VIEW_EDIT_PAGE_SAVE_CHANGES_TEXT: str = "Save Changes"
|
||||
@@ -23,17 +24,11 @@ VIEW_EDIT_PAGE_RESCAN_TEXT: str = "Rescan"
|
||||
VIEW_EDIT_PAGE_CONFIG_FILES_PLACEHOLDER_TEXT: str = "Found {} config files"
|
||||
VIEW_EDIT_PAGE_SEARCH_PLACEHOLDER_TEXT: str = "Search by Key Name, Type, Name/ID, Account ID or Region"
|
||||
VIEW_EDIT_PAGE_IMPORT_RESOURCES_PLACEHOLDER_TEXT: str = "Import Additional Resources"
|
||||
VIEW_EDIT_PAGE_CREATE_NEW_CONFIG_FILE_NO_DEFAULT_REGION_MESSAGE: str = \
|
||||
f"Resource mapping file {constants.RESOURCE_MAPPING_DEFAULT_CONFIG_FILE_NAME} is created"\
|
||||
f" with {constants.RESOURCE_MAPPING_DEFAULT_CONFIG_FILE_REGION} as the default region. "\
|
||||
f"See <a href=\"https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-core/configuring-credentials/\">"\
|
||||
f"<span style=\"color:#4A90E2;\">documentation</span></a> "\
|
||||
f"for configuring the AWS credentials and default region."
|
||||
|
||||
VIEW_EDIT_PAGE_SELECT_CONFIG_FILE_MESSAGE: str = "Please select the Config file you would like to view and modify..."
|
||||
VIEW_EDIT_PAGE_NO_CONFIG_FILE_FOUND_TITLE: str = "Unable to locate a resource mapping config file"
|
||||
VIEW_EDIT_PAGE_NO_CONFIG_FILE_FOUND_MESSAGE: str = \
|
||||
"<p><b>Unable to locate a resource mapping config file.</b></p>"\
|
||||
"<p>We can either make this file for you or you can rescan your directory.</p>"
|
||||
"<p>Please select your project's config file directory using the browse tool in the upper right.</p>"
|
||||
VIEW_EDIT_PAGE_SAVING_SUCCEED_MESSAGE: str = "Config file {} is saved successfully."
|
||||
|
||||
IMPORT_RESOURCES_PAGE_BACK_TEXT: str = "Back"
|
||||
|
||||
@@ -14,13 +14,14 @@ MAIN_PAGE_LAYOUT_MARGIN_TOPBOTTOM: int = 15
|
||||
INTERACTION_COMPONENT_HEIGHT: int = 25
|
||||
|
||||
"""error page related constants"""
|
||||
ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT: int = 10
|
||||
ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM: int = 10
|
||||
ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT: int = 25
|
||||
ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM: int = 20
|
||||
ERROR_PAGE_LAYOUT_SPACING: int = 15
|
||||
|
||||
ERROR_PAGE_MAIN_WINDOW_WIDTH: int = 600
|
||||
ERROR_PAGE_MAIN_WINDOW_HEIGHT: int = 145
|
||||
ERROR_PAGE_MAIN_WINDOW_WIDTH: int = 100
|
||||
ERROR_PAGE_MAIN_WINDOW_HEIGHT: int = 75
|
||||
|
||||
ERROR_PAGE_NOTIFICATION_AREA_HEIGHT: int = 100
|
||||
ERROR_PAGE_NOTIFICATION_AREA_HEIGHT: int = 30
|
||||
ERROR_PAGE_FOOTER_AREA_HEIGHT: int = 45
|
||||
|
||||
OK_BUTTON_WIDTH: int = 90
|
||||
|
||||
@@ -35,6 +35,17 @@ QComboBox
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
[HighlightField=true]
|
||||
{
|
||||
border:3px solid #E57829;
|
||||
}
|
||||
|
||||
[HighlightText=true]
|
||||
{
|
||||
font-weight: bold;
|
||||
color: #E57829;
|
||||
}
|
||||
|
||||
QToolTip
|
||||
{
|
||||
color: black;
|
||||
@@ -356,7 +367,7 @@ QScrollBar::handle:hover
|
||||
}
|
||||
|
||||
/* NotificationFrame */
|
||||
QFrame#NotificationFrame
|
||||
NotificationFrame
|
||||
{
|
||||
background-color: transparent;
|
||||
border: 1px solid #E57829;
|
||||
@@ -365,17 +376,38 @@ QFrame#NotificationFrame
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
QFrame#ErrorPage
|
||||
NotificationFrame QLabel#NotificationIcon
|
||||
{
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid #4A90E2;
|
||||
border-radius: 2px;
|
||||
margin: 0px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
QFrame#ErrorPage QLabel#NotificationIcon
|
||||
{
|
||||
padding-left: 15px;
|
||||
padding-left: 0px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
NotificationFrame QLabel#NotificationTitle
|
||||
{
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
NotificationFrame#ErrorPage
|
||||
{
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
NotificationFrame#ErrorPage QLabel#NotificationIcon
|
||||
{
|
||||
qproperty-alignment: "AlignTop";
|
||||
}
|
||||
|
||||
NotificationFrame#ErrorPage QLabel#NotificationTitle
|
||||
{
|
||||
padding-top: 0px;
|
||||
text-align: top;
|
||||
}
|
||||
|
||||
NotificationFrame#ErrorPage QLabel#NotificationContent
|
||||
{
|
||||
padding-top: 0px;
|
||||
text-align: top;
|
||||
qproperty-wordWrap: false;
|
||||
}
|
||||
|
||||
+27546
-27257
File diff suppressed because it is too large
Load Diff
+29
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import (call, MagicMock, patch)
|
||||
|
||||
from controller.error_controller import ErrorController
|
||||
|
||||
class TestErrorController(TestCase):
|
||||
"""
|
||||
ErrorController unit test cases
|
||||
"""
|
||||
def setUp(self) -> None:
|
||||
view_manager_patcher: patch = patch("controller.error_controller.ViewManager")
|
||||
self.addCleanup(view_manager_patcher.stop)
|
||||
self._mock_view_manager: MagicMock = view_manager_patcher.start()
|
||||
|
||||
self._mocked_view_manager: MagicMock = self._mock_view_manager.get_instance.return_value
|
||||
self._mocked_error_page: MagicMock = self._mocked_view_manager.get_error_page.return_value
|
||||
|
||||
self._test_error_controller: ErrorController = ErrorController()
|
||||
self._test_error_controller.setup()
|
||||
|
||||
def test_setup_with_expected_behavior_connected(self) -> None:
|
||||
self._mocked_error_page.ok_button.clicked.connect.assert_called_once_with(self._test_error_controller._ok)
|
||||
+9
-34
@@ -22,6 +22,8 @@ class TestViewEditController(TestCase):
|
||||
TODO: add test cases once error handling is ready
|
||||
"""
|
||||
_expected_account_id: str = "1234567890"
|
||||
_expected_account_id_attribute_name = "AccountId"
|
||||
_expected_account_id_template_vale: str = "EMPTY"
|
||||
_expected_region: str = "aws-global"
|
||||
_expected_config_directory: str = "dummy/directory/"
|
||||
_expected_config_file_name: str = "dummy.json"
|
||||
@@ -98,8 +100,6 @@ class TestViewEditController(TestCase):
|
||||
self._test_view_edit_controller._filter_based_on_search_text)
|
||||
self._mocked_view_edit_page.cancel_button.clicked.connect.assert_called_once_with(
|
||||
self._test_view_edit_controller._cancel)
|
||||
self._mocked_view_edit_page.create_new_button.clicked.connect.assert_called_once_with(
|
||||
self._test_view_edit_controller._create_new_config_file)
|
||||
self._mocked_view_edit_page.rescan_button.clicked.connect.assert_called_once_with(
|
||||
self._test_view_edit_controller._rescan_config_directory)
|
||||
|
||||
@@ -301,6 +301,7 @@ class TestViewEditController(TestCase):
|
||||
expected_new_config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX)
|
||||
assert self._mocked_configuration_manager.configuration.config_files == []
|
||||
self._mocked_view_edit_page.set_config_files.assert_called_with([])
|
||||
self._mocked_view_edit_page.set_config_location.assert_called_with(expected_new_config_directory)
|
||||
self._test_view_edit_controller.set_notification_page_frame_text_sender.emit.assert_called_once_with(
|
||||
notification_label_text.NOTIFICATION_LOADING_MESSAGE)
|
||||
|
||||
@@ -418,13 +419,18 @@ class TestViewEditController(TestCase):
|
||||
self, mock_json_utils: MagicMock, mock_file_utils: MagicMock) -> None:
|
||||
self._mocked_view_edit_page.config_file_combobox.currentText.return_value = \
|
||||
TestViewEditController._expected_config_file_name
|
||||
expected_json_dict: Dict[str, any] = {"dummyKey": "dummyValue"}
|
||||
expected_json_dict: Dict[str, any] = {
|
||||
"dummyKey": "dummyValue",
|
||||
self._expected_account_id_attribute_name: self._expected_account_id_template_vale}
|
||||
mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME = self._expected_account_id_attribute_name
|
||||
mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE = self._expected_account_id_template_vale
|
||||
mock_json_utils.validate_resources_according_to_json_schema.return_value = []
|
||||
mock_json_utils.convert_resources_to_json_dict.return_value = expected_json_dict
|
||||
mock_file_utils.join_path.return_value = TestViewEditController._expected_config_file_full_path
|
||||
mocked_call_args: call = self._mocked_view_edit_page.save_changes_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering save_changes_button connected function
|
||||
assert expected_json_dict["AccountId"] == self._mocked_configuration_manager.configuration.account_id
|
||||
mock_json_utils.convert_resources_to_json_dict.assert_called_once()
|
||||
mock_json_utils.write_into_json_file.assert_called_once_with(
|
||||
TestViewEditController._expected_config_file_full_path, expected_json_dict)
|
||||
@@ -468,37 +474,6 @@ class TestViewEditController(TestCase):
|
||||
mocked_call_args[0]() # triggering cancel_button connected function
|
||||
mock_application.instance.return_value.quit.assert_called_once()
|
||||
|
||||
@patch("controller.view_edit_controller.file_utils")
|
||||
@patch("controller.view_edit_controller.json_utils")
|
||||
def test_page_create_new_button_invoke_view_edit_page_set_config_files_with_expected_config_files(
|
||||
self, mock_json_utils: MagicMock, mock_file_utils: MagicMock) -> None:
|
||||
expected_config_files: List[str] = ["dummyfile"]
|
||||
mock_file_utils.find_files_with_suffix_under_directory.return_value = expected_config_files
|
||||
mocked_call_args: call = \
|
||||
self._mocked_view_edit_page.create_new_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering create_new_button connected function
|
||||
mock_file_utils.join_path.assert_called_once()
|
||||
mock_json_utils.create_empty_resource_mapping_file.assert_called_once()
|
||||
mock_file_utils.find_files_with_suffix_under_directory.assert_called_once()
|
||||
self._mocked_view_edit_page.set_config_files.assert_called_with(expected_config_files)
|
||||
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
|
||||
|
||||
@patch("controller.view_edit_controller.file_utils")
|
||||
@patch("controller.view_edit_controller.json_utils")
|
||||
def test_page_create_new_button_post_notification_when_create_file_throw_exception(
|
||||
self, mock_json_utils: MagicMock, mock_file_utils: MagicMock) -> None:
|
||||
expected_config_files: List[str] = ["dummyfile"]
|
||||
mock_json_utils.create_empty_resource_mapping_file.side_effect = IOError("dummy IO error")
|
||||
mocked_call_args: call = \
|
||||
self._mocked_view_edit_page.create_new_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering create_new_button connected function
|
||||
mock_file_utils.join_path.assert_called_once()
|
||||
mock_json_utils.create_empty_resource_mapping_file.assert_called_once()
|
||||
mock_file_utils.find_files_with_suffix_under_directory.assert_not_called()
|
||||
assert len(self._test_view_edit_controller.set_notification_frame_text_sender.emit.mock_calls) == 2
|
||||
|
||||
@patch("controller.view_edit_controller.file_utils")
|
||||
def test_page_rescan_button_post_notification_when_find_files_throw_exception(
|
||||
self, mock_file_utils: MagicMock) -> None:
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestJsonUtils(TestCase):
|
||||
json_utils._RESOURCE_MAPPING_TYPE_JSON_KEY_NAME: _expected_bucket_type,
|
||||
json_utils._RESOURCE_MAPPING_NAMEID_JSON_KEY_NAME: _expected_bucket_name
|
||||
}},
|
||||
json_utils._RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: _expected_account_id,
|
||||
json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: _expected_account_id,
|
||||
json_utils._RESOURCE_MAPPING_REGION_JSON_KEY_NAME: _expected_region,
|
||||
json_utils._RESOURCE_MAPPING_VERSION_JSON_KEY_NAME: json_utils._RESOURCE_MAPPING_JSON_FORMAT_VERSION
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class TestJsonUtils(TestCase):
|
||||
|
||||
def test_convert_resources_to_json_dict_return_expected_json_dict(self) -> None:
|
||||
old_json_dict: Dict[str, any] = {
|
||||
json_utils._RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: TestJsonUtils._expected_account_id,
|
||||
json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: TestJsonUtils._expected_account_id,
|
||||
json_utils._RESOURCE_MAPPING_REGION_JSON_KEY_NAME: TestJsonUtils._expected_region
|
||||
}
|
||||
actual_json_dict: Dict[str, any] = \
|
||||
@@ -76,20 +76,6 @@ class TestJsonUtils(TestCase):
|
||||
json_utils.convert_json_dict_to_resources(TestJsonUtils._expected_json_dict)
|
||||
assert actual_resources == [TestJsonUtils._expected_bucket_resource_mapping]
|
||||
|
||||
def test_create_empty_resource_mapping_file_succeed(self) -> None:
|
||||
mocked_open: MagicMock = MagicMock()
|
||||
self._mock_open.return_value.__enter__.return_value = mocked_open
|
||||
expected_json_dict: Dict[str, any] = \
|
||||
{json_utils._RESOURCE_MAPPING_JSON_KEY_NAME: {},
|
||||
json_utils._RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: TestJsonUtils._expected_account_id,
|
||||
json_utils._RESOURCE_MAPPING_REGION_JSON_KEY_NAME: TestJsonUtils._expected_region,
|
||||
json_utils._RESOURCE_MAPPING_VERSION_JSON_KEY_NAME: json_utils._RESOURCE_MAPPING_JSON_FORMAT_VERSION}
|
||||
json_utils.create_empty_resource_mapping_file(TestJsonUtils._expected_file_name,
|
||||
TestJsonUtils._expected_account_id,
|
||||
TestJsonUtils._expected_region)
|
||||
self._mock_open.assert_called_once_with(TestJsonUtils._expected_file_name, "w")
|
||||
self._mock_json_dump.assert_called_once_with(expected_json_dict, mocked_open, indent=4, sort_keys=True)
|
||||
|
||||
def test_read_from_json_file_return_expected_json_dict(self) -> None:
|
||||
mocked_open: MagicMock = MagicMock()
|
||||
self._mock_open.return_value.__enter__.return_value = mocked_open
|
||||
@@ -114,12 +100,18 @@ class TestJsonUtils(TestCase):
|
||||
|
||||
def test_validate_json_dict_according_to_json_schema_raise_error_when_json_dict_has_no_accountid(self) -> None:
|
||||
invalid_json_dict: Dict[str, any] = copy.deepcopy(TestJsonUtils._expected_json_dict)
|
||||
invalid_json_dict.pop(json_utils._RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME)
|
||||
invalid_json_dict.pop(json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME)
|
||||
self.assertRaises(KeyError, json_utils.validate_json_dict_according_to_json_schema, invalid_json_dict)
|
||||
|
||||
def test_validate_json_dict_according_to_json_schema_pass_when_json_dict_has_template_accountid(self) -> None:
|
||||
valid_json_dict: Dict[str, any] = copy.deepcopy(TestJsonUtils._expected_json_dict)
|
||||
valid_json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = \
|
||||
json_utils.RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE
|
||||
json_utils.validate_json_dict_according_to_json_schema(valid_json_dict)
|
||||
|
||||
def test_validate_json_dict_according_to_json_schema_raise_error_when_json_dict_has_invalid_accountid(self) -> None:
|
||||
invalid_json_dict: Dict[str, any] = copy.deepcopy(TestJsonUtils._expected_json_dict)
|
||||
invalid_json_dict[json_utils._RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = \
|
||||
invalid_json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = \
|
||||
TestJsonUtils._expected_invalid_account_id
|
||||
self.assertRaises(ValueError, json_utils.validate_json_dict_according_to_json_schema, invalid_json_dict)
|
||||
|
||||
|
||||
@@ -22,16 +22,16 @@ logger = logging.getLogger(__name__)
|
||||
_RESOURCE_MAPPING_JSON_KEY_NAME: str = "AWSResourceMappings"
|
||||
_RESOURCE_MAPPING_TYPE_JSON_KEY_NAME: str = "Type"
|
||||
_RESOURCE_MAPPING_NAMEID_JSON_KEY_NAME: str = "Name/ID"
|
||||
_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: str = "AccountId"
|
||||
_RESOURCE_MAPPING_REGION_JSON_KEY_NAME: str = "Region"
|
||||
_RESOURCE_MAPPING_VERSION_JSON_KEY_NAME: str = "Version"
|
||||
_RESOURCE_MAPPING_JSON_FORMAT_VERSION: str = "1.0.0"
|
||||
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN: str = "^[0-9]{12}$"
|
||||
RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: str = "AccountId"
|
||||
RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE: str = "EMPTY"
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN: str = f"^[0-9]{{12}}|{RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE}$"
|
||||
_RESOURCE_MAPPING_REGION_PATTERN: str = "^[a-z]{2}-[a-z]{4,9}-[0-9]{1}$"
|
||||
_RESOURCE_MAPPING_VERSION_PATTERN: str = "^[0-9]{1}.[0-9]{1}.[0-9]{1}$"
|
||||
|
||||
|
||||
def _add_validation_error_message(errors: Dict[int, List[str]], row: int, error_message: str) -> None:
|
||||
if row in errors.keys():
|
||||
errors[row].append(error_message)
|
||||
@@ -64,9 +64,9 @@ def _validate_required_key_in_json_dict(json_dict: Dict[str, any], json_dict_nam
|
||||
|
||||
def convert_resources_to_json_dict(resources: List[ResourceMappingAttributes],
|
||||
old_json_dict: Dict[str, any]) -> Dict[str, any]:
|
||||
default_account_id: str = old_json_dict[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]
|
||||
default_account_id: str = old_json_dict[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]
|
||||
default_region: str = old_json_dict[_RESOURCE_MAPPING_REGION_JSON_KEY_NAME]
|
||||
json_dict: Dict[str, any] = {_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: default_account_id,
|
||||
json_dict: Dict[str, any] = {RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: default_account_id,
|
||||
_RESOURCE_MAPPING_REGION_JSON_KEY_NAME: default_region,
|
||||
_RESOURCE_MAPPING_VERSION_JSON_KEY_NAME: _RESOURCE_MAPPING_JSON_FORMAT_VERSION,
|
||||
_RESOURCE_MAPPING_JSON_KEY_NAME: {}}
|
||||
@@ -76,7 +76,7 @@ def convert_resources_to_json_dict(resources: List[ResourceMappingAttributes],
|
||||
json_resource_attributes = {_RESOURCE_MAPPING_TYPE_JSON_KEY_NAME: resource.type,
|
||||
_RESOURCE_MAPPING_NAMEID_JSON_KEY_NAME: resource.name_id}
|
||||
if not resource.account_id == default_account_id:
|
||||
json_resource_attributes[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = resource.account_id
|
||||
json_resource_attributes[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = resource.account_id
|
||||
|
||||
if not resource.region == default_region:
|
||||
json_resource_attributes[_RESOURCE_MAPPING_REGION_JSON_KEY_NAME] = resource.region
|
||||
@@ -88,7 +88,7 @@ def convert_resources_to_json_dict(resources: List[ResourceMappingAttributes],
|
||||
|
||||
|
||||
def convert_json_dict_to_resources(json_dict: Dict[str, any]) -> List[ResourceMappingAttributes]:
|
||||
default_account_id: str = json_dict[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]
|
||||
default_account_id: str = json_dict[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]
|
||||
default_region: str = json_dict[_RESOURCE_MAPPING_REGION_JSON_KEY_NAME]
|
||||
|
||||
resources: List[ResourceMappingAttributes] = []
|
||||
@@ -102,8 +102,8 @@ def convert_json_dict_to_resources(json_dict: Dict[str, any]) -> List[ResourceMa
|
||||
.build_type(resource_value[_RESOURCE_MAPPING_TYPE_JSON_KEY_NAME]) \
|
||||
.build_name_id(resource_value[_RESOURCE_MAPPING_NAMEID_JSON_KEY_NAME])
|
||||
|
||||
if _RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME in resource_value.keys():
|
||||
resource_builder.build_account_id(resource_value[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME])
|
||||
if RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME in resource_value.keys():
|
||||
resource_builder.build_account_id(resource_value[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME])
|
||||
else:
|
||||
resource_builder.build_account_id(default_account_id)
|
||||
|
||||
@@ -119,14 +119,6 @@ def convert_json_dict_to_resources(json_dict: Dict[str, any]) -> List[ResourceMa
|
||||
return resources
|
||||
|
||||
|
||||
def create_empty_resource_mapping_file(file_name: str, account_id: str, region: str) -> None:
|
||||
json_dict: Dict[str, any] = {_RESOURCE_MAPPING_JSON_KEY_NAME: {},
|
||||
_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: account_id,
|
||||
_RESOURCE_MAPPING_REGION_JSON_KEY_NAME: region,
|
||||
_RESOURCE_MAPPING_VERSION_JSON_KEY_NAME: _RESOURCE_MAPPING_JSON_FORMAT_VERSION}
|
||||
write_into_json_file(file_name, json_dict)
|
||||
|
||||
|
||||
def read_from_json_file(file_name: str) -> Dict[str, any]:
|
||||
try:
|
||||
json_dict: Dict[str, any] = {}
|
||||
@@ -179,7 +171,7 @@ def validate_resources_according_to_json_schema(resources: List[ResourceMappingA
|
||||
invalid_resources, row_count,
|
||||
error_messages.INVALID_FORMAT_UNEXPECTED_VALUE_IN_TABLE_ERROR_MESSAGE.format(
|
||||
resource.account_id,
|
||||
_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME,
|
||||
RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME,
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN))
|
||||
|
||||
if not re.match(_RESOURCE_MAPPING_REGION_PATTERN, resource.region):
|
||||
@@ -198,11 +190,11 @@ def validate_json_dict_according_to_json_schema(json_dict: Dict[str, any]) -> No
|
||||
_validate_required_key_in_json_dict(json_dict, "root", _RESOURCE_MAPPING_VERSION_JSON_KEY_NAME)
|
||||
_validate_required_key_in_json_dict(json_dict, "root", _RESOURCE_MAPPING_JSON_KEY_NAME)
|
||||
|
||||
_validate_required_key_in_json_dict(json_dict, "root", _RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME)
|
||||
if not re.match(_RESOURCE_MAPPING_ACCOUNTID_PATTERN, json_dict[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]):
|
||||
_validate_required_key_in_json_dict(json_dict, "root", RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME)
|
||||
if not re.match(_RESOURCE_MAPPING_ACCOUNTID_PATTERN, json_dict[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME]):
|
||||
raise ValueError(error_messages.INVALID_FORMAT_UNEXPECTED_VALUE_IN_FILE_ERROR_MESSAGE.format(
|
||||
json_dict[_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME],
|
||||
f"root/{_RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME}",
|
||||
json_dict[RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME],
|
||||
f"root/{RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME}",
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN))
|
||||
|
||||
_validate_required_key_in_json_dict(json_dict, "root", _RESOURCE_MAPPING_REGION_JSON_KEY_NAME)
|
||||
|
||||
@@ -8,7 +8,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
from PySide2.QtCore import Slot
|
||||
from PySide2.QtGui import (QIcon, QPixmap)
|
||||
from PySide2.QtWidgets import (QFrame, QHBoxLayout, QLabel, QLayout, QLineEdit, QPushButton,
|
||||
QSizePolicy, QWidget)
|
||||
QSizePolicy, QVBoxLayout, QWidget)
|
||||
|
||||
|
||||
class SearchFilterLineEdit(QLineEdit):
|
||||
@@ -25,39 +25,61 @@ class SearchFilterLineEdit(QLineEdit):
|
||||
|
||||
class NotificationFrame(QFrame):
|
||||
"""NotificationFrame is composed of information icon, notification title and close icon"""
|
||||
def __init__(self, parent: QWidget, pixmap: QPixmap, title: str, closeable: bool) -> None:
|
||||
def __init__(self, parent: QWidget, pixmap: QPixmap, content: str, closeable: bool, title: str = '') -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.setObjectName("NotificationFrame")
|
||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum))
|
||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
|
||||
self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
|
||||
|
||||
self._header_layout: QHBoxLayout = QHBoxLayout(self)
|
||||
self._header_layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
self._header_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self._header_layout.setSpacing(0)
|
||||
|
||||
icon_label: QLabel = QLabel(self)
|
||||
icon_label.setObjectName("NotificationIcon")
|
||||
icon_label.setPixmap(pixmap)
|
||||
self._header_layout.addWidget(icon_label)
|
||||
|
||||
text_widget = QWidget(self)
|
||||
text_layout: QVBoxLayout = QVBoxLayout(text_widget)
|
||||
text_layout.setContentsMargins(0, 0, 0, 0)
|
||||
text_layout.setSpacing(5)
|
||||
|
||||
self._title_label: QLabel = QLabel(title, self)
|
||||
self._title_label.setOpenExternalLinks(True)
|
||||
self._title_label.setObjectName("NotificationTitle")
|
||||
self._title_label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred))
|
||||
self._title_label.setWordWrap(True)
|
||||
self._title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
text_layout.addWidget(self._title_label)
|
||||
if not title:
|
||||
self._title_label.hide()
|
||||
|
||||
self._content_label: QLabel = QLabel(content, self)
|
||||
self._content_label.setOpenExternalLinks(True)
|
||||
self._content_label.setObjectName("NotificationContent")
|
||||
self._content_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
self._content_label.setWordWrap(True)
|
||||
text_layout.addWidget(self._content_label)
|
||||
|
||||
self._header_layout.addWidget(text_widget)
|
||||
|
||||
header_layout: QHBoxLayout = QHBoxLayout(self)
|
||||
header_layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
header_layout.setContentsMargins(0, 0, 0, 0)
|
||||
header_layout.addWidget(icon_label)
|
||||
header_layout.addWidget(self._title_label)
|
||||
if closeable:
|
||||
cancel_button: QPushButton = QPushButton(self)
|
||||
cancel_button.setIcon(QIcon(":/stylesheet/img/close.svg"))
|
||||
cancel_button.setFlat(True)
|
||||
cancel_button.clicked.connect(self.hide)
|
||||
header_layout.addWidget(cancel_button)
|
||||
self.setLayout(header_layout)
|
||||
self._header_layout.addWidget(cancel_button)
|
||||
self.setLayout(self._header_layout)
|
||||
|
||||
self.setVisible(False)
|
||||
|
||||
@Slot(str)
|
||||
def set_frame_text_receiver(self, text: str) -> None:
|
||||
self._title_label.setText(text)
|
||||
def set_frame_text_receiver(self, content: str, title: str = '') -> None:
|
||||
if title:
|
||||
self._title_label.setText(title)
|
||||
self._title_label.show()
|
||||
else:
|
||||
self._title_label.hide()
|
||||
|
||||
self._content_label.setText(content)
|
||||
self.setVisible(True)
|
||||
|
||||
@@ -5,6 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QPixmap
|
||||
from PySide2.QtWidgets import (QHBoxLayout, QLayout, QPushButton, QSizePolicy, QSpacerItem, QVBoxLayout, QWidget)
|
||||
|
||||
@@ -24,7 +25,13 @@ class ErrorPage(QWidget):
|
||||
|
||||
page_vertical_layout: QVBoxLayout = QVBoxLayout(self)
|
||||
page_vertical_layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
page_vertical_layout.setMargin(0)
|
||||
page_vertical_layout.setContentsMargins(
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM
|
||||
)
|
||||
page_vertical_layout.setSpacing(view_size_constants.ERROR_PAGE_LAYOUT_SPACING)
|
||||
|
||||
self._setup_notification_area()
|
||||
page_vertical_layout.addWidget(self._notification_area)
|
||||
@@ -39,15 +46,16 @@ class ErrorPage(QWidget):
|
||||
view_size_constants.ERROR_PAGE_NOTIFICATION_AREA_HEIGHT)
|
||||
|
||||
notification_area_layout: QVBoxLayout = QVBoxLayout(self._notification_area)
|
||||
notification_area_layout.setSpacing(0)
|
||||
notification_area_layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
notification_area_layout.setContentsMargins(
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT,
|
||||
view_size_constants.MAIN_PAGE_LAYOUT_MARGIN_TOPBOTTOM,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT, 0)
|
||||
notification_area_layout.setContentsMargins(0, 0, 0, 0)
|
||||
notification_area_layout.setSpacing(0)
|
||||
notification_area_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
|
||||
notification_frame: NotificationFrame = \
|
||||
NotificationFrame(self, QPixmap(":/error_report_warning.svg"),
|
||||
error_messages.ERROR_PAGE_TOOL_SETUP_ERROR_MESSAGE, False)
|
||||
NotificationFrame(self, QPixmap(":/error_report_helper.svg"),
|
||||
error_messages.ERROR_PAGE_TOOL_SETUP_ERROR_MESSAGE,
|
||||
False, error_messages.ERROR_PAGE_TOOL_SETUP_ERROR_TITLE)
|
||||
notification_frame.setObjectName("ErrorPage")
|
||||
notification_frame.setMinimumSize(view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH,
|
||||
view_size_constants.ERROR_PAGE_NOTIFICATION_AREA_HEIGHT)
|
||||
@@ -62,11 +70,8 @@ class ErrorPage(QWidget):
|
||||
|
||||
footer_area_layout: QHBoxLayout = QHBoxLayout(self._footer_area)
|
||||
footer_area_layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
footer_area_layout.setContentsMargins(
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT,
|
||||
view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM)
|
||||
footer_area_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_area_layout.setAlignment(Qt.AlignBottom | Qt.AlignRight)
|
||||
|
||||
footer_area_spacer: QSpacerItem = QSpacerItem(view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH,
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT,
|
||||
|
||||
@@ -18,6 +18,9 @@ from model.resource_proxy_model import ResourceProxyModel
|
||||
from model.resource_table_model import (ResourceTableConstants, ResourceTableModel)
|
||||
from view.common_view_components import (NotificationFrame, SearchFilterLineEdit)
|
||||
|
||||
HIGHLIGHT_FIELD_PROPERTY = 'HighlightField'
|
||||
HIGHLIGHT_TEXT_PROPERTY = 'HighlightText'
|
||||
|
||||
|
||||
class ViewEditPageConstants(object):
|
||||
NOTIFICATION_PAGE_INDEX = 0
|
||||
@@ -180,6 +183,7 @@ class ViewEditPage(QWidget):
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT)
|
||||
self._config_file_combobox.setCurrentIndex(-1)
|
||||
header_area_layout.addWidget(self._config_file_combobox)
|
||||
self._config_file_combobox.currentIndexChanged.connect(self._set_config_file_combobox_style)
|
||||
|
||||
header_area_spacer: QSpacerItem = QSpacerItem(view_size_constants.TOOL_APPLICATION_MAIN_WINDOW_WIDTH,
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT,
|
||||
@@ -202,7 +206,6 @@ class ViewEditPage(QWidget):
|
||||
|
||||
self._config_location_button: QPushButton = QPushButton(self._header_area)
|
||||
self._config_location_button.setIcon(QIcon(":/Gallery/Folder.svg"))
|
||||
self._config_location_button.setFlat(True)
|
||||
self._config_location_button.setMinimumSize(view_size_constants.INTERACTION_COMPONENT_HEIGHT,
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT)
|
||||
header_area_layout.addWidget(self._config_location_button)
|
||||
@@ -341,13 +344,6 @@ class ViewEditPage(QWidget):
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT)
|
||||
footer_area_layout.addWidget(self._save_changes_button)
|
||||
|
||||
self._create_new_button: QPushButton = QPushButton(self._footer_area)
|
||||
self._create_new_button.setObjectName("Primary")
|
||||
self._create_new_button.setText(notification_label_text.VIEW_EDIT_PAGE_CREATE_NEW_TEXT)
|
||||
self._create_new_button.setMinimumSize(view_size_constants.CREATE_NEW_BUTTON_WIDTH,
|
||||
view_size_constants.INTERACTION_COMPONENT_HEIGHT)
|
||||
footer_area_layout.addWidget(self._create_new_button)
|
||||
|
||||
self._rescan_button: QPushButton = QPushButton(self._footer_area)
|
||||
self._rescan_button.setObjectName("Secondary")
|
||||
self._rescan_button.setText(notification_label_text.VIEW_EDIT_PAGE_RESCAN_TEXT)
|
||||
@@ -398,10 +394,6 @@ class ViewEditPage(QWidget):
|
||||
def search_filter_input(self) -> QLineEdit:
|
||||
return self._search_filter_line_edit
|
||||
|
||||
@property
|
||||
def create_new_button(self) -> QPushButton:
|
||||
return self._create_new_button
|
||||
|
||||
@property
|
||||
def rescan_button(self) -> QPushButton:
|
||||
return self._rescan_button
|
||||
@@ -442,24 +434,53 @@ class ViewEditPage(QWidget):
|
||||
if config_files:
|
||||
self._notification_page_frame.set_frame_text_receiver(
|
||||
notification_label_text.VIEW_EDIT_PAGE_SELECT_CONFIG_FILE_MESSAGE)
|
||||
self._create_new_button.setVisible(False)
|
||||
self._rescan_button.setVisible(False)
|
||||
else:
|
||||
self._notification_page_frame.set_frame_text_receiver(
|
||||
notification_label_text.VIEW_EDIT_PAGE_NO_CONFIG_FILE_FOUND_MESSAGE)
|
||||
self._create_new_button.setVisible(True)
|
||||
notification_label_text.VIEW_EDIT_PAGE_NO_CONFIG_FILE_FOUND_MESSAGE,
|
||||
notification_label_text.VIEW_EDIT_PAGE_NO_CONFIG_FILE_FOUND_TITLE)
|
||||
self._rescan_button.setVisible(True)
|
||||
|
||||
self._config_file_combobox.blockSignals(False)
|
||||
self.set_config_location()
|
||||
|
||||
def set_config_location(self, config_location: str) -> None:
|
||||
self._config_file_combobox.blockSignals(False)
|
||||
self._set_config_file_combobox_style()
|
||||
if len(config_files) == 1:
|
||||
# If there's only one config file under the selected directory, load it automatically.
|
||||
self._config_file_combobox.setCurrentIndex(0)
|
||||
|
||||
def _set_config_file_combobox_style(self):
|
||||
if self._config_file_combobox.count() > 0 and self._config_file_combobox.currentIndex() == -1:
|
||||
self._config_file_combobox.setProperty(HIGHLIGHT_FIELD_PROPERTY, True)
|
||||
else:
|
||||
self._config_file_combobox.setProperty(HIGHLIGHT_FIELD_PROPERTY, False)
|
||||
|
||||
# Update the style
|
||||
self._config_file_combobox.setStyle(self._config_file_combobox.style())
|
||||
|
||||
def set_config_location(self, config_location: str = None) -> None:
|
||||
"""Set config file location in text label"""
|
||||
self._config_location_text.clear()
|
||||
|
||||
metrics: QFontMetrics = QFontMetrics(self._config_location_text.font())
|
||||
elided_text: str = metrics.elidedText(config_location, Qt.ElideMiddle, self._config_location_text.width())
|
||||
self._config_location_text.setText(elided_text)
|
||||
self._config_location_text.setToolTip(config_location)
|
||||
if not config_location:
|
||||
self._config_location_text.setText(notification_label_text.VIEW_EDIT_PAGE_INVALID_CONFIG_LOCATION_TEXT)
|
||||
self._config_location_text.setProperty(HIGHLIGHT_TEXT_PROPERTY, True)
|
||||
|
||||
self._config_location_button.setFlat(False)
|
||||
self._config_location_button.setProperty(HIGHLIGHT_FIELD_PROPERTY, True)
|
||||
else:
|
||||
metrics: QFontMetrics = QFontMetrics(self._config_location_text.font())
|
||||
elided_text: str = metrics.elidedText(config_location, Qt.ElideMiddle, self._config_location_text.width())
|
||||
self._config_location_text.setText(elided_text)
|
||||
self._config_location_text.setToolTip(config_location)
|
||||
self._config_location_text.setProperty(HIGHLIGHT_TEXT_PROPERTY, False)
|
||||
|
||||
self._config_location_button.setFlat(True)
|
||||
self._config_location_button.setProperty(HIGHLIGHT_FIELD_PROPERTY, False)
|
||||
|
||||
# Update the style
|
||||
self._config_location_text.setStyle(self._config_location_text.style())
|
||||
self._config_location_button.setStyle(self._config_location_button.style())
|
||||
|
||||
def set_table_view_page_interactions_enabled(self, enabled: bool) -> None:
|
||||
self._table_view_page.setEnabled(enabled)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"AWSResourceMappings": {},
|
||||
"AccountId": "EMPTY",
|
||||
"Region": "us-east-1",
|
||||
"Version": "1.0.0"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Amazon":
|
||||
{
|
||||
"AWSCore":
|
||||
{
|
||||
"ProfileName": "default",
|
||||
"ResourceMappingConfigFileName": "default_aws_resource_mappings.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,12 @@
|
||||
"isTemplated": false,
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"file": "Config/default_aws_resource_mappings.json",
|
||||
"origin": "Config/default_aws_resource_mappings.json",
|
||||
"isTemplated": false,
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"file": "EngineFinder.cmake",
|
||||
"origin": "EngineFinder.cmake",
|
||||
@@ -246,6 +252,12 @@
|
||||
"isTemplated": true,
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"file": "Registry/awscoreconfiguration.setreg",
|
||||
"origin": "Registry/awscoreconfiguration.setreg",
|
||||
"isTemplated": false,
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"file": "Resources/LegacyLogoLauncher.bmp",
|
||||
"origin": "Resources/LegacyLogoLauncher.bmp",
|
||||
|
||||
Reference in New Issue
Block a user