Create config files automatically and UX style updates (#3514)

monroegm-disable-blank-issue-2
Junbo Liang 4 years ago committed by GitHub
parent dd3bdcd3f4
commit 9fc824d98b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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;
padding-left: 0px;
padding-right: 15px;
}
QFrame#ErrorPage QLabel#NotificationIcon
NotificationFrame QLabel#NotificationTitle
{
padding-left: 15px;
padding-right: 15px;
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;
}

@ -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)

@ -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)
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)
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)
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.set_config_location()
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:
def set_config_location(self, config_location: str = None) -> None:
"""Set config file location in text label"""
self._config_location_text.clear()
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",

Loading…
Cancel
Save