-
Notifications
You must be signed in to change notification settings - Fork 34
feat: base code for supporting custom search command #1693
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
Changes from 4 commits
4b215e3
5a87671
65007d4
0816ddd
a373ed0
2a4c32b
42c8796
a4adaaa
863d5a0
c829179
3776f0e
40ac685
004561d
b54ca97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| # | ||
| # Copyright 2025 Splunk Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
|
|
||
| # TODO: Update the list as and when Splunk introduces new commands. | ||
| # Links to use: https://docs.splunk.com/Documentation/SplunkCloud/latest/SearchReference | ||
| # https://docs.splunk.com/Documentation/Splunk/latest/SearchReference | ||
| SPLUNK_COMMANDS = [ | ||
| "abstract", | ||
| "accum", | ||
| "addcoltotals", | ||
| "addinfo", | ||
| "addtotals", | ||
| "analyzefields", | ||
| "anomalies", | ||
| "anomalousvalue", | ||
| "anomalydetection", | ||
| "append", | ||
| "appendcols", | ||
| "appendpipe", | ||
| "arules", | ||
| "associate", | ||
| "autoregress", | ||
| "awssnsalert", | ||
| "bin", | ||
| "bucket", | ||
| "bucketdir", | ||
| "chart", | ||
| "cluster", | ||
| "cofilter", | ||
| "collect", | ||
| "concurrency", | ||
| "contingency", | ||
| "convert", | ||
| "correlate", | ||
| "ctable", | ||
| "datamodel", | ||
| "datamodelsimple", | ||
| "dbinspect", | ||
| "dbxquery", | ||
| "dedup", | ||
| "delete", | ||
| "delta", | ||
| "diff", | ||
| "entitymerge", | ||
| "erex", | ||
| "eval", | ||
| "eventcount", | ||
| "eventstats", | ||
| "extract", | ||
| "fieldformat", | ||
| "fields", | ||
| "fieldsummary", | ||
| "filldown", | ||
| "fillnull", | ||
| "findtypes", | ||
| "folderize", | ||
| "foreach", | ||
| "format", | ||
| "from", | ||
| "fromjson", | ||
| "gauge", | ||
| "gentimes", | ||
| "geom", | ||
| "geomfilter", | ||
| "geostats", | ||
| "head", | ||
| "highlight", | ||
| "history", | ||
| "iconify", | ||
| "inputcsv", | ||
| "inputintelligence", | ||
| "inputlookup", | ||
| "iplocation", | ||
| "join", | ||
| "kmeans", | ||
| "kvform", | ||
| "loadjob", | ||
| "localize", | ||
| "localop", | ||
| "lookup", | ||
| "makecontinuous", | ||
| "makemv", | ||
| "makeresults", | ||
| "map", | ||
| "mcollect", | ||
| "metadata", | ||
| "metasearch", | ||
| "meventcollect", | ||
| "mpreview", | ||
| "msearch", | ||
| "mstats", | ||
| "multikv", | ||
| "multisearch", | ||
| "mvcombine", | ||
| "mvexpand", | ||
| "nomv", | ||
| "outlier", | ||
| "outputcsv", | ||
| "outputlookup", | ||
| "outputtext", | ||
| "overlap", | ||
| "pivot", | ||
| "predict", | ||
| "rangemap", | ||
| "rare", | ||
| "regex", | ||
| "reltime", | ||
| "rename", | ||
| "replace", | ||
| "require", | ||
| "rest", | ||
| "return", | ||
| "reverse", | ||
| "rex", | ||
| "rtorder", | ||
| "run", | ||
| "savedsearch", | ||
| "script", | ||
| "scrub", | ||
| "search", | ||
| "searchtxn", | ||
| "selfjoin", | ||
| "sendalert", | ||
| "sendemail", | ||
| "set", | ||
| "setfields", | ||
| "sichart", | ||
| "sirare", | ||
| "sistats", | ||
| "sitimechart", | ||
| "sitop", | ||
| "sort", | ||
| "spath", | ||
| "stats", | ||
| "strcat", | ||
| "streamstats", | ||
| "table", | ||
| "tags", | ||
| "tail", | ||
| "timechart", | ||
| "timewrap", | ||
| "tojson", | ||
| "top", | ||
| "transaction", | ||
| "transpose", | ||
| "trendline", | ||
| "tscollect", | ||
| "tstats", | ||
| "typeahead", | ||
| "typelearner", | ||
| "typer", | ||
| "union", | ||
| "uniq", | ||
| "untable", | ||
| "walklex", | ||
| "where", | ||
| "x11", | ||
| "xmlkv", | ||
| "xmlunescape", | ||
| "xpath", | ||
| "xyseries", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| from typing import Any, Dict, List | ||
| import logging | ||
| import itertools | ||
| from splunk_add_on_ucc_framework.const import SPLUNK_COMMANDS | ||
|
|
||
| import jsonschema | ||
|
|
||
|
|
@@ -47,8 +48,14 @@ class GlobalConfigValidator: | |
| Custom validation should be implemented here. | ||
| """ | ||
|
|
||
| def __init__(self, source_dir: str, global_config: global_config_lib.GlobalConfig): | ||
| self._source_dir = source_dir | ||
| def __init__( | ||
| self, | ||
| internal_root_dir: str, | ||
| global_config: global_config_lib.GlobalConfig, | ||
| **kwargs: Any, | ||
| ): | ||
| self._internal_root_dir = internal_root_dir | ||
| self._source_dir = kwargs.get("source", "") | ||
| self._global_config = global_config | ||
| self._config = global_config.content | ||
| self.resolved_configuration = global_config.resolved_configuration | ||
|
|
@@ -58,7 +65,7 @@ def _validate_config_against_schema(self) -> None: | |
| Validates config against JSON schema. | ||
| Raises jsonschema.ValidationError if config is not valid. | ||
| """ | ||
| schema_path = os.path.join(self._source_dir, "schema", "schema.json") | ||
| schema_path = os.path.join(self._internal_root_dir, "schema", "schema.json") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this change?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "source_dir" seemed an ambiguous variable name - does source_dir point to source code of add-on that is being built or UCC code source, hence, I have renamed the |
||
| with open(schema_path, encoding="utf-8") as f_schema: | ||
| schema_raw = f_schema.read() | ||
| schema = json.loads(schema_raw) | ||
|
|
@@ -710,6 +717,49 @@ def _validate_meta_default_view(self) -> None: | |
| 'meta.defaultView == "dashboard" but there is no dashboard defined in globalConfig' | ||
| ) | ||
|
|
||
| def _validate_custom_search_commands(self) -> None: | ||
| for command in self._global_config.custom_search_commands: | ||
| file_path = os.path.join(self._source_dir, "bin", command["fileName"]) | ||
| if not os.path.isfile(file_path): | ||
kkedziak-splunk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| raise GlobalConfigValidatorException( | ||
| f"{command['fileName']} is not present in `{os.path.join(self._source_dir, 'bin')}` directory. " | ||
| "Please ensure the file exists." | ||
| ) | ||
|
|
||
| if (command.get("requiredSearchAssistant", False) is False) and ( | ||
| command.get("description") | ||
| or command.get("usage") | ||
| or command.get("syntax") | ||
| ): | ||
| logger.warning( | ||
| "requiredSearchAssistant is set to false " | ||
| "but attributes required for 'searchbnf.conf' is defined which is not required." | ||
| ) | ||
| if (command.get("requiredSearchAssistant", False) is True) and not ( | ||
| command.get("description") | ||
| and command.get("usage") | ||
| and command.get("syntax") | ||
| ): | ||
| raise GlobalConfigValidatorException( | ||
| "One of the attributes among `description`, `usage`, `syntax`" | ||
| " is not been defined in globalConfig. Define them as requiredSearchAssistant is set to True." | ||
| ) | ||
|
|
||
| if command["commandName"] in SPLUNK_COMMANDS: | ||
| raise GlobalConfigValidatorException( | ||
| f"CommandName: {command['commandName']}" | ||
| " cannot have the same name as Splunk built-in command." | ||
| ) | ||
|
|
||
| fileName_without_extension = command["fileName"].replace(".py", "") | ||
| if command["commandName"] == fileName_without_extension: | ||
| # Here we are generating file based on commandName therefore | ||
| # the core logic should not have the same name as commandName | ||
| raise GlobalConfigValidatorException( | ||
| f"Filename: {fileName_without_extension} and CommandName: {command['commandName']}" | ||
| " should not be same for custom search command." | ||
| ) | ||
|
|
||
| def validate(self) -> None: | ||
| self._validate_config_against_schema() | ||
| if self._global_config.has_pages(): | ||
|
|
@@ -723,6 +773,7 @@ def validate(self) -> None: | |
| self._validate_checkbox_group() | ||
| self._validate_groups() | ||
| self._validate_field_modifications() | ||
| self._validate_custom_search_commands() | ||
| self._validate_alerts() | ||
| self._validate_meta_default_view() | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.