From fa731fcb9484b51c2001817da904992a35b2b4ac Mon Sep 17 00:00:00 2001 From: hetangmodi-crest Date: Wed, 16 Apr 2025 12:15:19 +0530 Subject: [PATCH 1/4] feat: generation of searchbnf.conf --- docs/generated_files.md | 1 + .../generators/conf_files/__init__.py | 2 + .../conf_files/create_searchbnf_conf.py | 54 +++++++++++++++++++ .../generators/file_const.py | 2 + .../conf_files/searchbnf_conf.template | 6 +++ 5 files changed, 65 insertions(+) create mode 100644 splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py create mode 100644 splunk_add_on_ucc_framework/templates/conf_files/searchbnf_conf.template diff --git a/docs/generated_files.md b/docs/generated_files.md index 09a27cc892..40e7eea2c7 100644 --- a/docs/generated_files.md +++ b/docs/generated_files.md @@ -14,6 +14,7 @@ The following table describes the files generated by UCC framework. | alert_actions.conf | output/<YOUR_ADDON_NAME>/default | Generates `alert_actions.conf` and `alert_actions.conf.spec` file for the custom alert actions defined in globalConfig | | eventtypes.conf | output/<YOUR_ADDON_NAME>/default | Generates `eventtypes.conf` file if the sourcetype is mentioned in Adaptive Response of custom alert action in globalConfig | | tags.conf | output/<YOUR_ADDON_NAME>/default | Generates `tags.conf` file based on the `eventtypes.conf` created for custom alert actions. | +| searchbnf.conf | output/<YOUR_ADDON_NAME>/default | Generates `searchbnf.conf` for custom search commands provided in the globalConfig. | | _account.conf | output/<YOUR_ADDON_NAME>/README | Generates `_account.conf.spec` file for the configuration mentioned in globalConfig | | _settings.conf | output/<YOUR_ADDON_NAME>/README | Generates `_settings.conf.spec` file for the Proxy, Logging or Custom Tab mentioned in globalConfig | | configuration.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates configuration.xml file in `default/data/ui/views/` folder if configuration is defined in globalConfig. | diff --git a/splunk_add_on_ucc_framework/generators/conf_files/__init__.py b/splunk_add_on_ucc_framework/generators/conf_files/__init__.py index d6159cf629..f9d562730c 100644 --- a/splunk_add_on_ucc_framework/generators/conf_files/__init__.py +++ b/splunk_add_on_ucc_framework/generators/conf_files/__init__.py @@ -24,6 +24,7 @@ from .create_web_conf import WebConf from .create_account_conf import AccountConf from .create_settings_conf import SettingsConf +from .create_searchbnf_conf import SearchbnfConf __all__ = [ "FileGenerator", @@ -37,4 +38,5 @@ "InputsConf", "AccountConf", "SettingsConf", + "SearchbnfConf", ] diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py new file mode 100644 index 0000000000..39506aff21 --- /dev/null +++ b/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py @@ -0,0 +1,54 @@ +# +# Copyright 2025 Splunk Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from typing import Any, Dict + +from splunk_add_on_ucc_framework.generators.file_generator import FileGenerator + + +class SearchbnfConf(FileGenerator): + __description__ = "Generates `searchbnf.conf` for custom search commands provided in the globalConfig." + + def _set_attributes(self, **kwargs: Any) -> None: + self.conf_file = "searchbnf.conf" + self.searchbnf_info = [] + if self._global_config.has_custom_search_commands(): + for command in self._global_config.custom_search_commands: + if command.get("requiredSearchAssistant", False): + searchbnf_dict = { + "command_name": command["commandName"], + "description": command["description"], + "syntax": command["syntax"], + "usage": command["usage"], + } + self.searchbnf_info.append(searchbnf_dict) + + def generate(self) -> Dict[str, str]: + if not self.searchbnf_info: + return {"": ""} + + file_path = self.get_file_output_path(["default", self.conf_file]) + self.set_template_and_render( + template_file_path=["conf_files"], file_name="searchbnf_conf.template" + ) + rendered_content = self._template.render( + searchbnf_info=self.searchbnf_info, + ) + self.writer( + file_name=self.conf_file, + file_path=file_path, + content=rendered_content, + ) + return {self.conf_file: file_path} diff --git a/splunk_add_on_ucc_framework/generators/file_const.py b/splunk_add_on_ucc_framework/generators/file_const.py index 0ebb7e65c2..34c4b67734 100644 --- a/splunk_add_on_ucc_framework/generators/file_const.py +++ b/splunk_add_on_ucc_framework/generators/file_const.py @@ -35,6 +35,7 @@ WebConf, AccountConf, SettingsConf, + SearchbnfConf, ) __all__ = ["FileClass", "GEN_FILE_LIST"] @@ -59,6 +60,7 @@ class FileClass(NamedTuple): ), FileClass("eventtypes.conf", EventtypesConf, ["default"]), FileClass("tags.conf", TagsConf, ["default"]), + FileClass("searchbnf.conf", SearchbnfConf, ["default"]), FileClass("_account.conf", AccountConf, ["README"]), FileClass("_settings.conf", SettingsConf, ["README"]), FileClass( diff --git a/splunk_add_on_ucc_framework/templates/conf_files/searchbnf_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/searchbnf_conf.template new file mode 100644 index 0000000000..c7227070ca --- /dev/null +++ b/splunk_add_on_ucc_framework/templates/conf_files/searchbnf_conf.template @@ -0,0 +1,6 @@ +{% for info in searchbnf_info -%} +[{{info["command_name"]}}] +syntax = {{info["syntax"]}} +description = {{info["description"]}} +usage = {{info["usage"]}} +{% endfor -%} \ No newline at end of file From f13fde57f7783003d53270f936714e5acf55f7b9 Mon Sep 17 00:00:00 2001 From: hetangmodi-crest Date: Wed, 16 Apr 2025 12:16:58 +0530 Subject: [PATCH 2/4] tests: added respective unit and smoke test case --- tests/smoke/test_ucc_build.py | 1 + .../default/searchbnf.conf | 9 ++ .../conf_files/test_conf_files_init.py | 2 + .../conf_files/test_create_searchbnf_conf.py | 117 ++++++++++++++++++ tests/unit/generators/test_doc_generator.py | 1 + tests/unit/test_utils.py | 1 + 6 files changed, 131 insertions(+) create mode 100644 tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/searchbnf.conf create mode 100644 tests/unit/generators/conf_files/test_create_searchbnf_conf.py diff --git a/tests/smoke/test_ucc_build.py b/tests/smoke/test_ucc_build.py index 3e0926880d..0d14c04fb0 100644 --- a/tests/smoke/test_ucc_build.py +++ b/tests/smoke/test_ucc_build.py @@ -153,6 +153,7 @@ def test_ucc_generate_with_everything(caplog): ("default", "splunk_ta_uccexample_settings.conf"), ("default", "web.conf"), ("default", "server.conf"), + ("default", "searchbnf.conf"), ("default", "data", "ui", "alerts", "test_alert.html"), ("default", "data", "ui", "nav", "default.xml"), ("default", "data", "ui", "views", "configuration.xml"), diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/searchbnf.conf b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/searchbnf.conf new file mode 100644 index 0000000000..16d6021c1f --- /dev/null +++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/searchbnf.conf @@ -0,0 +1,9 @@ +[generatetextcommand] +syntax = generatetextcommand count= text= +description = This command generates COUNT occurrences of a TEXT string. +usage = public + +[filtercommand] +syntax = | filtercommand contains='value1' replace='value to be replaced,value to replace with' +description = It filters records from the events stream returning only those which has :code:`contains` in them and replaces :code:`replace_array[0]` with :code:`replace_array[1]`. +usage = public diff --git a/tests/unit/generators/conf_files/test_conf_files_init.py b/tests/unit/generators/conf_files/test_conf_files_init.py index 18e764f396..a242a9a4ed 100644 --- a/tests/unit/generators/conf_files/test_conf_files_init.py +++ b/tests/unit/generators/conf_files/test_conf_files_init.py @@ -11,6 +11,7 @@ def test___init__conf(): "InputsConf", "AccountConf", "SettingsConf", + "SearchbnfConf", ] expected_modules = [ "file_generator", @@ -24,6 +25,7 @@ def test___init__conf(): "create_web_conf", "create_account_conf", "create_settings_conf", + "create_searchbnf_conf", ] import splunk_add_on_ucc_framework.generators.conf_files as conf diff --git a/tests/unit/generators/conf_files/test_create_searchbnf_conf.py b/tests/unit/generators/conf_files/test_create_searchbnf_conf.py new file mode 100644 index 0000000000..481257ba0a --- /dev/null +++ b/tests/unit/generators/conf_files/test_create_searchbnf_conf.py @@ -0,0 +1,117 @@ +from pytest import fixture +from splunk_add_on_ucc_framework.generators.conf_files import SearchbnfConf +from splunk_add_on_ucc_framework import __file__ as ucc_framework_file +import os.path +from textwrap import dedent + +UCC_DIR = os.path.dirname(ucc_framework_file) + + +@fixture +def custom_search_command_without_search_assistance(): + return [ + { + "commandName": "testcommand2", + "commandType": "streaming", + "fileName": "test2.py", + } + ] + + +def test_set_attributes_without_custom_command( + global_config_only_configuration, input_dir, output_dir, ucc_dir, ta_name +): + searchbnf_conf = SearchbnfConf( + global_config_only_configuration, + input_dir, + output_dir, + ucc_dir=ucc_dir, + addon_name=ta_name, + ) + assert searchbnf_conf.searchbnf_info == [] + + +def test_set_attributes( + global_config_all_json, input_dir, output_dir, ucc_dir, ta_name +): + searchbnf_conf = SearchbnfConf( + global_config_all_json, + input_dir, + output_dir, + ucc_dir=ucc_dir, + addon_name=ta_name, + ) + assert searchbnf_conf.conf_file == "searchbnf.conf" + assert searchbnf_conf.searchbnf_info == [ + { + "command_name": "generatetextcommand", + "description": "This command generates COUNT occurrences of a TEXT string.", + "syntax": "generatetextcommand count= text=", + "usage": "public", + } + ] + + +def test_set_attributes_without_search_assistance( + global_config_all_json, + input_dir, + output_dir, + ucc_dir, + ta_name, + custom_search_command_without_search_assistance, +): + global_config_all_json._content[ + "customSearchCommand" + ] = custom_search_command_without_search_assistance + searchbnf_conf = SearchbnfConf( + global_config_all_json, + input_dir, + output_dir, + ucc_dir=ucc_dir, + addon_name=ta_name, + ) + assert searchbnf_conf.searchbnf_info == [] + + +def test_generate_conf_without_custom_command( + global_config_only_configuration, input_dir, output_dir, ucc_dir, ta_name +): + searchbnf_conf = SearchbnfConf( + global_config_only_configuration, + input_dir, + output_dir, + ucc_dir=ucc_dir, + addon_name=ta_name, + ) + file_paths = searchbnf_conf.generate() + + # Assert that no files are returned since no custom command is configured + assert file_paths == {"": ""} + + +def test_generate_conf(global_config_all_json, input_dir, output_dir, ta_name): + searchbnf_conf = SearchbnfConf( + global_config_all_json, + input_dir, + output_dir, + ucc_dir=UCC_DIR, + addon_name=ta_name, + ) + file_paths = searchbnf_conf.generate() + exp_fname = "searchbnf.conf" + + assert file_paths == {exp_fname: f"{output_dir}/{ta_name}/default/{exp_fname}"} + + with open(file_paths["searchbnf.conf"]) as fp: + content = fp.read() + + expected_content = dedent( + """ + [generatetextcommand] + syntax = generatetextcommand count= text= + description = This command generates COUNT occurrences of a TEXT string. + usage = public + """ + ).lstrip() + + assert content == expected_content diff --git a/tests/unit/generators/test_doc_generator.py b/tests/unit/generators/test_doc_generator.py index 08b103c3f9..491bd75711 100644 --- a/tests/unit/generators/test_doc_generator.py +++ b/tests/unit/generators/test_doc_generator.py @@ -21,6 +21,7 @@ def test_generate_docs(): | alert_actions.conf | output/<YOUR_ADDON_NAME>/default | Generates `alert_actions.conf` and `alert_actions.conf.spec` file for the custom alert actions defined in globalConfig | | eventtypes.conf | output/<YOUR_ADDON_NAME>/default | Generates `eventtypes.conf` file if the sourcetype is mentioned in Adaptive Response of custom alert action in globalConfig | | tags.conf | output/<YOUR_ADDON_NAME>/default | Generates `tags.conf` file based on the `eventtypes.conf` created for custom alert actions. | +| searchbnf.conf | output/<YOUR_ADDON_NAME>/default | Generates `searchbnf.conf` for custom search commands provided in the globalConfig. | | _account.conf | output/<YOUR_ADDON_NAME>/README | Generates `_account.conf.spec` file for the configuration mentioned in globalConfig | | _settings.conf | output/<YOUR_ADDON_NAME>/README | Generates `_settings.conf.spec` file for the Proxy, Logging or Custom Tab mentioned in globalConfig | | configuration.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates configuration.xml file in `default/data/ui/views/` folder if configuration is defined in globalConfig. | diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index f0e97c0087..35673fd024 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -33,6 +33,7 @@ def test_get_j2_env(): "conf_files/settings_conf.template", "conf_files/tags_conf.template", "conf_files/web_conf.template", + "conf_files/searchbnf_conf.template", ] assert sorted(expected_list_of_templates) == sorted(list_of_templates) From 5770fa0628943e214ef43ec4884e9d5a76ca6e18 Mon Sep 17 00:00:00 2001 From: hetangmodi-crest Date: Wed, 16 Apr 2025 12:17:53 +0530 Subject: [PATCH 3/4] chore: updated syntax for csc in valid_config.json --- tests/unit/test_global_config.py | 4 ++-- tests/unit/testdata/valid_config.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_global_config.py b/tests/unit/test_global_config.py index f1529dbb45..9b00ad8ede 100644 --- a/tests/unit/test_global_config.py +++ b/tests/unit/test_global_config.py @@ -74,8 +74,8 @@ def test_global_config_custom_search_commands(global_config_all_json): "fileName": "generatetext.py", "commandType": "generating", "requiredSearchAssistant": True, - "description": " This command generates COUNT occurrences of a TEXT string.", - "syntax": "mycommand count= text=", + "description": "This command generates COUNT occurrences of a TEXT string.", + "syntax": "generatetextcommand count= text=", "usage": "public", "arguments": [ { diff --git a/tests/unit/testdata/valid_config.json b/tests/unit/testdata/valid_config.json index 37fff6704d..7019c6d5b4 100644 --- a/tests/unit/testdata/valid_config.json +++ b/tests/unit/testdata/valid_config.json @@ -1363,8 +1363,8 @@ "fileName": "generatetext.py", "commandType": "generating", "requiredSearchAssistant": true, - "description": " This command generates COUNT occurrences of a TEXT string.", - "syntax": "mycommand count= text=", + "description": "This command generates COUNT occurrences of a TEXT string.", + "syntax": "generatetextcommand count= text=", "usage": "public", "arguments": [ { From 14be2cde64a3dde1644e4c6cf1bc8b5a4a97dd97 Mon Sep 17 00:00:00 2001 From: hetangmodi-crest Date: Fri, 18 Apr 2025 12:43:07 +0530 Subject: [PATCH 4/4] chore: return empty dict --- .../generators/conf_files/create_searchbnf_conf.py | 2 +- tests/unit/generators/conf_files/test_create_searchbnf_conf.py | 2 +- tests/unit/test_utils.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py index 39506aff21..f102644eb2 100644 --- a/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py +++ b/splunk_add_on_ucc_framework/generators/conf_files/create_searchbnf_conf.py @@ -37,7 +37,7 @@ def _set_attributes(self, **kwargs: Any) -> None: def generate(self) -> Dict[str, str]: if not self.searchbnf_info: - return {"": ""} + return {} file_path = self.get_file_output_path(["default", self.conf_file]) self.set_template_and_render( diff --git a/tests/unit/generators/conf_files/test_create_searchbnf_conf.py b/tests/unit/generators/conf_files/test_create_searchbnf_conf.py index 481257ba0a..0b43f5d96a 100644 --- a/tests/unit/generators/conf_files/test_create_searchbnf_conf.py +++ b/tests/unit/generators/conf_files/test_create_searchbnf_conf.py @@ -86,7 +86,7 @@ def test_generate_conf_without_custom_command( file_paths = searchbnf_conf.generate() # Assert that no files are returned since no custom command is configured - assert file_paths == {"": ""} + assert file_paths == {} def test_generate_conf(global_config_all_json, input_dir, output_dir, ta_name): diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 35673fd024..6112c633e8 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -32,7 +32,7 @@ def test_get_j2_env(): "conf_files/server_conf.template", "conf_files/settings_conf.template", "conf_files/tags_conf.template", - "conf_files/web_conf.template", + "web_conf.template", "conf_files/searchbnf_conf.template", ] assert sorted(expected_list_of_templates) == sorted(list_of_templates)