From 48a5913d5ad3761cac67c1ec67e5fa6802be5b66 Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Sun, 26 Mar 2023 14:07:13 +0300
Subject: [PATCH 01/11] Fix [Target Path] UI should get "store://datasets/"
format as an input to the function (#1681)
---
src/common/TargetPath/TargetPath.js | 22 +++++++--
src/common/TargetPath/targetPath.util.js | 46 ++++++++++---------
.../FeatureSetsPanelDataSource.js | 20 +++++++-
.../featureSetsPanelDataSource.util.js | 17 +++----
.../FeatureSetsPanelDataSourceTable.js | 19 --------
src/reducers/artifactsReducer.js | 8 ++--
src/utils/panelPathScheme.js | 23 ++++++----
7 files changed, 86 insertions(+), 69 deletions(-)
delete mode 100644 src/components/FeatureSetsPanel/FeatureSetsPanelDataSourceTable/FeatureSetsPanelDataSourceTable.js
diff --git a/src/common/TargetPath/TargetPath.js b/src/common/TargetPath/TargetPath.js
index dc59a80ba..cce71de7d 100644
--- a/src/common/TargetPath/TargetPath.js
+++ b/src/common/TargetPath/TargetPath.js
@@ -40,7 +40,7 @@ import {
} from './targetPath.util'
import featureStoreActions from '../../actions/featureStore'
import projectAction from '../../actions/projects'
-import { MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../constants'
+import { ARTIFACT_OTHER_TYPE, DATASET_TYPE, MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../constants'
import { fetchArtifact, fetchArtifacts } from '../../reducers/artifactsReducer'
import { getFeatureReference } from '../../utils/resources'
@@ -141,8 +141,22 @@ const TargetPath = ({
dataInputState.storePathType &&
dataInputState.project
) {
- if (dataInputState.storePathType === 'artifacts' && dataInputState.artifacts.length === 0) {
- dispatch(fetchArtifacts({ project: dataInputState.project }))
+ if (
+ dataInputState.storePathType !== 'feature-vectors' &&
+ dataInputState.artifacts.length === 0
+ ) {
+ dispatch(
+ fetchArtifacts({
+ project: dataInputState.project,
+ filters: null,
+ config: {
+ params: {
+ category:
+ dataInputState.storePathType === 'artifacts' ? ARTIFACT_OTHER_TYPE : DATASET_TYPE
+ }
+ }
+ })
+ )
.unwrap()
.then(artifacts => {
setDataInputState(prev => ({
@@ -189,7 +203,7 @@ const TargetPath = ({
const projectItem = dataInputState.projectItem
if (dataInputState.inputProjectItemPathEntered && storePathType && projectName && projectItem) {
- if (storePathType === 'artifacts' && dataInputState.artifactsReferences.length === 0) {
+ if (storePathType !== 'feature-vectors' && dataInputState.artifactsReferences.length === 0) {
dispatch(fetchArtifact({ project: projectName, artifact: projectItem }))
.unwrap()
.then(artifacts => {
diff --git a/src/common/TargetPath/targetPath.util.js b/src/common/TargetPath/targetPath.util.js
index 2a9bfaf8b..121f7f633 100644
--- a/src/common/TargetPath/targetPath.util.js
+++ b/src/common/TargetPath/targetPath.util.js
@@ -26,7 +26,7 @@ import {
S3_INPUT_PATH_SCHEME,
V3IO_INPUT_PATH_SCHEME
} from '../../constants'
-import { isNil } from 'lodash'
+import { isNil, uniqBy } from 'lodash'
import { getArtifactReference, getParsedResource } from '../../utils/resources'
export const pathPlaceholders = {
@@ -54,7 +54,12 @@ export const targetPathInitialState = {
}
export const pathTips = storePathType => {
- const pathType = storePathType === 'feature-vectors' ? 'feature-vector' : 'artifact'
+ const pathType =
+ storePathType === 'feature-vectors'
+ ? 'feature-vector'
+ : storePathType === 'artifacts'
+ ? 'artifact'
+ : 'dataset'
return {
[MLRUN_STORAGE_INPUT_PATH_SCHEME]: `${pathType}s/my-project/my-${pathType}:my-tag" or "${pathType}s/my-project/my-${pathType}@my-uid`,
@@ -70,6 +75,10 @@ export const storePathTypes = [
label: 'Artifacts',
id: 'artifacts'
},
+ {
+ label: 'Datasets',
+ id: 'datasets'
+ },
{
label: 'Feature vectors',
id: 'feature-vectors'
@@ -80,11 +89,11 @@ export const handleStoreInputPathChange = (targetPathState, setTargetPathState,
const pathItems = value.split('/')
const [projectItem, projectItemReference] = getParsedResource(pathItems[2])
const projectItems =
- targetPathState[pathItems[0] === 'artifacts' ? 'artifacts' : 'featureVectors']
+ targetPathState[pathItems[0] !== 'feature-vectors' ? 'artifacts' : 'featureVectors']
const projectItemIsEntered = projectItems.find(project => project.id === projectItem)
const projectItemsReferences =
targetPathState[
- pathItems[0] === 'artifacts' ? 'artifactsReferences' : 'featureVectorsReferences'
+ pathItems[0] !== 'feature-vectors' ? 'artifactsReferences' : 'featureVectorsReferences'
]
const projectItemReferenceIsEntered = projectItemsReferences.find(
projectItemRef => projectItemRef.id === projectItemReference
@@ -206,23 +215,12 @@ export const generateComboboxMatchesList = (
} else if (!inputProjectPathEntered && storePathTypes.some(type => type.id === storePathType)) {
return projects.filter(proj => proj.id.startsWith(project))
} else if (!inputProjectItemPathEntered) {
- const selectedStorePathType = storePathType
- const projectItems =
- selectedStorePathType === 'artifacts'
- ? artifacts
- : selectedStorePathType === 'feature-vectors'
- ? featureVectors
- : null
+ const projectItems = storePathType === 'feature-vectors' ? featureVectors : artifacts
return projectItems ? projectItems.filter(projItem => projItem.id.startsWith(projectItem)) : []
} else if (!inputProjectItemReferencePathEntered) {
- const selectedStorePathType = storePathType
const projectItemsReferences =
- selectedStorePathType === 'artifacts'
- ? artifactsReferences
- : selectedStorePathType === 'feature-vectors'
- ? featureVectorsReferences
- : null
+ storePathType === 'feature-vectors' ? featureVectorsReferences : artifactsReferences
return projectItemsReferences
? projectItemsReferences.filter(projectItem =>
@@ -234,8 +232,8 @@ export const generateComboboxMatchesList = (
}
}
-export const generateArtifactsList = artifacts =>
- artifacts
+export const generateArtifactsList = artifacts => {
+ const generatedArtifacts = artifacts
.map(artifact => {
const key = artifact.link_iteration ? artifact.link_iteration.db_key : artifact.key ?? ''
return {
@@ -246,8 +244,11 @@ export const generateArtifactsList = artifacts =>
.filter(artifact => artifact.label !== '')
.sort((prevArtifact, nextArtifact) => prevArtifact.id.localeCompare(nextArtifact.id))
-export const generateArtifactsReferencesList = artifacts =>
- artifacts
+ return uniqBy(generatedArtifacts, 'id')
+}
+
+export const generateArtifactsReferencesList = artifacts => {
+ const generatedArtifacts = artifacts
.map(artifact => {
const artifactReference = getArtifactReference(artifact)
@@ -268,3 +269,6 @@ export const generateArtifactsReferencesList = artifacts =>
return prevRefTree.localeCompare(nextRefTree)
}
})
+
+ return uniqBy(generatedArtifacts, 'id')
+}
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSource.js b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSource.js
index c473253e1..1a2aac80b 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSource.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSource.js
@@ -25,7 +25,11 @@ import PropTypes from 'prop-types'
import FeatureSetsPanelDataSourceView from './FeatureSetsPanelDataSourceView'
import featureStoreActions from '../../../actions/featureStore'
-import { MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../../constants'
+import {
+ ARTIFACT_OTHER_TYPE,
+ DATASET_TYPE,
+ MLRUN_STORAGE_INPUT_PATH_SCHEME
+} from '../../../constants'
import { getParsedResource } from '../../../utils/resources'
import {
CSV,
@@ -95,7 +99,18 @@ const FeatureSetsPanelDataSource = ({
useEffect(() => {
if (urlProjectItemTypeEntered && urlProjectPathEntered && artifacts.length === 0) {
- dispatch(fetchArtifacts({ project: data.url.project }))
+ dispatch(
+ fetchArtifacts({
+ project: data.url.project,
+ filters: null,
+ config: {
+ params: {
+ category:
+ data.url.projectItemType === 'artifacts' ? ARTIFACT_OTHER_TYPE : DATASET_TYPE
+ }
+ }
+ })
+ )
.unwrap()
.then(artifacts => {
if (artifacts?.length > 0) {
@@ -106,6 +121,7 @@ const FeatureSetsPanelDataSource = ({
}, [
artifacts.length,
data.url.project,
+ data.url.projectItemType,
dispatch,
urlProjectItemTypeEntered,
urlProjectPathEntered
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
index c55cff1e6..abfe478fb 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
@@ -76,9 +76,7 @@ export const generateComboboxMatchesList = (
urlProjectItemTypeEntered
) => {
if (!urlProjectItemTypeEntered) {
- return projectItemsPathTypes.some(type =>
- type.id.startsWith(url.projectItemType)
- )
+ return projectItemsPathTypes.some(type => type.id.startsWith(url.projectItemType))
? projectItemsPathTypes
: []
} else if (
@@ -105,20 +103,19 @@ export const projectItemsPathTypes = [
{
label: 'Artifacts',
id: 'artifacts'
+ },
+ {
+ label: 'Datasets',
+ id: 'datasets'
}
]
-export const isUrlInputValid = (
- pathInputType,
- pathInputValue,
- dataSourceKind
-) => {
+export const isUrlInputValid = (pathInputType, pathInputValue, dataSourceKind) => {
const regExp =
dataSourceKind === CSV
? /^artifacts\/(.+?)\/(.+?)(#(.+?))?(:(.+?))?(@(.+))?(? 0 && /.*?\/(.*?)/.test(pathInputValue)
+ const defaultValidation = pathInputValue.length > 0 && /.*?\/(.*?)/.test(pathInputValue)
switch (pathInputType) {
case MLRUN_STORAGE_INPUT_PATH_SCHEME:
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSourceTable/FeatureSetsPanelDataSourceTable.js b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSourceTable/FeatureSetsPanelDataSourceTable.js
deleted file mode 100644
index 6cd08cc01..000000000
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSourceTable/FeatureSetsPanelDataSourceTable.js
+++ /dev/null
@@ -1,19 +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.
-*/
diff --git a/src/reducers/artifactsReducer.js b/src/reducers/artifactsReducer.js
index e53d44583..3c0033b0a 100644
--- a/src/reducers/artifactsReducer.js
+++ b/src/reducers/artifactsReducer.js
@@ -86,9 +86,11 @@ export const fetchArtifact = createAsyncThunk('fetchArtifact', ({ project, artif
return filterArtifacts(data.artifacts)
})
})
-export const fetchArtifacts = createAsyncThunk('fetchArtifacts', ({ project, filters }) => {
- return artifactsApi.getArtifacts(project, filters).then(({ data }) => {
- return filterArtifacts(data.artifacts)
+export const fetchArtifacts = createAsyncThunk('fetchArtifacts', ({ project, filters, config }) => {
+ return artifactsApi.getArtifacts(project, filters, config).then(({ data }) => {
+ const result = parseArtifacts(data.artifacts)
+
+ return generateArtifacts(filterArtifacts(result))
})
})
export const fetchArtifactTags = createAsyncThunk('fetchArtifactTags', ({ project, category }) => {
diff --git a/src/utils/panelPathScheme.js b/src/utils/panelPathScheme.js
index 4c0a43b8c..140a16b29 100644
--- a/src/utils/panelPathScheme.js
+++ b/src/utils/panelPathScheme.js
@@ -25,6 +25,7 @@ import {
S3_INPUT_PATH_SCHEME,
V3IO_INPUT_PATH_SCHEME
} from '../constants'
+import { uniqBy } from 'lodash'
export const generateProjectsList = (projectsList, currentProject) =>
projectsList
@@ -40,24 +41,23 @@ export const generateProjectsList = (projectsList, currentProject) =>
: prevProject.id.localeCompare(nextProject.id)
})
-export const generateArtifactsList = artifacts =>
- artifacts
+export const generateArtifactsList = artifacts => {
+ const generatedArtifacts = artifacts
.map(artifact => {
- const key = artifact.link_iteration
- ? artifact.link_iteration.db_key
- : artifact.key ?? ''
+ const key = artifact.link_iteration ? artifact.link_iteration.db_key : artifact.key ?? ''
return {
label: key,
id: key
}
})
.filter(artifact => artifact.label !== '')
- .sort((prevArtifact, nextArtifact) =>
- prevArtifact.id.localeCompare(nextArtifact.id)
- )
+ .sort((prevArtifact, nextArtifact) => prevArtifact.id.localeCompare(nextArtifact.id))
-export const generateArtifactsReferencesList = artifacts =>
- artifacts
+ return uniqBy(generatedArtifacts, 'id')
+}
+
+export const generateArtifactsReferencesList = artifacts => {
+ const generatedArtifacts = artifacts
.map(artifact => {
const artifactReference = getArtifactReference(artifact)
@@ -79,6 +79,9 @@ export const generateArtifactsReferencesList = artifacts =>
}
})
+ return uniqBy(generatedArtifacts, 'id')
+}
+
export const pathPlaceholders = {
[MLRUN_STORAGE_INPUT_PATH_SCHEME]: 'artifacts/my-project/my-artifact:my-tag',
[S3_INPUT_PATH_SCHEME]: 'bucket/path',
From 53da373ad26e9f0e65925ee9427b50261299b7b9 Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Mon, 27 Mar 2023 17:30:28 +0300
Subject: [PATCH 02/11] Fix [Feature Set] Target path for partition parquet
should be taken as Folder `1.3.x` (#1682)
---
.../FeatureSetsPanelTargetStore.js | 100 +++++++++---------
.../FeatureSetsPanelTargetStoreView.js | 24 ++++-
.../featureSetsPanelTargetStore.scss | 2 +-
.../featureSetsPanelTargetStore.util.js | 16 +++
4 files changed, 87 insertions(+), 55 deletions(-)
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStore.js b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStore.js
index 4b6cb7ab9..6f5011eaa 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStore.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStore.js
@@ -274,9 +274,11 @@ const FeatureSetsPanelTargetStore = ({
const handleDiscardPathChange = kind => {
const currentStoreType = kind === ONLINE ? NOSQL : kind
- const currentKind = featureStore.newFeatureSet.spec.targets.find(el => el.kind === currentStoreType)
+ const currentKind = featureStore.newFeatureSet.spec.targets.find(
+ el => el.kind === currentStoreType
+ )
- if (currentKind .path.length > 0) {
+ if (currentKind.path.length > 0) {
setData(state => ({
...state,
[kind]: {
@@ -564,57 +566,57 @@ const FeatureSetsPanelTargetStore = ({
}
const triggerPartitionCheckbox = (id, kind) => {
- if (kind === EXTERNAL_OFFLINE || kind === PARQUET) {
- setData(state => {
- let path = state[kind].path
-
- if (
- kind === PARQUET &&
- !targetsPathEditData.parquet.isEditMode &&
- !targetsPathEditData.parquet.isModified
- ) {
- path = generatePath(
- frontendSpec.feature_store_data_prefixes,
- project,
- data[kind].kind,
- featureStore.newFeatureSet.metadata.name,
- data[kind].partitioned ? PARQUET : ''
- )
- }
+ setData(state => {
+ let path = state[kind].path
+
+ if (
+ kind === PARQUET &&
+ !targetsPathEditData.parquet.isEditMode &&
+ !targetsPathEditData.parquet.isModified
+ ) {
+ path = generatePath(
+ frontendSpec.feature_store_data_prefixes,
+ project,
+ data[kind].kind,
+ featureStore.newFeatureSet.metadata.name,
+ data[kind].partitioned ? PARQUET : ''
+ )
+ } else if (kind === PARQUET && targetsPathEditData.parquet.isModified) {
+ path = state[kind].partitioned ? `${path}.parquet` : path.replace(/\.[^.]+$/, '')
+ }
- return data[kind]?.partitioned
- ? {
- ...state,
- [kind]: {
- ...dataInitialState[kind],
- path,
- kind: PARQUET
- }
+ return data[kind]?.partitioned
+ ? {
+ ...state,
+ [kind]: {
+ ...dataInitialState[kind],
+ path,
+ kind: PARQUET
}
- : {
- ...state,
- [kind]: {
- ...state[kind],
- path,
- partitioned: state[kind].partitioned === id ? '' : id,
- key_bucketing_number: '',
- partition_cols: '',
- time_partitioning_granularity: 'hour'
- }
+ }
+ : {
+ ...state,
+ [kind]: {
+ ...state[kind],
+ path,
+ partitioned: state[kind].partitioned === id ? '' : id,
+ key_bucketing_number: '',
+ partition_cols: '',
+ time_partitioning_granularity: 'hour'
}
- })
+ }
+ })
- if (data[kind].partitioned) {
- setShowAdvanced(state => ({ ...state, [kind]: false }))
- setPartitionRadioButtonsState(state => ({
- ...state,
- [kind]: 'districtKeys'
- }))
- setSelectedPartitionKind(state => ({
- ...state,
- [kind]: [...selectedPartitionKindInitialState[kind]]
- }))
- }
+ if (data[kind].partitioned) {
+ setShowAdvanced(state => ({ ...state, [kind]: false }))
+ setPartitionRadioButtonsState(state => ({
+ ...state,
+ [kind]: 'districtKeys'
+ }))
+ setSelectedPartitionKind(state => ({
+ ...state,
+ [kind]: [...selectedPartitionKindInitialState[kind]]
+ }))
}
const targets = cloneDeep(featureStore.newFeatureSet.spec.targets).map(targetKind => {
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js
index 9c9ab59ab..541151efc 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js
@@ -34,7 +34,9 @@ import {
externalOfflineKindOptions,
ONLINE,
PARQUET,
- checkboxModels
+ checkboxModels,
+ isParquetPathValid,
+ getInvalidParquetPathMessage
} from './featureSetsPanelTargetStore.util'
import { ReactComponent as Online } from 'igz-controls/images/nosql.svg'
@@ -178,14 +180,18 @@ const FeatureSetsPanelTargetStoreView = ({
density="normal"
floatingLabel
focused={frontendSpecIsNotEmpty}
- invalid={!validation.isOfflineTargetPathValid}
+ invalid={isParquetPathValid(
+ validation.isOfflineTargetPathValid,
+ data.parquet
+ )}
+ invalidText={getInvalidParquetPathMessage(data.parquet)}
label="Path"
- onChange={path =>
+ onChange={path => {
setData(state => ({
...state,
parquet: { ...state.parquet, path }
}))
- }
+ }}
placeholder={
'v3io:///projects/{project}/FeatureStore/{name}/{run_id}/parquet/sets/{name}.parquet'
}
@@ -201,7 +207,14 @@ const FeatureSetsPanelTargetStoreView = ({
wrapperClassName="offline-path"
/>
-
+
)}
triggerPartitionCheckbox(id, PARQUET)}
selectedId={data.parquet.partitioned}
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.scss b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.scss
index f75800957..7a23bfa27 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.scss
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.scss
@@ -82,7 +82,7 @@
.partition-fields {
width: 100%;
- margin-bottom: 10px;
+ margin: 10px 0;
&__checkbox-container {
display: flex;
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.util.js b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.util.js
index 83e2c9fa3..2464a53b9 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.util.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/featureSetsPanelTargetStore.util.js
@@ -218,3 +218,19 @@ export const handlePathChange = (
}))
}
}
+
+export const isParquetPathValid = (validation, parquet) => {
+ return (
+ !validation ||
+ Boolean(parquet.partitioned && /\.\w*\s*$/.test(parquet.path)) ||
+ Boolean(!parquet.partitioned && !/\.parquet\s*$|\.pq\s*$/.test(parquet.path))
+ )
+}
+
+export const getInvalidParquetPathMessage = parquet => {
+ return parquet.partitioned && /\.\w*\s*$/.test(parquet.path)
+ ? 'The partitioned Parquet target for storey engine must be a directory. (The directory name must not end in .parquet/.pq.)'
+ : !parquet.partitioned && !/\.parquet\s*$|\.pq\s*$/.test(parquet.path)
+ ? 'The Parquet target for storey engine file path must have a .parquet/.pq suffix.'
+ : 'This field is invalid.'
+}
From 4886625552cb31e0731902452ec700f72d7ce961 Mon Sep 17 00:00:00 2001
From: illia-prokopchuk <78905712+illia-prokopchuk@users.noreply.github.com>
Date: Mon, 27 Mar 2023 17:34:29 +0300
Subject: [PATCH 03/11] Fix [Project settings] "Something went wrong" during
project creation `1.3.x` (#1685)
---
src/reducers/projectReducer.js | 54 +---------------------------------
1 file changed, 1 insertion(+), 53 deletions(-)
diff --git a/src/reducers/projectReducer.js b/src/reducers/projectReducer.js
index 29f05bc88..62e73a127 100644
--- a/src/reducers/projectReducer.js
+++ b/src/reducers/projectReducer.js
@@ -251,59 +251,7 @@ const projectReducer = (state = initialState, { type, payload }) => {
error: null,
loading: false,
project: {
- data: null,
- error: null,
- loading: false,
- dataSets: {
- data: null,
- error: null,
- loading: false
- },
- failedJobs: {
- data: [],
- error: null,
- loading: false
- },
- featureSets: {
- data: null,
- error: null,
- loading: false
- },
- files: {
- data: null,
- error: null,
- loading: false
- },
- jobs: {
- data: null,
- error: null,
- loading: false
- },
- functions: {
- data: null,
- error: null,
- loading: false
- },
- models: {
- data: [],
- error: null,
- loading: false
- },
- runningJobs: {
- data: [],
- error: null,
- loading: false
- },
- scheduledJobs: {
- data: [],
- error: null,
- loading: false
- },
- workflows: {
- data: [],
- error: null,
- loading: false
- }
+ ...initialState.project
}
}
case FETCH_PROJECT_BEGIN:
From f7df6dc46a2c25aa485031d444e180e1d7e53352 Mon Sep 17 00:00:00 2001
From: Ilank <63646693+ilan7empest@users.noreply.github.com>
Date: Thu, 30 Mar 2023 10:03:22 +0300
Subject: [PATCH 04/11] Impl [Models] Adjust the Average Latency calculation in
model endpoints tab `1.3.x` (#1690)
---
src/utils/createArtifactsContent.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/utils/createArtifactsContent.js b/src/utils/createArtifactsContent.js
index 03d1ebfa1..e3741d597 100644
--- a/src/utils/createArtifactsContent.js
+++ b/src/utils/createArtifactsContent.js
@@ -374,7 +374,7 @@ export const createModelEndpointsRowData = (artifact, project) => {
? `store://functions/${artifact.spec.function_uri}`
: ''
const { key: functionName } = parseUri(functionUri)
- const averageLatency = artifact.status?.metrics?.latency_avg_1h?.values?.[0]?.[1]
+ const averageLatency = artifact.status?.metrics?.real_time?.latency_avg_1h?.[0]?.[1]
return {
data: {
From 85f5a0ddfdd197ef34dc6168d5144e4744597689 Mon Sep 17 00:00:00 2001
From: Tom Tankilevitch <59158507+Tankilevitch@users.noreply.github.com>
Date: Mon, 10 Apr 2023 23:34:47 +0300
Subject: [PATCH 05/11] uncomment side branches from build flow (#1694) (#1695)
---
.github/workflows/build.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 1a44d4868..b8b7ded00 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -18,7 +18,7 @@ on:
push:
branches:
- development
-# - '[0-9]+.[0-9]+.x'
+ - '[0-9]+.[0-9]+.x'
workflow_dispatch:
inputs:
From ec9c8536318d4d0b66ad11c51ef4ca1805e2a9fe Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Mon, 24 Apr 2023 10:08:35 +0300
Subject: [PATCH 06/11] Fix [Jobs] [Feature sets] UI should get
"store://datasets/" format as an input to the function `1.3.x` (#1698)
---
src/common/TargetPath/TargetPath.js | 10 +-
src/common/TargetPath/targetPath.util.js | 6 +-
.../FeatureSetsPanelDataSourceView.js | 41 +++------
.../featureSetsPanelDataSource.util.js | 4 +-
.../JobsPanelDataInputs.js | 19 +++-
.../jobsPanelDataInputs.util.js | 92 +++++--------------
.../EditableDataInputsRow.js | 44 +++------
.../JobsPanelDataInputsTable.js | 33 +++----
src/utils/panelPathScheme.js | 17 ++++
9 files changed, 106 insertions(+), 160 deletions(-)
diff --git a/src/common/TargetPath/TargetPath.js b/src/common/TargetPath/TargetPath.js
index cce71de7d..797e9b4d6 100644
--- a/src/common/TargetPath/TargetPath.js
+++ b/src/common/TargetPath/TargetPath.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, { useState, useEffect } from 'react'
+import React, { useState, useEffect, useMemo } from 'react'
import { useParams } from 'react-router-dom'
import { useDispatch } from 'react-redux'
import { get, isNil, uniqBy } from 'lodash'
@@ -252,6 +252,10 @@ const TargetPath = ({
setDataInputState
])
+ const generatedPathTips = useMemo(() => {
+ return pathTips(dataInputState.storePathType)
+ }, [dataInputState.storePathType])
+
return (
<>
{
+export const pathTips = projectItem => {
const pathType =
- storePathType === 'feature-vectors'
+ projectItem === 'feature-vectors'
? 'feature-vector'
- : storePathType === 'artifacts'
+ : projectItem === 'artifacts'
? 'artifact'
: 'dataset'
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSourceView.js b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSourceView.js
index ad7349319..e37286098 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSourceView.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/FeatureSetsPanelDataSourceView.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 from 'react'
+import React, { useMemo } from 'react'
import PropTypes from 'prop-types'
import cronstrue from 'cronstrue'
@@ -29,15 +29,10 @@ import ScheduleFeatureSet from '../ScheduleFeatureSet/ScheduleFeatureSet'
import Select from '../../../common/Select/Select'
import { Button } from 'igz-controls/components'
-import {
- comboboxSelectList,
- CSV,
- kindOptions,
- PARQUET
-} from './featureSetsPanelDataSource.util'
+import { comboboxSelectList, CSV, kindOptions, PARQUET } from './featureSetsPanelDataSource.util'
import { MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../../constants'
import { SECONDARY_BUTTON } from 'igz-controls/constants'
-import { pathPlaceholders } from '../../../utils/panelPathScheme'
+import { pathTips } from '../../../utils/panelPathScheme'
import { ReactComponent as Pencil } from 'igz-controls/images/edit.svg'
@@ -61,6 +56,10 @@ const FeatureSetsPanelDataSourceView = ({
urlProjectItemTypeEntered,
validation
}) => {
+ const generatedPathTips = useMemo(() => {
+ return pathTips(data.url.projectItemType)
+ }, [data.url.projectItemType])
+
return (
@@ -78,18 +77,14 @@ const FeatureSetsPanelDataSourceView = ({
comboboxClassName="url"
hideSearchInput={!urlProjectItemTypeEntered}
inputDefaultValue={
- data.url.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME
- ? data.url.projectItemType
- : ''
+ data.url.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME ? data.url.projectItemType : ''
}
inputOnChange={path => {
handleUrlPathChange(path)
}}
inputPlaceholder={data.url.placeholder}
invalid={!validation.isUrlValid}
- invalidText={`Field must be in "${
- pathPlaceholders[data.url.pathType]
- }" format`}
+ invalidText={`Field must be in "${generatedPathTips[data.url.pathType]}" format`}
matches={comboboxMatches}
maxSuggestedMatches={3}
onBlur={handleUrlOnBlur}
@@ -109,9 +104,7 @@ const FeatureSetsPanelDataSourceView = ({
className="schedule-tumbler"
label={
<>
- {data.schedule
- ? cronstrue.toString(data.schedule)
- : 'Schedule'}
+ {data.schedule ? cronstrue.toString(data.schedule) : 'Schedule'}
>
}
@@ -136,10 +129,7 @@ const FeatureSetsPanelDataSourceView = ({
invalid={!validation.isParseDatesValid}
label="Parse Dates"
onBlur={event => {
- if (
- featureStore.newFeatureSet.spec.source.parse_dates !==
- event.target.value
- ) {
+ if (featureStore.newFeatureSet.spec.source.parse_dates !== event.target.value) {
setNewFeatureSetDataSourceParseDates(event.target.value)
}
}}
@@ -150,17 +140,12 @@ const FeatureSetsPanelDataSourceView = ({
}))
}
placeholder="col_name1,col_name2,..."
- setInvalid={value =>
- setValidation(state => ({ ...state, isParseDatesValid: value }))
- }
+ setInvalid={value => setValidation(state => ({ ...state, isParseDatesValid: value }))}
type="text"
/>
)}
{data.kind === PARQUET && (
-
+
)}
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
index abfe478fb..1b77cde8f 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanelDataSource/featureSetsPanelDataSource.util.js
@@ -113,8 +113,8 @@ export const projectItemsPathTypes = [
export const isUrlInputValid = (pathInputType, pathInputValue, dataSourceKind) => {
const regExp =
dataSourceKind === CSV
- ? /^artifacts\/(.+?)\/(.+?)(#(.+?))?(:(.+?))?(@(.+))?(? 0 && /.*?\/(.*?)/.test(pathInputValue)
switch (pathInputType) {
diff --git a/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js b/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
index 00dbe32c2..71c7dee0b 100644
--- a/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
+++ b/src/components/JobsPanelDataInputs/JobsPanelDataInputs.js
@@ -42,7 +42,7 @@ import {
} from './jobsPanelDataInputs.util'
import featureStoreActions from '../../actions/featureStore'
import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
-import { MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../constants'
+import { ARTIFACT_OTHER_TYPE, DATASET_TYPE, MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../constants'
import { getFeatureReference, getParsedResource } from '../../utils/resources'
import {
generateArtifactsList,
@@ -112,10 +112,21 @@ const JobsPanelDataInputs = ({
useEffect(() => {
const storePathType = getInputValue('storePathType')
const projectName = getInputValue('project')
+ const projectItem = getInputValue('projectItem')
if (inputsState.inputProjectPathEntered && storePathType && projectName) {
- if (storePathType === 'artifacts' && inputsState.artifacts.length === 0) {
- dispatch(fetchArtifacts({ project: projectName }))
+ if (storePathType !== 'feature-vectors' && inputsState.artifacts.length === 0) {
+ dispatch(
+ fetchArtifacts({
+ project: projectName,
+ filters: null,
+ config: {
+ params: {
+ category: projectItem === 'artifacts' ? ARTIFACT_OTHER_TYPE : DATASET_TYPE
+ }
+ }
+ })
+ )
.unwrap()
.then(artifacts => {
inputsDispatch({
@@ -156,7 +167,7 @@ const JobsPanelDataInputs = ({
const projectItem = getInputValue('projectItem')
if (inputsState.inputProjectItemPathEntered && storePathType && projectName && projectItem) {
- if (storePathType === 'artifacts' && inputsState.artifactsReferences.length === 0) {
+ if (storePathType !== 'feature-vectors' && inputsState.artifactsReferences.length === 0) {
dispatch(fetchArtifact({ project: projectName, artifact: projectItem }))
.unwrap()
.then(artifacts => {
diff --git a/src/components/JobsPanelDataInputs/jobsPanelDataInputs.util.js b/src/components/JobsPanelDataInputs/jobsPanelDataInputs.util.js
index 3203a6692..33f44be0c 100644
--- a/src/components/JobsPanelDataInputs/jobsPanelDataInputs.util.js
+++ b/src/components/JobsPanelDataInputs/jobsPanelDataInputs.util.js
@@ -50,9 +50,7 @@ export const generateComboboxMatchesList = (
selectedDataInputPath
) => {
if (!inputStorePathTypeEntered) {
- return storePathTypes.some(type =>
- type.id.startsWith(newInput.path.storePathType)
- )
+ return storePathTypes.some(type => type.id.startsWith(newInput.path.storePathType))
? storePathTypes
: []
} else if (
@@ -71,32 +69,24 @@ export const generateComboboxMatchesList = (
} else if (!inputProjectItemPathEntered) {
const selectedStorePathType =
newInput.path.storePathType || selectedDataInputPath.value.split('/')[0]
- const projectItems =
- selectedStorePathType === 'artifacts'
- ? artifacts
- : selectedStorePathType === 'feature-vectors'
- ? featureVectors
- : null
+ const projectItems = selectedStorePathType === 'feature-vectors' ? featureVectors : artifacts
return projectItems
? projectItems.filter(projectItem => {
return isEveryObjectValueEmpty(selectedDataInputPath)
? projectItem.id.startsWith(newInput.path.projectItem)
- : projectItem.id.startsWith(
- selectedDataInputPath.value.split('/')[2]
- )
+ : projectItem.id.startsWith(selectedDataInputPath.value.split('/')[2])
})
: []
} else if (!inputProjectItemReferencePathEntered) {
const selectedStorePathType =
newInput.path.storePathType || selectedDataInputPath.value.split('/')[0]
const projectItemsReferences =
- selectedStorePathType === 'artifacts'
+ selectedStorePathType !== 'feature-vectors'
? artifactsReferences
: selectedStorePathType === 'feature-vectors'
? featureVectorsReferences
: null
-
return projectItemsReferences
? projectItemsReferences.filter(projectItem => {
return isEveryObjectValueEmpty(selectedDataInputPath)
@@ -125,8 +115,7 @@ export const handleAddItem = (
newInputUrlPath,
setDataInputsValidations
) => {
- const isMlRunStorePath =
- newItemObj.path.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME
+ const isMlRunStorePath = newItemObj.path.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME
let mlRunStorePath = ''
if (isMlRunStorePath) {
@@ -145,9 +134,7 @@ export const handleAddItem = (
if (newItemObj.name.length === 0 || !pathInputIsValid) {
setDataInputsValidations({
- isNameValid:
- !isNameNotUnique(newItemObj.name, dataInputs) &&
- newItemObj.name.length > 0,
+ isNameValid: !isNameNotUnique(newItemObj.name, dataInputs) && newItemObj.name.length > 0,
isPathValid: pathInputIsValid
})
} else {
@@ -208,15 +195,9 @@ export const handleEdit = (
if (selectedItem.newDataInputName) {
delete currentDataObj[selectedItem.name]
- currentDataObj[selectedItem.newDataInputName] = joinDataOfArrayOrObject(
- selectedItem.path,
- ''
- )
+ currentDataObj[selectedItem.newDataInputName] = joinDataOfArrayOrObject(selectedItem.path, '')
} else {
- currentDataObj[selectedItem.name] = joinDataOfArrayOrObject(
- selectedItem.path,
- ''
- )
+ currentDataObj[selectedItem.name] = joinDataOfArrayOrObject(selectedItem.path, '')
}
setCurrentPanelData({ ...currentDataObj })
@@ -242,10 +223,7 @@ export const handleEdit = (
})
}
-export const resetDataInputsData = (
- inputsDispatch,
- setDataInputsValidations
-) => {
+export const resetDataInputsData = (inputsDispatch, setDataInputsValidations) => {
inputsDispatch({
type: inputsActions.REMOVE_NEW_INPUT_DATA
})
@@ -287,15 +265,11 @@ export const handleDelete = (
setCurrentPanelData({ ...newInputs })
panelDispatch({
type: setPreviousPanelData,
- payload: previousPanelData.filter(
- dataItem => dataItem.data.name !== selectedItem.data.name
- )
+ payload: previousPanelData.filter(dataItem => dataItem.data.name !== selectedItem.data.name)
})
panelDispatch({
type: setCurrentTableData,
- payload: currentTableData.filter(
- dataItem => dataItem.data.name !== selectedItem.data.name
- )
+ payload: currentTableData.filter(dataItem => dataItem.data.name !== selectedItem.data.name)
})
}
@@ -342,6 +316,10 @@ export const storePathTypes = [
label: 'Artifacts',
id: 'artifacts'
},
+ {
+ label: 'Datasets',
+ id: 'datasets'
+ },
{
label: 'Feature vectors',
id: 'feature-vectors'
@@ -389,10 +367,7 @@ export const handleInputPathTypeChange = (
export const handleInputPathChange = (inputsDispatch, inputsState, path) => {
if (inputsState.newInput.path.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME) {
- if (
- path.length === 0 &&
- inputsState.newInputDefaultPathProject.length > 0
- ) {
+ if (path.length === 0 && inputsState.newInputDefaultPathProject.length > 0) {
inputsDispatch({
type: inputsActions.SET_NEW_INPUT_DEFAULT_PATH_PROJECT,
payload: ''
@@ -408,12 +383,7 @@ export const handleInputPathChange = (inputsDispatch, inputsState, path) => {
}
}
-export const handleStoreInputPathChange = (
- isNewInput,
- inputsDispatch,
- inputsState,
- path
-) => {
+export const handleStoreInputPathChange = (isNewInput, inputsDispatch, inputsState, path) => {
const pathItems = path.split('/')
const [projectItem, projectItemReference] = getParsedResource(pathItems[2])
@@ -457,29 +427,22 @@ export const handleStoreInputPathChange = (
storePathType: pathItems[0] ?? inputsState.newInput.path.storePathType,
project: pathItems[1] ?? inputsState.newInput.path.project,
projectItem: projectItem ?? inputsState.newInput.path.projectItem,
- projectItemReference:
- projectItemReference ?? inputsState.newInput.path.projectItemReference
+ projectItemReference: projectItemReference ?? inputsState.newInput.path.projectItemReference
}
})
}
const projectItems =
- inputsState[pathItems[0] === 'artifacts' ? 'artifacts' : 'featureVectors']
- const projectItemIsEntered = projectItems.find(
- project => project.id === projectItem
- )
+ inputsState[pathItems[0] !== 'feature-vectors' ? 'artifacts' : 'featureVectors']
+ const projectItemIsEntered = projectItems.find(project => project.id === projectItem)
const projectItemsReferences =
inputsState[
- pathItems[0] === 'artifacts'
- ? 'artifactsReferences'
- : 'featureVectorsReferences'
+ pathItems[0] !== 'feature-vectors' ? 'artifactsReferences' : 'featureVectorsReferences'
]
const projectItemReferenceIsEntered = projectItemsReferences.find(
projectItemRef => projectItemRef.id === projectItemReference
)
- const isInputStorePathTypeValid = storePathTypes.some(type =>
- type.id.startsWith(pathItems[0])
- )
+ const isInputStorePathTypeValid = storePathTypes.some(type => type.id.startsWith(pathItems[0]))
inputsDispatch({
type: inputsActions.SET_INPUT_STORE_PATH_TYPE_ENTERED,
@@ -505,7 +468,7 @@ export const isPathInputValid = (pathInputType, pathInputValue) => {
case MLRUN_STORAGE_INPUT_PATH_SCHEME:
return (
valueIsNotEmpty &&
- /^(artifacts|feature-vectors)\/(.+?)\/(.+?)(#(.+?))?(:(.+?))?(@(.+))?$/.test(
+ /^(artifacts|datasets|feature-vectors)\/(.+?)\/(.+?)(#(.+?))?(:(.+?))?(@(.+))?$/.test(
pathInputValue
)
)
@@ -516,12 +479,3 @@ export const isPathInputValid = (pathInputType, pathInputValue) => {
return valueIsNotEmpty
}
}
-
-export const pathTips = {
- [MLRUN_STORAGE_INPUT_PATH_SCHEME]:
- 'artifacts/my-project/my-artifact:my-tag" or "artifacts/my-project/my-artifact@my-uid',
- [S3_INPUT_PATH_SCHEME]: 'bucket/path',
- [GOOGLE_STORAGE_INPUT_PATH_SCHEME]: 'bucket/path',
- [AZURE_STORAGE_INPUT_PATH_SCHEME]: 'container/path',
- [V3IO_INPUT_PATH_SCHEME]: 'container-name/file'
-}
diff --git a/src/elements/EditableDataInputsRow/EditableDataInputsRow.js b/src/elements/EditableDataInputsRow/EditableDataInputsRow.js
index e861f4d7a..55cddbc0b 100644
--- a/src/elements/EditableDataInputsRow/EditableDataInputsRow.js
+++ b/src/elements/EditableDataInputsRow/EditableDataInputsRow.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, useEffect, useRef, useState } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
@@ -25,18 +25,12 @@ import Combobox from '../../common/Combobox/Combobox'
import Input from '../../common/Input/Input'
import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
-import {
- comboboxSelectList,
- pathTips
-} from '../../components/JobsPanelDataInputs/jobsPanelDataInputs.util'
+import { comboboxSelectList } from '../../components/JobsPanelDataInputs/jobsPanelDataInputs.util'
import { inputsActions } from '../../components/JobsPanelDataInputs/jobsPanelDataInputsReducer'
import { MLRUN_STORAGE_INPUT_PATH_SCHEME } from '../../constants'
-import {
- applyEditButtonHandler,
- handleEditInputPath
-} from './EditableDataInputsRow.utils'
+import { applyEditButtonHandler, handleEditInputPath } from './EditableDataInputsRow.utils'
import { isNameNotUnique } from '../../components/JobsPanel/jobsPanel.util'
-import { pathPlaceholders } from '../../utils/panelPathScheme'
+import { pathPlaceholders, pathTips } from '../../utils/panelPathScheme'
import { ReactComponent as Checkmark } from 'igz-controls/images/checkmark.svg'
@@ -64,24 +58,15 @@ const EditableDataInputsRow = ({
})
const tableRowRef = useRef(null)
- const comboboxClassNames = classNames(
- 'input-row__item',
- requiredField.path && 'required'
- )
- const inputNameClassNames = classNames(
- 'input',
- requiredField.name && 'input_required'
- )
+ const comboboxClassNames = classNames('input-row__item', requiredField.path && 'required')
+ const inputNameClassNames = classNames('input', requiredField.name && 'input_required')
useEffect(() => {
if (selectDefaultValue.label?.length === 0) {
setSelectDefaultValue({
id: selectedDataInput.data.path.pathType,
label: selectedDataInput.data.path.pathType,
- className: `path-type-${selectedDataInput.data.path.pathType?.replace(
- /:\/\/.*$/g,
- ''
- )}`
+ className: `path-type-${selectedDataInput.data.path.pathType?.replace(/:\/\/.*$/g, '')}`
})
}
}, [selectDefaultValue.label, selectedDataInput.data.path.pathType])
@@ -143,6 +128,10 @@ const EditableDataInputsRow = ({
})
}
+ const generatedPathTips = useMemo(() => {
+ return pathTips(selectedDataInput.data.path.projectItem)
+ }, [selectedDataInput.data.path.projectItem])
+
return (
{selectedDataInput.isDefault ? (
@@ -155,8 +144,7 @@ const EditableDataInputsRow = ({
className={inputNameClassNames}
density="dense"
invalid={
- inputName !== selectedDataInput.data.name &&
- isNameNotUnique(inputName, content)
+ inputName !== selectedDataInput.data.name && isNameNotUnique(inputName, content)
}
invalidText="Name already exists"
onChange={name => {
@@ -182,12 +170,11 @@ const EditableDataInputsRow = ({
inputPlaceholder={inputsState.pathPlaceholder}
invalid={!isPathValid}
invalidText={`Field must be in "${
- pathTips[selectedDataInput.data.path.pathType]
+ generatedPathTips[selectedDataInput.data.path.pathType]
}" format`}
matches={comboboxMatchesList}
maxSuggestedMatches={
- inputsState.selectedDataInput.data.path.pathType ===
- MLRUN_STORAGE_INPUT_PATH_SCHEME
+ inputsState.selectedDataInput.data.path.pathType === MLRUN_STORAGE_INPUT_PATH_SCHEME
? 3
: 2
}
@@ -214,8 +201,7 @@ const EditableDataInputsRow = ({
-
resetDataInputsData(inputsDispatch, setValidation)}
- >
+ resetDataInputsData(inputsDispatch, setValidation)}>
}>
diff --git a/src/utils/panelPathScheme.js b/src/utils/panelPathScheme.js
index 140a16b29..f7f58bd90 100644
--- a/src/utils/panelPathScheme.js
+++ b/src/utils/panelPathScheme.js
@@ -89,3 +89,20 @@ export const pathPlaceholders = {
[AZURE_STORAGE_INPUT_PATH_SCHEME]: 'container/path',
[V3IO_INPUT_PATH_SCHEME]: 'container-name/file'
}
+
+export const pathTips = projectItem => {
+ const pathType =
+ projectItem === 'feature-vectors'
+ ? 'feature-vector'
+ : projectItem === 'artifacts'
+ ? 'artifact'
+ : 'dataset'
+
+ return {
+ [MLRUN_STORAGE_INPUT_PATH_SCHEME]: `${pathType}s/my-project/my-${pathType}:my-tag" or "${pathType}s/my-project/my-${pathType}@my-uid`,
+ [S3_INPUT_PATH_SCHEME]: 'bucket/path',
+ [GOOGLE_STORAGE_INPUT_PATH_SCHEME]: 'bucket/path',
+ [AZURE_STORAGE_INPUT_PATH_SCHEME]: 'container/path',
+ [V3IO_INPUT_PATH_SCHEME]: 'container-name/file'
+ }
+}
From 0ffa8283fd8744eb0825935398e5e3cab2bf6103 Mon Sep 17 00:00:00 2001
From: Andrew Mavdryk
Date: Mon, 24 Apr 2023 10:11:59 +0300
Subject: [PATCH 07/11] Impl [Artifacts] Disable `preview` and `download`
buttons for not allowed prefixes `1.3.x` (#1700)
---
src/common/Download/Download.js | 20 ++++++---
src/common/Download/download.scss | 25 ++++++++---
src/components/Datasets/Datasets.js | 14 ++++---
src/components/Datasets/datasets.util.js | 7 +++-
src/components/Files/Files.js | 12 +++---
src/components/Files/files.util.js | 14 ++++++-
src/components/ModelsPage/Models/Models.js | 14 ++++---
.../ModelsPage/Models/models.util.js | 7 +++-
src/components/Table/Table.js | 5 ++-
src/components/Table/table.scss | 9 +++-
src/elements/TableCell/TableCell.js | 15 +++----
src/utils/createArtifactsContent.js | 41 +++++++++++++------
src/utils/generateTableContent.js | 5 ++-
13 files changed, 131 insertions(+), 57 deletions(-)
diff --git a/src/common/Download/Download.js b/src/common/Download/Download.js
index 9c7e70f2c..4cefa9da6 100644
--- a/src/common/Download/Download.js
+++ b/src/common/Download/Download.js
@@ -20,6 +20,7 @@ such restriction.
import React, { useState, useEffect, useCallback, useRef } from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
+import classnames from 'classnames'
import { useDispatch } from 'react-redux'
import { useParams } from 'react-router-dom'
@@ -34,10 +35,14 @@ import colors from 'igz-controls/scss/colors.scss'
const DEFAULT_FILE_NAME = 'mlrun-file'
-const Download = ({ fileName, path, user }) => {
+const Download = ({ disabled, fileName, path, user }) => {
const [progress, setProgress] = useState(0)
const [isDownload, setDownload] = useState(false)
const params = useParams()
+ const downloadContainerClassnames = classnames(
+ 'download-container',
+ disabled && 'download-container_disabled'
+ )
const downloadRef = useRef(null)
const dispatch = useDispatch()
@@ -149,14 +154,17 @@ const Download = ({ fileName, path, user }) => {
}, [downloadCallback, downloadRef])
const handleClick = () => {
- if (downloadRef.current?.cancel) {
- return downloadRef.current.cancel('cancel')
+ if (!disabled) {
+ if (downloadRef.current?.cancel) {
+ return downloadRef.current.cancel('cancel')
+ }
+
+ setDownload(!isDownload)
}
- setDownload(!isDownload)
}
return (
-
+
{
{!isDownload ? (
-
+
{
const navigate = useNavigate()
const location = useLocation()
const dispatch = useDispatch()
+ const frontendSpec = useSelector(store => store.appStore.frontendSpec)
const detailsFormInitialValues = useMemo(
() => ({
@@ -185,10 +186,11 @@ const Datasets = () => {
setSelectedRowData,
filtersStore.iter,
filtersStore.tag,
- params.projectName
+ params.projectName,
+ frontendSpec
)
},
- [dispatch, filtersStore.iter, filtersStore.tag, params.projectName]
+ [dispatch, filtersStore.iter, filtersStore.tag, frontendSpec, params.projectName]
)
const handleRemoveRowData = useCallback(
@@ -219,10 +221,12 @@ const Datasets = () => {
const tableContent = useMemo(() => {
return filtersStore.groupBy === GROUP_BY_NAME
? latestItems.map(contentItem => {
- return createDatasetsRowData(contentItem, params.projectName, true)
+ return createDatasetsRowData(contentItem, params.projectName, frontendSpec, true)
})
- : datasets.map(contentItem => createDatasetsRowData(contentItem, params.projectName))
- }, [datasets, filtersStore.groupBy, latestItems, params.projectName])
+ : datasets.map(contentItem =>
+ createDatasetsRowData(contentItem, params.projectName, frontendSpec)
+ )
+ }, [datasets, filtersStore.groupBy, frontendSpec, latestItems, params.projectName])
useEffect(() => {
dispatch(removeDataSet({}))
diff --git a/src/components/Datasets/datasets.util.js b/src/components/Datasets/datasets.util.js
index 7888d532e..94ee25945 100644
--- a/src/components/Datasets/datasets.util.js
+++ b/src/components/Datasets/datasets.util.js
@@ -105,7 +105,8 @@ export const fetchDataSetRowData = async (
setSelectedRowData,
iter,
tag,
- projectName
+ projectName,
+ frontendSpec
) => {
const dataSetIdentifier = getArtifactIdentifier(dataSet)
@@ -124,7 +125,9 @@ export const fetchDataSetRowData = async (
return {
...state,
[dataSetIdentifier]: {
- content: result.map(artifact => createDatasetsRowData(artifact, projectName)),
+ content: result.map(artifact =>
+ createDatasetsRowData(artifact, projectName, frontendSpec)
+ ),
error: null,
loading: false
}
diff --git a/src/components/Files/Files.js b/src/components/Files/Files.js
index 5bbc2d132..1ccf04a10 100644
--- a/src/components/Files/Files.js
+++ b/src/components/Files/Files.js
@@ -74,6 +74,7 @@ const Files = () => {
const dispatch = useDispatch()
const filesRef = useRef(null)
const pageData = useMemo(() => generatePageData(selectedFile), [selectedFile])
+ const frontendSpec = useSelector(store => store.appStore.frontendSpec)
const detailsFormInitialValues = useMemo(
() => ({
@@ -172,10 +173,11 @@ const Files = () => {
dispatch,
params.projectName,
filtersStore.iter,
- filtersStore.tag
+ filtersStore.tag,
+ frontendSpec
)
},
- [dispatch, filtersStore.iter, filtersStore.tag, params.projectName]
+ [dispatch, filtersStore.iter, filtersStore.tag, frontendSpec, params.projectName]
)
const { latestItems, handleExpandRow } = useGroupContent(
@@ -190,10 +192,10 @@ const Files = () => {
const tableContent = useMemo(() => {
return filtersStore.groupBy === GROUP_BY_NAME
? latestItems.map(contentItem => {
- return createFilesRowData(contentItem, params.projectName, true)
+ return createFilesRowData(contentItem, params.projectName, frontendSpec, true)
})
- : files.map(contentItem => createFilesRowData(contentItem, params.projectName))
- }, [files, filtersStore.groupBy, latestItems, params.projectName])
+ : files.map(contentItem => createFilesRowData(contentItem, params.projectName, frontendSpec))
+ }, [files, filtersStore.groupBy, frontendSpec, latestItems, params.projectName])
const applyDetailsChanges = useCallback(
changes => {
diff --git a/src/components/Files/files.util.js b/src/components/Files/files.util.js
index c7c6bf5e9..b0254498a 100644
--- a/src/components/Files/files.util.js
+++ b/src/components/Files/files.util.js
@@ -100,7 +100,15 @@ export const filters = [
]
export const actionsMenuHeader = 'Register artifact'
-export const fetchFilesRowData = (file, setSelectedRowData, dispatch, projectName, iter, tag) => {
+export const fetchFilesRowData = (
+ file,
+ setSelectedRowData,
+ dispatch,
+ projectName,
+ iter,
+ tag,
+ frontendSpec
+) => {
const fileIdentifier = getArtifactIdentifier(file)
setSelectedRowData(state => ({
@@ -117,7 +125,9 @@ export const fetchFilesRowData = (file, setSelectedRowData, dispatch, projectNam
setSelectedRowData(state => ({
...state,
[fileIdentifier]: {
- content: result.map(artifact => createFilesRowData(artifact, projectName)),
+ content: result.map(artifact =>
+ createFilesRowData(artifact, projectName, frontendSpec)
+ ),
error: null,
loading: false
}
diff --git a/src/components/ModelsPage/Models/Models.js b/src/components/ModelsPage/Models/Models.js
index 195b40d89..a14326265 100644
--- a/src/components/ModelsPage/Models/Models.js
+++ b/src/components/ModelsPage/Models/Models.js
@@ -79,6 +79,7 @@ const Models = ({ fetchModelFeatureVector }) => {
const modelsRef = useRef(null)
const pageData = useMemo(() => generatePageData(selectedModel), [selectedModel])
const { fetchData, models, setModels, toggleConvertedYaml } = useModelsPage()
+ const frontendSpec = useSelector(store => store.appStore.frontendSpec)
const detailsFormInitialValues = useMemo(
() => ({
@@ -171,10 +172,11 @@ const Models = ({ fetchModelFeatureVector }) => {
setSelectedRowData,
filtersStore.iter,
filtersStore.tag,
- params.projectName
+ params.projectName,
+ frontendSpec
)
},
- [dispatch, filtersStore.iter, filtersStore.tag, params.projectName]
+ [dispatch, filtersStore.iter, filtersStore.tag, frontendSpec, params.projectName]
)
const applyDetailsChanges = useCallback(
@@ -224,10 +226,12 @@ const Models = ({ fetchModelFeatureVector }) => {
const tableContent = useMemo(() => {
return filtersStore.groupBy === GROUP_BY_NAME
? latestItems.map(contentItem => {
- return createModelsRowData(contentItem, params.projectName, true)
+ return createModelsRowData(contentItem, params.projectName, frontendSpec, true)
})
- : models.map(contentItem => createModelsRowData(contentItem, params.projectName))
- }, [filtersStore.groupBy, latestItems, models, params.projectName])
+ : models.map(contentItem =>
+ createModelsRowData(contentItem, params.projectName, frontendSpec)
+ )
+ }, [filtersStore.groupBy, frontendSpec, latestItems, models, params.projectName])
const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent } = useSortTable({
headers: tableContent[0]?.content,
diff --git a/src/components/ModelsPage/Models/models.util.js b/src/components/ModelsPage/Models/models.util.js
index a3d89456f..b7c7a85a2 100644
--- a/src/components/ModelsPage/Models/models.util.js
+++ b/src/components/ModelsPage/Models/models.util.js
@@ -80,7 +80,8 @@ export const fetchModelsRowData = async (
setSelectedRowData,
iter,
tag,
- projectName
+ projectName,
+ frontendSpec
) => {
const modelIdentifier = getArtifactIdentifier(model)
@@ -99,7 +100,9 @@ export const fetchModelsRowData = async (
return {
...state,
[modelIdentifier]: {
- content: result.map(artifact => createModelsRowData(artifact, projectName)),
+ content: result.map(artifact =>
+ createModelsRowData(artifact, projectName, frontendSpec)
+ ),
error: null,
loading: false
}
diff --git a/src/components/Table/Table.js b/src/components/Table/Table.js
index fa37344a0..42ab1e35c 100644
--- a/src/components/Table/Table.js
+++ b/src/components/Table/Table.js
@@ -68,6 +68,7 @@ const Table = ({
const { isStagingMode } = useMode()
const params = useParams()
const tableStore = useSelector(store => store.tableStore)
+ const frontendSpec = useSelector(store => store.appStore.frontendSpec)
useEffect(() => {
const calculatePanelHeight = () => {
@@ -103,6 +104,7 @@ const Table = ({
pageData.page,
tableStore.isTablePanelOpen,
params,
+ frontendSpec,
isStagingMode,
!isEveryObjectValueEmpty(selectedItem)
)
@@ -131,7 +133,8 @@ const Table = ({
pageData.mainRowItemsCount,
pageData.page,
selectedItem,
- tableStore.isTablePanelOpen
+ tableStore.isTablePanelOpen,
+ frontendSpec
])
return (
diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss
index 19c22cd24..80eaa8005 100644
--- a/src/components/Table/table.scss
+++ b/src/components/Table/table.scss
@@ -252,7 +252,14 @@
font-size: 15px;
background-color: transparent;
border: none;
- cursor: pointer;
+
+ &:disabled {
+ cursor: default;
+ }
+
+ &:not(:disabled) {
+ cursor: pointer;
+ }
}
.expand-arrow {
diff --git a/src/elements/TableCell/TableCell.js b/src/elements/TableCell/TableCell.js
index 1c625eb02..b6c1b760e 100644
--- a/src/elements/TableCell/TableCell.js
+++ b/src/elements/TableCell/TableCell.js
@@ -118,7 +118,9 @@ const TableCell = ({
} else if (data.type === 'buttonPopout') {
return (
-
{
dispatch(
showArtifactsPreview({
@@ -128,19 +130,18 @@ const TableCell = ({
)
}}
>
- }>
-
-
-
+
+
)
} else if (data.type === 'buttonDownload') {
return (
- }>
+ }>
@@ -148,7 +149,7 @@ const TableCell = ({
} else if (data.type === BUTTON_COPY_URI_CELL_TYPE) {
return (
- data.actionHandler(item)}>
+ data.actionHandler(item)}>
diff --git a/src/utils/createArtifactsContent.js b/src/utils/createArtifactsContent.js
index e3741d597..17f6de1b2 100644
--- a/src/utils/createArtifactsContent.js
+++ b/src/utils/createArtifactsContent.js
@@ -43,21 +43,21 @@ import { ReactComponent as SeverityOk } from 'igz-controls/images/severity-ok.sv
import { ReactComponent as SeverityWarning } from 'igz-controls/images/severity-warning.svg'
import { ReactComponent as SeverityError } from 'igz-controls/images/severity-error.svg'
-export const createArtifactsContent = (artifacts, page, pageTab, project) => {
+export const createArtifactsContent = (artifacts, page, pageTab, project, frontendSpec) => {
return (artifacts.filter(artifact => !artifact.link_iteration) ?? []).map(artifact => {
if (page === ARTIFACTS_PAGE) {
return createArtifactsRowData(artifact)
} else if (page === MODELS_PAGE) {
if (pageTab === MODELS_TAB) {
- return createModelsRowData(artifact, project)
+ return createModelsRowData(artifact, project, frontendSpec)
} else if (pageTab === MODEL_ENDPOINTS_TAB) {
return createModelEndpointsRowData(artifact, project)
}
} else if (page === FILES_PAGE) {
- return createFilesRowData(artifact, project)
+ return createFilesRowData(artifact, project, frontendSpec)
}
- return createDatasetsRowData(artifact, project)
+ return createDatasetsRowData(artifact, project, frontendSpec)
})
}
@@ -104,8 +104,11 @@ const createArtifactsRowData = artifact => {
}
}
-export const createModelsRowData = (artifact, project, showExpandButton) => {
+export const createModelsRowData = (artifact, project, frontendSpec, showExpandButton) => {
const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
+ const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
+ return artifact.target_path?.startsWith?.(prefix)
+ })
return {
data: {
@@ -215,14 +218,16 @@ export const createModelsRowData = (artifact, project, showExpandButton) => {
headerId: 'popupt',
value: '',
class: 'table-cell-icon',
- type: 'buttonPopout'
+ type: 'buttonPopout',
+ disabled: !isTargetPathValid
},
{
id: `buttonDownload.${artifact.ui.identifierUnique}`,
headerId: 'download',
value: '',
class: 'table-cell-icon',
- type: 'buttonDownload'
+ type: 'buttonDownload',
+ disabled: !isTargetPathValid
},
{
id: `buttonCopy.${artifact.ui.identifierUnique}`,
@@ -236,8 +241,11 @@ export const createModelsRowData = (artifact, project, showExpandButton) => {
}
}
-export const createFilesRowData = (artifact, project, showExpandButton) => {
+export const createFilesRowData = (artifact, project, frontendSpec, showExpandButton) => {
const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
+ const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
+ return artifact.target_path?.startsWith?.(prefix)
+ })
return {
data: {
@@ -331,14 +339,16 @@ export const createFilesRowData = (artifact, project, showExpandButton) => {
headerId: 'popout',
value: '',
class: 'table-cell-icon',
- type: 'buttonPopout'
+ type: 'buttonPopout',
+ disabled: !isTargetPathValid
},
{
id: `buttonDownload.${artifact.ui.identifierUnique}`,
headerId: 'download',
value: '',
class: 'table-cell-icon',
- type: 'buttonDownload'
+ type: 'buttonDownload',
+ disabled: !isTargetPathValid
},
{
id: `buttonCopy.${artifact.ui.identifierUnique}`,
@@ -477,8 +487,11 @@ export const createModelEndpointsRowData = (artifact, project) => {
}
}
-export const createDatasetsRowData = (artifact, project, showExpandButton) => {
+export const createDatasetsRowData = (artifact, project, frontendSpec, showExpandButton) => {
const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
+ const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
+ return artifact.target_path?.startsWith?.(prefix)
+ })
return {
data: {
@@ -565,14 +578,16 @@ export const createDatasetsRowData = (artifact, project, showExpandButton) => {
headerId: 'popout',
value: '',
class: 'table-cell-icon',
- type: 'buttonPopout'
+ type: 'buttonPopout',
+ disabled: !isTargetPathValid
},
{
id: `buttonDownload.${artifact.ui.identifierUnique}`,
headerId: 'download',
value: '',
class: 'table-cell-icon',
- type: 'buttonDownload'
+ type: 'buttonDownload',
+ disabled: !isTargetPathValid
},
{
id: `buttonCopy.${artifact.ui.identifierUnique}`,
diff --git a/src/utils/generateTableContent.js b/src/utils/generateTableContent.js
index 81f04c99c..c21913c9f 100644
--- a/src/utils/generateTableContent.js
+++ b/src/utils/generateTableContent.js
@@ -46,6 +46,7 @@ export const generateTableContent = (
page,
isTablePanelOpen,
params,
+ frontendSpec,
isStagingMode,
isSelectedItem
) => {
@@ -59,7 +60,7 @@ export const generateTableContent = (
? createFunctionsContent(group, isSelectedItem, params.pageTab, params.projectName, true)
: page === FEATURE_STORE_PAGE
? createFeatureStoreContent(group, params.pageTab, params.projectName, isTablePanelOpen)
- : createArtifactsContent(group, page, params.pageTab, params.projectName)
+ : createArtifactsContent(group, page, params.pageTab, params.projectName, frontendSpec)
)
} else if (!isEmpty(content) && (groupFilter === GROUP_BY_NONE || !groupFilter)) {
return page === CONSUMER_GROUP_PAGE
@@ -70,7 +71,7 @@ export const generateTableContent = (
page === FILES_PAGE ||
page === DATASETS_PAGE ||
(page === MODELS_PAGE && params.pageTab !== REAL_TIME_PIPELINES_TAB)
- ? createArtifactsContent(content, page, params.pageTab, params.projectName)
+ ? createArtifactsContent(content, page, params.pageTab, params.projectName, frontendSpec)
: page === FEATURE_STORE_PAGE
? createFeatureStoreContent(content, params.pageTab, params.projectName, isTablePanelOpen)
: createFunctionsContent(content, isSelectedItem, params)
From 27b698feff2ad02f1c1297e6440b67d95e02463f Mon Sep 17 00:00:00 2001
From: Andrew Mavdryk
Date: Sun, 7 May 2023 19:31:36 +0300
Subject: [PATCH 08/11] Fix [Projects] Projects screen may explode the system
`1.3.x` (#1712)
---
src/actions/projects.js | 8 ++-
src/api/projects-api.js | 7 +--
src/common/ActionsMenu/ActionsMenu.js | 30 +++++----
src/components/ProjectsPage/Projects.js | 62 ++++++++++++++-----
src/components/ProjectsPage/ProjectsView.js | 2 +-
src/components/ProjectsPage/projectsData.js | 4 +-
.../ActionMenuItem/ActionsMenuItem.js | 7 ++-
7 files changed, 76 insertions(+), 44 deletions(-)
diff --git a/src/actions/projects.js b/src/actions/projects.js
index 06142cc12..40c5fca17 100644
--- a/src/actions/projects.js
+++ b/src/actions/projects.js
@@ -179,6 +179,8 @@ const projectsAction = {
.getProject(project)
.then(response => {
dispatch(projectsAction.fetchProjectSuccess(response?.data))
+
+ return response?.data
})
.catch(error => {
dispatch(projectsAction.fetchProjectFailure(error))
@@ -485,11 +487,11 @@ const projectsAction = {
type: FETCH_PROJECT_SUMMARY_SUCCESS,
payload: summary
}),
- fetchProjects: () => dispatch => {
+ fetchProjects: params => dispatch => {
dispatch(projectsAction.fetchProjectsBegin())
return projectsApi
- .getProjects()
+ .getProjects(params)
.then(response => {
dispatch(projectsAction.fetchProjectsSuccess(response.data.projects))
@@ -508,7 +510,7 @@ const projectsAction = {
dispatch(projectsAction.fetchProjectsNamesBegin())
return projectsApi
- .getProjectsNames()
+ .getProjects({ format: 'name_only' })
.then(({ data: { projects } }) => {
dispatch(projectsAction.fetchProjectsNamesSuccess(projects))
diff --git a/src/api/projects-api.js b/src/api/projects-api.js
index b690e574e..557836c2f 100644
--- a/src/api/projects-api.js
+++ b/src/api/projects-api.js
@@ -70,12 +70,9 @@ const projectsApi = {
getProjectScheduledJobs: project => mainHttpClient.get(`/projects/${project}/schedules`),
getProjectSecrets: project =>
mainHttpClient.get(`/projects/${project}/secret-keys?provider=kubernetes`),
- getProjects: () => mainHttpClient.get('/projects'),
- getProjectsNames: () =>
+ getProjects: params =>
mainHttpClient.get('/projects', {
- params: {
- format: 'name_only'
- }
+ params
}),
getProjectSummaries: cancelToken =>
mainHttpClient.get('/project-summaries', {
diff --git a/src/common/ActionsMenu/ActionsMenu.js b/src/common/ActionsMenu/ActionsMenu.js
index c60b2d12e..acbd3d124 100644
--- a/src/common/ActionsMenu/ActionsMenu.js
+++ b/src/common/ActionsMenu/ActionsMenu.js
@@ -44,10 +44,7 @@ const ActionsMenu = ({ dataItem, menu, time }) => {
'actions-menu__container',
isShowMenu && 'actions-menu__container-active'
)
- const dropDownMenuClassNames = classnames(
- 'actions-menu__body',
- isShowMenu && 'show'
- )
+ const dropDownMenuClassNames = classnames('actions-menu__body', isShowMenu && 'show')
let idTimeout = null
const offset = 15
@@ -67,21 +64,18 @@ const ActionsMenu = ({ dataItem, menu, time }) => {
const dropDownMenuRect = dropDownMenuRef.current.getBoundingClientRect()
if (
- actionMenuRect.top +
- actionMenuRect.height +
- offset +
- dropDownMenuRect.height >=
+ actionMenuRect.top + actionMenuRect.height + offset + dropDownMenuRect.height >=
window.innerHeight
) {
- dropDownMenuRef.current.style.top = `${actionMenuRect.top -
- dropDownMenuRect.height}px`
- dropDownMenuRef.current.style.left = `${actionMenuRect.left -
- dropDownMenuRect.width +
- offset}px`
+ dropDownMenuRef.current.style.top = `${actionMenuRect.top - dropDownMenuRect.height}px`
+ dropDownMenuRef.current.style.left = `${
+ actionMenuRect.left - dropDownMenuRect.width + offset
+ }px`
} else {
dropDownMenuRef.current.style.top = `${actionMenuRect.bottom}px`
- dropDownMenuRef.current.style.left = `${actionMenuRect.left -
- (dropDownMenuRect.width - offset)}px`
+ dropDownMenuRef.current.style.left = `${
+ actionMenuRect.left - (dropDownMenuRect.width - offset)
+ }px`
}
}
@@ -128,7 +122,11 @@ const ActionsMenu = ({ dataItem, menu, time }) => {
setIsShowMenu(false)}
+ onClick={event => {
+ setIsShowMenu(false)
+ setRenderMenu(false)
+ event.stopPropagation()
+ }}
ref={dropDownMenuRef}
>
{actionMenu.map(
diff --git a/src/components/ProjectsPage/Projects.js b/src/components/ProjectsPage/Projects.js
index 7e9b9b000..e116401ec 100644
--- a/src/components/ProjectsPage/Projects.js
+++ b/src/components/ProjectsPage/Projects.js
@@ -28,14 +28,14 @@ import ProjectsView from './ProjectsView'
import {
generateProjectActionsMenu,
- successProjectDeletingMessage,
+ handleDeleteProjectError,
projectsSortOptions,
- handleDeleteProjectError
+ successProjectDeletingMessage
} from './projectsData'
import nuclioActions from '../../actions/nuclio'
-import { setNotification } from '../../reducers/notificationReducer'
import projectsAction from '../../actions/projects'
import { DANGER_BUTTON, FORBIDDEN_ERROR_STATUS_CODE, PRIMARY_BUTTON } from 'igz-controls/constants'
+import { setNotification } from '../../reducers/notificationReducer'
import { useNuclioMode } from '../../hooks/nuclioMode.hook'
@@ -44,6 +44,7 @@ const Projects = ({
createNewProject,
deleteProject,
fetchNuclioFunctions,
+ fetchProject,
fetchProjects,
fetchProjectsNames,
fetchProjectsSummary,
@@ -72,6 +73,10 @@ const Projects = ({
const { isNuclioModeDisabled } = useNuclioMode()
+ const fetchMinimalProjects = useCallback(() => {
+ fetchProjects({ format: 'minimal' })
+ }, [fetchProjects])
+
const isValidProjectState = useCallback(
project => {
return (
@@ -106,12 +111,12 @@ const Projects = ({
}
removeProjects()
- fetchProjects()
+ fetchMinimalProjects()
fetchProjectsNames()
fetchProjectsSummary(source.token)
}, [
fetchNuclioFunctions,
- fetchProjects,
+ fetchMinimalProjects,
fetchProjectsNames,
fetchProjectsSummary,
isNuclioModeDisabled,
@@ -135,7 +140,7 @@ const Projects = ({
project => {
changeProjectState(project.metadata.name, 'archived')
.then(() => {
- fetchProjects()
+ fetchMinimalProjects()
})
.catch(error => {
dispatch(
@@ -152,7 +157,7 @@ const Projects = ({
})
setConfirmData(null)
},
- [changeProjectState, dispatch, fetchProjects]
+ [changeProjectState, dispatch, fetchMinimalProjects]
)
const handleDeleteProject = useCallback(
@@ -160,7 +165,7 @@ const Projects = ({
setConfirmData(null)
deleteProject(project.metadata.name, deleteNonEmpty)
.then(() => {
- fetchProjects()
+ fetchMinimalProjects()
dispatch(
setNotification({
status: 200,
@@ -180,16 +185,16 @@ const Projects = ({
)
})
},
- [deleteProject, dispatch, fetchProjects]
+ [deleteProject, dispatch, fetchMinimalProjects]
)
const handleUnarchiveProject = useCallback(
project => {
changeProjectState(project.metadata.name, 'online').then(() => {
- fetchProjects()
+ fetchMinimalProjects()
})
},
- [changeProjectState, fetchProjects]
+ [changeProjectState, fetchMinimalProjects]
)
const convertToYaml = useCallback(
@@ -239,11 +244,37 @@ const Projects = ({
[handleDeleteProject]
)
+ const viewYaml = useCallback(
+ projectMinimal => {
+ if (projectMinimal?.metadata?.name) {
+ fetchProject(projectMinimal.metadata.name)
+ .then(project => {
+ convertToYaml(project)
+ })
+ .catch(() => {
+ setConvertedYaml('')
+
+ dispatch(
+ setNotification({
+ status: 400,
+ id: Math.random(),
+ retry: () => viewYaml(projectMinimal),
+ message: "Failed to fetch project's YAML"
+ })
+ )
+ })
+ } else {
+ setConvertedYaml('')
+ }
+ },
+ [convertToYaml, dispatch, fetchProject]
+ )
+
useEffect(() => {
setActionsMenu(
generateProjectActionsMenu(
projectStore.projects,
- convertToYaml,
+ viewYaml,
onArchiveProject,
handleUnarchiveProject,
onDeleteProject
@@ -254,7 +285,8 @@ const Projects = ({
onArchiveProject,
onDeleteProject,
handleUnarchiveProject,
- projectStore.projects
+ projectStore.projects,
+ viewYaml
])
useEffect(() => {
@@ -262,12 +294,12 @@ const Projects = ({
fetchNuclioFunctions()
}
- fetchProjects()
+ fetchMinimalProjects()
fetchProjectsNames()
fetchProjectsSummary(source.token)
}, [
+ fetchMinimalProjects,
fetchNuclioFunctions,
- fetchProjects,
fetchProjectsNames,
fetchProjectsSummary,
isNuclioModeDisabled,
diff --git a/src/components/ProjectsPage/ProjectsView.js b/src/components/ProjectsPage/ProjectsView.js
index b157b5434..7d1afe337 100644
--- a/src/components/ProjectsPage/ProjectsView.js
+++ b/src/components/ProjectsPage/ProjectsView.js
@@ -78,7 +78,7 @@ const ProjectsView = ({
return (
- {projectStore.loading &&
}
+ {(projectStore.loading || projectStore.project.loading) &&
}
{createProject && (
,
- onClick: toggleConvertToYaml
+ onClick: viewYaml
},
{
label: 'Archive',
diff --git a/src/elements/ActionMenuItem/ActionsMenuItem.js b/src/elements/ActionMenuItem/ActionsMenuItem.js
index f51649957..a0b06bc74 100644
--- a/src/elements/ActionMenuItem/ActionsMenuItem.js
+++ b/src/elements/ActionMenuItem/ActionsMenuItem.js
@@ -44,8 +44,11 @@ const ActionsMenuItem = ({ dataItem, isIconDisplayed, menuItem }) => {
{
- event.stopPropagation()
- !menuItem.disabled && menuItem.onClick(dataItem)
+ if (!menuItem.disabled) {
+ menuItem.onClick(dataItem)
+ } else {
+ event.stopPropagation()
+ }
}}
>
{menuItem.label}
From 93ff0046dcd7497f1e53711e4d4aaa5ae8062365 Mon Sep 17 00:00:00 2001
From: Ilank <63646693+ilan7empest@users.noreply.github.com>
Date: Tue, 9 May 2023 12:35:59 +0300
Subject: [PATCH 09/11] Fix [Models] UI crash on attempt to open Models screen
`1.3.x` (#1707)
---
src/components/Table/table.scss | 2 +-
src/utils/createArtifactsContent.js | 26 ++++++++++++++------------
2 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss
index 80eaa8005..1e7eba9d1 100644
--- a/src/components/Table/table.scss
+++ b/src/components/Table/table.scss
@@ -325,7 +325,7 @@
&:first-child {
position: sticky;
top: 50px;
- z-index: 1;
+ z-index: 3;
background-color: $white;
.table-body__cell {
diff --git a/src/utils/createArtifactsContent.js b/src/utils/createArtifactsContent.js
index 17f6de1b2..a6a33cb1a 100644
--- a/src/utils/createArtifactsContent.js
+++ b/src/utils/createArtifactsContent.js
@@ -104,11 +104,17 @@ const createArtifactsRowData = artifact => {
}
}
+const getIter = artifact => (isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`)
+const getIsTargetPathValid = (artifact, frontendSpec) =>
+ frontendSpec?.allowed_artifact_path_prefixes_list
+ ? frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
+ return artifact.target_path?.startsWith?.(prefix)
+ })
+ : false
+
export const createModelsRowData = (artifact, project, frontendSpec, showExpandButton) => {
- const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
- const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
- return artifact.target_path?.startsWith?.(prefix)
- })
+ const iter = getIter(artifact)
+ const isTargetPathValid = getIsTargetPathValid(artifact, frontendSpec)
return {
data: {
@@ -242,10 +248,8 @@ export const createModelsRowData = (artifact, project, frontendSpec, showExpandB
}
export const createFilesRowData = (artifact, project, frontendSpec, showExpandButton) => {
- const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
- const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
- return artifact.target_path?.startsWith?.(prefix)
- })
+ const iter = getIter(artifact)
+ const isTargetPathValid = getIsTargetPathValid(artifact, frontendSpec)
return {
data: {
@@ -488,10 +492,8 @@ export const createModelEndpointsRowData = (artifact, project) => {
}
export const createDatasetsRowData = (artifact, project, frontendSpec, showExpandButton) => {
- const iter = isNaN(parseInt(artifact?.iter)) ? '' : ` #${artifact?.iter}`
- const isTargetPathValid = frontendSpec.allowed_artifact_path_prefixes_list.some(prefix => {
- return artifact.target_path?.startsWith?.(prefix)
- })
+ const iter = getIter(artifact)
+ const isTargetPathValid = getIsTargetPathValid(artifact, frontendSpec)
return {
data: {
From a03a756ce158f6ae643111f6862ccabb4fb3f186 Mon Sep 17 00:00:00 2001
From: mariana-furyk
Date: Wed, 17 May 2023 11:33:38 +0300
Subject: [PATCH 10/11] Fix [Functions] nuclio function does shown the current
function image
---
src/constants.js | 1 +
src/utils/createFunctionsContent.js | 13 +++++++++++--
src/utils/parseFunction.js | 3 ++-
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/constants.js b/src/constants.js
index 418f04e17..9e707bbec 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -182,6 +182,7 @@ export const FETCH_FUNCTION_TEMPLATE_SUCCESS = 'FETCH_FUNCTION_TEMPLATE_SUCCESS'
export const FUNCTION_TYPE_JOB = 'job'
export const FUNCTION_TYPE_LOCAL = 'local'
export const FUNCTION_TYPE_SERVING = 'serving'
+export const FUNCTION_TYPE_NUCLIO = 'nuclio'
export const GET_FUNCTION_BEGIN = 'GET_FUNCTION_BEGIN'
export const GET_FUNCTION_FAILURE = 'GET_FUNCTION_FAILURE'
export const GET_FUNCTION_SUCCESS = 'GET_FUNCTION_SUCCESS'
diff --git a/src/utils/createFunctionsContent.js b/src/utils/createFunctionsContent.js
index e7eb8327e..5d876fa63 100644
--- a/src/utils/createFunctionsContent.js
+++ b/src/utils/createFunctionsContent.js
@@ -18,7 +18,13 @@ under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import { formatDatetime } from './datetime'
-import { FUNCTIONS_PAGE, MODELS_PAGE, REAL_TIME_PIPELINES_TAB } from '../constants'
+import {
+ FUNCTION_TYPE_NUCLIO,
+ FUNCTION_TYPE_SERVING,
+ FUNCTIONS_PAGE,
+ MODELS_PAGE,
+ REAL_TIME_PIPELINES_TAB
+} from '../constants'
import { generateLinkToDetailsPanel } from './generateLinkToDetailsPanel'
const createFunctionsContent = (functions, pageTab, projectName, showExpandButton) =>
@@ -140,7 +146,10 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto
id: `image.${func.ui.identifierUnique}`,
headerId: 'image',
headerLabel: 'Image',
- value: func.image,
+ value:
+ func.type === FUNCTION_TYPE_NUCLIO || func.type === FUNCTION_TYPE_SERVING
+ ? func.container_image
+ : func.image,
class: 'table-cell-1'
},
{
diff --git a/src/utils/parseFunction.js b/src/utils/parseFunction.js
index 53658daae..bb6774504 100644
--- a/src/utils/parseFunction.js
+++ b/src/utils/parseFunction.js
@@ -28,6 +28,7 @@ export const parseFunction = (func, projectName, customState) => {
base_spec: func.spec?.base_spec ?? {},
build: func.spec?.build ?? {},
command: func.spec?.command,
+ container_image: func?.status?.container_image ?? '',
default_class: func.spec?.default_class ?? '',
default_handler: func.spec?.default_handler ?? '',
description: func.spec?.description ?? '',
@@ -51,7 +52,7 @@ export const parseFunction = (func, projectName, customState) => {
type: func.kind,
volume_mounts: func.spec?.volume_mounts ?? [],
volumes: func.spec?.volumes ?? [],
- updated: new Date(func.metadata?.updated ?? ''),
+ updated: new Date(func.metadata?.updated ?? '')
}
item.ui = {
From 21869c19681d46cbeed73e26545691122f554010 Mon Sep 17 00:00:00 2001
From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com>
Date: Thu, 25 May 2023 13:02:33 +0300
Subject: [PATCH 11/11] Fix [Functions] nuclio remote function doesn't show the
current function image `1.3.x` (#1738)
---
src/constants.js | 1 +
src/utils/createFunctionsContent.js | 5 ++++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/constants.js b/src/constants.js
index 9e707bbec..46eb6bc34 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -183,6 +183,7 @@ export const FUNCTION_TYPE_JOB = 'job'
export const FUNCTION_TYPE_LOCAL = 'local'
export const FUNCTION_TYPE_SERVING = 'serving'
export const FUNCTION_TYPE_NUCLIO = 'nuclio'
+export const FUNCTION_TYPE_REMOTE = 'remote'
export const GET_FUNCTION_BEGIN = 'GET_FUNCTION_BEGIN'
export const GET_FUNCTION_FAILURE = 'GET_FUNCTION_FAILURE'
export const GET_FUNCTION_SUCCESS = 'GET_FUNCTION_SUCCESS'
diff --git a/src/utils/createFunctionsContent.js b/src/utils/createFunctionsContent.js
index 5d876fa63..0b4a0e622 100644
--- a/src/utils/createFunctionsContent.js
+++ b/src/utils/createFunctionsContent.js
@@ -20,6 +20,7 @@ such restriction.
import { formatDatetime } from './datetime'
import {
FUNCTION_TYPE_NUCLIO,
+ FUNCTION_TYPE_REMOTE,
FUNCTION_TYPE_SERVING,
FUNCTIONS_PAGE,
MODELS_PAGE,
@@ -147,7 +148,9 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto
headerId: 'image',
headerLabel: 'Image',
value:
- func.type === FUNCTION_TYPE_NUCLIO || func.type === FUNCTION_TYPE_SERVING
+ func.type === FUNCTION_TYPE_NUCLIO ||
+ func.type === FUNCTION_TYPE_SERVING ||
+ func.type === FUNCTION_TYPE_REMOTE
? func.container_image
: func.image,
class: 'table-cell-1'