From 48500234850448c13db078c86f31e69b484958e9 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Sat, 23 Nov 2024 15:18:07 +0200 Subject: [PATCH 01/14] addn initial Alert table --- src/App.js | 4 + src/api/alerts-api.js | 39 ++ .../ProjectsAlerts/ProjectsAlerts.js | 62 +++ .../ProjectsAlerts/ProjectsAlertsFilters.js | 134 ++++++ .../ProjectsAlerts/ProjectsAlertsView.js | 121 +++++ src/components/ProjectsAlerts/alerts.util.js | 63 +++ src/components/ProjectsAlerts/alertsData.json | 432 ++++++++++++++++++ src/constants.js | 13 + src/elements/AlertsTableRow/AlertsTableRow.js | 78 ++++ src/elements/TableCell/TableCell.js | 24 + src/elements/TableTypeCell/TableTypeCell.js | 3 +- src/reducers/alertsReducer.js | 68 +++ src/store/toolkitStore.js | 2 + src/utils/createAlertsContent.js | 121 +++++ 14 files changed, 1163 insertions(+), 1 deletion(-) create mode 100644 src/api/alerts-api.js create mode 100644 src/components/ProjectsAlerts/ProjectsAlerts.js create mode 100644 src/components/ProjectsAlerts/ProjectsAlertsFilters.js create mode 100644 src/components/ProjectsAlerts/ProjectsAlertsView.js create mode 100644 src/components/ProjectsAlerts/alerts.util.js create mode 100644 src/components/ProjectsAlerts/alertsData.json create mode 100644 src/elements/AlertsTableRow/AlertsTableRow.js create mode 100644 src/reducers/alertsReducer.js create mode 100644 src/utils/createAlertsContent.js diff --git a/src/App.js b/src/App.js index 816efedfe2..ffb6ccd1f8 100755 --- a/src/App.js +++ b/src/App.js @@ -103,6 +103,7 @@ const FeatureVectors = lazyRetry( const ProjectsJobsMonitoring = lazyRetry( () => import('./components/ProjectsJobsMonitoring/ProjectsJobsMonitoring') ) +const ProjectsAlerts = lazyRetry(() => import('./components/ProjectsAlerts/ProjectsAlerts')) const JobsMonitoring = lazyRetry( () => import('./components/ProjectsJobsMonitoring/JobsMonitoring/JobsMonitoring') ) @@ -152,6 +153,9 @@ const App = () => { } /> } /> + {/*TODO: the Alerts Route will be updated with ML-8368 */} + } /> + {!isNuclioModeDisabled && ( { + return new Promise(resolve => { + setTimeout(() => { + resolve({ data: dummyData }) + }, 1000) + }) + }, + getAlert: (project, alertName, config) => { + return new Promise(resolve => { + setTimeout(() => { + resolve({ data: dummyData }) + }, 1000) + }) + } +} + +export default alertsApi diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js new file mode 100644 index 0000000000..43f1795eba --- /dev/null +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -0,0 +1,62 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import { useCallback, useMemo, useState } from 'react' + +import { useSelector } from 'react-redux' + +import ProjectAlertsView from './ProjectsAlertsView' + +import { getAlertsFiltersConfig } from './alerts.util' + +import alertsData from './alertsData.json' + +const ProjectsAlerts = () => { + const [alerts] = useState(alertsData) + // const [selectedAlert, setSelectedAlert] = useState({}) + const [selectedAlert] = useState({}) + // const [requestErrorMessage, setRequestErrorMessage] = useState('') + const [requestErrorMessage] = useState('') + const alertsStore = useSelector(state => state.alertsStore) + const filtersStore = useSelector(store => store.filtersStore) + + const alertsFiltersConfig = useMemo(() => getAlertsFiltersConfig(), []) + + const refreshAlertsCallback = useCallback(filters => { + console.log(filters) + }, []) + + return ( + []} // TODO + alerts={alerts} + alertsFiltersConfig={alertsFiltersConfig} + alertsStore={alertsStore} + refreshAlertsCallback={refreshAlertsCallback} + filtersStore={filtersStore} + pageData={{}} //TODO + requestErrorMessage={requestErrorMessage} + selectedAlert={selectedAlert} + /> + ) +} + +ProjectsAlerts.propTypes = {} + +export default ProjectsAlerts diff --git a/src/components/ProjectsAlerts/ProjectsAlertsFilters.js b/src/components/ProjectsAlerts/ProjectsAlertsFilters.js new file mode 100644 index 0000000000..c09119bde4 --- /dev/null +++ b/src/components/ProjectsAlerts/ProjectsAlertsFilters.js @@ -0,0 +1,134 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import { useMemo } from 'react' + +import { truncate } from 'lodash' +import { useForm, useFormState } from 'react-final-form' +import { useSelector } from 'react-redux' + +import { FormSelect, FormInput } from 'igz-controls/components' +import { FormOnChange } from 'iguazio.dashboard-react-controls/dist/components' + +import { generateProjectsList } from '../../utils/projects' +import { + allProjectsOption, + filterAlertsEntityTypeOptions, + filterAlertsEventTypeOptions, + filterAlertsSeverityOptions +} from './alerts.util' + +import { + APPLICATION, + ENDPOINT, + ENDPOINT_APPLICATION, + ENDPOINT_RESULT, + ENTITY_ID, + ENTITY_TYPE, + EVENT_TYPE, + FILTER_ALL_ITEMS, + JOB_NAME, + PROJECT_FILTER, + SEVERITY +} from '../../constants' +import StatusFilter from '../../common/StatusFilter/StatusFilter' + +const ProjectsAlertsFilters = () => { + const form = useForm() + const { + values: { [ENTITY_TYPE]: entityType } + } = useFormState() + + const projectStore = useSelector(state => state.projectStore) + + const projectsList = useMemo(() => { + const generatedProjects = generateProjectsList(projectStore.projectsNames.data) + + return [...allProjectsOption, ...generatedProjects].map(item => ({ + ...item, + label: truncate(item.label, { length: 26 }) + })) + }, [projectStore.projectsNames.data]) + + const handleInputChange = (value, inputName) => { + form.change(inputName, value || '') + } + + return ( + <> +
+ +
+
+ +
+ + {(entityType === FILTER_ALL_ITEMS || entityType === APPLICATION) && ( +
+ + handleInputChange(value, ENTITY_ID)} name={ENTITY_ID} /> +
+ )} + {entityType === JOB_NAME && ( +
+ + handleInputChange(value, JOB_NAME)} name={JOB_NAME} /> +
+ )} + {entityType === ENDPOINT && ( + <> +
+ + handleInputChange(value, ENDPOINT_APPLICATION)} + name={ENDPOINT_APPLICATION} + /> +
+
+ + handleInputChange(value, ENDPOINT_RESULT)} + name={ENDPOINT_RESULT} + /> +
+ + )} +
+ +
+
+ +
+ + ) +} + +export default ProjectsAlertsFilters diff --git a/src/components/ProjectsAlerts/ProjectsAlertsView.js b/src/components/ProjectsAlerts/ProjectsAlertsView.js new file mode 100644 index 0000000000..000e33561f --- /dev/null +++ b/src/components/ProjectsAlerts/ProjectsAlertsView.js @@ -0,0 +1,121 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import { useMemo, useState } from 'react' + +import Table from '../../components/Table/Table' +import { createAlertRowData } from '../../utils/createAlertsContent' +import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs' +import PropTypes from 'prop-types' +import Loader from '../../common/Loader/Loader' +import NoData from '../../common/NoData/NoData' +import { getNoDataMessage } from '../../utils/getNoDataMessage' +import { ALERTS_FILTERS, ALERTS_PAGE } from '../../constants' + +import AlertsTableRow from '../../elements/AlertsTableRow/AlertsTableRow' + +const ProjectAlertsView = ({ + actionsMenu, + alerts, + alertsFiltersConfig, + alertsStore, + handleRefresh, + filtersStore, + pageData, + refreshAlertsCallback, + requestErrorMessage, + selectedAlert +}) => { + const [selectedModelEndpoint] = useState({}) + + const tableContent = useMemo(() => { + return alerts.activations.map(alert => createAlertRowData(alert)) + }, [alerts.activations]) + + return ( + <> +
+
+ +
+
+
+ {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {alertsStore.loading ? ( + + ) : tableContent.length === 0 ? ( + + ) : ( + + {tableContent.map((tableItem, index) => ( + {}} + rowIndex={index} + rowItem={tableItem} + actionsMenu={[]} + selectedItem={selectedModelEndpoint} + /> + ))} +
+ )} +
+
+
+ + ) +} + +ProjectAlertsView.propTypes = { + actionsMenu: PropTypes.func.isRequired, + alertsStore: PropTypes.object.isRequired, + pageData: PropTypes.object.isRequired, + refreshAlertsCallback: PropTypes.func.isRequired, + selectedAlert: PropTypes.object.isRequired +} +export default ProjectAlertsView diff --git a/src/components/ProjectsAlerts/alerts.util.js b/src/components/ProjectsAlerts/alerts.util.js new file mode 100644 index 0000000000..27b95b099b --- /dev/null +++ b/src/components/ProjectsAlerts/alerts.util.js @@ -0,0 +1,63 @@ +import { + APPLICATION, + DATES_FILTER, + ENDPOINT, + ENTITY_TYPE, + EVENT_TYPE, + FILTER_ALL_ITEMS, + JOB_KIND_JOB, + LABELS_FILTER, + NAME_FILTER, + PROJECT_FILTER, + STATUS_FILTER +} from '../../constants' + +export const getAlertsFiltersConfig = () => { + return { + [NAME_FILTER]: { label: 'Name:' }, + [DATES_FILTER]: { label: 'Start time:' }, + [PROJECT_FILTER]: { label: 'Project:' }, + [STATUS_FILTER]: { label: 'Status:' }, + [ENTITY_TYPE]: { label: 'Entity Type:' }, + [EVENT_TYPE]: { label: 'Event Type' }, + [LABELS_FILTER]: { label: 'Labels:' } + } +} + +export const allProjectsOption = [ + { + id: FILTER_ALL_ITEMS, + label: 'All' + } +] + +export const filterAlertsEntityTypeOptions = [ + { label: 'All', id: FILTER_ALL_ITEMS }, + { label: 'Jobs', id: JOB_KIND_JOB }, + { label: 'Endpoint', id: ENDPOINT }, + { label: 'Application', id: APPLICATION } +] + +export const filterAlertsSeverityOptions = [ + { label: 'All', id: FILTER_ALL_ITEMS, status: FILTER_ALL_ITEMS }, + { label: 'Critical', id: 'critical', status: 'critical' }, + { label: 'High', id: 'high', status: 'high' }, + { label: 'Low', id: 'low', status: 'low' } +] + +export const filterAlertsEventTypeOptions = [ + { label: 'All', id: FILTER_ALL_ITEMS }, + { label: 'Job Failed', id: 'job-failed' }, + { label: 'Data Drift Detected', id: 'data-drift-detected' }, + { label: 'Data Drift Suspected', id: 'data-drift-suspected' }, + { label: 'Conc Drift Detected', id: 'concept-drift-detected' }, + { label: 'Conc Drift Suspected', id: 'concept-drift-suspected' }, + { label: 'MM Perf. Detected', id: 'model-performance-detected' }, + { label: 'MM Perf. Suspected', id: 'model-performance-suspected' }, + { label: 'S Perf. Detected', id: 'system-performance-detected' }, + { label: 'S Perf. Suspected', id: 'system-performance-suspected' }, + { label: 'MM App Ano. Detected', id: 'mm-app-anomaly-detected' }, + { label: 'MM App Ano. Suspected', id: 'mm-app-anomaly-suspected' }, + { label: 'MM App Failed', id: 'mm-app-failed' }, + { label: 'MM App Failed', id: 'failed' } +] diff --git a/src/components/ProjectsAlerts/alertsData.json b/src/components/ProjectsAlerts/alertsData.json new file mode 100644 index 0000000000..7461874c40 --- /dev/null +++ b/src/components/ProjectsAlerts/alertsData.json @@ -0,0 +1,432 @@ +{ + "activations": [ + { + "name": "alert_1", + "project": "workflow-proj", + "activation_time": "2024-10-23T10:15:30Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "webhook", + "err": "Timeout error" + } + ], + "entity_id": "model-endpoint-1.app-1.result-1", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 3, + "period": "30m" + }, + "event_kind": "data-drift-detected", + "severity": "critical", + "number_of_events": 15 + }, + { + "name": "alert_2", + "project": "tutorial-admin", + "activation_time": "2024-10-24T12:20:30Z", + "notifications": [ + { + "kind": "slack", + "err": "Failed to send notification due to network issue" + }, + { + "kind": "git", + "err": "" + } + ], + "entity_id": "job-entity-2.app-2.result-2", + "entity_kind": "job", + "criteria": { + "count": 10, + "period": "2h" + }, + "event_kind": "concept-drift-suspected", + "severity": "low", + "number_of_events": 7 + }, + { + "name": "alert_3", + "project": "workflow-proj", + "activation_time": "2024-10-25T14:45:00Z", + "notifications": [ + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "model-endpoint-3.app-3.result-3", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 2, + "period": "1h" + }, + "event_kind": "mm-app-anomaly-detected", + "severity": "high", + "number_of_events": 12 + }, + { + "name": "alert_4", + "project": "analysis-hub", + "activation_time": "2024-10-26T08:10:20Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "git", + "err": "Authentication failed" + } + ], + "entity_id": "model-monitoring-4.app-4.result-4", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 8, + "period": "1h" + }, + "event_kind": "model-performance-detected", + "severity": "low", + "number_of_events": 5 + }, + { + "name": "alert_5", + "project": "reporting-app", + "activation_time": "2024-10-27T16:55:10Z", + "notifications": [ + { + "kind": "slack", + "err": "" + } + ], + "entity_id": "job-entity-5.app-5.result-5", + "entity_kind": "job", + "criteria": { + "count": 4, + "period": "3h" + }, + "event_kind": "system-performance-suspected", + "severity": "critical", + "number_of_events": 20 + }, + { + "name": "alert_6", + "project": "tutorial-admin", + "activation_time": "2024-10-28T09:25:40Z", + "notifications": [ + { + "kind": "email", + "err": "SMTP server not reachable" + } + ], + "entity_id": "model-endpoint-6.app-6.result-6", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 6, + "period": "2h" + }, + "event_kind": "mm-app-failed", + "severity": "high", + "number_of_events": 3 + }, + { + "name": "alert_7", + "project": "workflow-proj", + "activation_time": "2024-10-29T07:30:50Z", + "notifications": [ + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "model-monitoring-7.app-7.result-7", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 1, + "period": "30m" + }, + "event_kind": "model-performance-suspected", + "severity": "low", + "number_of_events": 4 + }, + { + "name": "alert_8", + "project": "analysis-hub", + "activation_time": "2024-10-30T11:15:10Z", + "notifications": [ + { + "kind": "slack", + "err": "Failed to send notification due to network issue" + } + ], + "entity_id": "job-entity-8.app-8.result-8", + "entity_kind": "job", + "criteria": { + "count": 9, + "period": "4h" + }, + "event_kind": "concept-drift-detected", + "severity": "critical", + "number_of_events": 16 + }, + { + "name": "alert_9", + "project": "reporting-app", + "activation_time": "2024-10-31T17:05:35Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "model-endpoint-9.app-9.result-9", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 7, + "period": "1h" + }, + "event_kind": "data-drift-suspected", + "severity": "high", + "number_of_events": 18 + }, + { + "name": "alert_10", + "project": "workflow-proj", + "activation_time": "2024-11-01T06:20:15Z", + "notifications": [ + { + "kind": "git", + "err": "" + } + ], + "entity_id": "model-endpoint-10.app-10.result-10", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 5, + "period": "2h" + }, + "event_kind": "system-performance-detected", + "severity": "low", + "number_of_events": 9 + }, + { + "name": "alert_11", + "project": "analysis-hub", + "activation_time": "2024-11-02T14:10:50Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "webhook", + "err": "API endpoint not responding" + } + ], + "entity_id": "model-monitoring-11.app-11.result-11", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 10, + "period": "3h" + }, + "event_kind": "mm-app-anomaly-suspected", + "severity": "critical", + "number_of_events": 13 + }, + { + "name": "alert_12", + "project": "tutorial-admin", + "activation_time": "2024-11-03T18:35:25Z", + "notifications": [ + { + "kind": "slack", + "err": "" + } + ], + "entity_id": "job-entity-12.app-12.result-12", + "entity_kind": "job", + "criteria": { + "count": 3, + "period": "1h" + }, + "event_kind": "failed", + "severity": "low", + "number_of_events": 14 + }, + { + "name": "alert_13", + "project": "workflow-proj", + "activation_time": "2024-11-04T09:05:00Z", + "notifications": [ + { + "kind": "email", + "err": "SMTP server not reachable" + } + ], + "entity_id": "model-endpoint-13.app-13.result-13", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 8, + "period": "4h" + }, + "event_kind": "concept-drift-suspected", + "severity": "critical", + "number_of_events": 6 + }, + { + "name": "alert_14", + "project": "reporting-app", + "activation_time": "2024-11-05T13:15:45Z", + "notifications": [ + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "model-monitoring-14.app-14.result-14", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 2, + "period": "30m" + }, + "event_kind": "mm-app-failed", + "severity": "high", + "number_of_events": 11 + }, + { + "name": "alert_15", + "project": "tutorial-admin", + "activation_time": "2024-11-06T12:25:20Z", + "notifications": [ + { + "kind": "git", + "err": "" + } + ], + "entity_id": "job-entity-15.app-15.result-15", + "entity_kind": "job", + "criteria": { + "count": 5, + "period": "2h" + }, + "event_kind": "data-drift-detected", + "severity": "low", + "number_of_events": 2 + }, + { + "name": "alert_16", + "project": "workflow-proj", + "activation_time": "2024-11-07T14:00:35Z", + "notifications": [ + { + "kind": "slack", + "err": "Failed to send notification due to network issue" + } + ], + "entity_id": "model-endpoint-16.app-16.result-16", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 7, + "period": "1h" + }, + "event_kind": "system-performance-suspected", + "severity": "critical", + "number_of_events": 17 + }, + { + "name": "alert_17", + "project": "analysis-hub", + "activation_time": "2024-11-08T08:40:55Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "model-monitoring-17.app-17.result-17", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 9, + "period": "3h" + }, + "event_kind": "model-performance-detected", + "severity": "high", + "number_of_events": 19 + }, + { + "name": "alert_18", + "project": "reporting-app", + "activation_time": "2024-11-09T11:50:30Z", + "notifications": [ + { + "kind": "webhook", + "err": "" + } + ], + "entity_id": "job-entity-18.app-18.result-18", + "entity_kind": "job", + "criteria": { + "count": 6, + "period": "4h" + }, + "event_kind": "concept-drift-detected", + "severity": "low", + "number_of_events": 8 + }, + { + "name": "alert_19", + "project": "tutorial-admin", + "activation_time": "2024-11-10T10:10:10Z", + "notifications": [ + { + "kind": "git", + "err": "" + } + ], + "entity_id": "model-endpoint-19.app-19.result-19", + "entity_kind": "model-endpoint-result", + "criteria": { + "count": 4, + "period": "2h" + }, + "event_kind": "mm-app-anomaly-detected", + "severity": "critical", + "number_of_events": 10 + }, + { + "name": "alert_20", + "project": "workflow-proj", + "activation_time": "2024-11-11T13:30:45Z", + "notifications": [ + { + "kind": "email", + "err": "" + }, + { + "kind": "slack", + "err": "Failed to send notification due to network issue" + } + ], + "entity_id": "model-monitoring-20.app-20.result-20", + "entity_kind": "model-monitoring-application", + "criteria": { + "count": 1, + "period": "30m" + }, + "event_kind": "mm-app-failed", + "severity": "high", + "number_of_events": 5 + } + ] +} diff --git a/src/constants.js b/src/constants.js index 37ae6c60ad..f0ca61af92 100644 --- a/src/constants.js +++ b/src/constants.js @@ -90,6 +90,9 @@ export const JOBS_MONITORING_JOBS_TAB = 'jobs' export const JOBS_MONITORING_WORKFLOWS_TAB = 'workflows' export const JOBS_MONITORING_SCHEDULED_TAB = 'scheduled' +export const ALERTS_PAGE = 'ALERTS' +export const ALERTS_FILTERS = 'alerts' + export const MODELS_PAGE = 'MODELS' export const MODELS_TAB = 'models' export const MODEL_ENDPOINTS_TAB = 'model-endpoints' @@ -563,6 +566,16 @@ export const AUTO_REFRESH_ID = 'auto-refresh' export const AUTO_REFRESH = 'Auto Refresh' export const ANY_TIME = 'Any time' export const STATUS_FILTER_NAME = 'state' +export const APPLICATION = 'application' +export const ENDPOINT = 'endpoint' +export const ENTITY_TYPE = 'entity-type' +export const ENTITY_ID = 'entity-id' +export const EVENT_TYPE = 'event-type' +export const SEVERITY = 'severity' +export const JOB = 'job' +export const JOB_NAME = 'job-name' +export const ENDPOINT_APPLICATION = 'endpoint-application' +export const ENDPOINT_RESULT = 'endpoint-result' export const FILTER_MENU = 'filterMenu' export const FILTER_MENU_MODAL = 'filterMenuModal' diff --git a/src/elements/AlertsTableRow/AlertsTableRow.js b/src/elements/AlertsTableRow/AlertsTableRow.js new file mode 100644 index 0000000000..358537fa31 --- /dev/null +++ b/src/elements/AlertsTableRow/AlertsTableRow.js @@ -0,0 +1,78 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import { useRef } from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { useParams } from 'react-router-dom' + +import TableCell from '../TableCell/TableCell' + +import { DETAILS_OVERVIEW_TAB } from '../../constants' +import { getFunctionIdentifier } from '../../utils/getUniqueIdentifier' + +const AlertsTableRow = ({ handleExpandRow, handleSelectItem, rowItem, selectedItem }) => { + const parent = useRef() + const params = useParams() + + const rowClassNames = classnames( + 'table-row', + 'table-body-row', + 'parent-row', + getFunctionIdentifier(selectedItem, true) === rowItem.data?.ui?.identifierUnique + ) + + return ( + + <> + {rowItem.content.map((value, index) => { + return ( + !value.hidden && ( + + ) + ) + })} + + + ) +} + +AlertsTableRow.propTypes = { + handleSelectItem: PropTypes.func.isRequired, + mainRowItemsCount: PropTypes.number, + rowIndex: PropTypes.number.isRequired, + rowItem: PropTypes.shape({}).isRequired, + selectedItem: PropTypes.shape({}).isRequired +} + +export default AlertsTableRow diff --git a/src/elements/TableCell/TableCell.js b/src/elements/TableCell/TableCell.js index 97ea553654..8c2071c03a 100644 --- a/src/elements/TableCell/TableCell.js +++ b/src/elements/TableCell/TableCell.js @@ -37,6 +37,12 @@ import { truncateUid } from '../../utils' import { ReactComponent as ArtifactView } from 'igz-controls/images/eye-icon.svg' import { ReactComponent as Arrow } from 'igz-controls/images/arrow.svg' +import { ReactComponent as Package } from 'igz-controls/images/package.svg' +import { ReactComponent as Slack } from 'igz-controls/images/slack-icon.svg' +import { ReactComponent as Git } from 'igz-controls/images/navbar/mlrun-jobs-and-workflows.svg' +import { ReactComponent as Webhook } from 'igz-controls/images/model-serving-mini.svg' + +import { capitalize } from 'lodash' const TableCell = ({ className = '', @@ -119,6 +125,24 @@ const TableCell = ({ ))} ) + } else if (data.headerId === 'notifications') { + return ( + + {data.value.map(icon => ( +
+ } + > + {icon.kind === 'email' && } + {icon.kind === 'slack' && } + {icon.kind === 'git' && } + {icon.kind === 'webhook' && } + +
+ ))} + + ) } else if (Array.isArray(data.value)) { return ( diff --git a/src/elements/TableTypeCell/TableTypeCell.js b/src/elements/TableTypeCell/TableTypeCell.js index 6fc068769b..95a0bb9f57 100644 --- a/src/elements/TableTypeCell/TableTypeCell.js +++ b/src/elements/TableTypeCell/TableTypeCell.js @@ -84,12 +84,13 @@ const TableTypeCell = ({ className = '', data }) => { > {typesOfJob[data.value]?.icon ?? capitalize(data.value)} + {data.text &&
{data.value}
} ) } TableTypeCell.propTypes = { - className: PropTypes.string, + text: PropTypes.string, data: PropTypes.shape({}).isRequired } diff --git a/src/reducers/alertsReducer.js b/src/reducers/alertsReducer.js new file mode 100644 index 0000000000..462d88c0ed --- /dev/null +++ b/src/reducers/alertsReducer.js @@ -0,0 +1,68 @@ +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' + +import alertsApi from '../api/alerts-api' +import { defaultPendingHandler } from './redux.util' + +const initialState = { + alerts: [], + error: null, + loading: false +} + +export const fetchAlert = createAsyncThunk( + 'fetchAlert', + ({ project, filters, config = {}, params = {} }) => { + return alertsApi.getAlerts(project, filters, config).then(({ data }) => { + return data + }) + } +) +export const fetchAlerts = createAsyncThunk( + 'fetchAlerts', + ({ project, filters, config = {}, params = {} }) => { + return alertsApi.getAlert(project, filters, (config = {}), (params = {})).then(({ data }) => { + return {} + }) + } +) + +const alertsSlice = createSlice({ + name: 'alertsStore', + initialState, + reducers: { + removeAlerts(state) { + state.alerts = initialState.alerts + state.error = null + state.loading = false + } + }, + extraReducers: builder => { + builder + .addCase(fetchAlert.pending, defaultPendingHandler) + .addCase(fetchAlert.fulfilled, (state, action) => { + state.alerts = action.payload + state.loading = false + }) + .addCase(fetchAlert.rejected, (state, action) => { + state.alerts = [] + state.error = action.payload + state.loading = false + }) + .addCase(fetchAlerts.pending, state => { + state.loading = true + state.error = null + }) + .addCase(fetchAlerts.fulfilled, (state, action) => { + state.loading = false + state.alerts = action.payload + }) + .addCase(fetchAlerts.rejected, (state, action) => { + state.loading = false + state.error = action.payload + }) + } +}) + +export const { removeAlerts } = alertsSlice.actions + +export default alertsSlice.reducer diff --git a/src/store/toolkitStore.js b/src/store/toolkitStore.js index abbc5df990..137061bc99 100644 --- a/src/store/toolkitStore.js +++ b/src/store/toolkitStore.js @@ -19,6 +19,7 @@ such restriction. */ import { configureStore } from '@reduxjs/toolkit' +import alertsStore from '../reducers/alertsReducer' import appStore from '../reducers/appReducer' import artifactsStore from '../reducers/artifactsReducer' import detailsStore from '../reducers/detailsReducer' @@ -36,6 +37,7 @@ import workflowsStore from '../reducers/workflowReducer' const toolkitStore = configureStore({ reducer: { + alertsStore, appStore, artifactsStore, detailsStore, diff --git a/src/utils/createAlertsContent.js b/src/utils/createAlertsContent.js new file mode 100644 index 0000000000..c87a4283c5 --- /dev/null +++ b/src/utils/createAlertsContent.js @@ -0,0 +1,121 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ + +import { formatDatetime } from './datetime' +import { JOB_KIND_SPARK } from '../constants' + +export const createAlertRowData = ({ name, ...alert }) => { + return { + data: { + ...alert + }, + content: [ + { + id: 'key.alert', // @TODO create id + headerId: 'alertname', + headerLabel: 'Alert Name', + value: name, + className: 'table-cell-1', + getLink: () => {}, + showStatus: true, + tooltip: name, + type: 'link' + }, + { + headerId: 'projectname', + headerLabel: 'Project name', + id: 'projectname', + // id: `projectName.${identifierUnique}`, @TODO create projectName.id + value: alert.project, + className: 'table-cell-1' + }, + { + headerId: 'eventtype', + headerLabel: 'Event Type', + id: 'eventtype', + // id: `type.${identifierUnique}`, @TODO create id + value: alert.event_kind, + className: 'table-cell-1' + }, + { + headerId: 'entityid', + headerLabel: 'Entity ID', + id: 'entityid', + // id: `uid.${identifierUnique}`,//@TODO create id + value: alert.entity_id, // @TODO add id based on type + className: 'table-cell-1' + }, + { + headerId: 'entitytype', + headerLabel: 'Entity Type', + id: 'entitytype', + // id: `entitytype.${identifierUnique}`,//@TODO create id + // value: type, //@TODO add jobs label type to alert object in response + value: 'job', + className: 'table-cell-small', + type: 'type' + }, + { + // id: `timestamp.${alert.ui.identifierUnique}`, //@TODO create id + headerId: 'timestamp', + id: 'timestamp', + headerLabel: 'Timestamp', + value: formatDatetime(alert.activation_time, '-'), + className: 'table-cell-1' + }, + { + headerId: 'severity', + headerLabel: 'severity', + id: 'severity', + // id: `severity.${identifierUnique}`, //@TODO create id + // value: alert.severity, + value: JOB_KIND_SPARK, + text: alert.severity, + className: 'table-cell-1', + type: 'type' + }, + { + headerId: 'criteriacount', + id: 'criteriacount', + headerLabel: 'Trigger count', + // id: `severity.${identifierUnique}`, //@TODO create id + value: alert.criteria.count, + className: 'table-cell-1' + }, + { + // id: `criteriatime.${alert.ui.identifierUnique}`,//@TODO create id + id: 'criteriatime', + headerId: 'criteriatime', + headerLabel: 'Trigger time', + value: alert.criteria.period, + // value: formatDatetime(alert.status?.first_request, '-'), + className: 'table-cell-1' + }, + { + // id: `notifications.${alert.ui.identifierUnique}`,//@TODO create id + id: 'notifications', + headerId: 'notifications', + headerLabel: 'Notifications', + value: alert.notifications || [], + className: 'table-cell-1' + } + ] + } +} From 529f22700490d6c889ed13a6dfc0bbe55909b79d Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Mon, 25 Nov 2024 09:22:02 +0200 Subject: [PATCH 02/14] add createAlertsContent logic --- src/components/Files/Files.js | 1 + .../ProjectsAlerts/ProjectsAlerts.js | 2 +- src/components/ProjectsAlerts/alertsData.json | 72 ++++---- src/components/ProjectsAlerts/critical.svg | 3 + src/components/ProjectsAlerts/endpoint.svg | 3 + src/components/ProjectsAlerts/git.svg | 10 ++ src/components/ProjectsAlerts/job.svg | 3 + src/components/ProjectsAlerts/low.svg | 3 + src/components/ProjectsAlerts/mail.svg | 3 + src/components/ProjectsAlerts/normal.svg | 3 + src/components/ProjectsAlerts/slack.svg | 10 ++ src/components/ProjectsAlerts/webhook.svg | 9 + src/components/Table/table.scss | 21 ++- src/constants.js | 5 + src/utils/createAlertsContent.js | 154 ++++++++++++++---- 15 files changed, 236 insertions(+), 66 deletions(-) create mode 100644 src/components/ProjectsAlerts/critical.svg create mode 100644 src/components/ProjectsAlerts/endpoint.svg create mode 100644 src/components/ProjectsAlerts/git.svg create mode 100644 src/components/ProjectsAlerts/job.svg create mode 100644 src/components/ProjectsAlerts/low.svg create mode 100644 src/components/ProjectsAlerts/mail.svg create mode 100644 src/components/ProjectsAlerts/normal.svg create mode 100644 src/components/ProjectsAlerts/slack.svg create mode 100644 src/components/ProjectsAlerts/webhook.svg diff --git a/src/components/Files/Files.js b/src/components/Files/Files.js index bf15a458f1..bbedd49c11 100644 --- a/src/components/Files/Files.js +++ b/src/components/Files/Files.js @@ -135,6 +135,7 @@ const Files = () => { ) .unwrap() .then(result => { + console.log(result) if (result) { setFiles(result) setMaxArtifactsErrorIsShown(result.length === 1000) diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js index 43f1795eba..3290358a61 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -37,7 +37,7 @@ const ProjectsAlerts = () => { const filtersStore = useSelector(store => store.filtersStore) const alertsFiltersConfig = useMemo(() => getAlertsFiltersConfig(), []) - + console.log(alerts) const refreshAlertsCallback = useCallback(filters => { console.log(filters) }, []) diff --git a/src/components/ProjectsAlerts/alertsData.json b/src/components/ProjectsAlerts/alertsData.json index 7461874c40..b1bf066b2b 100644 --- a/src/components/ProjectsAlerts/alertsData.json +++ b/src/components/ProjectsAlerts/alertsData.json @@ -12,16 +12,24 @@ { "kind": "webhook", "err": "Timeout error" + }, + { + "kind": "git", + "err": "Error" + }, + { + "kind": "slack", + "err": "Failed to send notification due to network issue" } ], - "entity_id": "model-endpoint-1.app-1.result-1", + "entity_id": "5662c7a4470ef28ed1df450e80d37624de7e1749.histogram-data-drift.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 3, "period": "30m" }, "event_kind": "data-drift-detected", - "severity": "critical", + "severity": "medium", "number_of_events": 15 }, { @@ -38,7 +46,7 @@ "err": "" } ], - "entity_id": "job-entity-2.app-2.result-2", + "entity_id": "trainer-train.5a8037bd46cf192ce7e94b25f06a8d13", "entity_kind": "job", "criteria": { "count": 10, @@ -58,7 +66,7 @@ "err": "" } ], - "entity_id": "model-endpoint-3.app-3.result-3", + "entity_id": "application-1", "entity_kind": "model-monitoring-application", "criteria": { "count": 2, @@ -82,8 +90,8 @@ "err": "Authentication failed" } ], - "entity_id": "model-monitoring-4.app-4.result-4", - "entity_kind": "model-monitoring-application", + "entity_id": "d5e6f4b3970ef28ed1df450e80d37624de7e1749.feature-importance.general_drift", + "entity_kind": "model-endpoint-result", "criteria": { "count": 8, "period": "1h" @@ -102,14 +110,14 @@ "err": "" } ], - "entity_id": "job-entity-5.app-5.result-5", + "entity_id": "batch-inference-v2-infer.7ad15b839fc206e4ba9f02c84f31e57a", "entity_kind": "job", "criteria": { "count": 4, "period": "3h" }, "event_kind": "system-performance-suspected", - "severity": "critical", + "severity": "high", "number_of_events": 20 }, { @@ -122,7 +130,7 @@ "err": "SMTP server not reachable" } ], - "entity_id": "model-endpoint-6.app-6.result-6", + "entity_id": "a2b4c6d8970ef28ed1df450e80d37624de7e1749.statistical-shift.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 6, @@ -142,8 +150,8 @@ "err": "" } ], - "entity_id": "model-monitoring-7.app-7.result-7", - "entity_kind": "model-monitoring-application", + "entity_id": "c6d3e7f0970ef28ed1df450e80d37624de7e1749.data-quality-issue.drift", + "entity_kind": "model-endpoint-result", "criteria": { "count": 1, "period": "30m" @@ -162,14 +170,14 @@ "err": "Failed to send notification due to network issue" } ], - "entity_id": "job-entity-8.app-8.result-8", + "entity_id": "batch-inference-v1-infer.c3b47590a6821d7ef9f2ab04c8e163a5", "entity_kind": "job", "criteria": { "count": 9, "period": "4h" }, "event_kind": "concept-drift-detected", - "severity": "critical", + "severity": "medium", "number_of_events": 16 }, { @@ -186,7 +194,7 @@ "err": "" } ], - "entity_id": "model-endpoint-9.app-9.result-9", + "entity_id": "d9e8f5b4970ef28ed1df450e80d37624de7e1749.input-missing-values.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 7, @@ -206,14 +214,14 @@ "err": "" } ], - "entity_id": "model-endpoint-10.app-10.result-10", + "entity_id": "c2a3e1f8970ef28ed1df450e80d37624de7e1749.class-distribution.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 5, "period": "2h" }, "event_kind": "system-performance-detected", - "severity": "low", + "severity": "medium", "number_of_events": 9 }, { @@ -230,14 +238,14 @@ "err": "API endpoint not responding" } ], - "entity_id": "model-monitoring-11.app-11.result-11", - "entity_kind": "model-monitoring-application", + "entity_id": "f3d4b5a1970ef28ed1df450e80d37624de7e1749.outlier-detection.general_drift", + "entity_kind": "model-endpoint-result", "criteria": { "count": 10, "period": "3h" }, "event_kind": "mm-app-anomaly-suspected", - "severity": "critical", + "severity": "high", "number_of_events": 13 }, { @@ -250,7 +258,7 @@ "err": "" } ], - "entity_id": "job-entity-12.app-12.result-12", + "entity_id": "tutorial-function.af2de87229ab49139748db785e0c3d6b", "entity_kind": "job", "criteria": { "count": 3, @@ -270,14 +278,14 @@ "err": "SMTP server not reachable" } ], - "entity_id": "model-endpoint-13.app-13.result-13", + "entity_id": "d5e6f4b3970ef28ed1df450e80d37624de7e1749.feature-importance.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 8, "period": "4h" }, "event_kind": "concept-drift-suspected", - "severity": "critical", + "severity": "high", "number_of_events": 6 }, { @@ -290,7 +298,7 @@ "err": "" } ], - "entity_id": "model-monitoring-14.app-14.result-14", + "entity_id": "application-2", "entity_kind": "model-monitoring-application", "criteria": { "count": 2, @@ -310,7 +318,7 @@ "err": "" } ], - "entity_id": "job-entity-15.app-15.result-15", + "entity_id": "get-data.a23bcd3ef5d9f0174abc8e09324a76bc", "entity_kind": "job", "criteria": { "count": 5, @@ -330,14 +338,14 @@ "err": "Failed to send notification due to network issue" } ], - "entity_id": "model-endpoint-16.app-16.result-16", + "entity_id": "c9e7f6a3970ef28ed1df450e80d37624de7e1749.null-value-frequency.general_drift", "entity_kind": "model-endpoint-result", "criteria": { "count": 7, "period": "1h" }, "event_kind": "system-performance-suspected", - "severity": "critical", + "severity": "high", "number_of_events": 17 }, { @@ -354,8 +362,8 @@ "err": "" } ], - "entity_id": "model-monitoring-17.app-17.result-17", - "entity_kind": "model-monitoring-application", + "entity_id": "b4d5f8e7970ef28ed1df450e80d37624de7e1749.imbalance-ratio.general_drift", + "entity_kind": "model-endpoint-result", "criteria": { "count": 9, "period": "3h" @@ -374,7 +382,7 @@ "err": "" } ], - "entity_id": "job-entity-18.app-18.result-18", + "entity_id": "get-dev.af12d780b9ce4a6c0357ef3941b5ad68", "entity_kind": "job", "criteria": { "count": 6, @@ -401,7 +409,7 @@ "period": "2h" }, "event_kind": "mm-app-anomaly-detected", - "severity": "critical", + "severity": "high", "number_of_events": 10 }, { @@ -418,8 +426,8 @@ "err": "Failed to send notification due to network issue" } ], - "entity_id": "model-monitoring-20.app-20.result-20", - "entity_kind": "model-monitoring-application", + "entity_id": "d5e6f4b3970ef28ed1df450e80d37624de7e1749.feature-importance.general_drift", + "entity_kind": "model-endpoint-result", "criteria": { "count": 1, "period": "30m" diff --git a/src/components/ProjectsAlerts/critical.svg b/src/components/ProjectsAlerts/critical.svg new file mode 100644 index 0000000000..34d57e917b --- /dev/null +++ b/src/components/ProjectsAlerts/critical.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/endpoint.svg b/src/components/ProjectsAlerts/endpoint.svg new file mode 100644 index 0000000000..a272f69118 --- /dev/null +++ b/src/components/ProjectsAlerts/endpoint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/git.svg b/src/components/ProjectsAlerts/git.svg new file mode 100644 index 0000000000..3f5b868fbb --- /dev/null +++ b/src/components/ProjectsAlerts/git.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/ProjectsAlerts/job.svg b/src/components/ProjectsAlerts/job.svg new file mode 100644 index 0000000000..fee73b3b7d --- /dev/null +++ b/src/components/ProjectsAlerts/job.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/low.svg b/src/components/ProjectsAlerts/low.svg new file mode 100644 index 0000000000..a563a956ed --- /dev/null +++ b/src/components/ProjectsAlerts/low.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/mail.svg b/src/components/ProjectsAlerts/mail.svg new file mode 100644 index 0000000000..e3fc6513c5 --- /dev/null +++ b/src/components/ProjectsAlerts/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/normal.svg b/src/components/ProjectsAlerts/normal.svg new file mode 100644 index 0000000000..888b8eb777 --- /dev/null +++ b/src/components/ProjectsAlerts/normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ProjectsAlerts/slack.svg b/src/components/ProjectsAlerts/slack.svg new file mode 100644 index 0000000000..71cbe06b14 --- /dev/null +++ b/src/components/ProjectsAlerts/slack.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/ProjectsAlerts/webhook.svg b/src/components/ProjectsAlerts/webhook.svg new file mode 100644 index 0000000000..cdb37ad900 --- /dev/null +++ b/src/components/ProjectsAlerts/webhook.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index d55267ef06..b26def55b0 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -99,6 +99,20 @@ flex: 1; max-width: 150px; } + + &-notification { + + .notifications { + margin-right:12px + } + + .notification-fail { + + path { + fill: #C4C2C8; + } + } + } } &:has(.actions-menu__container-active) { @@ -200,7 +214,7 @@ * { visibility: hidden; } - + .chip { visibility: hidden; @@ -266,6 +280,11 @@ .chip_short { max-width: 100px; } + + .severity-cell { + display: flex; + gap: 4px; + } } } } diff --git a/src/constants.js b/src/constants.js index f0ca61af92..f82e53c44a 100644 --- a/src/constants.js +++ b/src/constants.js @@ -92,6 +92,11 @@ export const JOBS_MONITORING_SCHEDULED_TAB = 'scheduled' export const ALERTS_PAGE = 'ALERTS' export const ALERTS_FILTERS = 'alerts' +export const MODEL_ENDPOINT_RESULT = 'model-endpoint-result' +export const MODEL_MONITORING_APPLICATION = 'model-monitoring-application' +export const SEVERITY_LOW = 'low' +export const SEVERITY_MEDIUM = 'medium' +export const SEVERITY_HIGH = 'high' export const MODELS_PAGE = 'MODELS' export const MODELS_TAB = 'models' diff --git a/src/utils/createAlertsContent.js b/src/utils/createAlertsContent.js index c87a4283c5..3419df29cf 100644 --- a/src/utils/createAlertsContent.js +++ b/src/utils/createAlertsContent.js @@ -17,79 +17,168 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ +import { upperFirst } from 'lodash' import { formatDatetime } from './datetime' -import { JOB_KIND_SPARK } from '../constants' + +import { ReactComponent as Application } from 'igz-controls/images/application-icon.svg' +import { ReactComponent as Endpoint } from '../components/ProjectsAlerts/endpoint.svg' +import { ReactComponent as Job } from '../components/ProjectsAlerts/job.svg' +import { ReactComponent as Low } from '../components/ProjectsAlerts/low.svg' +import { ReactComponent as Normal } from '../components/ProjectsAlerts/normal.svg' +import { ReactComponent as High } from '../components/ProjectsAlerts/critical.svg' +import { ReactComponent as Git } from '../components/ProjectsAlerts/git.svg' +import { ReactComponent as Mail } from '../components/ProjectsAlerts/mail.svg' +import { ReactComponent as Webhook } from '../components/ProjectsAlerts/webhook.svg' +import { ReactComponent as Slack } from '../components/ProjectsAlerts/slack.svg' + +import { + APPLICATION, + ENDPOINT, + JOB, + MODEL_ENDPOINT_RESULT, + MODEL_MONITORING_APPLICATION, + SEVERITY_HIGH, + SEVERITY_LOW, + SEVERITY_MEDIUM +} from '../constants' + +const getEntityTypeData = entityType => { + switch (entityType) { + case MODEL_ENDPOINT_RESULT: + return { + value: , + tooltip: upperFirst(ENDPOINT) + } + case MODEL_MONITORING_APPLICATION: + return { + value: , + tooltip: upperFirst(APPLICATION) + } + case JOB: + return { + value: , + tooltip: upperFirst(JOB) + } + } +} + +const getSeverityData = severity => { + switch (severity) { + case SEVERITY_LOW: + return { + value: ( +
+ + {upperFirst(SEVERITY_LOW)} +
+ ), + tooltip: upperFirst(SEVERITY_LOW) + } + case SEVERITY_MEDIUM: + return { + value: ( +
+ + {upperFirst(SEVERITY_MEDIUM)} +
+ ), + tooltip: upperFirst(SEVERITY_MEDIUM) + } + case SEVERITY_HIGH: + return { + value: ( +
+ + {upperFirst(SEVERITY_MEDIUM)} +
+ ), + + tooltip: upperFirst(SEVERITY_HIGH) + } + } +} + +const alertsNotifications = { + webhook: , + git: , + slack: , + email: +} + +const getNotificationData = notifications => + notifications.map(notification => { + const failClass = notification.err === '' ? 'notification-fail' : '' + + return { + icon: ( +
{alertsNotifications[notification.kind]}
+ ), + tooltip: upperFirst(notification.kind) + } + }) export const createAlertRowData = ({ name, ...alert }) => { + // TODO: create ID for alert return { data: { ...alert }, content: [ { - id: 'key.alert', // @TODO create id - headerId: 'alertname', + id: `alertName.${alert.id}`, + headerId: 'alertName', headerLabel: 'Alert Name', value: name, className: 'table-cell-1', - getLink: () => {}, + getLink: () => {}, // TODO create link showStatus: true, tooltip: name, type: 'link' }, { - headerId: 'projectname', + id: `projectName.${alert.id}`, + headerId: 'projectName', headerLabel: 'Project name', - id: 'projectname', - // id: `projectName.${identifierUnique}`, @TODO create projectName.id value: alert.project, className: 'table-cell-1' }, { - headerId: 'eventtype', + id: `eventType.${alert.id}`, + headerId: 'eventType', headerLabel: 'Event Type', - id: 'eventtype', - // id: `type.${identifierUnique}`, @TODO create id - value: alert.event_kind, + value: alert.event_kind.split('-').join(' '), className: 'table-cell-1' }, { - headerId: 'entityid', + id: `entityId.${alert.id}`, + headerId: 'entityId', headerLabel: 'Entity ID', - id: 'entityid', - // id: `uid.${identifierUnique}`,//@TODO create id - value: alert.entity_id, // @TODO add id based on type + value: alert.entity_id, className: 'table-cell-1' }, { - headerId: 'entitytype', + id: `entityType.${alert.id}`, + headerId: 'entityType', headerLabel: 'Entity Type', - id: 'entitytype', - // id: `entitytype.${identifierUnique}`,//@TODO create id - // value: type, //@TODO add jobs label type to alert object in response - value: 'job', + value: getEntityTypeData(alert.entity_kind).value, className: 'table-cell-small', - type: 'type' + tooltip: getEntityTypeData(alert.entity_kind).tooltip }, { - // id: `timestamp.${alert.ui.identifierUnique}`, //@TODO create id + id: `timestamp.${alert.id}`, headerId: 'timestamp', - id: 'timestamp', headerLabel: 'Timestamp', value: formatDatetime(alert.activation_time, '-'), className: 'table-cell-1' }, { + id: `entityType.${alert.id}`, headerId: 'severity', headerLabel: 'severity', - id: 'severity', - // id: `severity.${identifierUnique}`, //@TODO create id - // value: alert.severity, - value: JOB_KIND_SPARK, - text: alert.severity, - className: 'table-cell-1', - type: 'type' + value: getSeverityData(alert.severity).value, + tooltip: getSeverityData(alert.severity).tooltip, + className: 'table-cell-1' }, { headerId: 'criteriacount', @@ -113,8 +202,9 @@ export const createAlertRowData = ({ name, ...alert }) => { id: 'notifications', headerId: 'notifications', headerLabel: 'Notifications', - value: alert.notifications || [], - className: 'table-cell-1' + value: getNotificationData(alert.notifications), + className: 'table-cell-1 table-cell-notification', + type: 'icons' } ] } From 319d29eecb0ab752086168e56e459f810e8faac2 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Mon, 25 Nov 2024 17:28:10 +0200 Subject: [PATCH 03/14] update createAlertRowData logic --- .../ProjectsAlerts/ProjectsAlerts.js | 2 +- src/components/ProjectsAlerts/alertsData.json | 20 ++++--- src/components/Table/table.scss | 10 +--- src/elements/TableCell/TableCell.js | 24 --------- src/utils/createAlertsContent.js | 53 ++++++++++++------- 5 files changed, 50 insertions(+), 59 deletions(-) diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js index 3290358a61..43f1795eba 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -37,7 +37,7 @@ const ProjectsAlerts = () => { const filtersStore = useSelector(store => store.filtersStore) const alertsFiltersConfig = useMemo(() => getAlertsFiltersConfig(), []) - console.log(alerts) + const refreshAlertsCallback = useCallback(filters => { console.log(filters) }, []) diff --git a/src/components/ProjectsAlerts/alertsData.json b/src/components/ProjectsAlerts/alertsData.json index b1bf066b2b..72679035f2 100644 --- a/src/components/ProjectsAlerts/alertsData.json +++ b/src/components/ProjectsAlerts/alertsData.json @@ -11,15 +11,15 @@ }, { "kind": "webhook", - "err": "Timeout error" + "err": "" }, { "kind": "git", - "err": "Error" + "err": "" }, { "kind": "slack", - "err": "Failed to send notification due to network issue" + "err": "" } ], "entity_id": "5662c7a4470ef28ed1df450e80d37624de7e1749.histogram-data-drift.general_drift", @@ -38,12 +38,20 @@ "activation_time": "2024-10-24T12:20:30Z", "notifications": [ { - "kind": "slack", - "err": "Failed to send notification due to network issue" + "kind": "email", + "err": "fail" + }, + { + "kind": "webhook", + "err": "fail" }, { "kind": "git", - "err": "" + "err": "fail" + }, + { + "kind": "slack", + "err": "fail" } ], "entity_id": "trainer-train.5a8037bd46cf192ce7e94b25f06a8d13", diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index b26def55b0..e175e1b27c 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -101,15 +101,10 @@ } &-notification { - - .notifications { - margin-right:12px - } - .notification-fail { - path { - fill: #C4C2C8; + fill: $white; + stroke: $silver; } } } @@ -215,7 +210,6 @@ visibility: hidden; } - .chip { visibility: hidden; } diff --git a/src/elements/TableCell/TableCell.js b/src/elements/TableCell/TableCell.js index 8c2071c03a..97ea553654 100644 --- a/src/elements/TableCell/TableCell.js +++ b/src/elements/TableCell/TableCell.js @@ -37,12 +37,6 @@ import { truncateUid } from '../../utils' import { ReactComponent as ArtifactView } from 'igz-controls/images/eye-icon.svg' import { ReactComponent as Arrow } from 'igz-controls/images/arrow.svg' -import { ReactComponent as Package } from 'igz-controls/images/package.svg' -import { ReactComponent as Slack } from 'igz-controls/images/slack-icon.svg' -import { ReactComponent as Git } from 'igz-controls/images/navbar/mlrun-jobs-and-workflows.svg' -import { ReactComponent as Webhook } from 'igz-controls/images/model-serving-mini.svg' - -import { capitalize } from 'lodash' const TableCell = ({ className = '', @@ -125,24 +119,6 @@ const TableCell = ({ ))} ) - } else if (data.headerId === 'notifications') { - return ( - - {data.value.map(icon => ( -
- } - > - {icon.kind === 'email' && } - {icon.kind === 'slack' && } - {icon.kind === 'git' && } - {icon.kind === 'webhook' && } - -
- ))} - - ) } else if (Array.isArray(data.value)) { return ( diff --git a/src/utils/createAlertsContent.js b/src/utils/createAlertsContent.js index 3419df29cf..f3e07ecdda 100644 --- a/src/utils/createAlertsContent.js +++ b/src/utils/createAlertsContent.js @@ -38,6 +38,7 @@ import { JOB, MODEL_ENDPOINT_RESULT, MODEL_MONITORING_APPLICATION, + SEVERITY, SEVERITY_HIGH, SEVERITY_LOW, SEVERITY_MEDIUM @@ -60,6 +61,10 @@ const getEntityTypeData = entityType => { value: , tooltip: upperFirst(JOB) } + default: + return { + value: + } } } @@ -96,6 +101,10 @@ const getSeverityData = severity => { tooltip: upperFirst(SEVERITY_HIGH) } + default: + return { + value: + } } } @@ -108,18 +117,26 @@ const alertsNotifications = { const getNotificationData = notifications => notifications.map(notification => { - const failClass = notification.err === '' ? 'notification-fail' : '' - return { icon: ( -
{alertsNotifications[notification.kind]}
+
+ {alertsNotifications[notification.kind]} +
), tooltip: upperFirst(notification.kind) } }) export const createAlertRowData = ({ name, ...alert }) => { - // TODO: create ID for alert + const createAlertsId = (activationTime = new Date().toISOString()) => { + const date = new Date(activationTime) + + return date.getTime().toString(36).slice(-7) + } + alert.id = createAlertsId(alert.activation_time) + return { data: { ...alert @@ -131,7 +148,7 @@ export const createAlertRowData = ({ name, ...alert }) => { headerLabel: 'Alert Name', value: name, className: 'table-cell-1', - getLink: () => {}, // TODO create link + getLink: () => {}, //TODO: Implement in ML-8368 showStatus: true, tooltip: name, type: 'link' @@ -173,37 +190,33 @@ export const createAlertRowData = ({ name, ...alert }) => { className: 'table-cell-1' }, { - id: `entityType.${alert.id}`, - headerId: 'severity', - headerLabel: 'severity', + id: `severity.${alert.id}`, + headerId: SEVERITY, + headerLabel: upperFirst(SEVERITY), value: getSeverityData(alert.severity).value, tooltip: getSeverityData(alert.severity).tooltip, className: 'table-cell-1' }, { - headerId: 'criteriacount', - id: 'criteriacount', - headerLabel: 'Trigger count', - // id: `severity.${identifierUnique}`, //@TODO create id + id: `criteriaCount.${alert.id}`, + headerId: 'criteriaCount', + headerLabel: 'Trigger criteria count', value: alert.criteria.count, className: 'table-cell-1' }, { - // id: `criteriatime.${alert.ui.identifierUnique}`,//@TODO create id - id: 'criteriatime', - headerId: 'criteriatime', - headerLabel: 'Trigger time', + id: `criteriaTime.${alert.id}`, + headerId: 'criteriaTime', + headerLabel: 'Trigger criteria time period', value: alert.criteria.period, - // value: formatDatetime(alert.status?.first_request, '-'), className: 'table-cell-1' }, { - // id: `notifications.${alert.ui.identifierUnique}`,//@TODO create id - id: 'notifications', + id: `notifications.${alert.id}`, headerId: 'notifications', headerLabel: 'Notifications', value: getNotificationData(alert.notifications), - className: 'table-cell-1 table-cell-notification', + className: 'table-cell-small table-cell-notification', type: 'icons' } ] From 2fae8fe735cbe7da896005e2e0452e1bb8a16f7e Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Wed, 27 Nov 2024 15:31:50 +0200 Subject: [PATCH 04/14] added entity-type and event-type to parseAlertsQueryParamsCallback --- src/components/ProjectsAlerts/alerts.util.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/ProjectsAlerts/alerts.util.js b/src/components/ProjectsAlerts/alerts.util.js index 3f4a87d27b..a653799ef1 100644 --- a/src/components/ProjectsAlerts/alerts.util.js +++ b/src/components/ProjectsAlerts/alerts.util.js @@ -65,6 +65,15 @@ export const parseAlertsQueryParamsCallback = (paramName, paramValue) => { return filteredStatuses?.length ? filteredStatuses : null } + + if (paramName === ENTITY_TYPE) { + return filterAlertsEntityTypeOptions.find(type => type.id === paramValue)?.id + } + + if (paramName === EVENT_TYPE) { + return filterAlertsEventTypeOptions.find(type => type.id === paramValue)?.id + } + return paramValue } From 14afa07dd978faf2a580af327e95939930cdd3e4 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Sun, 1 Dec 2024 11:33:10 +0200 Subject: [PATCH 05/14] add alerts table, alerts reducer, alerts-api --- src/api/alerts-api.js | 18 +++- .../ProjectsAlerts/ProjectsAlerts.js | 80 +++++++++++++++- .../ProjectsAlerts/ProjectsAlertsFilters.js | 30 ++++-- .../ProjectsAlerts/ProjectsAlertsView.js | 95 ++++++++++++------- src/components/ProjectsAlerts/alerts.scss | 16 ++++ src/components/ProjectsAlerts/alerts.util.js | 32 +++---- src/components/ProjectsAlerts/alertsData.json | 20 ++++ src/components/Table/table.scss | 20 ++-- src/constants.js | 2 + src/elements/AlertsTableRow/AlertsTableRow.js | 2 + src/reducers/alertsReducer.js | 27 +++--- src/utils/createAlertsContent.js | 45 +++++---- src/utils/parseAlert.js | 31 ++++++ 13 files changed, 307 insertions(+), 111 deletions(-) create mode 100644 src/components/ProjectsAlerts/alerts.scss create mode 100644 src/utils/parseAlert.js diff --git a/src/api/alerts-api.js b/src/api/alerts-api.js index ad5f5f298e..8f9616c580 100644 --- a/src/api/alerts-api.js +++ b/src/api/alerts-api.js @@ -20,19 +20,35 @@ such restriction. import dummyData from '../components/ProjectsAlerts/alertsData.json' const alertsApi = { - getAlerts: (project, filters, config = {}, params = {}) => { + getAlerts: (project, filters, config = {}) => { + // TODO:ML-8514 update newConfig & remove dummy data + // const newConfig = { + // ...config, + // params: { + // ...config.params + // } + // } return new Promise(resolve => { setTimeout(() => { resolve({ data: dummyData }) }, 1000) }) + // return mainHttpClient.get(`/projects/${project}/alerts/${alertName}/activations`, newConfig) }, getAlert: (project, alertName, config) => { + // TODO:ML-8514 update newConfig & remove dummy data + // const newConfig = { + // ...config, + // params: { + // ...config.params + // } + // } return new Promise(resolve => { setTimeout(() => { resolve({ data: dummyData }) }, 1000) }) + // return mainHttpClient.get(`/projects/${project}/alerts/activations`, newConfig) } } diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js index d4a96d1813..d34cf9b0b8 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -17,18 +17,32 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useRef, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' import ProjectAlertsView from './ProjectsAlertsView' import { getAlertsFiltersConfig, parseAlertsQueryParamsCallback } from './alerts.util' import { useFiltersFromSearchParams } from '../../hooks/useFiltersFromSearchParams.hook' -import alertsData from './alertsData.json' +import { useParams } from 'react-router-dom' +import { fetchAlerts } from '../../reducers/alertsReducer' +import { useVirtualization } from '../../hooks/useVirtualization.hook' +import { createAlertRowData } from '../../utils/createAlertsContent' +import { useInitialTableFetch } from '../../hooks/useInitialTableFetch.hook' + +import cssVariables from './alerts.scss' const ProjectsAlerts = () => { - const [alerts] = useState(alertsData) + const [alerts, setAlerts] = useState([]) + const [requestErrorMessage, setRequestErrorMessage] = useState('') const [selectedAlert] = useState({}) + const [selectedRowData] = useState({}) + const params = useParams() + const dispatch = useDispatch() + const alertsStore = useSelector(state => state.alertsStore) + + const abortControllerRef = useRef(new AbortController()) const alertsFiltersConfig = useMemo(() => getAlertsFiltersConfig(), []) @@ -37,17 +51,75 @@ const ProjectsAlerts = () => { parseAlertsQueryParamsCallback ) - const refreshAlertsCallback = useCallback(() => {}, []) + const fetchData = useCallback( + filters => { + abortControllerRef.current = new AbortController() + dispatch( + fetchAlerts({ + project: params.id, + filters, + config: { + ui: { + controller: abortControllerRef.current, + setRequestErrorMessage + }, + params: { + format: 'minimal' + } + } + }) + ) + .unwrap() + .then(data => { + setAlerts(data) + }) + }, + [dispatch, params.id] + ) + const tableContent = useMemo(() => { + return alerts.map(alert => createAlertRowData(alert)) + }, [alerts]) + + const refreshAlertsCallback = useCallback( + filters => { + setAlerts([]) + return fetchData(filters) + }, + [fetchData] + ) + + useInitialTableFetch({ + fetchData, + filters: alertsFilters + }) + + const virtualizationConfig = useVirtualization({ + rowsData: { + content: tableContent, + expandedRowsData: selectedRowData, + selectedItem: selectedAlert + }, + heightData: { + headerRowHeight: cssVariables.$alertsHeaderRowHeight, + rowHeight: cssVariables.$alertsRowHeight, + rowHeightExtended: cssVariables.$alertsRowHeightExtended + }, + activateTableScroll: true + }) return ( []} // TODO filters={alertsFilters} pageData={{}} //TODO refreshAlertsCallback={refreshAlertsCallback} + requestErrorMessage={requestErrorMessage} selectedAlert={selectedAlert} + tableContent={tableContent} + virtualizationConfig={virtualizationConfig} /> ) } diff --git a/src/components/ProjectsAlerts/ProjectsAlertsFilters.js b/src/components/ProjectsAlerts/ProjectsAlertsFilters.js index 6e12f3f64c..de803a77b4 100644 --- a/src/components/ProjectsAlerts/ProjectsAlertsFilters.js +++ b/src/components/ProjectsAlerts/ProjectsAlertsFilters.js @@ -17,12 +17,13 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import { useMemo } from 'react' +import { useCallback, useEffect, useMemo } from 'react' -import { truncate } from 'lodash' +import { truncate, upperFirst } from 'lodash' import { useForm, useFormState } from 'react-final-form' import { useSelector } from 'react-redux' +import StatusFilter from '../../common/StatusFilter/StatusFilter' import { FormSelect, FormInput } from 'igz-controls/components' import { FormOnChange } from 'iguazio.dashboard-react-controls/dist/components' @@ -45,10 +46,9 @@ import { FILTER_ALL_ITEMS, JOB, JOB_NAME, - PROJECT_FILTER, + PROJECTS_FILTER, SEVERITY } from '../../constants' -import StatusFilter from '../../common/StatusFilter/StatusFilter' const ProjectsAlertsFilters = () => { const form = useForm() @@ -67,6 +67,22 @@ const ProjectsAlertsFilters = () => { })) }, [projectStore.projectsNames.data]) + const getFieldsToReset = useCallback(entityType => { + const fieldsByType = { + [FILTER_ALL_ITEMS]: [ENTITY_ID], + [APPLICATION]: [ENTITY_ID], + [JOB]: [JOB_NAME], + [ENDPOINT]: [ENDPOINT_APPLICATION, ENDPOINT_RESULT] + } + + const allFields = [ENTITY_ID, JOB_NAME, ENDPOINT_APPLICATION, ENDPOINT_RESULT] + return allFields.filter(field => !(fieldsByType[entityType] ?? []).includes(field)) + }, []) + + useEffect(() => { + getFieldsToReset(entityType).forEach(field => form.change(field, '')) + }, [entityType, form, getFieldsToReset]) + const handleInputChange = (value, inputName) => { form.change(inputName, value || '') } @@ -74,7 +90,7 @@ const ProjectsAlertsFilters = () => { return ( <>
- +
{ {(entityType === FILTER_ALL_ITEMS || entityType === APPLICATION) && (
- + handleInputChange(value, ENTITY_ID)} name={ENTITY_ID} />
)} {entityType === JOB && (
- + handleInputChange(value, JOB_NAME)} name={JOB_NAME} />
)} diff --git a/src/components/ProjectsAlerts/ProjectsAlertsView.js b/src/components/ProjectsAlerts/ProjectsAlertsView.js index ced2c880b5..08c93728fc 100644 --- a/src/components/ProjectsAlerts/ProjectsAlertsView.js +++ b/src/components/ProjectsAlerts/ProjectsAlertsView.js @@ -17,31 +17,33 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs' -import ActionBar from '../ActionBar/ActionBar' -import ProjectsAlertsFilters from './ProjectsAlertsFilters' - -import { ALERTS_FILTERS, ALERTS_PAGE } from '../../constants' - import PropTypes from 'prop-types' + +import ActionBar from '../ActionBar/ActionBar' import AlertsTableRow from '../../elements/AlertsTableRow/AlertsTableRow' -import { useMemo } from 'react' -import { createAlertRowData } from '../../utils/createAlertsContent' +import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs' +import Loader from '../../common/Loader/Loader' +import NoData from '../../common/NoData/NoData' +import ProjectsAlertsFilters from './ProjectsAlertsFilters' import Table from '../Table/Table' +import { getNoDataMessage } from '../../utils/getNoDataMessage' +import { ALERTS_FILTERS, ALERTS_PAGE, FUNCTION_FILTERS } from '../../constants' +import { VIRTUALIZATION_CONFIG } from '../../types' +import { isRowRendered } from '../../hooks/useVirtualization.hook' + const ProjectAlertsView = ({ actionsMenu, - alerts, alertsFiltersConfig, + alertsStore, filters, pageData, refreshAlertsCallback, - selectedAlert + requestErrorMessage, + selectedAlert, + tableContent, + virtualizationConfig }) => { - const tableContent = useMemo(() => { - return alerts.activations.map(alert => createAlertRowData(alert)) - }, [alerts.activations]) - return ( <>
@@ -64,27 +66,45 @@ const ProjectAlertsView = ({
- - {tableContent.map((tableItem, index) => ( - {}} - rowIndex={index} - rowItem={tableItem} - actionsMenu={[]} - selectedItem={selectedAlert} - /> - ))} -
+ {alertsStore.loading ? ( + + ) : tableContent.length === 0 ? ( + + ) : ( + + {tableContent.map( + (tableItem, index) => + isRowRendered(virtualizationConfig, index) && ( + {}} + rowIndex={index} + rowItem={tableItem} + actionsMenu={[]} + selectedItem={selectedAlert} + /> + ) + )} +
+ )}
@@ -95,6 +115,9 @@ const ProjectAlertsView = ({ ProjectAlertsView.propTypes = { alertsFiltersConfig: PropTypes.object.isRequired, filters: PropTypes.object.isRequired, - refreshAlertsCallback: PropTypes.func.isRequired + refreshAlertsCallback: PropTypes.func.isRequired, + requestErrorMessage: PropTypes.string.isRequired, + tableContent: PropTypes.arrayOf(PropTypes.object).isRequired, + virtualizationConfig: VIRTUALIZATION_CONFIG.isRequired } export default ProjectAlertsView diff --git a/src/components/ProjectsAlerts/alerts.scss b/src/components/ProjectsAlerts/alerts.scss new file mode 100644 index 0000000000..eb9b553a95 --- /dev/null +++ b/src/components/ProjectsAlerts/alerts.scss @@ -0,0 +1,16 @@ +@import '~igz-controls/scss/variables'; +@import 'src/scss/mixins'; + +$alertsRowHeight: $rowHeight; +$alertsHeaderRowHeight: $headerRowHeight; +$alertsRowHeightExtended: $rowHeightExtended; + +.alerts-table { + @include rowsHeight($alertsHeaderRowHeight, $alertsRowHeight, $alertsRowHeightExtended); +} + +:export { + alertsRowHeight: $alertsRowHeight; + alertsHeaderRowHeight: $alertsHeaderRowHeight; + alertsRowHeightExtended: $alertsRowHeightExtended; +} diff --git a/src/components/ProjectsAlerts/alerts.util.js b/src/components/ProjectsAlerts/alerts.util.js index a653799ef1..4c63036e28 100644 --- a/src/components/ProjectsAlerts/alerts.util.js +++ b/src/components/ProjectsAlerts/alerts.util.js @@ -30,7 +30,7 @@ import { JOB_KIND_JOB, JOB_NAME, NAME_FILTER, - PROJECT_FILTER, + PROJECTS_FILTER, SEVERITY } from '../../constants' import { @@ -41,16 +41,16 @@ import { export const getAlertsFiltersConfig = () => { return { - [NAME_FILTER]: { label: 'Alert name', initialValue: '' }, + [NAME_FILTER]: { label: 'Alert Name:', initialValue: '' }, [DATES_FILTER]: { label: 'Start time:', initialValue: getDatePickerFilterValue(datePickerPastOptions, PAST_24_HOUR_DATE_OPTION) }, - [PROJECT_FILTER]: { label: 'Project:', initialValue: FILTER_ALL_ITEMS, isModal: true }, + [PROJECTS_FILTER]: { label: 'Project:', initialValue: FILTER_ALL_ITEMS, isModal: true }, [ENTITY_TYPE]: { label: 'Entity Type', initialValue: FILTER_ALL_ITEMS, isModal: true }, [ENTITY_ID]: { label: 'Entity ID:', initialValue: '', isModal: true }, - [JOB_NAME]: { label: 'Job name:', initialValue: '', isModal: true }, - [ENDPOINT_APPLICATION]: { label: 'Endpoint:', initialValue: '', isModal: true }, + [JOB_NAME]: { label: 'Job Name:', initialValue: '', isModal: true }, + [ENDPOINT_APPLICATION]: { label: 'Application Name:', initialValue: '', isModal: true }, [ENDPOINT_RESULT]: { label: 'Result:', initialValue: '', isModal: true }, [SEVERITY]: { label: 'Severity:', initialValue: [FILTER_ALL_ITEMS], isModal: true }, [EVENT_TYPE]: { label: 'Event Type:', initialValue: FILTER_ALL_ITEMS, isModal: true } @@ -101,16 +101,16 @@ export const filterAlertsSeverityOptions = [ export const filterAlertsEventTypeOptions = [ { label: 'All', id: FILTER_ALL_ITEMS }, { label: 'Job Failed', id: 'job-failed' }, - { label: 'Data Drift Detected', id: 'data-drift-detected' }, - { label: 'Data Drift Suspected', id: 'data-drift-suspected' }, - { label: 'Conc Drift Detected', id: 'concept-drift-detected' }, - { label: 'Conc Drift Suspected', id: 'concept-drift-suspected' }, - { label: 'MM Perf. Detected', id: 'model-performance-detected' }, - { label: 'MM Perf. Suspected', id: 'model-performance-suspected' }, - { label: 'S Perf. Detected', id: 'system-performance-detected' }, - { label: 'S Perf. Suspected', id: 'system-performance-suspected' }, - { label: 'MM App Ano. Detected', id: 'mm-app-anomaly-detected' }, - { label: 'MM App Ano. Suspected', id: 'mm-app-anomaly-suspected' }, - { label: 'MM App Failed', id: 'mm-app-failed' }, + { label: 'Data Drift Detected', id: 'data drift detected' }, + { label: 'Data Drift Suspected', id: 'data drift suspected' }, + { label: 'Conc Drift Detected', id: 'concept drift detected' }, + { label: 'Conc Drift Suspected', id: 'concept drift suspected' }, + { label: 'MM Perf. Detected', id: 'model performance detected' }, + { label: 'MM Perf. Suspected', id: 'model performance suspected' }, + { label: 'S Perf. Detected', id: 'system performance detected' }, + { label: 'S Perf. Suspected', id: 'system performance suspected' }, + { label: 'MM App Ano. Detected', id: 'mm app anomaly detected' }, + { label: 'MM App Ano. Suspected', id: 'mm app anomaly suspected' }, + { label: 'MM App Failed', id: 'mm app failed' }, { label: 'MM App Failed', id: 'failed' } ] diff --git a/src/components/ProjectsAlerts/alertsData.json b/src/components/ProjectsAlerts/alertsData.json index 72679035f2..c882070214 100644 --- a/src/components/ProjectsAlerts/alertsData.json +++ b/src/components/ProjectsAlerts/alertsData.json @@ -1,6 +1,7 @@ { "activations": [ { + "id": "a1b2c3d4-5662c7a4470ef28ed1df450e80d37624de7e1749", "name": "alert_1", "project": "workflow-proj", "activation_time": "2024-10-23T10:15:30Z", @@ -33,6 +34,7 @@ "number_of_events": 15 }, { + "id": "b2c3d4e5-6772c8b5581ef39fd2df561f91e47635f8f2e263", "name": "alert_2", "project": "tutorial-admin", "activation_time": "2024-10-24T12:20:30Z", @@ -65,6 +67,7 @@ "number_of_events": 7 }, { + "id": "c3d4e5f6-7883d9c6692ef40fe3df672a02f58746f9f3e374", "name": "alert_3", "project": "workflow-proj", "activation_time": "2024-10-25T14:45:00Z", @@ -85,6 +88,7 @@ "number_of_events": 12 }, { + "id": "d4e5f6g7-8994ea1a780f0b1fg4df783b13f69857a0d4e485", "name": "alert_4", "project": "analysis-hub", "activation_time": "2024-10-26T08:10:20Z", @@ -109,6 +113,7 @@ "number_of_events": 5 }, { + "id": "e5f6g7h8-9105fb2b891f1c2hg5df894c24f70968b1e5f596", "name": "alert_5", "project": "reporting-app", "activation_time": "2024-10-27T16:55:10Z", @@ -129,6 +134,7 @@ "number_of_events": 20 }, { + "id": "f6g7h8i9-1026fc3c902f2d3hi6df915d35g81079c2f6g717", "name": "alert_6", "project": "tutorial-admin", "activation_time": "2024-10-28T09:25:40Z", @@ -149,6 +155,7 @@ "number_of_events": 3 }, { + "id": "g7h8i9j0-2137gd4d013f3e4ij7df026e46h92180d3g7h829", "name": "alert_7", "project": "workflow-proj", "activation_time": "2024-10-29T07:30:50Z", @@ -169,6 +176,7 @@ "number_of_events": 4 }, { + "id": "h8i9j0k1-3248he5e124g4f5jk8df137f57i032c4h8i9310", "name": "alert_8", "project": "analysis-hub", "activation_time": "2024-10-30T11:15:10Z", @@ -189,6 +197,7 @@ "number_of_events": 16 }, { + "id": "i9j0k1l2-4359if6f235h5g6kl9df248g68j143d5i9j0421", "name": "alert_9", "project": "reporting-app", "activation_time": "2024-10-31T17:05:35Z", @@ -213,6 +222,7 @@ "number_of_events": 18 }, { + "id": "j0k1l2m3-5460jg7g346i6h7lm0df359h79k254e6j0k1532", "name": "alert_10", "project": "workflow-proj", "activation_time": "2024-11-01T06:20:15Z", @@ -233,6 +243,7 @@ "number_of_events": 9 }, { + "id": "k1l2m3n4-6571kh8h457j7i8mn1df460i80l365f7k1l2643", "name": "alert_11", "project": "analysis-hub", "activation_time": "2024-11-02T14:10:50Z", @@ -257,6 +268,7 @@ "number_of_events": 13 }, { + "id": "l2m3n4o5-7682li9i568k8jno2df571j91m476g8l2m3754", "name": "alert_12", "project": "tutorial-admin", "activation_time": "2024-11-03T18:35:25Z", @@ -277,6 +289,7 @@ "number_of_events": 14 }, { + "id": "m3n4o5p6-8793mj0j679l9knp3df682k02n587h9m3n4875", "name": "alert_13", "project": "workflow-proj", "activation_time": "2024-11-04T09:05:00Z", @@ -297,6 +310,7 @@ "number_of_events": 6 }, { + "id": "n4o5p6q7-9804nk1k780m0lop4df793l13o698i0n4o5986", "name": "alert_14", "project": "reporting-app", "activation_time": "2024-11-05T13:15:45Z", @@ -317,6 +331,7 @@ "number_of_events": 11 }, { + "id": "o5p6q7r8-0915ol2l891n1mp5df804m24p709j1o5p6097", "name": "alert_15", "project": "tutorial-admin", "activation_time": "2024-11-06T12:25:20Z", @@ -337,6 +352,7 @@ "number_of_events": 2 }, { + "id": "p6q7r8s9-1026pm3m902o2nq6df915n35q810k2p6q7108", "name": "alert_16", "project": "workflow-proj", "activation_time": "2024-11-07T14:00:35Z", @@ -357,6 +373,7 @@ "number_of_events": 17 }, { + "id": "q7r8s9t0-2137qn4n013p3or7df026o46r92190l3q7r8219", "name": "alert_17", "project": "analysis-hub", "activation_time": "2024-11-08T08:40:55Z", @@ -381,6 +398,7 @@ "number_of_events": 19 }, { + "id": "r8s9t0u1-3248ro5o124q4ps8df137p57s032m4r8s9320", "name": "alert_18", "project": "reporting-app", "activation_time": "2024-11-09T11:50:30Z", @@ -401,6 +419,7 @@ "number_of_events": 8 }, { + "id":"s9t0u1v2-4359sp6p235r4qt9df248q68t143n5s9t0421", "name": "alert_19", "project": "tutorial-admin", "activation_time": "2024-11-10T10:10:10Z", @@ -421,6 +440,7 @@ "number_of_events": 10 }, { + "id": "t0u1v2w3-5460tq7q346s5ru0df359r79u254o6t0u1532", "name": "alert_20", "project": "workflow-proj", "activation_time": "2024-11-11T13:30:45Z", diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index e175e1b27c..5a204f9d26 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -101,11 +101,8 @@ } &-notification { - .notification-fail { - path { - fill: $white; - stroke: $silver; - } + .notification-fail svg > * { + fill: $silver; } } } @@ -118,16 +115,16 @@ &.table { &__scrolled { .table-cell-name { - &:after { - content: ''; + &::after { position: absolute; top: 0; - bottom: 0; right: 0; + bottom: 0; width: 5px; background-color: inherit; border-right: $secondaryBorder; - box-shadow: 2px 0px 2px -1px rgba($black, 0.2); + box-shadow: 2px 0 2px -1px rgba($black, 0.2); + content: ''; } } @@ -206,7 +203,8 @@ &_hidden { font-size: 0; - * { + + * { visibility: hidden; } @@ -238,9 +236,9 @@ a { position: relative; + width: 100%; margin: 0; text-decoration: none; - width: 100%; span { display: block; diff --git a/src/constants.js b/src/constants.js index 45dda23527..c5fed2ce7c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -99,6 +99,7 @@ export const MODEL_MONITORING_APPLICATION = 'model-monitoring-application' export const SEVERITY_LOW = 'low' export const SEVERITY_MEDIUM = 'medium' export const SEVERITY_HIGH = 'high' +export const SEVERITY_CRITICAL = 'critical' export const MODELS_PAGE = 'MODELS' export const MODELS_TAB = 'models' @@ -568,6 +569,7 @@ export const LABELS_FILTER = 'labels' export const NAME_FILTER = 'name' export const DATES_FILTER = 'dates' export const PROJECT_FILTER = 'project' +export const PROJECTS_FILTER = 'projects-list' export const TYPE_FILTER = 'type' export const SHOW_UNTAGGED_FILTER = 'showUntagged' export const SORT_BY = 'sortBy' diff --git a/src/elements/AlertsTableRow/AlertsTableRow.js b/src/elements/AlertsTableRow/AlertsTableRow.js index 889ce1bcbc..5527bc74e3 100644 --- a/src/elements/AlertsTableRow/AlertsTableRow.js +++ b/src/elements/AlertsTableRow/AlertsTableRow.js @@ -26,6 +26,8 @@ import TableCell from '../TableCell/TableCell' import { DETAILS_OVERVIEW_TAB } from '../../constants' +// TODO: rowIsExpanded logic will be part of ML-8516 +// TODO: selected row logic will be part of ML-8104 const AlertsTableRow = ({ handleExpandRow, handleSelectItem, rowItem, selectedItem }) => { const parent = useRef() const params = useParams() diff --git a/src/reducers/alertsReducer.js b/src/reducers/alertsReducer.js index 462d88c0ed..47dee29305 100644 --- a/src/reducers/alertsReducer.js +++ b/src/reducers/alertsReducer.js @@ -2,6 +2,7 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import alertsApi from '../api/alerts-api' import { defaultPendingHandler } from './redux.util' +import { parseAlerts } from '../utils/parseAlert' const initialState = { alerts: [], @@ -9,22 +10,16 @@ const initialState = { loading: false } -export const fetchAlert = createAsyncThunk( - 'fetchAlert', - ({ project, filters, config = {}, params = {} }) => { - return alertsApi.getAlerts(project, filters, config).then(({ data }) => { - return data - }) - } -) -export const fetchAlerts = createAsyncThunk( - 'fetchAlerts', - ({ project, filters, config = {}, params = {} }) => { - return alertsApi.getAlert(project, filters, (config = {}), (params = {})).then(({ data }) => { - return {} - }) - } -) +export const fetchAlert = createAsyncThunk('fetchAlert', ({ project, filters, config }) => { + return alertsApi.getAlert(project, filters, config).then(({ data }) => { + return parseAlerts(data.activations) + }) +}) +export const fetchAlerts = createAsyncThunk('fetchAlerts', ({ project, filters, config }) => { + return alertsApi.getAlerts(project, filters, config).then(({ data }) => { + return parseAlerts(data.activations) + }) +}) const alertsSlice = createSlice({ name: 'alertsStore', diff --git a/src/utils/createAlertsContent.js b/src/utils/createAlertsContent.js index f3e07ecdda..6169ff76cf 100644 --- a/src/utils/createAlertsContent.js +++ b/src/utils/createAlertsContent.js @@ -18,19 +18,19 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import { upperFirst } from 'lodash' - import { formatDatetime } from './datetime' -import { ReactComponent as Application } from 'igz-controls/images/application-icon.svg' -import { ReactComponent as Endpoint } from '../components/ProjectsAlerts/endpoint.svg' -import { ReactComponent as Job } from '../components/ProjectsAlerts/job.svg' -import { ReactComponent as Low } from '../components/ProjectsAlerts/low.svg' -import { ReactComponent as Normal } from '../components/ProjectsAlerts/normal.svg' -import { ReactComponent as High } from '../components/ProjectsAlerts/critical.svg' -import { ReactComponent as Git } from '../components/ProjectsAlerts/git.svg' -import { ReactComponent as Mail } from '../components/ProjectsAlerts/mail.svg' -import { ReactComponent as Webhook } from '../components/ProjectsAlerts/webhook.svg' -import { ReactComponent as Slack } from '../components/ProjectsAlerts/slack.svg' +import { ReactComponent as Application } from 'igz-controls/images/entity-type-application.svg' +import { ReactComponent as Endpoint } from 'igz-controls/images/entity-type-endpoint.svg' +import { ReactComponent as Critical } from 'igz-controls/images/severity-critical.svg' +import { ReactComponent as Email } from 'igz-controls/images/email-icon.svg' +import { ReactComponent as Git } from 'igz-controls/images/git-icon.svg' +import { ReactComponent as High } from 'igz-controls/images/severity-high.svg' +import { ReactComponent as Job } from 'igz-controls/images/entity-type-job.svg' +import { ReactComponent as Low } from 'igz-controls/images/severity-low.svg' +import { ReactComponent as Normal } from 'igz-controls/images/severity-normal.svg' +import { ReactComponent as Slack } from 'igz-controls/images/slack-icon-colored.svg' +import { ReactComponent as Webhook } from 'igz-controls/images/webhook-icon.svg' import { APPLICATION, @@ -39,6 +39,7 @@ import { MODEL_ENDPOINT_RESULT, MODEL_MONITORING_APPLICATION, SEVERITY, + SEVERITY_CRITICAL, SEVERITY_HIGH, SEVERITY_LOW, SEVERITY_MEDIUM @@ -95,12 +96,21 @@ const getSeverityData = severity => { value: (
- {upperFirst(SEVERITY_MEDIUM)} + {upperFirst(SEVERITY_HIGH)}
), - tooltip: upperFirst(SEVERITY_HIGH) } + case SEVERITY_CRITICAL: + return { + value: ( +
+ + {upperFirst(SEVERITY_CRITICAL)} +
+ ), + tooltip: upperFirst(SEVERITY_CRITICAL) + } default: return { value: @@ -112,7 +122,7 @@ const alertsNotifications = { webhook: , git: , slack: , - email: + email: } const getNotificationData = notifications => @@ -130,12 +140,7 @@ const getNotificationData = notifications => }) export const createAlertRowData = ({ name, ...alert }) => { - const createAlertsId = (activationTime = new Date().toISOString()) => { - const date = new Date(activationTime) - - return date.getTime().toString(36).slice(-7) - } - alert.id = createAlertsId(alert.activation_time) + alert.id = alert.id.slice(-6) return { data: { diff --git a/src/utils/parseAlert.js b/src/utils/parseAlert.js new file mode 100644 index 0000000000..aa112a6178 --- /dev/null +++ b/src/utils/parseAlert.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +export const parseAlerts = alerts => { + return alerts.map(alert => { + return { + ...alert, + ui: { + ...alert, + identifier: `${alert.id.slice(-6)}`, + identifierUnique: `${alert.name}.${alert.id.slice(-6)}` + } + } + }) +} From 80502e071e4e8b0de752ffa1df7f81eb62d80f50 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Sun, 1 Dec 2024 12:50:41 +0200 Subject: [PATCH 06/14] added copyright to alertsReducer --- src/components/ProjectsAlerts/warning.svg | 0 src/reducers/alertsReducer.js | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/components/ProjectsAlerts/warning.svg diff --git a/src/components/ProjectsAlerts/warning.svg b/src/components/ProjectsAlerts/warning.svg new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/reducers/alertsReducer.js b/src/reducers/alertsReducer.js index 47dee29305..4682691ebf 100644 --- a/src/reducers/alertsReducer.js +++ b/src/reducers/alertsReducer.js @@ -1,3 +1,22 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import alertsApi from '../api/alerts-api' From a7c421d984ae54875976b9a5dc20b6981921843a Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Sun, 1 Dec 2024 13:00:49 +0200 Subject: [PATCH 07/14] remove console.log --- src/components/Files/Files.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Files/Files.js b/src/components/Files/Files.js index 134c8769dd..7fae84a117 100644 --- a/src/components/Files/Files.js +++ b/src/components/Files/Files.js @@ -140,7 +140,6 @@ const Files = () => { ) .unwrap() .then(result => { - console.log(result) if (result) { setFiles(result) setMaxArtifactsErrorIsShown(result.length === 1000) From 8db9250cfa608708c2fb41911b9d386041fb3a9b Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Sun, 1 Dec 2024 15:03:04 +0200 Subject: [PATCH 08/14] add filtersStore to NoData props function --- src/components/ProjectsAlerts/ProjectsAlerts.js | 2 ++ src/components/ProjectsAlerts/ProjectsAlertsView.js | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js index d34cf9b0b8..47208fc23f 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -41,6 +41,7 @@ const ProjectsAlerts = () => { const params = useParams() const dispatch = useDispatch() const alertsStore = useSelector(state => state.alertsStore) + const filtersStore = useSelector(store => store.filtersStore) const abortControllerRef = useRef(new AbortController()) @@ -114,6 +115,7 @@ const ProjectsAlerts = () => { alertsStore={alertsStore} actionsMenu={() => []} // TODO filters={alertsFilters} + filtersStore={filtersStore} pageData={{}} //TODO refreshAlertsCallback={refreshAlertsCallback} requestErrorMessage={requestErrorMessage} diff --git a/src/components/ProjectsAlerts/ProjectsAlertsView.js b/src/components/ProjectsAlerts/ProjectsAlertsView.js index 08c93728fc..a38468d23f 100644 --- a/src/components/ProjectsAlerts/ProjectsAlertsView.js +++ b/src/components/ProjectsAlerts/ProjectsAlertsView.js @@ -28,7 +28,7 @@ import ProjectsAlertsFilters from './ProjectsAlertsFilters' import Table from '../Table/Table' import { getNoDataMessage } from '../../utils/getNoDataMessage' -import { ALERTS_FILTERS, ALERTS_PAGE, FUNCTION_FILTERS } from '../../constants' +import { ALERTS_FILTERS, ALERTS_PAGE } from '../../constants' import { VIRTUALIZATION_CONFIG } from '../../types' import { isRowRendered } from '../../hooks/useVirtualization.hook' @@ -37,6 +37,7 @@ const ProjectAlertsView = ({ alertsFiltersConfig, alertsStore, filters, + filtersStore, pageData, refreshAlertsCallback, requestErrorMessage, @@ -76,7 +77,7 @@ const ProjectAlertsView = ({ requestErrorMessage, ALERTS_PAGE, null, - FUNCTION_FILTERS + filtersStore )} /> ) : ( @@ -115,6 +116,7 @@ const ProjectAlertsView = ({ ProjectAlertsView.propTypes = { alertsFiltersConfig: PropTypes.object.isRequired, filters: PropTypes.object.isRequired, + filtersStore: PropTypes.object.isRequired, refreshAlertsCallback: PropTypes.func.isRequired, requestErrorMessage: PropTypes.string.isRequired, tableContent: PropTypes.arrayOf(PropTypes.object).isRequired, From 9c8e25b84ac16b8ca914db187abbb72c144f7bf4 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Mon, 2 Dec 2024 06:47:28 +0200 Subject: [PATCH 09/14] remove local icons --- src/components/ProjectsAlerts/critical.svg | 3 --- src/components/ProjectsAlerts/endpoint.svg | 3 --- src/components/ProjectsAlerts/git.svg | 10 ---------- src/components/ProjectsAlerts/job.svg | 3 --- src/components/ProjectsAlerts/low.svg | 3 --- src/components/ProjectsAlerts/mail.svg | 3 --- src/components/ProjectsAlerts/normal.svg | 3 --- src/components/ProjectsAlerts/slack.svg | 10 ---------- src/components/ProjectsAlerts/warning.svg | 0 src/components/ProjectsAlerts/webhook.svg | 9 --------- 10 files changed, 47 deletions(-) delete mode 100644 src/components/ProjectsAlerts/critical.svg delete mode 100644 src/components/ProjectsAlerts/endpoint.svg delete mode 100644 src/components/ProjectsAlerts/git.svg delete mode 100644 src/components/ProjectsAlerts/job.svg delete mode 100644 src/components/ProjectsAlerts/low.svg delete mode 100644 src/components/ProjectsAlerts/mail.svg delete mode 100644 src/components/ProjectsAlerts/normal.svg delete mode 100644 src/components/ProjectsAlerts/slack.svg delete mode 100644 src/components/ProjectsAlerts/warning.svg delete mode 100644 src/components/ProjectsAlerts/webhook.svg diff --git a/src/components/ProjectsAlerts/critical.svg b/src/components/ProjectsAlerts/critical.svg deleted file mode 100644 index 34d57e917b..0000000000 --- a/src/components/ProjectsAlerts/critical.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/endpoint.svg b/src/components/ProjectsAlerts/endpoint.svg deleted file mode 100644 index a272f69118..0000000000 --- a/src/components/ProjectsAlerts/endpoint.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/git.svg b/src/components/ProjectsAlerts/git.svg deleted file mode 100644 index 3f5b868fbb..0000000000 --- a/src/components/ProjectsAlerts/git.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/components/ProjectsAlerts/job.svg b/src/components/ProjectsAlerts/job.svg deleted file mode 100644 index fee73b3b7d..0000000000 --- a/src/components/ProjectsAlerts/job.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/low.svg b/src/components/ProjectsAlerts/low.svg deleted file mode 100644 index a563a956ed..0000000000 --- a/src/components/ProjectsAlerts/low.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/mail.svg b/src/components/ProjectsAlerts/mail.svg deleted file mode 100644 index e3fc6513c5..0000000000 --- a/src/components/ProjectsAlerts/mail.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/normal.svg b/src/components/ProjectsAlerts/normal.svg deleted file mode 100644 index 888b8eb777..0000000000 --- a/src/components/ProjectsAlerts/normal.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/ProjectsAlerts/slack.svg b/src/components/ProjectsAlerts/slack.svg deleted file mode 100644 index 71cbe06b14..0000000000 --- a/src/components/ProjectsAlerts/slack.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/components/ProjectsAlerts/warning.svg b/src/components/ProjectsAlerts/warning.svg deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/components/ProjectsAlerts/webhook.svg b/src/components/ProjectsAlerts/webhook.svg deleted file mode 100644 index cdb37ad900..0000000000 --- a/src/components/ProjectsAlerts/webhook.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - From 67c1af53e7fb1bcdeb3191517c4ee248f0bcc445 Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Tue, 3 Dec 2024 09:03:22 +0200 Subject: [PATCH 10/14] create getAlerts func in mock server and move alerts.json to mock data folder --- src/api/alerts-api.js | 44 +++++++---------- .../ProjectsAlerts/ProjectsAlerts.js | 8 +++- src/reducers/alertsReducer.js | 47 +++++++++++++++---- .../mockServer/data/alerts.json | 0 tests/mockServer/mock.js | 9 ++++ 5 files changed, 69 insertions(+), 39 deletions(-) rename src/components/ProjectsAlerts/alertsData.json => tests/mockServer/data/alerts.json (100%) diff --git a/src/api/alerts-api.js b/src/api/alerts-api.js index 8f9616c580..84904257fc 100644 --- a/src/api/alerts-api.js +++ b/src/api/alerts-api.js @@ -17,38 +17,28 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import dummyData from '../components/ProjectsAlerts/alertsData.json' +import { mainHttpClient } from '../httpClient' const alertsApi = { getAlerts: (project, filters, config = {}) => { - // TODO:ML-8514 update newConfig & remove dummy data - // const newConfig = { - // ...config, - // params: { - // ...config.params - // } - // } - return new Promise(resolve => { - setTimeout(() => { - resolve({ data: dummyData }) - }, 1000) - }) - // return mainHttpClient.get(`/projects/${project}/alerts/${alertName}/activations`, newConfig) + // TODO:ML-8514 update newConfig + const newConfig = { + ...config, + params: { + ...config.params + } + } + return mainHttpClient.get(`/projects/${project}/alert-activations`, newConfig) }, getAlert: (project, alertName, config) => { - // TODO:ML-8514 update newConfig & remove dummy data - // const newConfig = { - // ...config, - // params: { - // ...config.params - // } - // } - return new Promise(resolve => { - setTimeout(() => { - resolve({ data: dummyData }) - }, 1000) - }) - // return mainHttpClient.get(`/projects/${project}/alerts/activations`, newConfig) + // TODO:ML-8514 update newConfig + const newConfig = { + ...config, + params: { + ...config.params + } + } + return mainHttpClient.get('/projects/{project}/alerts/{alertName}/activations', newConfig) } } diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/ProjectsAlerts/ProjectsAlerts.js index 47208fc23f..f35060b3e1 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/ProjectsAlerts/ProjectsAlerts.js @@ -71,8 +71,12 @@ const ProjectsAlerts = () => { }) ) .unwrap() - .then(data => { - setAlerts(data) + .then(alerts => { + if (alerts?.length > 0) { + setAlerts(alerts) + } else { + setAlerts([]) + } }) }, [dispatch, params.id] diff --git a/src/reducers/alertsReducer.js b/src/reducers/alertsReducer.js index 4682691ebf..e8db44ac21 100644 --- a/src/reducers/alertsReducer.js +++ b/src/reducers/alertsReducer.js @@ -22,6 +22,7 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import alertsApi from '../api/alerts-api' import { defaultPendingHandler } from './redux.util' import { parseAlerts } from '../utils/parseAlert' +import { largeResponseCatchHandler } from '../utils/largeResponseCatchHandler' const initialState = { alerts: [], @@ -29,16 +30,42 @@ const initialState = { loading: false } -export const fetchAlert = createAsyncThunk('fetchAlert', ({ project, filters, config }) => { - return alertsApi.getAlert(project, filters, config).then(({ data }) => { - return parseAlerts(data.activations) - }) -}) -export const fetchAlerts = createAsyncThunk('fetchAlerts', ({ project, filters, config }) => { - return alertsApi.getAlerts(project, filters, config).then(({ data }) => { - return parseAlerts(data.activations) - }) -}) +export const fetchAlert = createAsyncThunk( + 'fetchAlert', + ({ project, filters, config }, thunkAPI) => { + return alertsApi + .getAlert(project, filters, config) + .then(({ data }) => { + return parseAlerts(data.alerts) + }) + .catch(error => { + largeResponseCatchHandler( + error, + 'Failed to fetch alerts', + thunkAPI.dispatch, + config?.ui?.setRequestErrorMessage + ) + }) + } +) +export const fetchAlerts = createAsyncThunk( + 'fetchAlerts', + ({ project, filters, config }, thunkAPI) => { + return alertsApi + .getAlerts(project, filters, config) + .then(({ data }) => { + return parseAlerts(data.alerts) + }) + .catch(error => { + largeResponseCatchHandler( + error, + 'Failed to fetch alerts', + thunkAPI.dispatch, + config?.ui?.setRequestErrorMessage + ) + }) + } +) const alertsSlice = createSlice({ name: 'alertsStore', diff --git a/src/components/ProjectsAlerts/alertsData.json b/tests/mockServer/data/alerts.json similarity index 100% rename from src/components/ProjectsAlerts/alertsData.json rename to tests/mockServer/data/alerts.json diff --git a/tests/mockServer/mock.js b/tests/mockServer/mock.js index 3f2b44bb04..6b7d439e3c 100644 --- a/tests/mockServer/mock.js +++ b/tests/mockServer/mock.js @@ -42,6 +42,7 @@ import { } from 'lodash' import mime from 'mime-types' +import alerts from './data/alerts.json' import frontendSpec from './data/frontendSpec.json' import projects from './data/projects.json' import projectsSummary from './data/summary.json' @@ -847,6 +848,13 @@ function getRun(req, res) { res.send({ data: run_prj_uid }) } +// TODO:ML-8368 add getAlert controller + +function getAlerts(req, res) { + // TODO:ML-8514 Update getAlerts to support both parameters and query strings. + res.send({ alerts: alerts.activations }) +} + function patchRun(req, res) { const collectedRun = runs.runs .filter(run => run.metadata.project === req.params.project) @@ -2577,6 +2585,7 @@ app.get(`${mlrunAPIIngress}/project-summaries/:project`, getProjectSummary) app.get(`${mlrunAPIIngress}/projects/:project/runs`, getRuns) app.get(`${mlrunAPIIngress}/projects/*/runs`, getRuns) +app.get(`${mlrunAPIIngress}/projects/*/alert-activations`, getAlerts) app.get(`${mlrunAPIIngress}/run/:project/:uid`, getRun) app.patch(`${mlrunAPIIngress}/run/:project/:uid`, patchRun) app.delete(`${mlrunAPIIngress}/projects/:project/runs/:uid`, deleteRun) From 3cb61582a15658601e9503291fbe602fa16c91ab Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Thu, 5 Dec 2024 05:48:30 +0200 Subject: [PATCH 11/14] fix pr comments, rename Projectert to Alert across all relevant files --- .../{ProjectsAlerts/ProjectsAlerts.js => Alerts/Alerts.js} | 4 +++- .../ProjectsAlertsFilters.js => Alerts/AlertsFilters.js} | 2 +- .../ProjectsAlertsView.js => Alerts/AlertsView.js} | 0 src/components/{ProjectsAlerts => Alerts}/alerts.scss | 0 src/components/{ProjectsAlerts => Alerts}/alerts.util.js | 0 5 files changed, 4 insertions(+), 2 deletions(-) rename src/components/{ProjectsAlerts/ProjectsAlerts.js => Alerts/Alerts.js} (99%) rename src/components/{ProjectsAlerts/ProjectsAlertsFilters.js => Alerts/AlertsFilters.js} (100%) rename src/components/{ProjectsAlerts/ProjectsAlertsView.js => Alerts/AlertsView.js} (100%) rename src/components/{ProjectsAlerts => Alerts}/alerts.scss (100%) rename src/components/{ProjectsAlerts => Alerts}/alerts.util.js (100%) diff --git a/src/components/ProjectsAlerts/ProjectsAlerts.js b/src/components/Alerts/Alerts.js similarity index 99% rename from src/components/ProjectsAlerts/ProjectsAlerts.js rename to src/components/Alerts/Alerts.js index f35060b3e1..a499ec2948 100644 --- a/src/components/ProjectsAlerts/ProjectsAlerts.js +++ b/src/components/Alerts/Alerts.js @@ -18,6 +18,7 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import { useCallback, useMemo, useRef, useState } from 'react' +import { useParams } from 'react-router-dom' import { useDispatch, useSelector } from 'react-redux' import ProjectAlertsView from './ProjectsAlertsView' @@ -25,7 +26,6 @@ import ProjectAlertsView from './ProjectsAlertsView' import { getAlertsFiltersConfig, parseAlertsQueryParamsCallback } from './alerts.util' import { useFiltersFromSearchParams } from '../../hooks/useFiltersFromSearchParams.hook' -import { useParams } from 'react-router-dom' import { fetchAlerts } from '../../reducers/alertsReducer' import { useVirtualization } from '../../hooks/useVirtualization.hook' import { createAlertRowData } from '../../utils/createAlertsContent' @@ -81,6 +81,7 @@ const ProjectsAlerts = () => { }, [dispatch, params.id] ) + const tableContent = useMemo(() => { return alerts.map(alert => createAlertRowData(alert)) }, [alerts]) @@ -88,6 +89,7 @@ const ProjectsAlerts = () => { const refreshAlertsCallback = useCallback( filters => { setAlerts([]) + return fetchData(filters) }, [fetchData] diff --git a/src/components/ProjectsAlerts/ProjectsAlertsFilters.js b/src/components/Alerts/AlertsFilters.js similarity index 100% rename from src/components/ProjectsAlerts/ProjectsAlertsFilters.js rename to src/components/Alerts/AlertsFilters.js index de803a77b4..55508b2a6b 100644 --- a/src/components/ProjectsAlerts/ProjectsAlertsFilters.js +++ b/src/components/Alerts/AlertsFilters.js @@ -74,8 +74,8 @@ const ProjectsAlertsFilters = () => { [JOB]: [JOB_NAME], [ENDPOINT]: [ENDPOINT_APPLICATION, ENDPOINT_RESULT] } - const allFields = [ENTITY_ID, JOB_NAME, ENDPOINT_APPLICATION, ENDPOINT_RESULT] + return allFields.filter(field => !(fieldsByType[entityType] ?? []).includes(field)) }, []) diff --git a/src/components/ProjectsAlerts/ProjectsAlertsView.js b/src/components/Alerts/AlertsView.js similarity index 100% rename from src/components/ProjectsAlerts/ProjectsAlertsView.js rename to src/components/Alerts/AlertsView.js diff --git a/src/components/ProjectsAlerts/alerts.scss b/src/components/Alerts/alerts.scss similarity index 100% rename from src/components/ProjectsAlerts/alerts.scss rename to src/components/Alerts/alerts.scss diff --git a/src/components/ProjectsAlerts/alerts.util.js b/src/components/Alerts/alerts.util.js similarity index 100% rename from src/components/ProjectsAlerts/alerts.util.js rename to src/components/Alerts/alerts.util.js From 525a426356e32a106e958c09dcf3e39dbc3c47dc Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Thu, 5 Dec 2024 07:12:59 +0200 Subject: [PATCH 12/14] fix pr comments, rename Projectert to Alert across all relevant files --- src/App.js | 2 +- src/components/Alerts/Alerts.js | 12 +++++++----- src/components/Alerts/AlertsFilters.js | 4 ++-- src/components/Alerts/AlertsView.js | 13 ++++++++----- src/elements/TableTypeCell/TableTypeCell.js | 3 +-- src/utils/createAlertsContent.js | 21 ++++++++++----------- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/App.js b/src/App.js index ffb6ccd1f8..d6f743d4c4 100755 --- a/src/App.js +++ b/src/App.js @@ -103,7 +103,7 @@ const FeatureVectors = lazyRetry( const ProjectsJobsMonitoring = lazyRetry( () => import('./components/ProjectsJobsMonitoring/ProjectsJobsMonitoring') ) -const ProjectsAlerts = lazyRetry(() => import('./components/ProjectsAlerts/ProjectsAlerts')) +const ProjectsAlerts = lazyRetry(() => import('./components/Alerts/Alerts')) const JobsMonitoring = lazyRetry( () => import('./components/ProjectsJobsMonitoring/JobsMonitoring/JobsMonitoring') ) diff --git a/src/components/Alerts/Alerts.js b/src/components/Alerts/Alerts.js index a499ec2948..2251491a8f 100644 --- a/src/components/Alerts/Alerts.js +++ b/src/components/Alerts/Alerts.js @@ -18,10 +18,10 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import { useCallback, useMemo, useRef, useState } from 'react' -import { useParams } from 'react-router-dom' +import { useParams, useSearchParams } from 'react-router-dom' import { useDispatch, useSelector } from 'react-redux' -import ProjectAlertsView from './ProjectsAlertsView' +import AlertsView from './AlertsView' import { getAlertsFiltersConfig, parseAlertsQueryParamsCallback } from './alerts.util' import { useFiltersFromSearchParams } from '../../hooks/useFiltersFromSearchParams.hook' @@ -33,12 +33,13 @@ import { useInitialTableFetch } from '../../hooks/useInitialTableFetch.hook' import cssVariables from './alerts.scss' -const ProjectsAlerts = () => { +const Alerts = () => { const [alerts, setAlerts] = useState([]) const [requestErrorMessage, setRequestErrorMessage] = useState('') const [selectedAlert] = useState({}) const [selectedRowData] = useState({}) const params = useParams() + const [, setSearchParams] = useSearchParams() const dispatch = useDispatch() const alertsStore = useSelector(state => state.alertsStore) const filtersStore = useSelector(store => store.filtersStore) @@ -115,7 +116,7 @@ const ProjectsAlerts = () => { }) return ( - { refreshAlertsCallback={refreshAlertsCallback} requestErrorMessage={requestErrorMessage} selectedAlert={selectedAlert} + setSearchParams={setSearchParams} tableContent={tableContent} virtualizationConfig={virtualizationConfig} /> ) } -export default ProjectsAlerts +export default Alerts diff --git a/src/components/Alerts/AlertsFilters.js b/src/components/Alerts/AlertsFilters.js index 55508b2a6b..f869f716b5 100644 --- a/src/components/Alerts/AlertsFilters.js +++ b/src/components/Alerts/AlertsFilters.js @@ -50,7 +50,7 @@ import { SEVERITY } from '../../constants' -const ProjectsAlertsFilters = () => { +const AlertsFilters = () => { const form = useForm() const { values: { [ENTITY_TYPE]: entityType } @@ -148,4 +148,4 @@ const ProjectsAlertsFilters = () => { ) } -export default ProjectsAlertsFilters +export default AlertsFilters diff --git a/src/components/Alerts/AlertsView.js b/src/components/Alerts/AlertsView.js index a38468d23f..4722681f75 100644 --- a/src/components/Alerts/AlertsView.js +++ b/src/components/Alerts/AlertsView.js @@ -24,7 +24,7 @@ import AlertsTableRow from '../../elements/AlertsTableRow/AlertsTableRow' import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs' import Loader from '../../common/Loader/Loader' import NoData from '../../common/NoData/NoData' -import ProjectsAlertsFilters from './ProjectsAlertsFilters' +import AlertsFilters from './AlertsFilters' import Table from '../Table/Table' import { getNoDataMessage } from '../../utils/getNoDataMessage' @@ -32,7 +32,7 @@ import { ALERTS_FILTERS, ALERTS_PAGE } from '../../constants' import { VIRTUALIZATION_CONFIG } from '../../types' import { isRowRendered } from '../../hooks/useVirtualization.hook' -const ProjectAlertsView = ({ +const AlertsView = ({ actionsMenu, alertsFiltersConfig, alertsStore, @@ -42,6 +42,7 @@ const ProjectAlertsView = ({ refreshAlertsCallback, requestErrorMessage, selectedAlert, + setSearchParams, tableContent, virtualizationConfig }) => { @@ -61,10 +62,11 @@ const ProjectAlertsView = ({ filters={filters} handleRefresh={refreshAlertsCallback} page={ALERTS_PAGE} + setSearchParams={setSearchParams} withRefreshButton withoutExpandButton > - + {alertsStore.loading ? ( @@ -113,13 +115,14 @@ const ProjectAlertsView = ({ ) } -ProjectAlertsView.propTypes = { +AlertsView.propTypes = { alertsFiltersConfig: PropTypes.object.isRequired, filters: PropTypes.object.isRequired, filtersStore: PropTypes.object.isRequired, refreshAlertsCallback: PropTypes.func.isRequired, requestErrorMessage: PropTypes.string.isRequired, + setSearchParams: PropTypes.func.isRequired, tableContent: PropTypes.arrayOf(PropTypes.object).isRequired, virtualizationConfig: VIRTUALIZATION_CONFIG.isRequired } -export default ProjectAlertsView +export default AlertsView diff --git a/src/elements/TableTypeCell/TableTypeCell.js b/src/elements/TableTypeCell/TableTypeCell.js index 95a0bb9f57..6fc068769b 100644 --- a/src/elements/TableTypeCell/TableTypeCell.js +++ b/src/elements/TableTypeCell/TableTypeCell.js @@ -84,13 +84,12 @@ const TableTypeCell = ({ className = '', data }) => { > {typesOfJob[data.value]?.icon ?? capitalize(data.value)} - {data.text &&
{data.value}
} ) } TableTypeCell.propTypes = { - text: PropTypes.string, + className: PropTypes.string, data: PropTypes.shape({}).isRequired } diff --git a/src/utils/createAlertsContent.js b/src/utils/createAlertsContent.js index 6169ff76cf..e9abb189d6 100644 --- a/src/utils/createAlertsContent.js +++ b/src/utils/createAlertsContent.js @@ -17,6 +17,7 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ +import classNames from 'classnames' import { upperFirst } from 'lodash' import { formatDatetime } from './datetime' @@ -127,20 +128,18 @@ const alertsNotifications = { const getNotificationData = notifications => notifications.map(notification => { + const tableCellClassName = classNames('table-cell-notification__content', { + 'notification-fail': notification.err !== '' + }) + return { - icon: ( -
- {alertsNotifications[notification.kind]} -
- ), + icon:
{alertsNotifications[notification.kind]}
, tooltip: upperFirst(notification.kind) } }) export const createAlertRowData = ({ name, ...alert }) => { - alert.id = alert.id.slice(-6) + alert.id = alert.id.slice(-6) // Use the last 6 characters of the database ID as the alert ID return { data: { @@ -169,7 +168,7 @@ export const createAlertRowData = ({ name, ...alert }) => { id: `eventType.${alert.id}`, headerId: 'eventType', headerLabel: 'Event Type', - value: alert.event_kind.split('-').join(' '), + value: alert.event_kind?.split('-')?.join(' '), className: 'table-cell-1' }, { @@ -206,14 +205,14 @@ export const createAlertRowData = ({ name, ...alert }) => { id: `criteriaCount.${alert.id}`, headerId: 'criteriaCount', headerLabel: 'Trigger criteria count', - value: alert.criteria.count, + value: alert.criteria?.count, className: 'table-cell-1' }, { id: `criteriaTime.${alert.id}`, headerId: 'criteriaTime', headerLabel: 'Trigger criteria time period', - value: alert.criteria.period, + value: alert.criteria?.period, className: 'table-cell-1' }, { From 328df9275d9c43cba624607ff32d2c514eea2b1c Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Thu, 5 Dec 2024 12:27:25 +0200 Subject: [PATCH 13/14] add dashes for the filterAlertsEventTypeOptions ids --- src/components/Alerts/alerts.util.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/Alerts/alerts.util.js b/src/components/Alerts/alerts.util.js index 4c63036e28..6b6bba376f 100644 --- a/src/components/Alerts/alerts.util.js +++ b/src/components/Alerts/alerts.util.js @@ -101,16 +101,16 @@ export const filterAlertsSeverityOptions = [ export const filterAlertsEventTypeOptions = [ { label: 'All', id: FILTER_ALL_ITEMS }, { label: 'Job Failed', id: 'job-failed' }, - { label: 'Data Drift Detected', id: 'data drift detected' }, - { label: 'Data Drift Suspected', id: 'data drift suspected' }, - { label: 'Conc Drift Detected', id: 'concept drift detected' }, - { label: 'Conc Drift Suspected', id: 'concept drift suspected' }, - { label: 'MM Perf. Detected', id: 'model performance detected' }, - { label: 'MM Perf. Suspected', id: 'model performance suspected' }, - { label: 'S Perf. Detected', id: 'system performance detected' }, - { label: 'S Perf. Suspected', id: 'system performance suspected' }, - { label: 'MM App Ano. Detected', id: 'mm app anomaly detected' }, - { label: 'MM App Ano. Suspected', id: 'mm app anomaly suspected' }, - { label: 'MM App Failed', id: 'mm app failed' }, + { label: 'Data Drift Detected', id: 'data-drift-detected' }, + { label: 'Data Drift Suspected', id: 'data-drift-suspected' }, + { label: 'Conc Drift Detected', id: 'concept-drift-detected' }, + { label: 'Conc Drift Suspected', id: 'concept-drift-suspected' }, + { label: 'MM Perf. Detected', id: 'model-performance-detected' }, + { label: 'MM Perf. Suspected', id: 'model-performance-suspected' }, + { label: 'S Perf. Detected', id: 'system-performance-detected' }, + { label: 'S Perf. Suspected', id: 'system-performance-suspected' }, + { label: 'MM App Ano. Detected', id: 'mm-app-anomaly-detected' }, + { label: 'MM App Ano. Suspected', id: 'mm-app-anomaly-suspected' }, + { label: 'MM App Failed', id: 'mm-app-failed' }, { label: 'MM App Failed', id: 'failed' } ] From fdf7b2344421d77c854fab41dbaa3764525635fb Mon Sep 17 00:00:00 2001 From: pinis-gini-apps Date: Thu, 5 Dec 2024 12:46:19 +0200 Subject: [PATCH 14/14] update DRC version to 2.2.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f84231f54a..120549d7d8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "final-form-arrays": "^3.1.0", "fs-extra": "^10.0.0", "identity-obj-proxy": "^3.0.0", - "iguazio.dashboard-react-controls": "2.2.7", + "iguazio.dashboard-react-controls": "2.2.8", "is-wsl": "^1.1.0", "js-base64": "^2.5.2", "js-yaml": "^4.1.0",