diff --git a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx index ca0d0ca171..9ef6064413 100644 --- a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx +++ b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx @@ -74,6 +74,10 @@ const CONDITIONS = [ label: t('!= (Is Not Equal)'), value: '!=', }, + { + label: t('Not Null'), + value: 'not null', + }, ]; const RETENTION_OPTIONS = [ @@ -215,6 +219,10 @@ export const StyledInputContainer = styled.div` flex: 1 1 auto; } + input[disabled] { + color: ${({ theme }) => theme.colors.grayscale.base}; + } + textarea { height: 160px; resize: none; @@ -455,7 +463,9 @@ const AlertReportModal: FunctionComponent = ({ > | null>(); const [isHidden, setIsHidden] = useState(true); const [contentType, setContentType] = useState('dashboard'); + // Dropdown options + const [conditionNotNull, setConditionNotNull] = useState(false); const [sourceOptions, setSourceOptions] = useState([]); const [dashboardOptions, setDashboardOptions] = useState([]); const [chartOptions, setChartOptions] = useState([]); @@ -536,6 +546,10 @@ const AlertReportModal: FunctionComponent = ({ const data: any = { ...currentAlert, + validator_type: conditionNotNull ? 'not null' : 'operator', + validator_config_json: conditionNotNull + ? {} + : currentAlert?.validator_config_json, chart: contentType === 'chart' ? currentAlert?.chart?.value : undefined, dashboard: contentType === 'dashboard' @@ -566,7 +580,11 @@ const AlertReportModal: FunctionComponent = ({ delete data.last_value; delete data.last_value_row_json; - updateResource(update_id, data).then(() => { + updateResource(update_id, data).then(response => { + if (!response) { + return; + } + if (onAdd) { onAdd(); } @@ -577,6 +595,10 @@ const AlertReportModal: FunctionComponent = ({ } else if (currentAlert) { // Create createResource(data).then(response => { + if (!response) { + return; + } + if (onAdd) { onAdd(response); } @@ -787,6 +809,8 @@ const AlertReportModal: FunctionComponent = ({ }; const onConditionChange = (op: Operator) => { + setConditionNotNull(op === 'not null'); + const config = { op, threshold: currentAlert @@ -851,8 +875,9 @@ const AlertReportModal: FunctionComponent = ({ } else if ( !!currentAlert.database && currentAlert.sql?.length && - !!currentAlert.validator_config_json?.op && - currentAlert.validator_config_json?.threshold !== undefined + (conditionNotNull || !!currentAlert.validator_config_json?.op) && + (conditionNotNull || + currentAlert.validator_config_json?.threshold !== undefined) ) { setDisableSave(false); } else { @@ -890,6 +915,13 @@ const AlertReportModal: FunctionComponent = ({ setNotificationSettings(settings); setContentType(resource.chart ? 'chart' : 'dashboard'); + const validatorConfig = + typeof resource.validator_config_json === 'string' + ? JSON.parse(resource.validator_config_json) + : resource.validator_config_json; + + setConditionNotNull(resource.validator_type === 'not null'); + setCurrentAlert({ ...resource, chart: resource.chart @@ -913,9 +945,11 @@ const AlertReportModal: FunctionComponent = ({ })), // @ts-ignore: Type not assignable validator_config_json: - typeof resource.validator_config_json === 'string' - ? JSON.parse(resource.validator_config_json) - : resource.validator_config_json, + resource.validator_type === 'not null' + ? { + op: 'not null', + } + : validatorConfig, }); } }); @@ -935,7 +969,7 @@ const AlertReportModal: FunctionComponent = ({ sql: '', type: isReport ? 'Report' : 'Alert', validator_config_json: {}, - validator_type: 'not null', + validator_type: '', }); setNotificationSettings([]); @@ -960,6 +994,7 @@ const AlertReportModal: FunctionComponent = ({ currentAlert.chart, contentType, notificationSettings, + conditionNotNull, ] : [], ); @@ -1138,6 +1173,7 @@ const AlertReportModal: FunctionComponent = ({ ' | '<=' | '>=' | '==' | '!='; +export type Operator = '<' | '>' | '<=' | '>=' | '==' | '!=' | 'not null'; export type AlertObject = { active?: boolean; diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts index fedcc347d1..4ea89b10c0 100644 --- a/superset-frontend/src/views/CRUD/hooks.ts +++ b/superset-frontend/src/views/CRUD/hooks.ts @@ -254,18 +254,23 @@ export function useSingleViewResource( ({ json = {} }) => { updateState({ resource: json.result, + error: null, }); return json.id; }, - createErrorHandler(errMsg => + createErrorHandler(errMsg => { handleErrorMsg( t( 'An error occurred while creating %ss: %s', resourceLabel, JSON.stringify(errMsg), ), - ), - ), + ); + + updateState({ + error: errMsg, + }); + }), ) .finally(() => { updateState({ loading: false }); @@ -287,18 +292,25 @@ export function useSingleViewResource( ({ json = {} }) => { updateState({ resource: json.result, + error: null, }); return json.result; }, - createErrorHandler(errMsg => + createErrorHandler(errMsg => { handleErrorMsg( t( 'An error occurred while fetching %ss: %s', resourceLabel, JSON.stringify(errMsg), ), - ), - ), + ); + + updateState({ + error: errMsg, + }); + + return errMsg; + }), ) .finally(() => { updateState({ loading: false });