}>
{file}
diff --git a/src/common/TargetPath/TargetPath.js b/src/common/TargetPath/TargetPath.js
index 18747eedc..17b863a28 100644
--- a/src/common/TargetPath/TargetPath.js
+++ b/src/common/TargetPath/TargetPath.js
@@ -180,7 +180,7 @@ const TargetPath = ({
dataInputState.storePathType === 'feature-vectors' &&
dataInputState.featureVectors.length === 0
) {
- dispatch(featureStoreActions.fetchFeatureVectors(dataInputState.project)).then(
+ dispatch(featureStoreActions.fetchFeatureVectors(dataInputState.project, {}, {}, true)).then(
featureVectors => {
const featureVectorsList = uniqBy(featureVectors, 'metadata.name')
.map(featureVector => ({
diff --git a/src/components/AddToFeatureVectorPage/AddToFeatureVectorPage.js b/src/components/AddToFeatureVectorPage/AddToFeatureVectorPage.js
index 74fa93f6e..e4fa12e53 100644
--- a/src/components/AddToFeatureVectorPage/AddToFeatureVectorPage.js
+++ b/src/components/AddToFeatureVectorPage/AddToFeatureVectorPage.js
@@ -20,7 +20,6 @@ such restriction.
import React, { useCallback, useEffect, useRef, useState, useMemo } from 'react'
import { connect, useDispatch, useSelector } from 'react-redux'
import { useNavigate, useParams } from 'react-router-dom'
-import axios from 'axios'
import AddToFeatureVectorView from './AddToFeatureVectorView'
import FeaturesTablePanel from '../../elements/FeaturesTablePanel/FeaturesTablePanel'
@@ -30,11 +29,11 @@ import {
FEATURE_STORE_PAGE,
GROUP_BY_NAME,
GROUP_BY_NONE,
- TAG_FILTER_ALL_ITEMS
+ TAG_FILTER_ALL_ITEMS,
+ REQUEST_CANCELED
} from '../../constants'
import featureStoreActions from '../../actions/featureStore'
import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
-import { cancelRequest } from '../../utils/cancelRequest'
import { createFeaturesRowData } from '../../utils/createFeatureStoreContent'
import { filters } from './addToFeatureVectorPage.util'
import { getFeatureIdentifier } from '../../utils/getUniqueIdentifier'
@@ -43,6 +42,7 @@ import { parseFeatures } from '../../utils/parseFeatures'
import { setFilters } from '../../reducers/filtersReducer'
import { setNotification } from '../../reducers/notificationReducer'
import { setTablePanelOpen } from '../../reducers/tableReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useGetTagOptions } from '../../hooks/useGetTagOptions.hook'
import { useGroupContent } from '../../hooks/groupContent.hook'
import { useYaml } from '../../hooks/yaml.hook'
@@ -62,6 +62,7 @@ const AddToFeatureVectorPage = ({
const [selectedRowData, setSelectedRowData] = useState({})
const [convertedYaml, toggleConvertedYaml] = useYaml('')
const addToFeatureVectorPageRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const params = useParams()
const navigate = useNavigate()
const tableStore = useSelector(store => store.tableStore)
@@ -93,16 +94,13 @@ const AddToFeatureVectorPage = ({
navigateToFeatureVectorsScreen()
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response.status || 400,
- id: Math.random(),
- message:
- error.response.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'You are not permitted to create new feature vector.'
- : 'Feature vector creation failed.',
- retry: handleCreateFeatureVector
- })
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'You are not permitted to create new feature vector'
+ : 'Feature vector creation failed'
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () =>
+ handleCreateFeatureVector(featureVector)
)
if (error.response.status === FORBIDDEN_ERROR_STATUS_CODE) {
@@ -140,10 +138,10 @@ const AddToFeatureVectorPage = ({
const fetchData = useCallback(
async filters => {
+ abortControllerRef.current = new AbortController()
+
const config = {
- cancelToken: new axios.CancelToken(cancel => {
- addToFeatureVectorPageRef.current.cancel = cancel
- })
+ signal: abortControllerRef.current.signal
}
fetchFeatures(filters.project, filters, config).then(result => {
@@ -248,7 +246,7 @@ const AddToFeatureVectorPage = ({
removeFeature()
removeFeatures()
setSelectedRowData({})
- cancelRequest(addToFeatureVectorPageRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [removeFeature, removeFeatures])
@@ -263,13 +261,7 @@ const AddToFeatureVectorPage = ({
useEffect(() => {
if (isEveryObjectValueEmpty(tableStore.features.featureVector)) {
navigateToFeatureVectorsScreen()
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Please, create a feature vector first'
- })
- )
+ showErrorNotification(dispatch, {}, 'Please, create a feature vector first')
} else {
dispatch(setTablePanelOpen(true))
}
diff --git a/src/components/ArtifactsActionBar/ArtifactsActionBar.js b/src/components/ArtifactsActionBar/ArtifactsActionBar.js
index 894298281..1211ae2a4 100644
--- a/src/components/ArtifactsActionBar/ArtifactsActionBar.js
+++ b/src/components/ArtifactsActionBar/ArtifactsActionBar.js
@@ -31,11 +31,16 @@ import FilterMenuModal from '../FilterMenuModal/FilterMenuModal'
import ArtifactsFilters from './ArtifactsFilters'
import NameFilter from '../../common/NameFilter/NameFilter'
-import { setFieldState } from 'igz-controls/utils/form.util'
-import { GROUP_BY_NAME, GROUP_BY_NONE, TAG_FILTER, TAG_FILTER_ALL_ITEMS } from '../../constants'
-import { removeFilters, setFilters } from '../../reducers/filtersReducer'
-
+import {
+ GROUP_BY_NAME,
+ GROUP_BY_NONE,
+ REQUEST_CANCELED,
+ TAG_FILTER,
+ TAG_FILTER_ALL_ITEMS
+} from '../../constants'
import detailsActions from '../../actions/details'
+import { removeFilters, setFilters } from '../../reducers/filtersReducer'
+import { setFieldState } from 'igz-controls/utils/form.util'
import { ReactComponent as RefreshIcon } from 'igz-controls/images/refresh.svg'
@@ -127,7 +132,7 @@ function ArtifactsActionBar({
const refresh = formState => {
if (changes.counter > 0 && cancelRequest) {
- cancelRequest('cancel')
+ cancelRequest(REQUEST_CANCELED)
} else {
handleRefresh({
name: formState.values.name,
diff --git a/src/components/ConsumerGroup/ConsumerGroup.js b/src/components/ConsumerGroup/ConsumerGroup.js
index 1aef5c966..eabc0676d 100644
--- a/src/components/ConsumerGroup/ConsumerGroup.js
+++ b/src/components/ConsumerGroup/ConsumerGroup.js
@@ -32,7 +32,7 @@ import { RoundedIcon } from 'igz-controls/components'
import nuclioActions from '../../actions/nuclio'
import { generatePageData } from './consumerGroup.util.js'
import { getNoDataMessage } from '../../utils/getNoDataMessage'
-import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { ReactComponent as RefreshIcon } from 'igz-controls/images/refresh.svg'
@@ -86,13 +86,12 @@ const ConsumerGroup = ({
useEffect(() => {
if (!isEmpty(currentV3ioStream) && nuclioStore.v3ioStreamShardLags.error) {
- dispatch(
- setNotification({
- status: nuclioStore.v3ioStreamShardLags.error?.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch v3io stream shard lags',
- retry: () => refreshConsumerGroup(currentV3ioStream)
- })
+ showErrorNotification(
+ dispatch,
+ nuclioStore.v3ioStreamShardLags.error,
+ 'Failed to fetch v3io stream shard lags',
+ '',
+ () => refreshConsumerGroup(currentV3ioStream)
)
resetV3ioStreamShardLagsError()
}
diff --git a/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.js b/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.js
index f47cef4d5..acf9daa6c 100644
--- a/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.js
+++ b/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.js
@@ -30,7 +30,7 @@ import { GROUP_BY_NONE } from '../../constants'
import { areNuclioStreamsEnabled } from '../../utils/helper'
import { isProjectValid } from '../../utils/handleRedirect'
import { setFilters } from '../../reducers/filtersReducer'
-import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
const ConsumerGroupsWrapper = ({
fetchNuclioV3ioStreams,
@@ -62,13 +62,8 @@ const ConsumerGroupsWrapper = ({
useEffect(() => {
if (v3ioStreams.error) {
- dispatch(
- setNotification({
- status: v3ioStreams.error?.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch v3io streams',
- retry: () => refreshConsumerGroups()
- })
+ showErrorNotification(dispatch, v3ioStreams.error, 'Failed to fetch v3io streams', () =>
+ refreshConsumerGroups()
)
resetV3ioStreamsError()
diff --git a/src/components/CreateJobPage/CreateJobPage.js b/src/components/CreateJobPage/CreateJobPage.js
index f56bb2120..effcd99b1 100644
--- a/src/components/CreateJobPage/CreateJobPage.js
+++ b/src/components/CreateJobPage/CreateJobPage.js
@@ -77,24 +77,26 @@ const CreateJobPage = ({
useEffect(() => {
fetchFunctions(selectedProject).then(functions => {
- const filteredFunctions = functions.filter(func => includes(functionRunKinds, func.kind))
-
- const groupedFunctions = Object.values(
- filteredFunctions.reduce((prev, curr) => {
- if (!prev[curr.metadata.name]) {
- prev[curr.metadata.name] = {
- name: curr.metadata.name,
- functions: []
+ if (functions) {
+ const filteredFunctions = functions.filter(func => includes(functionRunKinds, func.kind))
+
+ const groupedFunctions = Object.values(
+ filteredFunctions.reduce((prev, curr) => {
+ if (!prev[curr.metadata.name]) {
+ prev[curr.metadata.name] = {
+ name: curr.metadata.name,
+ functions: []
+ }
}
- }
- prev[curr.metadata.name].functions.push(curr)
+ prev[curr.metadata.name].functions.push(curr)
- return prev
- }, {})
- )
+ return prev
+ }, {})
+ )
- return setFunctions(groupedFunctions)
+ return setFunctions(groupedFunctions)
+ }
})
if (isEmpty(functionsStore.templatesCatalog)) {
diff --git a/src/components/Datasets/Datasets.js b/src/components/Datasets/Datasets.js
index 8700be457..55df8265b 100644
--- a/src/components/Datasets/Datasets.js
+++ b/src/components/Datasets/Datasets.js
@@ -33,6 +33,7 @@ import {
FILTER_MENU_MODAL,
GROUP_BY_NAME,
GROUP_BY_NONE,
+ REQUEST_CANCELED,
TAG_FILTER_ALL_ITEMS
} from '../../constants'
import {
@@ -50,9 +51,9 @@ import {
handleApplyDetailsChanges,
registerDatasetTitle
} from './datasets.util'
-import { largeResponseCatchHandler } from '../../utils/largeResponseCatchHandler'
-import { cancelRequest } from '../../utils/cancelRequest'
+import { createDatasetsRowData } from '../../utils/createArtifactsContent'
import { getArtifactIdentifier } from '../../utils/getUniqueIdentifier'
+import { getViewMode } from '../../utils/helper'
import { isDetailsTabExists } from '../../utils/isDetailsTabExists'
import { openPopUp } from 'igz-controls/utils/common.util'
import { setArtifactTags } from '../../utils/artifacts.util'
@@ -60,10 +61,8 @@ import { setFilters } from '../../reducers/filtersReducer'
import { setNotification } from '../../reducers/notificationReducer'
import { useGetTagOptions } from '../../hooks/useGetTagOptions.hook'
import { useGroupContent } from '../../hooks/groupContent.hook'
-import { useYaml } from '../../hooks/yaml.hook'
-import { getViewMode } from '../../utils/helper'
-import { createDatasetsRowData } from '../../utils/createArtifactsContent'
import { useSortTable } from '../../hooks/useSortTable.hook'
+import { useYaml } from '../../hooks/yaml.hook'
const Datasets = () => {
const [datasets, setDatasets] = useState([])
@@ -76,6 +75,7 @@ const Datasets = () => {
const artifactsStore = useSelector(store => store.artifactsStore)
const filtersStore = useSelector(store => store.filtersStore)
const datasetsRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const viewMode = getViewMode(window.location.search)
const params = useParams()
const navigate = useNavigate()
@@ -100,21 +100,35 @@ const Datasets = () => {
const fetchData = useCallback(
filters => {
- dispatch(fetchDataSets({ project: params.projectName, filters, setLargeRequestErrorMessage }))
+ abortControllerRef.current = new AbortController()
+
+ dispatch(
+ fetchDataSets({
+ project: params.projectName,
+ filters,
+ config: {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ }
+ })
+ )
.unwrap()
.then(dataSetsResponse => {
- setArtifactTags(
- dataSetsResponse,
- setDatasets,
- setAllDatasets,
- filters,
- dispatch,
- DATASETS_PAGE
- )
-
- return dataSetsResponse
+ if (dataSetsResponse) {
+ setArtifactTags(
+ dataSetsResponse,
+ setDatasets,
+ setAllDatasets,
+ filters,
+ dispatch,
+ DATASETS_PAGE
+ )
+
+ return dataSetsResponse
+ }
})
- .catch(largeResponseCatchHandler)
},
[dispatch, params.projectName]
)
@@ -305,7 +319,7 @@ const Datasets = () => {
setAllDatasets([])
dispatch(removeDataSets())
setSelectedDataset({})
- cancelRequest(datasetsRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [dispatch])
diff --git a/src/components/DetailsPreview/DetailsPreview.js b/src/components/DetailsPreview/DetailsPreview.js
index 6a0fdac32..9f71d3906 100644
--- a/src/components/DetailsPreview/DetailsPreview.js
+++ b/src/components/DetailsPreview/DetailsPreview.js
@@ -17,7 +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 React, { useEffect, useMemo, useRef, useState } from 'react'
+import React, { useEffect, useMemo, useState } from 'react'
import { isEqual } from 'lodash'
import PropTypes from 'prop-types'
import classnames from 'classnames'
@@ -34,7 +34,6 @@ import { ReactComponent as Popout } from 'igz-controls/images/popout.svg'
const DetailsPreview = ({ artifact, handlePreview }) => {
const [preview, setPreview] = useState([])
const [noData, setNoData] = useState(false)
- const previewRef = useRef({ current: {} })
const params = useParams()
const popupButtonIsDisplayed = useMemo(() => {
@@ -52,7 +51,6 @@ const DetailsPreview = ({ artifact, handlePreview }) => {
useEffect(() => {
return () => {
setPreview([])
- cancelRequest('cancel')
}
}, [artifact])
@@ -60,10 +58,6 @@ const DetailsPreview = ({ artifact, handlePreview }) => {
getArtifactPreview(params.projectName, artifact, noData, setNoData, setPreview)
}, [artifact, noData, params.projectName])
- const cancelRequest = message => {
- previewRef.current?.cancel && previewRef.current.cancel(message)
- }
-
return (
{popupButtonIsDisplayed && (
diff --git a/src/components/FeatureStore/FeatureSets/FeatureSets.js b/src/components/FeatureStore/FeatureSets/FeatureSets.js
index b31bd0f99..ba9730026 100644
--- a/src/components/FeatureStore/FeatureSets/FeatureSets.js
+++ b/src/components/FeatureStore/FeatureSets/FeatureSets.js
@@ -20,7 +20,6 @@ such restriction.
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { connect, useSelector, useDispatch } from 'react-redux'
-import axios from 'axios'
import { cloneDeep } from 'lodash'
import FeatureSetsView from './FeatureSetsView'
@@ -32,11 +31,11 @@ import {
FEATURE_STORE_PAGE,
GROUP_BY_NAME,
GROUP_BY_NONE,
+ REQUEST_CANCELED,
TAG_FILTER_ALL_ITEMS,
TAG_LATEST
} from '../../../constants'
import { featureSetsActionCreator, featureSetsFilters, generatePageData } from './featureSets.util'
-import { cancelRequest } from '../../../utils/cancelRequest'
import { checkTabIsValid, handleApplyDetailsChanges } from '../featureStore.util'
import { createFeatureSetsRowData } from '../../../utils/createFeatureStoreContent'
import { getFeatureSetIdentifier } from '../../../utils/getUniqueIdentifier'
@@ -72,6 +71,7 @@ const FeatureSets = ({
const featureStore = useSelector(store => store.featureStore)
const filtersStore = useSelector(store => store.filtersStore)
const featureStoreRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
@@ -104,30 +104,31 @@ const FeatureSets = ({
const fetchData = useCallback(
filters => {
+ abortControllerRef.current = new AbortController()
+
const config = {
- cancelToken: new axios.CancelToken(cancel => {
- featureStoreRef.current.cancel = cancel
- }),
ui: {
+ controller: abortControllerRef.current,
setLargeRequestErrorMessage
}
}
- return fetchFeatureSets(params.projectName, filters, config)
- .then(result => {
- if (result) {
- setFeatureSets(parseFeatureSets(result))
+ return fetchFeatureSets(params.projectName, filters, config).then(result => {
+ if (result) {
+ setFeatureSets(parseFeatureSets(result))
- return result
- }
- })
+ return result
+ }
+ })
},
[fetchFeatureSets, params.projectName]
)
const handleRefresh = filters => {
dispatch(getFilterTagOptions({ fetchTags: fetchFeatureSetsTags, project: params.projectName }))
-
+ setFeatureSets([])
+ setSelectedFeatureSet({})
+ setSelectedRowData({})
return fetchData(filters)
}
@@ -342,7 +343,7 @@ const FeatureSets = ({
removeFeatureSet()
setSelectedFeatureSet({})
setSelectedRowData({})
- cancelRequest(featureStoreRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [removeFeatureSet, removeFeatureSets, params.projectName])
diff --git a/src/components/FeatureStore/FeatureVectors/FeatureVectors.js b/src/components/FeatureStore/FeatureVectors/FeatureVectors.js
index a180a7987..683a5b43a 100644
--- a/src/components/FeatureStore/FeatureVectors/FeatureVectors.js
+++ b/src/components/FeatureStore/FeatureVectors/FeatureVectors.js
@@ -20,7 +20,6 @@ such restriction.
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { connect, useDispatch, useSelector } from 'react-redux'
-import axios from 'axios'
import { cloneDeep, isEmpty } from 'lodash'
import FeatureVectorsView from './FeatureVectorsView'
@@ -31,6 +30,7 @@ import {
FEATURE_VECTORS_TAB,
GROUP_BY_NAME,
GROUP_BY_NONE,
+ REQUEST_CANCELED,
TAG_FILTER_ALL_ITEMS,
TAG_FILTER_LATEST
} from '../../../constants'
@@ -41,16 +41,16 @@ import {
generatePageData
} from './featureVectors.util'
import { DANGER_BUTTON, LABEL_BUTTON } from 'igz-controls/constants'
-import { cancelRequest } from '../../../utils/cancelRequest'
import { checkTabIsValid, handleApplyDetailsChanges } from '../featureStore.util'
import { createFeatureVectorsRowData } from '../../../utils/createFeatureStoreContent'
import { getFeatureVectorIdentifier } from '../../../utils/getUniqueIdentifier'
import { getFilterTagOptions, setFilters } from '../../../reducers/filtersReducer'
import { isDetailsTabExists } from '../../../utils/isDetailsTabExists'
-import { parseFeatureVectors } from '../../../utils/parseFeatureVectors'
import { parseFeatureTemplate } from '../../../utils/parseFeatureTemplate'
+import { parseFeatureVectors } from '../../../utils/parseFeatureVectors'
import { setFeaturesPanelData } from '../../../reducers/tableReducer'
import { setNotification } from '../../../reducers/notificationReducer'
+import { showErrorNotification } from '../../../utils/notifications.util'
import { useGetTagOptions } from '../../../hooks/useGetTagOptions.hook'
import { useGroupContent } from '../../../hooks/groupContent.hook'
import { useOpenPanel } from '../../../hooks/openPanel.hook'
@@ -74,6 +74,7 @@ const FeatureVectors = ({
const featureStore = useSelector(store => store.featureStore)
const filtersStore = useSelector(store => store.filtersStore)
const featureVectorsRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
@@ -97,11 +98,11 @@ const FeatureVectors = ({
const fetchData = useCallback(
filters => {
+ abortControllerRef.current = new AbortController()
+
const config = {
- cancelToken: new axios.CancelToken(cancel => {
- featureVectorsRef.current.cancel = cancel
- }),
ui: {
+ controller: abortControllerRef.current,
setLargeRequestErrorMessage
}
}
@@ -152,14 +153,9 @@ const FeatureVectors = ({
fetchData({ ...filtersStore, tag })
})
})
- .catch(() => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => handleDeleteFeatureVector(featureVector),
- message: 'Feature vector failed to delete'
- })
+ .catch(error => {
+ showErrorNotification(dispatch, error, '', 'Feature vector failed to delete', () =>
+ handleDeleteFeatureVector(featureVector)
)
})
@@ -204,6 +200,9 @@ const FeatureVectors = ({
dispatch(
getFilterTagOptions({ fetchTags: fetchFeatureVectorsTags, project: params.projectName })
)
+ setFeatureVectors([])
+ setSelectedFeatureVector({})
+ setSelectedRowData({})
return fetchData(filters)
}
@@ -416,7 +415,7 @@ const FeatureVectors = ({
removeFeatureVectors()
setSelectedFeatureVector({})
setSelectedRowData({})
- cancelRequest(featureVectorsRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
setCreateVectorPopUpIsOpen(false)
}
}, [removeFeatureVector, removeFeatureVectors, setCreateVectorPopUpIsOpen, params.projectName])
diff --git a/src/components/FeatureStore/Features/Features.js b/src/components/FeatureStore/Features/Features.js
index de310b055..2ad6539bf 100644
--- a/src/components/FeatureStore/Features/Features.js
+++ b/src/components/FeatureStore/Features/Features.js
@@ -19,7 +19,6 @@ such restriction.
*/
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
-import axios from 'axios'
import { connect, useDispatch, useSelector } from 'react-redux'
import AddToFeatureVectorPopUp from '../../../elements/AddToFeatureVectorPopUp/AddToFeatureVectorPopUp'
@@ -28,17 +27,18 @@ import FeaturesView from './FeaturesView'
import { FeatureStoreContext } from '../FeatureStore'
import {
+ CANCEL_REQUEST_TIMEOUT,
FEATURES_TAB,
FEATURE_STORE_PAGE,
GROUP_BY_NAME,
GROUP_BY_NONE,
- TAG_FILTER_ALL_ITEMS,
- LARGE_REQUEST_CANCELED
+ LARGE_REQUEST_CANCELED,
+ REQUEST_CANCELED,
+ TAG_FILTER_ALL_ITEMS
} from '../../../constants'
import { createFeaturesRowData } from '../../../utils/createFeatureStoreContent'
import { featuresActionCreator, featuresFilters } from './features.util'
import { getFeatureIdentifier } from '../../../utils/getUniqueIdentifier'
-import { cancelRequest } from '../../../utils/cancelRequest'
import { getFilterTagOptions, setFilters } from '../../../reducers/filtersReducer'
import { parseFeatures } from '../../../utils/parseFeatures'
import { setTablePanelOpen } from '../../../reducers/tableReducer'
@@ -69,6 +69,7 @@ const Features = ({
const filtersStore = useSelector(store => store.filtersStore)
const tableStore = useSelector(store => store.tableStore)
const featureStoreRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const dispatch = useDispatch()
const { toggleConvertedYaml } = React.useContext(FeatureStoreContext)
@@ -93,13 +94,13 @@ const Features = ({
const fetchData = useCallback(
filters => {
+ abortControllerRef.current = new AbortController()
+
const cancelRequestTimeout = setTimeout(() => {
- cancelRequest(featureStoreRef, LARGE_REQUEST_CANCELED)
- }, 30000)
+ abortControllerRef.current.abort(LARGE_REQUEST_CANCELED)
+ }, CANCEL_REQUEST_TIMEOUT)
const config = {
- cancelToken: new axios.CancelToken(cancel => {
- featureStoreRef.current.cancel = cancel
- })
+ signal: abortControllerRef.current.signal
}
return Promise.allSettled([
@@ -112,7 +113,10 @@ const Features = ({
return nextValue.value ? prevValue.concat(nextValue.value) : prevValue
}, [])
- if (features.length > 1500) {
+ if (
+ features.length > 1500 ||
+ abortControllerRef.current?.signal?.reason === LARGE_REQUEST_CANCELED
+ ) {
showLargeResponsePopUp(setLargeRequestErrorMessage)
setFeatures([])
} else {
@@ -125,12 +129,6 @@ const Features = ({
return result
})
- .catch(error => {
- if (error.message === LARGE_REQUEST_CANCELED) {
- showLargeResponsePopUp(setLargeRequestErrorMessage)
- setFeatures([])
- }
- })
.finally(() => clearTimeout(cancelRequestTimeout))
},
[fetchEntities, fetchFeatures, params.projectName]
@@ -138,6 +136,12 @@ const Features = ({
const handleRefresh = filters => {
dispatch(getFilterTagOptions({ fetchTags: fetchFeatureSetsTags, project: params.projectName }))
+ setFeatures([])
+ removeFeature()
+ removeEntity()
+ removeFeatures()
+ removeEntities()
+ setSelectedRowData({})
return fetchData(filters)
}
@@ -270,7 +274,7 @@ const Features = ({
removeFeatures()
removeEntities()
setSelectedRowData({})
- cancelRequest('cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [removeEntities, removeEntity, removeFeature, removeFeatures, params.projectName])
diff --git a/src/components/FeatureStore/featureStore.util.js b/src/components/FeatureStore/featureStore.util.js
index e98161ab8..04bcc8606 100644
--- a/src/components/FeatureStore/featureStore.util.js
+++ b/src/components/FeatureStore/featureStore.util.js
@@ -30,8 +30,9 @@ import {
TAG_LATEST
} from '../../constants'
import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
-import { truncateUid } from '../../utils'
import { convertChipsData } from '../../utils/convertChipsData'
+import { showErrorNotification } from '../../utils/notifications.util'
+import { truncateUid } from '../../utils'
export const createFeatureSetTitle = 'Create set'
export const createFeatureVectorTitle = 'Create vector'
@@ -125,27 +126,24 @@ export const handleApplyDetailsChanges = (
return response
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'Permission denied.'
- : 'Failed to update.',
- retry: () => handleApplyDetailsChanges(
- changes,
- fetchData,
- projectName,
- itemName,
- pageTab,
- selectedItem,
- setNotification,
- updateFeatureStoreData,
- filters,
- dispatch
- )
- })
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Permission denied'
+ : 'Failed to update'
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () =>
+ handleApplyDetailsChanges(
+ changes,
+ fetchData,
+ projectName,
+ itemName,
+ pageTab,
+ selectedItem,
+ setNotification,
+ updateFeatureStoreData,
+ filters,
+ dispatch
+ )
)
})
}
diff --git a/src/components/Files/Files.js b/src/components/Files/Files.js
index 7e350fcf9..a611182e2 100644
--- a/src/components/Files/Files.js
+++ b/src/components/Files/Files.js
@@ -33,6 +33,7 @@ import {
FILTER_MENU_MODAL,
GROUP_BY_NAME,
GROUP_BY_NONE,
+ REQUEST_CANCELED,
TAG_FILTER_ALL_ITEMS
} from '../../constants'
import {
@@ -44,9 +45,7 @@ import {
handleApplyDetailsChanges,
registerArtifactTitle
} from './files.util'
-import { cancelRequest } from '../../utils/cancelRequest'
import { createFilesRowData } from '../../utils/createArtifactsContent'
-import {largeResponseCatchHandler } from '../../utils/largeResponseCatchHandler'
import { fetchFile, fetchFiles, removeFile, removeFiles } from '../../reducers/artifactsReducer'
import { getArtifactIdentifier } from '../../utils/getUniqueIdentifier'
import { isDetailsTabExists } from '../../utils/isDetailsTabExists'
@@ -71,6 +70,7 @@ const Files = () => {
const artifactsStore = useSelector(store => store.artifactsStore)
const filtersStore = useSelector(store => store.filtersStore)
const params = useParams()
+ const abortControllerRef = useRef(new AbortController())
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
@@ -92,14 +92,28 @@ const Files = () => {
const fetchData = useCallback(
filters => {
- dispatch(fetchFiles({ project: params.projectName, filters, setLargeRequestErrorMessage }))
+ abortControllerRef.current = new AbortController()
+
+ dispatch(
+ fetchFiles({
+ project: params.projectName,
+ filters,
+ config: {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ }
+ })
+ )
.unwrap()
.then(filesResponse => {
- setArtifactTags(filesResponse, setFiles, setAllFiles, filters, dispatch, FILES_PAGE)
+ if (filesResponse) {
+ setArtifactTags(filesResponse, setFiles, setAllFiles, filters, dispatch, FILES_PAGE)
- return filesResponse
+ return filesResponse
+ }
})
- .catch(largeResponseCatchHandler)
},
[dispatch, params.projectName]
)
@@ -261,7 +275,7 @@ const Files = () => {
setAllFiles([])
dispatch(removeFiles())
setSelectedFile({})
- cancelRequest('cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [params.projectName, dispatch])
diff --git a/src/components/FilterMenu/FilterMenu.js b/src/components/FilterMenu/FilterMenu.js
index 723e317c2..d882d3bba 100644
--- a/src/components/FilterMenu/FilterMenu.js
+++ b/src/components/FilterMenu/FilterMenu.js
@@ -45,6 +45,7 @@ import {
NAME_FILTER,
PERIOD_FILTER,
PROJECT_FILTER,
+ REQUEST_CANCELED,
SHOW_ITERATIONS,
SHOW_UNTAGGED_FILTER,
SHOW_UNTAGGED_ITEMS,
@@ -142,7 +143,7 @@ const FilterMenu = ({
const applyChanges = (data, isRefreshed) => {
if (isRefreshed && changes.counter > 0) {
- cancelRequest('cancel')
+ cancelRequest(REQUEST_CANCELED)
} else {
if ((params.jobId || params.name) && !isRefreshed) {
navigate(
diff --git a/src/components/FunctionsPage/Functions.js b/src/components/FunctionsPage/Functions.js
index 86a1f704e..cd9392079 100644
--- a/src/components/FunctionsPage/Functions.js
+++ b/src/components/FunctionsPage/Functions.js
@@ -26,6 +26,14 @@ import FunctionsView from './FunctionsView'
import JobWizard from '../JobWizard/JobWizard'
import NewFunctionPopUp from '../../elements/NewFunctionPopUp/NewFunctionPopUp'
+import {
+ FUNCTIONS_PAGE,
+ GROUP_BY_NAME,
+ PANEL_FUNCTION_CREATE_MODE,
+ SHOW_UNTAGGED_ITEMS,
+ TAG_LATEST,
+ REQUEST_CANCELED
+} from '../../constants'
import {
detailsMenu,
FUNCTIONS_EDITABLE_STATES,
@@ -34,26 +42,19 @@ import {
page,
getFunctionsEditableTypes
} from './functions.util'
-import { isDetailsTabExists } from '../../utils/isDetailsTabExists'
-import { getFunctionIdentifier } from '../../utils/getUniqueIdentifier'
-import { getFunctionLogs } from '../../utils/getFunctionLogs'
-import { parseFunctions } from '../../utils/parseFunctions'
-import { setFilters } from '../../reducers/filtersReducer'
+import createFunctionsContent from '../../utils/createFunctionsContent'
import functionsActions from '../../actions/functions'
-import { setNotification } from '../../reducers/notificationReducer'
import jobsActions from '../../actions/jobs'
-import {
- FUNCTIONS_PAGE,
- GROUP_BY_NAME,
- PANEL_FUNCTION_CREATE_MODE,
- LARGE_REQUEST_CANCELED,
- SHOW_UNTAGGED_ITEMS,
- TAG_LATEST
-} from '../../constants'
-import createFunctionsContent from '../../utils/createFunctionsContent'
import { DANGER_BUTTON, LABEL_BUTTON } from 'igz-controls/constants'
import { functionRunKinds } from '../Jobs/jobs.util'
+import { getFunctionIdentifier } from '../../utils/getUniqueIdentifier'
+import { getFunctionLogs } from '../../utils/getFunctionLogs'
+import { isDetailsTabExists } from '../../utils/isDetailsTabExists'
import { openPopUp } from 'igz-controls/utils/common.util'
+import { parseFunctions } from '../../utils/parseFunctions'
+import { setFilters } from '../../reducers/filtersReducer'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useGroupContent } from '../../hooks/groupContent.hook'
import { useMode } from '../../hooks/mode.hook'
import { useYaml } from '../../hooks/yaml.hook'
@@ -83,32 +84,46 @@ const Functions = ({
const filtersStore = useSelector(store => store.filtersStore)
const [selectedRowData, setSelectedRowData] = useState({})
const [largeRequestErrorMessage, setLargeRequestErrorMessage] = useState('')
- let fetchFunctionLogsTimeout = useRef(null)
+ const fetchFunctionLogsTimeout = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const { isStagingMode } = useMode()
const params = useParams()
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
- const refreshFunctions = useCallback(
+ const fetchData = useCallback(
filters => {
- return fetchFunctions(params.projectName, filters, setLargeRequestErrorMessage)
- .then(functions => {
+ abortControllerRef.current = new AbortController()
+
+ return fetchFunctions(params.projectName, filters, {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ }).then(functions => {
+ if (functions) {
const newFunctions = parseFunctions(functions, params.projectName)
setFunctions(newFunctions)
return newFunctions
- })
- .catch(error => {
- if (error.message === LARGE_REQUEST_CANCELED) {
- setFunctions([])
- }
- })
+ }
+ })
},
[fetchFunctions, params.projectName]
)
+ const refreshFunctions = useCallback(
+ filters => {
+ setFunctions([])
+ setSelectedFunction({})
+
+ return fetchData(filters)
+ },
+ [fetchData]
+ )
+
const handleExpand = useCallback(
(func, content) => {
const funcIdentifier = getFunctionIdentifier(func)
@@ -179,10 +194,10 @@ const Functions = ({
setDetailsLogs,
offset,
navigate,
- refreshFunctions
+ fetchData
)
},
- [fetchFunctionLogs, navigate, refreshFunctions]
+ [fetchFunctionLogs, navigate, fetchData]
)
const handleRemoveLogs = useCallback(() => {
@@ -205,22 +220,17 @@ const Functions = ({
message: 'Function deleted successfully'
})
)
- refreshFunctions()
+ fetchData()
})
- .catch(() => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => removeFunction(func),
- message: 'Function failed to delete'
- })
- )
+ .catch(error => {
+ showErrorNotification(dispatch, error, 'Function failed to delete', '', () => {
+ removeFunction(func)
+ })
})
setConfirmData(null)
},
- [deleteFunction, dispatch, navigate, params.projectName, refreshFunctions, selectedFunction]
+ [deleteFunction, dispatch, navigate, params.projectName, fetchData, selectedFunction]
)
const onRemoveFunction = useCallback(
@@ -264,13 +274,7 @@ const Functions = ({
dispatch(jobsActions.fetchJobFunctionSuccess(func.ui.originalContent))
setJobWizardMode(PANEL_FUNCTION_CREATE_MODE)
} else {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Failed to retrieve function data'
- })
- )
+ showErrorNotification(dispatch, {}, '', 'Failed to retrieve function data')
}
},
hidden:
@@ -305,13 +309,14 @@ const Functions = ({
)
useEffect(() => {
- refreshFunctions(filtersStore.filters)
+ fetchData(filtersStore.filters)
return () => {
setSelectedFunction({})
setFunctions([])
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
- }, [filtersStore.filters, params.projectName, refreshFunctions])
+ }, [filtersStore.filters, params.projectName, fetchData])
useEffect(() => {
setTaggedFunctions(
@@ -394,7 +399,7 @@ const Functions = ({
setFunctionsPanelIsOpen(false)
removeNewFunction()
- return refreshFunctions().then(() => {
+ return fetchData().then(() => {
dispatch(
setNotification({
status: 200,
@@ -415,7 +420,7 @@ const Functions = ({
setEditableItem(null)
removeNewFunction()
- return refreshFunctions().then(functions => {
+ return fetchData().then(functions => {
const currentItem = functions.find(func => func.name === name && func.tag === tag)
navigate(`/projects/${params.projectName}/functions/${currentItem.hash}/${tab}`)
@@ -429,23 +434,18 @@ const Functions = ({
})
}
- const handleDeployFunctionFailure = () => {
+ const handleDeployFunctionFailure = error => {
const { name, tag } = functionsStore.newFunction.metadata
setFunctionsPanelIsOpen(false)
removeNewFunction()
- return refreshFunctions().then(functions => {
+ return fetchData().then(functions => {
const currentItem = functions.find(func => func.name === name && func.tag === tag)
+ showErrorNotification(dispatch, error, '', 'Function deployment failed to initiate')
+
navigate(`/projects/${params.projectName}/functions/${currentItem.hash}/overview`)
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Function deployment failed to initiate'
- })
- )
})
}
diff --git a/src/components/FunctionsPanel/FunctionsPanel.js b/src/components/FunctionsPanel/FunctionsPanel.js
index 655464669..af19bdf06 100644
--- a/src/components/FunctionsPanel/FunctionsPanel.js
+++ b/src/components/FunctionsPanel/FunctionsPanel.js
@@ -255,8 +255,8 @@ const FunctionsPanel = ({
.then(response => {
handleDeployFunctionSuccess(response.data.ready)
})
- .catch(() => {
- handleDeployFunctionFailure()
+ .catch((error) => {
+ handleDeployFunctionFailure(error)
})
}
diff --git a/src/components/JobWizard/JobWizard.js b/src/components/JobWizard/JobWizard.js
index 3f75309c6..2a3227172 100644
--- a/src/components/JobWizard/JobWizard.js
+++ b/src/components/JobWizard/JobWizard.js
@@ -38,22 +38,6 @@ import Loader from '../../common/Loader/Loader'
import ScheduleWizard from '../SheduleWizard/ScheduleWizard'
import { Wizard } from 'igz-controls/components'
-import functionsActions from '../../actions/functions'
-import jobsActions from '../../actions/jobs'
-import projectsAction from '../../actions/projects'
-import { MODAL_MAX } from 'igz-controls/constants'
-import {
- generateJobRequestData,
- generateJobWizardData,
- generateJobWizardDefaultData,
- getNewJobErrorMsg,
- getSaveJobErrorMsg
-} from './JobWizard.util'
-import { resetModalFilter } from '../../reducers/filtersReducer'
-import { scheduledJobsActionCreator } from '../Jobs/ScheduledJobs/scheduledJobs.util'
-import { setFieldState } from 'igz-controls/utils/form.util'
-import { setNotification } from '../../reducers/notificationReducer'
-import { useModalBlockHistory } from '../../hooks/useModalBlockHistory.hook'
import {
ADVANCED_STEP,
DATA_INPUTS_STEP,
@@ -70,8 +54,25 @@ import {
RUN_DETAILS_STEP,
SCHEDULE_TAB
} from '../../constants'
-import { JOB_WIZARD_MODE } from '../../types'
+import {
+ generateJobRequestData,
+ generateJobWizardData,
+ generateJobWizardDefaultData,
+ getNewJobErrorMsg,
+ getSaveJobErrorMsg
+} from './JobWizard.util'
+import functionsActions from '../../actions/functions'
+import jobsActions from '../../actions/jobs'
+import projectsAction from '../../actions/projects'
import { FUNCTIONS_SELECTION_FUNCTIONS_TAB } from './JobWizardSteps/JobWizardFunctionSelection/jobWizardFunctionSelection.util'
+import { JOB_WIZARD_MODE } from '../../types'
+import { MODAL_MAX } from 'igz-controls/constants'
+import { resetModalFilter } from '../../reducers/filtersReducer'
+import { scheduledJobsActionCreator } from '../Jobs/ScheduledJobs/scheduledJobs.util'
+import { setFieldState } from 'igz-controls/utils/form.util'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
+import { useModalBlockHistory } from '../../hooks/useModalBlockHistory.hook'
import './jobWizard.scss'
@@ -315,13 +316,7 @@ const JobWizard = ({
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response.status || 400,
- id: Math.random(),
- message: getNewJobErrorMsg(error)
- })
- )
+ showErrorNotification(dispatch, error, '', getNewJobErrorMsg(error))
})
},
[dispatch, mode, navigate, onSuccessRequest, resolveModal, runNewJob]
@@ -366,13 +361,7 @@ const JobWizard = ({
navigate(`/projects/${params.projectName}/jobs/${SCHEDULE_TAB}`)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response.status || 400,
- id: Math.random(),
- message: getSaveJobErrorMsg(error)
- })
- )
+ showErrorNotification(dispatch, error, '', getSaveJobErrorMsg(error))
})
},
[dispatch, editJob, mode, navigate, onSuccessRequest, resolveModal]
diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardFunctionSelection/JobWizardFunctionSelection.js b/src/components/JobWizard/JobWizardSteps/JobWizardFunctionSelection/JobWizardFunctionSelection.js
index 1ee0161ec..4ae2e6143 100644
--- a/src/components/JobWizard/JobWizardSteps/JobWizardFunctionSelection/JobWizardFunctionSelection.js
+++ b/src/components/JobWizard/JobWizardSteps/JobWizardFunctionSelection/JobWizardFunctionSelection.js
@@ -239,33 +239,35 @@ const JobWizardFunctionSelection = ({
const onSelectedProjectNameChange = currentValue => {
dispatch(functionsActions.fetchFunctions(currentValue, {})).then(functions => {
- const validFunctions = functions.filter(func => {
- return includes(functionRunKinds, func.kind)
- })
+ if (functions) {
+ const validFunctions = functions.filter(func => {
+ return includes(functionRunKinds, func.kind)
+ })
- const groupedFunctions = Object.values(
- validFunctions.reduce((prev, curr) => {
- if (!prev[curr.metadata.name]) {
- prev[curr.metadata.name] = {
- name: curr.metadata.name,
- functions: []
+ const groupedFunctions = Object.values(
+ validFunctions.reduce((prev, curr) => {
+ if (!prev[curr.metadata.name]) {
+ prev[curr.metadata.name] = {
+ name: curr.metadata.name,
+ functions: []
+ }
}
- }
- prev[curr.metadata.name].functions.push(curr)
+ prev[curr.metadata.name].functions.push(curr)
- return prev
- }, {})
- )
+ return prev
+ }, {})
+ )
- setFunctions(groupedFunctions)
+ setFunctions(groupedFunctions)
- if (filterByName.length > 0) {
- const filteredFunctions = validFunctions.filter(func => {
- return func.metadata.name.includes(filterByName)
- })
+ if (filterByName.length > 0) {
+ const filteredFunctions = validFunctions.filter(func => {
+ return func.metadata.name.includes(filterByName)
+ })
- setFilteredFunctions(filteredFunctions)
+ setFilteredFunctions(filteredFunctions)
+ }
}
})
diff --git a/src/components/Jobs/MonitorJobs/MonitorJobs.js b/src/components/Jobs/MonitorJobs/MonitorJobs.js
index eea5a70e7..770770d7c 100644
--- a/src/components/Jobs/MonitorJobs/MonitorJobs.js
+++ b/src/components/Jobs/MonitorJobs/MonitorJobs.js
@@ -38,7 +38,7 @@ import {
JOBS_PAGE,
MONITOR_JOBS_TAB,
PANEL_RERUN_MODE,
- LARGE_REQUEST_CANCELED
+ REQUEST_CANCELED
} from '../../../constants'
import {
generateActionsMenu,
@@ -49,18 +49,19 @@ import {
import { JobsContext } from '../Jobs'
import { createJobsMonitorTabContent } from '../../../utils/createJobsContent'
import { datePickerOptions, PAST_WEEK_DATE_OPTION } from '../../../utils/datePicker.util'
+import { enrichRunWithFunctionFields, handleAbortJob } from '../jobs.util'
import { getCloseDetailsLink } from '../../../utils/getCloseDetailsLink'
+import { getJobLogs } from '../../../utils/getJobLogs.util'
import { getNoDataMessage } from '../../../utils/getNoDataMessage'
-import { enrichRunWithFunctionFields, handleAbortJob } from '../jobs.util'
import { isDetailsTabExists } from '../../../utils/isDetailsTabExists'
import { openPopUp } from 'igz-controls/utils/common.util'
-import { getJobLogs } from '../../../utils/getJobLogs.util'
import { parseJob } from '../../../utils/parseJob'
+import { setFilters } from '../../../reducers/filtersReducer'
import { setNotification } from '../../../reducers/notificationReducer'
+import { showErrorNotification } from '../../../utils/notifications.util'
import { useMode } from '../../../hooks/mode.hook'
import { usePods } from '../../../hooks/usePods.hook'
import { useYaml } from '../../../hooks/yaml.hook'
-import { setFilters } from '../../../reducers/filtersReducer'
const MonitorJobs = ({
abortJob,
@@ -89,6 +90,7 @@ const MonitorJobs = ({
const dispatch = useDispatch()
const { isStagingMode } = useMode()
const fetchJobFunctionsPromiseRef = useRef()
+ const abortControllerRef = useRef(new AbortController())
const {
editableItem,
handleMonitoring,
@@ -136,14 +138,31 @@ const MonitorJobs = ({
const refreshJobs = useCallback(
filters => {
+ if (params.jobName) {
+ setJobRuns([])
+ } else {
+ setJobs([])
+ }
+ abortControllerRef.current = new AbortController()
+
if (filters.dates) {
setDateFilter(filters.dates.value)
}
const fetchData = params.jobName ? fetchAllJobRuns : fetchJobs
- fetchData(params.projectName, filters, params.jobName ?? false, setLargeRequestErrorMessage)
- .then(jobs => {
+ fetchData(
+ params.projectName,
+ filters,
+ {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ },
+ params.jobName ?? false
+ ).then(jobs => {
+ if (jobs) {
const parsedJobs = jobs.map(job => parseJob(job, MONITOR_JOBS_TAB))
if (params.jobName) {
@@ -151,27 +170,10 @@ const MonitorJobs = ({
} else {
setJobs(parsedJobs)
}
- })
- .catch(error => {
- if (error.message === LARGE_REQUEST_CANCELED) {
- if (params.jobName) {
- setJobRuns([])
- } else {
- setJobs([])
- }
- } else {
- dispatch(
- setNotification({
- status: error?.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch jobs',
- retry: () => refreshJobs(filters)
- })
- )
- }
- })
+ }
+ })
},
- [dispatch, fetchAllJobRuns, fetchJobs, params.jobName, params.projectName]
+ [fetchAllJobRuns, fetchJobs, params.jobName, params.projectName]
)
const onAbortJob = useCallback(
@@ -223,13 +225,8 @@ const MonitorJobs = ({
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- retry: () => handleDeleteJob(job),
- message: error.response?.data?.detail || 'Deleting job failed'
- })
+ showErrorNotification(dispatch, error, 'Deleting job failed', '', () =>
+ handleDeleteJob(job)
)
})
},
@@ -398,6 +395,7 @@ const MonitorJobs = ({
return () => {
setJobs([])
setJobRuns([])
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [params.projectName])
diff --git a/src/components/Jobs/MonitorWorkflows/MonitorWorkflows.js b/src/components/Jobs/MonitorWorkflows/MonitorWorkflows.js
index af63cbe79..88fc18467 100644
--- a/src/components/Jobs/MonitorWorkflows/MonitorWorkflows.js
+++ b/src/components/Jobs/MonitorWorkflows/MonitorWorkflows.js
@@ -36,7 +36,7 @@ import {
JOBS_PAGE,
MONITOR_JOBS_TAB,
MONITOR_WORKFLOWS_TAB,
- PANEL_RERUN_MODE,
+ PANEL_RERUN_MODE, REQUEST_CANCELED,
WORKFLOW_GRAPH_VIEW
} from '../../../constants'
import {
@@ -45,10 +45,10 @@ import {
generatePageData,
monitorWorkflowsActionCreator
} from './monitorWorkflows.util'
-import { enrichRunWithFunctionFields, handleAbortJob } from '../jobs.util'
import { DANGER_BUTTON } from 'igz-controls/constants'
import { JobsContext } from '../Jobs'
import { createJobsWorkflowsTabContent } from '../../../utils/createJobsContent'
+import { enrichRunWithFunctionFields, handleAbortJob } from '../jobs.util'
import { getFunctionLogs } from '../../../utils/getFunctionLogs'
import { getJobLogs } from '../../../utils/getJobLogs.util'
import { getNoDataMessage } from '../../../utils/getNoDataMessage'
@@ -59,6 +59,7 @@ import { parseFunction } from '../../../utils/parseFunction'
import { parseJob } from '../../../utils/parseJob'
import { setFilters } from '../../../reducers/filtersReducer'
import { setNotification } from '../../../reducers/notificationReducer'
+import { showErrorNotification } from '../../../utils/notifications.util'
import { useMode } from '../../../hooks/mode.hook'
import { usePods } from '../../../hooks/usePods.hook'
import { useSortTable } from '../../../hooks/useSortTable.hook'
@@ -108,6 +109,7 @@ const MonitorWorkflows = ({
} = React.useContext(JobsContext)
let fetchFunctionLogsTimeout = useRef(null)
const fetchJobFunctionsPromiseRef = useRef()
+ const abortControllerRef = useRef(new AbortController())
usePods(fetchJobPods, removePods, selectedJob)
@@ -215,13 +217,7 @@ const MonitorWorkflows = ({
const handleCatchRequest = useCallback(
(error, message) => {
- dispatch(
- setNotification({
- status: error?.response?.status || 400,
- id: Math.random(),
- message
- })
- )
+ showErrorNotification(dispatch, error, message, '')
navigate(
location.pathname
.split('/')
@@ -302,7 +298,14 @@ const MonitorWorkflows = ({
const getWorkflows = useCallback(
filter => {
- fetchWorkflows(params.projectName, filter, setLargeRequestErrorMessage)
+ abortControllerRef.current = new AbortController()
+
+ fetchWorkflows(params.projectName, filter, {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ })
},
[fetchWorkflows, params.projectName]
)
@@ -317,14 +320,8 @@ const MonitorWorkflows = ({
const workflow = { ...workflowsStore.activeWorkflow?.data }
const getWorkflow = () => {
fetchWorkflow(params.projectName, params.workflowId).catch(error => {
+ showErrorNotification(dispatch, error, 'Failed to fetch workflow')
navigate(`/projects/${params.projectName}/jobs/${MONITOR_WORKFLOWS_TAB}`, { replace: true })
- dispatch(
- setNotification({
- status: error?.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch workflow'
- })
- )
})
}
@@ -491,6 +488,7 @@ const MonitorWorkflows = ({
setItemIsSelected(false)
setSelectedJob({})
setSelectedFunction({})
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [params.projectName, params.workflowId])
diff --git a/src/components/Jobs/ScheduledJobs/ScheduledJobs.js b/src/components/Jobs/ScheduledJobs/ScheduledJobs.js
index 1899330b2..550d5a7d1 100644
--- a/src/components/Jobs/ScheduledJobs/ScheduledJobs.js
+++ b/src/components/Jobs/ScheduledJobs/ScheduledJobs.js
@@ -17,7 +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 React, { useCallback, useState, useMemo, useEffect } from 'react'
+import React, { useCallback, useState, useMemo, useEffect, useRef } from 'react'
import { useParams } from 'react-router-dom'
import { connect, useDispatch, useSelector } from 'react-redux'
@@ -35,19 +35,20 @@ import {
LABELS_FILTER,
NAME_FILTER,
PANEL_EDIT_MODE,
- LARGE_REQUEST_CANCELED,
- SCHEDULE_TAB
+ SCHEDULE_TAB,
+ REQUEST_CANCELED
} from '../../../constants'
import { DANGER_BUTTON, FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
import { JobsContext } from '../Jobs'
import { createJobsScheduleTabContent } from '../../../utils/createJobsContent'
+import { getErrorMsg, openPopUp } from 'igz-controls/utils/common.util'
import { getJobFunctionData } from '../jobs.util'
import { getNoDataMessage } from '../../../utils/getNoDataMessage'
-import { getErrorDetail, openPopUp } from 'igz-controls/utils/common.util'
import { parseJob } from '../../../utils/parseJob'
import { scheduledJobsActionCreator } from './scheduledJobs.util'
import { setFilters } from '../../../reducers/filtersReducer'
import { setNotification } from '../../../reducers/notificationReducer'
+import { showErrorNotification } from '../../../utils/notifications.util'
import { useYaml } from '../../../hooks/yaml.hook'
import { ReactComponent as Yaml } from 'igz-controls/images/yaml.svg'
@@ -68,6 +69,7 @@ const ScheduledJobs = ({
const [convertedYaml, toggleConvertedYaml] = useYaml('')
const [editableItem, setEditableItem] = useState(null)
const [largeRequestErrorMessage, setLargeRequestErrorMessage] = useState('')
+ const abortControllerRef = useRef(new AbortController())
const dispatch = useDispatch()
const params = useParams()
const filtersStore = useSelector(store => store.filtersStore)
@@ -97,25 +99,27 @@ const ScheduledJobs = ({
const refreshJobs = useCallback(
filters => {
- fetchJobs(params.projectName, filters, true, setLargeRequestErrorMessage)
+ setJobs([])
+ abortControllerRef.current = new AbortController()
+
+ fetchJobs(
+ params.projectName,
+ filters,
+ {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ },
+ true
+ )
.then(jobs => {
- setJobs(jobs.map(job => parseJob(job, SCHEDULE_TAB)))
- })
- .catch(error => {
- if (error.message !== LARGE_REQUEST_CANCELED) {
- dispatch(
- setNotification({
- status: error?.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch jobs',
- retry: () => refreshJobs(filters),
- error
- })
- )
+ if (jobs) {
+ setJobs(jobs.map(job => parseJob(job, SCHEDULE_TAB)))
}
})
},
- [dispatch, fetchJobs, params.projectName]
+ [fetchJobs, params.projectName]
)
const handleRunJob = useCallback(
@@ -137,20 +141,12 @@ const ScheduledJobs = ({
)
})
.catch(error => {
- const errorMsg = getErrorDetail(error) || 'Job failed to start.'
+ const customErrorMsg =
+ error.response.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'You are not permitted to run a new job'
+ : getErrorMsg(error, 'Job failed to start')
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: item => handleRunJob(item),
- message:
- error.response.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'You are not permitted to run new job.'
- : errorMsg,
- error
- })
- )
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => handleRunJob(job))
})
},
[dispatch, handleRunScheduledJob, params.projectName]
@@ -251,6 +247,7 @@ const ScheduledJobs = ({
return () => {
setJobs([])
setDataIsLoaded(false)
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [params.projectName])
diff --git a/src/components/Jobs/jobs.util.js b/src/components/Jobs/jobs.util.js
index e9c386466..f2d3bd401 100644
--- a/src/components/Jobs/jobs.util.js
+++ b/src/components/Jobs/jobs.util.js
@@ -33,10 +33,10 @@ import {
JOB_KIND_SPARK
} from '../../constants'
import jobsActions from '../../actions/jobs'
-import { generateKeyValues } from '../../utils'
-import { setNotification } from '../../reducers/notificationReducer'
import { generateFunctionPriorityLabel } from '../../utils/generateFunctionPriorityLabel'
+import { generateKeyValues } from '../../utils'
import { parseKeyValues } from '../../utils/object'
+import { showErrorNotification } from '../../utils/notifications.util'
export const page = JOBS_PAGE
export const getInfoHeaders = isSpark =>
@@ -260,23 +260,17 @@ export const handleAbortJob = (
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- retry: () =>
- handleAbortJob(
- abortJob,
- projectName,
- job,
- filtersStore,
- setNotification,
- refreshJobs,
- setConfirmData,
- dispatch
- ),
- message: error.response?.data?.detail || 'Aborting job failed'
- })
+ showErrorNotification(dispatch, error, 'Aborting job failed', '', () =>
+ handleAbortJob(
+ abortJob,
+ projectName,
+ job,
+ filtersStore,
+ setNotification,
+ refreshJobs,
+ setConfirmData,
+ dispatch
+ )
)
})
setConfirmData(null)
@@ -342,13 +336,7 @@ export const enrichRunWithFunctionFields = (
return jobRun
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch function tag'
- })
- )
+ showErrorNotification(dispatch, error, 'Failed to fetch function tag', '')
})
}
diff --git a/src/components/JobsPanel/JobsPanel.js b/src/components/JobsPanel/JobsPanel.js
index 02ceb84d6..31deb4958 100644
--- a/src/components/JobsPanel/JobsPanel.js
+++ b/src/components/JobsPanel/JobsPanel.js
@@ -26,9 +26,6 @@ import { isEmpty } from 'lodash'
import JobsPanelView from './JobsPanelView'
-import { MONITOR_JOBS_TAB, PANEL_DEFAULT_ACCESS_KEY, SCHEDULE_TAB } from '../../constants'
-import jobsActions from '../../actions/jobs'
-import functionActions from '../../actions/functions'
import {
getMethodOptions,
getVersionOptions,
@@ -37,10 +34,13 @@ import {
generateRequestData,
generateTableDataFromDefaultData
} from './jobsPanel.util'
-import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
+import functionActions from '../../actions/functions'
+import jobsActions from '../../actions/jobs'
+import { MONITOR_JOBS_TAB, PANEL_DEFAULT_ACCESS_KEY, SCHEDULE_TAB } from '../../constants'
import { initialState, panelReducer, panelActions } from './panelReducer'
+import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
import { parseKeyValues } from '../../utils'
-import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import './jobsPanel.scss'
@@ -117,13 +117,7 @@ const JobsPanel = ({
useEffect(() => {
if (!functionsStore.template.name && functionsStore.error) {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Function template could not be loaded'
- })
- )
+ showErrorNotification(dispatch, functionsStore.error, '', 'Function template could not be loaded')
closePanel()
removeFunctionsError()
}
diff --git a/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js b/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
index 71c7dee0b..fd8cdb1c1 100644
--- a/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
+++ b/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
@@ -135,7 +135,7 @@ const JobsPanelDataInputs = ({
})
})
} else if (storePathType === 'feature-vectors' && inputsState.featureVectors.length === 0) {
- fetchFeatureVectors(projectName).then(featureVectors => {
+ fetchFeatureVectors(projectName, {}, {}, true).then(featureVectors => {
const featureVectorsList = uniqBy(featureVectors, 'metadata.name')
.map(featureVector => ({
label: featureVector.metadata.name,
diff --git a/src/components/ModelsPage/ModelEndpoints/ModelEndpoints.js b/src/components/ModelsPage/ModelEndpoints/ModelEndpoints.js
index 09833ab59..06252fe9b 100644
--- a/src/components/ModelsPage/ModelEndpoints/ModelEndpoints.js
+++ b/src/components/ModelsPage/ModelEndpoints/ModelEndpoints.js
@@ -22,16 +22,19 @@ import { useDispatch, useSelector } from 'react-redux'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { isEmpty, orderBy } from 'lodash'
-import ModelEndpointsView from './ModelEndpointsView'
+import ArtifactsTableRow from '../../../elements/ArtifactsTableRow/ArtifactsTableRow'
+import FilterMenu from '../../FilterMenu/FilterMenu'
+import ModelsPageTabs from '../ModelsPageTabs/ModelsPageTabs'
+import NoData from '../../../common/NoData/NoData'
+import Table from '../../Table/Table'
import detailsActions from '../../../actions/details'
-import { GROUP_BY_NONE, MODEL_ENDPOINTS_TAB } from '../../../constants'
-import { cancelRequest } from '../../../utils/cancelRequest'
+import { GROUP_BY_NONE, MODEL_ENDPOINTS_TAB, MODELS_PAGE, REQUEST_CANCELED } from '../../../constants'
import { createModelEndpointsRowData } from '../../../utils/createArtifactsContent'
import { fetchModelEndpoints, removeModelEndpoints } from '../../../reducers/artifactsReducer'
-import { generatePageData } from './modelEndpoints.util'
+import { filters, generatePageData } from './modelEndpoints.util'
+import { getNoDataMessage } from '../../../utils/getNoDataMessage'
import { isDetailsTabExists } from '../../../utils/isDetailsTabExists'
-import { largeResponseCatchHandler } from '../../../utils/largeResponseCatchHandler'
import { setFilters } from '../../../reducers/filtersReducer'
import { useModelsPage } from '../ModelsPage.context'
@@ -47,6 +50,7 @@ const ModelEndpoints = () => {
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
+ const abortControllerRef = useRef(new AbortController())
const modelEndpointsRef = useRef(null)
const pageData = useMemo(() => generatePageData(), [])
const { toggleConvertedYaml } = useModelsPage()
@@ -66,26 +70,44 @@ const ModelEndpoints = () => {
const fetchData = useCallback(
filters => {
+ abortControllerRef.current = new AbortController()
+
dispatch(
fetchModelEndpoints({
project: params.projectName,
filters,
+ config: {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ },
params: {
metric: 'latency_avg_1h',
start: 'now-10m'
- },
- setLargeRequestErrorMessage
+ }
})
)
.unwrap()
- .then(result => {
- setModelEndpoints(result)
+ .then(modelEndpoints => {
+ if (modelEndpoints) {
+ setModelEndpoints(modelEndpoints)
+ }
})
- .catch(largeResponseCatchHandler)
},
[dispatch, params.projectName]
)
+ const handleRefresh = useCallback(
+ filters => {
+ setModelEndpoints([])
+ setSelectedModelEndpoint({})
+
+ return fetchData(filters)
+ },
+ [fetchData]
+ )
+
const handleSelectItem = useCallback(
modelEndpoint => {
if (!isEmpty(modelEndpoint)) {
@@ -112,7 +134,7 @@ const ModelEndpoints = () => {
setModelEndpoints([])
dispatch(removeModelEndpoints())
setSelectedModelEndpoint({})
- cancelRequest(modelEndpointsRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [dispatch])
@@ -158,19 +180,60 @@ const ModelEndpoints = () => {
}, [params.projectName, sortedContent])
return (
-
+
+
+
+ {artifactsStore.loading ? null : modelEndpoints.length === 0 ? (
+
+ ) : (
+ <>
+
handleSelectItem({})}
+ pageData={pageData}
+ retryRequest={fetchData}
+ selectedItem={selectedModelEndpoint}
+ tab={MODEL_ENDPOINTS_TAB}
+ tableHeaders={tableContent[0]?.content ?? []}
+ >
+ {tableContent.map((tableItem, index) => {
+ return (
+
+ )
+ })}
+
+ >
+ )}
+
+
)
}
diff --git a/src/components/ModelsPage/ModelEndpoints/ModelEndpointsView.js b/src/components/ModelsPage/ModelEndpoints/ModelEndpointsView.js
deleted file mode 100644
index 764d310d2..000000000
--- a/src/components/ModelsPage/ModelEndpoints/ModelEndpointsView.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-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 React from 'react'
-import PropTypes from 'prop-types'
-
-import ArtifactsTableRow from '../../../elements/ArtifactsTableRow/ArtifactsTableRow'
-import FilterMenu from '../../FilterMenu/FilterMenu'
-import ModelsPageTabs from '../ModelsPageTabs/ModelsPageTabs'
-import NoData from '../../../common/NoData/NoData'
-import Table from '../../Table/Table'
-
-import { filters } from './modelEndpoints.util'
-import { MODEL_ENDPOINTS_TAB, MODELS_PAGE } from '../../../constants'
-import { getNoDataMessage } from '../../../utils/getNoDataMessage'
-import { ACTIONS_MENU } from '../../../types'
-
-const ModelEndpointsView = React.forwardRef(
- (
- {
- actionsMenu,
- artifactsStore,
- fetchData,
- filtersStore,
- largeRequestErrorMessage,
- modelEndpoints,
- pageData,
- selectedModelEndpoint,
- setSelectedModelEndpoint,
- tableContent
- },
- ref
- ) => {
- return (
- <>
-
-
-
- {artifactsStore.loading ? null : modelEndpoints.length === 0 ? (
-
- ) : (
- <>
-
setSelectedModelEndpoint({})}
- pageData={pageData}
- retryRequest={fetchData}
- selectedItem={selectedModelEndpoint}
- tab={MODEL_ENDPOINTS_TAB}
- tableHeaders={tableContent[0]?.content ?? []}
- >
- {tableContent.map((tableItem, index) => {
- return (
-
- )
- })}
-
- >
- )}
-
-
- >
- )
- }
-)
-
-ModelEndpointsView.propTypes = {
- actionsMenu: ACTIONS_MENU.isRequired,
- artifactsStore: PropTypes.object.isRequired,
- fetchData: PropTypes.func.isRequired,
- filtersStore: PropTypes.object.isRequired,
- largeRequestErrorMessage: PropTypes.string.isRequired,
- modelEndpoints: PropTypes.arrayOf(PropTypes.object).isRequired,
- pageData: PropTypes.object.isRequired,
- selectedModelEndpoint: PropTypes.object.isRequired,
- setSelectedModelEndpoint: PropTypes.func.isRequired,
- tableContent: PropTypes.arrayOf(PropTypes.object).isRequired
-}
-
-export default ModelEndpointsView
diff --git a/src/components/ModelsPage/Models/Models.js b/src/components/ModelsPage/Models/Models.js
index ca4bdc219..4fc6e7d8b 100644
--- a/src/components/ModelsPage/Models/Models.js
+++ b/src/components/ModelsPage/Models/Models.js
@@ -41,7 +41,8 @@ import {
TAG_FILTER_ALL_ITEMS,
FILTER_MENU_MODAL,
GROUP_BY_NONE,
- MODELS_FILTERS
+ MODELS_FILTERS,
+ REQUEST_CANCELED
} from '../../../constants'
import {
checkForSelectedModel,
@@ -53,7 +54,6 @@ import {
handleApplyDetailsChanges
} from './models.util'
import detailsActions from '../../../actions/details'
-import { cancelRequest } from '../../../utils/cancelRequest'
import { createModelsRowData } from '../../../utils/createArtifactsContent'
import { getArtifactIdentifier } from '../../../utils/getUniqueIdentifier'
import { isDetailsTabExists } from '../../../utils/isDetailsTabExists'
@@ -68,7 +68,6 @@ import { useGetTagOptions } from '../../../hooks/useGetTagOptions.hook'
import { getViewMode } from '../../../utils/helper'
import { useMode } from '../../../hooks/mode.hook'
import { setArtifactTags } from '../../../utils/artifacts.util'
-import { largeResponseCatchHandler } from '../../../utils/largeResponseCatchHandler'
const Models = ({ fetchModelFeatureVector }) => {
const [models, setModels] = useState([])
@@ -85,6 +84,7 @@ const Models = ({ fetchModelFeatureVector }) => {
const location = useLocation()
const dispatch = useDispatch()
const modelsRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const viewMode = getViewMode(window.location.search)
const pageData = useMemo(
() => generatePageData(selectedModel, viewMode),
@@ -108,16 +108,28 @@ const Models = ({ fetchModelFeatureVector }) => {
const fetchData = useCallback(
async filters => {
+ abortControllerRef.current = new AbortController()
+
return dispatch(
- fetchModels({ project: params.projectName, filters, setLargeRequestErrorMessage })
+ fetchModels({
+ project: params.projectName,
+ filters,
+ config: {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ }
+ })
)
.unwrap()
.then(modelsResponse => {
- setArtifactTags(modelsResponse, setModels, setAllModels, filters, dispatch, MODELS_TAB)
+ if (modelsResponse) {
+ setArtifactTags(modelsResponse, setModels, setAllModels, filters, dispatch, MODELS_TAB)
- return modelsResponse
+ return modelsResponse
+ }
})
- .catch(largeResponseCatchHandler)
},
[dispatch, setModels, params.projectName]
)
@@ -288,7 +300,7 @@ const Models = ({ fetchModelFeatureVector }) => {
setAllModels([])
dispatch(removeModels())
setSelectedModel({})
- cancelRequest(modelsRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [dispatch, setModels, setAllModels])
diff --git a/src/components/ModelsPage/Models/models.util.js b/src/components/ModelsPage/Models/models.util.js
index 9307616b2..505526929 100644
--- a/src/components/ModelsPage/Models/models.util.js
+++ b/src/components/ModelsPage/Models/models.util.js
@@ -31,22 +31,24 @@ import {
FULL_VIEW_MODE,
MODEL_TYPE
} from '../../../constants'
-import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
-import { applyTagChanges } from '../../../utils/artifacts.util'
-import { createModelsRowData, getIsTargetPathValid } from '../../../utils/createArtifactsContent'
-import { getArtifactIdentifier } from '../../../utils/getUniqueIdentifier'
-import { searchArtifactItem } from '../../../utils/searchArtifactItem'
import {
fetchModel,
showArtifactsPreview,
updateArtifact
} from '../../../reducers/artifactsReducer'
+import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { applyTagChanges } from '../../../utils/artifacts.util'
import { convertChipsData } from '../../../utils/convertChipsData'
-import { sortListByDate } from '../../../utils'
import { copyToClipboard } from '../../../utils/copyToClipboard'
+import { createModelsRowData, getIsTargetPathValid } from '../../../utils/createArtifactsContent'
import { generateUri } from '../../../utils/resources'
+import { getArtifactIdentifier } from '../../../utils/getUniqueIdentifier'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
import { handleDeleteArtifact } from '../../../utils/handleDeleteArtifact'
+import { searchArtifactItem } from '../../../utils/searchArtifactItem'
import { setDownloadItem, setShowDownloadsList } from '../../../reducers/downloadReducer'
+import { showErrorNotification } from '../../../utils/notifications.util'
+import { sortListByDate } from '../../../utils'
import { ReactComponent as TagIcon } from 'igz-controls/images/tag-icon.svg'
import { ReactComponent as YamlIcon } from 'igz-controls/images/yaml.svg'
@@ -235,17 +237,13 @@ export const handleApplyDetailsChanges = (
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'Permission denied'
- : 'Failed to update the model',
- retry: () =>
- dispatch(updateArtifact({ project: projectName, data: artifactItem })).unwrap()
- })
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Permission denied'
+ : getErrorMsg(error, 'Failed to update the model')
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () =>
+ handleApplyDetailsChanges(changes, projectName, selectedItem, setNotification, dispatch)
)
})
}
diff --git a/src/components/ModelsPage/RealTimePipelines/RealTimePipelines.js b/src/components/ModelsPage/RealTimePipelines/RealTimePipelines.js
index 82566bba0..b6aed8abf 100644
--- a/src/components/ModelsPage/RealTimePipelines/RealTimePipelines.js
+++ b/src/components/ModelsPage/RealTimePipelines/RealTimePipelines.js
@@ -20,16 +20,28 @@ such restriction.
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useNavigate, useParams } from 'react-router-dom'
+import classnames from 'classnames'
-import RealTimePipelinesView from './RealTimePipelinesView'
+import FilterMenu from '../../FilterMenu/FilterMenu'
+import FunctionsTableRow from '../../../elements/FunctionsTableRow/FunctionsTableRow'
+import Loader from '../../../common/Loader/Loader'
+import ModelsPageTabs from '../ModelsPageTabs/ModelsPageTabs'
+import NoData from '../../../common/NoData/NoData'
+import Pipeline from '../../Pipeline/Pipeline'
+import Table from '../../Table/Table'
+import { getNoDataMessage } from '../../../utils/getNoDataMessage'
-import { GROUP_BY_NAME, MODELS_PAGE, REAL_TIME_PIPELINES_TAB } from '../../../constants'
-import { fetchArtifactsFunctions, removePipelines } from '../../../reducers/artifactsReducer'
import createFunctionsContent from '../../../utils/createFunctionsContent'
-import { cancelRequest } from '../../../utils/cancelRequest'
-import { generatePageData } from './realTimePipelines.util'
-import { largeResponseCatchHandler } from '../../../utils/largeResponseCatchHandler'
+import {
+ GROUP_BY_NAME,
+ MODELS_PAGE,
+ REAL_TIME_PIPELINES_TAB,
+ REQUEST_CANCELED
+} from '../../../constants'
+import { fetchArtifactsFunctions, removePipelines } from '../../../reducers/artifactsReducer'
+import { filters, generatePageData } from './realTimePipelines.util'
import { getFunctionIdentifier } from '../../../utils/getUniqueIdentifier'
+import { largeResponseCatchHandler } from '../../../utils/largeResponseCatchHandler'
import { setFilters } from '../../../reducers/filtersReducer'
import { useGroupContent } from '../../../hooks/groupContent.hook'
import { useModelsPage } from '../ModelsPage.context'
@@ -46,9 +58,15 @@ const RealTimePipelines = () => {
const navigate = useNavigate()
const dispatch = useDispatch()
const pipelinesRef = useRef(null)
+ const abortControllerRef = useRef(new AbortController())
const pageData = useMemo(() => generatePageData(params.pipelineId), [params.pipelineId])
const { toggleConvertedYaml } = useModelsPage()
+ const filterMenuClassNames = classnames(
+ 'content__action-bar-wrapper',
+ params.pipelineId && 'content__action-bar-wrapper_hidden'
+ )
+
const actionsMenu = useMemo(
() => [
[
@@ -64,11 +82,18 @@ const RealTimePipelines = () => {
const fetchData = useCallback(
filters => {
+ abortControllerRef.current = new AbortController()
+
dispatch(
fetchArtifactsFunctions({
project: params.projectName,
filters,
- setLargeRequestErrorMessage
+ config: {
+ ui: {
+ controller: abortControllerRef.current,
+ setLargeRequestErrorMessage
+ }
+ }
})
)
.unwrap()
@@ -80,7 +105,9 @@ const RealTimePipelines = () => {
)
)
})
- .catch(largeResponseCatchHandler)
+ .catch(error =>
+ largeResponseCatchHandler(error, 'Failed to fetch real-time pipelines', dispatch)
+ )
},
[dispatch, params.projectName]
)
@@ -101,6 +128,16 @@ const RealTimePipelines = () => {
[params.projectName]
)
+ const handleRefresh = useCallback(
+ filters => {
+ setPipelines([])
+ setSelectedRowData({})
+
+ return fetchData(filters)
+ },
+ [fetchData]
+ )
+
const handleCollapse = useCallback(
func => {
const funcIdentifier = getFunctionIdentifier(func)
@@ -152,7 +189,7 @@ const RealTimePipelines = () => {
return () => {
setPipelines([])
dispatch(removePipelines())
- cancelRequest(pipelinesRef, 'cancel')
+ abortControllerRef.current.abort(REQUEST_CANCELED)
}
}, [dispatch])
@@ -167,21 +204,67 @@ const RealTimePipelines = () => {
}, [navigate, params.pipelineId, params.projectName, pipelines])
return (
-
+ <>
+ {artifactsStore.loading &&
}
+
+
+
+ {artifactsStore.loading ? null : pipelines.length === 0 ? (
+
+ ) : params.pipelineId ? (
+
+ ) : (
+ <>
+
+ {tableContent.map((tableItem, index) => {
+ return (
+ {}}
+ rowIndex={index}
+ key={index}
+ rowItem={tableItem}
+ selectedItem={{}}
+ selectedRowData={selectedRowData}
+ />
+ )
+ })}
+
+ >
+ )}
+
+
+ >
)
}
diff --git a/src/components/ModelsPage/RealTimePipelines/RealTimePipelinesView.js b/src/components/ModelsPage/RealTimePipelines/RealTimePipelinesView.js
deleted file mode 100644
index c533b6d77..000000000
--- a/src/components/ModelsPage/RealTimePipelines/RealTimePipelinesView.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-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 React from 'react'
-import classnames from 'classnames'
-import PropTypes from 'prop-types'
-
-import FilterMenu from '../../FilterMenu/FilterMenu'
-import FunctionsTableRow from '../../../elements/FunctionsTableRow/FunctionsTableRow'
-import ModelsPageTabs from '../ModelsPageTabs/ModelsPageTabs'
-import Pipeline from '../../Pipeline/Pipeline'
-import Table from '../../Table/Table'
-import Loader from '../../../common/Loader/Loader'
-
-import { filters } from './realTimePipelines.util'
-import { MODELS_PAGE, REAL_TIME_PIPELINES_TAB } from '../../../constants'
-import NoData from '../../../common/NoData/NoData'
-import { getNoDataMessage } from '../../../utils/getNoDataMessage'
-import { ACTIONS_MENU } from '../../../types'
-
-const RealTimePipelinesView = React.forwardRef(
- (
- {
- actionsMenu,
- artifactsStore,
- expand,
- fetchData,
- filtersStore,
- handleExpandAll,
- handleExpandRow,
- largeRequestErrorMessage,
- pageData,
- params,
- pipelines,
- selectedRowData,
- tableContent
- },
- ref
- ) => {
- const filterMenuClassNames = classnames(
- 'content__action-bar-wrapper',
- params.pipelineId && 'content__action-bar-wrapper_hidden'
- )
-
- return (
- <>
- {artifactsStore.loading &&
}
-
-
-
- {artifactsStore.loading ? null : pipelines.length === 0 ? (
-
- ) : params.pipelineId ? (
-
- ) : (
- <>
-
- {tableContent.map((tableItem, index) => {
- return (
- {}}
- rowIndex={index}
- key={index}
- rowItem={tableItem}
- selectedItem={{}}
- selectedRowData={selectedRowData}
- />
- )
- })}
-
- >
- )}
-
-
- >
- )
- }
-)
-
-RealTimePipelinesView.propTypes = {
- actionsMenu: ACTIONS_MENU.isRequired,
- artifactsStore: PropTypes.object.isRequired,
- expand: PropTypes.bool.isRequired,
- fetchData: PropTypes.func.isRequired,
- filtersStore: PropTypes.object.isRequired,
- handleExpandAll: PropTypes.func.isRequired,
- handleExpandRow: PropTypes.func.isRequired,
- largeRequestErrorMessage: PropTypes.string.isRequired,
- pageData: PropTypes.object.isRequired,
- params: PropTypes.object.isRequired,
- pipelines: PropTypes.arrayOf(PropTypes.object).isRequired,
- selectedRowData: PropTypes.object.isRequired,
- tableContent: PropTypes.arrayOf(PropTypes.object).isRequired
-}
-
-export default RealTimePipelinesView
diff --git a/src/components/Project/ProjectMonitor.js b/src/components/Project/ProjectMonitor.js
index 45acdeac1..b6ae4bc35 100644
--- a/src/components/Project/ProjectMonitor.js
+++ b/src/components/Project/ProjectMonitor.js
@@ -26,16 +26,16 @@ import ProjectMonitorView from './ProjectMonitorView'
import RegisterArtifactModal from '../RegisterArtifactModal/RegisterArtifactModal'
import RegisterModelModal from '../../elements/RegisterModelModal/RegisterModelModal'
-import { DATASET_TYPE, DATASETS, MODEL_TYPE } from '../../constants'
-
import featureStoreActions from '../../actions/featureStore'
import functionsActions from '../../actions/functions'
import nuclioAction from '../../actions/nuclio'
import projectsAction from '../../actions/projects'
+import { DATASET_TYPE, DATASETS, MODEL_TYPE } from '../../constants'
import { areNuclioStreamsEnabled } from '../../utils/helper'
import { generateCreateNewOptions, handleFetchProjectError } from './project.utils'
import { openPopUp } from 'igz-controls/utils/common.util'
import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useNuclioMode } from '../../hooks/nuclioMode.hook'
const ProjectMonitor = ({
@@ -197,7 +197,7 @@ const ProjectMonitor = ({
setShowFunctionsPanel(false)
removeNewFunction()
- const funcs = await fetchProjectFunctions(params.projectName).catch(() => {
+ const funcs = await fetchProjectFunctions(params.projectName).catch(error => {
dispatch(
setNotification({
status: 200,
@@ -206,13 +206,7 @@ const ProjectMonitor = ({
})
)
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Failed to fetch functions'
- })
- )
+ showErrorNotification(dispatch, error, '', 'Failed to fetch functions')
})
if (!isEmpty(funcs)) {
@@ -234,28 +228,15 @@ const ProjectMonitor = ({
}
}
- const handleDeployFunctionFailure = async () => {
+ const handleDeployFunctionFailure = async deployError => {
const { name, tag } = functionsStore.newFunction.metadata
setShowFunctionsPanel(false)
removeNewFunction()
- const funcs = await fetchProjectFunctions(params.projectName).catch(() => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Function deployment failed to initiate'
- })
- )
-
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Failed to fetch functions'
- })
- )
+ const funcs = await fetchProjectFunctions(params.projectName).catch(error => {
+ showErrorNotification(dispatch, deployError, '', 'Function deployment failed to initiate')
+ showErrorNotification(dispatch, error, '', 'Failed to fetch functions')
})
if (!isEmpty(funcs)) {
@@ -267,13 +248,7 @@ const ProjectMonitor = ({
navigate(`/projects/${params.projectName}/functions/${currentItem.metadata.hash}/overview`)
}
- return dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Function deployment failed to initiate'
- })
- )
+ showErrorNotification(dispatch, deployError, '', 'Function deployment failed to initiate')
}
}
diff --git a/src/components/ProjectSettings/ProjectSettings.js b/src/components/ProjectSettings/ProjectSettings.js
index fcedb409a..c3c7616c7 100644
--- a/src/components/ProjectSettings/ProjectSettings.js
+++ b/src/components/ProjectSettings/ProjectSettings.js
@@ -27,9 +27,6 @@ import ProjectSettingsSecrets from '../../elements/ProjectSettingsSecrets/Projec
import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs'
import ContentMenu from '../../elements/ContentMenu/ContentMenu'
-import { setNotification } from '../../reducers/notificationReducer'
-import projectsIguazioApi from '../../api/projects-iguazio-api'
-import { PROJECTS_SETTINGS_MEMBERS_TAB, PROJECTS_SETTINGS_SECRETS_TAB } from '../../constants'
import {
COMPLETED_STATE,
generateMembers,
@@ -38,12 +35,16 @@ import {
tabs,
validTabs
} from './projectSettings.util'
-import { isProjectValid } from '../../utils/handleRedirect'
import {
initialMembersState,
membersActions,
membersReducer
} from '../../elements/MembersPopUp/membersReducer'
+import projectsIguazioApi from '../../api/projects-iguazio-api'
+import { PROJECTS_SETTINGS_MEMBERS_TAB, PROJECTS_SETTINGS_SECRETS_TAB } from '../../constants'
+import { isProjectValid } from '../../utils/handleRedirect'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import './projectSettings.scss'
@@ -97,15 +98,7 @@ const ProjectSettings = ({ frontendSpec, projectStore }) => {
return projectsIguazioApi
.getProjectMembers(projectId)
.then(membersResponse => generateMembers(membersResponse, membersDispatch))
- .catch(() =>
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Failed to fetch data'
- })
- )
- )
+ .catch(error => showErrorNotification(dispatch, error, 'Failed to fetch project members'))
},
[dispatch]
)
diff --git a/src/components/ProjectsPage/Projects.js b/src/components/ProjectsPage/Projects.js
index 7fad69fdc..3bcd81fa4 100644
--- a/src/components/ProjectsPage/Projects.js
+++ b/src/components/ProjectsPage/Projects.js
@@ -17,12 +17,11 @@ 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 React, { useEffect, useState, useCallback } from 'react'
-import { connect, useDispatch } from 'react-redux'
-import yaml from 'js-yaml'
+import React, { useEffect, useState, useCallback, useRef } from 'react'
import FileSaver from 'file-saver'
+import yaml from 'js-yaml'
+import { connect, useDispatch } from 'react-redux'
import { orderBy } from 'lodash'
-import axios from 'axios'
import { useParams } from 'react-router-dom'
import ProjectsView from './ProjectsView'
@@ -37,6 +36,7 @@ import nuclioActions from '../../actions/nuclio'
import projectsAction from '../../actions/projects'
import { DANGER_BUTTON, FORBIDDEN_ERROR_STATUS_CODE, PRIMARY_BUTTON } from 'igz-controls/constants'
import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useNuclioMode } from '../../hooks/nuclioMode.hook'
import { useMode } from '../../hooks/mode.hook'
@@ -64,7 +64,7 @@ const Projects = ({
const [isDescendingOrder, setIsDescendingOrder] = useState(false)
const [selectedProjectsState, setSelectedProjectsState] = useState('active')
const [sortProjectId, setSortProjectId] = useState('byName')
- const [source] = useState(axios.CancelToken.source())
+ const abortControllerRef = useRef(new AbortController())
const urlParams = useParams()
const dispatch = useDispatch()
const { isDemoMode } = useMode()
@@ -105,20 +105,21 @@ const Projects = ({
)
const refreshProjects = useCallback(() => {
+ abortControllerRef.current = new AbortController()
+
if (!isNuclioModeDisabled) {
fetchNuclioFunctions()
}
removeProjects()
fetchMinimalProjects()
- fetchProjectsSummary(source.token)
+ fetchProjectsSummary(abortControllerRef.current.signal)
}, [
fetchNuclioFunctions,
fetchMinimalProjects,
fetchProjectsSummary,
isNuclioModeDisabled,
- removeProjects,
- source.token
+ removeProjects
])
const handleSearchOnFocus = useCallback(() => {
@@ -140,16 +141,13 @@ const Projects = ({
fetchMinimalProjects()
})
.catch(error => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => handleArchiveProject(project),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? `You are not allowed to archive ${project.metadata.name} project`
- : `Failed to archive ${project.metadata.name} project`
- })
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? `You are not allowed to archive ${project.metadata.name} project`
+ : `Failed to archive ${project.metadata.name} project`
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () =>
+ handleArchiveProject(project)
)
})
setConfirmData(null)
@@ -177,7 +175,6 @@ const Projects = ({
handleDeleteProject,
project,
setConfirmData,
- setNotification,
dispatch,
deleteNonEmpty
)
@@ -251,14 +248,9 @@ const Projects = ({
FileSaver.saveAs(blob, `${projectMinimal.metadata.name}.yaml`)
})
- .catch(() => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => exportYaml(projectMinimal),
- message: "Failed to fetch project's YAML"
- })
+ .catch(error => {
+ showErrorNotification(dispatch, error, '', "Failed to fetch project's YAML", () =>
+ exportYaml(projectMinimal)
)
})
}
@@ -273,16 +265,11 @@ const Projects = ({
.then(project => {
convertToYaml(project)
})
- .catch(() => {
+ .catch((error) => {
setConvertedYaml('')
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => viewYaml(projectMinimal),
- message: "Failed to fetch project's YAML"
- })
+ showErrorNotification(dispatch, error, '', "Failed to fetch project's YAML", () =>
+ viewYaml(projectMinimal)
)
})
} else {
@@ -321,20 +308,14 @@ const Projects = ({
}
fetchMinimalProjects()
- fetchProjectsSummary(source.token)
- }, [
- fetchMinimalProjects,
- fetchNuclioFunctions,
- fetchProjectsSummary,
- isNuclioModeDisabled,
- source.token
- ])
+ fetchProjectsSummary(abortControllerRef.current.signal)
+ }, [fetchMinimalProjects, fetchNuclioFunctions, fetchProjectsSummary, isNuclioModeDisabled])
useEffect(() => {
return () => {
- source.cancel('canceled')
+ abortControllerRef.current.abort()
}
- }, [source])
+ }, [])
useEffect(() => {
setFilteredProjects(handleSortProjects(projectStore.projects.filter(handleFilterProject)))
@@ -361,10 +342,11 @@ const Projects = ({
createNewProject({
metadata: {
name: formState.values.name,
- labels: formState.values.labels?.reduce((acc, labelData) => {
- acc[labelData.key] = labelData.value
- return acc
- }, {}) ?? {}
+ labels:
+ formState.values.labels?.reduce((acc, labelData) => {
+ acc[labelData.key] = labelData.value
+ return acc
+ }, {}) ?? {}
},
spec: {
description: formState.values.description
diff --git a/src/components/ProjectsPage/projectsData.js b/src/components/ProjectsPage/projectsData.js
index 88e2a6031..3c2e1fac8 100644
--- a/src/components/ProjectsPage/projectsData.js
+++ b/src/components/ProjectsPage/projectsData.js
@@ -20,6 +20,7 @@ such restriction.
import React from 'react'
import { DANGER_BUTTON, FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { showErrorNotification } from '../../utils/notifications.util'
import { ReactComponent as ArchiveIcon } from 'igz-controls/images/archive-icon.svg'
import { ReactComponent as Delete } from 'igz-controls/images/delete.svg'
@@ -109,7 +110,6 @@ export const handleDeleteProjectError = (
handleDeleteProject,
project,
setConfirmData,
- setNotification,
dispatch,
deleteNonEmpty
) => {
@@ -130,16 +130,10 @@ export const handleDeleteProjectError = (
}
})
} else {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- retry: () => handleDeleteProject(project),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? `You are not allowed to delete ${project.metadata.name} project`
- : `Failed to delete ${project.metadata.name} project`
- })
- )
+ const customErrorMsg = error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? `You are not allowed to delete ${project.metadata.name} project`
+ : `Failed to delete ${project.metadata.name} project`
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => handleDeleteProject(project))
}
}
diff --git a/src/constants.js b/src/constants.js
index 1bebdf50e..49706b9c6 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -47,15 +47,18 @@ export const DENSITY_NORMAL = 'normal'
export const DENSITY_MEDIUM = 'medium'
export const DENSITY_CHUNKY = 'chunky'
-export const NAVBAR_WIDTH_CLOSED = 57
-export const NAVBAR_WIDTH_OPENED = 245
-
export const FULL_VIEW_MODE = 'full'
-export const LARGE_REQUEST_CANCELED = 'Request canceled'
+export const LARGE_REQUEST_CANCELED = 'Large request canceled'
+export const REQUEST_CANCELED = 'Request canceled'
+export const DEFAULT_ABORT_MSG = 'canceled'
export const MODEL_PATH_DATA_INPUT = 'model_path'
+export const NAVBAR_WIDTH_CLOSED = 57
+export const NAVBAR_WIDTH_OPENED = 245
+export const CANCEL_REQUEST_TIMEOUT = 60000
+
/*=========== PAGES & TABS =============*/
export const PROJECTS_PAGE = 'PROJECTS'
diff --git a/src/elements/AddArtifactTagPopUp/AddArtifactTagPopUp.js b/src/elements/AddArtifactTagPopUp/AddArtifactTagPopUp.js
index 5f4e3da11..07f382069 100644
--- a/src/elements/AddArtifactTagPopUp/AddArtifactTagPopUp.js
+++ b/src/elements/AddArtifactTagPopUp/AddArtifactTagPopUp.js
@@ -27,10 +27,11 @@ import { createForm } from 'final-form'
import { Button, FormInput, Modal } from 'igz-controls/components'
import { DATASET_TYPE, MODEL_TYPE } from '../../constants'
-import { setNotification } from '../../reducers/notificationReducer'
import { SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants'
-import { getValidationRules } from 'igz-controls/utils/validation.util'
import { addTag } from '../../reducers/artifactsReducer'
+import { getValidationRules } from 'igz-controls/utils/validation.util'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useModalBlockHistory } from '../../hooks/useModalBlockHistory.hook'
const AddArtifactTagPopUp = ({
@@ -100,13 +101,8 @@ const AddArtifactTagPopUp = ({
onAddTag && onAddTag(filtersStore)
})
.catch(error => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Failed to add a tag',
- retry: addArtifactTag
- })
+ showErrorNotification(dispatch, error, 'Failed to add a tag', '', () =>
+ addArtifactTag(values)
)
})
diff --git a/src/elements/AddToFeatureVectorPopUp/AddToFeatureVectorPopUp.js b/src/elements/AddToFeatureVectorPopUp/AddToFeatureVectorPopUp.js
index 7e8244b37..e5cf3d2a9 100644
--- a/src/elements/AddToFeatureVectorPopUp/AddToFeatureVectorPopUp.js
+++ b/src/elements/AddToFeatureVectorPopUp/AddToFeatureVectorPopUp.js
@@ -71,18 +71,20 @@ const AddToFeatureVectorPopUp = ({
const onSelectProject = projectName => {
setSelectedProject(projectName)
fetchFeatureVectors(projectName).then(result => {
- const featureVectorsOptions = result.map(featureVector => {
- return {
- id: featureVector.metadata.name,
- label: featureVector.metadata.name
- }
- })
+ if (result) {
+ const featureVectorsOptions = result.map(featureVector => {
+ return {
+ id: featureVector.metadata.name,
+ label: featureVector.metadata.name
+ }
+ })
- setFeatureVectors(result)
- setFeatureVectorsList(uniqBy(featureVectorsOptions, 'id'))
- setFeatureVectorTagsList([])
- setSelectedFeatureVector('')
- setSelectedFeatureVectorTag('')
+ setFeatureVectors(result)
+ setFeatureVectorsList(uniqBy(featureVectorsOptions, 'id'))
+ setFeatureVectorTagsList([])
+ setSelectedFeatureVector('')
+ setSelectedFeatureVectorTag('')
+ }
})
}
diff --git a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.js b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.js
index c428e3b95..bc0f825ec 100644
--- a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.js
+++ b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.js
@@ -26,12 +26,14 @@ import { debounce } from 'lodash'
import Input from '../../common/Input/Input'
import { Button, PopUpDialog } from 'igz-controls/components'
-import { setNotification } from '../../reducers/notificationReducer'
import projectsIguazioApi from '../../api/projects-iguazio-api'
-import { deleteUnsafeHtml } from '../../utils'
import { FORBIDDEN_ERROR_STATUS_CODE, SECONDARY_BUTTON, LABEL_BUTTON } from 'igz-controls/constants'
-import { useDetectOutsideClick } from 'igz-controls/hooks'
+import { deleteUnsafeHtml } from '../../utils'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
+import { useDetectOutsideClick } from 'igz-controls/hooks'
import { ReactComponent as SearchIcon } from 'igz-controls/images/search.svg'
@@ -106,20 +108,12 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => {
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'Missing edit permission for the project.'
- : 'Failed to edit project data.',
- retry:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? null
- : () => applyChanges(newOwnerId)
- })
- )
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Missing edit permission for the project'
+ : getErrorMsg(error, 'Failed to edit project data')
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges(newOwnerId))
})
.finally(handleOnClose)
}
@@ -156,13 +150,7 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => {
})
)
} catch (error) {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch users.'
- })
- )
+ showErrorNotification(dispatch, error, 'Failed to fetch users')
}
resolve()
diff --git a/src/elements/DeployModelPopUp/DeployModelPopUp.js b/src/elements/DeployModelPopUp/DeployModelPopUp.js
index 66c0d972d..25a017e9f 100644
--- a/src/elements/DeployModelPopUp/DeployModelPopUp.js
+++ b/src/elements/DeployModelPopUp/DeployModelPopUp.js
@@ -30,14 +30,15 @@ import { useLocation } from 'react-router-dom'
import Loader from '../../common/Loader/Loader'
import { Button, FormInput, FormKeyValueTable, FormSelect, Modal } from 'igz-controls/components'
-import { setNotification } from '../../reducers/notificationReducer'
-import { MODAL_SM, SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants'
import { FUNCTION_TYPE_SERVING, MODELS_TAB } from '../../constants'
+import { MODAL_SM, SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants'
+import { buildFunction, fetchArtifactsFunctions } from '../../reducers/artifactsReducer'
import { generateUri } from '../../utils/resources'
import { getValidationRules } from 'igz-controls/utils/validation.util'
import { setFieldState } from 'igz-controls/utils/form.util'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { useModalBlockHistory } from '../../hooks/useModalBlockHistory.hook'
-import { buildFunction, fetchArtifactsFunctions } from '../../reducers/artifactsReducer'
import { ReactComponent as QuestionMarkIcon } from 'igz-controls/images/question-mark.svg'
@@ -167,14 +168,9 @@ const DeployModelPopUp = ({ isOpen, model, onResolve }) => {
})
)
})
- .catch(() => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: 'Model deployment failed to initiate',
- retry: deployModel
- })
+ .catch(error => {
+ showErrorNotification(dispatch, error, '', 'Model deployment failed to initiate', () =>
+ deployModel(values)
)
})
.finally(() => {
diff --git a/src/elements/FeaturesTablePanel/FeaturesTablePanel.js b/src/elements/FeaturesTablePanel/FeaturesTablePanel.js
index 42f2942d8..13c008113 100644
--- a/src/elements/FeaturesTablePanel/FeaturesTablePanel.js
+++ b/src/elements/FeaturesTablePanel/FeaturesTablePanel.js
@@ -34,6 +34,7 @@ import {
updateGroupedFeatures
} from '../../reducers/tableReducer'
import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { showErrorNotification } from '../../utils/notifications.util'
const FeaturesTablePanel = ({
createNewFeatureVector,
@@ -108,20 +109,15 @@ const FeaturesTablePanel = ({
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message:
- tableStore.features.isNewFeatureVector &&
- error.response.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'You are not permitted to create new feature vector.'
- : tableStore.features.isNewFeatureVector
- ? 'Feature vector creation failed.'
- : 'Failed to add features',
- retry: addFeatures
- })
- )
+ const customErrorMsg =
+ tableStore.features.isNewFeatureVector &&
+ error.response.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'You are not permitted to create new feature vector'
+ : tableStore.features.isNewFeatureVector
+ ? 'Feature vector creation failed'
+ : 'Failed to add features'
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => addFeatures())
})
dispatch(setTablePanelOpen(false))
diff --git a/src/elements/MembersPopUp/MembersPopUp.js b/src/elements/MembersPopUp/MembersPopUp.js
index ed09f1eda..b6d28482c 100644
--- a/src/elements/MembersPopUp/MembersPopUp.js
+++ b/src/elements/MembersPopUp/MembersPopUp.js
@@ -29,17 +29,19 @@ import Input from '../../common/Input/Input'
import Select from '../../common/Select/Select'
import { Button, ConfirmDialog, RoundedIcon, Tip } from 'igz-controls/components'
-import projectsIguazioApi from '../../api/projects-iguazio-api'
-import { getRoleOptions, initialNewMembersRole } from './membersPopUp.util'
-import { membersActions } from './membersReducer'
import {
DANGER_BUTTON,
LABEL_BUTTON,
PRIMARY_BUTTON,
SECONDARY_BUTTON
} from 'igz-controls/constants'
+import projectsIguazioApi from '../../api/projects-iguazio-api'
import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
+import { getRoleOptions, initialNewMembersRole } from './membersPopUp.util'
import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible'
+import { membersActions } from './membersReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import { ReactComponent as Add } from 'igz-controls/images/add.svg'
import { ReactComponent as Close } from 'igz-controls/images/close.svg'
@@ -172,21 +174,14 @@ const MembersPopUp = ({
changeMembersCallback(response.data.data.id)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'Missing edit permission for the project.'
- : 'Failed to edit project data.',
- retry:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? null
- : () => applyMembersChanges(changesBody)
- })
- )
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Missing edit permission for the project'
+ : getErrorMsg(error, 'Failed to edit project data')
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => applyMembersChanges())
})
+
handleOnClose()
}
@@ -297,13 +292,7 @@ const MembersPopUp = ({
setNewMembersSuggestionList(suggestionList)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch users.'
- })
- )
+ showErrorNotification(dispatch, error, 'Failed to fetch users')
})
}, 400)
diff --git a/src/elements/ProjectSettingsGeneral/ProjectSettingsGeneral.js b/src/elements/ProjectSettingsGeneral/ProjectSettingsGeneral.js
index bb7835660..d44ed031f 100644
--- a/src/elements/ProjectSettingsGeneral/ProjectSettingsGeneral.js
+++ b/src/elements/ProjectSettingsGeneral/ProjectSettingsGeneral.js
@@ -25,14 +25,16 @@ import { useParams } from 'react-router-dom'
import ProjectSettingsGeneralView from './ProjectSettingsGeneralView'
-import { ARTIFACT_PATH, DATA, LABELS, PARAMS, SOURCE_URL } from '../../constants'
-import { setNotification } from '../../reducers/notificationReducer'
-import projectsApi from '../../api/projects-api'
import projectsAction from '../../actions/projects'
-import { initialEditProjectData } from './projectSettingsGeneral.utils'
-import { deleteUnsafeHtml } from '../../utils/string'
-import { KEY_CODES } from '../../constants'
+import projectsApi from '../../api/projects-api'
+import { ARTIFACT_PATH, DATA, LABELS, PARAMS, SOURCE_URL } from '../../constants'
import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { KEY_CODES } from '../../constants'
+import { deleteUnsafeHtml } from '../../utils/string'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
+import { initialEditProjectData } from './projectSettingsGeneral.utils'
+import { setNotification } from '../../reducers/notificationReducer'
+import { showErrorNotification } from '../../utils/notifications.util'
import './projectSettingsGeneral.scss'
@@ -87,19 +89,13 @@ const ProjectSettingsGeneral = ({
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? 'Missing edit permission for the project.'
- : 'Failed to edit project data.',
- retry:
- error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
- ? null
- : () => sendProjectSettingsData(type, data, labels)
- })
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Missing edit permission for the project'
+ : getErrorMsg(error, 'Failed to edit project data')
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () =>
+ sendProjectSettingsData(type, data, labels)
)
})
},
diff --git a/src/elements/ProjectSettingsSecrets/ProjectSettingsSecrets.js b/src/elements/ProjectSettingsSecrets/ProjectSettingsSecrets.js
index 7eb274470..ad4427897 100644
--- a/src/elements/ProjectSettingsSecrets/ProjectSettingsSecrets.js
+++ b/src/elements/ProjectSettingsSecrets/ProjectSettingsSecrets.js
@@ -21,15 +21,17 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { connect, useDispatch } from 'react-redux'
import { useParams } from 'react-router-dom'
-import projectApi from '../../api/projects-api'
-import projectsAction from '../../actions/projects'
-import ProjectSettingsSecretsView from './ProjectSettingsSecretsView'
-import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
import {
ADD_PROJECT_SECRET,
DELETE_PROJECT_SECRET,
EDIT_PROJECT_SECRET
} from './ProjectSettingsSecrets.utils'
+import ProjectSettingsSecretsView from './ProjectSettingsSecretsView'
+import projectApi from '../../api/projects-api'
+import projectsAction from '../../actions/projects'
+import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
+import { showErrorNotification } from '../../utils/notifications.util'
const ProjectSettingsSecrets = ({
fetchProjectSecrets,
@@ -45,27 +47,16 @@ const ProjectSettingsSecrets = ({
const fetchSecrets = useCallback(() => {
setIsUserAllowed(true)
fetchProjectSecrets(params.projectName).catch(error => {
- if (error.response?.status === FORBIDDEN_ERROR_STATUS_CODE) {
- setIsUserAllowed(false)
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Permission denied.'
- })
- )
- } else {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Failed to fetch project data.',
- retry: () => fetchSecrets()
- })
- )
- }
+ const customErrorMsg =
+ error.response?.status === FORBIDDEN_ERROR_STATUS_CODE
+ ? 'Permission denied'
+ : getErrorMsg(error, 'Failed to fetch project data')
+
+ showErrorNotification(dispatch, error, '', customErrorMsg, () => {
+ fetchSecrets()
+ })
})
- }, [dispatch, fetchProjectSecrets, params.projectName, setNotification])
+ }, [dispatch, fetchProjectSecrets, params.projectName])
useEffect(() => {
fetchSecrets()
@@ -109,14 +100,8 @@ const ProjectSettingsSecrets = ({
})
)
})
- .catch(err => {
- dispatch(
- setNotification({
- status: 400,
- id: Math.random(),
- message: err.message
- })
- )
+ .catch(error => {
+ showErrorNotification(dispatch, error, 'Failed to update secrets')
})
},
[dispatch, params.projectName, setNotification]
diff --git a/src/httpClient.js b/src/httpClient.js
index 7ab8b9e5c..5f8fd8b8e 100755
--- a/src/httpClient.js
+++ b/src/httpClient.js
@@ -19,10 +19,10 @@ such restriction.
*/
import axios from 'axios'
import qs from 'qs'
-import { ConfirmDialog } from 'igz-controls/components'
+import { ConfirmDialog } from 'igz-controls/components'
+import { CANCEL_REQUEST_TIMEOUT, LARGE_REQUEST_CANCELED } from './constants'
import { openPopUp } from 'igz-controls/utils/common.util'
-import { LARGE_REQUEST_CANCELED } from './constants'
const headers = {
'Cache-Control': 'no-cache'
@@ -53,20 +53,49 @@ export const iguazioHttpClient = axios.create({
headers
})
+const getAbortSignal = (controller, abortCallback, timeoutMs) => {
+ let timeoutId = null
+ const newController = new AbortController()
+ const abortController = controller || newController
+
+ if (timeoutMs) {
+ timeoutId = setTimeout(() => abortController.abort(LARGE_REQUEST_CANCELED), timeoutMs)
+ }
+
+ abortController.signal.onabort = event => {
+ if (timeoutId) {
+ clearTimeout(timeoutId)
+ }
+
+ if (abortCallback) {
+ abortCallback(event)
+ }
+ }
+
+ return [abortController.signal, timeoutId]
+}
+
let requestId = 1
let requestTimeouts = {}
+let largeResponsePopUpIsOpen = false
// Request interceptor
mainHttpClient.interceptors.request.use(
config => {
if (config?.ui?.setLargeRequestErrorMessage) {
- const cancelTokenSource = axios.CancelToken.source()
-
- config.cancelToken = cancelTokenSource.token
- requestTimeouts[requestId] = setTimeout(() => {
- showLargeResponsePopUp(config.ui.setLargeRequestErrorMessage)
- cancelTokenSource.cancel(LARGE_REQUEST_CANCELED)
- }, 30000)
+ const [signal, timeoutId] = getAbortSignal(
+ config.ui?.controller,
+ abortEvent => {
+ if (abortEvent.target.reason === LARGE_REQUEST_CANCELED) {
+ showLargeResponsePopUp(config.ui.setLargeRequestErrorMessage)
+ }
+ },
+ CANCEL_REQUEST_TIMEOUT
+ )
+
+ config.signal = signal
+
+ requestTimeouts[requestId] = timeoutId
config.ui.requestId = requestId
requestId++
}
@@ -99,15 +128,29 @@ mainHttpClient.interceptors.response.use(
return response
},
- error => Promise.reject(error)
+ error => {
+ if (error.config?.ui?.requestId) {
+ clearTimeout(requestTimeouts[error.config.ui.requestId])
+ delete requestTimeouts[error.config.ui.requestId]
+ }
+
+ return Promise.reject(error)
+ }
)
export const showLargeResponsePopUp = setLargeRequestErrorMessage => {
- const errorMessage =
- 'The query result is too large to display. Add a filter (or narrow it) to retrieve fewer results.'
+ if (!largeResponsePopUpIsOpen) {
+ const errorMessage =
+ 'The query result is too large to display. Add a filter (or narrow it) to retrieve fewer results.'
- setLargeRequestErrorMessage(errorMessage)
- openPopUp(ConfirmDialog, {
- message: errorMessage
- })
+ setLargeRequestErrorMessage(errorMessage)
+ largeResponsePopUpIsOpen = true
+
+ openPopUp(ConfirmDialog, {
+ message: errorMessage,
+ closePopUp: () => {
+ largeResponsePopUpIsOpen = false
+ }
+ })
+ }
}
diff --git a/src/reducers/artifactsReducer.js b/src/reducers/artifactsReducer.js
index 28635e27f..b144b057f 100644
--- a/src/reducers/artifactsReducer.js
+++ b/src/reducers/artifactsReducer.js
@@ -18,7 +18,6 @@ under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
-import artifactsApi from '../api/artifacts-api'
import {
defaultFulfilledHandler,
defaultPendingHandler,
@@ -26,14 +25,16 @@ import {
hideLoading,
showLoading
} from './redux.util'
+import artifactsApi from '../api/artifacts-api'
+import functionsApi from '../api/functions-api'
+import { ARTIFACTS, DATASETS, FUNCTION_TYPE_SERVING, MODELS_TAB } from '../constants'
import { filterArtifacts } from '../utils/filterArtifacts'
-import { parseArtifacts } from '../utils/parseArtifacts'
import { generateArtifacts } from '../utils/generateArtifacts'
-import { ARTIFACTS, DATASETS, FUNCTION_TYPE_SERVING, MODELS_TAB } from '../constants'
+import { generateModelEndpoints } from '../utils/generateModelEndpoints'
import { getArtifactIdentifier } from '../utils/getUniqueIdentifier'
-import functionsApi from '../api/functions-api'
+import { parseArtifacts } from '../utils/parseArtifacts'
import { parseFunctions } from '../utils/parseFunctions'
-import { generateModelEndpoints } from '../utils/generateModelEndpoints'
+import { largeResponseCatchHandler } from '../utils/largeResponseCatchHandler'
const initialState = {
artifacts: [],
@@ -114,14 +115,21 @@ export const fetchDataSet = createAsyncThunk('fetchDataSet', ({ project, dataSet
})
export const fetchDataSets = createAsyncThunk(
'fetchDataSets',
- ({ project, filters, config, setLargeRequestErrorMessage }) => {
+ ({ project, filters, config }, thunkAPI) => {
+ setTimeout(() => {
+ thunkAPI.dispatch(fetchDataSets.pending())
+ })
+
return artifactsApi
- .getDataSets(project, filters, config, setLargeRequestErrorMessage)
+ .getDataSets(project, filters, config)
.then(({ data }) => {
const result = parseArtifacts(data.artifacts)
return generateArtifacts(filterArtifacts(result), DATASETS, data.artifacts)
})
+ .catch(error =>
+ largeResponseCatchHandler(error, 'Failed to fetch datasets', thunkAPI.dispatch)
+ )
}
)
export const fetchFile = createAsyncThunk('fetchFile', ({ project, file, iter, tag }) => {
@@ -133,36 +141,44 @@ export const fetchFile = createAsyncThunk('fetchFile', ({ project, file, iter, t
})
export const fetchFiles = createAsyncThunk(
'fetchFiles',
- ({ project, filters, setLargeRequestErrorMessage }) => {
- return artifactsApi.getFiles(project, filters, setLargeRequestErrorMessage).then(({ data }) => {
- const result = parseArtifacts(data.artifacts)
-
- return generateArtifacts(filterArtifacts(result), ARTIFACTS, data.artifacts)
+ ({ project, filters, config }, thunkAPI) => {
+ setTimeout(() => {
+ thunkAPI.dispatch(fetchFiles.pending())
})
+
+ return artifactsApi
+ .getFiles(project, filters, config)
+ .then(({ data }) => {
+ const result = parseArtifacts(data.artifacts)
+
+ return generateArtifacts(filterArtifacts(result), ARTIFACTS, data.artifacts)
+ })
+ .catch(error =>
+ largeResponseCatchHandler(error, 'Failed to fetch artifacts', thunkAPI.dispatch)
+ )
}
)
export const fetchArtifactsFunctions = createAsyncThunk(
'fetchArtifactsFunctions',
- ({ project, filters, setLargeRequestErrorMessage }) => {
- return functionsApi
- .getFunctions(project, filters, null, setLargeRequestErrorMessage)
- .then(({ data }) => {
- return parseFunctions(
- data.funcs.filter(
- func => func.kind === FUNCTION_TYPE_SERVING && func.metadata.tag?.length
- )
- )
- })
+ ({ project, filters, config }) => {
+ return functionsApi.getFunctions(project, filters, config, null).then(({ data }) => {
+ return parseFunctions(
+ data.funcs.filter(func => func.kind === FUNCTION_TYPE_SERVING && func.metadata.tag?.length)
+ )
+ })
}
)
export const fetchModelEndpoints = createAsyncThunk(
'fetchModelEndpoints',
- ({ project, filters, params, setLargeRequestErrorMessage }) => {
+ ({ project, filters, config, params }, thunkAPI) => {
return artifactsApi
- .getModelEndpoints(project, filters, params, setLargeRequestErrorMessage)
+ .getModelEndpoints(project, filters, config, params)
.then(({ data: { endpoints = [] } }) => {
return generateModelEndpoints(endpoints)
})
+ .catch(error =>
+ largeResponseCatchHandler(error, 'Failed to fetch model endpoints', thunkAPI.dispatch)
+ )
}
)
export const fetchModel = createAsyncThunk('fetchModel', ({ project, model, iter, tag }) => {
@@ -174,14 +190,21 @@ export const fetchModel = createAsyncThunk('fetchModel', ({ project, model, iter
})
export const fetchModels = createAsyncThunk(
'fetchModels',
- ({ project, filters, setLargeRequestErrorMessage }) => {
+ ({ project, filters, config }, thunkAPI) => {
+ setTimeout(() => {
+ thunkAPI.dispatch(fetchModels.pending())
+ })
+
return artifactsApi
- .getModels(project, filters, setLargeRequestErrorMessage)
+ .getModels(project, filters, config)
.then(({ data }) => {
const result = filterArtifacts(parseArtifacts(data.artifacts))
return generateArtifacts(result, MODELS_TAB, data.artifacts)
})
+ .catch(error => {
+ largeResponseCatchHandler(error, 'Failed to fetch models', thunkAPI.dispatch)
+ })
}
)
export const replaceTag = createAsyncThunk('replaceTag', ({ project, tag, data }) => {
diff --git a/src/reducers/redux.util.js b/src/reducers/redux.util.js
index 2209779eb..044f242b5 100644
--- a/src/reducers/redux.util.js
+++ b/src/reducers/redux.util.js
@@ -31,6 +31,6 @@ export const defaultFulfilledHandler = state => {
state.loading = false
}
export const defaultRejectedHandler = (state, action) => {
- state.error = action.error
state.loading = false
+ state.error = action.error
}
diff --git a/src/utils/artifacts.util.js b/src/utils/artifacts.util.js
index eb5101738..7255e1c8c 100644
--- a/src/utils/artifacts.util.js
+++ b/src/utils/artifacts.util.js
@@ -19,13 +19,14 @@ such restriction.
*/
import { cloneDeep } from 'lodash'
-import { deleteTag, editTag, addTag } from '../reducers/artifactsReducer'
-import { TAG_FILTER_ALL_ITEMS, TAG_FILTER_LATEST } from '../constants'
import artifactApi from '../api/artifacts-api'
import { ARTIFACT_TYPE, DATASET_TYPE, MODEL_TYPE } from '../constants'
+import { TAG_FILTER_ALL_ITEMS, TAG_FILTER_LATEST } from '../constants'
+import { deleteTag, editTag, addTag } from '../reducers/artifactsReducer'
import { getArtifactIdentifier } from './getUniqueIdentifier'
import { parseArtifacts } from './parseArtifacts'
import { setFilters, setModalFiltersValues } from '../reducers/filtersReducer'
+import { showErrorNotification } from './notifications.util'
export const applyTagChanges = (changes, artifactItem, projectName, dispatch, setNotification) => {
let updateTagPromise = Promise.resolve()
@@ -78,14 +79,8 @@ export const applyTagChanges = (changes, artifactItem, projectName, dispatch, se
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- message: 'Failed to update the tag',
- retry: () =>
- applyTagChanges(changes, artifactItem, projectName, dispatch, setNotification)
- })
+ showErrorNotification(dispatch, error, '', 'Failed to update the tag', () =>
+ applyTagChanges(changes, artifactItem, projectName, dispatch, setNotification)
)
})
} else {
diff --git a/src/utils/copyToClipboard.js b/src/utils/copyToClipboard.js
index eb176b5e7..d165e60cc 100644
--- a/src/utils/copyToClipboard.js
+++ b/src/utils/copyToClipboard.js
@@ -18,6 +18,7 @@ under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import { setNotification } from '../reducers/notificationReducer'
+import { showErrorNotification } from './notifications.util'
export const copyToClipboard = (textToCopy, dispatch) => {
navigator.clipboard
@@ -31,14 +32,7 @@ export const copyToClipboard = (textToCopy, dispatch) => {
})
)
})
- .catch(err => {
- dispatch(
- setNotification({
- error: err,
- status: 400,
- id: Math.random(),
- message: 'Copy to clipboard failed'
- })
- )
+ .catch(error => {
+ showErrorNotification(dispatch, error, '', 'Copy to clipboard failed')
})
}
diff --git a/src/utils/getArtifactPreview.js b/src/utils/getArtifactPreview.js
index ce24de705..777c9d6c6 100644
--- a/src/utils/getArtifactPreview.js
+++ b/src/utils/getArtifactPreview.js
@@ -62,7 +62,7 @@ export const fetchArtifactPreviewFromExtraData = (
noData,
setNoData,
setPreview,
- cancelToken
+ signal
) => {
artifact.extra_data.forEach(previewItem => {
fetchArtifactPreview(
@@ -71,7 +71,7 @@ export const fetchArtifactPreviewFromExtraData = (
previewItem.path.startsWith('/User') && (artifact.ui.user || artifact.producer.owner),
previewItem.path.replace(/.*\./g, ''),
artifact.db_key,
- cancelToken
+ signal
)
.then(content => {
setPreview({ ...content, header: previewItem.header })
@@ -180,9 +180,9 @@ export const fetchArtifactPreview = (
user,
fileFormat,
artifactName,
- cancelToken
+ signal
) => {
- return api.getArtifactPreview(projectName, path, user, fileFormat, cancelToken).then(res => {
+ return api.getArtifactPreview(projectName, path, user, fileFormat, signal).then(res => {
return createArtifactPreviewContent(res, fileFormat, path, artifactName)
})
}
diff --git a/src/utils/handleDeleteArtifact.js b/src/utils/handleDeleteArtifact.js
index 80d70ceb4..0ce706274 100644
--- a/src/utils/handleDeleteArtifact.js
+++ b/src/utils/handleDeleteArtifact.js
@@ -19,6 +19,7 @@ such restriction.
*/
import { deleteArtifact } from '../reducers/artifactsReducer'
import { setNotification } from '../reducers/notificationReducer'
+import { showErrorNotification } from './notifications.util'
export const handleDeleteArtifact = (
dispatch,
@@ -43,23 +44,17 @@ export const handleDeleteArtifact = (
)
})
.catch(error => {
- dispatch(
- setNotification({
- status: error.response?.status || 400,
- id: Math.random(),
- retry: () =>
- handleDeleteArtifact(
- dispatch,
- project,
- key,
- tag,
- uid,
- refreshArtifacts,
- filters,
- artifactType
- ),
- message: error.response?.data?.detail || `Deleting ${artifactType} failed`
- })
+ showErrorNotification(dispatch, error, `Deleting ${artifactType} failed`, '', () =>
+ handleDeleteArtifact(
+ dispatch,
+ project,
+ key,
+ tag,
+ uid,
+ refreshArtifacts,
+ filters,
+ artifactType
+ )
)
})
}
diff --git a/src/utils/largeResponseCatchHandler.js b/src/utils/largeResponseCatchHandler.js
index ce37d957c..9896b52b4 100644
--- a/src/utils/largeResponseCatchHandler.js
+++ b/src/utils/largeResponseCatchHandler.js
@@ -17,10 +17,15 @@ 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 { LARGE_REQUEST_CANCELED } from '../constants'
+import { DEFAULT_ABORT_MSG, LARGE_REQUEST_CANCELED, REQUEST_CANCELED } from '../constants'
+import { showErrorNotification } from './notifications.util'
-export const largeResponseCatchHandler = error => {
- if (error.message !== LARGE_REQUEST_CANCELED) {
- throw error
+export const largeResponseCatchHandler = (error, defaultError, dispatch) => {
+ if (
+ ![LARGE_REQUEST_CANCELED, REQUEST_CANCELED, DEFAULT_ABORT_MSG].includes(error.message) &&
+ error &&
+ dispatch
+ ) {
+ showErrorNotification(dispatch, error, defaultError)
}
}
diff --git a/src/utils/cancelRequest.js b/src/utils/notifications.util.js
similarity index 61%
rename from src/utils/cancelRequest.js
rename to src/utils/notifications.util.js
index 506856009..4966d78bf 100644
--- a/src/utils/cancelRequest.js
+++ b/src/utils/notifications.util.js
@@ -17,6 +17,21 @@ 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 cancelRequest = (ref, message) => {
- ref.current?.cancel && ref.current.cancel(message)
+
+import { setNotification } from '../reducers/notificationReducer'
+import { getErrorMsg } from 'igz-controls/utils/common.util'
+
+export const showErrorNotification = (dispatch, error, defaultErrorMsg, customErrorMsg, retryCallback) => {
+ const notificationData = {
+ status: error?.response?.status || 400,
+ id: Math.random(),
+ message: customErrorMsg || getErrorMsg(error, defaultErrorMsg),
+ error
+ }
+
+ if (retryCallback) {
+ notificationData.retry = retryCallback
+ }
+
+ dispatch(setNotification(notificationData))
}
diff --git a/src/utils/rerunJob.js b/src/utils/rerunJob.js
deleted file mode 100644
index 97e90d06c..000000000
--- a/src/utils/rerunJob.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-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.
-*/
-
From 7fa605fbe2c6f433c8b6efd1581c83b6cd38e693 Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:07:52 +0200
Subject: [PATCH 4/7] Fix [Models] Model deploy in the UI does not impact
(#2095)
---
src/elements/DeployModelPopUp/DeployModelPopUp.js | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/elements/DeployModelPopUp/DeployModelPopUp.js b/src/elements/DeployModelPopUp/DeployModelPopUp.js
index 25a017e9f..f26cf4fe2 100644
--- a/src/elements/DeployModelPopUp/DeployModelPopUp.js
+++ b/src/elements/DeployModelPopUp/DeployModelPopUp.js
@@ -64,7 +64,7 @@ const DeployModelPopUp = ({ isOpen, model, onResolve }) => {
})
)
const location = useLocation()
- const { handleCloseModal } = useModalBlockHistory(onResolve, formRef.current)
+ const { handleCloseModal, resolveModal } = useModalBlockHistory(onResolve, formRef.current)
const getTagOptions = useCallback((functionList, selectedFunctionName) => {
return chain(functionList)
@@ -145,9 +145,9 @@ const DeployModelPopUp = ({ isOpen, model, onResolve }) => {
func => func.name === values.selectedFunctionName && func.tag === values.selectedTag
)
const classArguments = mapValues(keyBy(values.arguments, 'key'), 'value')
- const servingFunctionCopy = cloneDeep(servingFunction)
+ const servingFunctionCopy = cloneDeep(servingFunction.ui.originalContent)
- servingFunctionCopy.graph.routes[values.modelName] = {
+ servingFunctionCopy.spec.graph.routes[values.modelName] = {
class_args: {
model_path: generateUri(model, MODELS_TAB),
...classArguments
@@ -159,7 +159,7 @@ const DeployModelPopUp = ({ isOpen, model, onResolve }) => {
return dispatch(buildFunction({ funcData: { function: servingFunctionCopy } }))
.unwrap()
.then(response => {
- formRef.current = null
+ resolveModal()
dispatch(
setNotification({
status: response.status,
@@ -172,9 +172,7 @@ const DeployModelPopUp = ({ isOpen, model, onResolve }) => {
showErrorNotification(dispatch, error, '', 'Model deployment failed to initiate', () =>
deployModel(values)
)
- })
- .finally(() => {
- onResolve()
+ resolveModal()
})
}
From 9e932fee810d0c2550dbae13051dc5fa2bc38467 Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:08:14 +0200
Subject: [PATCH 5/7] Fix [Functions Panel] function create - deploy button
(#2100)
---
src/components/FunctionsPanel/FunctionsPanelView.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/components/FunctionsPanel/FunctionsPanelView.js b/src/components/FunctionsPanel/FunctionsPanelView.js
index 2603d4d6e..c2035249e 100644
--- a/src/components/FunctionsPanel/FunctionsPanelView.js
+++ b/src/components/FunctionsPanel/FunctionsPanelView.js
@@ -34,8 +34,7 @@ import { Button, ConfirmDialog } from 'igz-controls/components'
import { FUNCTION_PANEL_MODE } from '../../types'
import { runtimeSections } from './functionsPanel.util'
-
-import { PANEL_DEFAULT_ACCESS_KEY } from '../../constants'
+import { JOB_KIND_JOB, PANEL_DEFAULT_ACCESS_KEY } from '../../constants'
import { LABEL_BUTTON, SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants'
import { ReactComponent as Arrow } from 'igz-controls/images/arrow.svg'
@@ -185,7 +184,7 @@ const FunctionsPanelView = ({
/>
handleSave(true)}
disabled={!checkValidation()}
/>
From 190ad2e7ae2db9f014858444dbee48859c871faf Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:08:52 +0200
Subject: [PATCH 6/7] Fix [Jobs] UI crash on 'pathname' (#2104)
---
src/components/DetailsInputs/DetailsInputs.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/components/DetailsInputs/DetailsInputs.js b/src/components/DetailsInputs/DetailsInputs.js
index 818d756e0..0c8bcf13a 100644
--- a/src/components/DetailsInputs/DetailsInputs.js
+++ b/src/components/DetailsInputs/DetailsInputs.js
@@ -94,7 +94,8 @@ const DetailsInputs = ({ inputs }) => {
ui: {
inputName: key,
inputPath: value,
- isPreviewable: false
+ isPreviewable: false,
+ artifactLink: ''
}
}
])
@@ -107,7 +108,8 @@ const DetailsInputs = ({ inputs }) => {
ui: {
inputName: key,
inputPath: value,
- isPreviewable: false
+ isPreviewable: false,
+ artifactLink: ''
}
}
])
From 96e2e585867c21267dd8661e901bb39decb2a9e1 Mon Sep 17 00:00:00 2001
From: Ilank <63646693+ilan7empest@users.noreply.github.com>
Date: Mon, 4 Dec 2023 16:09:13 +0700
Subject: [PATCH 7/7] Fix [UI] add data-testid for elements in batch run wizard
(#2105)
---
src/common/Input/Input.js | 9 ++++++---
src/common/Search/Search.js | 9 +++++++--
.../FilterMenuModal/FilterMenuModal.js | 3 +++
src/components/JobWizard/JobWizard.js | 2 ++
.../JobWizardFunctionSelection.js | 7 ++++++-
.../JobWizardParameters.js | 2 +-
.../JobWizardRunDetails.js | 11 ++++++++--
.../FormDataInputsTable.js | 2 +-
.../FormEnvironmentVariablesTable.js | 4 ++--
.../FormParametersRow/FormParametersRow.js | 20 +++++++++----------
.../FormParametersTable.js | 2 +-
.../FormResourcesUnits/FormResourcesUnits.js | 2 +-
.../FormVolumesTable/FormVolumesTable.js | 2 +-
13 files changed, 50 insertions(+), 25 deletions(-)
diff --git a/src/common/Input/Input.js b/src/common/Input/Input.js
index aad8d34da..a62866dc5 100644
--- a/src/common/Input/Input.js
+++ b/src/common/Input/Input.js
@@ -45,6 +45,7 @@ const Input = React.forwardRef(
focused,
iconClass,
iconOnClick,
+ id,
infoLabel,
inputIcon,
invalid,
@@ -221,7 +222,7 @@ const Input = React.forwardRef(
return (
{},
+ iconOnClick: () => { },
+ id: '',
infoLabel: false,
inputIcon: null,
invalid: false,
@@ -368,6 +370,7 @@ Input.propTypes = {
focused: PropTypes.bool,
iconClass: PropTypes.string,
iconOnClick: PropTypes.func,
+ id: PropTypes.string,
infoLabel: PropTypes.bool,
inputIcon: PropTypes.element,
invalid: PropTypes.bool,
diff --git a/src/common/Search/Search.js b/src/common/Search/Search.js
index d31ac132e..3652845d8 100644
--- a/src/common/Search/Search.js
+++ b/src/common/Search/Search.js
@@ -31,6 +31,7 @@ import './search.scss'
const Search = ({
className,
+ id,
matches,
onChange,
onFocus,
@@ -119,6 +120,7 @@ const Search = ({
>
- match ? `${match} ` : match
+ labelHtml: item.replace(
+ new RegExp(searchValue.toLocaleLowerCase(), 'gi'),
+ match => (match ? `${match} ` : match)
)
}}
name={item}
@@ -177,6 +180,7 @@ const Search = ({
Search.defaultProps = {
className: '',
+ id:'search',
matches: [],
onFocus: () => {},
placeholder: '',
@@ -187,6 +191,7 @@ Search.defaultProps = {
Search.propTypes = {
className: PropTypes.string,
+ id: PropTypes.string,
matches: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func,
diff --git a/src/components/FilterMenuModal/FilterMenuModal.js b/src/components/FilterMenuModal/FilterMenuModal.js
index 780d2938e..d70c368a6 100644
--- a/src/components/FilterMenuModal/FilterMenuModal.js
+++ b/src/components/FilterMenuModal/FilterMenuModal.js
@@ -155,6 +155,7 @@ const FilterMenuModal = ({
return (