Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions ui/src/components/EventConfigEventCreate/BasicInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</FeatherInput>
<div class="spacer"></div>
<label class="label">Event Label:</label>
<div class="spacer"></div>
<div class="spacer"></div>
<FeatherInput
label=""
data-test="event-label"
Expand Down Expand Up @@ -122,6 +122,15 @@
/>
</div>
<div class="spacer"></div>
<div>
<MaskElements
data-test="mask-elements"
@setMaskElements="setMaskElements"
:maskElements="maskElements"
:errors="errors"
/>
</div>
<div class="spacer"></div>
<div class="action-container">
<FeatherButton
secondary
Expand Down Expand Up @@ -158,8 +167,9 @@ import { FeatherSelect, ISelectItemType } from '@featherds/select'
import { FeatherTextarea } from '@featherds/textarea'
import vkbeautify from 'vkbeautify'
import AlarmDataInfo from './AlarmDataInfo.vue'
import { DestinationOptions, SeverityOptions } from './constants'
import { DestinationOptions, MAX_MASK_ELEMENTS, SeverityOptions } from './constants'
import { validateEvent } from './eventValidator'
import MaskElements from './MaskElements.vue'

const router = useRouter()
const store = useEventModificationStore()
Expand All @@ -174,6 +184,9 @@ const snackbar = useSnackbar()
const destination = ref<ISelectItemType>({ _text: '', _value: '' })
const severity = ref<ISelectItemType>({ _text: '', _value: '' })
const alarmType = ref<ISelectItemType>({ _text: '', _value: '' })
const maskElements = ref<Array<{ name: ISelectItemType; value: string }>>([
{ name: { _text: '', _value: '' }, value: '' }
])
const addAlarmData = ref(false)
const reductionKey = ref('')
const autoClean = ref(false)
Expand All @@ -182,6 +195,10 @@ const clearKey = ref('')
const xmlContent = computed(() => {
return vkbeautify.xml(
`<event xmlns="http://xmlns.opennms.org/xsd/eventconf">
${maskElements.value.length > 0 ? `<mask>${maskElements.value.map((me) => `<maskelement>
<mename>${me.name._value}</mename>
<mevalue>${me.value}</mevalue>
</maskelement>`).join('')}</mask>` : ''}
<uei>${eventUei.value}</uei>
<event-label>${eventLabel.value}</event-label>
<descr><![CDATA[${eventDescription.value}]]></descr>
Expand Down Expand Up @@ -210,6 +227,9 @@ const resetValues = () => {
alarmType.value = { _text: '', _value: '' }
autoClean.value = false
clearKey.value = ''
maskElements.value = [
{ name: { _text: '', _value: '' }, value: '' }
]
}

const loadInitialValues = (val: EventConfigEvent | null) => {
Expand Down Expand Up @@ -242,6 +262,17 @@ const loadInitialValues = (val: EventConfigEvent | null) => {
autoClean.value = alarmDataElement?.getAttribute('auto-clean') === 'true' ? true : false
clearKey.value = alarmDataElement?.getAttribute('clear-key') || ''
}
const maskElementList = xmlDoc.getElementsByTagName('maskelement')
maskElements.value = []
for (let i = 0; i < maskElementList.length; i++) {
maskElements.value.push({
name: {
_text: maskElementList[i].getElementsByTagName('mename')[0].textContent || '',
_value: maskElementList[i].getElementsByTagName('mename')[0].textContent || ''
},
value: maskElementList[i].getElementsByTagName('mevalue')[0].textContent || ''
})
}
} else {
resetValues()
}
Expand Down Expand Up @@ -277,6 +308,35 @@ const setAlarmData = (key: string, value: any) => {
}
}

const setMaskElements = (key: string, value: any, index: number) => {
if (index === undefined) {
return
}

if (key === 'setName') {
maskElements.value[index].name = value
}

if (key === 'setValue') {
maskElements.value[index].value = value
}

if (key === 'addMaskRow') {
if (maskElements.value.length < MAX_MASK_ELEMENTS) {
maskElements.value.push({
name: { _text: '', _value: '' },
value: ''
})
} else {
snackbar.showSnackBar({ msg: `Maximum of ${MAX_MASK_ELEMENTS} mask elements allowed.`, error: true })
}
}

if (key === 'removeMaskRow') {
maskElements.value.splice(index, 1)
}
}

const handleSaveEvent = async () => {
if (!store.eventModificationState.eventConfigEvent || !store.selectedSource) {
return
Expand Down Expand Up @@ -335,7 +395,8 @@ watchEffect(() => {
reductionKey.value,
alarmType.value._value as string,
autoClean.value,
clearKey.value
clearKey.value,
maskElements.value
)
isValid.value = Object.keys(currentErrors).length === 0
errors.value = currentErrors as EventFormErrors
Expand Down
149 changes: 149 additions & 0 deletions ui/src/components/EventConfigEventCreate/MaskElements.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<template>
<div
class="mask-elements"
v-if="store.selectedSource && store.eventModificationState.eventConfigEvent"
>
<div class="section-content">
<div class="mask-elements-header">
<h3>Mask Elements</h3>
<FeatherButton
secondary
@click="$emit('setMaskElements', 'addMaskRow', null, -1)"
data-test="add-mask-row-button"
>
<FeatherIcon :icon="Add" />
Add
</FeatherButton>
</div>
<div
v-for="(row, index) in maskElements"
:key="index"
class="form-row"
>
<div class="dropdown">
<FeatherSelect
label="Element Name"
:options="availableMaskOptions(index)"
:error="errors.maskElements?.[index]?.name"
:modelValue="MaskElementNameOptions.find(
(o: ISelectItemType) => o._value === row.name._value
)"
@update:modelValue="$emit('setMaskElements', 'setName', $event, index)"
data-test="mask-element-name"
/>
</div>
<div class="input-field">
<FeatherInput
label="Element Value"
:model-value="row.value"
:error="errors.maskElements?.[index]?.value"
@update:model-value="$emit('setMaskElements', 'setValue', $event, index)"
data-test="mask-element-value"
/>
<FeatherButton
secondary
data-test="remove-mask-row-button"
@click="$emit('setMaskElements', 'removeMaskRow', null, index)"
>
<FeatherIcon :icon="Delete" />
</FeatherButton>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { useEventModificationStore } from '@/stores/eventModificationStore'
import { EventFormErrors } from '@/types/eventConfig'
import { FeatherButton } from '@featherds/button'
import { FeatherIcon } from '@featherds/icon'
import Add from '@featherds/icon/action/Add'
import Delete from '@featherds/icon/action/Delete'
import { FeatherInput } from '@featherds/input'
import { FeatherSelect, ISelectItemType } from '@featherds/select'
import { MaskElementNameOptions } from './constants'

defineEmits<{
(e: 'setMaskElements', key: string, value: any, index: number): void
}>()
const props = defineProps<{
maskElements: Array<{ name: ISelectItemType; value: string }>
errors: EventFormErrors
}>()

const store = useEventModificationStore()
const elements = ref<Array<{ name: ISelectItemType; value: string }>>([
{ name: { _text: '', _value: '' }, value: '' }
])

const availableMaskOptions = (index: number): ISelectItemType[] => {
const selectedNames = elements.value.map(r => r.name._value)
return MaskElementNameOptions.filter(option => {
const value = option._value as string
return (
!selectedNames.includes(value) ||
elements.value[index].name._value === value
)
})
}

watch(() => props, () => {
elements.value = props.maskElements
}, { immediate: true, deep: true })
</script>

<style scoped lang="scss">
@use '@featherds/styles/themes/variables';
@use '@featherds/styles/mixins/typography';

.mask-elements {
.mask-elements-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}

.form-row {
display: flex;
align-items: flex-start;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 10px;

.dropdown,
.input-field {
flex: 1;
}

.input-field {
display: flex;
align-items: flex-start;
gap: 10px;

>div {
flex: 1;
}

>button {
min-width: 40px !important;
height: 40px !important;
display: flex;
align-items: center;
justify-content: center;
line-height: 0px;

span {
svg {
fill: #a5021f;
font-size: 22px;
}
}
}
}
}

}
</style>

32 changes: 32 additions & 0 deletions ui/src/components/EventConfigEventCreate/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ISelectItemType } from '@featherds/select'

export const MAX_MASK_ELEMENTS = 12

export const statusOptions: ISelectItemType[] = [
{ _text: 'Enable', _value: 'enable' },
{ _text: 'Disable', _value: 'disable' }
Expand Down Expand Up @@ -41,6 +43,36 @@ export const DestinationOptions: ISelectItemType[] = [
{ _text: Destination.DiscardTraps, _value: Destination.DiscardTraps }
]

export enum MaskElementName {
Uei = 'uei',
Source = 'source',
NodeId = 'nodeid',
Host = 'host',
Interface = 'interface',
SnmpHost = 'snmphost',
Service = 'service',
Id = 'id',
Specific = 'specific',
Generic = 'generic',
Community = 'community',
Trapoid = 'trapoid'
}

export const MaskElementNameOptions: ISelectItemType[] = [
{ _text: MaskElementName.Uei, _value: MaskElementName.Uei },
{ _text: MaskElementName.Source, _value: MaskElementName.Source },
{ _text: MaskElementName.NodeId, _value: MaskElementName.NodeId },
{ _text: MaskElementName.Host, _value: MaskElementName.Host },
{ _text: MaskElementName.Interface, _value: MaskElementName.Interface },
{ _text: MaskElementName.SnmpHost, _value: MaskElementName.SnmpHost },
{ _text: MaskElementName.Service, _value: MaskElementName.Service },
{ _text: MaskElementName.Id, _value: MaskElementName.Id },
{ _text: MaskElementName.Specific, _value: MaskElementName.Specific },
{ _text: MaskElementName.Generic, _value: MaskElementName.Generic },
{ _text: MaskElementName.Community, _value: MaskElementName.Community },
{ _text: MaskElementName.Trapoid, _value: MaskElementName.Trapoid }
]

export enum AlarmType {
One = '1',
Two = '2',
Expand Down
26 changes: 25 additions & 1 deletion ui/src/components/EventConfigEventCreate/eventValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export const validateEvent = (
reductionKey: string,
alarmType: string,
autoClean: boolean,
clearKey: string
clearKey: string,
maskElements: Array<{ name?: { _text?: string; _value?: string }; value?: string }>
): EventFormErrors => {
const errors: EventFormErrors = {}

Expand Down Expand Up @@ -55,6 +56,29 @@ export const validateEvent = (
}
}

const maskElementErrors: Array<{ name?: string; value?: string }> = []
const namesSet = new Set<string>()
maskElements.forEach((element, index) => {
const elementErrors: { name?: string; value?: string } = {}
if (!element.name?._value || element.name?._value.trim() === '') {
elementErrors.name = 'Mask Element Name is required.'
} else if (namesSet.has(element.name._value)) {
elementErrors.name = 'Mask Element Name must be unique.'
} else {
namesSet.add(element.name._value)
}

if (!element.value || element.value.trim() === '') {
elementErrors.value = 'Mask Element Value is required.'
}

maskElementErrors[index] = elementErrors
})

if (maskElementErrors.some(err => Object.keys(err).length > 0)) {
errors.maskElements = maskElementErrors
}

return errors
}

Loading