From 9c6ecbb9cdd81f476b63e81c2b84cf626a9ecad6 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Fri, 8 Mar 2024 17:13:35 +0530 Subject: [PATCH] take UI to completion, add migration to allow email service in catalog --- .../V20__alert_constraint_update.sql | 6 + ui/app/alert-config/new.tsx | 222 +++--------------- ui/app/alert-config/page.tsx | 48 ++-- ui/app/alert-config/validation.ts | 12 +- ui/app/api/alert-config/route.ts | 1 + ui/components/AlertDropdown.tsx | 23 +- 6 files changed, 73 insertions(+), 239 deletions(-) create mode 100644 nexus/catalog/migrations/V20__alert_constraint_update.sql diff --git a/nexus/catalog/migrations/V20__alert_constraint_update.sql b/nexus/catalog/migrations/V20__alert_constraint_update.sql new file mode 100644 index 0000000000..18ddb7d9f8 --- /dev/null +++ b/nexus/catalog/migrations/V20__alert_constraint_update.sql @@ -0,0 +1,6 @@ +ALTER TABLE peerdb_stats.alerting_config +DROP CONSTRAINT alerting_config_service_type_check; + +ALTER TABLE peerdb_stats.alerting_config +ADD CONSTRAINT alerting_config_service_type_check +CHECK (service_type IN ('slack', 'email')); \ No newline at end of file diff --git a/ui/app/alert-config/new.tsx b/ui/app/alert-config/new.tsx index 53059bbaab..ca2181b3c9 100644 --- a/ui/app/alert-config/new.tsx +++ b/ui/app/alert-config/new.tsx @@ -15,21 +15,11 @@ import { slackConfigType, } from './validation'; -export interface AlertConfigProps { - id?: bigint; - serviceType: string; - authToken: string; - channelIdString: string; - slotLagGBAlertThreshold: number; - openConnectionsAlertThreshold: number; - forEdit?: boolean; -} - -export type serviceType2 = 'slack' | 'email'; +export type ServiceType = 'slack' | 'email'; -export interface AlertConfigProps2 { +export interface AlertConfigProps { id?: bigint; - serviceType: serviceType2; + serviceType: ServiceType; alertConfig: serviceConfigType; forEdit?: boolean; } @@ -121,8 +111,8 @@ function getEmailProps( ); } -function getExtraProps( - serviceType: serviceType2, +function getServiceFields( + serviceType: ServiceType, config: T, setConfig: Dispatch> ) { @@ -141,170 +131,34 @@ function getExtraProps( } } -export function NewConfig(alertProps: AlertConfigProps2) { - const [serviceType, setServiceType] = useState( +export function NewConfig(alertProps: AlertConfigProps) { + const [serviceType, setServiceType] = useState( alertProps.serviceType ); const [config, setConfig] = useState( alertProps.alertConfig ); - const [slotLagGBAlertThreshold, setSlotLagGBAlertThreshold] = - useState(alertProps.alertConfig.slot_lag_mb_alert_threshold); - const [openConnectionsAlertThreshold, setOpenConnectionsAlertThreshold] = - useState(alertProps.alertConfig.open_connections_alert_threshold); + const [loading, setLoading] = useState(false); const handleAdd = async () => { - if (serviceType !== 'slack') { - notifyErr('Service Type must be selected'); + if (!serviceType) { + notifyErr('Service type must be selected'); return; } const alertConfigReq: alertConfigType = { + id: Number(alertProps.id || -1), serviceType: serviceType, serviceConfig: config, }; - const alertReqValidity = alertConfigReqSchema.safeParse(alertConfigReq); - if (!alertReqValidity.success) { - notifyErr(alertReqValidity.error.issues[0].message); - return; - } - setLoading(true); - if (alertProps.forEdit) { - alertConfigReq.id = Number(alertProps.id); - } - const createRes = await fetch('/api/alert-config', { - method: alertProps.forEdit ? 'PUT' : 'POST', - body: JSON.stringify(alertConfigReq), - }); - const createStatus = await createRes.text(); - setLoading(false); - if (createStatus !== 'success') { - notifyErr('Something went wrong. Please try again'); - return; - } - - window.location.reload(); - }; - const extraProps = getExtraProps(serviceType, config, setConfig); - return ( -
-
-
-

Alert Provider

- val && setServiceType(val.value)} - theme={SelectTheme} - /> -
-
-

Slot Lag Alert Threshold (in GB)

- setSlotLagGBAlertThreshold(e.target.valueAsNumber)} - /> -
-
-

Open Connections Alert Threshold

- - setOpenConnectionsAlertThreshold(e.target.valueAsNumber) - } - /> -
- {getExtraProps(serviceType, config, setConfig)} - - -
-
- ); -} -// -export const NewAlertConfig = (alertProps: AlertConfigProps) => { - const [serviceType, setServiceType] = useState('slack'); - const [config, setConfig] = useState({ - auth_token: alertProps.authToken ?? '', - channel_ids: alertProps.channelIdString?.split(',')!, - slot_lag_mb_alert_threshold: - alertProps.slotLagGBAlertThreshold * 1000 || 20000, - open_connections_alert_threshold: - alertProps.openConnectionsAlertThreshold || 5, - }); - const [authToken, setAuthToken] = useState(alertProps.authToken); - const [channelIdString, setChannelIdString] = useState( - alertProps.channelIdString - ); - const [slotLagGBAlertThreshold, setSlotLagGBAlertThreshold] = - useState(alertProps.slotLagGBAlertThreshold); - const [openConnectionsAlertThreshold, setOpenConnectionsAlertThreshold] = - useState(alertProps.openConnectionsAlertThreshold); - const [loading, setLoading] = useState(false); - const handleAdd = async () => { - if (serviceType !== 'slack') { - notifyErr('Service Type must be selected'); - return; - } - const alertConfigReq: alertConfigType = { - serviceType: serviceType, - serviceConfig: { - auth_token: authToken ?? '', - channel_ids: channelIdString?.split(',')!, - slot_lag_mb_alert_threshold: slotLagGBAlertThreshold * 1000 || 20000, - open_connections_alert_threshold: openConnectionsAlertThreshold || 5, - }, - }; const alertReqValidity = alertConfigReqSchema.safeParse(alertConfigReq); if (!alertReqValidity.success) { notifyErr(alertReqValidity.error.issues[0].message); return; } + setLoading(true); if (alertProps.forEdit) { alertConfigReq.id = Number(alertProps.id); @@ -313,6 +167,7 @@ export const NewAlertConfig = (alertProps: AlertConfigProps) => { method: alertProps.forEdit ? 'PUT' : 'POST', body: JSON.stringify(alertConfigReq), }); + const createStatus = await createRes.text(); setLoading(false); if (createStatus !== 'success') { @@ -322,7 +177,7 @@ export const NewAlertConfig = (alertProps: AlertConfigProps) => { window.location.reload(); }; - + const ServiceFields = getServiceFields(serviceType, config, setConfig); return (
{ value: 'slack', label: 'Slack', }, + { + value: 'email', + label: 'Email', + }, ]} placeholder='Select provider' defaultValue={{ @@ -348,32 +207,10 @@ export const NewAlertConfig = (alertProps: AlertConfigProps) => { label: 'Slack', }} formatOptionLabel={ConfigLabel} - onChange={(val, _) => val && setServiceType(val.value)} + onChange={(val, _) => val && setServiceType(val.value as ServiceType)} theme={SelectTheme} />
-
-

Authorisation Token

- setAuthToken(e.target.value)} - /> -
- -
-

Channel IDs

- setChannelIdString(e.target.value)} - /> -
-

Slot Lag Alert Threshold (in GB)

{ variant='simple' type={'number'} placeholder='optional' - value={slotLagGBAlertThreshold} - onChange={(e) => setSlotLagGBAlertThreshold(e.target.valueAsNumber)} + value={config.slot_lag_mb_alert_threshold / 1000} + onChange={(e) => + setConfig((previous) => ({ + ...previous, + slot_lag_mb_alert_threshold: e.target.valueAsNumber * 1000, + })) + } />
-

Open Connections Alert Threshold

{ variant='simple' type={'number'} placeholder='optional' - value={openConnectionsAlertThreshold} + value={config.open_connections_alert_threshold} onChange={(e) => - setOpenConnectionsAlertThreshold(e.target.valueAsNumber) + setConfig((previous) => ({ + ...previous, + open_connections_alert_threshold: e.target.valueAsNumber, + })) } />
- + {ServiceFields} - {/*{inEditOrAddMode && }*/} - {inEditOrAddMode && } + {inEditOrAddMode && } ); }; diff --git a/ui/app/alert-config/validation.ts b/ui/app/alert-config/validation.ts index ee1156cf1e..46631573f9 100644 --- a/ui/app/alert-config/validation.ts +++ b/ui/app/alert-config/validation.ts @@ -3,16 +3,16 @@ import z from 'zod'; const baseServiceConfigSchema = z.object({ slot_lag_mb_alert_threshold: z .number({ - invalid_type_error: 'Threshold must be a number', + invalid_type_error: 'Slot threshold must be a number', }) - .int() - .min(0, 'Threshold must be non-negative'), + .int({ message: 'Slot threshold must be a valid integer' }) + .min(0, 'Slot threshold must be non-negative'), open_connections_alert_threshold: z .number({ invalid_type_error: 'Threshold must be a number', }) - .int() - .min(0, 'Threshold must be non-negative'), + .int({ message: 'Connections threshold must be a valid integer' }) + .min(0, 'Connections threshold must be non-negative'), }); const slackServiceConfigSchema = z.intersection( @@ -52,7 +52,7 @@ export const serviceConfigSchema = z.union([ emailServiceConfigSchema, ]); export const alertConfigReqSchema = z.object({ - id: z.optional(z.number()), + id: z.optional(z.number({ invalid_type_error: 'ID must be a valid number' })), serviceType: z.enum(['slack', 'email'], { errorMap: (issue, ctx) => ({ message: 'Invalid service type' }), }), diff --git a/ui/app/api/alert-config/route.ts b/ui/app/api/alert-config/route.ts index 7f66da191f..9b8ff019ed 100644 --- a/ui/app/api/alert-config/route.ts +++ b/ui/app/api/alert-config/route.ts @@ -23,6 +23,7 @@ export async function POST(request: Request) { if (createRes.id) { createStatus = 'success'; } + return new Response(createStatus); } diff --git a/ui/components/AlertDropdown.tsx b/ui/components/AlertDropdown.tsx index dd3ae482e3..bfc30648a7 100644 --- a/ui/components/AlertDropdown.tsx +++ b/ui/components/AlertDropdown.tsx @@ -40,19 +40,16 @@ const AlertDropdown = ({ borderRadius: '0.5rem', }} > - - - - - - + + +