From d200c69213e4365323c3253b0abdd90d2b679eb2 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 09:19:57 +0200 Subject: [PATCH 1/8] refactor: allow to have search view as default one --- .../data_ui_generator.py | 14 ++++- tests/unit/test_data_ui_generator.py | 56 +++++++++++++------ 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/splunk_add_on_ucc_framework/data_ui_generator.py b/splunk_add_on_ucc_framework/data_ui_generator.py index d9c004360..fea68eb39 100644 --- a/splunk_add_on_ucc_framework/data_ui_generator.py +++ b/splunk_add_on_ucc_framework/data_ui_generator.py @@ -28,17 +28,25 @@ def _pretty_print_xml(string: str) -> str: return minidom.parseString(string).toprettyxml(indent=" ") -def generate_nav_default_xml(include_inputs: bool, include_dashboard: bool) -> str: +def generate_nav_default_xml( + include_inputs: bool, include_dashboard: bool, search_view_default: bool = False +) -> str: """ Generates `default/data/ui/nav/default.xml` file. """ nav = ET.Element("nav") if include_inputs: ET.SubElement(nav, "view", attrib={"name": "inputs"}) - ET.SubElement(nav, "view", attrib={"name": "configuration", "default": "true"}) + if search_view_default is True: + ET.SubElement(nav, "view", attrib={"name": "configuration"}) + else: + ET.SubElement(nav, "view", attrib={"name": "configuration", "default": "true"}) if include_dashboard: ET.SubElement(nav, "view", attrib={"name": "dashboard"}) - ET.SubElement(nav, "view", attrib={"name": "search"}) + if search_view_default is True: + ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) + else: + ET.SubElement(nav, "view", attrib={"name": "search"}) nav_as_string = ET.tostring(nav, encoding="unicode") return _pretty_print_xml(nav_as_string) diff --git a/tests/unit/test_data_ui_generator.py b/tests/unit/test_data_ui_generator.py index 0412d8405..02e688554 100644 --- a/tests/unit/test_data_ui_generator.py +++ b/tests/unit/test_data_ui_generator.py @@ -1,17 +1,11 @@ import sys import pytest +import xmldiff.main from splunk_add_on_ucc_framework import data_ui_generator -PYTEST_SKIP_REASON = """Python 3.8 and higher preserves the order of the attrib -fields when `tostring` function is used. -https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.tostring -""" - - -@pytest.mark.skipif(sys.version_info >= (3, 8), reason=PYTEST_SKIP_REASON) def test_generate_nav_default_xml(): result = data_ui_generator.generate_nav_default_xml( include_inputs=True, @@ -26,10 +20,12 @@ def test_generate_nav_default_xml(): """ - assert expected_result == result + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False -@pytest.mark.skipif(sys.version_info >= (3, 8), reason=PYTEST_SKIP_REASON) def test_generate_nav_default_xml_only_configuration(): result = data_ui_generator.generate_nav_default_xml( include_inputs=False, @@ -42,10 +38,31 @@ def test_generate_nav_default_xml_only_configuration(): """ - assert expected_result == result + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False + + +def test_generate_nav_default_xml_with_search_view_default(): + result = data_ui_generator.generate_nav_default_xml( + include_inputs=False, + include_dashboard=False, + search_view_default=True, + ) + + expected_result = """ + +""" + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False -@pytest.mark.skipif(sys.version_info >= (3, 8), reason=PYTEST_SKIP_REASON) def test_generate_views_inputs_xml(): result = data_ui_generator.generate_views_inputs_xml("Splunk_TA_UCCExample") @@ -54,10 +71,12 @@ def test_generate_views_inputs_xml(): """ - assert expected_result == result + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False -@pytest.mark.skipif(sys.version_info >= (3, 8), reason=PYTEST_SKIP_REASON) def test_generate_views_configuration_xml(): result = data_ui_generator.generate_views_configuration_xml("Splunk_TA_UCCExample") @@ -66,10 +85,12 @@ def test_generate_views_configuration_xml(): """ - assert expected_result == result + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False -@pytest.mark.skipif(sys.version_info >= (3, 8), reason=PYTEST_SKIP_REASON) def test_generate_views_redirect_xml(): result = data_ui_generator.generate_views_redirect_xml("Splunk_TA_UCCExample") @@ -78,4 +99,7 @@ def test_generate_views_redirect_xml(): """ - assert expected_result == result + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False From 26f33939490f6ade8f45bd681247fcc8e51ed53d Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 09:20:19 +0200 Subject: [PATCH 2/8] test(coverage): update fail_under number --- .coveragerc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 0de7328a4..2a1049437 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,6 +3,7 @@ plugins = covdefaults omit = splunk_add_on_ucc_framework/commands/imports.py splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_action_helper.py.template + splunk_add_on_ucc_framework/templates/input.module-template [report] -fail_under = 77 +fail_under = 79 From 524916e7d34775729999a80dee41b4728187b861 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 09:25:29 +0200 Subject: [PATCH 3/8] test: add test for generate_views_dashboard_xml --- tests/unit/test_data_ui_generator.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_data_ui_generator.py b/tests/unit/test_data_ui_generator.py index 02e688554..2e65c0fa9 100644 --- a/tests/unit/test_data_ui_generator.py +++ b/tests/unit/test_data_ui_generator.py @@ -1,6 +1,3 @@ -import sys - -import pytest import xmldiff.main from splunk_add_on_ucc_framework import data_ui_generator @@ -91,6 +88,20 @@ def test_generate_views_configuration_xml(): assert " ".join([str(item) for item in diff]), False +def test_generate_views_dashboard_xml(): + result = data_ui_generator.generate_views_dashboard_xml("Splunk_TA_UCCExample") + + expected_result = """ + + + +""" + diff = xmldiff.main.diff_texts(result, expected_result) + + if diff: + assert " ".join([str(item) for item in diff]), False + + def test_generate_views_redirect_xml(): result = data_ui_generator.generate_views_redirect_xml("Splunk_TA_UCCExample") From 0c1a4ee9c907f9a4890a403a3f3531dcbe114859 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 09:57:35 +0200 Subject: [PATCH 4/8] feat: add ability to default to search view --- splunk_add_on_ucc_framework/commands/build.py | 3 +++ splunk_add_on_ucc_framework/global_config.py | 2 +- splunk_add_on_ucc_framework/schema/schema.json | 6 +++++- .../package_files_conflict_test/globalConfig.json | 7 ++++--- .../package_global_config_configuration/globalConfig.json | 2 +- .../package_global_config_everything/globalConfig.json | 4 ++-- .../globalConfig.json | 4 ++-- .../package_global_config_multi_input/globalConfig.json | 4 ++-- .../package_global_config_only_one_tab/globalConfig.json | 4 ++-- ui/src/components/BaseFormView/BaseFormConfigMock.ts | 1 + ui/src/mocks/globalConfigMock.ts | 1 + ui/src/types/globalConfig/meta.ts | 1 + 12 files changed, 25 insertions(+), 14 deletions(-) diff --git a/splunk_add_on_ucc_framework/commands/build.py b/splunk_add_on_ucc_framework/commands/build.py index 60b1b8a9d..0af29e9da 100644 --- a/splunk_add_on_ucc_framework/commands/build.py +++ b/splunk_add_on_ucc_framework/commands/build.py @@ -249,6 +249,7 @@ def generate_data_ui( addon_name: str, include_inputs: bool, include_dashboard: bool, + search_view_default: bool = False, ) -> None: # Create directories in the output folder for add-on's UI nav and views. os.makedirs( @@ -272,6 +273,7 @@ def generate_data_ui( default_xml_content = data_ui_generator.generate_nav_default_xml( include_inputs=include_inputs, include_dashboard=include_dashboard, + search_view_default=search_view_default, ) default_xml_file.write(default_xml_content) with open( @@ -553,6 +555,7 @@ def generate( ta_name, global_config.has_inputs(), global_config.has_dashboard(), + global_config.meta.get("searchViewDefault", False), ) logger.info("Copied UCC template directory") global_config_file = ( diff --git a/splunk_add_on_ucc_framework/global_config.py b/splunk_add_on_ucc_framework/global_config.py index a99d8951b..7d4796257 100644 --- a/splunk_add_on_ucc_framework/global_config.py +++ b/splunk_add_on_ucc_framework/global_config.py @@ -113,7 +113,7 @@ def alerts(self) -> List[Dict[str, Any]]: return self._content.get("alerts", []) @property - def meta(self) -> Dict[str, str]: + def meta(self) -> Dict[str, Any]: return self._content["meta"] @property diff --git a/splunk_add_on_ucc_framework/schema/schema.json b/splunk_add_on_ucc_framework/schema/schema.json index 9b3398ed0..28bc7ee8e 100644 --- a/splunk_add_on_ucc_framework/schema/schema.json +++ b/splunk_add_on_ucc_framework/schema/schema.json @@ -1510,6 +1510,10 @@ "type": "boolean", "default": true }, + "searchViewDefault": { + "type": "boolean", + "default": false + }, "os-dependentLibraries": { "type": "array", "items": { @@ -1559,7 +1563,7 @@ } }, "required": ["displayName", "name", "restRoot", "version"], - "description": "Meta deta regarding build", + "description": "Metadata regarding build", "additionalProperties": false }, "NumberValidator": { diff --git a/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json b/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json index 33935d372..8c80f3fc5 100644 --- a/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json +++ b/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json @@ -81,7 +81,8 @@ ] }, "defaultValue": "INFO", - "field": "loglevel" + "field": "loglevel", + "required": true } ], "title": "Logging" @@ -206,9 +207,9 @@ "meta": { "name": "test_addon", "restRoot": "test_addon", - "version": "5.41.0Reda406cf", + "version": "5.43.0R524916e7", "displayName": "This is my add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json index 66c318dc8..13c02ece2 100644 --- a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json @@ -387,6 +387,6 @@ "version": "1.1.1", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json index 81089ed6d..4ae01a8bf 100644 --- a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json @@ -1578,9 +1578,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.42.1R043a5287", + "version": "5.43.0R524916e7", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json index cc30cb20b..00dbcd806 100644 --- a/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json @@ -1186,9 +1186,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.42.1R67372a0b", + "version": "5.43.0R524916e7", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json index 4e3cf52dd..8f46ec79f 100644 --- a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json @@ -431,9 +431,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.41.0Reda406cf", + "version": "5.43.0R524916e7", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json b/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json index 1ffe3345b..cf775f317 100644 --- a/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json @@ -36,9 +36,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.41.0Reda406cf", + "version": "5.43.0R524916e7", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.6", - "_uccVersion": "5.42.1" + "_uccVersion": "5.43.0" } } diff --git a/ui/src/components/BaseFormView/BaseFormConfigMock.ts b/ui/src/components/BaseFormView/BaseFormConfigMock.ts index b38b08770..fe14d6463 100644 --- a/ui/src/components/BaseFormView/BaseFormConfigMock.ts +++ b/ui/src/components/BaseFormView/BaseFormConfigMock.ts @@ -167,6 +167,7 @@ const globalConfigMockCustomControl = { displayName: 'Demo Add-on for Splunk', schemaVersion: '0.0.3', checkForUpdates: false, + searchViewDefault: false, }, } satisfies z.input; diff --git a/ui/src/mocks/globalConfigMock.ts b/ui/src/mocks/globalConfigMock.ts index bd7738394..64a528815 100644 --- a/ui/src/mocks/globalConfigMock.ts +++ b/ui/src/mocks/globalConfigMock.ts @@ -248,6 +248,7 @@ const globalConfigMock = { displayName: 'Demo Add-on for Splunk', schemaVersion: '0.0.3', checkForUpdates: false, + searchViewDefault: false, }, } satisfies z.input; diff --git a/ui/src/types/globalConfig/meta.ts b/ui/src/types/globalConfig/meta.ts index d0a1d19b6..26d9665ad 100644 --- a/ui/src/types/globalConfig/meta.ts +++ b/ui/src/types/globalConfig/meta.ts @@ -8,6 +8,7 @@ export const meta = z.object({ version: z.string(), schemaVersion: z.string().optional(), checkForUpdates: z.boolean().default(true).optional(), + searchViewDefault: z.boolean().default(false).optional(), }); export type meta = z.infer; From 1bf1cc9ba35099d2ca164f08fd43a103db234e8e Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 13:11:03 +0200 Subject: [PATCH 5/8] test(coverage): fail_under=78.5 --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 2a1049437..1736e29e4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,4 +6,4 @@ omit = splunk_add_on_ucc_framework/templates/input.module-template [report] -fail_under = 79 +fail_under = 78.5 From adb70a7f0ad38e619a181272853916ec93c7559c Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Wed, 15 May 2024 15:51:41 +0200 Subject: [PATCH 6/8] refactor: combine statements --- splunk_add_on_ucc_framework/data_ui_generator.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/splunk_add_on_ucc_framework/data_ui_generator.py b/splunk_add_on_ucc_framework/data_ui_generator.py index fea68eb39..be0dd5a37 100644 --- a/splunk_add_on_ucc_framework/data_ui_generator.py +++ b/splunk_add_on_ucc_framework/data_ui_generator.py @@ -39,14 +39,12 @@ def generate_nav_default_xml( ET.SubElement(nav, "view", attrib={"name": "inputs"}) if search_view_default is True: ET.SubElement(nav, "view", attrib={"name": "configuration"}) + ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) else: ET.SubElement(nav, "view", attrib={"name": "configuration", "default": "true"}) + ET.SubElement(nav, "view", attrib={"name": "search"}) if include_dashboard: ET.SubElement(nav, "view", attrib={"name": "dashboard"}) - if search_view_default is True: - ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) - else: - ET.SubElement(nav, "view", attrib={"name": "search"}) nav_as_string = ET.tostring(nav, encoding="unicode") return _pretty_print_xml(nav_as_string) From 2955c31d7eb99a58a814a1ded3cc5d1ac25b2521 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Thu, 16 May 2024 13:58:14 +0200 Subject: [PATCH 7/8] refactor: revert last commit due to change in the order in the nav panel --- splunk_add_on_ucc_framework/data_ui_generator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/splunk_add_on_ucc_framework/data_ui_generator.py b/splunk_add_on_ucc_framework/data_ui_generator.py index be0dd5a37..fea68eb39 100644 --- a/splunk_add_on_ucc_framework/data_ui_generator.py +++ b/splunk_add_on_ucc_framework/data_ui_generator.py @@ -39,12 +39,14 @@ def generate_nav_default_xml( ET.SubElement(nav, "view", attrib={"name": "inputs"}) if search_view_default is True: ET.SubElement(nav, "view", attrib={"name": "configuration"}) - ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) else: ET.SubElement(nav, "view", attrib={"name": "configuration", "default": "true"}) - ET.SubElement(nav, "view", attrib={"name": "search"}) if include_dashboard: ET.SubElement(nav, "view", attrib={"name": "dashboard"}) + if search_view_default is True: + ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) + else: + ET.SubElement(nav, "view", attrib={"name": "search"}) nav_as_string = ET.tostring(nav, encoding="unicode") return _pretty_print_xml(nav_as_string) From 27693e2c401ba7b6efaba72d81c8e3eb3e18a606 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Tue, 21 May 2024 13:13:17 +0200 Subject: [PATCH 8/8] feat: allow users to choose the default page --- splunk_add_on_ucc_framework/commands/build.py | 6 +- .../data_ui_generator.py | 24 +++++-- .../global_config_validator.py | 37 +++++++---- .../schema/schema.json | 19 +++++- .../globalConfig.json | 21 ++---- .../globalConfig.json | 4 +- .../globalConfig.json | 2 +- .../globalConfig.json | 28 ++------ .../globalConfig.json | 2 +- .../globalConfig.json | 6 +- tests/unit/test_data_ui_generator.py | 66 ++++++++++++++----- tests/unit/test_global_config_validator.py | 14 ++++ ...shboard_page_but_no_dashboard_defined.json | 55 ++++++++++++++++ ...ult_inputs_page_but_no_inputs_defined.json | 55 ++++++++++++++++ 14 files changed, 257 insertions(+), 82 deletions(-) create mode 100644 tests/unit/testdata/invalid_config_meta_default_dashboard_page_but_no_dashboard_defined.json create mode 100644 tests/unit/testdata/invalid_config_meta_default_inputs_page_but_no_inputs_defined.json diff --git a/splunk_add_on_ucc_framework/commands/build.py b/splunk_add_on_ucc_framework/commands/build.py index a45f9776b..b3cb8382b 100644 --- a/splunk_add_on_ucc_framework/commands/build.py +++ b/splunk_add_on_ucc_framework/commands/build.py @@ -249,7 +249,7 @@ def generate_data_ui( addon_name: str, include_inputs: bool, include_dashboard: bool, - search_view_default: bool = False, + default_view: str, ) -> None: # Create directories in the output folder for add-on's UI nav and views. os.makedirs( @@ -273,7 +273,7 @@ def generate_data_ui( default_xml_content = data_ui_generator.generate_nav_default_xml( include_inputs=include_inputs, include_dashboard=include_dashboard, - search_view_default=search_view_default, + default_view=default_view, ) default_xml_file.write(default_xml_content) with open( @@ -557,7 +557,7 @@ def generate( ta_name, global_config.has_inputs(), global_config.has_dashboard(), - global_config.meta.get("searchViewDefault", False), + global_config.meta.get("default_view", data_ui_generator.DEFAULT_VIEW), ) logger.info("Copied UCC template directory") global_config_file = ( diff --git a/splunk_add_on_ucc_framework/data_ui_generator.py b/splunk_add_on_ucc_framework/data_ui_generator.py index fea68eb39..b397aee48 100644 --- a/splunk_add_on_ucc_framework/data_ui_generator.py +++ b/splunk_add_on_ucc_framework/data_ui_generator.py @@ -20,6 +20,8 @@ from xml.etree import ElementTree as ET from defusedxml import minidom +DEFAULT_VIEW = "configuration" + def _pretty_print_xml(string: str) -> str: """ @@ -29,21 +31,29 @@ def _pretty_print_xml(string: str) -> str: def generate_nav_default_xml( - include_inputs: bool, include_dashboard: bool, search_view_default: bool = False + include_inputs: bool, include_dashboard: bool, default_view: str ) -> str: """ Generates `default/data/ui/nav/default.xml` file. + + The validation is being done in `_validate_meta_default_view` function from `global_config_validator.py` file. """ nav = ET.Element("nav") if include_inputs: - ET.SubElement(nav, "view", attrib={"name": "inputs"}) - if search_view_default is True: - ET.SubElement(nav, "view", attrib={"name": "configuration"}) - else: + if default_view == "inputs": + ET.SubElement(nav, "view", attrib={"name": "inputs", "default": "true"}) + else: + ET.SubElement(nav, "view", attrib={"name": "inputs"}) + if default_view == "configuration": ET.SubElement(nav, "view", attrib={"name": "configuration", "default": "true"}) + else: + ET.SubElement(nav, "view", attrib={"name": "configuration"}) if include_dashboard: - ET.SubElement(nav, "view", attrib={"name": "dashboard"}) - if search_view_default is True: + if default_view == "dashboard": + ET.SubElement(nav, "view", attrib={"name": "dashboard", "default": "true"}) + else: + ET.SubElement(nav, "view", attrib={"name": "dashboard"}) + if default_view == "search": ET.SubElement(nav, "view", attrib={"name": "search", "default": "true"}) else: ET.SubElement(nav, "view", attrib={"name": "search"}) diff --git a/splunk_add_on_ucc_framework/global_config_validator.py b/splunk_add_on_ucc_framework/global_config_validator.py index d64b2cbf1..875f0c5db 100644 --- a/splunk_add_on_ucc_framework/global_config_validator.py +++ b/splunk_add_on_ucc_framework/global_config_validator.py @@ -21,11 +21,11 @@ import logging import itertools - import jsonschema from splunk_add_on_ucc_framework import dashboard as dashboard_lib from splunk_add_on_ucc_framework import global_config as global_config_lib +from splunk_add_on_ucc_framework import data_ui_generator from splunk_add_on_ucc_framework.tabs import resolve_tab, Tab logger = logging.getLogger("ucc_gen") @@ -43,6 +43,7 @@ class GlobalConfigValidator: def __init__(self, source_dir: str, global_config: global_config_lib.GlobalConfig): self._source_dir = source_dir + self._global_config = global_config self._config = global_config.content @property @@ -613,11 +614,11 @@ def _is_circular( visited = self._is_circular( mods, visited, all_entity_fields, influenced_field ) - # all of dependent modifications fields are dead_end + # All dependent modifications fields are dead_end visited[current_field] = DEAD_END return visited - def _check_if_cilcular( + def _check_if_circular( self, all_entity_fields: List[Any], fields_with_mods: List[Any], @@ -672,7 +673,7 @@ def _get_all_entities( return all_fields - def _get_all_modifiction_data( + def _get_all_modification_data( self, collections: List[Dict[str, Any]], ) -> List[Any]: @@ -692,9 +693,9 @@ def _get_all_modifiction_data( def _validate_field_modifications(self) -> None: """ Validates: - Circular dependencies - If fields try modify itself - If fields try modify unexisting field + * Circular dependencies + * If fields try to modify itself + * If fields try to modify field that do not exist """ pages = self._config["pages"] @@ -706,9 +707,9 @@ def _validate_field_modifications(self) -> None: fields_with_mods_config, all_modifications_config, all_fields_config, - ) = self._get_all_modifiction_data(tabs) + ) = self._get_all_modification_data(tabs) - self._check_if_cilcular( + self._check_if_circular( all_fields_config, fields_with_mods_config, all_modifications_config ) @@ -720,12 +721,25 @@ def _validate_field_modifications(self) -> None: fields_with_mods_inputs, all_modifications_inputs, all_fields_inputs, - ) = self._get_all_modifiction_data(services) + ) = self._get_all_modification_data(services) - self._check_if_cilcular( + self._check_if_circular( all_fields_inputs, fields_with_mods_inputs, all_modifications_inputs ) + def _validate_meta_default_view(self) -> None: + default_view = self._global_config.meta.get( + "defaultView", data_ui_generator.DEFAULT_VIEW + ) + if default_view == "inputs" and not self._global_config.has_inputs(): + raise GlobalConfigValidatorException( + 'meta.defaultView == "inputs" but there is no inputs defined in globalConfig' + ) + if default_view == "dashboard" and not self._global_config.has_dashboard(): + raise GlobalConfigValidatorException( + 'meta.defaultView == "dashboard" but there is no dashboard defined in globalConfig' + ) + def validate(self) -> None: self._validate_config_against_schema() self._validate_configuration_tab_table_has_name_field() @@ -740,3 +754,4 @@ def validate(self) -> None: self._validate_checkbox_group() self._validate_groups() self._validate_field_modifications() + self._validate_meta_default_view() diff --git a/splunk_add_on_ucc_framework/schema/schema.json b/splunk_add_on_ucc_framework/schema/schema.json index 3927fdaec..ad3865cf3 100644 --- a/splunk_add_on_ucc_framework/schema/schema.json +++ b/splunk_add_on_ucc_framework/schema/schema.json @@ -1513,9 +1513,22 @@ "type": "boolean", "default": true }, - "searchViewDefault": { - "type": "boolean", - "default": false + "defaultView": { + "type": "string", + "anyOf": [ + { + "const": "inputs" + }, + { + "const": "configuration" + }, + { + "const": "dashboard" + }, + { + "const": "search" + } + ] }, "os-dependentLibraries": { "type": "array", diff --git a/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json b/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json index 8c80f3fc5..cbe6df45a 100644 --- a/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json +++ b/tests/testdata/test_addons/package_files_conflict_test/globalConfig.json @@ -117,19 +117,12 @@ "required": true }, { - "type": "text", - "label": "Interval", - "validators": [ - { - "type": "regex", - "errorMsg": "Interval must be an integer.", - "pattern": "^\\-[1-9]\\d*$|^\\d*$" - } - ], - "defaultValue": "300", + "type": "interval", "field": "interval", + "label": "Interval", "help": "Time interval of the data input, in seconds.", - "required": true + "required": true, + "defaultValue": "300" }, { "type": "singleSelect", @@ -207,9 +200,9 @@ "meta": { "name": "test_addon", "restRoot": "test_addon", - "version": "5.43.0R524916e7", + "version": "5.44.0R7f88cfdd", "displayName": "This is my add-on", - "schemaVersion": "0.0.6", - "_uccVersion": "5.43.0" + "schemaVersion": "0.0.7", + "_uccVersion": "5.44.0" } } diff --git a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json index 13c02ece2..69fc1e228 100644 --- a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json @@ -386,7 +386,7 @@ "restRoot": "splunk_ta_uccexample", "version": "1.1.1", "displayName": "Splunk UCC test Add-on", - "schemaVersion": "0.0.6", - "_uccVersion": "5.43.0" + "schemaVersion": "0.0.7", + "_uccVersion": "5.44.0" } } diff --git a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json index e10b794f1..01cd0271b 100644 --- a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json @@ -1550,7 +1550,7 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.44.0Re9cd7340", + "version": "5.44.0R7f88cfdd", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.7", "_uccVersion": "5.44.0" diff --git a/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json index 00dbcd806..b19fc1c35 100644 --- a/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_everything_uccignore/globalConfig.json @@ -602,16 +602,9 @@ } }, { - "type": "text", - "label": "Interval", - "validators": [ - { - "type": "regex", - "errorMsg": "Interval must be an integer.", - "pattern": "^\\-[1-9]\\d*$|^\\d*$" - } - ], + "type": "interval", "field": "interval", + "label": "Interval", "help": "Time interval of the data input, in seconds.", "required": true }, @@ -791,16 +784,9 @@ "required": true }, { - "type": "text", - "label": "Interval", - "validators": [ - { - "type": "regex", - "errorMsg": "Interval must be an integer.", - "pattern": "^\\-[1-9]\\d*$|^\\d*$" - } - ], + "type": "interval", "field": "interval", + "label": "Interval", "help": "Time interval of the data input, in seconds .", "required": true }, @@ -1186,9 +1172,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.43.0R524916e7", + "version": "5.44.0R7f88cfdd", "displayName": "Splunk UCC test Add-on", - "schemaVersion": "0.0.6", - "_uccVersion": "5.43.0" + "schemaVersion": "0.0.7", + "_uccVersion": "5.44.0" } } diff --git a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json index af6f8feae..9b08d61a9 100644 --- a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json @@ -431,7 +431,7 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.44.0Re9cd7340", + "version": "5.44.0R7f88cfdd", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.7", "_uccVersion": "5.44.0" diff --git a/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json b/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json index cf775f317..cd2e7abb3 100644 --- a/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_only_one_tab/globalConfig.json @@ -36,9 +36,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.43.0R524916e7", + "version": "5.44.0R7f88cfdd", "displayName": "Splunk UCC test Add-on", - "schemaVersion": "0.0.6", - "_uccVersion": "5.43.0" + "schemaVersion": "0.0.7", + "_uccVersion": "5.44.0" } } diff --git a/tests/unit/test_data_ui_generator.py b/tests/unit/test_data_ui_generator.py index 2e65c0fa9..0ca2c14a6 100644 --- a/tests/unit/test_data_ui_generator.py +++ b/tests/unit/test_data_ui_generator.py @@ -7,6 +7,7 @@ def test_generate_nav_default_xml(): result = data_ui_generator.generate_nav_default_xml( include_inputs=True, include_dashboard=True, + default_view="configuration", ) expected_result = """ @@ -19,14 +20,14 @@ def test_generate_nav_default_xml(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" def test_generate_nav_default_xml_only_configuration(): result = data_ui_generator.generate_nav_default_xml( include_inputs=False, include_dashboard=False, + default_view="configuration", ) expected_result = """ @@ -37,15 +38,53 @@ def test_generate_nav_default_xml_only_configuration(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" + + +def test_generate_nav_default_xml_with_default_inputs_page(): + result = data_ui_generator.generate_nav_default_xml( + include_inputs=True, + include_dashboard=False, + default_view="inputs", + ) + + expected_result = """ + +""" + diff = xmldiff.main.diff_texts(result, expected_result) + + assert " ".join([str(item) for item in diff]) == "" + + +def test_generate_nav_default_xml_with_default_dashboard_page(): + result = data_ui_generator.generate_nav_default_xml( + include_inputs=True, + include_dashboard=True, + default_view="dashboard", + ) + + expected_result = """ + +""" + diff = xmldiff.main.diff_texts(result, expected_result) + + assert " ".join([str(item) for item in diff]) == "" def test_generate_nav_default_xml_with_search_view_default(): result = data_ui_generator.generate_nav_default_xml( include_inputs=False, include_dashboard=False, - search_view_default=True, + default_view="search", ) expected_result = """ @@ -56,8 +95,7 @@ def test_generate_nav_default_xml_with_search_view_default(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" def test_generate_views_inputs_xml(): @@ -70,8 +108,7 @@ def test_generate_views_inputs_xml(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" def test_generate_views_configuration_xml(): @@ -84,22 +121,20 @@ def test_generate_views_configuration_xml(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" def test_generate_views_dashboard_xml(): result = data_ui_generator.generate_views_dashboard_xml("Splunk_TA_UCCExample") expected_result = """ - + """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" def test_generate_views_redirect_xml(): @@ -112,5 +147,4 @@ def test_generate_views_redirect_xml(): """ diff = xmldiff.main.diff_texts(result, expected_result) - if diff: - assert " ".join([str(item) for item in diff]), False + assert " ".join([str(item) for item in diff]) == "" diff --git a/tests/unit/test_global_config_validator.py b/tests/unit/test_global_config_validator.py index 8bd8823c8..63f6d5f81 100644 --- a/tests/unit/test_global_config_validator.py +++ b/tests/unit/test_global_config_validator.py @@ -342,6 +342,20 @@ def test_config_validation_when_deprecated_placeholder_is_used(caplog): "undefined_entity_field_name which is not defined in entity" ), ), + ( + "invalid_config_meta_default_inputs_page_but_no_inputs_defined.json", + False, + ( + 'meta.defaultView == "inputs" but there is no inputs defined in globalConfig' + ), + ), + ( + "invalid_config_meta_default_dashboard_page_but_no_dashboard_defined.json", + False, + ( + 'meta.defaultView == "dashboard" but there is no dashboard defined in globalConfig' + ), + ), ], ) def test_config_validation_when_error(filename, is_yaml, exception_message): diff --git a/tests/unit/testdata/invalid_config_meta_default_dashboard_page_but_no_dashboard_defined.json b/tests/unit/testdata/invalid_config_meta_default_dashboard_page_but_no_dashboard_defined.json new file mode 100644 index 000000000..f82410494 --- /dev/null +++ b/tests/unit/testdata/invalid_config_meta_default_dashboard_page_but_no_dashboard_defined.json @@ -0,0 +1,55 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "logging", + "entity": [ + { + "type": "singleSelect", + "label": "Log level", + "options": { + "disableSearch": true, + "autoCompleteFields": [ + { + "value": "DEBUG", + "label": "DEBUG" + }, + { + "value": "INFO", + "label": "INFO" + }, + { + "value": "WARNING", + "label": "WARNING" + }, + { + "value": "ERROR", + "label": "ERROR" + }, + { + "value": "CRITICAL", + "label": "CRITICAL" + } + ] + }, + "defaultValue": "INFO", + "field": "loglevel" + } + ], + "title": "Logging" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "1.0.0", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "defaultView": "dashboard" + } +} diff --git a/tests/unit/testdata/invalid_config_meta_default_inputs_page_but_no_inputs_defined.json b/tests/unit/testdata/invalid_config_meta_default_inputs_page_but_no_inputs_defined.json new file mode 100644 index 000000000..e09e97723 --- /dev/null +++ b/tests/unit/testdata/invalid_config_meta_default_inputs_page_but_no_inputs_defined.json @@ -0,0 +1,55 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "logging", + "entity": [ + { + "type": "singleSelect", + "label": "Log level", + "options": { + "disableSearch": true, + "autoCompleteFields": [ + { + "value": "DEBUG", + "label": "DEBUG" + }, + { + "value": "INFO", + "label": "INFO" + }, + { + "value": "WARNING", + "label": "WARNING" + }, + { + "value": "ERROR", + "label": "ERROR" + }, + { + "value": "CRITICAL", + "label": "CRITICAL" + } + ] + }, + "defaultValue": "INFO", + "field": "loglevel" + } + ], + "title": "Logging" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "1.0.0", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "defaultView": "inputs" + } +}