From 6b15a29805efd460efe852357a556c15b6f2e440 Mon Sep 17 00:00:00 2001 From: Szymon Oleksy Date: Thu, 22 Feb 2024 12:40:03 +0100 Subject: [PATCH 1/3] fix: modifyFieldsOnValue schema and tests --- .../global_config_validator.py | 88 +++++++++++++++++++ .../schema/schema.json | 36 ++++---- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/splunk_add_on_ucc_framework/global_config_validator.py b/splunk_add_on_ucc_framework/global_config_validator.py index bd41c8a35..9e17227c7 100644 --- a/splunk_add_on_ucc_framework/global_config_validator.py +++ b/splunk_add_on_ucc_framework/global_config_validator.py @@ -560,6 +560,93 @@ def _validate_groups(self) -> None: f"Service {service['name']} uses group field {group_field} which is not defined in entity" ) + def _is_circular(self, mods, visited, all_entity_fields, current_field): + visited[current_field] = 'visited' + current_field_mods = next((mod for mod in mods if mod["fieldId"] == current_field), None) + if current_field_mods is None: + # no more dependent modification fields + visited[current_field] = 'dead_end' + return visited + else: + for influenced_field in current_field_mods["influenced_fields"]: + if influenced_field not in all_entity_fields: + raise GlobalConfigValidatorException( + """Modification in field '{0}' for not existing field '{1}'""".format(current_field, influenced_field) + ) + if influenced_field == current_field: + raise GlobalConfigValidatorException( + """Field '{0}' tries to modify itself""".format(current_field) + ) + if visited[influenced_field] == 'visited': + raise GlobalConfigValidatorException( + """Circular modifications for field '{0}' included in modifications in field '{1}'""".format(influenced_field, current_field) + ) + else: + visited = self._is_circular(mods, visited, all_entity_fields,influenced_field) + # all of dependent modifications fields are dead_end + visited[current_field] = 'dead_end' + return visited + + def _check_if_cilcular(self, all_entity_fields, fields_with_mods, modification_dict): + visited = {} + + for field in all_entity_fields: + visited[field] = 'not_visited' + + for start_field in fields_with_mods: + #DFS algorithm for all fields with modifications + visited = self._is_circular(modification_dict, visited, all_entity_fields,start_field) + + def _get_mods_data_for_single_entity(self,all_fields,fields_with_mods,all_modifications,entity): + all_fields.append(entity["field"]) + if "modifyFieldsOnValue" in entity: + influenced_fields = set() + fields_with_mods.append(entity["field"]) + for mods in entity["modifyFieldsOnValue"]: + for mod in mods["fieldsToModify"]: + influenced_fields.add(mod['fieldId']) + all_modifications.append({"fieldId":entity["field"], "influenced_fields":influenced_fields}) + return [all_fields, fields_with_mods, all_modifications] + + def _get_all_modifiction_data(self, all_fields, fields_with_mods, all_modifications, entityCollector): + entities = entityCollector["entity"] + for entity in entities: + if entity['type'] == "oauth": + for type in entity['options']['auth_type']: + for oauthEntity in entity['options'][type]: + [all_fields, fields_with_mods, all_modifications] = self._get_mods_data_for_single_entity(all_fields, fields_with_mods, all_modifications, oauthEntity) + else: + [all_fields, fields_with_mods, all_modifications] = self._get_mods_data_for_single_entity(all_fields, fields_with_mods, all_modifications, entity) + + return [fields_with_mods, all_modifications, all_fields] + + def _validate_field_modifications(self) -> None: + pages = self._config["pages"] + configuration = pages["configuration"] + tabs = configuration["tabs"] + + # lists initialised here as fields need to be unique across configuration page + fields_with_mods_config = [] + all_modifications_config = [] + all_fields_config = [] + + for tab in tabs: + [fields_with_mods_config, all_modifications_config, all_fields_config] = self._get_all_modifiction_data(all_fields_config,fields_with_mods_config,all_modifications_config,tab) + + self._check_if_cilcular(all_fields_config, fields_with_mods_config, all_modifications_config) + + inputs = pages['inputs'] + services = inputs["services"] + + # lists initialised here as fields need to be unique across inputs page + fields_with_mods_inputs = [] + all_modifications_inputs = [] + all_fields_inputs = [] + for service in services: + [fields_with_mods_inputs, all_modifications_inputs, all_fields_inputs] = self._get_all_modifiction_data(all_fields_config,fields_with_mods_config,all_modifications_config,service) + + self._check_if_cilcular(all_fields_inputs, fields_with_mods_inputs, all_modifications_inputs) + def validate(self) -> None: self._validate_config_against_schema() self._validate_configuration_tab_table_has_name_field() @@ -573,3 +660,4 @@ def validate(self) -> None: self._warn_on_placeholder_usage() self._validate_checkbox_group() self._validate_groups() + self._validate_field_modifications() diff --git a/splunk_add_on_ucc_framework/schema/schema.json b/splunk_add_on_ucc_framework/schema/schema.json index 34d28f05f..67a986965 100644 --- a/splunk_add_on_ucc_framework/schema/schema.json +++ b/splunk_add_on_ucc_framework/schema/schema.json @@ -446,12 +446,12 @@ }, "requiredWhenVisible": { "type": "boolean" - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type"], @@ -533,12 +533,12 @@ }, "rowsMax": { "type": "number" - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type"], @@ -658,12 +658,12 @@ "items": { "$ref": "#/definitions/ValueLabelPair" } - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type", "options"], @@ -780,12 +780,12 @@ "delimiter": { "type": "string", "maxLength": 1 - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type", "options"], @@ -1020,13 +1020,13 @@ "items": { "$ref": "#/definitions/ValueLabelPair" } - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["items"], "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type", "options"], @@ -1099,12 +1099,12 @@ "type": "string" }, "uniqueItems": true - }, - "modifyFieldsOnValue": { - "$ref": "#/definitions/modifyFieldsOnValue" } }, "additionalProperties": false + }, + "modifyFieldsOnValue": { + "$ref": "#/definitions/modifyFieldsOnValue" } }, "required": ["field", "label", "type"], From a9e840a673d406ce3a3e2499249e9d6768324ffd Mon Sep 17 00:00:00 2001 From: Szymon Oleksy Date: Thu, 22 Feb 2024 13:41:16 +0100 Subject: [PATCH 2/3] fix: lint --- .../global_config_validator.py | 173 +++++++++++++----- 1 file changed, 128 insertions(+), 45 deletions(-) diff --git a/splunk_add_on_ucc_framework/global_config_validator.py b/splunk_add_on_ucc_framework/global_config_validator.py index 9e17227c7..e2a33a07f 100644 --- a/splunk_add_on_ucc_framework/global_config_validator.py +++ b/splunk_add_on_ucc_framework/global_config_validator.py @@ -560,92 +560,175 @@ def _validate_groups(self) -> None: f"Service {service['name']} uses group field {group_field} which is not defined in entity" ) - def _is_circular(self, mods, visited, all_entity_fields, current_field): - visited[current_field] = 'visited' - current_field_mods = next((mod for mod in mods if mod["fieldId"] == current_field), None) + def _is_circular( + self, + mods: List[Any], + visited: Dict[str, str], + all_entity_fields: List[Any], + current_field: str, + ) -> Dict[str, str]: + """ + Checks if there is circular modification based on visited list and DFS algorithm + """ + DEAD_END = "dead_end" + VISITING = "visited" + visited[current_field] = VISITING + + current_field_mods = next( + (mod for mod in mods if mod["fieldId"] == current_field), None + ) if current_field_mods is None: # no more dependent modification fields - visited[current_field] = 'dead_end' + visited[current_field] = DEAD_END return visited else: for influenced_field in current_field_mods["influenced_fields"]: if influenced_field not in all_entity_fields: raise GlobalConfigValidatorException( - """Modification in field '{0}' for not existing field '{1}'""".format(current_field, influenced_field) + f"""Modification in field '{current_field}' for not existing field '{influenced_field}'""" ) if influenced_field == current_field: raise GlobalConfigValidatorException( - """Field '{0}' tries to modify itself""".format(current_field) + f"""Field '{current_field}' tries to modify itself""" ) - if visited[influenced_field] == 'visited': + if visited[influenced_field] == VISITING: raise GlobalConfigValidatorException( - """Circular modifications for field '{0}' included in modifications in field '{1}'""".format(influenced_field, current_field) + f"""Circular modifications for field '{influenced_field}' in field '{current_field}'""" ) else: - visited = self._is_circular(mods, visited, all_entity_fields,influenced_field) + visited = self._is_circular( + mods, visited, all_entity_fields, influenced_field + ) # all of dependent modifications fields are dead_end - visited[current_field] = 'dead_end' + visited[current_field] = DEAD_END return visited - def _check_if_cilcular(self, all_entity_fields, fields_with_mods, modification_dict): + def _check_if_cilcular( + self, + all_entity_fields: List[Any], + fields_with_mods: List[Any], + modifications: List[Any], + ) -> None: visited = {} for field in all_entity_fields: - visited[field] = 'not_visited' + visited[field] = "not_visited" - for start_field in fields_with_mods: - #DFS algorithm for all fields with modifications - visited = self._is_circular(modification_dict, visited, all_entity_fields,start_field) + for start_field in fields_with_mods: + # DFS algorithm for all fields with modifications + visited = self._is_circular( + modifications, visited, all_entity_fields, start_field + ) - def _get_mods_data_for_single_entity(self,all_fields,fields_with_mods,all_modifications,entity): + def _get_mods_data_for_single_entity( + self, + all_fields: List[Any], + fields_with_mods: List[Any], + all_modifications: List[Any], + entity: Dict[str, Any], + ) -> List[Any]: + """ + Add modification entity data to lists and returns them + """ all_fields.append(entity["field"]) if "modifyFieldsOnValue" in entity: influenced_fields = set() fields_with_mods.append(entity["field"]) for mods in entity["modifyFieldsOnValue"]: for mod in mods["fieldsToModify"]: - influenced_fields.add(mod['fieldId']) - all_modifications.append({"fieldId":entity["field"], "influenced_fields":influenced_fields}) + influenced_fields.add(mod["fieldId"]) + all_modifications.append( + {"fieldId": entity["field"], "influenced_fields": influenced_fields} + ) return [all_fields, fields_with_mods, all_modifications] - def _get_all_modifiction_data(self, all_fields, fields_with_mods, all_modifications, entityCollector): + def _get_all_modifiction_data( + self, + all_fields: List[Any], + fields_with_mods: List[Any], + all_modifications: List[Any], + entityCollector: Dict[str, Any], + ) -> List[Any]: entities = entityCollector["entity"] - for entity in entities: - if entity['type'] == "oauth": - for type in entity['options']['auth_type']: - for oauthEntity in entity['options'][type]: - [all_fields, fields_with_mods, all_modifications] = self._get_mods_data_for_single_entity(all_fields, fields_with_mods, all_modifications, oauthEntity) + for entity in entities: + if entity["type"] == "oauth": + for oauthType in entity["options"]["auth_type"]: + for oauthEntity in entity["options"][oauthType]: + [ + all_fields, + fields_with_mods, + all_modifications, + ] = self._get_mods_data_for_single_entity( + all_fields, fields_with_mods, all_modifications, oauthEntity + ) else: - [all_fields, fields_with_mods, all_modifications] = self._get_mods_data_for_single_entity(all_fields, fields_with_mods, all_modifications, entity) + [ + all_fields, + fields_with_mods, + all_modifications, + ] = self._get_mods_data_for_single_entity( + all_fields, fields_with_mods, all_modifications, entity + ) return [fields_with_mods, all_modifications, all_fields] def _validate_field_modifications(self) -> None: + """ + Validates: + Circular dependencies + If fields try modify itself + If fields try modify unexisting field + """ pages = self._config["pages"] - configuration = pages["configuration"] - tabs = configuration["tabs"] - - # lists initialised here as fields need to be unique across configuration page - fields_with_mods_config = [] - all_modifications_config = [] - all_fields_config = [] - for tab in tabs: - [fields_with_mods_config, all_modifications_config, all_fields_config] = self._get_all_modifiction_data(all_fields_config,fields_with_mods_config,all_modifications_config,tab) - - self._check_if_cilcular(all_fields_config, fields_with_mods_config, all_modifications_config) + if "configuration" in pages: + configuration = pages["configuration"] + tabs = configuration["tabs"] + + # lists initialised here as fields need to be unique across configuration page + fields_with_mods_config: List[Any] = [] + all_modifications_config: List[Any] = [] + all_fields_config: List[str] = [] + + for tab in tabs: + [ + fields_with_mods_config, + all_modifications_config, + all_fields_config, + ] = self._get_all_modifiction_data( + all_fields_config, + fields_with_mods_config, + all_modifications_config, + tab, + ) - inputs = pages['inputs'] - services = inputs["services"] + self._check_if_cilcular( + all_fields_config, fields_with_mods_config, all_modifications_config + ) - # lists initialised here as fields need to be unique across inputs page - fields_with_mods_inputs = [] - all_modifications_inputs = [] - all_fields_inputs = [] - for service in services: - [fields_with_mods_inputs, all_modifications_inputs, all_fields_inputs] = self._get_all_modifiction_data(all_fields_config,fields_with_mods_config,all_modifications_config,service) + if "inputs" in pages: + inputs = pages["inputs"] + services = inputs["services"] + + # lists initialised here as fields need to be unique across inputs page + fields_with_mods_inputs = [] + all_modifications_inputs = [] + all_fields_inputs = [] + for service in services: + [ + fields_with_mods_inputs, + all_modifications_inputs, + all_fields_inputs, + ] = self._get_all_modifiction_data( + all_fields_config, + fields_with_mods_config, + all_modifications_config, + service, + ) - self._check_if_cilcular(all_fields_inputs, fields_with_mods_inputs, all_modifications_inputs) + self._check_if_cilcular( + all_fields_inputs, fields_with_mods_inputs, all_modifications_inputs + ) def validate(self) -> None: self._validate_config_against_schema() From 818dcb54c645d135537ae7937536d7f6a31c2a5f Mon Sep 17 00:00:00 2001 From: Szymon Oleksy Date: Mon, 26 Feb 2024 01:11:50 +0100 Subject: [PATCH 3/3] test: refactor plus tests for mods on value --- .../global_config_validator.py | 106 ++++----- tests/unit/test_global_config_validator.py | 42 ++++ ...h_modification_circular_modifications.json | 214 ++++++++++++++++++ ...ig_with_modification_for_field_itself.json | 78 +++++++ ...h_modification_for_unexisiting_fields.json | 78 +++++++ ...fig_with_modification_on_value_change.json | 203 +++++++++++++++++ 6 files changed, 660 insertions(+), 61 deletions(-) create mode 100644 tests/unit/testdata/invalid_config_with_modification_circular_modifications.json create mode 100644 tests/unit/testdata/invalid_config_with_modification_for_field_itself.json create mode 100644 tests/unit/testdata/invalid_config_with_modification_for_unexisiting_fields.json create mode 100644 tests/unit/testdata/valid_config_with_modification_on_value_change.json diff --git a/splunk_add_on_ucc_framework/global_config_validator.py b/splunk_add_on_ucc_framework/global_config_validator.py index e2a33a07f..e789ed82b 100644 --- a/splunk_add_on_ucc_framework/global_config_validator.py +++ b/splunk_add_on_ucc_framework/global_config_validator.py @@ -19,6 +19,8 @@ import re from typing import Any, Dict, List import logging +import itertools + import jsonschema @@ -609,10 +611,7 @@ def _check_if_cilcular( fields_with_mods: List[Any], modifications: List[Any], ) -> None: - visited = {} - - for field in all_entity_fields: - visited[field] = "not_visited" + visited = {field: "not_visited" for field in all_entity_fields} for start_field in fields_with_mods: # DFS algorithm for all fields with modifications @@ -620,9 +619,8 @@ def _check_if_cilcular( modifications, visited, all_entity_fields, start_field ) + @staticmethod def _get_mods_data_for_single_entity( - self, - all_fields: List[Any], fields_with_mods: List[Any], all_modifications: List[Any], entity: Dict[str, Any], @@ -630,7 +628,6 @@ def _get_mods_data_for_single_entity( """ Add modification entity data to lists and returns them """ - all_fields.append(entity["field"]) if "modifyFieldsOnValue" in entity: influenced_fields = set() fields_with_mods.append(entity["field"]) @@ -640,35 +637,43 @@ def _get_mods_data_for_single_entity( all_modifications.append( {"fieldId": entity["field"], "influenced_fields": influenced_fields} ) - return [all_fields, fields_with_mods, all_modifications] + return [fields_with_mods, all_modifications] - def _get_all_modifiction_data( - self, - all_fields: List[Any], - fields_with_mods: List[Any], - all_modifications: List[Any], - entityCollector: Dict[str, Any], + @staticmethod + def _get_all_entities( + collections: List[Dict[str, Any]], ) -> List[Any]: - entities = entityCollector["entity"] - for entity in entities: + all_fields = [] + + tab_entities: List[Any] = [ + el.get("entity") for el in collections if el.get("entity") + ] + all_entities = list(itertools.chain.from_iterable(tab_entities)) + + for entity in all_entities: if entity["type"] == "oauth": for oauthType in entity["options"]["auth_type"]: for oauthEntity in entity["options"][oauthType]: - [ - all_fields, - fields_with_mods, - all_modifications, - ] = self._get_mods_data_for_single_entity( - all_fields, fields_with_mods, all_modifications, oauthEntity - ) + all_fields.append(oauthEntity) else: - [ - all_fields, - fields_with_mods, - all_modifications, - ] = self._get_mods_data_for_single_entity( - all_fields, fields_with_mods, all_modifications, entity - ) + all_fields.append(entity) + + return all_fields + + def _get_all_modifiction_data( + self, + collections: List[Dict[str, Any]], + ) -> List[Any]: + fields_with_mods: List[Any] = [] + all_modifications: List[Any] = [] + all_fields: List[str] = [] + + entities = self._get_all_entities(collections) + for entity in entities: + self._get_mods_data_for_single_entity( + fields_with_mods, all_modifications, entity + ) + all_fields.append(entity["field"]) return [fields_with_mods, all_modifications, all_fields] @@ -685,22 +690,11 @@ def _validate_field_modifications(self) -> None: configuration = pages["configuration"] tabs = configuration["tabs"] - # lists initialised here as fields need to be unique across configuration page - fields_with_mods_config: List[Any] = [] - all_modifications_config: List[Any] = [] - all_fields_config: List[str] = [] - - for tab in tabs: - [ - fields_with_mods_config, - all_modifications_config, - all_fields_config, - ] = self._get_all_modifiction_data( - all_fields_config, - fields_with_mods_config, - all_modifications_config, - tab, - ) + ( + fields_with_mods_config, + all_modifications_config, + all_fields_config, + ) = self._get_all_modifiction_data(tabs) self._check_if_cilcular( all_fields_config, fields_with_mods_config, all_modifications_config @@ -710,21 +704,11 @@ def _validate_field_modifications(self) -> None: inputs = pages["inputs"] services = inputs["services"] - # lists initialised here as fields need to be unique across inputs page - fields_with_mods_inputs = [] - all_modifications_inputs = [] - all_fields_inputs = [] - for service in services: - [ - fields_with_mods_inputs, - all_modifications_inputs, - all_fields_inputs, - ] = self._get_all_modifiction_data( - all_fields_config, - fields_with_mods_config, - all_modifications_config, - service, - ) + ( + fields_with_mods_inputs, + all_modifications_inputs, + all_fields_inputs, + ) = self._get_all_modifiction_data(services) self._check_if_cilcular( all_fields_inputs, fields_with_mods_inputs, all_modifications_inputs diff --git a/tests/unit/test_global_config_validator.py b/tests/unit/test_global_config_validator.py index fd45f2aa0..ca522260d 100644 --- a/tests/unit/test_global_config_validator.py +++ b/tests/unit/test_global_config_validator.py @@ -341,3 +341,45 @@ def test_config_validation_when_error(filename, is_yaml, exception_message): (msg,) = exc_info.value.args assert msg == exception_message + + +def test_config_validation_modifications_on_change(): + global_config_path = helpers.get_testdata_file_path( + "valid_config_with_modification_on_value_change.json" + ) + global_config = global_config_lib.GlobalConfig(global_config_path, False) + + validator = GlobalConfigValidator(helpers.get_path_to_source_dir(), global_config) + + with does_not_raise(): + validator.validate() + + +@pytest.mark.parametrize( + "filename,raise_message", + [ + ( + "invalid_config_with_modification_for_field_itself.json", + "Field 'text1' tries to modify itself", + ), + ( + "invalid_config_with_modification_for_unexisiting_fields.json", + "Modification in field 'text1' for not existing field 'text2'", + ), + ( + "invalid_config_with_modification_circular_modifications.json", + "Circular modifications for field 'text1' in field 'text7'", + ), + ], +) +def test_invalid_config_modifications_correct_raises(filename, raise_message): + global_config_path = helpers.get_testdata_file_path(filename) + global_config = global_config_lib.GlobalConfig(global_config_path, False) + + validator = GlobalConfigValidator(helpers.get_path_to_source_dir(), global_config) + + with pytest.raises(GlobalConfigValidatorException) as exc_info: + validator.validate() + + (msg,) = exc_info.value.args + assert msg == raise_message diff --git a/tests/unit/testdata/invalid_config_with_modification_circular_modifications.json b/tests/unit/testdata/invalid_config_with_modification_circular_modifications.json new file mode 100644 index 000000000..3c8e0406f --- /dev/null +++ b/tests/unit/testdata/invalid_config_with_modification_circular_modifications.json @@ -0,0 +1,214 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "account", + "table": { + "actions": [ + "edit", + "delete", + "clone" + ], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Auth Type", + "field": "auth_type" + } + ] + }, + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "string", + "errorMsg": "Length of ID should be between 1 and 50", + "minLength": 1, + "maxLength": 50 + }, + { + "type": "regex", + "errorMsg": "Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + } + ], + "field": "name", + "help": "Enter a unique name for this account.", + "required": true + }, + { + "type": "text", + "label": "text1 MODIFICATION TEST", + "help": "text1", + "field": "text1", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text2", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text3", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text2 MODIFICATION TEST", + "help": "text1", + "field": "text2", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text3", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text4", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text3 MODIFICATION TEST", + "help": "text1", + "field": "text3" + }, + { + "type": "text", + "label": "text4 MODIFICATION TEST", + "help": "text1", + "field": "text4", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text5", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text5", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text5 MODIFICATION TEST", + "help": "text1", + "field": "text5", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text6", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text6", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text6 MODIFICATION TEST", + "help": "text1", + "field": "text6", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text7", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text7", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text7 MODIFICATION TEST", + "help": "text1", + "field": "text7", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text1", + "disabled": false + } + ] + } + ] + } + ], + "title": "Account" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "5.39.0Ra9e840a6", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "_uccVersion": "5.39.0" + } +} diff --git a/tests/unit/testdata/invalid_config_with_modification_for_field_itself.json b/tests/unit/testdata/invalid_config_with_modification_for_field_itself.json new file mode 100644 index 000000000..eb7145f94 --- /dev/null +++ b/tests/unit/testdata/invalid_config_with_modification_for_field_itself.json @@ -0,0 +1,78 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "account", + "table": { + "actions": [ + "edit", + "delete", + "clone" + ], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Auth Type", + "field": "auth_type" + } + ] + }, + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "string", + "errorMsg": "Length of ID should be between 1 and 50", + "minLength": 1, + "maxLength": 50 + }, + { + "type": "regex", + "errorMsg": "Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + } + ], + "field": "name", + "help": "Enter a unique name for this account.", + "required": true + }, + { + "type": "text", + "label": "text1 MODIFICATION TEST", + "help": "text1", + "field": "text1", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text1", + "disabled": false + } + ] + } + ] + } + ], + "title": "Account" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "5.39.0Ra9e840a6", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "_uccVersion": "5.39.0" + } +} diff --git a/tests/unit/testdata/invalid_config_with_modification_for_unexisiting_fields.json b/tests/unit/testdata/invalid_config_with_modification_for_unexisiting_fields.json new file mode 100644 index 000000000..63f12ce8d --- /dev/null +++ b/tests/unit/testdata/invalid_config_with_modification_for_unexisiting_fields.json @@ -0,0 +1,78 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "account", + "table": { + "actions": [ + "edit", + "delete", + "clone" + ], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Auth Type", + "field": "auth_type" + } + ] + }, + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "string", + "errorMsg": "Length of ID should be between 1 and 50", + "minLength": 1, + "maxLength": 50 + }, + { + "type": "regex", + "errorMsg": "Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + } + ], + "field": "name", + "help": "Enter a unique name for this account.", + "required": true + }, + { + "type": "text", + "label": "text1 MODIFICATION TEST", + "help": "text1", + "field": "text1", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text2", + "disabled": false + } + ] + } + ] + } + ], + "title": "Account" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "5.39.0Ra9e840a6", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "_uccVersion": "5.39.0" + } +} diff --git a/tests/unit/testdata/valid_config_with_modification_on_value_change.json b/tests/unit/testdata/valid_config_with_modification_on_value_change.json new file mode 100644 index 000000000..f39158c62 --- /dev/null +++ b/tests/unit/testdata/valid_config_with_modification_on_value_change.json @@ -0,0 +1,203 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "account", + "table": { + "actions": [ + "edit", + "delete", + "clone" + ], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Auth Type", + "field": "auth_type" + } + ] + }, + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "string", + "errorMsg": "Length of ID should be between 1 and 50", + "minLength": 1, + "maxLength": 50 + }, + { + "type": "regex", + "errorMsg": "Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + } + ], + "field": "name", + "help": "Enter a unique name for this account.", + "required": true + }, + { + "type": "text", + "label": "text1 MODIFICATION TEST", + "help": "text1", + "field": "text1", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text2", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text3", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text2 MODIFICATION TEST", + "help": "text1", + "field": "text2", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text3", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text4", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text3 MODIFICATION TEST", + "help": "text1", + "field": "text3" + }, + { + "type": "text", + "label": "text4 MODIFICATION TEST", + "help": "text1", + "field": "text4", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text5", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text5", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text5 MODIFICATION TEST", + "help": "text1", + "field": "text5", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text6", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text6", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text6 MODIFICATION TEST", + "help": "text1", + "field": "text6", + "modifyFieldsOnValue": [ + { + "fieldValue": 1, + "fieldsToModify": [ + { + "fieldId": "text7", + "disabled": false + } + ] + }, + { + "fieldValue": 0, + "fieldsToModify": [ + { + "fieldId": "text7", + "disabled": true + } + ] + } + ] + }, + { + "type": "text", + "label": "text7 MODIFICATION TEST", + "help": "text1", + "field": "text7" + } + ], + "title": "Account" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "5.39.0Ra9e840a6", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "_uccVersion": "5.39.0" + } +}