Skip to content

Commit

Permalink
Merge branch 'develop' into feat/handlers-custom-logic
Browse files Browse the repository at this point in the history
  • Loading branch information
kkedziak-splunk committed Feb 11, 2025
2 parents fcb65aa + 0f67e30 commit c66bd46
Show file tree
Hide file tree
Showing 68 changed files with 3,500 additions and 81 deletions.
10 changes: 5 additions & 5 deletions docs/generated_files.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ The following table describes the files generated by UCC framework.
| tags.conf | output/<YOUR_ADDON_NAME>/default | Generates `tags.conf` file based on the `eventtypes.conf` created for custom alert actions. |
| _account.conf | output/&lt;YOUR_ADDON_NAME&gt;/README | Generates `<YOUR_ADDON_NAME>_account.conf.spec` file for the configuration mentioned in globalConfig |
| _settings.conf | output/&lt;YOUR_ADDON_NAME&gt;/README | Generates `<YOUR_ADDON_NAME>_settings.conf.spec` file for the Proxy, Logging or Custom Tab mentioned in globalConfig |
| configuration.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates configuration.xml file in `default/data/ui/views/` folder if globalConfig is present. |
| dashboard.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates dashboard.xml file based on dashboard configuration present in globalConfig in `default/data/ui/views` folder. |
| default.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/nav | Generates default.xml file based on configs present in globalConfigin in `default/data/ui/nav` folder. |
| configuration.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates configuration.xml file in `default/data/ui/views/` folder if configuration is defined in globalConfig. |
| dashboard.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates dashboard.xml file based on dashboard configuration present in globalConfig, in `default/data/ui/views` folder. |
| default.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/nav | Generates default.xml file based on configs present in globalConfig, in `default/data/ui/nav` folder. |
| inputs.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates inputs.xml based on inputs configuration present in globalConfig, in `default/data/ui/views/inputs.xml` folder |
| _redirect.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates ta_name_redirect.xml file, if oauth is mentioned in globalConfig,in `default/data/ui/views/` folder. |
| _.html | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/alerts | Generates `alert_name.html` file based on alerts configuration present in globalConfig in `default/data/ui/alerts` folder. |
| _redirect.xml | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/views | Generates ta_name_redirect.xml file, if oauth is mentioned in globalConfig, in `default/data/ui/views/` folder. |
| _.html | output/&lt;YOUR_ADDON_NAME&gt;/default/data/ui/alerts | Generates `alert_name.html` file based on alerts configuration present in globalConfig, in `default/data/ui/alerts` folder. |

Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,17 @@ def __add_schemas_object(
) -> OpenAPIObject:
if open_api_object.components is not None:
open_api_object.components.schemas = {}
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity, without=["name"]
)
open_api_object.components.schemas[schema_name] = schema_object
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity, without=["name"]
)
open_api_object.components.schemas[schema_name] = schema_object
if hasattr(global_config.pages, "inputs") and hasattr( # type: ignore[attr-defined]
global_config.pages.inputs, "services" # type: ignore[attr-defined]
):
Expand Down Expand Up @@ -378,18 +380,20 @@ def __assign_ta_paths(
def __add_paths(
open_api_object: OpenAPIObject, global_config: DataClasses
) -> OpenAPIObject:
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{tab.name}" # type: ignore[attr-defined]
if hasattr(tab, "table")
else f"/{global_config.meta.restRoot}_settings/{tab.name}", # type: ignore[attr-defined]
path_name=tab.name,
actions=tab.table.actions
if hasattr(tab, "table") and hasattr(tab.table, "actions")
else None,
page=GloblaConfigPages.CONFIGURATION,
)
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{tab.name}" # type: ignore[attr-defined]
if hasattr(tab, "table")
else f"/{global_config.meta.restRoot}_settings/{tab.name}", # type: ignore[attr-defined]
path_name=tab.name,
actions=tab.table.actions
if hasattr(tab, "table") and hasattr(tab.table, "actions")
else None,
page=GloblaConfigPages.CONFIGURATION,
)
if hasattr(global_config.pages, "inputs") and hasattr( # type: ignore[attr-defined]
global_config.pages.inputs, "services" # type: ignore[attr-defined]
):
Expand Down
32 changes: 25 additions & 7 deletions splunk_add_on_ucc_framework/data_ui_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
# nosemgrep: splunk.use-defused-xml
from xml.etree import ElementTree as ET
from defusedxml import minidom

DEFAULT_VIEW = "configuration"
from typing import Optional


def _pretty_print_xml(string: str) -> str:
Expand All @@ -31,23 +30,41 @@ def _pretty_print_xml(string: str) -> str:


def generate_nav_default_xml(
include_inputs: bool, include_dashboard: bool, default_view: str
include_inputs: bool,
include_dashboard: bool,
include_configuration: bool,
default_view: Optional[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 default_view is None:
# we do this calculation as all the below properties are now optional
if include_configuration:
default_view = "configuration"
elif include_inputs:
default_view = "inputs"
elif include_dashboard:
default_view = "dashboard"
else:
default_view = "search"

if include_inputs:
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_configuration:
if default_view == "configuration":
ET.SubElement(
nav, "view", attrib={"name": "configuration", "default": "true"}
)
else:
ET.SubElement(nav, "view", attrib={"name": "configuration"})
if include_dashboard:
if default_view == "dashboard":
ET.SubElement(nav, "view", attrib={"name": "dashboard", "default": "true"})
Expand All @@ -57,6 +74,7 @@ def generate_nav_default_xml(
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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def _set_attributes(self, **kwargs: Any) -> None:
self.input_names: List[Dict[str, List[str]]] = []
self.disable = False
self.service_name = ""
self.default_value_info: Dict[str, Dict[str, str]] = {}
if self._global_config:
for service in self._global_config.inputs:
properties = []
Expand All @@ -42,6 +43,7 @@ def _set_attributes(self, **kwargs: Any) -> None:
{service["name"]: ["placeholder = placeholder"]}
)
continue
self.default_value_info[service["name"]] = {}
for entity in service.get("entity", {"field": "name"}):
# TODO: add the details and updates on what to skip and process
if entity["field"] == "name":
Expand All @@ -52,6 +54,15 @@ def _set_attributes(self, **kwargs: Any) -> None:
f"{entity['field']} = {entity.get('help', '').replace(nl, ' ')} "
f"{'' if entity.get('defaultValue') is None else ' Default: ' + str(entity['defaultValue'])}"
)
if entity.get("defaultValue"):
if type(entity["defaultValue"]) is bool:
self.default_value_info[service["name"]].update(
{entity["field"]: str(entity["defaultValue"]).lower()}
)
else:
self.default_value_info[service["name"]].update(
{f"{entity['field']}": f"{str(entity['defaultValue'])}"}
)

self.input_names.append({service["name"]: properties})

Expand All @@ -68,7 +79,10 @@ def generate_conf(self) -> Union[Dict[str, str], None]:
)

rendered_content = self._template.render(
input_names=stanzas, disabled=self.disable, service_name=self.service_name
input_names=stanzas,
disabled=self.disable,
service_name=self.service_name,
default_values=self.default_value_info,
)
self.writer(
file_name=self.conf_file,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

class AlertActionsHtml(HTMLGenerator):
__description__ = (
"Generates `alert_name.html` file based on alerts configuration present in globalConfig"
"Generates `alert_name.html` file based on alerts configuration present in globalConfig,"
" in `default/data/ui/alerts` folder."
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@
# limitations under the License.
#
from splunk_add_on_ucc_framework.generators.xml_files import XMLGenerator
from typing import Any, Dict
from typing import Any, Dict, Union
from splunk_add_on_ucc_framework import data_ui_generator


class ConfigurationXml(XMLGenerator):
__description__ = "Generates configuration.xml file in `default/data/ui/views/` folder if globalConfig is present."
__description__ = (
"Generates configuration.xml file in `default/data/ui/views/` folder if "
"configuration is defined in globalConfig."
)

def _set_attributes(self, **kwargs: Any) -> None:
self.configuration_xml_content = (
data_ui_generator.generate_views_configuration_xml(
self._addon_name,
if self._global_config and self._global_config.has_configuration():
self.configuration_xml_content = (
data_ui_generator.generate_views_configuration_xml(
self._addon_name,
)
)
)

def generate_xml(self) -> Dict[str, str]:
def generate_xml(self) -> Union[Dict[str, str], None]:
if self._global_config and not self._global_config.has_configuration():
return None
file_path = self.get_file_output_path(
["default", "data", "ui", "views", "configuration.xml"]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

class DashboardXml(XMLGenerator):
__description__ = (
"Generates dashboard.xml file based on dashboard configuration present in globalConfig"
"Generates dashboard.xml file based on dashboard configuration present in globalConfig,"
" in `default/data/ui/views` folder."
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

class DefaultXml(XMLGenerator):
__description__ = (
"Generates default.xml file based on configs present in globalConfig"
"in in `default/data/ui/nav` folder."
"Generates default.xml file based on configs present in globalConfig,"
" in `default/data/ui/nav` folder."
)

def _set_attributes(self, **kwargs: Any) -> None:
Expand All @@ -45,9 +45,8 @@ def _set_attributes(self, **kwargs: Any) -> None:
self.default_xml_content = data_ui_generator.generate_nav_default_xml(
include_inputs=self._global_config.has_inputs(),
include_dashboard=self._global_config.has_dashboard(),
default_view=self._global_config.meta.get(
"default_view", data_ui_generator.DEFAULT_VIEW
),
include_configuration=self._global_config.has_configuration(),
default_view=self._global_config.meta.get("default_view"),
)

def generate_xml(self) -> Dict[str, str]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class RedirectXml(XMLGenerator):
__description__ = (
"Generates ta_name_redirect.xml file, if oauth is mentioned in globalConfig,"
"in `default/data/ui/views/` folder."
" in `default/data/ui/views/` folder."
)

def _set_attributes(self, **kwargs: Any) -> None:
Expand Down
16 changes: 12 additions & 4 deletions splunk_add_on_ucc_framework/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,14 @@ def expand(self) -> None:
self.expand_entities()

def expand_tabs(self) -> None:
for i, tab in enumerate(self._content["pages"]["configuration"]["tabs"]):
self._content["pages"]["configuration"]["tabs"][i] = resolve_tab(tab)
if self.has_configuration():
for i, tab in enumerate(self._content["pages"]["configuration"]["tabs"]):
self._content["pages"]["configuration"]["tabs"][i] = resolve_tab(tab)

def expand_entities(self) -> None:
self._expand_entities(self._content["pages"]["configuration"]["tabs"])
self._expand_entities(
self._content["pages"].get("configuration", {}).get("tabs")
)
self._expand_entities(self._content["pages"].get("inputs", {}).get("services"))
self._expand_entities(self._content.get("alerts"))

Expand All @@ -126,7 +129,9 @@ def inputs(self) -> List[Any]:

@property
def tabs(self) -> List[Any]:
return self._content["pages"]["configuration"]["tabs"]
if "configuration" in self._content["pages"]:
return self._content["pages"]["configuration"]["tabs"]
return []

@property
def dashboard(self) -> Dict[str, Any]:
Expand Down Expand Up @@ -216,6 +221,9 @@ def add_ucc_version(self, version: str) -> None:
def has_inputs(self) -> bool:
return bool(self.inputs)

def has_configuration(self) -> bool:
return bool(self.tabs)

def has_alerts(self) -> bool:
return bool(self.alerts)

Expand Down
2 changes: 1 addition & 1 deletion splunk_add_on_ucc_framework/global_config_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def _dump_with_migrated_entities(
global_config.content["pages"].get("inputs", {}).get("services"), entity_type
)
_collapse_entities(
global_config.content["pages"]["configuration"].get("tabs"), entity_type
global_config.content["pages"].get("configuration", {}).get("tabs"), entity_type
)
_collapse_entities(global_config.content.get("alerts"), entity_type)

Expand Down
26 changes: 16 additions & 10 deletions splunk_add_on_ucc_framework/global_config_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

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
from splunk_add_on_ucc_framework.exceptions import GlobalConfigValidatorException

Expand Down Expand Up @@ -54,10 +53,13 @@ def __init__(self, source_dir: str, global_config: global_config_lib.GlobalConfi
self._config = global_config.content

@property
def config_tabs(self) -> List[Tab]:
return [
resolve_tab(tab) for tab in self._config["pages"]["configuration"]["tabs"]
]
def config_tabs(self) -> List[Any]:
if self._global_config.has_configuration():
return [
resolve_tab(tab)
for tab in self._config["pages"]["configuration"]["tabs"]
]
return []

def _validate_config_against_schema(self) -> None:
"""
Expand Down Expand Up @@ -430,7 +432,6 @@ def _validate_duplicates(self) -> None:
not required in schema, so this checks if globalConfig has inputs
"""
pages = self._config["pages"]

self._validate_tabs_duplicates(self.config_tabs)

inputs = pages.get("inputs")
Expand Down Expand Up @@ -700,9 +701,14 @@ def _validate_field_modifications(self) -> None:
)

def _validate_meta_default_view(self) -> None:
default_view = self._global_config.meta.get(
"defaultView", data_ui_generator.DEFAULT_VIEW
)
default_view = self._global_config.meta.get("defaultView")
if (
default_view == "configuration"
and not self._global_config.has_configuration()
):
raise GlobalConfigValidatorException(
'meta.defaultView == "configuration" but there is no configuration defined in globalConfig'
)
if default_view == "inputs" and not self._global_config.has_inputs():
raise GlobalConfigValidatorException(
'meta.defaultView == "inputs" but there is no inputs defined in globalConfig'
Expand All @@ -715,8 +721,8 @@ def _validate_meta_default_view(self) -> None:
def validate(self) -> None:
self._validate_config_against_schema()
self._validate_configuration_tab_table_has_name_field()
self._validate_custom_rest_handlers()
self._validate_file_type_entity()
self._validate_custom_rest_handlers()
self._validate_validators()
self._validate_multilevel_menu()
self._validate_duplicates()
Expand Down
3 changes: 0 additions & 3 deletions splunk_add_on_ucc_framework/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2701,9 +2701,6 @@
"$ref": "#/definitions/DashboardPage"
}
},
"required": [
"configuration"
],
"additionalProperties": false
},
"RegexValidator": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ python.version = python3
{% if disabled and service_name == input_name%}
disabled = true
{% endif %}
{% for item, value in default_values[input_name].items() %}
{{item}} = {{value}}
{% endfor %}
{% endfor %}
Loading

0 comments on commit c66bd46

Please sign in to comment.