From 1e146c95332d6a9a5a211e8f92aacc0a494f86d8 Mon Sep 17 00:00:00 2001 From: Wojciech Zyla Date: Mon, 3 Jul 2023 15:28:45 +0200 Subject: [PATCH] fix: add conditional profiles fix: add conversion of conditional profiles fix: validate if any varBind, pattern or conditions were added fix: remove filed and patterns columns from profiles screen, rename fileds in profile form fix: rename UI fileds in profiles change conversion to int and float --- .../SC4SNMP_UI_backend/common/conversions.py | 87 +++++++-- .../src/components/menu_header/Header.jsx | 1 + .../components/profiles/AddProfileModal.jsx | 10 +- .../src/components/profiles/Condition.jsx | 35 +++- .../src/components/profiles/Conditional.jsx | 177 ++++++++++++++++++ .../src/components/profiles/Conditionaln.jsx | 116 ++++++++++++ .../src/components/profiles/FieldPatterns.jsx | 16 +- .../src/components/profiles/ProfilesList.jsx | 12 +- .../src/components/profiles/VarBinds.jsx | 30 +-- .../validation/ValidateProfiles.jsx | 95 +++++++++- .../manager/src/store/profile-contxt.jsx | 3 + .../src/store/profiles-validation-contxt.jsx | 97 ++++++++-- 12 files changed, 580 insertions(+), 99 deletions(-) create mode 100644 frontend/packages/manager/src/components/profiles/Conditional.jsx create mode 100644 frontend/packages/manager/src/components/profiles/Conditionaln.jsx diff --git a/backend/SC4SNMP_UI_backend/common/conversions.py b/backend/SC4SNMP_UI_backend/common/conversions.py index 0f2a3ab..bab5693 100644 --- a/backend/SC4SNMP_UI_backend/common/conversions.py +++ b/backend/SC4SNMP_UI_backend/common/conversions.py @@ -46,6 +46,29 @@ def ui2backend(self, document: dict, **kwargs): class ProfileConversion(Conversion): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.__backend2ui_conditional_operations = { + "lt": "less than", + "gt": "greater than", + "equal": "equal", + "in": "in" + } + self.__ui2backend_conditional_operations = {} + for key, value in self.__backend2ui_conditional_operations.items(): + self.__ui2backend_conditional_operations[value] = key + + self.__backend2ui_profile_types = { + "field": "smart" + } + self.__ui2backend_profile_types = {} + for key, value in self.__backend2ui_profile_types.items(): + self.__ui2backend_profile_types[value] = key + + def __string_value_to_numeric(self, value: str): + if value.isnumeric(): + value = int(value) + elif value.replace(".", "").isnumeric(): + value = float(value) + return value def _backend2ui_map(self, document: dict, **kwargs): profile_name = None @@ -67,20 +90,42 @@ def _backend2ui_map(self, document: dict, **kwargs): if "condition" in document[profile_name]: backend_condition = document[profile_name]["condition"] - condition_type = backend_condition["type"] - field = backend_condition["field"] if condition_type == "field" else "" + condition_type = self.__backend2ui_profile_types[backend_condition["type"]] + field = backend_condition["field"] if backend_condition["type"] == "field" else "" patterns = [{"pattern": p} for p in backend_condition["patterns"]] \ - if condition_type == "field" else [] + if backend_condition["type"] == "field" else [] conditions = { "condition": condition_type, "field": field, - "patterns": patterns + "patterns": patterns, + "conditions": [] + } + elif "conditions" in document[profile_name]: + conditional = [] + for back_condition in document[profile_name]["conditions"]: + field = back_condition["field"] + operation = self.__backend2ui_conditional_operations[back_condition["operation"]] + value = [] + if operation == "in": + for v in back_condition["value"]: + value.append(str(v)) + else: + value.append(str(back_condition["value"])) + conditional.append( + {"field": field, "operation": operation, "value": value} + ) + conditions = { + "condition": "conditional", + "field": "", + "patterns": [], + "conditions": conditional } else: conditions = { - "condition": "None", + "condition": "standard", "field": "", - "patterns": [] + "patterns": [], + "conditions": [] } result = { "_id": str(document["_id"]), @@ -92,16 +137,30 @@ def _backend2ui_map(self, document: dict, **kwargs): return result def _ui2backend_map(self, document: dict, **kwargs): - if document['conditions']['condition'] == "field": - conditions = { + conditions = None + condition = None + if document['conditions']['condition'] == "smart": + condition = { 'type': 'field', 'field': document['conditions']['field'], 'patterns': [el['pattern'] for el in document['conditions']['patterns']] } - elif document['conditions']['condition'] == "None": - conditions = None - else: - conditions = { + elif document['conditions']['condition'] == "conditional": + conditions = [] + for ui_condition in document['conditions']['conditions']: + field = ui_condition["field"] + operation = self.__ui2backend_conditional_operations[ui_condition["operation"]] + if operation == "in": + value = [] + for v in ui_condition["value"]: + value.append(self.__string_value_to_numeric(v)) + else: + value = self.__string_value_to_numeric(ui_condition["value"][0]) + conditions.append( + {"field": field, "operation": operation, "value": value} + ) + elif document['conditions']['condition'] != "standard": + condition = { 'type': document['conditions']['condition'] } var_binds = [] @@ -119,8 +178,10 @@ def _ui2backend_map(self, document: dict, **kwargs): 'varBinds': var_binds } } + if condition is not None: + item[document['profileName']].update({'condition': condition}) if conditions is not None: - item[document['profileName']].update({'condition': conditions}) + item[document['profileName']].update({'conditions': conditions}) return item diff --git a/frontend/packages/manager/src/components/menu_header/Header.jsx b/frontend/packages/manager/src/components/menu_header/Header.jsx index 6c7e37d..9bf86a0 100644 --- a/frontend/packages/manager/src/components/menu_header/Header.jsx +++ b/frontend/packages/manager/src/components/menu_header/Header.jsx @@ -25,6 +25,7 @@ function Header(){ ProfCtx.setCondition("None"); ProfCtx.setConditionField(""); ProfCtx.setConditionPatterns([]); + ProfCtx.setConditional([]); ProfCtx.setAddOpen(true); ProfCtx.setIsEdit(false); }; diff --git a/frontend/packages/manager/src/components/profiles/AddProfileModal.jsx b/frontend/packages/manager/src/components/profiles/AddProfileModal.jsx index 1f04be6..dfdb42b 100644 --- a/frontend/packages/manager/src/components/profiles/AddProfileModal.jsx +++ b/frontend/packages/manager/src/components/profiles/AddProfileModal.jsx @@ -81,7 +81,8 @@ function AddProfileModal(props) { conditions: { condition: ProfCtx.condition, field: ProfCtx.conditionField, - patterns: ProfCtx.conditionPatterns + patterns: ProfCtx.conditionPatterns, + conditions: ProfCtx.conditional } }; @@ -140,7 +141,12 @@ function AddProfileModal(props) { - +
+ + {((ValCtx.varBindsExistErrors) ? +

{ValCtx.varBindsExistErrors}

+ : null)} +
diff --git a/frontend/packages/manager/src/components/profiles/Condition.jsx b/frontend/packages/manager/src/components/profiles/Condition.jsx index 26cdb34..916efd6 100644 --- a/frontend/packages/manager/src/components/profiles/Condition.jsx +++ b/frontend/packages/manager/src/components/profiles/Condition.jsx @@ -8,6 +8,7 @@ import P from "@splunk/react-ui/Paragraph"; import FieldPatterns from "./FieldPatterns"; import {validationGroup, validationMessage} from "../../styles/ValidationStyles"; import ProfilesValidationContxt from "../../store/profiles-validation-contxt"; +import Conditional from "./Conditional"; function Condition(props){ const ProfCtx = useContext(ProfileContext); @@ -23,35 +24,49 @@ function Condition(props){ return(
- { - ProfCtx.condition === 'field' ? ( + ProfCtx.condition === 'smart' ? (
- +
{((ValCtx.conditionFieldErrors) ? ValCtx.conditionFieldErrors.map((el) =>

{el}

) :

)}

- - + +
+ + {((ValCtx.patternsExistErrors) ? +

{ValCtx.patternsExistErrors}

+ : null)} +
) : null } - {ProfCtx.condition === 'conditional' ? ( -
- ) : null} + { + ProfCtx.condition === 'conditional' ? ( + +
+ + {((ValCtx.conditionalExistErrors) ? +

{ValCtx.conditionalExistErrors}

+ : null)} +
+
+ ) : null + }
) } diff --git a/frontend/packages/manager/src/components/profiles/Conditional.jsx b/frontend/packages/manager/src/components/profiles/Conditional.jsx new file mode 100644 index 0000000..378a037 --- /dev/null +++ b/frontend/packages/manager/src/components/profiles/Conditional.jsx @@ -0,0 +1,177 @@ +import React, {useContext, useEffect, useState} from 'react'; +import {createDOMID} from '@splunk/ui-utils/id'; +import Text from "@splunk/react-ui/Text"; +import P from "@splunk/react-ui/Paragraph"; +import FormRows from "@splunk/react-ui/FormRows"; +import ProfileContext from "../../store/profile-contxt"; +import {validationGroup, validationMessage} from "../../styles/ValidationStyles"; +import ProfilesValidationContxt from "../../store/profiles-validation-contxt"; +import Select from "@splunk/react-ui/Select"; +import Card from '@splunk/react-ui/Card'; +import ConditionalIn from "./Conditionaln"; + +function Conditional(props){ + const ProfCtx = useContext(ProfileContext); + const ValCtx = useContext(ProfilesValidationContxt); + const [indices, setIndices] = useState({}); + const [reload, setReload] = useState(true); + const [rowItems, setRowItems] = useState([]); + + const handleRequestRemove = (e, { index }) => { + const indicesCopy = {...indices}; + let keyToDelete; + const conditionsCopy = ProfCtx.conditional; + conditionsCopy.splice(index, 1); + + const indicesKeys = Object.keys(indices) + indicesKeys.forEach((keyID) => { + if (indices[`${keyID}`] > index){ + indicesCopy[`${keyID}`] -= 1; + } + if (indices[`${keyID}`] === index){ + keyToDelete = keyID; + } + }) + delete indicesCopy[`${keyToDelete}`]; + + // Update field errors indexes after deleting an element + const errorField = ValCtx.conditionalFieldErrors; + if (errorField){ + const errorKeys = Object.keys(errorField); + errorKeys.forEach((errorID) => { + if (Number(errorID) === index){delete errorField[Number(errorID)];} + if (Number(errorID) > index) { + errorField[Number(errorID)-1] = errorField[Number(errorID)]; + delete errorField[Number(errorID)] + } + }) + ValCtx.setConditionalFieldErrors(errorField); + } + + // Update values errors indexes after deleting an element + const errorValue = ValCtx.conditionalValuesErrors; + if (errorValue){ + const errorKeys = Object.keys(errorValue); + errorKeys.forEach((errorID) => { + if (Number(errorID) === index){delete errorValue[Number(errorID)];} + if (Number(errorID) > index) { + errorValue[Number(errorID)-1] = errorValue[Number(errorID)]; + delete errorValue[Number(errorID)] + } + }) + ValCtx.setConditionalValuesErrors(errorValue); + } + + setRowItems((prev) => FormRows.removeRow(index, prev)); + setIndices(indicesCopy); + ProfCtx.setConditionPatterns(conditionsCopy); + setReload((prev)=>{return !prev}); + }; + + const handleField = (index, e) => { + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[index].field = e.target.value + ProfCtx.setConditional(conditionalCopy); + } + + const handleOperation = (index, operationValue) => { + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[index].operation = operationValue + ProfCtx.setConditional(conditionalCopy); + setReload((prev)=>{return !prev}); + } + + const handleValue = (index, e) => { + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[index].value[0] = e.target.value + ProfCtx.setConditional(conditionalCopy); + } + + const handleRequestAdd = () => { + const indicesCopy = indices; + const newIndex = rowItems.length; + const keyID = createDOMID(); + const conditionalCopy = ProfCtx.conditional; + conditionalCopy.push({field: "", operation: "equal", value: [""]}); + indicesCopy[`${keyID}`] = newIndex; + setIndices(indicesCopy); + ProfCtx.setConditional(conditionalCopy); + setReload((prev)=>{return !prev}); + }; + + const loadFormRows = () => { + let index = -1; + const newIndices = {}; + const items = ProfCtx.conditional.map(condition => { + index += 1; + const indexCopy = index; + const keyID = createDOMID(); + newIndices[`${keyID}`] = indexCopy; + return ( + + + +
+ handleField(newIndices[`${keyID}`], e)} + error={((ValCtx.conditionalFieldErrors && newIndices[`${keyID}`] in ValCtx.conditionalFieldErrors))}/> + {((ValCtx.conditionalFieldErrors && newIndices[`${keyID}`] in ValCtx.conditionalFieldErrors) ? + ValCtx.conditionalFieldErrors[newIndices[`${keyID}`]].map((el) => +

{el}

) :

)} +

+
+ +

+

+ { + ProfCtx.conditional[newIndices[`${keyID}`]].operation === 'in' ? ( +
+ + {((ValCtx.conditionalValuesExistErrors && newIndices[`${keyID}`] in ValCtx.conditionalValuesExistErrors) ? +

{ValCtx.conditionalValuesExistErrors[newIndices[`${keyID}`]]}

+ : null)} +
+ ) : ( +
+ handleValue(newIndices[`${keyID}`], e)} + error={((ValCtx.conditionalValuesErrors && newIndices[`${keyID}`] in ValCtx.conditionalValuesErrors + && 0 in ValCtx.conditionalValuesErrors[newIndices[`${keyID}`]]))}/> + {((ValCtx.conditionalValuesErrors && newIndices[`${keyID}`] in ValCtx.conditionalValuesErrors + && 0 in ValCtx.conditionalValuesErrors[newIndices[`${keyID}`]]) ? + ValCtx.conditionalValuesErrors[newIndices[`${keyID}`]][0].map((el) => +

{el}

) :

)} +

+ ) + } +
+
+
+ ); + }); + setIndices(newIndices); + return items; + } + + useEffect(() => { + let isMounted = true; + setRowItems(loadFormRows()); + return () => { isMounted = false } + }, [props.newSubmit, reload]); + + return ( + + {rowItems} + + ) +} + +export default Conditional; diff --git a/frontend/packages/manager/src/components/profiles/Conditionaln.jsx b/frontend/packages/manager/src/components/profiles/Conditionaln.jsx new file mode 100644 index 0000000..05b0f6a --- /dev/null +++ b/frontend/packages/manager/src/components/profiles/Conditionaln.jsx @@ -0,0 +1,116 @@ +import React, {useContext, useEffect, useState} from 'react'; +import {createDOMID} from '@splunk/ui-utils/id'; +import Text from "@splunk/react-ui/Text"; +import P from "@splunk/react-ui/Paragraph"; +import FormRows from "@splunk/react-ui/FormRows"; +import ProfileContext from "../../store/profile-contxt"; +import {validationGroup, validationMessage} from "../../styles/ValidationStyles"; +import ProfilesValidationContxt from "../../store/profiles-validation-contxt"; + +function ConditionalIn(props){ + const ProfCtx = useContext(ProfileContext); + const ValCtx = useContext(ProfilesValidationContxt); + const [indices, setIndices] = useState({}); + const [reload, setReload] = useState(true); + const [rowItems, setRowItems] = useState([]); + + const handleRequestRemove = (e, { index }) => { + const indicesCopy = {...indices}; + let keyToDelete; + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[props.conditionIndex].value.splice(index, 1); + + const indicesKeys = Object.keys(indices) + indicesKeys.forEach((keyID) => { + if (indices[`${keyID}`] > index){ + indicesCopy[`${keyID}`] -= 1; + } + if (indices[`${keyID}`] === index){ + keyToDelete = keyID; + } + }) + delete indicesCopy[`${keyToDelete}`]; + + // Update errors indexes after deleting an element + const errors = ValCtx.conditionalValuesErrors; + if (errors && errors.hasOwnProperty(props.conditionIndex)){ + const error = errors[props.conditionIndex] + const errorKeys = Object.keys(error); + errorKeys.forEach((errorID) => { + if (Number(errorID) === index){delete error[Number(errorID)];} + if (Number(errorID) > index) { + error[Number(errorID)-1] = error[Number(errorID)]; + delete error[Number(errorID)] + } + }) + errors[props.conditionIndex] = error + ValCtx.setConditionalValuesErrors(errors); + } + setRowItems((prev) => FormRows.removeRow(index, prev)); + setIndices(indicesCopy); + ProfCtx.setConditional(conditionalCopy); + setReload((prev)=>{return !prev}); + }; + + const handleItemValue = (index, e) => { + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[props.conditionIndex].value[index] = e.target.value + ProfCtx.setConditional(conditionalCopy); + } + + const handleRequestAdd = () => { + const indicesCopy = indices; + const newIndex = rowItems.length; + const keyID = createDOMID(); + const conditionalCopy = ProfCtx.conditional; + conditionalCopy[props.conditionIndex].value.push(""); + indicesCopy[`${keyID}`] = newIndex; + setIndices(indicesCopy); + ProfCtx.setConditionPatterns(conditionalCopy); + setReload((prev)=>{return !prev}); + }; + + const loadFormRows = () => { + let index = -1; + const newIndices = {} + const items = ProfCtx.conditional[props.conditionIndex].value.map(value => { + index += 1; + const indexCopy = index; + const keyID = createDOMID(); + newIndices[`${keyID}`] = indexCopy; + return ( + +
+ handleItemValue(newIndices[`${keyID}`], e)} + error={((ValCtx.conditionalValuesErrors && props.conditionIndex in ValCtx.conditionalValuesErrors)) && + newIndices[`${keyID}`] in ValCtx.conditionalValuesErrors[props.conditionIndex]}/> + {((ValCtx.conditionalValuesErrors && props.conditionIndex in ValCtx.conditionalValuesErrors + && newIndices[`${keyID}`] in ValCtx.conditionalValuesErrors[props.conditionIndex]) ? + ValCtx.conditionalValuesErrors[props.conditionIndex][newIndices[`${keyID}`]].map((el) => +

{el}

) :

)} +

+
+ ); + }); + setIndices(newIndices); + return items; + } + + useEffect(() => { + let isMounted = true; + setRowItems(loadFormRows()); + return () => { isMounted = false } + }, [props.newSubmit, reload]); + + return ( + + {rowItems} + + ) +} + +export default ConditionalIn; diff --git a/frontend/packages/manager/src/components/profiles/FieldPatterns.jsx b/frontend/packages/manager/src/components/profiles/FieldPatterns.jsx index 086d7e8..731e79d 100644 --- a/frontend/packages/manager/src/components/profiles/FieldPatterns.jsx +++ b/frontend/packages/manager/src/components/profiles/FieldPatterns.jsx @@ -63,20 +63,6 @@ function FieldPatterns(props){ const patternsCopy = ProfCtx.conditionPatterns; patternsCopy.push({pattern: ""}); indicesCopy[`${keyID}`] = newIndex; - setRowItems((prev) => - FormRows.addRow( - -
- handleItemValue(indices[`${keyID}`], e)} - error={((ValCtx.conditionPatternsErrors && indices[`${keyID}`] in ValCtx.conditionPatternsErrors))}/> - {((ValCtx.conditionPatternsErrors && indices[`${keyID}`] in ValCtx.conditionPatternsErrors) ? - ValCtx.conditionPatternsErrors[indices[`${keyID}`]].map((el) => -

{el}

) :

)} -

-
, - prev - ) - ); setIndices(indicesCopy); ProfCtx.setConditionPatterns(patternsCopy); setReload((prev)=>{return !prev}); @@ -91,7 +77,7 @@ function FieldPatterns(props){ const keyID = createDOMID(); newIndices[`${keyID}`] = indexCopy; return ( - +
handleItemValue(newIndices[`${keyID}`], e)} error={((ValCtx.conditionPatternsErrors && newIndices[`${keyID}`] in ValCtx.conditionPatternsErrors))}/> diff --git a/frontend/packages/manager/src/components/profiles/ProfilesList.jsx b/frontend/packages/manager/src/components/profiles/ProfilesList.jsx index 7ae3836..bc432f3 100644 --- a/frontend/packages/manager/src/components/profiles/ProfilesList.jsx +++ b/frontend/packages/manager/src/components/profiles/ProfilesList.jsx @@ -21,9 +21,6 @@ function getExpansionRow(row) { {/* Empty cell */} {/* Empty cell */} {/* Empty cell */} - {/* Empty cell */} - {row.conditions.patterns && row.conditions.patterns.map(value => -

{value.pattern}

)}
{row.varBinds.map((value) => (

{value.family}

@@ -52,12 +49,10 @@ function ProfilesList() { const columns = [ {sortKey: 'profileName', label: 'Profile name'}, {sortKey: 'frequency', label: 'Frequency'}, - {sortKey: 'condition', label: 'Condition'}, - {sortKey: 'field', label: 'Field'}, - {sortKey: 'patterns', label: 'Patterns'}, + {sortKey: 'profileType', label: 'Profile type'}, {sortKey: `mibFamily`, label: 'MIB family'}, {sortKey: `mibCategory`, label: 'MIB category'}, - {sortKey: `index`, label: 'Index'}, + {sortKey: `index`, label: 'MIB Index'}, {sortKey: `actions`, label: 'Actions'}, ]; @@ -112,6 +107,7 @@ function ProfilesList() { ProfCtx.setCondition(row.conditions.condition); ProfCtx.setConditionField(row.conditions.field); ProfCtx.setConditionPatterns(row.conditions.patterns); + ProfCtx.setConditional(row.conditions.conditions); ProfCtx.setIsEdit(true); ProfCtx.setAddOpen(true); }; @@ -186,8 +182,6 @@ function ProfilesList() { {row.profileName} {row.frequency} {row.conditions.condition} - {row.conditions.field} - {/* Condition patterns is empty in this view */} {(row.varBinds.length === 1) ? `1 MIB family` : `${row.varBinds.length} MIB families`} {/* MIB category is empty in this view */} diff --git a/frontend/packages/manager/src/components/profiles/VarBinds.jsx b/frontend/packages/manager/src/components/profiles/VarBinds.jsx index d3d8a06..288c23c 100644 --- a/frontend/packages/manager/src/components/profiles/VarBinds.jsx +++ b/frontend/packages/manager/src/components/profiles/VarBinds.jsx @@ -75,30 +75,6 @@ function VarBinds(props){ const varBindsCopy = ProfCtx.varBinds; varBindsCopy.push({family: "", category: "", index: ""}); indicesCopy[`${keyID}`] = newIndex; - setRowItems((prev) => - FormRows.addRow( - -
-
- handleItemValueFamily(indices[`${keyID}`], e)} - error={((ValCtx.varBindsErrors && indices[`${keyID}`] in ValCtx.varBindsErrors))}/> - handleItemValueCategory(indices[`${keyID}`], e)} - error={((ValCtx.varBindsErrors && indices[`${keyID}`] in ValCtx.varBindsErrors))}/> - handleItemValueIndex(indices[`${keyID}`], e)} - error={((ValCtx.varBindsErrors && indices[`${keyID}`] in ValCtx.varBindsErrors))}/> -
- {((ValCtx.varBindsErrors && indices[`${keyID}`] in ValCtx.varBindsErrors) ? - ValCtx.varBindsErrors[indices[`${keyID}`]].map((el) =>

{el}

) : -

)} -

-
, - prev - ) - ); setIndices(indicesCopy); ProfCtx.setVarBinds(varBindsCopy); setReload((prev)=>{return !prev}); @@ -116,13 +92,13 @@ function VarBinds(props){
- handleItemValueFamily(newIndices[`${keyID}`], e)} error={((ValCtx.varBindsErrors && newIndices[`${keyID}`] in ValCtx.varBindsErrors))}/> - handleItemValueCategory(newIndices[`${keyID}`], e)} error={((ValCtx.varBindsErrors && newIndices[`${keyID}`] in ValCtx.varBindsErrors))}/> - handleItemValueIndex(newIndices[`${keyID}`], e)} error={((ValCtx.varBindsErrors && newIndices[`${keyID}`] in ValCtx.varBindsErrors))}/>
diff --git a/frontend/packages/manager/src/components/validation/ValidateProfiles.jsx b/frontend/packages/manager/src/components/validation/ValidateProfiles.jsx index 4bda4d4..b798df0 100644 --- a/frontend/packages/manager/src/components/validation/ValidateProfiles.jsx +++ b/frontend/packages/manager/src/components/validation/ValidateProfiles.jsx @@ -3,10 +3,20 @@ import React from 'react'; const validateProfiles = (validationObj) => { /* 'errors' is an object storing error messages for each field. Example data structure for 'errors': + profileName: ["message1", "message2"] -> list of messages for profile name + frequency: ["message1", "message2"] -> list of messages for frequency + conditionField: ["message1", "message2"] -> list of messages for 'field' input + conditionPatterns: {2: ["message1", "message2"], 5: ["message3"]} -> key indicates index of a pattern. Each pattern has its own list of errors + + conditionalField: {2: ["message1", "message2"], 5: ["message3"]} -> key indicates index of a conditional key. Each pattern has its own list of errors + + conditionalValues: {2:{1:["message1", "message2"], 3:["message1"]}, 6:{0:["message1"]}} -> key indicates index of a condition. Each condition + is represented by an object where keys correspond to indices of values of this condition. Each value has its own list of errors. + varBinds: {0: ["message1", "message2"], 3: ["message3"]} -> key indicates index of a varBind. Each varBind has its own list of errors */ @@ -15,7 +25,13 @@ const validateProfiles = (validationObj) => { frequency: [], conditionField: [], conditionPatterns: {}, - varBinds: {} + patternsExist: "", + conditionalField: {}, + conditionalValues: {}, + conditionalValuesExist: {}, + conditionalExist: "", + varBinds: {}, + varBindsExist: "" }; let isValid = true; @@ -28,21 +44,21 @@ const validateProfiles = (validationObj) => { errors.profileName.push("Profile Name can consist only of upper and lower english letters, " + "numbers and two special characters: '-' and '_'. No spaces are allowed."); isValid = false; - }; - }; + } + } // Validate Frequency if (validationObj.hasOwnProperty("frequency")){ if (!(Number.isInteger(validationObj.frequency) && validationObj.frequency > 0)){ errors.frequency.push("Frequency must be a positive integer"); isValid = false; - }; - }; + } + } let message; // Validate Condition if (validationObj.hasOwnProperty("conditions")){ - if (validationObj.conditions.condition === "field"){ + if (validationObj.conditions.condition === "smart"){ // Validate 'field' input if (validationObj.conditions.field.length === 0){ errors.conditionField.push("Field is required"); @@ -51,7 +67,12 @@ const validateProfiles = (validationObj) => { errors.conditionField.push("Field can consist only of upper and lower english letters, " + "numbers and three special characters: '.' '-' and '_'. No spaces are allowed."); isValid = false; - }; + } + // Check if patterns exist + if ( validationObj.conditions.patterns.length === 0){ + errors.patternsExist = "At least one patter must be specified."; + isValid = false; + } // Validate each pattern for (let i = 0; i < validationObj.conditions.patterns.length; i++){ if (validationObj.conditions.patterns[i].pattern.length === 0){ @@ -72,12 +93,66 @@ const validateProfiles = (validationObj) => { } isValid = false; }; */ - }; - }; - }; + } + }else if (validationObj.conditions.condition === "conditional"){ + // Validate 'field' input + let field + let values; + if (validationObj.conditions.conditions.length === 0){ + errors.conditionalExist = "At least one condition must be specified."; + isValid = false; + } + for (let i = 0; i < validationObj.conditions.conditions.length; i++){ + field = validationObj.conditions.conditions[i].field; + if (field.length === 0){ + if (errors.conditionalField.hasOwnProperty(i)){ + errors.conditionalField[i].push("Field is required"); + }else{ + errors.conditionalField[i] = ["Field is required"]; + } + isValid = false; + }else if (!field.match(/^[.a-zA-Z0-9_-]+$/)){ + if (errors.conditionalField.hasOwnProperty(i)){ + errors.conditionalField[i].push("Field can consist only of upper and lower english letters, " + + "numbers and three special characters: '.' '-' and '_'. No spaces are allowed."); + }else{ + errors.conditionalField[i] = ["Field can consist only of upper and lower english letters, " + + "numbers and three special characters: '.' '-' and '_'. No spaces are allowed."]; + } + isValid = false; + } + + values = validationObj.conditions.conditions[i].value; + if (values.length === 0){ + errors.conditionalValuesExist[i] = "At least one value must be specified."; + isValid = false; + } + let conditionsErrors = {} + for (let j = 0; j < values.length; j++){ + if (values[j].length === 0){ + if (errors.conditionalValues.hasOwnProperty(i) && errors.conditionalValues[i].hasOwnProperty(j)){ + errors.conditionalValues[i][j].push("Value is required"); + }else if (errors.conditionalValues.hasOwnProperty(i)){ + errors.conditionalValues[i][j] = ["Value is required"]; + } + else{ + conditionsErrors = {} + conditionsErrors[j] = ["Value is required"] + errors.conditionalValues[i] = conditionsErrors; + } + isValid = false; + } + } + } + } + } // Validate VarBinds if (validationObj.hasOwnProperty("varBinds")){ + if (validationObj.varBinds.length === 0){ + errors.varBindsExist = "At least one varBind must be specified."; + isValid = false; + } for (let i = 0; i < validationObj.varBinds.length; i++){ if (validationObj.varBinds[i].family.length === 0){ message = "MIB-Component is required"; diff --git a/frontend/packages/manager/src/store/profile-contxt.jsx b/frontend/packages/manager/src/store/profile-contxt.jsx index bda89c2..c39a9a4 100644 --- a/frontend/packages/manager/src/store/profile-contxt.jsx +++ b/frontend/packages/manager/src/store/profile-contxt.jsx @@ -24,6 +24,7 @@ export function ProfileContxtProvider(props) { const [condition, setCondition] = useState("None"); const [conditionField, setConditionField] = useState(""); const [conditionPatterns, setConditionPatterns] = useState([]); + const [conditional, setConditional] = useState([]); const [isEdit, setIsEdit] = useState(false); function profilesChangeHandler() { @@ -54,6 +55,8 @@ export function ProfileContxtProvider(props) { setConditionField, conditionPatterns, setConditionPatterns, + conditional, + setConditional, isEdit, setIsEdit }; diff --git a/frontend/packages/manager/src/store/profiles-validation-contxt.jsx b/frontend/packages/manager/src/store/profiles-validation-contxt.jsx index c533689..0ab7b24 100644 --- a/frontend/packages/manager/src/store/profiles-validation-contxt.jsx +++ b/frontend/packages/manager/src/store/profiles-validation-contxt.jsx @@ -6,8 +6,14 @@ export function ProfilesValidationContxtProvider(props){ const [profileNameErrors, setProfileNameErrors] = useState(null); const [frequencyErrors, setFrequencyErrors] = useState(null); const [varBindsErrors, setVarBindsErrors] = useState(null); + const [varBindsExistErrors, setVarBindsExistErrors] = useState(null); const [conditionFieldErrors, setConditionFieldErrors] = useState(null); const [conditionPatternsErrors, setConditionPatternsErrors] = useState(null); + const [patternsExistErrors, setPatternsExistErrors] = useState(null); + const [conditionalFieldErrors, setConditionalFieldErrors] = useState(null); + const [conditionalValuesErrors, setConditionalValuesErrors] = useState(null); + const [conditionalValuesExistErrors, setConditionalValuesExistErrors] = useState(null); + const [conditionalExistErrors, setConditionalExistErrors] = useState(null); const resetAllErrors = () =>{ setProfileNameErrors(null); @@ -15,6 +21,12 @@ export function ProfilesValidationContxtProvider(props){ setConditionFieldErrors(null); setConditionPatternsErrors(null); setVarBindsErrors(null); + setConditionalFieldErrors(null); + setConditionalValuesErrors(null); + setVarBindsExistErrors(null); + setPatternsExistErrors(null); + setConditionalValuesExistErrors(null); + setConditionalExistErrors(null); }; const resetErrors = (category) =>{ @@ -34,6 +46,24 @@ export function ProfilesValidationContxtProvider(props){ case "varBinds": setVarBindsErrors(null); break; + case "conditionalField": + setConditionalFieldErrors(null); + break; + case "conditionalValues": + setConditionalValuesErrors(null); + break; + case "patternsExist": + setPatternsExistErrors(null); + break; + case "conditionalValuesExist": + setConditionalValuesExistErrors(null); + break; + case "conditionalExist": + setConditionalExistErrors(null); + break; + case "varBindsExist": + setVarBindsExistErrors(null); + break; default: break; } @@ -56,25 +86,66 @@ export function ProfilesValidationContxtProvider(props){ case "varBinds": setVarBindsErrors(errors); break; + case "conditionalField": + setConditionalFieldErrors(errors); + break; + case "conditionalValues": + setConditionalValuesErrors(errors); + break; + case "patternsExist": + setPatternsExistErrors(errors); + break; + case "conditionalValuesExist": + setConditionalValuesExistErrors(errors); + break; + case "conditionalExist": + setConditionalExistErrors(errors); + break; + case "varBindsExist": + setVarBindsExistErrors(errors); + break; default: break; } }; const context = { - profileNameErrors: profileNameErrors, - setProfileNameErrors: setProfileNameErrors, - frequencyErrors: frequencyErrors, - setFrequencyErrors: setFrequencyErrors, - varBindsErrors: varBindsErrors, - setVarBindsErrors: setVarBindsErrors, - conditionFieldErrors: conditionFieldErrors, - setConditionFieldErrors: setConditionFieldErrors, - conditionPatternsErrors: conditionPatternsErrors, - setConditionPatternsErrors: setConditionPatternsErrors, - resetAllErrors: resetAllErrors, - resetErrors: resetErrors, - setErrors: setErrors, + profileNameErrors, + setProfileNameErrors, + + frequencyErrors, + setFrequencyErrors, + + varBindsErrors, + setVarBindsErrors, + + varBindsExistErrors, + setVarBindsExistErrors, + + conditionFieldErrors, + setConditionFieldErrors, + + conditionPatternsErrors, + setConditionPatternsErrors, + + patternsExistErrors, + setPatternsExistErrors, + + conditionalFieldErrors, + setConditionalFieldErrors, + + conditionalValuesErrors, + setConditionalValuesErrors, + + conditionalValuesExistErrors, + setConditionalValuesExistErrors, + + conditionalExistErrors, + setConditionalExistErrors, + + resetAllErrors, + resetErrors, + setErrors, }; return (