Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: provide support for .conf-only add-ons #1546

Merged
merged 11 commits into from
Feb 14, 2025
6 changes: 6 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ When you use UCC to create an add-on, the following elements are generated and s
* the monitoring dashboard. For more information, see [Dashboard](dashboard.md),
* the necessary files defined for the alert action, if you defined the alert action in the `globalConfig` file. For more information, see [Alert actions](alert_actions/index.md).

UCC now provides support for `.conf-only-TA's`, ensuring the following elements are generated and stored in their respective directories:

* `app.conf` is generated in the `default` directory.
* Metadata files are stored in the `metadata` folder.
* No Python or JavaScript files are created.

You can extend your add-ons with the following files:

* to extend the UI, use custom codes. For more information, see [Custom hook](custom_ui_extensions/custom_hook.md).
Expand Down
44 changes: 21 additions & 23 deletions splunk_add_on_ucc_framework/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ def _modify_and_replace_token_for_oauth_templates(
redirect_html_src = os.path.join(
outputdir, ta_name, "appserver", "templates", "redirect.html"
)

if global_config.has_oauth():
html_template_path = os.path.join(outputdir, ta_name, "appserver", "templates")
with open(os.path.join(html_template_path, "redirect.html")) as f:
Expand Down Expand Up @@ -473,24 +472,22 @@ def generate(
sys.exit(1)
global_config.parse_user_defined_handlers()
scheme = global_config_builder_schema.GlobalConfigBuilderSchema(global_config)
utils.recursive_overwrite(
os.path.join(internal_root_dir, "package"),
os.path.join(output_directory, ta_name),
ui_source_map,
)
if global_config.has_pages():
utils.recursive_overwrite(
os.path.join(internal_root_dir, "package"),
os.path.join(output_directory, ta_name),
ui_source_map,
)
global_config_file = (
"globalConfig.yaml" if gc_path.endswith(".yaml") else "globalConfig.json"
)
output_global_config_path = os.path.join(
output_directory,
ta_name,
"appserver",
"static",
"js",
"build",
global_config_file,
output_build_path = os.path.join(
output_directory, ta_name, "appserver", "static", "js", "build"
)
global_config.dump(output_global_config_path)
if not os.path.isdir(output_build_path):
# this path may not exist for the .conf-only add-ons
os.makedirs(output_build_path)
global_config.dump(os.path.join(output_build_path, global_config_file))
logger.info("Copied globalConfig to output")
ucc_lib_target = os.path.join(output_directory, ta_name, "lib")
try:
Expand Down Expand Up @@ -524,13 +521,14 @@ def generate(
)
)
# TODO: all FILES GENERATED object: generated_files, use it for comparison
builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name))
builder_obj.build()
_modify_and_replace_token_for_oauth_templates(
ta_name,
global_config,
output_directory,
)
if global_config.has_pages():
builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name))
builder_obj.build()
_modify_and_replace_token_for_oauth_templates(
ta_name,
global_config,
output_directory,
)
if global_config.has_inputs():
logger.info("Generating inputs code")
_add_modular_input(ta_name, global_config, output_directory)
Expand Down Expand Up @@ -617,7 +615,7 @@ def generate(
)

ui_available = False
if global_config:
if global_config and global_config.has_pages():
ui_available = global_config.meta.get("isVisible", True)
# NOTE: merging source and generated 'app.conf' as per previous design
AppConf(
Expand Down
152 changes: 77 additions & 75 deletions splunk_add_on_ucc_framework/commands/openapi_generator/ucc_to_oas.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,52 +158,53 @@ def __add_schemas_object(
) -> OpenAPIObject:
if open_api_object.components is not None:
open_api_object.components.schemas = {}
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity, without=["name"]
)
open_api_object.components.schemas[schema_name] = schema_object
if hasattr(global_config.pages, "inputs") and hasattr( # type: ignore[attr-defined]
global_config.pages.inputs, "services" # type: ignore[attr-defined]
):
additional_input_entities = [
json_to_object.DataClasses(
json={
"field": "disabled",
"type": "singleSelect",
"options": {
"autoCompleteFields": [
{"value": "False"},
{"value": "True"},
]
},
}
)
]
for service in global_config.pages.inputs.services: # type: ignore[attr-defined]
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
without=["name"],
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
without=["disabled"],
)
open_api_object.components.schemas[schema_name] = schema_object
if hasattr(global_config, "pages"):
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs:
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=tab.name, entities=tab.entity, without=["name"]
)
open_api_object.components.schemas[schema_name] = schema_object
if hasattr(global_config.pages, "inputs") and hasattr(
global_config.pages.inputs, "services"
):
additional_input_entities = [
json_to_object.DataClasses(
json={
"field": "disabled",
"type": "singleSelect",
"options": {
"autoCompleteFields": [
{"value": "False"},
{"value": "True"},
]
},
}
)
]
for service in global_config.pages.inputs.services:
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
without=["name"],
)
open_api_object.components.schemas[schema_name] = schema_object
schema_name, schema_object = __get_schema_object(
name=service.name,
entities=service.entity + additional_input_entities,
without=["disabled"],
)
open_api_object.components.schemas[schema_name] = schema_object

return open_api_object

Expand Down Expand Up @@ -380,35 +381,36 @@ def __assign_ta_paths(
def __add_paths(
open_api_object: OpenAPIObject, global_config: DataClasses
) -> OpenAPIObject:
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs: # type: ignore[attr-defined]
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{tab.name}" # type: ignore[attr-defined]
if hasattr(tab, "table")
else f"/{global_config.meta.restRoot}_settings/{tab.name}", # type: ignore[attr-defined]
path_name=tab.name,
actions=tab.table.actions
if hasattr(tab, "table") and hasattr(tab.table, "actions")
else None,
page=GloblaConfigPages.CONFIGURATION,
)
if hasattr(global_config.pages, "inputs") and hasattr( # type: ignore[attr-defined]
global_config.pages.inputs, "services" # type: ignore[attr-defined]
):
for service in global_config.pages.inputs.services: # type: ignore[attr-defined]
if hasattr(service, "table"):
actions = service.table.actions
else:
actions = global_config.pages.inputs.table.actions # type: ignore[attr-defined]
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{service.name}", # type: ignore[attr-defined]
path_name=service.name,
actions=actions,
page=GloblaConfigPages.INPUTS,
)
if hasattr(global_config, "pages"):
pages = getattr(global_config, "pages", None)
if hasattr(pages, "configuration"):
for tab in global_config.pages.configuration.tabs:
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{tab.name}" # type: ignore[attr-defined]
if hasattr(tab, "table")
else f"/{global_config.meta.restRoot}_settings/{tab.name}", # type: ignore[attr-defined]
path_name=tab.name,
actions=tab.table.actions
if hasattr(tab, "table") and hasattr(tab.table, "actions")
else None,
page=GloblaConfigPages.CONFIGURATION,
)
if hasattr(global_config.pages, "inputs") and hasattr(
global_config.pages.inputs, "services"
):
for service in global_config.pages.inputs.services:
if hasattr(service, "table"):
actions = service.table.actions
else:
actions = global_config.pages.inputs.table.actions
open_api_object = __assign_ta_paths(
open_api_object=open_api_object,
path=f"/{global_config.meta.restRoot}_{service.name}", # type: ignore[attr-defined]
path_name=service.name,
actions=actions,
page=GloblaConfigPages.INPUTS,
)
return open_api_object


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class AccountConf(ConfGenerator):

def _set_attributes(self, **kwargs: Any) -> None:
self.account_fields: List[Tuple[str, List[str]]] = []
if self._global_config and self._gc_schema:
if (
self._global_config
and self._global_config.has_configuration()
and self._gc_schema
):
self.conf_spec_file = (
self._global_config.namespace.lower() + "_account.conf.spec"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _set_attributes(self, **kwargs: Any) -> None:
self.disable = False
self.service_name = ""
self.default_value_info: Dict[str, Dict[str, str]] = {}
if self._global_config:
if self._global_config and self._global_config.has_inputs():
for service in self._global_config.inputs:
properties = []
if service.get("disableNewInput"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ class RestMapConf(ConfGenerator):

def _set_attributes(self, **kwargs: Any) -> None:
self.conf_file = "restmap.conf"
if self._gc_schema:
if self._global_config and self._global_config.has_pages() and 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:
if not (
self._global_config and self._global_config.has_pages() and self._gc_schema
):
return None

file_path = self.get_file_output_path(["default", self.conf_file])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ 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:
if (
self._global_config
and self._gc_schema
and self._global_config.has_configuration()
):
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ class WebConf(ConfGenerator):

def _set_attributes(self, **kwargs: Any) -> None:
self.conf_file = "web.conf"
if self._gc_schema:
if self._global_config and self._global_config.has_pages() and self._gc_schema:
self.endpoints = self._gc_schema.endpoints

def generate_conf(self) -> Union[Dict[str, str], None]:
if not self._gc_schema:
if not (
self._global_config and self._global_config.has_pages() and self._gc_schema
):
return None

file_path = self.get_file_output_path(["default", self.conf_file])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# limitations under the License.
#
from splunk_add_on_ucc_framework.generators.xml_files import XMLGenerator
from typing import Dict, Any
from typing import Dict, Any, Union
import os
from splunk_add_on_ucc_framework import data_ui_generator
import logging
Expand All @@ -41,15 +41,17 @@ def _set_attributes(self, **kwargs: Any) -> None:
"Skipping generating data/ui/nav/default.xml because file already exists."
)
else:
if self._global_config:
if self._global_config and self._global_config.has_pages():
self.default_xml_content = data_ui_generator.generate_nav_default_xml(
include_inputs=self._global_config.has_inputs(),
include_dashboard=self._global_config.has_dashboard(),
include_configuration=self._global_config.has_configuration(),
default_view=self._global_config.meta.get("default_view"),
)

def generate_xml(self) -> Dict[str, str]:
def generate_xml(self) -> Union[Dict[str, str], None]:
if self._global_config and not self._global_config.has_pages():
return None
file_path = self.get_file_output_path(
["default", "data", "ui", "nav", "default.xml"]
)
Expand Down
Loading
Loading