diff --git a/.markdownlint.yaml b/.markdownlint.yaml
index 4d85d055e..8d673078d 100644
--- a/.markdownlint.yaml
+++ b/.markdownlint.yaml
@@ -3,6 +3,14 @@
# MD001/heading-increment
MD001: false
+ul-style:
+ style: sublist
+
+MD007:
+ # set indent as 4 spaces
+ # reason: https://python-markdown.github.io/#differences
+ indent: 4
+
# MD010/no-hard-tabs
MD010:
# Include code blocks
diff --git a/docs/advanced/groups_feature.md b/docs/advanced/groups_feature.md
index 8e5c51044..17234c8b7 100644
--- a/docs/advanced/groups_feature.md
+++ b/docs/advanced/groups_feature.md
@@ -7,8 +7,8 @@ The groups will be displayed at the bottom of the form.
- `label` displays the title of a specific group.
- `fields` specifies the list of fields in a group. All fields must be present in the **entity**.
- `options`:
- - `isExpandable` can be used to hide/show fields of the group. The default value is **false**.
- - `expand` can be used to show all fields of the group while opening the form. The default value is **false**.
+ + `isExpandable` can be used to hide/show fields of the group. The default value is **false**.
+ + `expand` can be used to show all fields of the group while opening the form. The default value is **false**.
### Usage
diff --git a/docs/advanced/oauth_support.md b/docs/advanced/oauth_support.md
index 30590b4a4..ae43dd1a7 100644
--- a/docs/advanced/oauth_support.md
+++ b/docs/advanced/oauth_support.md
@@ -6,40 +6,40 @@ Auth can be used inside the entity tag. Use `type: "oauth"` in the entity list a
- `type` field value must be oauth.
- `options`:
- - `auth_type` must be present. It can have either ["basic", "oauth"] (If we want basic and oauth both support) or ["oauth"] (If we want oauth support only).
- - `basic` must be present only if the auth_type is ["basic"].
- - This will have a list of fields for you to add in the basic authentication flow. In the given example, it is username, password, and security_token.
- - **Note: As of now, if you are selecting basic as auth_type, then the username and password fields are mandatory.**
- - `oauth` will have a list of fields for you to add in the oauth authentication flow. In the given example, it is `client_id`, `client_secret`, `redirect_url`, `scope`, and `endpoint`.
- - These fields are mandatory:
- - `client_id` is the client id for applying auth to your app or apps.
- - `client_secret` is the client secret for applying auth to your app or apps.
- - `redirect_url` will show the redirect url, which needs to be put in the app's redirect url.
- - `endpoint` will be the endpoint for you to build oauth support. For example, for salesforce, it will either be "login.salesforce.com", "test.salesforce.com", or any other custom endpoint.
- - There is also the ability to specify separate endpoints for authorize and token. To do this, instead of the single 'endpoint' field, use two separate ones:
- - `endpoint_authorize` specifies the endpoint used for authorization, for example, login.salesforce.com.
- - `endpoint_token` specifies the endpoint used for the token acqusition, for example, api.login.salesforce.com.
- - `auth_code_endpoint` must be present and its value should be the endpoint value for getting the auth_code using the app. If the url to get the auth_code is https://login.salesforce.com/services/oauth2/authorize, then this will have the value /services/oauth2/authorize.
- - `access_token_endpoint` must be present and its value should be the endpoint value for getting the ccess_token using the auth_code received. If the url to get the access token is https://login.salesforce.com/services/oauth2/token, then it will have the value /services/oauth2/token.
- - `auth_label` allows the user to have the custom label for the Auth Type dropdown.
- - `oauth_popup_width` is the width in pixels of the pop-up window that will open for oauth authentication (Optional, defaults to 600).
- - `oauth_popup_height` is the height in pixels of the pop-up window that will open for oauth authentication (Optional, defaults to 600).
- - `oauth_timeout` is the timeout in seconds for oauth authentication (Optional, defaults to 180 seconds).
- - `oauth_state_enabled` is used to include the state for oauth authentication (default value is false).
- - `auth_endpoint_token_access_type` is an optional parameter that is mapped into the value of the token_access_type query param in the authorisation url.
+ + `auth_type` must be present. It can have either ["basic", "oauth"] (If we want basic and oauth both support) or ["oauth"] (If we want oauth support only).
+ + `basic` must be present only if the auth_type is ["basic"].
+ - This will have a list of fields for you to add in the basic authentication flow. In the given example, it is username, password, and security_token.
+ - **Note: As of now, if you are selecting basic as auth_type, then the username and password fields are mandatory.**
+ + `oauth` will have a list of fields for you to add in the oauth authentication flow. In the given example, it is `client_id`, `client_secret`, `redirect_url`, `scope`, and `endpoint`.
+ + These fields are mandatory:
+ - `client_id` is the client id for applying auth to your app or apps.
+ - `client_secret` is the client secret for applying auth to your app or apps.
+ - `redirect_url` will show the redirect url, which needs to be put in the app's redirect url.
+ - `endpoint` will be the endpoint for you to build oauth support. For example, for salesforce, it will either be "login.salesforce.com", "test.salesforce.com", or any other custom endpoint.
+ + There is also the ability to specify separate endpoints for authorize and token. To do this, instead of the single 'endpoint' field, use two separate ones:
+ - `endpoint_authorize` specifies the endpoint used for authorization, for example, login.salesforce.com.
+ - `endpoint_token` specifies the endpoint used for the token acqusition, for example, api.login.salesforce.com.
+ + `auth_code_endpoint` must be present and its value should be the endpoint value for getting the auth_code using the app. If the url to get the auth_code is https://login.salesforce.com/services/oauth2/authorize, then this will have the value /services/oauth2/authorize.
+ + `access_token_endpoint` must be present and its value should be the endpoint value for getting the ccess_token using the auth_code received. If the url to get the access token is https://login.salesforce.com/services/oauth2/token, then it will have the value /services/oauth2/token.
+ + `auth_label` allows the user to have the custom label for the Auth Type dropdown.
+ + `oauth_popup_width` is the width in pixels of the pop-up window that will open for oauth authentication (Optional, defaults to 600).
+ + `oauth_popup_height` is the height in pixels of the pop-up window that will open for oauth authentication (Optional, defaults to 600).
+ + `oauth_timeout` is the timeout in seconds for oauth authentication (Optional, defaults to 180 seconds).
+ + `oauth_state_enabled` is used to include the state for oauth authentication (default value is false).
+ + `auth_endpoint_token_access_type` is an optional parameter that is mapped into the value of the token_access_type query param in the authorisation url.
- - The fields allowed in the basic and oauth fields are the following:
- - `oauth_field` should be kept as it is and without any change.
- - `label` can be changed if the user wants to change the label of the field in UI.
- - `field` must keep it as it is for mandatory fields as mentioned before.
- - `help` can be changed the if user wants to change the help text displayed below the field.
- - `encrypted` should be true if the user wants that particular field encrypted, otherwise, there is no need to have this parameter.
- - `required` specifies whether the field is required or not. The default value is true.
- - `defaultValue` is the initial input value (string, number, or boolean).
- - `options`:
- - `placeholder`: (RENOUNCED) The placeholder for the field.
- - `disableonEdit`: When the form is in edit mode, the field becomes unable to be edited. The default value is false.
- - `enable`: The enable property sets whether a field is enabled or not. The default value is true.
+ + The fields allowed in the basic and oauth fields are the following:
+ - `oauth_field` should be kept as it is and without any change.
+ - `label` can be changed if the user wants to change the label of the field in UI.
+ - `field` must keep it as it is for mandatory fields as mentioned before.
+ - `help` can be changed the if user wants to change the help text displayed below the field.
+ - `encrypted` should be true if the user wants that particular field encrypted, otherwise, there is no need to have this parameter.
+ - `required` specifies whether the field is required or not. The default value is true.
+ - `defaultValue` is the initial input value (string, number, or boolean).
+ - `options`:
+ + `placeholder`: (RENOUNCED) The placeholder for the field.
+ + `disableonEdit`: When the form is in edit mode, the field becomes unable to be edited. The default value is false.
+ + `enable`: The enable property sets whether a field is enabled or not. The default value is true.
> [!WARNING]
> The [Placeholder](https://splunkui.splunkeng.com/Packages/react-ui/Text?section=develop) attribute is deprecated and renounced. Instead, we recommend to use the "help" attribute.
diff --git a/docs/alert_actions/alert_scripts.md b/docs/alert_actions/alert_scripts.md
index 72780bd70..612f9cebb 100644
--- a/docs/alert_actions/alert_scripts.md
+++ b/docs/alert_actions/alert_scripts.md
@@ -9,16 +9,16 @@ The following files would be created/ updated in the output folder once you exec
In the python file that is created, below are the methods that you can use or override for varying use cases:
- `process_event()`
- - This is the start point of where you require to write the logic of sending data from Splunk to any other
+ + This is the start point of where you require to write the logic of sending data from Splunk to any other
service via its APIs. Additionally, you can validate the parameters that are provided in the alert action
as client side validation (via JavaScript) isn't allowed in Splunk's alert action's HTML page for
security reasons. Note: This method must be overwritten.
- `get_events()` -> List[dict]
- - Used to get the events that triggered the alert. It returns a list of dictionary. A dictionary points to an event that triggered the alert, and each dictionary has the fields extracted by Splunk.
+ + Used to get the events that triggered the alert. It returns a list of dictionary. A dictionary points to an event that triggered the alert, and each dictionary has the fields extracted by Splunk.
- `addevent(raw: str, sourcetype: str)`
- - If you are bringing additional information from an outer service, you can write that information using this method. You write a single record using the method. This method will append all the records and will dump it to Splunk when `writeevents()` method is called.
+ + If you are bringing additional information from an outer service, you can write that information using this method. You write a single record using the method. This method will append all the records and will dump it to Splunk when `writeevents()` method is called.
- `writeevents(index: str, host: str, source: str)`
- - All the events added to the queue using `addevent()` method are written to Splunk with the details passed in the arguments.
+ + All the events added to the queue using `addevent()` method are written to Splunk with the details passed in the arguments.
An example of a script with validations:
diff --git a/docs/alert_actions/index.md b/docs/alert_actions/index.md
index 00b7b9c3e..7716b4487 100644
--- a/docs/alert_actions/index.md
+++ b/docs/alert_actions/index.md
@@ -25,7 +25,7 @@ Developers are required to add alerts in the global config file to create an Ale
| Property | Type | Description |
|---------------------------------------------------------------------------|--------|--------------------------------------------------------------------------------------------------------|
-| type\* | string | The type of the user input in the alert. Available choices: "text", "checkbox", "singleSelect", "radio", "singleSelectSplunkSearch". |
+| type\* | string | The type of the user input in the alert. Available choices: "text", "textarea", "checkbox", "singleSelect", "radio", "singleSelectSplunkSearch". |
| label\* | string | The text that would be shown in the alert action UI. |
| field\* | string | The field that would be used in the scripts to get the value from the user input. These are defined as `param.` in the `alert_actions.conf`. |
| options | array | Static choices that a user can select in the alert action UI. |
diff --git a/docs/contributing.md b/docs/contributing.md
index 3feac4a39..4a9f6a0be 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -129,7 +129,7 @@ gh pr merge main --auto --merge
```
- If a release encounters issues requiring a quick bug fix (handled by the UCC team):
- - Create a PR to the main branch with the fix, including tests that reproduce and then fix the issue.
- - Ensure CI passes and await team review.
- - Merge the PR using the merge commit option on GitHub.
- - Backport the bug fix PR to the develop branch.
+ + Create a PR to the main branch with the fix, including tests that reproduce and then fix the issue.
+ + Ensure CI passes and await team review.
+ + Merge the PR using the merge commit option on GitHub.
+ + Backport the bug fix PR to the develop branch.
diff --git a/docs/dashboard.md b/docs/dashboard.md
index 5676baa07..efacf4d1a 100644
--- a/docs/dashboard.md
+++ b/docs/dashboard.md
@@ -113,6 +113,27 @@ except Exception as e:
log.log_exception(logger, e, "Other")
```
+By default, the error section displays events logged with the ERROR level, but since version **5.50** UCC allows the user to define what level of logs should be displayed in this section. There are two levels to choose from:
+
+* ERROR
+* CRITICAL
+
+```json
+ "dashboard": {
+ "panels": [
+ {
+ "name": "default"
+ }
+ ],
+ "settings": {
+ "error_panel_log_lvl": [
+ "ERROR",
+ "CRITICAL"
+ ]
+ }
+ }
+```
+
## Configuration
To be able to add a monitoring dashboard page to an existing add-on, you need to adjust your
diff --git a/docs/generated_files.md b/docs/generated_files.md
index 1d7e0a2c6..8f6e3801b 100644
--- a/docs/generated_files.md
+++ b/docs/generated_files.md
@@ -8,6 +8,16 @@ Below table describes the files generated by UCC framework
| File Name | File Location | File Description |
| ------------ | ------------ | ----------------- |
+| app.conf | output/<YOUR_ADDON_NAME>/default | Generates `app.conf` with the details mentioned in globalConfig[meta] |
+| inputs.conf | output/<YOUR_ADDON_NAME>/default | Generates `inputs.conf` and `inputs.conf.spec` file for the services mentioned in globalConfig |
+| server.conf | output/<YOUR_ADDON_NAME>/default | Generates `server.conf` for the custom conf files created as per configurations in globalConfig |
+| restmap.conf | output/<YOUR_ADDON_NAME>/default | Generates `restmap.conf` for the custom REST handlers that are generated based on configs from globalConfig |
+| web.conf | output/<YOUR_ADDON_NAME>/default | Generates `web.conf` to expose the endpoints generated in `restmap.conf` which is generated based on configurations from globalConfig. |
+| 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. |
+| _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 globalConfig is present. |
| dashboard.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates dashboard.xml file based on dashboard configuration present in globalConfig in `default/data/ui/views` folder. |
| default.xml | output/<YOUR_ADDON_NAME>/default/data/ui/nav | Generates default.xml file based on configs present in globalConfigin in `default/data/ui/nav` folder. |
diff --git a/docs/metadata.md b/docs/metadata.md
index 57b51d947..4516390e3 100644
--- a/docs/metadata.md
+++ b/docs/metadata.md
@@ -9,8 +9,8 @@ Metadata contains general information about add-on build.
| Property | Type | Description |
|---------------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| displayName\* | string | Name displayed for end user. |
-| name\* | string | Name used for API endpoints and all code references separating separating endpoints from any other app. |
-| restRoot\* | string | String used to create API endpoints. Follow patter `/^\w+$/`. |
+| name\* | string | Name used for API endpoints and all code references separating endpoints from any other app. Please refer to [app.conf/[package]/id](https://docs.splunk.com/Documentation/Splunk/latest/Admin/Appconf#.5Bpackage.5D) for more details. |
+| restRoot\* | string | String used to create API endpoints, allows alphanumeric and `-` characters. |
| apiVersion | string | [Deprecated] Version of used API. |
| version\* | string | Version of the add-on. |
| schemaVersion | string | Version of JSON schema used in build process. |
diff --git a/docs/openapi.md b/docs/openapi.md
index e0851f3b4..3c487f676 100644
--- a/docs/openapi.md
+++ b/docs/openapi.md
@@ -28,9 +28,9 @@ The OpenAPI Description document can be used to create:
* interactive documentation that generates simple curl requests to all documented endpoints (check [this section](#how-to-get-curl-commands-and-use-them) for the relevant instructions).
* automation that uses the simple requests to create more complex solutions such as:
- * orchestration
- * mass load or migration
- * automated tests.
+ - orchestration
+ - mass load or migration
+ - automated tests.
Check [swagger](https://swagger.io/) or [other tools](https://github.com/OAI/OpenAPI-Specification/blob/main/IMPLEMENTATIONS.md) for more possibilities.
@@ -44,7 +44,7 @@ Check [swagger](https://swagger.io/) or [other tools](https://github.com/OAI/Ope
### Instructions
1. Open https://editor.swagger.io/
- * Alternatively, you can run your own instance of Swagger Editor
+ - Alternatively, you can run your own instance of Swagger Editor
by running the following command in terminal:
`docker run -p 8081:8080 swaggerapi/swagger-editor`
@@ -80,12 +80,14 @@ Make sure you clicked the Authorize button, gave the username and password, and
1. Go to the directory where you downloaded `openapi.json` file
2. Run the following command: `docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/openapi.json -g python -o /local/restapi_client`
- * make sure `openapi.json` is in the current directory
- * you can generate clients for other languages as well - run
+
+ - make sure `openapi.json` is in the current directory
+ - you can generate clients for other languages as well - run
`docker run --rm openapitools/openapi-generator-cli generate list`
to see the list of supported languages
+
3. The client should appear in `restapi_client`. Open that directory (`cd restapi_client`)
4. Install the client (`pip install .`)
5. See `README.md` for an example of usage
diff --git a/docs/quickstart.md b/docs/quickstart.md
index cbc9b0ab8..2589914bf 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -214,14 +214,12 @@ It accepts the following parameters:
the `output//appserver/static/js/build` directory.
* collects and installs the add-on's requirements into the
`output//lib` directory of add-on's package.
-* NOTE: For the add-on's requirements, the packages are installed according to
- following information:
- * `lib/requirements.txt` installs Python3 compatible packages into
- the `output//lib`.
- * It removes `setuptools*`, `bin*`, `pip*`, `distribute*`, and `wheel*` if
- they exist from `output//lib`
- * It removes the execute bit from every file under `output//lib`.
* replaces tokens in views.
* copies the add-on's `package/*` to the `output//*` directory.
* If an add-on requires some additional configurations in packaging,
then `ucc-gen` runs the code in the `additional_packaging.py` file as well.
+* **NOTE:** For the add-on's requirements, the packages are installed according to following information:
+ - `lib/requirements.txt` installs Python3 compatible packages into the `output//lib`.
+ - It removes `setuptools*`, `bin*`, `pip*`, `distribute*`, and `wheel*` if they exist from `output//lib`
+ - It removes the execute bit from every file under `output//lib`.
+* **NOTE:** The build won't be generated only when the add-on name in `meta[name]` of `globalConfig` and `info[id][name]` in `app.manifest` are not same.
diff --git a/docs/table.md b/docs/table.md
index 3da59bd14..0fd65011c 100644
--- a/docs/table.md
+++ b/docs/table.md
@@ -5,15 +5,15 @@ Tables include many built-in features such as sorting, filtering, and pagination
### Properties
- `header`* (Array Objects) specifies the list of columns in the table.
- - `field`* is he name of the field where the column data will be displayed.
- - `label`* is the title of the column.
- - [mapping](advanced/custom_mapping.md) is used to map field values to more meaningful values.
- - [customCell](custom_ui_extensions/custom_cell.md) is used to modify the default cell values.
+ + `field`* is he name of the field where the column data will be displayed.
+ + `label`* is the title of the column.
+ + [mapping](advanced/custom_mapping.md) is used to map field values to more meaningful values.
+ + [customCell](custom_ui_extensions/custom_cell.md) is used to modify the default cell values.
- `actions`* (Array) specifies what type of action can be performed on the stanza. The supported types are `edit`, `clone`, and `delete`.
- `moreInfo` (Array Objects) specifies the list of fields that will be displayed in row form when the user clicks the Row Expand button.
- - `field` is used to dispaly the data in a column.
- - `label` is the title of the column.
- - [mapping](advanced/custom_mapping.md) is used to map field values to more meaningful values.
+ + `field` is used to dispaly the data in a column.
+ + `label` is the title of the column.
+ + [mapping](advanced/custom_mapping.md) is used to map field values to more meaningful values.
- [customRow](custom_ui_extensions/custom_row.md) can be used to customise the moreInfo Component.
### List of built-in table fields for Modular Input
diff --git a/splunk_add_on_ucc_framework/app_conf.py b/splunk_add_on_ucc_framework/app_conf.py
deleted file mode 100644
index 5aff1e130..000000000
--- a/splunk_add_on_ucc_framework/app_conf.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Copyright 2024 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.
-#
-import time
-from typing import Sequence
-
-import addonfactory_splunk_conf_parser_lib as conf_parser
-from splunk_add_on_ucc_framework import app_manifest as app_manifest_lib
-
-APP_CONF_FILE_NAME = "app.conf"
-
-
-class AppConf:
- def __init__(self) -> None:
- self._app_conf = conf_parser.TABConfigParser()
-
- def read(self, path: str) -> None:
- self._app_conf.read(path)
-
- def update(
- self,
- version: str,
- app_manifest: app_manifest_lib.AppManifest,
- conf_file_names: Sequence[str],
- is_visible: bool,
- **kwargs: str,
- ) -> None:
- if "launcher" not in self._app_conf:
- self._app_conf.add_section("launcher")
- if "id" not in self._app_conf:
- self._app_conf.add_section("id")
- if "install" not in self._app_conf:
- self._app_conf.add_section("install")
- if "package" not in self._app_conf:
- self._app_conf.add_section("package")
- if "ui" not in self._app_conf:
- self._app_conf.add_section("ui")
- if "triggers" not in self._app_conf and conf_file_names:
- self._app_conf.add_section("triggers")
-
- self._app_conf["launcher"]["version"] = version
- self._app_conf["launcher"]["description"] = app_manifest.get_description()
- authors = app_manifest.get_authors()
- first_author = authors[0]
- self._app_conf["launcher"]["author"] = first_author["name"]
- self._app_conf["id"]["version"] = version
- self._app_conf["id"]["name"] = app_manifest.get_addon_name()
- self._app_conf["install"]["build"] = str(int(time.time()))
- self._app_conf["install"]["is_configured"] = "false"
- self._app_conf["install"]["state"] = "enabled"
- self._app_conf["package"]["id"] = app_manifest.get_addon_name()
- self._app_conf["package"]["check_for_updates"] = kwargs["check_for_updates"]
-
- self._app_conf["ui"]["label"] = app_manifest.get_title()
- if kwargs.get("supported_themes") != "":
- self._app_conf["ui"]["supported_themes"] = kwargs["supported_themes"]
- self._app_conf["ui"]["is_visible"] = "true" if is_visible else "false"
- for conf_file_name in conf_file_names:
- self._app_conf["triggers"][f"reload.{conf_file_name}"] = "simple"
-
- def write(self, path: str) -> None:
- with open(path, "w") as fd:
- self._app_conf.write(fd)
diff --git a/splunk_add_on_ucc_framework/commands/build.py b/splunk_add_on_ucc_framework/commands/build.py
index 224ce04e5..d1c2ccadc 100644
--- a/splunk_add_on_ucc_framework/commands/build.py
+++ b/splunk_add_on_ucc_framework/commands/build.py
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-import configparser
import glob
import json
import logging
@@ -34,9 +33,7 @@
utils,
)
from splunk_add_on_ucc_framework import dashboard
-from splunk_add_on_ucc_framework import app_conf as app_conf_lib
from splunk_add_on_ucc_framework import meta_conf as meta_conf_lib
-from splunk_add_on_ucc_framework import server_conf as server_conf_lib
from splunk_add_on_ucc_framework import app_manifest as app_manifest_lib
from splunk_add_on_ucc_framework import global_config as global_config_lib
from splunk_add_on_ucc_framework.commands.modular_alert_builder import (
@@ -54,6 +51,7 @@
ucc_to_oas,
)
from splunk_add_on_ucc_framework.generators.file_generator import begin
+from splunk_add_on_ucc_framework.generators.conf_files.create_app_conf import AppConf
logger = logging.getLogger("ucc_gen")
@@ -160,19 +158,6 @@ def _add_modular_input(
with open(helper_filename, "w") as helper_file:
helper_file.write(content)
- input_default = os.path.join(outputdir, ta_name, "default", "inputs.conf")
- config = configparser.ConfigParser()
- if os.path.exists(input_default):
- config.read(input_default)
-
- if config.has_section(input_name):
- config[input_name]["python.version"] = "python3"
- else:
- config[input_name] = {"python.version": "python3"}
-
- with open(input_default, "w") as configfile:
- config.write(configfile)
-
def _get_ignore_list(
addon_name: str, ucc_ignore_path: str, output_directory: str
@@ -454,6 +439,7 @@ def generate(
gc_path = _get_and_check_global_config_path(source, config_path)
if gc_path:
+ ui_available = True
logger.info(f"Using globalConfig file located @ {gc_path}")
global_config = global_config_lib.GlobalConfig(gc_path)
# handle the update of globalConfig before validating
@@ -536,31 +522,13 @@ def generate(
_add_modular_input(ta_name, global_config, output_directory)
if global_config.has_alerts():
logger.info("Generating alerts code")
- alert_builder.generate_alerts(
- global_config, ta_name, internal_root_dir, output_directory
- )
+ alert_builder.generate_alerts(global_config, ta_name, output_directory)
conf_file_names = []
conf_file_names.extend(list(scheme.settings_conf_file_names))
conf_file_names.extend(list(scheme.configs_conf_file_names))
conf_file_names.extend(list(scheme.oauth_conf_file_names))
- source_server_conf_path = os.path.join(source, "default", "server.conf")
- # For now, only create server.conf only if no server.conf is present in
- # the source package.
- if not os.path.isfile(source_server_conf_path):
- server_conf = server_conf_lib.ServerConf()
- server_conf.create_default(conf_file_names)
- output_server_conf_path = os.path.join(
- output_directory,
- ta_name,
- "default",
- server_conf_lib.SERVER_CONF_FILE_NAME,
- )
- server_conf.write(output_server_conf_path)
- logger.info(
- f"Created default {server_conf_lib.SERVER_CONF_FILE_NAME} file in the output folder"
- )
if global_config.has_dashboard():
logger.info("Including dashboard")
dashboard_definition_json_path = os.path.join(
@@ -632,31 +600,17 @@ def generate(
f"Updated {app_manifest_lib.APP_MANIFEST_FILE_NAME} file in the output folder"
)
- app_conf = app_conf_lib.AppConf()
- output_app_conf_path = os.path.join(
- output_directory, ta_name, "default", app_conf_lib.APP_CONF_FILE_NAME
- )
- app_conf.read(output_app_conf_path)
- should_be_visible = False
- check_for_updates = "true"
- supported_themes = ""
- if global_config:
- should_be_visible = True
- if global_config.meta.get("checkForUpdates") is False:
- check_for_updates = "false"
- if global_config.meta.get("supportedThemes") is not None:
- supported_themes = ", ".join(global_config.meta["supportedThemes"])
- app_conf.update(
- addon_version,
- app_manifest,
- conf_file_names,
- should_be_visible,
- check_for_updates=check_for_updates,
- supported_themes=supported_themes,
- )
- app_conf.write(output_app_conf_path)
- logger.info(f"Updated {app_conf_lib.APP_CONF_FILE_NAME} file in the output folder")
-
+ # NOTE: merging source and generated 'app.conf' as per previous design
+ AppConf(
+ global_config=global_config,
+ input_dir=source,
+ output_dir=output_directory,
+ ucc_dir=internal_root_dir,
+ addon_name=ta_name,
+ app_manifest=app_manifest,
+ addon_version=addon_version,
+ has_ui=ui_available,
+ ).generate()
license_dir = os.path.abspath(os.path.join(source, os.pardir, "LICENSES"))
if os.path.exists(license_dir):
logger.info("Copy LICENSES directory")
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_conf_gen.py b/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_conf_gen.py
deleted file mode 100644
index 790b66c6f..000000000
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_conf_gen.py
+++ /dev/null
@@ -1,274 +0,0 @@
-#
-# Copyright 2024 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.
-#
-import json
-import logging
-from os import linesep, makedirs, path as op
-import shutil
-from typing import Dict, Any
-
-from jinja2 import Environment, FileSystemLoader
-
-from splunk_add_on_ucc_framework.commands.modular_alert_builder import (
- arf_consts as ac,
-)
-from splunk_add_on_ucc_framework.commands.modular_alert_builder.alert_actions_helper import (
- write_file,
-)
-from splunk_add_on_ucc_framework.commands.modular_alert_builder.alert_actions_merge import (
- remove_alert_from_conf_file,
-)
-
-logger = logging.getLogger("ucc_gen")
-
-
-class AlertActionsConfGeneration:
- def __init__(
- self,
- input_setting: Dict[str, Any],
- package_path: str,
- internal_source_path: str,
- ) -> None:
- self._alert_conf_name = "alert_actions.conf"
- self._alert_spec_name = "alert_actions.conf.spec"
- self._eventtypes_conf = "eventtypes.conf"
- self._tags_conf = "tags.conf"
- self._alert_settings = input_setting[ac.MODULAR_ALERTS]
- self._package_path = package_path
- self._internal_source_path = internal_source_path
- # nosemgrep: splunk.autoescape-disabled, python.jinja2.security.audit.autoescape-disabled.autoescape-disabled
- self._templates = Environment(
- loader=FileSystemLoader(
- op.join(op.dirname(op.realpath(__file__)), "arf_template")
- ),
- trim_blocks=True,
- lstrip_blocks=True,
- keep_trailing_newline=True,
- )
- self._html_fields = [ac.PARAMETERS]
- self._default_conf_settings = {
- "python.version": "python3",
- "is_custom": 1,
- "payload_format": "json",
- "icon_path": "alerticon.png",
- }
-
- def get_local_conf_file_path(self, conf_name: str) -> str:
- local_path = op.join(self._package_path, "default")
- if not op.exists(local_path):
- makedirs(local_path)
-
- return op.join(local_path, conf_name)
-
- def get_spec_file_path(self) -> str:
- readme_path = op.join(self._package_path, "README")
- if not op.exists(readme_path):
- makedirs(readme_path)
- return op.join(readme_path, self._alert_spec_name)
-
- def generate_conf(self) -> None:
- logger.info(
- 'status="starting", operation="generate", '
- + 'object="alert_actions.conf", object_type="file"'
- )
- template = self._templates.get_template("alert_actions.conf.template")
- deny_list = frozenset(
- [
- "short_name",
- "alert_props",
- "parameters",
- "uuid",
- "code",
- "largeIcon",
- "smallIcon",
- "index",
- "iconFileName", # it is a config from globalConfig that gets written to icon_path
- "customScript", # it is a config from globalConfig only for Python script
- ]
- )
- alerts: Dict[str, Any] = {}
- for alert in self._alert_settings:
- alert_name = alert["short_name"]
- alerts[alert_name] = []
- for k, v in alert.items():
- if k == "adaptive_response":
- new_cam = {
- sub_k: sub_v
- for sub_k, sub_v in list(v.items())
- if sub_k != "sourcetype" and sub_v
- }
- value = f"param._cam = {json.dumps(new_cam)}"
- alerts[alert_name].append(value)
- elif k == "alert_props":
- if alert.get("iconFileName", "alerticon.png") != "alerticon.png":
- alert["alert_props"]["icon_path"] = alert["iconFileName"]
- else:
- # we copy UCC framework's alerticon.png only when a custom isn't provided
- shutil.copy(
- op.join(
- self._internal_source_path, "static", "alerticon.png"
- ),
- op.join(self._package_path, "appserver", "static"),
- )
- for pk, pv in v.items():
- value = f"{str(pk).strip()} = {str(pv).strip()}"
- alerts[alert_name].append(value)
- elif k not in deny_list:
- value = f"{str(k).strip()} = {str(v).strip()}"
- alerts[alert_name].append(value)
- for k, v in alert.items():
- if k == "parameters":
- for param in v:
- param_name = param["name"].strip()
- if param.get("default_value") is not None:
- param_default_value = str(
- param.get("default_value")
- ).strip()
- alerts[alert_name].append(
- f"param.{param_name} = {param_default_value}"
- )
- else:
- alerts[alert_name].append(f"param.{param_name} = ")
- final_string = template.render(alerts=alerts)
- text = linesep.join([s.strip() for s in final_string.splitlines()])
- write_file(
- self._alert_conf_name,
- self.get_local_conf_file_path(self._alert_conf_name),
- text,
- )
- logger.info(
- 'status="success", operation="generate", '
- + 'object="alert_actions.conf", object_type="file"'
- )
-
- def generate_eventtypes(self) -> None:
- logger.info(
- 'status="starting", operation="generate", '
- + 'object="eventtypes.conf", object_type="file"'
- )
- template = self._templates.get_template("eventtypes.conf.template")
- final_string = template.render(mod_alerts=self._alert_settings)
- text = linesep.join([s.strip() for s in final_string.splitlines()])
- file_path = self.get_local_conf_file_path(self._eventtypes_conf)
- write_file(self._eventtypes_conf, file_path, text)
-
- # remove the stanza if not checked
- for alert in self._alert_settings:
- if alert.get("adaptive_response") and alert["adaptive_response"].get(
- "sourcetype"
- ):
- continue
- remove_alert_from_conf_file(alert, file_path)
- logger.info(
- 'status="success", operation="generate", '
- + 'object="eventtypes.conf", object_type="file"'
- )
-
- def generate_tags(self) -> None:
- logger.info(
- 'status="starting", operation="generate", '
- + 'object="tags.conf", object_type="file"'
- )
- template = self._templates.get_template("tags.conf.template")
- final_string = template.render(mod_alerts=self._alert_settings)
- text = linesep.join([s.strip() for s in final_string.splitlines()])
- file_path = self.get_local_conf_file_path(self._tags_conf)
- write_file(self._tags_conf, file_path, text)
-
- # remove the stanza if not checked
- for alert in self._alert_settings:
- if alert.get("adaptive_response") and alert["adaptive_response"].get(
- "sourcetype"
- ):
- continue
- remove_alert_from_conf_file(alert, file_path)
- logger.info(
- 'status="success", operation="generate", '
- + 'object="tags.conf", object_type="file"'
- )
-
- def generate_spec(self) -> None:
- logger.info(
- 'status="starting", operation="generate", '
- + 'object="alert_actions.conf.spec", object_type="file"'
- )
- template = self._templates.get_template("alert_actions.conf.spec.template")
- _router = {
- "dropdownlist": "list",
- "text": "string",
- "textarea": "string",
- "checkbox": "bool",
- "password": "password",
- "dropdownlist_splunk_search": "list",
- "radio": "list",
- }
- alerts: Dict[str, Any] = {}
- for alert in self._alert_settings:
- alert_name = alert["short_name"]
- alerts[alert_name] = []
- for k, v in alert.items():
- if k == "adaptive_response":
- alerts[alert_name].append(
- "param._cam = Adaptive Response parameters."
- )
- elif k == "parameters":
- for param in v:
- format_type = _router[param["format_type"]]
- is_required = (
- "It's a required parameter."
- if param.get("required") and param["required"]
- else ""
- )
- param_default_value = param.get("default_value")
- default_value = (
- f"It's default value is {param_default_value}."
- if param_default_value
- else ""
- )
- value = (
- f'param.{param["name"]} = <{format_type}> '
- f'{param["label"]}. {is_required} {default_value}'
- )
- alerts[alert_name].append(value)
- final_string = template.render(alerts=alerts)
- text = linesep.join([s.strip() for s in final_string.splitlines()])
- write_file(self._alert_spec_name, self.get_spec_file_path(), text)
- logger.info(
- 'status="success", operation="generate", '
- + 'object="alert_actions.conf.spec", object_type="file"'
- )
-
- def handle(self) -> None:
- self.add_default_settings()
- self.generate_conf()
- self.generate_spec()
- self.generate_eventtypes()
- self.generate_tags()
-
- def add_default_settings(self) -> None:
- for alert in self._alert_settings:
- if ac.ALERT_PROPS not in list(alert.keys()):
- alert[ac.ALERT_PROPS] = {}
- for k, v in list(self._default_conf_settings.items()):
- if k in list(alert[ac.ALERT_PROPS].keys()):
- continue
-
- alert[ac.ALERT_PROPS][k] = v
- logger.info(
- 'status="success", operation="Add default setting", alert_name="%s", "%s"="%s"',
- alert[ac.SHORT_NAME],
- k,
- v,
- )
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_helper.py b/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_helper.py
index ad1b59669..8b250c4e3 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_helper.py
+++ b/splunk_add_on_ucc_framework/commands/modular_alert_builder/alert_actions_helper.py
@@ -14,8 +14,7 @@
# limitations under the License.
#
import logging
-import os.path as op
-from os import makedirs, remove
+from os import makedirs, remove, path
import addonfactory_splunk_conf_parser_lib as conf_parser
@@ -26,11 +25,15 @@
logger = logging.getLogger("ucc_gen")
-def write_file(file_name: str, file_path: str, content: str) -> None:
+def write_file(file_name: str, file_path: str, content: str, **kwargs: str) -> None:
+ """
+ :param merge_mode: only supported for .conf and .conf.spec files.
+ """
logger.debug('operation="write", object="%s" object_type="file"', file_path)
+ merge_mode = kwargs.get("merge_mode", "stanza_overwrite")
do_merge = False
- if file_name.endswith(".conf") or file_name.endswith("conf.spec"):
+ if file_name.endswith(".conf") or file_name.endswith(".conf.spec"):
do_merge = True
else:
logger.debug(
@@ -39,19 +42,19 @@ def write_file(file_name: str, file_path: str, content: str) -> None:
)
new_file = None
- if op.exists(file_path) and do_merge:
- new_file = op.join(op.dirname(file_path), "new_" + file_name)
+ if path.exists(file_path) and do_merge:
+ new_file = path.join(path.dirname(file_path), "new_" + file_name)
if new_file:
try:
with open(new_file, "w+") as fhandler:
fhandler.write(content)
- merge_conf_file(new_file, file_path)
+ merge_conf_file(new_file, file_path, merge_mode=merge_mode)
finally:
- if op.exists(new_file):
+ if path.exists(new_file):
remove(new_file)
else:
- if not op.exists(op.dirname(file_path)):
- makedirs(op.dirname(file_path))
+ if not path.exists(path.dirname(file_path)):
+ makedirs(path.dirname(file_path))
with open(file_path, "w+") as fhandler:
fhandler.write(content)
if do_merge:
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_consts.py b/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_consts.py
index cf23aaa7f..5ab91027e 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_consts.py
+++ b/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_consts.py
@@ -15,6 +15,4 @@
#
SHORT_NAME = "short_name"
-PARAMETERS = "parameters"
MODULAR_ALERTS = "modular_alerts"
-ALERT_PROPS = "alert_props"
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/builder.py b/splunk_add_on_ucc_framework/commands/modular_alert_builder/builder.py
index 7dfbd20cb..4bf09d4c1 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/builder.py
+++ b/splunk_add_on_ucc_framework/commands/modular_alert_builder/builder.py
@@ -18,7 +18,6 @@
from splunk_add_on_ucc_framework import global_config as global_config_lib
from splunk_add_on_ucc_framework.commands.modular_alert_builder import (
- alert_actions_conf_gen,
normalize,
)
from splunk_add_on_ucc_framework.commands.modular_alert_builder import (
@@ -31,7 +30,6 @@
def generate_alerts(
global_config: global_config_lib.GlobalConfig,
addon_name: str,
- internal_source_dir: str,
output_dir: str,
) -> None:
envs = normalize.normalize(
@@ -41,13 +39,6 @@ def generate_alerts(
package_dir = os.path.join(output_dir, addon_name)
schema_content = envs["schema.content"]
- conf_gen = alert_actions_conf_gen.AlertActionsConfGeneration(
- input_setting=schema_content,
- package_path=package_dir,
- internal_source_path=internal_source_dir,
- )
- conf_gen.handle()
-
py_gen = alert_actions_py_gen.AlertActionsPyGenerator(
addon_name=addon_name,
input_setting=schema_content,
diff --git a/splunk_add_on_ucc_framework/commands/rest_builder/builder.py b/splunk_add_on_ucc_framework/commands/rest_builder/builder.py
index 9c73f703e..9d534f09f 100644
--- a/splunk_add_on_ucc_framework/commands/rest_builder/builder.py
+++ b/splunk_add_on_ucc_framework/commands/rest_builder/builder.py
@@ -20,8 +20,6 @@
from splunk_add_on_ucc_framework.commands.rest_builder import (
global_config_builder_schema,
)
-from splunk_add_on_ucc_framework.rest_map_conf import RestmapConf
-from splunk_add_on_ucc_framework.web_conf import WebConf
from splunk_add_on_ucc_framework.global_config import OSDependentLibraryConfig
__all__ = ["RestBuilder"]
@@ -159,50 +157,12 @@ def _add_executable_attribute(file_path: str) -> None:
def build(self) -> None:
for endpoint in self._schema.endpoints:
- # If the endpoint is oauth, which is for getting accesstoken. Conf file entries should not get created.
- if endpoint._name != "oauth":
- if endpoint._name == "settings":
- self.output.put(
- self.output.default,
- f"{endpoint.conf_name}.conf",
- endpoint.generate_conf_with_default_values(),
- )
-
- self.output.put(
- self.output.readme,
- f"{endpoint.conf_name}.conf.spec",
- endpoint.generate_spec(),
- )
-
- # Add data input of self defined conf to inputs.conf.spec
- if endpoint._entities[0] and endpoint._entities[0]._conf_name:
- lines = [
- f"[{endpoint._name}://]",
- "placeholder = placeholder",
- ]
- self.output.put(
- self.output.readme, "inputs.conf.spec", "\n".join(lines)
- )
-
self.output.put(
self.output.bin,
endpoint.rh_name + ".py",
endpoint.generate_rh(),
)
- self.output.put(
- self.output.default,
- "restmap.conf",
- RestmapConf.build(
- self._schema.endpoints,
- self._schema.namespace,
- ),
- )
- self.output.put(
- self.output.default,
- "web.conf",
- WebConf.build(self._schema.endpoints),
- )
self.output.put(
self.output.bin,
"import_declare_test.py",
diff --git a/splunk_add_on_ucc_framework/commands/rest_builder/global_config_builder_schema.py b/splunk_add_on_ucc_framework/commands/rest_builder/global_config_builder_schema.py
index 74007c3a7..8551d9f5c 100644
--- a/splunk_add_on_ucc_framework/commands/rest_builder/global_config_builder_schema.py
+++ b/splunk_add_on_ucc_framework/commands/rest_builder/global_config_builder_schema.py
@@ -112,7 +112,7 @@ def _builder_configs(self) -> None:
conf_name=config.get("conf"),
)
endpoint.add_entity(entity)
- # If we have given oauth support then we have to add endpoint for accesstoken
+ # If we have given oauth support then we have to add endpoint for access_token
for entity_element in config["entity"]:
if entity_element["type"] == "oauth":
log_details = self.global_config.logging_tab
diff --git a/splunk_add_on_ucc_framework/dashboard.py b/splunk_add_on_ucc_framework/dashboard.py
index 52b5bf1d5..30f7460ce 100644
--- a/splunk_add_on_ucc_framework/dashboard.py
+++ b/splunk_add_on_ucc_framework/dashboard.py
@@ -62,7 +62,7 @@
"| join _time [search index=_internal source=*{addon_name}* action=events_ingested "
'| timechart sum(n_events) as \\"Number of events\\" ]'
)
-errors_count = "index=_internal source=*{addon_name}* log_level=ERROR | timechart count as Errors by exc_l"
+errors_count = "index=_internal source=*{addon_name}* log_level IN ({log_lvl}) | timechart count as Errors by exc_l"
events_count = (
"index=_internal source=*{addon_name}* action=events_ingested | "
'timechart sum(n_events) as \\"Number of events\\"'
@@ -131,7 +131,7 @@
'| rename event_input as \\"Input\\", events as \\"Number of events\\", sparkevent as \\"Event trendline\\"'
)
-errors_list_query = "index=_internal source=*{addon_name}* log_level=ERROR"
+errors_list_query = "index=_internal source=*{addon_name}* log_level IN ({log_lvl})"
resource_cpu_query = (
"index = _introspection component=PerProcess data.args=*{addon_name}* "
@@ -149,6 +149,7 @@ def generate_dashboard_content(
input_names: List[str],
definition_json_name: str,
lic_usg_search_params: Optional[Tuple[str, str]],
+ error_panel_log_lvl: str,
) -> str:
determine_by = lic_usg_search_params[0] if lic_usg_search_params else "s"
lic_usg_condition = (
@@ -169,7 +170,9 @@ def generate_dashboard_content(
addon_name=addon_name.lower(),
determine_by=determine_by,
),
- errors_count=errors_count.format(addon_name=addon_name.lower()),
+ errors_count=errors_count.format(
+ addon_name=addon_name.lower(), log_lvl=error_panel_log_lvl
+ ),
events_count=events_count.format(addon_name=addon_name.lower()),
)
)
@@ -182,7 +185,9 @@ def generate_dashboard_content(
data_ingestion=data_ingestion.format(
lic_usg_condition=lic_usg_condition, determine_by=determine_by
),
- errors_count=errors_count.format(addon_name=addon_name.lower()),
+ errors_count=errors_count.format(
+ addon_name=addon_name.lower(), log_lvl=error_panel_log_lvl
+ ),
events_count=events_count.format(addon_name=addon_name.lower()),
table_sourcetype=table_sourcetype_query.format(
lic_usg_condition=lic_usg_condition,
@@ -216,8 +221,12 @@ def generate_dashboard_content(
utils.get_j2_env()
.get_template(definition_json_name)
.render(
- errors_count=errors_count.format(addon_name=addon_name.lower()),
- errors_list=errors_list_query.format(addon_name=addon_name.lower()),
+ errors_count=errors_count.format(
+ addon_name=addon_name.lower(), log_lvl=error_panel_log_lvl
+ ),
+ errors_list=errors_list_query.format(
+ addon_name=addon_name.lower(), log_lvl=error_panel_log_lvl
+ ),
)
)
@@ -251,10 +260,16 @@ def generate_dashboard(
lic_usg_search_params = _get_license_usage_search_params(global_config.dashboard)
+ error_panel_log_lvl = _get_error_panel_log_lvl(global_config.dashboard)
+
if PANEL_DEFAULT in panel_names:
for definition_json_name in default_definition_json_filename.values():
content = generate_dashboard_content(
- addon_name, input_names, definition_json_name, lic_usg_search_params
+ addon_name,
+ input_names,
+ definition_json_name,
+ lic_usg_search_params,
+ error_panel_log_lvl,
)
with open(
os.path.join(definition_json_path, definition_json_name), "w"
@@ -303,6 +318,17 @@ def _get_license_usage_search_params(
return determine_by, lic_usg_condition
+def _get_error_panel_log_lvl(dashboard: Dict[Any, Any]) -> str:
+ try:
+ error_lvl = dashboard["settings"]["error_panel_log_lvl"]
+ except KeyError:
+ logger.info(
+ "No custom error log level found. Proceeding with default parameters."
+ )
+ return "ERROR"
+ return ", ".join(error_lvl)
+
+
def get_custom_json_content(custom_dashboard_path: str) -> Dict[Any, Any]:
custom_dashboard = load_custom_json(custom_dashboard_path)
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/__init__.py b/splunk_add_on_ucc_framework/generators/conf_files/__init__.py
new file mode 100644
index 000000000..07d3fd346
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/__init__.py
@@ -0,0 +1,40 @@
+#
+# Copyright 2024 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 .conf_generator import ConfGenerator
+from .create_alert_actions_conf import AlertActionsConf
+from .create_app_conf import AppConf
+from .create_eventtypes_conf import EventtypesConf
+from .create_inputs_conf import InputsConf
+from .create_restmap_conf import RestMapConf
+from .create_server_conf import ServerConf
+from .create_tags_conf import TagsConf
+from .create_web_conf import WebConf
+from .create_account_conf import AccountConf
+from .create_settings_conf import SettingsConf
+
+__all__ = [
+ "ConfGenerator",
+ "ServerConf",
+ "RestMapConf",
+ "WebConf",
+ "AlertActionsConf",
+ "EventtypesConf",
+ "TagsConf",
+ "AppConf",
+ "InputsConf",
+ "AccountConf",
+ "SettingsConf",
+]
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/conf_generator.py b/splunk_add_on_ucc_framework/generators/conf_files/conf_generator.py
new file mode 100644
index 000000000..8468cf94d
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/conf_generator.py
@@ -0,0 +1,47 @@
+#
+# Copyright 2024 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, Union, NoReturn
+from ..file_generator import FileGenerator
+
+
+class ConfGenerator(FileGenerator):
+ __description__ = "DESCRIBE THE CONF FILE THAT IS GENERATED"
+
+ def _set_attributes(self, **kwargs: Any) -> Union[NoReturn, None]:
+ # parse self._global_config and set the require attributes for self
+ raise NotImplementedError()
+
+ def generate(self) -> Dict[str, str]:
+ conf_files: Dict[str, str] = {}
+ conf_file = self.generate_conf()
+ conf_spec_file = self.generate_conf_spec()
+ if conf_file:
+ conf_files.update(conf_file)
+ if conf_spec_file:
+ conf_files.update(conf_spec_file)
+ return conf_files
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ # logic to pass the configs to template file
+ # uses the attributes set in _set_attributes method to render the template
+ # use self.get_file_output_path() to get the output file to create the file
+ return {"": ""}
+
+ def generate_conf_spec(self) -> Union[Dict[str, str], None]:
+ # logic to pass the configs to template file
+ # uses the attributes set in _set_attributes method to render the template
+ # use self.get_file_output_path() to get the output file to create the file
+ return {"": ""}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_account_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_account_conf.py
new file mode 100644
index 000000000..05837ffd8
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_account_conf.py
@@ -0,0 +1,59 @@
+#
+# Copyright 2024 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, Tuple, List, Dict, Union
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class AccountConf(ConfGenerator):
+ __description__ = (
+ "Generates `_account.conf.spec` "
+ "file for the configuration mentioned in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.account_fields: List[Tuple[str, List[str]]] = []
+ if self._global_config and self._gc_schema:
+ self.conf_spec_file = (
+ self._global_config.namespace.lower() + "_account.conf.spec"
+ )
+ for account in self._global_config.configs:
+ # If the endpoint is oauth, which is for getting access_token, conf file entries
+ # should not get created (compatibility to previous versions)
+ if account["name"] == "oauth":
+ continue
+ content = self._gc_schema._get_oauth_enitities(account["entity"])
+ fields = self._gc_schema._parse_fields(content)
+ self.account_fields.append(
+ ("", [f"{f._name} = " for f in fields])
+ )
+
+ def generate_conf_spec(self) -> Union[Dict[str, str], None]:
+ if not self.account_fields:
+ return None
+
+ file_path = self.get_file_output_path(["README", self.conf_spec_file])
+ self.set_template_and_render(
+ template_file_path=["README"], file_name="account_conf_spec.template"
+ )
+
+ rendered_content = self._template.render(account_stanzas=self.account_fields)
+ self.writer(
+ file_name=self.conf_spec_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_spec_file: file_path}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_alert_actions_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_alert_actions_conf.py
new file mode 100644
index 000000000..de448ab95
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_alert_actions_conf.py
@@ -0,0 +1,161 @@
+#
+# Copyright 2024 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.
+#
+import json
+import shutil
+from os import path
+from typing import Any, Dict, Union
+
+from splunk_add_on_ucc_framework.commands.modular_alert_builder import normalize
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class AlertActionsConf(ConfGenerator):
+ __description__ = (
+ "Generates `alert_actions.conf` and `alert_actions.conf.spec` file "
+ "for the custom alert actions defined in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "alert_actions.conf"
+ self.conf_spec_file = f"{self.conf_file}.spec"
+ if self._global_config is None:
+ return
+
+ envs = normalize.normalize(
+ self._global_config.alerts,
+ self._global_config.namespace,
+ )
+ schema_content = envs["schema.content"]
+ self._alert_settings = schema_content["modular_alerts"]
+
+ deny_list = frozenset(
+ [
+ "short_name",
+ "alert_props",
+ "parameters",
+ "uuid",
+ "code",
+ "largeIcon",
+ "smallIcon",
+ "index",
+ "iconFileName", # it is a config from globalConfig that gets written to icon_path
+ "customScript", # it is a config from globalConfig only for Python script
+ ]
+ )
+ _router = {
+ "dropdownlist": "list",
+ "text": "string",
+ "textarea": "string",
+ "checkbox": "bool",
+ "password": "password",
+ "dropdownlist_splunk_search": "list",
+ "radio": "list",
+ }
+
+ self.alerts: Dict[str, Any] = {}
+ self.alerts_spec: Dict[str, Any] = {}
+
+ for alert in self._alert_settings:
+ alert_name = alert["short_name"]
+ self.alerts[alert_name] = []
+ self.alerts_spec[alert_name] = []
+ # process the 'iconFileName' property for alert actions
+ if alert.get("iconFileName", "alerticon.png") != "alerticon.png":
+ self.alerts[alert_name].append(f"icon_path = {alert['iconFileName']}")
+ else:
+ self.alerts[alert_name].append("icon_path = alerticon.png")
+ # we copy UCC framework's alerticon.png only when a custom isn't provided
+ shutil.copy(
+ path.join(kwargs["ucc_dir"], "static", "alerticon.png"),
+ path.join(self._get_output_dir(), "appserver", "static"),
+ )
+ # process alert action properties in bulk
+ for k, v in alert.items():
+ if k == "adaptive_response":
+ new_cam = {
+ sub_k: sub_v
+ for sub_k, sub_v in list(v.items())
+ if sub_k != "sourcetype" and sub_v
+ }
+ value = f"param._cam = {json.dumps(new_cam)}"
+ self.alerts[alert_name].append(value)
+ self.alerts_spec[alert_name].append(
+ "param._cam = Adaptive Response parameters."
+ )
+ elif k == "parameters":
+ for param in v:
+ param_name = param["name"].strip()
+ if param.get("default_value") is not None:
+ self.alerts[alert_name].append(
+ f"param.{param_name} = {str(param['default_value']).strip()}"
+ )
+ else:
+ self.alerts[alert_name].append(f"param.{param_name} = ")
+
+ # fetching details for alert_actions.conf.spec file
+ format_type = _router[param["format_type"]]
+ is_required = (
+ "It's a required parameter."
+ if param.get("required") and param["required"]
+ else ""
+ )
+ param_default_value = param.get("default_value")
+ default_value = (
+ f"It's default value is {param_default_value}."
+ if param_default_value
+ else ""
+ )
+ value = (
+ f'param.{param["name"]} = <{format_type}> '
+ f'{param["label"]}. {is_required} {default_value}'
+ )
+ self.alerts_spec[alert_name].append(value)
+ elif k not in deny_list:
+ value = f"{str(k).strip()} = {str(v).strip()}"
+ self.alerts[alert_name].append(value)
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.alerts:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="alert_actions_conf.template"
+ )
+ rendered_content = self._template.render(alerts=self.alerts)
+ self.writer(
+ file_name=self.conf_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_file: file_path}
+
+ def generate_conf_spec(self) -> Union[Dict[str, str], None]:
+ if not self.alerts_spec:
+ return None
+
+ file_path = self.get_file_output_path(["README", self.conf_spec_file])
+ self.set_template_and_render(
+ template_file_path=["README"],
+ file_name="alert_actions_conf_spec.template",
+ )
+ rendered_content = self._template.render(alerts=self.alerts_spec)
+ self.writer(
+ file_name=self.conf_spec_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_spec_file: file_path}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_app_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_app_conf.py
new file mode 100644
index 000000000..00aa02fec
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_app_conf.py
@@ -0,0 +1,77 @@
+#
+# Copyright 2024 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 time import time
+from typing import Any, Dict
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class AppConf(ConfGenerator):
+ __description__ = (
+ "Generates `app.conf` with the details mentioned in globalConfig[meta]"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "app.conf"
+ self.check_for_updates = "true"
+ self.custom_conf = []
+ self.name = self._addon_name
+ self.id = self._addon_name
+ self.supported_themes = ""
+
+ if self._global_config and self._gc_schema:
+ self.custom_conf.extend(list(self._gc_schema.settings_conf_file_names))
+ self.custom_conf.extend(list(self._gc_schema.configs_conf_file_names))
+ self.custom_conf.extend(list(self._gc_schema.oauth_conf_file_names))
+
+ if self._global_config.meta.get("checkForUpdates") is False:
+ self.check_for_updates = "false"
+ if self._global_config.meta.get("supportedThemes") is not None:
+ self.supported_themes = ", ".join(
+ self._global_config.meta["supportedThemes"]
+ )
+
+ self.addon_version = kwargs["addon_version"]
+ self.is_visible = str(kwargs["has_ui"]).lower()
+ self.description = kwargs["app_manifest"].get_description()
+ self.author = kwargs["app_manifest"].get_authors()[0]["name"]
+ self.build = str(int(time()))
+
+ def generate_conf(self) -> Dict[str, str]:
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="app_conf.template"
+ )
+ rendered_content = self._template.render(
+ custom_conf=self.custom_conf,
+ addon_version=self.addon_version,
+ check_for_updates=self.check_for_updates,
+ supported_themes=self.supported_themes,
+ description=self.description,
+ author=self.author,
+ name=self.name,
+ build=self.build,
+ id=self.id,
+ label=self.description,
+ is_visible=self.is_visible,
+ )
+ self.writer(
+ file_name=self.conf_file,
+ file_path=file_path,
+ content=rendered_content,
+ merge_mode="item_overwrite",
+ )
+ return {self.conf_file: file_path}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_eventtypes_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_eventtypes_conf.py
new file mode 100644
index 000000000..517468e04
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_eventtypes_conf.py
@@ -0,0 +1,53 @@
+#
+# Copyright 2024 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, List, Union
+
+from splunk_add_on_ucc_framework.commands.modular_alert_builder import normalize
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class EventtypesConf(ConfGenerator):
+ __description__ = (
+ "Generates `eventtypes.conf` file if the sourcetype is mentioned"
+ " in Adaptive Response of custom alert action in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "eventtypes.conf"
+ self.alert_settings: Dict[str, List[Dict[str, Any]]] = {}
+ if self._global_config:
+ envs = normalize.normalize(
+ self._global_config.alerts,
+ self._global_config.namespace,
+ )
+ schema_content = envs["schema.content"]
+ self.alert_settings = schema_content["modular_alerts"]
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.alert_settings:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="eventtypes_conf.template"
+ )
+ rendered_content = self._template.render(mod_alerts=self.alert_settings)
+ 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/conf_files/create_inputs_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_inputs_conf.py
new file mode 100644
index 000000000..e5ec4f47e
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_inputs_conf.py
@@ -0,0 +1,90 @@
+#
+# Copyright 2024 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, List, Union
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class InputsConf(ConfGenerator):
+ __description__ = (
+ "Generates `inputs.conf` and `inputs.conf.spec` "
+ "file for the services mentioned in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "inputs.conf"
+ self.conf_spec_file = f"{self.conf_file}.spec"
+ self.input_names: List[Dict[str, List[str]]] = []
+ if self._global_config:
+ for service in self._global_config.inputs:
+ properties = []
+ if service.get("conf") is not None:
+ # Add data input of self defined conf to inputs.conf.spec
+ self.input_names.append(
+ {service["name"]: ["placeholder = placeholder"]}
+ )
+ continue
+ for entity in service.get("entity", {"field": "name"}):
+ # TODO: add the details and updates on what to skip and process
+ if entity["field"] == "name":
+ continue
+ nl = "\n" # hack for `f-string expression part cannot include a backslash`
+ # TODO: enhance the message formation for inputs.conf.spec file
+ properties.append(
+ f"{entity['field']} = {entity.get('help', '').replace(nl, ' ')} "
+ f"{'' if entity.get('defaultValue') is None else ' Default: ' + str(entity['defaultValue'])}"
+ )
+
+ self.input_names.append({service["name"]: properties})
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.input_names:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ stanzas: List[str] = []
+ for k in self.input_names:
+ stanzas.extend(k.keys())
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="inputs_conf.template"
+ )
+
+ rendered_content = self._template.render(input_names=stanzas)
+ self.writer(
+ file_name=self.conf_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_file: file_path}
+
+ def generate_conf_spec(self) -> Union[Dict[str, str], None]:
+ if not self.input_names:
+ return None
+
+ file_path = self.get_file_output_path(["README", self.conf_spec_file])
+ self.set_template_and_render(
+ template_file_path=["README"], file_name="inputs_conf_spec.template"
+ )
+
+ rendered_content = self._template.render(
+ input_stanzas=self.input_names,
+ )
+ self.writer(
+ file_name=self.conf_spec_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_spec_file: file_path}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_restmap_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_restmap_conf.py
new file mode 100644
index 000000000..6e0b5ca39
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_restmap_conf.py
@@ -0,0 +1,52 @@
+#
+# Copyright 2024 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, Union
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class RestMapConf(ConfGenerator):
+ __description__ = (
+ "Generates `restmap.conf` for the custom REST handlers that "
+ "are generated based on configs from globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "restmap.conf"
+ if self._gc_schema:
+ self.endpoints = self._gc_schema.endpoints
+ self.endpoint_names = ", ".join(sorted([ep.name for ep in self.endpoints]))
+ self.namespace = self._gc_schema.namespace
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self._gc_schema:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="restmap_conf.template"
+ )
+ rendered_content = self._template.render(
+ endpoints=self.endpoints,
+ endpoint_names=self.endpoint_names,
+ namespace=self.namespace,
+ )
+ 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/conf_files/create_server_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_server_conf.py
new file mode 100644
index 000000000..e49a3c100
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_server_conf.py
@@ -0,0 +1,53 @@
+#
+# Copyright 2024 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 os.path import isfile, join
+from typing import Any, Dict, Union
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class ServerConf(ConfGenerator):
+ __description__ = (
+ "Generates `server.conf` for the custom conf "
+ "files created as per configurations in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "server.conf"
+ self.custom_conf = []
+ if self._gc_schema:
+ self.custom_conf.extend(list(self._gc_schema.settings_conf_file_names))
+ self.custom_conf.extend(list(self._gc_schema.configs_conf_file_names))
+ self.custom_conf.extend(list(self._gc_schema.oauth_conf_file_names))
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.custom_conf:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ # For now, only create server.conf only if
+ # no server.conf is present in the source package.
+ if isfile(join(self._input_dir, "default", self.conf_file)):
+ return {"": ""}
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="server_conf.template"
+ )
+ rendered_content = self._template.render(custom_conf=self.custom_conf)
+ 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/conf_files/create_settings_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_settings_conf.py
new file mode 100644
index 000000000..f1c13bb3b
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_settings_conf.py
@@ -0,0 +1,77 @@
+#
+# Copyright 2024 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, Tuple, List, Dict, Union
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class SettingsConf(ConfGenerator):
+ __description__ = (
+ "Generates `_settings.conf.spec` "
+ "file for the Proxy, Logging or Custom Tab mentioned in globalConfig"
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.settings_stanzas: List[Tuple[str, List[str]]] = []
+ self.default_content: str = ""
+
+ if self._global_config and self._gc_schema:
+ self.conf_file = self._global_config.namespace.lower() + "_settings.conf"
+ self.conf_spec_file = f"{self.conf_file}.spec"
+ for setting in self._global_config.settings:
+ content = self._gc_schema._get_oauth_enitities(setting["entity"])
+ fields = self._gc_schema._parse_fields(content)
+ self.settings_stanzas.append(
+ (setting["name"], [f"{f._name} = " for f in fields])
+ )
+ if self._gc_schema._endpoints.get("settings") is not None:
+ self.default_content = self._gc_schema._endpoints[
+ "settings"
+ ].generate_conf_with_default_values()
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.default_content:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="settings_conf.template"
+ )
+
+ rendered_content = self._template.render(default_content=self.default_content)
+ self.writer(
+ file_name=self.conf_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_file: file_path}
+
+ def generate_conf_spec(self) -> Union[Dict[str, str], None]:
+ if not self.settings_stanzas:
+ return None
+
+ file_path = self.get_file_output_path(["README", self.conf_spec_file])
+ self.set_template_and_render(
+ template_file_path=["README"], file_name="settings_conf_spec.template"
+ )
+
+ rendered_content = self._template.render(settings_stanzas=self.settings_stanzas)
+ self.writer(
+ file_name=self.conf_spec_file,
+ file_path=file_path,
+ content=rendered_content,
+ )
+ return {self.conf_spec_file: file_path}
diff --git a/splunk_add_on_ucc_framework/generators/conf_files/create_tags_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_tags_conf.py
new file mode 100644
index 000000000..7306c864b
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_tags_conf.py
@@ -0,0 +1,52 @@
+#
+# Copyright 2024 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, List, Union
+from splunk_add_on_ucc_framework.commands.modular_alert_builder import normalize
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class TagsConf(ConfGenerator):
+ __description__ = (
+ "Generates `tags.conf` file based on the "
+ "`eventtypes.conf` created for custom alert actions."
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "tags.conf"
+ self.alert_settings: Dict[str, List[Dict[str, Any]]] = {}
+ if self._global_config:
+ envs = normalize.normalize(
+ self._global_config.alerts,
+ self._global_config.namespace,
+ )
+ schema_content = envs["schema.content"]
+ self.alert_settings = schema_content["modular_alerts"]
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self.alert_settings:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="tags_conf.template"
+ )
+ rendered_content = self._template.render(mod_alerts=self.alert_settings)
+ 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/conf_files/create_web_conf.py b/splunk_add_on_ucc_framework/generators/conf_files/create_web_conf.py
new file mode 100644
index 000000000..477d0803c
--- /dev/null
+++ b/splunk_add_on_ucc_framework/generators/conf_files/create_web_conf.py
@@ -0,0 +1,48 @@
+#
+# Copyright 2024 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, Union
+
+from splunk_add_on_ucc_framework.generators.conf_files import ConfGenerator
+
+
+class WebConf(ConfGenerator):
+ __description__ = (
+ "Generates `web.conf` to expose the endpoints generated in "
+ "`restmap.conf` which is generated based on configurations from globalConfig."
+ )
+
+ def _set_attributes(self, **kwargs: Any) -> None:
+ self.conf_file = "web.conf"
+ if self._gc_schema:
+ self.endpoints = self._gc_schema.endpoints
+
+ def generate_conf(self) -> Union[Dict[str, str], None]:
+ if not self._gc_schema:
+ return None
+
+ file_path = self.get_file_output_path(["default", self.conf_file])
+ self.set_template_and_render(
+ template_file_path=["conf_files"], file_name="web_conf.template"
+ )
+ rendered_content = self._template.render(
+ endpoints=self.endpoints,
+ )
+ 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 eac29f61b..b843b915c 100644
--- a/splunk_add_on_ucc_framework/generators/file_const.py
+++ b/splunk_add_on_ucc_framework/generators/file_const.py
@@ -15,6 +15,7 @@
#
from typing import List, NamedTuple, Type, Union
from .file_generator import FileGenerator
+
from splunk_add_on_ucc_framework.generators.xml_files import (
ConfigurationXml,
DashboardXml,
@@ -23,6 +24,18 @@
RedirectXml,
)
from splunk_add_on_ucc_framework.generators.html_files import AlertActionsHtml
+from splunk_add_on_ucc_framework.generators.conf_files import (
+ AlertActionsConf,
+ AppConf,
+ EventtypesConf,
+ InputsConf,
+ RestMapConf,
+ ServerConf,
+ TagsConf,
+ WebConf,
+ AccountConf,
+ SettingsConf,
+)
__all__ = ["FileClass", "GEN_FILE_LIST"]
@@ -35,6 +48,23 @@ class FileClass(NamedTuple):
GEN_FILE_LIST: List[FileClass] = [
+ FileClass("app.conf", AppConf, "default", AppConf.__description__),
+ FileClass("inputs.conf", InputsConf, "default", InputsConf.__description__),
+ FileClass("server.conf", ServerConf, "default", ServerConf.__description__),
+ FileClass("restmap.conf", RestMapConf, "default", RestMapConf.__description__),
+ FileClass("web.conf", WebConf, "default", WebConf.__description__),
+ FileClass(
+ "alert_actions.conf",
+ AlertActionsConf,
+ "default",
+ AlertActionsConf.__description__,
+ ),
+ FileClass(
+ "eventtypes.conf", EventtypesConf, "default", EventtypesConf.__description__
+ ),
+ FileClass("tags.conf", TagsConf, "default", TagsConf.__description__),
+ FileClass("_account.conf", AccountConf, "README", AccountConf.__description__),
+ FileClass("_settings.conf", SettingsConf, "README", SettingsConf.__description__),
FileClass(
"configuration.xml",
ConfigurationXml,
diff --git a/splunk_add_on_ucc_framework/rest_map_conf.py b/splunk_add_on_ucc_framework/rest_map_conf.py
deleted file mode 100644
index 00f050421..000000000
--- a/splunk_add_on_ucc_framework/rest_map_conf.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Copyright 2024 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 Sequence
-
-from splunk_add_on_ucc_framework.commands.rest_builder.endpoint.base import (
- RestEndpointBuilder,
-)
-
-
-class RestmapConf:
- _admin_template = """
-[admin:{namespace}]
-match = /
-members = {endpoints}
-"""
-
- _external_template = """
-[admin_external:{name}]
-handlertype = python
-python.version = python3
-handlerfile = {rh_name}.py
-handleractions = {actions}
-handlerpersistentmode = true
-"""
-
- @classmethod
- def build(cls, endpoints: Sequence[RestEndpointBuilder], namespace: str) -> str:
- externals = [
- cls._admin_template.format(
- namespace=namespace,
- endpoints=", ".join([ep.name for ep in endpoints]),
- )
- ]
- for endpoint in endpoints:
- external = cls._external_template.format(
- name=endpoint.name,
- rh_name=endpoint.rh_name,
- actions=", ".join(endpoint.actions()),
- )
- externals.append(external)
- return "".join(externals)
diff --git a/splunk_add_on_ucc_framework/schema/schema.json b/splunk_add_on_ucc_framework/schema/schema.json
index b9b71e8c2..e7048ce9e 100644
--- a/splunk_add_on_ucc_framework/schema/schema.json
+++ b/splunk_add_on_ucc_framework/schema/schema.json
@@ -19,6 +19,7 @@
"type": "string",
"enum": [
"text",
+ "textarea",
"checkbox",
"singleSelect",
"radio",
@@ -80,7 +81,11 @@
}
}
},
- "required": ["field", "label", "type"],
+ "required": [
+ "field",
+ "label",
+ "type"
+ ],
"additionalProperties": false
},
"Alerts": {
@@ -165,7 +170,12 @@
"description": "The sourcetype in which the result of the AR alert action would be written to. The value is updated in the alert action script. If you don’t specify any value you can update your alert action script manually once it is generated."
}
},
- "required": ["task", "subject", "category", "technology"]
+ "required": [
+ "task",
+ "subject",
+ "category",
+ "technology"
+ ]
},
"entity": {
"type": "array",
@@ -198,7 +208,11 @@
"description": "A Python script that would have validation and logic for alert action execution. The script should be present at /bin/. Refer documentation for more information."
}
},
- "required": ["name", "label", "description"],
+ "required": [
+ "name",
+ "label",
+ "description"
+ ],
"additionalProperties": false
},
"ConfigurationPage": {
@@ -233,7 +247,10 @@
"minItems": 1
}
},
- "required": ["title", "tabs"],
+ "required": [
+ "title",
+ "tabs"
+ ],
"description": "Page for account configuration, proxy configuration, and logging level configuration.",
"additionalProperties": false
},
@@ -260,7 +277,10 @@
"description": "Used to map field values to more meaningful values."
}
},
- "required": ["field", "label"]
+ "required": [
+ "field",
+ "label"
+ ]
},
"description": "Specifies the list of fields that will be displayed in row form when the user clicks the Row Expand button."
},
@@ -289,7 +309,10 @@
"description": "Used to modify the default cell values."
}
},
- "required": ["field", "label"]
+ "required": [
+ "field",
+ "label"
+ ]
}
},
"customRow": {
@@ -301,11 +324,18 @@
"description": "Specifies what type of action can be performed on the stanza. The supported types are edit, clone, and delete.",
"items": {
"type": "string",
- "enum": ["edit", "delete", "clone"]
+ "enum": [
+ "edit",
+ "delete",
+ "clone"
+ ]
}
}
},
- "required": ["header", "actions"],
+ "required": [
+ "header",
+ "actions"
+ ],
"description": "Configuration of single tab",
"additionalProperties": false
},
@@ -360,12 +390,29 @@
"search_condition"
],
"additionalProperties": false
+ },
+ "error_panel_log_lvl": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ "enum": [
+ "ERROR", "CRITICAL"
+ ]
+ }
+ ]
+ },
+ "uniqueItems": true
}
},
"additionalProperties": false
}
},
- "required": ["panels"],
+ "required": [
+ "panels"
+ ],
"description": "The dashboard page provides some additional information about the add-on operations to increase the visibility into what the add-on is actually doing under the hood.",
"additionalProperties": false
},
@@ -382,7 +429,9 @@
"const": "date"
}
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"additionalProperties": false
},
"EmailValidator": {
@@ -397,7 +446,9 @@
"description": "Exactly: email"
}
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"additionalProperties": false
},
"SaveValidatorHook": {
@@ -447,7 +498,10 @@
}
}
},
- "required": ["label", "fields"],
+ "required": [
+ "label",
+ "fields"
+ ],
"additionalProperties": false
}
},
@@ -480,7 +534,10 @@
"description": "It is a link where the user will be redirected."
}
},
- "required": ["text", "link"],
+ "required": [
+ "text",
+ "link"
+ ],
"additionalProperties": false
},
"help": {
@@ -490,7 +547,11 @@
"$ref": "#/definitions/tooltip"
}
},
- "required": ["field", "type", "options"],
+ "required": [
+ "field",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"TextEntity": {
@@ -583,7 +644,11 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type"],
+ "required": [
+ "field",
+ "label",
+ "type"
+ ],
"additionalProperties": false
},
"TextareaEntity": {
@@ -674,7 +739,11 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type"],
+ "required": [
+ "field",
+ "label",
+ "type"
+ ],
"additionalProperties": false
},
"SingleSelectEntity": {
@@ -814,7 +883,12 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"MultipleSelectEntity": {
@@ -952,7 +1026,12 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"CheckboxEntity": {
@@ -1014,7 +1093,11 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type"],
+ "required": [
+ "field",
+ "label",
+ "type"
+ ],
"additionalProperties": false
},
"CheckboxGroupEntity": {
@@ -1069,7 +1152,10 @@
"uniqueItems": true
}
},
- "required": ["label", "fields"],
+ "required": [
+ "label",
+ "fields"
+ ],
"additionalProperties": false
}
},
@@ -1121,7 +1207,9 @@
"additionalProperties": false
}
},
- "required": ["field"],
+ "required": [
+ "field"
+ ],
"additionalProperties": false
},
"minItems": 1
@@ -1130,7 +1218,9 @@
"$ref": "#/definitions/disableonEdit"
}
},
- "required": ["rows"],
+ "required": [
+ "rows"
+ ],
"additionalProperties": false
},
"validators": {
@@ -1147,7 +1237,12 @@
"type": "boolean"
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"RadioEntity": {
@@ -1203,14 +1298,21 @@
}
}
},
- "required": ["items"],
+ "required": [
+ "items"
+ ],
"additionalProperties": false
},
"modifyFieldsOnValue": {
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"FileEntity": {
@@ -1300,7 +1402,11 @@
"$ref": "#/definitions/modifyFieldsOnValue"
}
},
- "required": ["field", "label", "type"],
+ "required": [
+ "field",
+ "label",
+ "type"
+ ],
"additionalProperties": false
},
"OAuthEntity": {
@@ -1435,7 +1541,9 @@
}
},
"then": {
- "required": ["basic"]
+ "required": [
+ "basic"
+ ]
}
},
{
@@ -1452,15 +1560,24 @@
}
},
"then": {
- "required": ["oauth"]
+ "required": [
+ "oauth"
+ ]
}
}
],
"additionalProperties": false,
- "required": ["auth_type"]
+ "required": [
+ "auth_type"
+ ]
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"CustomEntity": {
@@ -1518,7 +1635,12 @@
"additionalProperties": false
}
},
- "required": ["field", "label", "type", "options"],
+ "required": [
+ "field",
+ "label",
+ "type",
+ "options"
+ ],
"additionalProperties": false
},
"AnyOfEntity": {
@@ -1586,7 +1708,10 @@
}
}
},
- "required": ["label", "children"]
+ "required": [
+ "label",
+ "children"
+ ]
}
},
{
@@ -1633,7 +1758,10 @@
"description": "Used to map field values to more meaningful values."
}
},
- "required": ["field", "label"]
+ "required": [
+ "field",
+ "label"
+ ]
}
},
"header": {
@@ -1661,7 +1789,10 @@
"description": "Used to modify the default cell values."
}
},
- "required": ["field", "label"]
+ "required": [
+ "field",
+ "label"
+ ]
}
},
"customRow": {
@@ -1673,11 +1804,20 @@
"description": "Specifies what type of action can be performed on the stanza. The supported types are edit, clone, and delete.",
"items": {
"type": "string",
- "enum": ["edit", "delete", "clone", "enable", "search"]
+ "enum": [
+ "edit",
+ "delete",
+ "clone",
+ "enable",
+ "search"
+ ]
}
}
},
- "required": ["header", "actions"],
+ "required": [
+ "header",
+ "actions"
+ ],
"description": "Displays input stanzas in a tabular format.",
"additionalProperties": false
},
@@ -1693,7 +1833,9 @@
"description": "Exactly: ipv4"
}
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"additionalProperties": false
},
"Meta": {
@@ -1846,11 +1988,19 @@
"type": "array",
"items": {
"type": "string",
- "enum": ["light", "dark"]
+ "enum": [
+ "light",
+ "dark"
+ ]
}
}
},
- "required": ["displayName", "name", "restRoot", "version"],
+ "required": [
+ "displayName",
+ "name",
+ "restRoot",
+ "version"
+ ],
"description": "Metadata regarding build",
"additionalProperties": false
},
@@ -1873,7 +2023,10 @@
"description": "Specifies if the number should be an integer (true) or can be a float (false). Default value is false"
}
},
- "required": ["type", "range"],
+ "required": [
+ "type",
+ "range"
+ ],
"additionalProperties": false
},
"RangeValue": {
@@ -1945,7 +2098,9 @@
}
},
"then": {
- "required": ["basic"]
+ "required": [
+ "basic"
+ ]
}
},
"OauthRequirement": {
@@ -1961,7 +2116,9 @@
}
},
"then": {
- "required": ["oauth"]
+ "required": [
+ "oauth"
+ ]
}
},
"Pages": {
@@ -1978,7 +2135,9 @@
"$ref": "#/definitions/DashboardPage"
}
},
- "required": ["configuration"],
+ "required": [
+ "configuration"
+ ],
"additionalProperties": false
},
"RegexValidator": {
@@ -1997,7 +2156,10 @@
"description": "Regex pattern."
}
},
- "required": ["type", "pattern"],
+ "required": [
+ "type",
+ "pattern"
+ ],
"additionalProperties": false
},
"StringValidator": {
@@ -2022,7 +2184,11 @@
"description": "Specifies the maximum number of characters allowed."
}
},
- "required": ["type", "minLength", "maxLength"],
+ "required": [
+ "type",
+ "minLength",
+ "maxLength"
+ ],
"additionalProperties": false
},
"TabContent": {
@@ -2052,7 +2218,10 @@
"style": {
"description": "By specifying this property in the global configuration file, the forms can either be opened as a new page or in a dialog.",
"type": "string",
- "enum": ["page", "dialog"]
+ "enum": [
+ "page",
+ "dialog"
+ ]
},
"conf": {
"type": "string",
@@ -2088,10 +2257,18 @@
},
"anyOf": [
{
- "required": ["entity", "name", "title"]
+ "required": [
+ "entity",
+ "name",
+ "title"
+ ]
},
{
- "required": ["customTab", "name", "title"]
+ "required": [
+ "customTab",
+ "name",
+ "title"
+ ]
}
],
"additionalProperties": false
@@ -2117,7 +2294,11 @@
"maxLength": 100
}
},
- "required": ["version", "product", "vendor"],
+ "required": [
+ "version",
+ "product",
+ "vendor"
+ ],
"additionalProperties": false
},
"UrlValidator": {
@@ -2132,7 +2313,9 @@
"description": "Exactly: url"
}
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"additionalProperties": false
},
"ValueLabelPair": {
@@ -2159,7 +2342,9 @@
"description": "Text displayed next to entity field"
}
},
- "required": ["label"],
+ "required": [
+ "label"
+ ],
"additionalProperties": false
},
"DashboardPanel": {
@@ -2171,7 +2356,9 @@
"maxLength": 100
}
},
- "required": ["name"],
+ "required": [
+ "name"
+ ],
"additionalProperties": false
},
"WarningMessage": {
@@ -2207,7 +2394,9 @@
"description": "Defines if message should disappear after any input changes."
}
},
- "required": ["message"]
+ "required": [
+ "message"
+ ]
},
"Field": {
"type": "string",
@@ -2245,9 +2434,15 @@
"type": "string"
}
},
- "required": ["slug", "linkText", "link"]
+ "required": [
+ "slug",
+ "linkText",
+ "link"
+ ]
},
- "required": ["text"]
+ "required": [
+ "text"
+ ]
}
}
},
@@ -2297,7 +2492,9 @@
"$ref": "#/definitions/markdownMessage"
}
},
- "required": ["fieldId"]
+ "required": [
+ "fieldId"
+ ]
},
"fieldToModify": {
"type": "object",
@@ -2319,7 +2516,12 @@
"mode": {
"type": "string",
"description": "Defines under which Mode modifications should occure. If not present modifications occure for all Mods",
- "enum": ["create", "edit", "config", "clone"]
+ "enum": [
+ "create",
+ "edit",
+ "config",
+ "clone"
+ ]
},
"fieldsToModify": {
"type": "array",
@@ -2330,7 +2532,10 @@
"minItems": 1
}
},
- "required": ["fieldValue", "fieldsToModify"]
+ "required": [
+ "fieldValue",
+ "fieldsToModify"
+ ]
},
"modifyFieldsOnValue": {
"type": "array",
@@ -2356,7 +2561,10 @@
"description": "Specify color of displayied text. Accepts all CSS colors"
}
},
- "required": ["markdownType", "text"]
+ "required": [
+ "markdownType",
+ "text"
+ ]
},
"markdownMessageHybrid": {
"type": "object",
@@ -2383,7 +2591,13 @@
"description": "Declare url that will use for redirection"
}
},
- "required": ["markdownType", "text", "token", "link", "linkText"]
+ "required": [
+ "markdownType",
+ "text",
+ "token",
+ "link",
+ "linkText"
+ ]
},
"markdownMessageLink": {
"type": "object",
@@ -2402,7 +2616,11 @@
"description": "Declare url that will use for redirection"
}
},
- "required": ["markdownType", "text", "link"]
+ "required": [
+ "markdownType",
+ "text",
+ "link"
+ ]
},
"markdownMessagePlainText": {
"type": "object",
@@ -2412,7 +2630,9 @@
"description": "Declare message content"
}
},
- "required": ["text"]
+ "required": [
+ "text"
+ ]
},
"markdownMessage": {
"description": "Message that will be displayed as additional information",
@@ -2462,7 +2682,13 @@
},
"levels": {
"type": "array",
- "default": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
+ "default": [
+ "DEBUG",
+ "INFO",
+ "WARNING",
+ "ERROR",
+ "CRITICAL"
+ ],
"description": "List of all possible log levels.",
"items": {
"type": "string"
@@ -2477,7 +2703,9 @@
"$ref": "#/definitions/help"
}
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"additionalProperties": false
},
"InputPageSingleTable": {
@@ -2531,7 +2759,10 @@
}
}
},
- "required": ["groupTitle", "groupName"]
+ "required": [
+ "groupTitle",
+ "groupName"
+ ]
}
},
"services": {
@@ -2568,7 +2799,10 @@
"style": {
"description": "By specifying this property in the global configuration file, the forms can either be opened as a new page or in a dialog.",
"type": "string",
- "enum": ["page", "dialog"]
+ "enum": [
+ "page",
+ "dialog"
+ ]
},
"hook": {
"description": "It is used to add custom behaviour to forms.",
@@ -2604,9 +2838,15 @@
}
},
"not": {
- "required": ["table"]
+ "required": [
+ "table"
+ ]
},
- "required": ["name", "title", "entity"]
+ "required": [
+ "name",
+ "title",
+ "entity"
+ ]
}
},
"hideFieldId": {
@@ -2616,7 +2856,11 @@
"type": "string"
}
},
- "required": ["title", "table", "services"],
+ "required": [
+ "title",
+ "table",
+ "services"
+ ],
"description": "Input Page definition where there is only one single tabel listing all services.",
"additionalProperties": false
},
@@ -2669,7 +2913,10 @@
"style": {
"description": "By specifying this property in the global configuration file, the forms can either be opened as a new page or in a dialog.",
"type": "string",
- "enum": ["page", "dialog"]
+ "enum": [
+ "page",
+ "dialog"
+ ]
},
"hook": {
"description": "It is used to add custom behaviour to forms.",
@@ -2696,7 +2943,12 @@
"maxLength": 100
}
},
- "required": ["name", "title", "table", "entity"]
+ "required": [
+ "name",
+ "title",
+ "table",
+ "entity"
+ ]
}
},
"hideFieldId": {
@@ -2706,7 +2958,10 @@
"type": "string"
}
},
- "required": ["title", "services"],
+ "required": [
+ "title",
+ "services"
+ ],
"description": "Input Page definition where there are tabs for each service with separate table listing input service entries.",
"additionalProperties": false
},
@@ -2781,7 +3036,9 @@
}
},
"additionalProperties": false,
- "required": ["range"]
+ "required": [
+ "range"
+ ]
},
"help": {
"$ref": "#/definitions/help"
@@ -2793,7 +3050,11 @@
"$ref": "#/definitions/required"
}
},
- "required": ["type", "field", "label"],
+ "required": [
+ "type",
+ "field",
+ "label"
+ ],
"additionalProperties": false
},
"IntervalValue": {
@@ -2834,7 +3095,11 @@
"$ref": "#/definitions/required"
}
},
- "required": ["type", "field", "label"],
+ "required": [
+ "type",
+ "field",
+ "label"
+ ],
"additionalProperties": false
}
},
@@ -2855,6 +3120,9 @@
"minItems": 1
}
},
- "required": ["meta", "pages"],
+ "required": [
+ "meta",
+ "pages"
+ ],
"additionalProperties": false
}
diff --git a/splunk_add_on_ucc_framework/server_conf.py b/splunk_add_on_ucc_framework/server_conf.py
deleted file mode 100644
index bf4c95477..000000000
--- a/splunk_add_on_ucc_framework/server_conf.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright 2024 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 Sequence
-
-import addonfactory_splunk_conf_parser_lib as conf_parser
-
-SERVER_CONF_FILE_NAME = "server.conf"
-
-
-class ServerConf:
- def __init__(self) -> None:
- self._server_conf = conf_parser.TABConfigParser()
-
- def create_default(self, conf_file_names: Sequence[str]) -> None:
- self._server_conf.add_section("shclustering")
- for conf_file_name in conf_file_names:
- self._server_conf["shclustering"][
- f"conf_replication_include.{conf_file_name}"
- ] = "true"
-
- def write(self, path: str) -> None:
- with open(path, "w") as fd:
- self._server_conf.write(fd)
diff --git a/splunk_add_on_ucc_framework/templates/README/account_conf_spec.template b/splunk_add_on_ucc_framework/templates/README/account_conf_spec.template
new file mode 100644
index 000000000..bdaa7adc4
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/README/account_conf_spec.template
@@ -0,0 +1,4 @@
+{% for stanza in account_stanzas %}
+[{{ stanza[0] }}]
+{{ stanza[1] | sort() | join("\n") }}
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.spec.template b/splunk_add_on_ucc_framework/templates/README/alert_actions_conf_spec.template
similarity index 89%
rename from splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.spec.template
rename to splunk_add_on_ucc_framework/templates/README/alert_actions_conf_spec.template
index 280d9c390..e2d64d50e 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.spec.template
+++ b/splunk_add_on_ucc_framework/templates/README/alert_actions_conf_spec.template
@@ -3,5 +3,4 @@
{% for param in params %}
{{ param }}
{% endfor %}
-
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/README/inputs_conf_spec.template b/splunk_add_on_ucc_framework/templates/README/inputs_conf_spec.template
new file mode 100644
index 000000000..e6e09dc31
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/README/inputs_conf_spec.template
@@ -0,0 +1,6 @@
+{% for stanza in input_stanzas %}
+{{ "[" ~ stanza.keys() | join("") ~ "://]" }}
+ {% for property in stanza.values() -%}
+{{ property | sort() | join("\n") }}
+ {% endfor %}
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/README/settings_conf_spec.template b/splunk_add_on_ucc_framework/templates/README/settings_conf_spec.template
new file mode 100644
index 000000000..c18a7b3c5
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/README/settings_conf_spec.template
@@ -0,0 +1,4 @@
+{% for stanza in settings_stanzas %}
+[{{ stanza[0] }}]
+{{ stanza[1] | sort() | join("\n") }}
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.template b/splunk_add_on_ucc_framework/templates/conf_files/alert_actions_conf.template
similarity index 61%
rename from splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.template
rename to splunk_add_on_ucc_framework/templates/conf_files/alert_actions_conf.template
index 280d9c390..a9e35e2cf 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/alert_actions.conf.template
+++ b/splunk_add_on_ucc_framework/templates/conf_files/alert_actions_conf.template
@@ -2,6 +2,8 @@
[{{ alert }}]
{% for param in params %}
{{ param }}
+python.version: python3
+is_custom: 1
+payload_format: json
{% endfor %}
-
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/app_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/app_conf.template
new file mode 100644
index 000000000..938e44d21
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/app_conf.template
@@ -0,0 +1,31 @@
+[launcher]
+version = {{ addon_version }}
+description = {{ description }}
+author = {{ author }}
+
+[id]
+version = {{ addon_version }}
+name = {{ name }}
+
+[install]
+build = {{ build }}
+is_configured = false
+state = enabled
+
+[package]
+id = {{ id }}
+check_for_updates = {{ check_for_updates }}
+
+[ui]
+label = {{ label }}
+{% if supported_themes %}
+supported_themes = {{ supported_themes }}
+{% endif %}
+is_visible = {{ is_visible }}
+
+{% if custom_conf %}
+[triggers]
+{% for conf in custom_conf %}
+reload.{{ conf }} = simple
+{% endfor %}
+{% endif %}
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/eventtypes.conf.template b/splunk_add_on_ucc_framework/templates/conf_files/eventtypes_conf.template
similarity index 66%
rename from splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/eventtypes.conf.template
rename to splunk_add_on_ucc_framework/templates/conf_files/eventtypes_conf.template
index a2466777d..d9b467dca 100644
--- a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/eventtypes.conf.template
+++ b/splunk_add_on_ucc_framework/templates/conf_files/eventtypes_conf.template
@@ -1,5 +1,5 @@
{% for alert in mod_alerts %}
- {% if alert.get("adaptive_response") and alert.adaptive_response.get("sourcetype") %}
+ {% if alert.get("adaptive_response", {}).get("sourcetype") %}
[{{ alert.short_name }}_modaction_result]
search = {{ 'sourcetype="' + alert.adaptive_response.sourcetype + '"' }}
{% endif %}
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/inputs_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/inputs_conf.template
new file mode 100644
index 000000000..32b840bb9
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/inputs_conf.template
@@ -0,0 +1,4 @@
+{% for input_name in input_names %}
+{{ "[" ~ input_name ~ "]"}}
+python.version = python3
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/restmap_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/restmap_conf.template
new file mode 100644
index 000000000..dd2e49f8b
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/restmap_conf.template
@@ -0,0 +1,12 @@
+[admin:{{ namespace }}]
+match = /
+members = {{ endpoint_names }}
+
+{% for endpoint in endpoints %}
+[admin_external:{{ endpoint.name }}]
+handlertype = python
+python.version = python3
+handlerfile = {{ endpoint.rh_name }}.py
+handleractions = {{ endpoint.actions() | join(', ') }}
+handlerpersistentmode = true
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/server_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/server_conf.template
new file mode 100644
index 000000000..b83497b73
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/server_conf.template
@@ -0,0 +1,4 @@
+[shclustering]
+{% for conf in custom_conf %}
+conf_replication_include.{{ conf }} = true
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/settings_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/settings_conf.template
new file mode 100644
index 000000000..4d6bb3c9f
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/settings_conf.template
@@ -0,0 +1 @@
+{{ default_content }}
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/tags.conf.template b/splunk_add_on_ucc_framework/templates/conf_files/tags_conf.template
similarity index 100%
rename from splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/tags.conf.template
rename to splunk_add_on_ucc_framework/templates/conf_files/tags_conf.template
diff --git a/splunk_add_on_ucc_framework/templates/conf_files/web_conf.template b/splunk_add_on_ucc_framework/templates/conf_files/web_conf.template
new file mode 100644
index 000000000..4cd6ceed4
--- /dev/null
+++ b/splunk_add_on_ucc_framework/templates/conf_files/web_conf.template
@@ -0,0 +1,9 @@
+{% for endpoint in endpoints %}
+[expose:{{ endpoint.name }}]
+pattern = {{ endpoint.name }}
+methods = POST, GET
+
+[expose:{{ endpoint.name }}_specified]
+pattern = {{ endpoint.name }}/*
+methods = POST, GET, DELETE
+{% endfor %}
diff --git a/splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/default_html_theme/textarea.html b/splunk_add_on_ucc_framework/templates/html_templates/textarea.html
similarity index 100%
rename from splunk_add_on_ucc_framework/commands/modular_alert_builder/arf_template/default_html_theme/textarea.html
rename to splunk_add_on_ucc_framework/templates/html_templates/textarea.html
diff --git a/splunk_add_on_ucc_framework/web_conf.py b/splunk_add_on_ucc_framework/web_conf.py
deleted file mode 100644
index 095962c4f..000000000
--- a/splunk_add_on_ucc_framework/web_conf.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Copyright 2024 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 Sequence
-
-from splunk_add_on_ucc_framework.commands.rest_builder.endpoint.base import (
- RestEndpointBuilder,
-)
-
-
-class WebConf:
- _template = """
-[expose:{name}]
-pattern = {name}
-methods = POST, GET
-"""
-
- _specified_template = """
-[expose:{name}_specified]
-pattern = {name}/*
-methods = POST, GET, DELETE
-"""
-
- @classmethod
- def build(cls, endpoints: Sequence[RestEndpointBuilder]) -> str:
- stanzas = []
- for endpoint in endpoints:
- stanzas.append(
- cls._template.format(
- namespace=endpoint.namespace,
- name=endpoint.name,
- )
- )
- stanzas.append(
- cls._specified_template.format(
- namespace=endpoint.namespace,
- name=endpoint.name,
- )
- )
- return "".join(stanzas)
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
index 44d8466d5..67a64483e 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
@@ -1,17 +1,17 @@
[]
-custom_endpoint =
-endpoint =
+access_token =
account_checkbox =
-account_radio =
account_multiple_select =
-example_help_link =
-username =
-password =
-token =
+account_radio =
+auth_type =
client_id =
client_secret =
+custom_endpoint =
+endpoint =
+example_help_link =
+instance_url =
+password =
redirect_url =
-access_token =
refresh_token =
-instance_url =
-auth_type =
\ No newline at end of file
+token =
+username =
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
index 4328f2f12..a359f3958 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
@@ -1,20 +1,20 @@
[proxy]
proxy_enabled =
+proxy_password =
+proxy_port =
+proxy_rdns =
proxy_type =
proxy_url =
-proxy_port =
proxy_username =
-proxy_password =
-proxy_rdns =
[logging]
loglevel =
[custom_abc]
-testString =
-testNumber =
-testRegex =
+testDate =
testEmail =
testIpv4 =
-testDate =
+testNumber =
+testRegex =
+testString =
testUrl =
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/app.conf b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/app.conf
index e127851ca..ad55141fd 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/app.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/app.conf
@@ -32,5 +32,4 @@ version = 1.0.0
[triggers]
reload.splunk_ta_uccexample_account = simple
reload.splunk_ta_uccexample_oauth = simple
-reload.splunk_ta_uccexample_settings = simple
-
+reload.splunk_ta_uccexample_settings = simple
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/restmap.conf b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/restmap.conf
index c43d121fb..411bf07f8 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/restmap.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/restmap.conf
@@ -1,4 +1,3 @@
-
[admin:splunk_ta_uccexample]
match = /
members = splunk_ta_uccexample_account, splunk_ta_uccexample_oauth, splunk_ta_uccexample_settings
@@ -22,4 +21,4 @@ handlertype = python
python.version = python3
handlerfile = splunk_ta_uccexample_rh_settings.py
handleractions = edit, list
-handlerpersistentmode = true
+handlerpersistentmode = true
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/server.conf b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/server.conf
index f89d933c0..5a7326859 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/server.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/server.conf
@@ -1,4 +1,4 @@
[shclustering]
conf_replication_include.splunk_ta_uccexample_settings = true
conf_replication_include.splunk_ta_uccexample_account = true
-conf_replication_include.splunk_ta_uccexample_oauth = true
+conf_replication_include.splunk_ta_uccexample_oauth = true
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/web.conf b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/web.conf
index 9f58918dd..c1495c278 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/web.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_configuration/Splunk_TA_UCCExample/default/web.conf
@@ -1,4 +1,3 @@
-
[expose:splunk_ta_uccexample_account]
pattern = splunk_ta_uccexample_account
methods = POST, GET
@@ -21,4 +20,4 @@ methods = POST, GET
[expose:splunk_ta_uccexample_settings_specified]
pattern = splunk_ta_uccexample_settings/*
-methods = POST, GET, DELETE
+methods = POST, GET, DELETE
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/alert_actions.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/alert_actions.conf.spec
index 12ca0db1e..46492994d 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/alert_actions.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/alert_actions.conf.spec
@@ -1,7 +1,8 @@
[test_alert]
param._cam = Adaptive Response parameters.
param.name = Name. It's a required parameter. It's default value is xyz.
+param.description = Description. It's a required parameter. It's default value is some sample description.
param.all_incidents = All Incidents.
param.table_list = Table List. It's default value is problem.
param.action = Action:. It's a required parameter. It's default value is update.
-param.account = Select Account. It's a required parameter.
+param.account = Select Account. It's a required parameter.
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/inputs.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/inputs.conf.spec
index eb0eade4d..d1d44e2c8 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/inputs.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/inputs.conf.spec
@@ -1,38 +1,38 @@
[example_input_one://]
-input_one_checkbox =
-input_one_radio =
-singleSelectTest =
-multipleSelectTest =
-interval =
-index =
account =
-object =
-object_fields =
-order_by =
-use_existing_checkpoint =
-start_date =
-limit =
-example_textarea_field =
example_help_link =
-hide_in_ui =
-hard_disabled =
+example_textarea_field = Help message
+hard_disabled =
+hide_in_ui =
+index = Default: default
+input_one_checkbox = This is an example checkbox for the input one entity Default: True
+input_one_radio = This is an example radio button for the input one entity Default: yes
+interval = Time interval of the data input, in seconds.
+limit = The maximum number of results returned by the query. Default: 1000
+multipleSelectTest = Default: a|b
+object = The name of the object to query for.
+object_fields = Object fields from which to collect data. Delimit multiple fields using a comma.
+order_by = The datetime field by which to query results in ascending order for indexing. Default: LastModifiedDate
+singleSelectTest =
+start_date = The datetime after which to query and index records, in this format: "YYYY-MM-DDThh:mm:ss.000z". Defaults to 90 days earlier from now.
+use_existing_checkpoint = Data input already exists. Select `No` if you want to reset the data collection. Default: yes
[example_input_two://]
-interval =
-index =
account =
-input_two_multiple_select =
-input_two_checkbox =
-input_two_radio =
-use_existing_checkpoint =
-start_date =
-example_help_link =
apis =
-hide_in_ui =
-hard_disabled =
+example_help_link =
+hard_disabled =
+hide_in_ui =
+index = Default: default
+input_two_checkbox = This is an example checkbox for the input two entity
+input_two_multiple_select = This is an example multipleSelect for input two entity
+input_two_radio = This is an example radio button for the input two entity
+interval = Time interval of the data input, in seconds.
+start_date = The date and time, in "YYYY-MM-DDThh:mm:ss.000z" format, after which to query and index records. The default is 90 days before today.
+use_existing_checkpoint = Data input already exists. Select `No` if you want to reset the data collection. Default: yes
[example_input_three://]
-interval =
+interval = Time interval of the data input, in seconds.
[example_input_four://]
-interval =
\ No newline at end of file
+interval = Time interval of the data input, in seconds.
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
index 06bdcd08f..f922e33d2 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_account.conf.spec
@@ -1,24 +1,24 @@
[]
-custom_endpoint =
-endpoint =
-url =
+access_token =
account_checkbox =
-account_radio =
account_multiple_select =
-example_help_link =
-config1_help_link =
-config2_help_link =
-username =
-password =
-token =
+account_radio =
+auth_type =
basic_oauth_text =
client_id =
client_secret =
-redirect_url =
-endpoint_token =
+config1_help_link =
+config2_help_link =
+custom_endpoint =
+endpoint =
endpoint_authorize =
+endpoint_token =
+example_help_link =
+instance_url =
oauth_oauth_text =
-access_token =
+password =
+redirect_url =
refresh_token =
-instance_url =
-auth_type =
\ No newline at end of file
+token =
+url =
+username =
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
index 4328f2f12..a359f3958 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/README/splunk_ta_uccexample_settings.conf.spec
@@ -1,20 +1,20 @@
[proxy]
proxy_enabled =
+proxy_password =
+proxy_port =
+proxy_rdns =
proxy_type =
proxy_url =
-proxy_port =
proxy_username =
-proxy_password =
-proxy_rdns =
[logging]
loglevel =
[custom_abc]
-testString =
-testNumber =
-testRegex =
+testDate =
testEmail =
testIpv4 =
-testDate =
+testNumber =
+testRegex =
+testString =
testUrl =
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/bin/test_alert.py b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/bin/test_alert.py
index c33336d17..a0db490ee 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/bin/test_alert.py
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/bin/test_alert.py
@@ -19,6 +19,10 @@ def validate_params(self):
if not self.get_param("name"):
self.log_error('name is a mandatory parameter, but its value is None.')
return False
+
+ if not self.get_param("description"):
+ self.log_error('description is a mandatory parameter, but its value is None.')
+ return False
if not self.get_param("action"):
self.log_error('action is a mandatory parameter, but its value is None.')
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/alert_actions.conf b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/alert_actions.conf
index 77d525655..57834810c 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/alert_actions.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/alert_actions.conf
@@ -1,13 +1,14 @@
[test_alert]
-label = Test Alert
-description = Description for test Alert Action
-param._cam = {"task": ["Create", "Update"], "subject": ["endpoint"], "category": ["Information Conveyance", "Information Portrayal"], "technology": [{"version": ["1.0.0"], "product": "Test Incident Update", "vendor": "Splunk"}], "supports_adhoc": true, "supports_cloud": true, "drilldown_uri": "search?q=search%20index%3D\"_internal\"&earliest=0&latest="}
+icon_path = test icon.png
python.version = python3
is_custom = 1
payload_format = json
-icon_path = test icon.png
+label = Test Alert
+description = Description for test Alert Action
+param._cam = {"task": ["Create", "Update"], "subject": ["endpoint"], "category": ["Information Conveyance", "Information Portrayal"], "technology": [{"version": ["1.0.0"], "product": "Test Incident Update", "vendor": "Splunk"}], "supports_adhoc": true, "supports_cloud": true, "drilldown_uri": "search?q=search%20index%3D\"_internal\"&earliest=0&latest="}
param.name = xyz
+param.description = some sample description
param.all_incidents = 0
param.table_list = problem
param.action = update
-param.account =
+param.account =
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/app.conf b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/app.conf
index e541f2c34..51a81dada 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/app.conf
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/app.conf
@@ -24,5 +24,4 @@ version = 5.5.8R5fd76615
[triggers]
reload.splunk_ta_uccexample_account = simple
reload.splunk_ta_uccexample_oauth = simple
-reload.splunk_ta_uccexample_settings = simple
-
+reload.splunk_ta_uccexample_settings = simple
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/data/ui/alerts/test_alert.html b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/data/ui/alerts/test_alert.html
index 5d91aae06..c655d084d 100644
--- a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/data/ui/alerts/test_alert.html
+++ b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/data/ui/alerts/test_alert.html
@@ -9,6 +9,16 @@
+
+
+
+
+
+ Please enter the description for the alert
+
+