fix(striker-ui): patch alert override CRUD operations; still has bugs

main
Tsu-ba-me 11 months ago
parent 93416b0913
commit 9a6a4f8b7c
  1. 132
      striker-ui/components/ManageMailRecipient/AddMailRecipientForm.tsx
  2. 38
      striker-ui/components/ManageMailRecipient/AlertOverrideInputGroup.tsx
  3. 26
      striker-ui/components/ManageMailRecipient/ManageAlertOverride.tsx
  4. 97
      striker-ui/components/ManageMailRecipient/ManageMailRecipient.tsx
  5. 7
      striker-ui/components/ManageMailRecipient/schema.ts
  6. 21
      striker-ui/types/ManageMailRecipient.d.ts

@ -111,6 +111,98 @@ const MAP_TO_LEVEL_LABEL: Record<number, string> = {
4: 'Info',
};
const getAlertOverrideRequestList = (
current: MailRecipientFormikMailRecipient,
initial?: MailRecipientFormikMailRecipient,
urlPrefix = '/alert-override',
): AlertOverrideRequest[] => {
const { uuid: mailRecipientUuid } = current;
if (!mailRecipientUuid) return [];
return Object.values(current.alertOverrides).reduce<AlertOverrideRequest[]>(
(previous, value) => {
if (value.delete && value.uuids) {
/**
* 1 or more existing records marked for removal.
*/
previous.push(
...Object.keys(value.uuids).map<AlertOverrideRequest>((uuid) => ({
method: 'delete',
url: `${urlPrefix}/${uuid}`,
})),
);
return previous;
}
const { level, target, uuids } = value;
if (!target) return previous;
const hosts: string[] = target.subnodes ?? [target.uuid];
if (uuids) {
/**
* Found existing alert override UUIDs; the requests must be updates.
*/
const slots: string[] = Object.keys(uuids);
const longest = Math.max(slots.length, hosts.length);
previous.push(
...Array.from({ length: longest }).map<AlertOverrideRequest>(
(ignore, i) => {
const host = hosts[i];
const slot = slots[i];
if (!slot) {
return {
body: { hostUuid: host, level, mailRecipientUuid },
method: 'post',
url: urlPrefix,
};
}
const url = `${urlPrefix}/${slot}`;
if (!host) {
return {
method: 'delete',
url,
};
}
return {
body: { hostUuid: host, level, mailRecipientUuid },
method: 'put',
url,
};
},
),
);
return previous;
}
/**
* No existing alert override UUIDs, meaning these are new records.
*/
previous.push(
...hosts.map<AlertOverrideRequest>((hostUuid) => ({
body: { hostUuid, level, mailRecipientUuid },
method: 'post',
url: urlPrefix,
})),
);
return previous;
},
[],
);
};
const AddMailRecipientForm: FC<AddMailRecipientFormProps> = (props) => {
const {
alertOverrideTargetOptions,
@ -132,7 +224,6 @@ const AddMailRecipientForm: FC<AddMailRecipientFormProps> = (props) => {
language: 'en_CA',
level: 2,
name: '',
uuid: mrUuid,
},
},
onSubmit: (values, { setSubmitting }) => {
@ -146,6 +237,8 @@ const AddMailRecipientForm: FC<AddMailRecipientFormProps> = (props) => {
let titleText: string = `Add mail recipient with the following?`;
let url: string = '/mail-recipient';
let alertOverrideRequestList: AlertOverrideRequest[];
if (previousFormikValues) {
actionProceedText = 'Update';
errorMessage = <>Failed to update mail server.</>;
@ -153,18 +246,49 @@ const AddMailRecipientForm: FC<AddMailRecipientFormProps> = (props) => {
successMessage = <>Mail recipient updated.</>;
titleText = `Update ${mailRecipient.name} with the following?`;
url += `/${mrUuid}`;
alertOverrideRequestList = getAlertOverrideRequestList(
mailRecipient,
previousFormikValues[mrUuid],
);
} else {
alertOverrideRequestList = getAlertOverrideRequestList(mailRecipient);
}
const { alertOverrides, uuid, ...mrBody } = mailRecipient;
const { alertOverrides, uuid: ignore, ...mrBody } = mailRecipient;
confirm.prepare({
actionProceedText,
content: <FormSummary entries={mrBody} />,
content: (
<>
<FormSummary entries={mrBody} />
<FormSummary
entries={Object.entries(alertOverrides).reduce<
Record<string, { level: number; name: string }>
>((previous, [valueId, value]) => {
if (!value.target) return previous;
previous[valueId] = {
level: value.level,
name: value.target.name,
};
return previous;
}, {})}
/>
</>
),
onCancelAppend: () => setSubmitting(false),
onProceedAppend: () => {
confirm.loading(true);
api[method](url, mrBody)
const promises = [api[method](url, mrBody)];
alertOverrideRequestList.forEach((request) => {
promises.push(api[request.method](request.url, request.body));
});
Promise.all(promises)
.then(() => confirm.finish('Success', { children: successMessage }))
.catch((error) => {
const emsg = handleAPIError(error);

@ -10,6 +10,7 @@ import { BodyText, SmallText } from '../Text';
import UncontrolledInput from '../UncontrolledInput';
const LEVEL_OPTIONS: SelectItem<number>[] = [
{ displayValue: 'Ignore', value: 0 },
{ displayValue: 'Critical', value: 1 },
{ displayValue: 'Warning', value: 2 },
{ displayValue: 'Notice', value: 3 },
@ -19,14 +20,18 @@ const LEVEL_OPTIONS: SelectItem<number>[] = [
const AlertOverrideInputGroup: FC<AlertOverrideInputGroupProps> = (props) => {
const {
alertOverrideTargetOptions,
alertOverrideUuid,
alertOverrideValueId,
mailRecipientUuid: mrUuid,
formikUtils,
} = props;
const aoUuid = useMemo<string>(
() => alertOverrideUuid ?? uuidv4(),
[alertOverrideUuid],
/**
* This is the alert override formik value identifier; not to be confused
* with the alert override UUID.
*/
const aoVid = useMemo<string>(
() => alertOverrideValueId ?? uuidv4(),
[alertOverrideValueId],
);
const { formik } = formikUtils;
@ -34,12 +39,17 @@ const AlertOverrideInputGroup: FC<AlertOverrideInputGroupProps> = (props) => {
values: { [mrUuid]: mailRecipient },
} = formik;
const {
alertOverrides: { [aoUuid]: alertOverride },
alertOverrides: { [aoVid]: alertOverride },
} = mailRecipient;
const overrideChain = useMemo<string>(
() => `${mrUuid}.alertOverrides.${aoUuid}`,
[aoUuid, mrUuid],
() => `${mrUuid}.alertOverrides.${aoVid}`,
[aoVid, mrUuid],
);
const deleteChain = useMemo<string>(
() => `${overrideChain}.delete`,
[overrideChain],
);
const targetChain = useMemo<string>(
() => `${overrideChain}.target`,
@ -105,7 +115,19 @@ const AlertOverrideInputGroup: FC<AlertOverrideInputGroupProps> = (props) => {
<IconButton
mapPreset="delete"
onClick={() => {
formik.setFieldValue(overrideChain, undefined, true);
if (alertOverride.uuids) {
formik.setFieldValue(deleteChain, true, true);
} else {
formik.setValues((previous) => {
const shallow = { ...previous };
const { [mrUuid]: mr } = shallow;
const { [aoVid]: remove, ...keep } = mr.alertOverrides;
mr.alertOverrides = { ...keep };
return shallow;
});
}
}}
size="small"
/>

@ -29,28 +29,34 @@ const ManageAlertOverride: FC<ManageAlertOverrideProps> = (props) => {
listEmpty="No alert overrides(s)"
listItems={alertOverrides}
onAdd={() => {
const aoUuid = uuidv4();
/**
* This is **not** the same as an alert override UUID because 1 alert
* override formik value can reference _n_ alert override rules, where
* _n_ is the number of subnodes per node. */
const valueId = uuidv4();
formik.setValues((previous) => {
const current = { ...previous };
const shallow = { ...previous };
const { [mrUuid]: mr } = shallow;
current[mrUuid].alertOverrides[aoUuid] = {
level: 2,
target: null,
uuid: aoUuid,
mr.alertOverrides = {
...mr.alertOverrides,
[valueId]: { level: 2, target: null },
};
return current;
return shallow;
});
}}
renderListItem={(uuid) => (
renderListItem={(valueId, value) =>
!value.delete && (
<AlertOverrideInputGroup
alertOverrideTargetOptions={alertOverrideTargetOptions}
alertOverrideUuid={uuid}
alertOverrideValueId={valueId}
formikUtils={formikUtils}
mailRecipientUuid={mrUuid}
/>
)}
)
}
/>
);
};

@ -24,26 +24,34 @@ const ManageMailRecipient: FC = () => {
Object.values(nodes)
.sort((a, b) => a.name.localeCompare(b.name))
.reduce<AlertOverrideTarget[]>((options, node) => {
options.push({
const nodeTarget: AlertOverrideTarget = {
description: node.description,
name: node.name,
node: node.uuid,
subnodes: [],
type: 'node',
uuid: node.uuid,
});
};
Object.values(node.hosts)
const subnodeTargets = Object.values(node.hosts)
.sort((a, b) => a.name.localeCompare(b.name))
.forEach((subnode) => {
if (subnode.type === 'dr') return;
.reduce<AlertOverrideTarget[]>((previous, subnode) => {
if (subnode.type === 'dr') return previous;
options.push({
previous.push({
name: subnode.name,
node: node.uuid,
type: 'subnode',
uuid: subnode.uuid,
});
});
nodeTarget.subnodes?.push(subnode.uuid);
return previous;
}, []);
// Append the options in sequence: node followed by its subnode(s).
options.push(nodeTarget, ...subnodeTargets);
return options;
}, []),
@ -59,40 +67,87 @@ const ManageMailRecipient: FC = () => {
const formikAlertOverrides = useMemo<
AlertOverrideFormikValues | undefined
>(() => {
if (!alertOverrides) return undefined;
if (!nodes || !alertOverrides) return undefined;
/**
* Group alert override rules based on node UUID. The groups will be used
* for comparison to see whether the subnodes are assigned the same alert
* level.
*
* If subnodes have the same level, they will be consolidated into a single
* target for display. Otherwise, every subnode will get its own visual.
*/
const groups = Object.values(alertOverrides).reduce<
Record<string, APIAlertOverrideOverview[]>
>((previous, override) => {
const {
node: { uuid: nodeUuid },
} = override;
if (previous[nodeUuid]) {
previous[nodeUuid].push(override);
} else {
previous[nodeUuid] = [override];
}
return previous;
}, {});
return Object.entries(groups).reduce<AlertOverrideFormikValues>(
(previous, pair) => {
const [nodeUuid, overrides] = pair;
const [firstOverride, ...restOverrides] = overrides;
const groups: Record<string, number> = {};
const sameLevel =
overrides.length > 1 &&
restOverrides.every(({ level }) => level === firstOverride.level);
return Object.values(alertOverrides).reduce<AlertOverrideFormikValues>(
(previous, value) => {
const { level, node, subnode, uuid } = value;
if (sameLevel) {
const {
0: { level },
} = overrides;
groups[node.uuid] = groups[node.uuid] ? groups[node.uuid] + 1 : 1;
const { [nodeUuid]: node } = nodes;
previous[uuid] = {
previous[nodeUuid] = {
level,
target:
groups[node.uuid] > 1
? {
target: {
description: node.description,
name: node.name,
node: node.uuid,
subnodes: overrides.map<string>(({ subnode: { uuid } }) => uuid),
type: 'node',
uuid: node.uuid,
}
: {
},
uuids: overrides.reduce<Record<string, string>>(
(uuids, { subnode, uuid: overrideUuid }) => {
uuids[overrideUuid] = subnode.uuid;
return uuids;
},
{},
),
};
} else {
overrides.forEach(({ level, node, subnode, uuid: overrideUuid }) => {
previous[subnode.uuid] = {
level,
target: {
name: subnode.name,
node: node.uuid,
type: 'subnode',
uuid: subnode.uuid,
},
uuid,
uuids: { [overrideUuid]: subnode.uuid },
};
});
}
return previous;
},
{},
);
}, [alertOverrides]);
}, [alertOverrides, nodes]);
return (
<>

@ -2,15 +2,16 @@ import * as yup from 'yup';
import buildYupDynamicObject from '../../lib/buildYupDynamicObject';
const alertLevelSchema = yup.number().oneOf([1, 2, 3, 4]);
const alertLevelSchema = yup.number().oneOf([0, 1, 2, 3, 4]);
const alertOverrideSchema = yup.object({
delete: yup.boolean().optional(),
level: alertLevelSchema.required(),
target: yup.object({
type: yup.string().oneOf(['node', 'subnode']).required(),
uuid: yup.string().uuid().required(),
}),
uuid: yup.string().uuid().required(),
uuid: yup.string().uuid().optional(),
});
const alertOverrideListSchema = yup.lazy((entries) =>
@ -23,7 +24,7 @@ const mailRecipientSchema = yup.object({
language: yup.string().oneOf(['en_CA']).optional(),
level: alertLevelSchema.required(),
name: yup.string().required(),
uuid: yup.string().uuid().required(),
uuid: yup.string().uuid().optional(),
});
const mailRecipientListSchema = yup.lazy((entries) =>

@ -1,23 +1,36 @@
type AlertOverrideRequest = {
body?: {
hostUuid: string;
level: number;
mailRecipientUuid: string;
};
method: 'delete' | 'post' | 'put';
url: string;
};
type AlertOverrideTarget = {
description?: string;
name: string;
node: string;
subnodes?: string[];
type: 'node' | 'subnode';
uuid: string;
};
type AlertOverrideFormikAlertOverride = {
delete?: boolean;
level: number;
target: AlertOverrideTarget | null;
uuid: string;
uuids?: Record<string, string>;
};
type AlertOverrideFormikValues = {
[uuid: string]: AlertOverrideFormikAlertOverride;
[valueId: string]: AlertOverrideFormikAlertOverride;
};
type MailRecipientFormikMailRecipient = APIMailRecipientDetail & {
type MailRecipientFormikMailRecipient = Omit<APIMailRecipientDetail, 'uuid'> & {
alertOverrides: AlertOverrideFormikValues;
uuid?: string;
};
type MailRecipientFormikValues = {
@ -54,7 +67,7 @@ type ManageAlertOverrideProps = Required<
/** AlertOverrideInputGroup */
type AlertOverrideInputGroupOptionalProps = {
alertOverrideUuid?: string;
alertOverrideValueId?: string;
};
type AlertOverrideInputGroupProps = AlertOverrideInputGroupOptionalProps &

Loading…
Cancel
Save