From 1f2ac90f7086f9311f0373584a6b15cb2cb6e68c Mon Sep 17 00:00:00 2001 From: Dipanshu Gupta Date: Tue, 18 Jun 2024 16:08:01 +0530 Subject: [PATCH 01/14] Very long pipeline names make pipelines list view scroll columns off screen --- .../pipelinesDetails/PipelineDetailsTitle.tsx | 6 +++-- .../pipeline/PipelineDetails.tsx | 4 +++- .../pipelineRun/PipelineRunDetails.tsx | 2 +- .../tables/experiment/ExperimentTableRow.tsx | 5 ++--- .../tables/pipeline/PipelinesTableRow.tsx | 4 ++-- .../PipelineVersionTableRow.tsx | 22 ++++++++++--------- .../global/runs/GlobalPipelineVersionRuns.tsx | 4 ++-- 7 files changed, 26 insertions(+), 21 deletions(-) diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx index 7a02caa2df..c114644f6b 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Label, Split, SplitItem } from '@patternfly/react-core'; +import { Label, Split, SplitItem, Truncate } from '@patternfly/react-core'; import { PipelineRunKFv2, StorageStateKF } from '~/concepts/pipelines/kfTypes'; import { computeRunStatus } from '~/concepts/pipelines/content/utils'; import PipelineRunTypeLabel from '~/concepts/pipelines/content/PipelineRunTypeLabel'; @@ -22,7 +22,9 @@ const PipelineDetailsTitle: React.FC = ({ return ( <> - {run.display_name} + + + {pipelineRunLabel && ( diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx index 78bf3552b5..43fa1888b4 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx @@ -98,7 +98,9 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) = breadcrumb={ {breadcrumbPath} - {pipeline?.display_name || 'Loading...'} + + + diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx index 363397d69f..b4f86b7d77 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx @@ -138,7 +138,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, version.pipeline_version_id, )} > - {version.display_name} + ) : ( 'Loading...' diff --git a/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx b/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx index 1ca7ac4cfb..901d5987e8 100644 --- a/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx +++ b/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; - import { ActionsColumn, IAction, Td, Tr } from '@patternfly/react-table'; - +import { Truncate } from '@patternfly/react-core'; import { ExperimentKFv2, StorageStateKF } from '~/concepts/pipelines/kfTypes'; import { CheckboxTd } from '~/components/table'; import { experimentRunsRoute } from '~/routes'; @@ -36,7 +35,7 @@ const ExperimentTableRow: React.FC = ({ }`} state={{ experiment }} > - {experiment.display_name} + {experiment.description} diff --git a/frontend/src/concepts/pipelines/content/tables/pipeline/PipelinesTableRow.tsx b/frontend/src/concepts/pipelines/content/tables/pipeline/PipelinesTableRow.tsx index 4114e00370..2cd996c41a 100644 --- a/frontend/src/concepts/pipelines/content/tables/pipeline/PipelinesTableRow.tsx +++ b/frontend/src/concepts/pipelines/content/tables/pipeline/PipelinesTableRow.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Td, Tbody, Tr, ActionsColumn } from '@patternfly/react-table'; +import { Td, Tbody, Tr, ActionsColumn, TableText } from '@patternfly/react-table'; import { useNavigate } from 'react-router-dom'; import { Skeleton } from '@patternfly/react-core'; import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes'; @@ -84,7 +84,7 @@ const PipelinesTableRow: React.FC = ({ /> {pipeline.display_name}} description={pipeline.description} descriptionAsMarkdown /> diff --git a/frontend/src/concepts/pipelines/content/tables/pipelineVersion/PipelineVersionTableRow.tsx b/frontend/src/concepts/pipelines/content/tables/pipelineVersion/PipelineVersionTableRow.tsx index afbf7404ab..e2a323e975 100644 --- a/frontend/src/concepts/pipelines/content/tables/pipelineVersion/PipelineVersionTableRow.tsx +++ b/frontend/src/concepts/pipelines/content/tables/pipelineVersion/PipelineVersionTableRow.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { ActionsColumn, Td, Tr } from '@patternfly/react-table'; +import { ActionsColumn, TableText, Td, Tr } from '@patternfly/react-table'; import { Link, useNavigate } from 'react-router-dom'; import { PipelineKFv2, PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes'; import { CheckboxTd, TableRowTitleDescription } from '~/components/table'; @@ -53,15 +53,17 @@ const PipelineVersionTableRow: React.FC = ({ - {version.display_name} - + + + {version.display_name} + + } description={version.description} descriptionAsMarkdown diff --git a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx index c6c634b163..256dce6f7c 100644 --- a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx +++ b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Link, useParams } from 'react-router-dom'; -import { Breadcrumb, BreadcrumbItem } from '@patternfly/react-core'; +import { Breadcrumb, BreadcrumbItem, Truncate } from '@patternfly/react-core'; import ApplicationsPage from '~/pages/ApplicationsPage'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { PipelineCoreDetailsPageComponent } from '~/concepts/pipelines/content/types'; @@ -49,7 +49,7 @@ const GlobalPipelineVersionRuns: PipelineCoreDetailsPageComponent = ({ breadcrum {breadcrumbPath} - {pipelineVersion?.display_name || 'Loading...'} + Runs From 75e082bad96ce1e0d937de0a04e681356dfeda68 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Tue, 18 Jun 2024 14:50:49 -0500 Subject: [PATCH 02/14] update double params to be fixed at 1 Refactor number input handling to display whole numbers as x.0 --- .../tests/mocked/pipelines/pipelineCreateRuns.cy.ts | 4 ++-- .../ParamsSection/NumberInputParam.tsx | 12 ++++++++++-- .../pipelineRun/SelectedNodeInputOutputTab.tsx | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelineCreateRuns.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelineCreateRuns.cy.ts index 7d361cf97d..4e270a2e84 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelineCreateRuns.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelineCreateRuns.cy.ts @@ -267,7 +267,7 @@ describe('Pipeline create runs', () => { }, double_param: { parameterType: InputDefinitionParameterType.DOUBLE, - defaultValue: 0.1, + defaultValue: 7.0, description: 'Some double helper text', isOptional: true, }, @@ -313,7 +313,7 @@ describe('Pipeline create runs', () => { paramsSection.findParamById('string_param').should('have.value', 'String default value'); cy.findByTestId('string_param-helper-text').should('have.text', 'Some string helper text'); - paramsSection.findParamById('double_param').should('have.value', '0.1'); + paramsSection.findParamById('double_param').should('have.value', '7.0'); cy.findByTestId('double_param-form-group').should('not.have.text', '*', { exact: false }); cy.findByTestId('double_param-helper-text').should('have.text', 'Some double helper text'); diff --git a/frontend/src/concepts/pipelines/content/createRun/contentSections/ParamsSection/NumberInputParam.tsx b/frontend/src/concepts/pipelines/content/createRun/contentSections/ParamsSection/NumberInputParam.tsx index 04d1b98a6f..fb44f00307 100644 --- a/frontend/src/concepts/pipelines/content/createRun/contentSections/ParamsSection/NumberInputParam.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/contentSections/ParamsSection/NumberInputParam.tsx @@ -1,5 +1,5 @@ import { TextInput } from '@patternfly/react-core'; -import React from 'react'; +import React, { useRef } from 'react'; import NumberInputWrapper from '~/components/NumberInputWrapper'; import { InputParamProps } from './types'; @@ -15,16 +15,24 @@ export const NumberInputParam: React.FC = ({ const [value, setValue] = React.useState( inputProps.value !== '' ? Number(inputProps.value) : '', ); + const isDefault = useRef(true); if (isFloat) { + // if the default value is a whole number, display it as x.0 + const displayValue = + typeof value === 'number' && Number.isInteger(value) && isDefault.current + ? value.toFixed(1) + : value; + return ( { + isDefault.current = false; setValue(typeof newValue === 'string' ? parseFloat(newValue) : newValue); onChange(event, newValue); }} diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/SelectedNodeInputOutputTab.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/SelectedNodeInputOutputTab.tsx index 3bd6701fc4..6ba2dec35d 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/SelectedNodeInputOutputTab.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/SelectedNodeInputOutputTab.tsx @@ -48,6 +48,9 @@ const SelectedNodeInputOutputTab: React.FC = ({ switch (type) { case InputDefinitionParameterType.DOUBLE: + return numberValue && Number.isInteger(numberValue) + ? numberValue.toFixed(1) + : numberValue; case InputDefinitionParameterType.INTEGER: return numberValue; case InputDefinitionParameterType.BOOLEAN: From 855bcd15893b23c0aca3e8b9f561800cc7e04352 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Tue, 18 Jun 2024 16:29:59 -0500 Subject: [PATCH 03/14] remove artifact preview --- .../artifacts/ArtifactNodeDetails.tsx | 11 +-- .../taskDetails/ArtifactPreview.tsx | 74 ------------------- .../taskDetails/TaskDetailsInputOutput.tsx | 2 - .../taskDetails/TaskDetailsPrintKeyValues.tsx | 3 +- 4 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/ArtifactPreview.tsx diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx index cda7ec68e1..676b3733de 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx @@ -10,7 +10,6 @@ import { DescriptionListGroup, DescriptionListTerm, DescriptionListDescription, - StackItem, } from '@patternfly/react-core'; import { Artifact } from '~/third_party/mlmd'; @@ -21,7 +20,6 @@ import PipelinesTableRowTime from '~/concepts/pipelines/content/tables/Pipelines import PipelineRunDrawerRightContent from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent'; import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas'; import { ArtifactUriLink } from '~/concepts/pipelines/content/artifacts/ArtifactUriLink'; -import ArtifactPreview from '~/concepts/pipelines/content/pipelinesDetails/taskDetails/ArtifactPreview'; type ArtifactNodeDetailsProps = Pick< React.ComponentProps, @@ -90,14 +88,7 @@ export const ArtifactNodeDetails: React.FC = ({ {artifactName} - - - - - - - - + diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/ArtifactPreview.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/ArtifactPreview.tsx deleted file mode 100644 index 2392ff8457..0000000000 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/ArtifactPreview.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import { Bullseye, CodeBlock, CodeBlockCode, Spinner } from '@patternfly/react-core'; -import { useIsAreaAvailable, SupportedArea } from '~/concepts/areas'; -import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { Artifact } from '~/third_party/mlmd'; -import { extractS3UriComponents } from '~/concepts/pipelines/content/artifacts/utils'; -import { fetchStorageObject } from '~/services/storageService'; - -type ArtifactPreviewProps = { - artifact: Artifact; - maxBytes?: number; - maxLines?: number; -}; - -const ArtifactPreview: React.FC = ({ - artifact, - maxBytes = 255, - maxLines = 4, -}) => { - const isS3EndpointAvailable = useIsAreaAvailable(SupportedArea.S3_ENDPOINT).status; - const { namespace } = usePipelinesAPI(); - const [preview, setPreview] = React.useState(null); - const [isLoading, setIsLoading] = React.useState(false); - - React.useEffect(() => { - const uri = artifact.getUri(); - if (!uri || !isS3EndpointAvailable) { - return; - } - - setPreview(null); - const uriComponents = extractS3UriComponents(uri); - if (!uriComponents) { - return; - } - setIsLoading(true); - fetchStorageObject(namespace, uriComponents.path, maxBytes) - .catch(() => null) - .then((text) => setPreview(text)) - .finally(() => setIsLoading(false)); - }, [artifact, isS3EndpointAvailable, maxBytes, namespace]); - - if (isLoading) { - return ( - - - - ); - } - - if (!preview) { - return null; - } - - // Try to parse the preview as JSON - let code = preview; - try { - code = JSON.parse(preview); - code = JSON.stringify(code, null, 2); - } catch { - // ignore - } - - code = code.split('\n').slice(0, maxLines).join('\n').trim(); - code = `${code}...`; - - return ( - - {code} - - ); -}; - -export default ArtifactPreview; diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx index 911ae667aa..53b4890d44 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx @@ -4,7 +4,6 @@ import TaskDetailsSection from '~/concepts/pipelines/content/pipelinesDetails/ta import TaskDetailsPrintKeyValues from '~/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsPrintKeyValues'; import { PipelineTaskArtifact } from '~/concepts/pipelines/topology'; import { ArtifactUriLink } from '~/concepts/pipelines/content/artifacts/ArtifactUriLink'; -import ArtifactPreview from './ArtifactPreview'; type TaskDetailsInputOutputProps = { type: 'Input' | 'Output'; @@ -29,7 +28,6 @@ const TaskDetailsInputOutput: React.FC = ({ return { label: artifactInputOutput.label, value: , - preview: , }; } diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsPrintKeyValues.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsPrintKeyValues.tsx index b5935bc144..5ecd331b4b 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsPrintKeyValues.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsPrintKeyValues.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Grid, GridItem, Truncate } from '@patternfly/react-core'; type TaskDetailsPrintKeyValuesProps = { - items: { label: string; value: React.ReactNode; preview?: React.ReactNode }[]; + items: { label: string; value: React.ReactNode }[]; }; const TaskDetailsPrintKeyValues: React.FC = ({ items }) => ( @@ -15,7 +15,6 @@ const TaskDetailsPrintKeyValues: React.FC = ({ i {result.value} - {result.preview && {result.preview}} ))} From 85e272c9e51bae959f32f40974e0ee4619967b23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:49:39 +0000 Subject: [PATCH 04/14] Bump ws from 7.5.9 to 7.5.10 in /frontend Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7814bcc5d2..549f0934fa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23400,9 +23400,9 @@ } }, "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -23968,9 +23968,9 @@ } }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "devOptional": true, "engines": { "node": ">=10.0.0" From a7e7bfa050fd6a7e4fbc38b3ff7d0534c8afceb1 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Tue, 18 Jun 2024 11:42:09 -0400 Subject: [PATCH 05/14] add breadcrumb navigation back to schedules tab for schedules --- .../pipelinesDetails/pipelineRun/PipelineRunDetails.tsx | 4 ++++ .../pipelineRunJob/PipelineRunJobDetails.tsx | 1 + .../pages/pipelines/global/GlobalPipelineCoreDetails.tsx | 9 ++++++++- frontend/src/routes/pipelines/experiments.ts | 7 ++++++- frontend/src/routes/pipelines/global.ts | 7 ++++++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx index b4f86b7d77..4cb6d7381a 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx @@ -33,6 +33,7 @@ import PipelineJobReferenceName from '~/concepts/pipelines/content/PipelineJobRe import useExecutionsForPipelineRun from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/useExecutionsForPipelineRun'; import { useGetEventsByExecutionIds } from '~/concepts/pipelines/apiHooks/mlmd/useGetEventsByExecutionId'; import { PipelineTopology } from '~/concepts/topology'; +import { StorageStateKF } from '~/concepts/pipelines/kfTypes'; import { usePipelineRunArtifacts } from './artifacts'; import { PipelineRunDetailsTabs } from './PipelineRunDetailsTabs'; @@ -136,6 +137,9 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, namespace, version.pipeline_id, version.pipeline_version_id, + run?.storage_state === StorageStateKF.ARCHIVED + ? PipelineRunType.ARCHIVED + : undefined, )} > diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx index a07f9a8834..611a81906f 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx @@ -102,6 +102,7 @@ const PipelineRunJobDetails: PipelineCoreDetailsPageComponent = ({ namespace, version.pipeline_id, version.pipeline_version_id, + PipelineRunType.SCHEDULED, )} > {version.display_name} diff --git a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx index 1b92deb875..ceefea5c70 100644 --- a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx +++ b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx @@ -8,6 +8,7 @@ import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability'; import { experimentRunsRoute, experimentSchedulesRoute, experimentsBaseRoute } from '~/routes'; import EnsureCompatiblePipelineServer from '~/concepts/pipelines/EnsureCompatiblePipelineServer'; import { ExperimentRunsContext } from '~/pages/pipelines/global/experiments/ExperimentRunsContext'; +import { PipelineRunType } from '~/pages/pipelines/global/runs'; type GlobalPipelineCoreDetailsProps = { pageName: string; @@ -64,7 +65,13 @@ export const GlobalExperimentDetails: React.FC< , {experiment?.display_name ? ( - + {experiment.display_name} ) : ( diff --git a/frontend/src/routes/pipelines/experiments.ts b/frontend/src/routes/pipelines/experiments.ts index 9f268a20b2..7af7f5b2b2 100644 --- a/frontend/src/routes/pipelines/experiments.ts +++ b/frontend/src/routes/pipelines/experiments.ts @@ -1,3 +1,5 @@ +import { PipelineRunType } from '~/pages/pipelines/global/runs'; + export const experimentsRootPath = '/experiments'; export const globExperimentsAll = `${experimentsRootPath}/*`; @@ -34,10 +36,13 @@ export const experimentsCloneScheduleRoute = ( export const experimentRunsRoute = ( namespace: string | undefined, experimentId: string | undefined, + runType?: PipelineRunType, ): string => !experimentId ? experimentsBaseRoute(namespace) - : `${experimentsBaseRoute(namespace)}/${experimentId}/runs`; + : `${experimentsBaseRoute(namespace)}/${experimentId}/runs${ + runType ? `?runType=${runType}` : '' + }`; export const experimentSchedulesRoute = ( namespace: string | undefined, diff --git a/frontend/src/routes/pipelines/global.ts b/frontend/src/routes/pipelines/global.ts index 685b43b8d0..32fa3276b2 100644 --- a/frontend/src/routes/pipelines/global.ts +++ b/frontend/src/routes/pipelines/global.ts @@ -1,3 +1,5 @@ +import { PipelineRunType } from '~/pages/pipelines/global/runs'; + const globNamespace = ':namespace'; export const globNamespaceAll = `/${globNamespace}?/*`; @@ -31,8 +33,11 @@ export const routePipelineVersionRunsNamespace = ( namespace: string, pipelineId: string, versionId: string, + runType?: PipelineRunType, ): string => - `${routePipelinesNamespace(namespace)}/${routePipelineVersionRuns(pipelineId, versionId)}`; + `${routePipelinesNamespace(namespace)}/${routePipelineVersionRuns(pipelineId, versionId)}${ + runType ? `?runType=${runType}` : '' + }`; export const routePipelineRunCreateNamespacePipelinesPage = (namespace?: string): string => `${routePipelinesNamespace(namespace)}/${globPipelineRunCreate}`; export const routePipelineRunCloneNamespacePipelinesPage = ( From 9d4c189750a8589b2fce78c7fd28db5b2500377d Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Tue, 18 Jun 2024 13:54:29 -0400 Subject: [PATCH 06/14] refactor breadcrumb for experiments to navigate to correct tabs --- .../pipelines/content/createRun/CloneRunPage.tsx | 2 +- .../pipelines/content/createRun/CreateRunPage.tsx | 4 +++- .../pipelinesDetails/pipeline/PipelineDetails.tsx | 4 ++-- .../pipelineRun/PipelineRunDetails.tsx | 9 +++++---- .../pipelineRunJob/PipelineRunJobDetails.tsx | 2 +- frontend/src/concepts/pipelines/content/types.ts | 3 ++- .../pipelines/global/GlobalPipelineCoreDetails.tsx | 13 +++---------- .../artifacts/ArtifactDetails/ArtifactDetails.tsx | 2 +- .../experiments/compareRuns/CompareRunsPage.tsx | 2 +- .../executions/details/ExecutionDetails.tsx | 2 +- .../global/runs/GlobalPipelineVersionRuns.tsx | 4 ++-- .../pipelines/ProjectPipelineBreadcrumbPage.tsx | 2 +- frontend/src/routes/pipelines/experiments.ts | 2 +- 13 files changed, 24 insertions(+), 27 deletions(-) diff --git a/frontend/src/concepts/pipelines/content/createRun/CloneRunPage.tsx b/frontend/src/concepts/pipelines/content/createRun/CloneRunPage.tsx index faf0c58670..7996a4a868 100644 --- a/frontend/src/concepts/pipelines/content/createRun/CloneRunPage.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/CloneRunPage.tsx @@ -21,7 +21,7 @@ const CloneRunPage: React.FC = ({ breadcrumbPath, contextPath }) => { title={title} breadcrumb={ - {breadcrumbPath} + {breadcrumbPath(runType)} {run ? `Duplicate of ${run.display_name}` : 'Duplicate'} diff --git a/frontend/src/concepts/pipelines/content/createRun/CreateRunPage.tsx b/frontend/src/concepts/pipelines/content/createRun/CreateRunPage.tsx index 3930ac2091..1ce7f64b25 100644 --- a/frontend/src/concepts/pipelines/content/createRun/CreateRunPage.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/CreateRunPage.tsx @@ -16,7 +16,9 @@ const CreateRunPage: React.FC = ({ breadcrumbPath, contextPath }) => title={title} breadcrumb={ - {breadcrumbPath} + {breadcrumbPath( + runType === PipelineRunType.SCHEDULED ? PipelineRunType.SCHEDULED : undefined, + )} {title} } diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx index 43fa1888b4..35b59ce72b 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx @@ -69,7 +69,7 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) = - {breadcrumbPath} + {breadcrumbPath()} {title} } @@ -97,7 +97,7 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) = - {breadcrumbPath} + {breadcrumbPath()} diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx index 4cb6d7381a..3836ed9107 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx @@ -93,6 +93,9 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, ); } + const runType = + run?.storage_state === StorageStateKF.ARCHIVED ? PipelineRunType.ARCHIVED : undefined; + return ( <> @@ -129,7 +132,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, loadError={error} breadcrumb={ - {breadcrumbPath} + {breadcrumbPath(runType)} {version ? ( diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx index 611a81906f..abb5335da2 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRunJob/PipelineRunJobDetails.tsx @@ -94,7 +94,7 @@ const PipelineRunJobDetails: PipelineCoreDetailsPageComponent = ({ loadError={error} breadcrumb={ - {breadcrumbPath} + {breadcrumbPath(PipelineRunType.SCHEDULED)} {version ? ( []; + breadcrumbPath: (runType?: PipelineRunType | null) => React.ReactElement[]; contextPath?: string; }; diff --git a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx index ceefea5c70..720f13bb32 100644 --- a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx +++ b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx @@ -8,7 +8,6 @@ import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability'; import { experimentRunsRoute, experimentSchedulesRoute, experimentsBaseRoute } from '~/routes'; import EnsureCompatiblePipelineServer from '~/concepts/pipelines/EnsureCompatiblePipelineServer'; import { ExperimentRunsContext } from '~/pages/pipelines/global/experiments/ExperimentRunsContext'; -import { PipelineRunType } from '~/pages/pipelines/global/runs'; type GlobalPipelineCoreDetailsProps = { pageName: string; @@ -27,7 +26,7 @@ const GlobalPipelineCoreDetails: React.FC = ({ [ ( @@ -57,7 +56,7 @@ export const GlobalExperimentDetails: React.FC< [ Experiments - {getProjectDisplayName(project)} @@ -65,13 +64,7 @@ export const GlobalExperimentDetails: React.FC< , {experiment?.display_name ? ( - + {experiment.display_name} ) : ( diff --git a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactDetails.tsx b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactDetails.tsx index 1ae310c802..f43a652aab 100644 --- a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactDetails.tsx +++ b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactDetails.tsx @@ -61,7 +61,7 @@ export const ArtifactDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPa loadError={artifactError} breadcrumb={ - {breadcrumbPath} + {breadcrumbPath()} diff --git a/frontend/src/pages/pipelines/global/experiments/compareRuns/CompareRunsPage.tsx b/frontend/src/pages/pipelines/global/experiments/compareRuns/CompareRunsPage.tsx index eaa594cfd8..5f48db21af 100644 --- a/frontend/src/pages/pipelines/global/experiments/compareRuns/CompareRunsPage.tsx +++ b/frontend/src/pages/pipelines/global/experiments/compareRuns/CompareRunsPage.tsx @@ -21,7 +21,7 @@ const CompareRunsPage: React.FC = ({ breadcrumbPath }) => { title="" breadcrumb={ - {breadcrumbPath} + {breadcrumbPath()} Compare runs } diff --git a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx index 1090db9ce6..51037b753f 100644 --- a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx +++ b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx @@ -96,7 +96,7 @@ const ExecutionDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, co loaded breadcrumb={ - {breadcrumbPath} + {breadcrumbPath()} {displayName} } diff --git a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx index 256dce6f7c..7f25baa5bc 100644 --- a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx +++ b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx @@ -29,7 +29,7 @@ const GlobalPipelineVersionRuns: PipelineCoreDetailsPageComponent = ({ breadcrum - {breadcrumbPath} + {breadcrumbPath()} {title} } @@ -46,7 +46,7 @@ const GlobalPipelineVersionRuns: PipelineCoreDetailsPageComponent = ({ breadcrum - {breadcrumbPath} + {breadcrumbPath()} diff --git a/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx b/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx index 37c751d9b6..da3400b369 100644 --- a/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx +++ b/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx @@ -19,7 +19,7 @@ const ProjectPipelineBreadcrumbPage: React.FC = ({ return ( [ Data Science Projects} diff --git a/frontend/src/routes/pipelines/experiments.ts b/frontend/src/routes/pipelines/experiments.ts index 7af7f5b2b2..56c8566320 100644 --- a/frontend/src/routes/pipelines/experiments.ts +++ b/frontend/src/routes/pipelines/experiments.ts @@ -36,7 +36,7 @@ export const experimentsCloneScheduleRoute = ( export const experimentRunsRoute = ( namespace: string | undefined, experimentId: string | undefined, - runType?: PipelineRunType, + runType?: PipelineRunType | null, ): string => !experimentId ? experimentsBaseRoute(namespace) From 8fa4f0eb030a2ff96fbcb7d1713aaf1bf3ca00a8 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Thu, 13 Jun 2024 10:05:48 -0400 Subject: [PATCH 07/14] Fix UX issues on archived experiment runs table --- .../pages/pipelines/pipelineRunTable.ts | 34 ++++++++++++++----- .../pipelineRun/PipelineRunTableRow.tsx | 7 +++- .../ExperimentRunsListBreadcrumb.tsx | 10 ++++-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunTable.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunTable.ts index 662c31ea89..31c8527789 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunTable.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunTable.ts @@ -10,15 +10,21 @@ class PipelineRunsRow extends TableRow { findColumnName(name: string) { return this.find().find(`[data-label=Name]`).contains(name); } +} - findColumnVersion(name: string) { - return this.find().find(`[data-label="Pipeline"]`).contains(name); +class PipelineRunTableRow extends PipelineRunsRow { + findKebabAction(name: string): Cypress.Chainable> { + this.find().findKebab().click(); + return cy.findByTestId('pipeline-run-table-row-actions').findByRole('menuitem', { name }); } +} +class PipelineRunJobTableRow extends PipelineRunsRow { findStatusSwitchByRowName() { return this.find().findByTestId('job-status-switch'); } } + class PipelineRunsTable { protected testId = ''; @@ -33,12 +39,6 @@ class PipelineRunsTable { return cy.findByTestId(this.testId); } - getRowByName(name: string) { - return new PipelineRunsRow(() => - this.find().find(`[data-label=Name]`).contains(name).parents('tr'), - ); - } - shouldRowNotBeVisible(name: string) { this.find() .parents() @@ -120,6 +120,12 @@ class ActiveRunsTable extends PipelineRunsTable { mockGetActiveRuns(runs: PipelineRunKFv2[], namespace: string, times?: number) { return this.mockGetRuns(runs, [], namespace, times); } + + getRowByName(name: string) { + return new PipelineRunTableRow(() => + this.find().find(`[data-label=Name]`).contains(name).parents('tr'), + ); + } } class ArchivedRunsTable extends PipelineRunsTable { constructor() { @@ -129,6 +135,12 @@ class ArchivedRunsTable extends PipelineRunsTable { mockGetArchivedRuns(runs: PipelineRunKFv2[], namespace: string, times?: number) { return this.mockGetRuns([], runs, namespace, times); } + + getRowByName(name: string) { + return new PipelineRunTableRow(() => + this.find().find(`[data-label=Name]`).contains(name).parents('tr'), + ); + } } class PipelineRunJobTable extends PipelineRunsTable { @@ -136,6 +148,12 @@ class PipelineRunJobTable extends PipelineRunsTable { super('schedules'); } + getRowByName(name: string) { + return new PipelineRunJobTableRow(() => + this.find().find(`[data-label=Name]`).contains(name).parents('tr'), + ); + } + findEmptyState() { return cy.findByTestId('schedules-empty-state'); } diff --git a/frontend/src/concepts/pipelines/content/tables/pipelineRun/PipelineRunTableRow.tsx b/frontend/src/concepts/pipelines/content/tables/pipelineRun/PipelineRunTableRow.tsx index c86d4b4dd3..e3750cb67f 100644 --- a/frontend/src/concepts/pipelines/content/tables/pipelineRun/PipelineRunTableRow.tsx +++ b/frontend/src/concepts/pipelines/content/tables/pipelineRun/PipelineRunTableRow.tsx @@ -22,6 +22,7 @@ import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas'; import { ArchiveRunModal } from '~/pages/pipelines/global/runs/ArchiveRunModal'; import PipelineRunTableRowExperiment from '~/concepts/pipelines/content/tables/pipelineRun/PipelineRunTableRowExperiment'; import { useContextExperimentArchived } from '~/pages/pipelines/global/experiments/ExperimentRunsContext'; +import { getDashboardMainContainer } from '~/utilities/utils'; type PipelineRunTableRowProps = { checkboxProps: Omit, 'id'>; @@ -161,7 +162,11 @@ const PipelineRunTableRow: React.FC = ({ {customCells} {hasRowActions && ( - + { const { experiment } = React.useContext(ExperimentRunsContext); + const displayName = experiment?.display_name || 'Loading...'; + return ( @@ -16,9 +18,13 @@ export const ExperimentRunsListBreadcrumb: React.FC = () => { - - {experiment?.storage_state === StorageStateKF.ARCHIVED && } + {/* A hack solution to get rid of the minWidth set on PF Truncate component + So we can show correct spacing between the title and the label + The min width is set to 12 characters: + https://github.com/patternfly/patternfly/blob/9499f0a70a18f51474285752a04928958d901829/src/patternfly/components/Truncate/truncate.scss#L4 */} + {displayName.length > 12 ? : <>{displayName}} + {experiment?.storage_state === StorageStateKF.ARCHIVED && } ); }; From 6a23b0196d6c30fa83f5064d492725954718717d Mon Sep 17 00:00:00 2001 From: Andrew Ballantyne Date: Wed, 19 Jun 2024 15:09:41 -0400 Subject: [PATCH 08/14] Upversion to v2.24.0 --- frontend/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/.env b/frontend/.env index 459f524fd9..f1796d3b53 100644 --- a/frontend/.env +++ b/frontend/.env @@ -8,4 +8,4 @@ ODH_FAVICON=odh-favicon.svg ODH_NOTEBOOK_REPO=opendatahub ########## Change this version with each dashboard release ########### -INTERNAL_DASHBOARD_VERSION=v2.23.0 +INTERNAL_DASHBOARD_VERSION=v2.24.0 From 1513643c0e647ed7efd38427df9e474950407375 Mon Sep 17 00:00:00 2001 From: manosnoam Date: Thu, 20 Jun 2024 15:11:13 +0300 Subject: [PATCH 09/14] Update references of docs.openshift.com to the latest OCP version Signed-off-by: manosnoam --- docs/dev-setup.md | 2 +- .../starburst-enterprise/starburstenterprise-app.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev-setup.md b/docs/dev-setup.md index eea4d051f8..947debb2f1 100644 --- a/docs/dev-setup.md +++ b/docs/dev-setup.md @@ -7,7 +7,7 @@ ODH requires the following to run: - [NodeJS and NPM](https://nodejs.org/) - Node recommended version -> `18.16.0` - NPM recommended version -> `9.6.7` -- [OpenShift CLI](https://docs.openshift.com/container-platform/4.12/cli_reference/openshift_cli/getting-started-cli.html) +- [OpenShift CLI](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/getting-started-cli.html) - [kustomize](https://github.com/kubernetes-sigs/kustomize) (if you need to do deployment) ### Additional tooling diff --git a/manifests/overlays/apps/apps-onprem/starburst-enterprise/starburstenterprise-app.yaml b/manifests/overlays/apps/apps-onprem/starburst-enterprise/starburstenterprise-app.yaml index b66bae1285..7aae61c2bb 100644 --- a/manifests/overlays/apps/apps-onprem/starburst-enterprise/starburstenterprise-app.yaml +++ b/manifests/overlays/apps/apps-onprem/starburst-enterprise/starburstenterprise-app.yaml @@ -30,7 +30,7 @@ spec: - Previously installed and configured Kubernetes, including access to **kubectl**. - An editor suitable for editing YAML files. - Your SEP (Starburst Enterprise) license file. - - The latest OpenShift Container Platform (OCP) client for your platform as described in the [OpenShift documentation](https://docs.openshift.com/container-platform/4.10/cli_reference/openshift_cli/getting-started-cli.html) and the **oc** executable copied into your path, usually **/usr/local/bin.** + - The latest OpenShift Container Platform (OCP) client for your platform as described in the [OpenShift documentation](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/getting-started-cli.html) and the **oc** executable copied into your path, usually **/usr/local/bin.** ## Installation From f0dc72551e65a467f248d7e1a70280f32f2f9376 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Thu, 20 Jun 2024 10:54:29 -0500 Subject: [PATCH 10/14] Cleanup S3 endpoint host in configurePipelinesServer utils --- .../cypress/tests/mocked/pipelines/pipelines.cy.ts | 2 +- .../configurePipelinesServer/__tests__/utils.spec.ts | 10 ++++++++++ .../content/configurePipelinesServer/utils.ts | 4 +++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelines.cy.ts index 0b72664277..1027f05a87 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/pipelines/pipelines.cy.ts @@ -126,7 +126,7 @@ describe('Pipelines', () => { dspVersion: 'v2', objectStorage: { externalStorage: { - host: 's3.amazonaws.com/', + host: 's3.amazonaws.com', scheme: 'https', bucket: 'sdsd', region: 'us-east-1', diff --git a/frontend/src/concepts/pipelines/content/configurePipelinesServer/__tests__/utils.spec.ts b/frontend/src/concepts/pipelines/content/configurePipelinesServer/__tests__/utils.spec.ts index 82da6d349d..b0b20d70b6 100644 --- a/frontend/src/concepts/pipelines/content/configurePipelinesServer/__tests__/utils.spec.ts +++ b/frontend/src/concepts/pipelines/content/configurePipelinesServer/__tests__/utils.spec.ts @@ -71,6 +71,16 @@ describe('configure pipeline server utils', () => { expect(spec.objectStorage.externalStorage?.host).toBe('s3.amazonaws.com'); }); + it('should cleanup S3 endpoint host', () => { + const secretsResponse = createSecretsResponse(); + const config = createPipelineServerConfig(); + config.objectStorage.newValue = [ + { key: AwsKeys.S3_ENDPOINT, value: 'https://s3.amazonaws.com/' }, + ]; + const spec = createDSPipelineResourceSpec(config, secretsResponse); + expect(spec.objectStorage.externalStorage?.host).toBe('s3.amazonaws.com'); + }); + it('should parse S3 endpoint without scheme', () => { const secretsResponse = createSecretsResponse(); const config = createPipelineServerConfig(); diff --git a/frontend/src/concepts/pipelines/content/configurePipelinesServer/utils.ts b/frontend/src/concepts/pipelines/content/configurePipelinesServer/utils.ts index 41584d343f..4741b6bba0 100644 --- a/frontend/src/concepts/pipelines/content/configurePipelinesServer/utils.ts +++ b/frontend/src/concepts/pipelines/content/configurePipelinesServer/utils.ts @@ -108,7 +108,7 @@ export const createDSPipelineResourceSpec = ( dspVersion: 'v2', objectStorage: { externalStorage: { - host: externalStorageHost, + host: cleanupEndpointHost(externalStorageHost), scheme: externalStorageScheme || 'https', bucket: awsRecord.AWS_S3_BUCKET || '', region: externalStorageRegion, @@ -157,3 +157,5 @@ export const getLabelName = (index: string): string => { const field = PIPELINE_AWS_FIELDS.find((currentField) => currentField.key === index); return field ? field.label : ''; }; + +const cleanupEndpointHost = (endpoint: string): string => endpoint.replace(/\/$/, '') || ''; From 97d5e3c1f1aada7ce7502462bbb3728d196ef159 Mon Sep 17 00:00:00 2001 From: Dipanshu Gupta Date: Fri, 21 Jun 2024 14:51:44 +0530 Subject: [PATCH 11/14] Pipeline system.ClassificationMetrics artifacts provide a uri but have no source in storage --- .../pipelines/content/artifacts/ArtifactUriLink.tsx | 7 +++++-- .../pipelineRun/artifacts/ArtifactNodeDetails.tsx | 2 +- .../taskDetails/TaskDetailsInputOutput.tsx | 2 +- .../artifacts/ArtifactDetails/ArtifactOverviewDetails.tsx | 2 +- .../global/experiments/artifacts/ArtifactsTable.tsx | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/concepts/pipelines/content/artifacts/ArtifactUriLink.tsx b/frontend/src/concepts/pipelines/content/artifacts/ArtifactUriLink.tsx index 6a2c9b1fa4..894e2d599f 100644 --- a/frontend/src/concepts/pipelines/content/artifacts/ArtifactUriLink.tsx +++ b/frontend/src/concepts/pipelines/content/artifacts/ArtifactUriLink.tsx @@ -14,16 +14,19 @@ import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { useIsAreaAvailable, SupportedArea } from '~/concepts/areas'; import { MAX_STORAGE_OBJECT_SIZE, fetchStorageObjectSize } from '~/services/storageService'; import { bytesAsRoundedGiB } from '~/utilities/number'; +import { ArtifactType } from '~/concepts/pipelines/kfTypes'; import { extractS3UriComponents, getArtifactUrlFromUri } from './utils'; interface ArtifactUriLinkProps { uri: string; + type: string; } -export const ArtifactUriLink: React.FC = ({ uri }) => { +export const ArtifactUriLink: React.FC = ({ uri, type }) => { const { namespace } = usePipelinesAPI(); const isS3EndpointAvailable = useIsAreaAvailable(SupportedArea.S3_ENDPOINT).status; const [size, setSize] = React.useState(null); + const isClassificationMetrics = type === ArtifactType.CLASSIFICATION_METRICS; const url = React.useMemo(() => { if (!uri || !isS3EndpointAvailable) { @@ -41,7 +44,7 @@ export const ArtifactUriLink: React.FC = ({ uri }) => { return getArtifactUrlFromUri(uri, namespace); }, [isS3EndpointAvailable, namespace, uri]); - if (!url) { + if (!url || isClassificationMetrics) { return uri; } diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx index 676b3733de..8ec39e35e9 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/ArtifactNodeDetails.tsx @@ -88,7 +88,7 @@ export const ArtifactNodeDetails: React.FC = ({ {artifactName} - + diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx index 53b4890d44..67ef8c9c89 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/taskDetails/TaskDetailsInputOutput.tsx @@ -27,7 +27,7 @@ const TaskDetailsInputOutput: React.FC = ({ if (artifact) { return { label: artifactInputOutput.label, - value: , + value: , }; } diff --git a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactOverviewDetails.tsx b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactOverviewDetails.tsx index 4cc242a507..aa6f49ac22 100644 --- a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactOverviewDetails.tsx +++ b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactDetails/ArtifactOverviewDetails.tsx @@ -34,7 +34,7 @@ export const ArtifactOverviewDetails: React.FC = ( <> URI - + )} diff --git a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactsTable.tsx b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactsTable.tsx index 4285892466..49ca53034f 100644 --- a/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactsTable.tsx +++ b/frontend/src/pages/pipelines/global/experiments/artifacts/ArtifactsTable.tsx @@ -151,7 +151,7 @@ export const ArtifactsTable: React.FC = ({ {artifact.id} {artifact.type} - + From 26bbd62a08e9370355e13ecf56ad52d7b37049d7 Mon Sep 17 00:00:00 2001 From: Dipanshu Gupta Date: Fri, 21 Jun 2024 18:47:33 +0530 Subject: [PATCH 12/14] Truncate min width issue --- .../pipelinesDetails/PipelineDetailsTitle.tsx | 3 ++- .../pipeline/PipelineDetails.tsx | 20 ++++++++++++++++--- .../pipelineRun/PipelineRunDetails.tsx | 9 +++++++-- .../tables/experiment/ExperimentTableRow.tsx | 3 ++- .../global/runs/GlobalPipelineVersionRuns.tsx | 6 +++++- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx index c114644f6b..8bc8db18ab 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx @@ -23,7 +23,8 @@ const PipelineDetailsTitle: React.FC = ({ <> - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + {pipelineRunLabel && ( diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx index 43fa1888b4..d6b79a35b3 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx @@ -99,14 +99,28 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) = {breadcrumbPath} - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + } - title={} + title={ + + } {...(pipelineVersion && { description: ( - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + ) : ( 'Loading...' )} - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + } diff --git a/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx b/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx index 901d5987e8..dc1a4e31c4 100644 --- a/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx +++ b/frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx @@ -35,7 +35,8 @@ const ExperimentTableRow: React.FC = ({ }`} state={{ experiment }} > - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + {experiment.description} diff --git a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx index 256dce6f7c..b241ae9fa5 100644 --- a/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx +++ b/frontend/src/pages/pipelines/global/runs/GlobalPipelineVersionRuns.tsx @@ -49,7 +49,11 @@ const GlobalPipelineVersionRuns: PipelineCoreDetailsPageComponent = ({ breadcrum {breadcrumbPath} - + {/* TODO: Remove the custom className after upgrading to PFv6 */} + Runs From 0af9464f23790fc556b22a4de4bb09c178468c4d Mon Sep 17 00:00:00 2001 From: Robert Sun <107655677+rsun19@users.noreply.github.com> Date: Fri, 21 Jun 2024 10:29:44 -0400 Subject: [PATCH 13/14] first round of updates to disable type assertions --- frontend/.eslintrc | 12 ++++++ .../src/__tests__/unit/testUtils/hooks.ts | 7 ++-- frontend/src/api/errorUtils.ts | 3 +- frontend/src/api/modelRegistry/errorUtils.ts | 2 +- frontend/src/api/pipelines/errorUtils.ts | 6 ++- frontend/src/api/prometheus/serving.ts | 36 +++++++++++------ frontend/src/api/trustyai/errorUtils.ts | 3 +- frontend/src/app/AppContext.ts | 10 +---- frontend/src/components/DocCardBadges.tsx | 4 +- .../FilterSidePanelCategoryItem.tsx | 12 +++--- frontend/src/components/OdhDocListItem.tsx | 5 ++- frontend/src/components/ToastNotification.tsx | 3 +- .../browserStorage/BrowserStorageContext.tsx | 1 + .../components/table/useTableColumnSort.ts | 2 + frontend/src/concepts/areas/utils.ts | 3 +- .../dashboard/DashboardSearchField.tsx | 6 ++- .../concepts/distributedWorkloads/utils.tsx | 3 +- .../src/concepts/docResources/docUtils.ts | 1 + frontend/src/concepts/metrics/utils.ts | 6 +-- .../context/ModelRegistryContext.tsx | 1 + .../content/PipelineAndVersionContext.tsx | 13 +++--- .../PipelineRunArtifactSelect.tsx | 10 +++-- .../ConfusionMatrixCompare.tsx | 12 +++--- .../metricsSection/confusionMatrix/utils.ts | 9 ++++- .../compareRuns/metricsSection/roc/utils.ts | 17 ++++---- .../compareRuns/metricsSection/utils.ts | 10 ++--- .../ConfigurePipelinesServerModal.tsx | 3 +- .../pipelines/content/createRun/RunPage.tsx | 2 +- .../content/tables/PipelineFilterBar.tsx | 8 ++-- .../content/tables/usePipelineFilter.ts | 8 ++-- .../pipelines/context/MlmdListContext.tsx | 1 + .../pipelines/context/PipelinesContext.tsx | 5 ++- frontend/src/concepts/pipelines/utils.ts | 2 +- .../topology/PipelineDefaultTaskGroup.tsx | 4 +- .../concepts/topology/PipelineTaskEdge.tsx | 7 +++- .../topology/customNodes/ArtifactTaskNode.tsx | 19 ++++----- .../topology/customNodes/StandardTaskNode.tsx | 4 +- .../trustyai/context/TrustyAIContext.tsx | 1 + .../concepts/trustyai/useManageTrustyAICR.ts | 21 +++++----- .../AcceleratorIdentifierMultiselect.tsx | 6 ++- .../pages/exploreApplication/EnableModal.tsx | 6 ++- .../home/resources/useResourcesSection.tsx | 3 +- .../home/resources/useSpecifiedResources.tsx | 4 +- .../learningCenter/ApplicationFilters.tsx | 5 +-- .../pages/learningCenter/DocTypeFilters.tsx | 40 ++++++++----------- .../pages/learningCenter/EnabledFilters.tsx | 14 +------ .../learningCenter/LearningCenterToolbar.tsx | 4 +- .../learningCenter/ProviderTypeFilters.tsx | 5 +-- .../ModelVersionSelector.tsx | 6 ++- .../ModelVersions/ModelVersionListView.tsx | 6 ++- .../ModelVersionsArchiveListView.tsx | 6 ++- .../RegisteredModelListView.tsx | 6 ++- .../RegisteredModelsArchiveListView.tsx | 6 ++- ...ustomServingRuntimeAPIProtocolSelector.tsx | 8 +++- .../customServingRuntimes/utils.ts | 16 ++++++-- .../modelServing/screens/global/utils.ts | 5 ++- .../metrics/EnsureMetricsAvailable.tsx | 5 ++- .../metrics/ModelServingMetricsContext.tsx | 14 +++++-- .../screens/metrics/bias/BiasChart.tsx | 4 +- .../MetricTypeField.tsx | 12 +++--- .../metrics/performance/ModelMeshMetrics.tsx | 9 +---- .../metrics/performance/ServerGraphs.tsx | 23 ++--------- .../modelServing/screens/metrics/utils.tsx | 4 +- .../screens/admin/NotebookControllerTabs.tsx | 6 ++- .../screens/admin/UserTableCellTransform.tsx | 1 + .../server/EnvironmentVariablesField.tsx | 3 +- .../server/EnvironmentVariablesRow.tsx | 4 +- .../executions/details/ExecutionDetails.tsx | 4 +- .../global/runs/GlobalPipelineRunsTabs.tsx | 7 +++- .../pages/projects/ProjectDetailsContext.tsx | 1 + .../deployedModels/DeployedModelsSection.tsx | 13 +++--- .../environmentVariables/EnvConfigMap.tsx | 5 ++- .../environmentVariables/EnvSecret.tsx | 5 ++- .../EnvTypeSelectField.tsx | 12 ++++-- .../spawner/environmentVariables/utils.ts | 8 ++-- .../projects/screens/spawner/spawnerUtils.ts | 5 ++- .../screens/spawner/useBuildStatuses.ts | 4 +- frontend/src/redux/context.ts | 1 + frontend/src/redux/store/store.ts | 4 +- frontend/src/typeHelpers.ts | 4 +- frontend/src/utilities/NavData.tsx | 5 +-- .../src/utilities/__tests__/utils.spec.ts | 20 +++++++++- .../utilities/useAcceleratorProfileState.ts | 5 +-- frontend/src/utilities/useDraggableTable.ts | 4 +- .../utilities/useWatchNotebooksForUsers.tsx | 11 +++-- frontend/src/utilities/utils.ts | 11 ++++- 86 files changed, 376 insertions(+), 266 deletions(-) diff --git a/frontend/.eslintrc b/frontend/.eslintrc index cca83da5be..ec95917b9b 100755 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -315,6 +315,18 @@ } ] } + }, + { + "files": ["*.ts", "*.tsx"], + "excludedFiles": ["**/__mocks__/**", "**/__tests__/**"], + "rules": { + "@typescript-eslint/consistent-type-assertions": [ + "error", + { + "assertionStyle": "never" + } + ] + } } ] } diff --git a/frontend/src/__tests__/unit/testUtils/hooks.ts b/frontend/src/__tests__/unit/testUtils/hooks.ts index c82a2ff7cd..86c308422f 100644 --- a/frontend/src/__tests__/unit/testUtils/hooks.ts +++ b/frontend/src/__tests__/unit/testUtils/hooks.ts @@ -67,8 +67,8 @@ export const renderHook = < options?: RenderHookOptions, ): RenderHookResultExt => { let updateCount = 0; - let prevResult: Result | undefined; - let currentResult: Result | undefined; + let prevResult: Result; + let currentResult: Result; const renderResult = renderHookRTL((props) => { updateCount++; @@ -80,8 +80,7 @@ export const renderHook = < const renderResultExt: RenderHookResultExt = { ...renderResult, - getPreviousResult: () => - updateCount > 1 ? (prevResult as Result) : renderResult.result.current, + getPreviousResult: () => (updateCount > 1 ? prevResult : renderResult.result.current), getUpdateCount: () => updateCount, diff --git a/frontend/src/api/errorUtils.ts b/frontend/src/api/errorUtils.ts index e972b16c41..1ac6f1c5e9 100644 --- a/frontend/src/api/errorUtils.ts +++ b/frontend/src/api/errorUtils.ts @@ -2,7 +2,7 @@ import { K8sStatus } from '@openshift/dynamic-plugin-sdk-utils'; import { AxiosError } from 'axios'; export const isK8sStatus = (data: unknown): data is K8sStatus => - (data as K8sStatus).kind === 'Status'; + typeof data === 'object' && data !== null && 'kind' in data && data.kind === 'Status'; export class K8sStatusError extends Error { public statusObject: K8sStatus; @@ -18,6 +18,7 @@ const isAxiosErrorWithResponseMessage = ( error?: Error | AxiosError, ): error is AxiosError<{ message: string }> => Boolean( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions error && typeof (error as AxiosError<{ message: string }>).response?.data.message === 'string', ); diff --git a/frontend/src/api/modelRegistry/errorUtils.ts b/frontend/src/api/modelRegistry/errorUtils.ts index d9a3238d74..1282802199 100644 --- a/frontend/src/api/modelRegistry/errorUtils.ts +++ b/frontend/src/api/modelRegistry/errorUtils.ts @@ -2,7 +2,7 @@ import { ModelRegistryError } from '~/concepts/modelRegistry/types'; import { isCommonStateError } from '~/utilities/useFetchState'; const isError = (e: unknown): e is ModelRegistryError => - ['code', 'message'].every((key) => key in (e as ModelRegistryError)); + typeof e === 'object' && e !== null && ['code', 'message'].every((key) => key in e); export const handleModelRegistryFailures = (promise: Promise): Promise => promise diff --git a/frontend/src/api/pipelines/errorUtils.ts b/frontend/src/api/pipelines/errorUtils.ts index 384e3d6d2f..4efaa5da7c 100644 --- a/frontend/src/api/pipelines/errorUtils.ts +++ b/frontend/src/api/pipelines/errorUtils.ts @@ -14,10 +14,12 @@ type ResultErrorKF = { }; const isErrorKF = (e: unknown): e is ErrorKF => - ['error', 'code', 'message'].every((key) => key in (e as ErrorKF)); + typeof e === 'object' && e !== null && ['error', 'code', 'message'].every((key) => key in e); const isErrorDetailsKF = (result: unknown): result is ResultErrorKF => - ['error_details', 'error_message'].every((key) => key in (result as ResultErrorKF)); + typeof result === 'object' && + result !== null && + ['error_details', 'error_message'].every((key) => key in result); export const handlePipelineFailures = (promise: Promise): Promise => promise diff --git a/frontend/src/api/prometheus/serving.ts b/frontend/src/api/prometheus/serving.ts index 9cf7f35188..102b389c2b 100644 --- a/frontend/src/api/prometheus/serving.ts +++ b/frontend/src/api/prometheus/serving.ts @@ -14,6 +14,7 @@ import { RefreshIntervalValue } from '~/concepts/metrics/const'; import useRefreshInterval from '~/utilities/useRefreshInterval'; import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas'; import { PROMETHEUS_BIAS_PATH } from '~/api/prometheus/const'; +import { EitherNotBoth } from '~/typeHelpers'; import useQueryRangeResourceData from './useQueryRangeResourceData'; import { defaultResponsePredicate, @@ -22,17 +23,26 @@ import { export const useModelServingMetrics = ( type: PerformanceMetricType, - queries: { [key in ModelMetricType]: string } | { [key in ServerMetricType]: string }, + queries: EitherNotBoth< + { [key in ModelMetricType]: string }, + { [key in ServerMetricType]: string } + >, timeframe: TimeframeTitle, lastUpdateTime: number, setLastUpdateTime: (time: number) => void, refreshInterval: RefreshIntervalTitle, namespace: string, ): { - data: Record< - ServerMetricType | ModelMetricType, - ContextResourceData - >; + data: { + [ServerMetricType.REQUEST_COUNT]: ContextResourceData; + [ServerMetricType.AVG_RESPONSE_TIME]: ContextResourceData; + [ServerMetricType.CPU_UTILIZATION]: ContextResourceData; + [ServerMetricType.MEMORY_UTILIZATION]: ContextResourceData; + [ModelMetricType.REQUEST_COUNT_FAILED]: ContextResourceData; + [ModelMetricType.REQUEST_COUNT_SUCCESS]: ContextResourceData; + [ModelMetricType.TRUSTY_AI_SPD]: ContextResourceData; + [ModelMetricType.TRUSTY_AI_DIR]: ContextResourceData; + }; refresh: () => void; } => { const [end, setEnd] = React.useState(lastUpdateTime); @@ -43,7 +53,7 @@ export const useModelServingMetrics = ( const serverRequestCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.REQUEST_COUNT], + queries[ServerMetricType.REQUEST_COUNT] ?? '', end, timeframe, defaultResponsePredicate, @@ -53,7 +63,7 @@ export const useModelServingMetrics = ( const serverAverageResponseTime = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.AVG_RESPONSE_TIME], + queries[ServerMetricType.AVG_RESPONSE_TIME] ?? '', end, timeframe, prometheusQueryRangeResponsePredicate, @@ -62,7 +72,7 @@ export const useModelServingMetrics = ( const serverCPUUtilization = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.CPU_UTILIZATION], + queries[ServerMetricType.CPU_UTILIZATION] ?? '', end, timeframe, defaultResponsePredicate, @@ -71,7 +81,7 @@ export const useModelServingMetrics = ( const serverMemoryUtilization = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.MEMORY_UTILIZATION], + queries[ServerMetricType.MEMORY_UTILIZATION] ?? '', end, timeframe, defaultResponsePredicate, @@ -80,7 +90,7 @@ export const useModelServingMetrics = ( const modelRequestSuccessCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.REQUEST_COUNT_SUCCESS], + queries[ModelMetricType.REQUEST_COUNT_SUCCESS] ?? '', end, timeframe, defaultResponsePredicate, @@ -89,7 +99,7 @@ export const useModelServingMetrics = ( const modelRequestFailedCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.REQUEST_COUNT_FAILED], + queries[ModelMetricType.REQUEST_COUNT_FAILED] ?? '', end, timeframe, defaultResponsePredicate, @@ -98,7 +108,7 @@ export const useModelServingMetrics = ( const modelTrustyAISPD = useQueryRangeResourceData( biasMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.TRUSTY_AI_SPD], + queries[ModelMetricType.TRUSTY_AI_SPD] ?? '', end, timeframe, prometheusQueryRangeResponsePredicate, @@ -108,7 +118,7 @@ export const useModelServingMetrics = ( const modelTrustyAIDIR = useQueryRangeResourceData( biasMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.TRUSTY_AI_DIR], + queries[ModelMetricType.TRUSTY_AI_DIR] ?? '', end, timeframe, prometheusQueryRangeResponsePredicate, diff --git a/frontend/src/api/trustyai/errorUtils.ts b/frontend/src/api/trustyai/errorUtils.ts index 60fd07a7f6..6f3625f38e 100644 --- a/frontend/src/api/trustyai/errorUtils.ts +++ b/frontend/src/api/trustyai/errorUtils.ts @@ -9,8 +9,7 @@ type TrustyAIClientErrorViolation = { }; const isTrustyAIClientError = (e: unknown): e is TrustyAIClientError => - typeof e === 'object' && - ['title', 'status', 'violations'].every((key) => key in (e as TrustyAIClientError)); + typeof e === 'object' && e !== null && ['title', 'status', 'violations'].every((key) => key in e); export const handleTrustyAIFailures = (promise: Promise): Promise => promise.then((result) => { diff --git a/frontend/src/app/AppContext.ts b/frontend/src/app/AppContext.ts index bdc0141450..2b51e99c15 100644 --- a/frontend/src/app/AppContext.ts +++ b/frontend/src/app/AppContext.ts @@ -8,13 +8,7 @@ type AppContextProps = { storageClasses: StorageClassKind[]; }; -const defaultAppContext: AppContextProps = { - buildStatuses: [], - // At runtime dashboardConfig is never null -- DO NOT DO THIS usually - dashboardConfig: null as unknown as DashboardConfigKind, - storageClasses: [] as StorageClassKind[], -}; - -export const AppContext = React.createContext(defaultAppContext); +// eslint-disable-next-line @typescript-eslint/consistent-type-assertions +export const AppContext = React.createContext({} as AppContextProps); export const useAppContext = (): AppContextProps => React.useContext(AppContext); diff --git a/frontend/src/components/DocCardBadges.tsx b/frontend/src/components/DocCardBadges.tsx index 9a82f54235..e0270402dd 100644 --- a/frontend/src/components/DocCardBadges.tsx +++ b/frontend/src/components/DocCardBadges.tsx @@ -5,7 +5,7 @@ import { QuickStartContext, QuickStartContextValues } from '@patternfly/quicksta import { OdhDocument, OdhDocumentType } from '~/types'; import { getQuickStartCompletionStatus, CompletionStatusEnum } from '~/utilities/quickStartUtils'; import { DOC_TYPE_TOOLTIPS } from '~/utilities/const'; -import { getLabelColorForDocType, getDuration } from '~/utilities/utils'; +import { getLabelColorForDocType, getDuration, asEnumMember } from '~/utilities/utils'; import { DOC_TYPE_LABEL } from '~/pages/learningCenter/const'; import './OdhCard.scss'; @@ -19,7 +19,7 @@ const DocCardBadges: React.FC = ({ odhDoc }) => { const [completionStatus, setCompletionStatus] = React.useState< CompletionStatusEnum | undefined >(); - const docType = odhDoc.spec.type as OdhDocumentType; + const docType = asEnumMember(odhDoc.spec.type, OdhDocumentType) ?? OdhDocumentType.Documentation; const docName = odhDoc.metadata.name; const duration = odhDoc.spec.durationMinutes; diff --git a/frontend/src/components/FilterSidePanelCategoryItem.tsx b/frontend/src/components/FilterSidePanelCategoryItem.tsx index 1c01ec741b..9dcb06ddd8 100644 --- a/frontend/src/components/FilterSidePanelCategoryItem.tsx +++ b/frontend/src/components/FilterSidePanelCategoryItem.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import { Checkbox } from '@patternfly/react-core'; +import { Checkbox, CheckboxProps } from '@patternfly/react-core'; -export interface FilterSidePanelCategoryItemProps extends React.HTMLProps { +export interface FilterSidePanelCategoryItemProps { id: string; /** Children nodes */ children: React.ReactNode; @@ -13,7 +13,7 @@ export interface FilterSidePanelCategoryItemProps extends React.HTMLProps) => void; + onChange?: CheckboxProps['onChange']; /** Flag to show if the Filter Item Checkbox is checked. */ checked?: boolean; /** Title of the checkbox */ @@ -27,7 +27,7 @@ const FilterSidePanelCategoryItem: React.FunctionComponent = ({ odhDoc, favorite, updateFav }; const renderTypeBadge = () => { - const docType = odhDoc.spec.type as OdhDocumentType; + const docType = + asEnumMember(odhDoc.spec.type, OdhDocumentType) ?? OdhDocumentType.Documentation; const typeBadgeClasses = classNames('odh-list-item__partner-badge odh-m-doc', { 'odh-m-documentation': docType === OdhDocumentType.Documentation, 'odh-m-tutorial': docType === OdhDocumentType.Tutorial, diff --git a/frontend/src/components/ToastNotification.tsx b/frontend/src/components/ToastNotification.tsx index 4f2a2299ab..b0a945705b 100644 --- a/frontend/src/components/ToastNotification.tsx +++ b/frontend/src/components/ToastNotification.tsx @@ -3,6 +3,7 @@ import { Alert, AlertActionCloseButton, AlertVariant } from '@patternfly/react-c import { AppNotification } from '~/redux/types'; import { ackNotification, hideNotification } from '~/redux/actions/actions'; import { useAppDispatch } from '~/redux/hooks'; +import { asEnumMember } from '~/utilities/utils'; const TOAST_NOTIFICATION_TIMEOUT = 8 * 1000; @@ -36,7 +37,7 @@ const ToastNotification: React.FC = ({ notification }) = return ( dispatch(ackNotification(notification))} /> diff --git a/frontend/src/components/browserStorage/BrowserStorageContext.tsx b/frontend/src/components/browserStorage/BrowserStorageContext.tsx index f64d105113..ff903a9cfa 100644 --- a/frontend/src/components/browserStorage/BrowserStorageContext.tsx +++ b/frontend/src/components/browserStorage/BrowserStorageContext.tsx @@ -48,6 +48,7 @@ export const useBrowserStorage = ( [isSessionStorage, jsonify, setJSONValue, setStringValue, storageKey], ); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return [(getValue(storageKey, jsonify, isSessionStorage) as T) ?? defaultValue, setValue]; }; diff --git a/frontend/src/components/table/useTableColumnSort.ts b/frontend/src/components/table/useTableColumnSort.ts index ce179df0a0..56c4519d68 100644 --- a/frontend/src/components/table/useTableColumnSort.ts +++ b/frontend/src/components/table/useTableColumnSort.ts @@ -105,7 +105,9 @@ const useTableColumnSort = ( return 0; } + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const dataValueA = a[columnField.field as keyof T]; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const dataValueB = b[columnField.field as keyof T]; if (typeof dataValueA === 'string' && typeof dataValueB === 'string') { return dataValueA.localeCompare(dataValueB); diff --git a/frontend/src/concepts/areas/utils.ts b/frontend/src/concepts/areas/utils.ts index 2957080141..5926dc80a3 100644 --- a/frontend/src/concepts/areas/utils.ts +++ b/frontend/src/concepts/areas/utils.ts @@ -15,8 +15,7 @@ const getFlags = (dashboardConfigSpec: DashboardConfigKind['spec']): FlagState = typeof value === 'boolean'; return { - ...Object.keys(flags).reduce((acc, key) => { - const value = flags[key as FeatureFlag]; + ...Object.entries(flags).reduce((acc, [key, value]) => { if (isFeatureFlag(key, value)) { acc[key] = key.startsWith('disable') ? !value : value; } diff --git a/frontend/src/concepts/dashboard/DashboardSearchField.tsx b/frontend/src/concepts/dashboard/DashboardSearchField.tsx index 18d45e1da6..44d96fa0a0 100644 --- a/frontend/src/concepts/dashboard/DashboardSearchField.tsx +++ b/frontend/src/concepts/dashboard/DashboardSearchField.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { InputGroup, SearchInput, InputGroupItem } from '@patternfly/react-core'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import { asEnumMember } from '~/utilities/utils'; // List all the possible search fields here export enum SearchType { @@ -48,7 +49,10 @@ const DashboardSearchField: React.FC = ({ }))} value={searchType} onChange={(key) => { - onSearchTypeChange(key as SearchType); + const enumMember = asEnumMember(key, SearchType); + if (enumMember !== null) { + onSearchTypeChange(enumMember); + } }} icon={icon} /> diff --git a/frontend/src/concepts/distributedWorkloads/utils.tsx b/frontend/src/concepts/distributedWorkloads/utils.tsx index db466618d7..29a4de3785 100644 --- a/frontend/src/concepts/distributedWorkloads/utils.tsx +++ b/frontend/src/concepts/distributedWorkloads/utils.tsx @@ -34,6 +34,7 @@ import { convertToUnit, } from '~/utilities/valueUnits'; import { WorkloadWithUsage } from '~/api'; +import { isEnumMember } from '~/utilities/utils'; export enum WorkloadStatusType { Pending = 'Pending', @@ -166,7 +167,7 @@ export const getWorkloadName = (workload: WorkloadKind): string => workload.metadata?.name || 'Unnamed'; export const isKnownWorkloadOwnerType = (s: string): s is WorkloadOwnerType => - (Object.values(WorkloadOwnerType) as string[]).includes(s); + isEnumMember(s, WorkloadOwnerType); export const getWorkloadOwner = ( workload: WorkloadKind, diff --git a/frontend/src/concepts/docResources/docUtils.ts b/frontend/src/concepts/docResources/docUtils.ts index e66d889d94..1abeb77360 100644 --- a/frontend/src/concepts/docResources/docUtils.ts +++ b/frontend/src/concepts/docResources/docUtils.ts @@ -30,6 +30,7 @@ export const getQuickStartDocs = ( // Get doc cards for the quick starts const docs: OdhDocument[] = quickStarts.map( (quickStart) => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions _.merge({}, quickStart, { spec: { type: OdhDocumentType.QuickStart, diff --git a/frontend/src/concepts/metrics/utils.ts b/frontend/src/concepts/metrics/utils.ts index 1f44bc91cf..c88e5d2592 100644 --- a/frontend/src/concepts/metrics/utils.ts +++ b/frontend/src/concepts/metrics/utils.ts @@ -1,12 +1,12 @@ import { SelectOptionObject } from '@patternfly/react-core/deprecated'; import { TimeframeTitle, RefreshIntervalTitle } from '~/concepts/metrics/types'; +import { isEnumMember } from '~/utilities/utils'; export const isTimeframeTitle = ( timeframe: string | SelectOptionObject, -): timeframe is TimeframeTitle => - Object.values(TimeframeTitle).includes(timeframe as TimeframeTitle); +): timeframe is TimeframeTitle => isEnumMember(timeframe.toString(), TimeframeTitle); export const isRefreshIntervalTitle = ( refreshInterval: string | SelectOptionObject, ): refreshInterval is RefreshIntervalTitle => - Object.values(RefreshIntervalTitle).includes(refreshInterval as RefreshIntervalTitle); + isEnumMember(refreshInterval.toString(), RefreshIntervalTitle); diff --git a/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx b/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx index eb3bdce52d..dfde7ebcc8 100644 --- a/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx +++ b/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx @@ -28,6 +28,7 @@ export const ModelRegistryContext = React.createContext undefined, refreshState: async () => undefined, diff --git a/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx b/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx index eed1a71d94..858635fbca 100644 --- a/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx +++ b/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx @@ -77,12 +77,13 @@ const PipelineAndVersionContextProvider: React.FC ({ pipelines: selectedPipelines, - versions: (Object.values(selectedVersions) as SelectedVersion[]) - .map((selectedVersion) => - selectedVersion.versions.map((version) => ({ - pipelineName: selectedVersion.pipelineName, - version, - })), + versions: Object.values(selectedVersions) + .map( + (selectedVersion) => + selectedVersion?.versions.map((version) => ({ + pipelineName: selectedVersion.pipelineName, + version, + })) ?? [], ) .flat() .filter((selection) => { diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx index 2b15849d79..97c5f0217c 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx @@ -49,10 +49,12 @@ export const PipelineRunArtifactSelect = ({ isOpen={isOpen} selected={selectedItemTitles} onSelect={(_event, value) => { - if (selectedItemTitles.includes(value as string)) { - setSelectedItemTitles(selectedItemTitles.filter((id) => id !== value)); - } else { - setSelectedItemTitles([...selectedItemTitles, value as string]); + if (typeof value === 'string') { + if (selectedItemTitles.includes(value)) { + setSelectedItemTitles(selectedItemTitles.filter((id) => id !== value)); + } else { + setSelectedItemTitles([...selectedItemTitles, value]); + } } }} onOpenChange={(nextOpen: boolean) => setIsOpen(nextOpen)} diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx index 60ec951f71..b4c3b7694c 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx @@ -49,13 +49,16 @@ const ConfusionMatrixCompare: React.FC = ({ const { configMap, runMap } = React.useMemo( () => - fullArtifactPaths.reduce( + fullArtifactPaths.reduce<{ + runMap: Record; + configMap: Record; + }>( (acc, fullPath) => { const customProperties = fullPath.linkedArtifact.artifact.getCustomPropertiesMap(); const data = customProperties.get('confusionMatrix')?.getStructValue()?.toJavaScript(); if (data) { - const confusionMatrixData = data.struct as unknown; + const confusionMatrixData = data.struct; if (isConfusionMatrix(confusionMatrixData)) { const runId = fullPath.run.run_id; const title = getFullArtifactPathLabel(fullPath); @@ -78,10 +81,7 @@ const ConfusionMatrixCompare: React.FC = ({ return acc; }, - { - runMap: {} as Record, - configMap: {} as Record, - }, + { runMap: {}, configMap: {} }, ), [fullArtifactPaths], ); diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts index 47fea29c76..2891587bd9 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts @@ -1,8 +1,12 @@ import { ConfusionMatrixInput } from '~/concepts/pipelines/content/artifacts/charts/confusionMatrix/types'; export const isConfusionMatrix = (obj: unknown): obj is ConfusionMatrixInput => { - const matrix = obj as ConfusionMatrixInput; + const matrix = obj; return ( + typeof matrix === 'object' && + matrix !== null && + 'annotationSpecs' in matrix && + 'rows' in matrix && Array.isArray(matrix.annotationSpecs) && matrix.annotationSpecs.every( (annotationSpec) => @@ -10,7 +14,8 @@ export const isConfusionMatrix = (obj: unknown): obj is ConfusionMatrixInput => ) && Array.isArray(matrix.rows) && matrix.rows.every( - (row) => Array.isArray(row.row) && row.row.every((value) => typeof value === 'number'), + (row) => + Array.isArray(row.row) && row.row.every((value: unknown) => typeof value === 'number'), ) ); }; diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts index c3bf9da5d8..74582cf10e 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts @@ -2,14 +2,15 @@ import { JavaScriptValue } from 'google-protobuf/google/protobuf/struct_pb'; import { ROCCurveConfig } from '~/concepts/pipelines/content/artifacts/charts/ROCCurve'; import { ConfidenceMetric } from './types'; -export const isConfidenceMetric = (obj: JavaScriptValue): obj is ConfidenceMetric => { - const metric = obj as ConfidenceMetric; - return ( - typeof metric.confidenceThreshold === 'number' && - typeof metric.falsePositiveRate === 'number' && - typeof metric.recall === 'number' - ); -}; +export const isConfidenceMetric = (obj: JavaScriptValue): obj is ConfidenceMetric => + typeof obj === 'object' && + obj !== null && + 'confidenceThreshold' in obj && + 'falsePositiveRate' in obj && + 'recall' in obj && + typeof obj.confidenceThreshold === 'number' && + typeof obj.falsePositiveRate === 'number' && + typeof obj.recall === 'number'; export const buildRocCurveConfig = ( confidenceMetricsArray: ConfidenceMetric[], diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts index 38a034f605..b5907e164e 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts @@ -58,7 +58,7 @@ export const getRunArtifacts = (mlmdPackages: PipelineRunRelatedMlmd[]): RunArti linkedArtifacts.push({ event, artifact, - } as LinkedArtifact); + }); } else { throw new Error(`The artifact with the following ID was not found: ${artifactId}`); } @@ -66,12 +66,12 @@ export const getRunArtifacts = (mlmdPackages: PipelineRunRelatedMlmd[]): RunArti return { execution, linkedArtifacts, - } as ExecutionArtifact; + }; }); return { run: mlmdPackage.run, executionArtifacts, - } as RunArtifact; + }; }); export const filterRunArtifactsByType = ( @@ -102,14 +102,14 @@ export const filterRunArtifactsByType = ( typeExecutions.push({ execution: e.execution, linkedArtifacts: typeArtifacts, - } as ExecutionArtifact); + }); } } if (typeExecutions.length > 0) { typeRuns.push({ run: runArtifact.run, executionArtifacts: typeExecutions, - } as RunArtifact); + }); } } return typeRuns; diff --git a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx index 252de75602..4a16b8725c 100644 --- a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx +++ b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx @@ -10,7 +10,6 @@ import { PipelinesDatabaseSection } from './PipelinesDatabaseSection'; import { ObjectStorageSection } from './ObjectStorageSection'; import { DATABASE_CONNECTION_FIELDS, - DatabaseConnectionKeys, EMPTY_DATABASE_CONNECTION, ExternalDatabaseSecret, } from './const'; @@ -48,7 +47,7 @@ export const ConfigurePipelinesServerModal: React.FC DATABASE_CONNECTION_FIELDS.filter((field) => field.isRequired) .map((field) => field.key) - .includes(key as DatabaseConnectionKeys) + .includes(key) ? !!value : true, ); diff --git a/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx b/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx index 8761f6f942..e9b928131c 100644 --- a/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx @@ -118,7 +118,7 @@ const RunPage: React.FC = ({ cloneRun, contextPath, testId }) => { > diff --git a/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx b/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx index 16bdea92fe..81c57a2034 100644 --- a/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx +++ b/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx @@ -41,11 +41,13 @@ export function FilterToolbar({ testId = 'filter-toolbar', ...toolbarGroupProps }: ToolbarFilterProps): React.JSX.Element { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const keys = Object.keys(filterOptions) as Array; const [open, setOpen] = React.useState(false); const [currentFilterType, setCurrentFilterType] = React.useState(keys[0]); const isToolbarChip = (v: unknown): v is ToolbarChip & { key: T } => - !!v && Object.keys(v as ToolbarChip).every((k) => ['key', 'node'].includes(k)); + !!v && Object.keys(v).every((k) => ['key', 'node'].includes(k)); + const filterItem = filterData[currentFilterType]; return ( <> @@ -111,9 +113,7 @@ export function FilterToolbar({ {filterOptionRenders[currentFilterType]({ onChange: (value, label) => onFilterUpdate(currentFilterType, label && value ? { label, value } : value), - ...(typeof filterData[currentFilterType] === 'string' - ? { value: filterData[currentFilterType] as string } - : (filterData[currentFilterType] as { label: string; value: string })), + ...(typeof filterItem === 'string' ? { value: filterItem } : filterItem), })} diff --git a/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts b/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts index 9a1cd1031d..70235f7631 100644 --- a/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts +++ b/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts @@ -14,13 +14,11 @@ export enum FilterOptions { PIPELINE_VERSION = 'pipeline_version', } -export const getDataValue = ( - data: R, -): string | undefined => { - if (typeof data === 'string') { +export const getDataValue = (data: string | { value: string } | undefined): string | undefined => { + if (typeof data === 'string' || typeof data === 'undefined') { return data; } - return (data as { label: string; value: string } | undefined)?.value; + return data.value; }; const defaultFilterData: FilterProps['filterData'] = { diff --git a/frontend/src/concepts/pipelines/context/MlmdListContext.tsx b/frontend/src/concepts/pipelines/context/MlmdListContext.tsx index 159e7ff51a..4ddeb44524 100644 --- a/frontend/src/concepts/pipelines/context/MlmdListContext.tsx +++ b/frontend/src/concepts/pipelines/context/MlmdListContext.tsx @@ -16,6 +16,7 @@ export interface MlmdListContextProps { setOrderBy: (orderBy: MlmdOrderBy | undefined) => void; } +// eslint-disable-next-line @typescript-eslint/consistent-type-assertions const MlmdListContext = React.createContext({} as MlmdListContextProps); export const MlmdListContextProvider: React.FC = ({ children }) => { diff --git a/frontend/src/concepts/pipelines/context/PipelinesContext.tsx b/frontend/src/concepts/pipelines/context/PipelinesContext.tsx index 8f9689f613..c58d9c74b8 100644 --- a/frontend/src/concepts/pipelines/context/PipelinesContext.tsx +++ b/frontend/src/concepts/pipelines/context/PipelinesContext.tsx @@ -51,6 +51,7 @@ const PipelinesContext = React.createContext({ serverTimedOut: false, ignoreTimedOut: () => undefined, namespace: '', + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions project: null as unknown as ProjectKind, refreshState: async () => undefined, refreshAPIState: () => undefined, @@ -58,7 +59,9 @@ const PipelinesContext = React.createContext({ loading: false, data: null, }), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions apiState: { apiAvailable: false, api: null as unknown as PipelineAPIState['api'] }, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions metadataStoreServiceClient: null as unknown as MetadataStoreServicePromiseClient, }); @@ -106,7 +109,7 @@ export const PipelineContextProvider = conditionalArea {}); enableDevTools([client]); } diff --git a/frontend/src/concepts/pipelines/utils.ts b/frontend/src/concepts/pipelines/utils.ts index 006bb883f9..e674ecae2d 100644 --- a/frontend/src/concepts/pipelines/utils.ts +++ b/frontend/src/concepts/pipelines/utils.ts @@ -32,4 +32,4 @@ export const isGeneratedDSPAExternalStorageSecret = (name: string): boolean => export const isRunSchedule = ( resource: PipelineRunKFv2 | PipelineRunJobKFv2, -): resource is PipelineRunJobKFv2 => !!(resource as PipelineRunJobKFv2).trigger; +): resource is PipelineRunJobKFv2 => 'trigger' in resource; diff --git a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx index 520ad656da..a48b905eaf 100644 --- a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx +++ b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx @@ -33,7 +33,7 @@ type PipelinesDefaultGroupInnerProps = Omit = observer( ({ element, selected, onSelect }) => { - const [hover, hoverRef] = useHover(); + const [hover, hoverRef] = useHover(); const popoverRef = React.useRef(null); const detailsLevel = element.getGraph().getDetailsLevel(); @@ -80,7 +80,7 @@ const DefaultTaskGroupInner: React.FunctionComponent - }> + {element.isCollapsed() ? ( = ({ element, ...props }) => { - const edge = element as Edge; + if (!isEdge(element)) { + throw new Error('Element is not Edge'); + } + const edge = element; return ( = observer( ({ element, selected, onSelect, ...rest }) => { const bounds = element.getBounds(); - const [isHover, hoverRef] = useHover(); + const [isHover, hoverRef] = useHover(); const detailsLevel = element.getGraph().getDetailsLevel(); const data = element.getData(); const scale = element.getGraph().getScale(); @@ -120,10 +121,7 @@ const ArtifactTaskNodeInner: React.FC = observer( const translateX = bounds.width / 2 - (iconSize / 2) * upScale; const translateY = iconPadding * upScale; return ( - } - > + {isHover || detailsLevel !== ScaleDetailsLevel.high ? ( = ({ element, ...rest }) => ( - -); +const ArtifactTaskNode: React.FC = ({ element, ...rest }) => { + if (!isNode(element)) { + throw new Error('Element is not Node'); + } + return ; +}; export default ArtifactTaskNode; diff --git a/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx b/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx index 7bdca3198e..5c32a68623 100644 --- a/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx +++ b/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx @@ -28,7 +28,7 @@ const StandardTaskNode: React.FunctionComponent = ({ ...rest }) => { const data = element.getData(); - const [hover, hoverRef] = useHover(); + const [hover, hoverRef] = useHover(); const detailsLevel = element.getGraph().getDetailsLevel(); // Set the cached node status to Succeeded @@ -48,7 +48,7 @@ const StandardTaskNode: React.FunctionComponent = ({ ) : null; return ( - }> + ({ data: DEFAULT_CONTEXT_DATA, refreshState: async () => undefined, refreshAPIState: () => undefined, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions apiState: { apiAvailable: false, api: null as unknown as TrustyAPIState['api'] }, }); diff --git a/frontend/src/concepts/trustyai/useManageTrustyAICR.ts b/frontend/src/concepts/trustyai/useManageTrustyAICR.ts index f75e70e739..e4034d397d 100644 --- a/frontend/src/concepts/trustyai/useManageTrustyAICR.ts +++ b/frontend/src/concepts/trustyai/useManageTrustyAICR.ts @@ -26,20 +26,17 @@ const useManageTrustyAICR = (namespace: string): UseManageTrustyAICRReturnType = showSuccess.current = true; } - const installCR = React.useCallback( - () => - createTrustyAICR(namespace) - .then(refresh) - .catch((e) => setInstallReqError(e)), - [namespace, refresh], - ); + const installCR = React.useCallback(async () => { + await createTrustyAICR(namespace) + .then(refresh) + .catch((e) => setInstallReqError(e)); + }, [namespace, refresh]); - const deleteCR = React.useCallback( - () => deleteTrustyAICR(namespace).then(refresh), - [namespace, refresh], - ); + const deleteCR = React.useCallback(async () => { + await deleteTrustyAICR(namespace).then(refresh); + }, [namespace, refresh]); - return { + return { error, isProgressing, isAvailable, diff --git a/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx b/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx index a71eb628e4..5e646a6c60 100644 --- a/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx +++ b/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx @@ -170,7 +170,11 @@ export const AcceleratorIdentifierMultiselect: React.FC onSelect(selection as string)} + onSelect={(ev, selection) => { + if (typeof selection === 'string') { + onSelect(selection); + } + }} onOpenChange={() => setIsOpen(false)} toggle={toggle} > diff --git a/frontend/src/pages/exploreApplication/EnableModal.tsx b/frontend/src/pages/exploreApplication/EnableModal.tsx index 17cc480bb0..39016cdd5c 100644 --- a/frontend/src/pages/exploreApplication/EnableModal.tsx +++ b/frontend/src/pages/exploreApplication/EnableModal.tsx @@ -12,8 +12,8 @@ import { import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { OdhApplication } from '~/types'; import { EnableApplicationStatus, useEnableApplication } from '~/utilities/useEnableApplication'; +import { asEnumMember } from '~/utilities/utils'; import EnableVariable from './EnableVariable'; - import './EnableModal.scss'; type EnableModalProps = { @@ -158,7 +158,9 @@ const EnableModal: React.FC = ({ selectedApp, shown, onClose } key={key} ref={index === 0 ? focusRef : undefined} label={enable.variableDisplayText?.[key] ?? ''} - inputType={enable.variables?.[key] as TextInputTypes} + inputType={ + asEnumMember(enable.variables?.[key], TextInputTypes) ?? TextInputTypes.text + } helperText={enable.variableHelpText?.[key] ?? ''} validationInProgress={validationInProgress} value={enableValues[key]} diff --git a/frontend/src/pages/home/resources/useResourcesSection.tsx b/frontend/src/pages/home/resources/useResourcesSection.tsx index 4b78d05407..ab66e247a0 100644 --- a/frontend/src/pages/home/resources/useResourcesSection.tsx +++ b/frontend/src/pages/home/resources/useResourcesSection.tsx @@ -12,7 +12,6 @@ import { TextVariants, } from '@patternfly/react-core'; import { ExclamationCircleIcon } from '@patternfly/react-icons'; -import { OdhDocument } from '~/types'; import OdhDocCard from '~/components/OdhDocCard'; import ScrolledGallery from '~/concepts/design/ScrolledGallery'; import CollapsibleSection from '~/concepts/design/CollapsibleSection'; @@ -65,7 +64,7 @@ export const useResourcesSection = (): React.ReactNode => { { + const foundDocs = specifiedDocs.reduce((acc, included) => { const doc = docs.find((d) => d.metadata.name === included.name && d.kind === included.kind); if (doc) { acc.push(doc); } return acc; - }, [] as OdhDocument[]); + }, []); return { docs: foundDocs, loaded, loadError }; }, [docs, specifiedDocs, loadError, loaded]); }; diff --git a/frontend/src/pages/learningCenter/ApplicationFilters.tsx b/frontend/src/pages/learningCenter/ApplicationFilters.tsx index eeb3b7f88f..389eec771b 100644 --- a/frontend/src/pages/learningCenter/ApplicationFilters.tsx +++ b/frontend/src/pages/learningCenter/ApplicationFilters.tsx @@ -34,8 +34,7 @@ const ApplicationFilters: React.FC = ({ docApps, catego return allApplications; }, [categoryApps, docApps]); - const onFilterChange = (docType: string, e: React.SyntheticEvent): void => { - const { checked } = e.target as React.AllHTMLAttributes; + const onFilterChange = (docType: string, checked: boolean): void => { const updatedQuery = [...providerFilters]; const index = updatedQuery.indexOf(docType); if (checked && index === -1) { @@ -71,7 +70,7 @@ const ApplicationFilters: React.FC = ({ docApps, catego id={application} key={application} checked={providerFilters.includes(application)} - onClick={(e) => onFilterChange(application, e)} + onChange={(_, checked) => onFilterChange(application, checked)} title={application} > {`${application} (${applications[application]})`} diff --git a/frontend/src/pages/learningCenter/DocTypeFilters.tsx b/frontend/src/pages/learningCenter/DocTypeFilters.tsx index 7b3d7c94ee..10ba55c421 100644 --- a/frontend/src/pages/learningCenter/DocTypeFilters.tsx +++ b/frontend/src/pages/learningCenter/DocTypeFilters.tsx @@ -4,6 +4,7 @@ import { FilterSidePanelCategory } from '@patternfly/react-catalog-view-extensio import FilterSidePanelCategoryItem from '~/components/FilterSidePanelCategoryItem'; import { OdhDocument, OdhDocumentType } from '~/types'; import { removeQueryArgument, setQueryArgument } from '~/utilities/router'; +import { asEnumMember, enumIterator } from '~/utilities/utils'; import { DOC_TYPE_FILTER_KEY } from './const'; import { useQueryFilters } from './useQueryFilters'; @@ -16,10 +17,11 @@ const DocTypeFilters: React.FC = ({ categoryApps }) => { const docTypeFilters = useQueryFilters(DOC_TYPE_FILTER_KEY); const docCounts = React.useMemo( () => - categoryApps.reduce( + categoryApps.reduce<{ [key in OdhDocumentType]: number }>( (acc, docApp) => { - if (docApp.spec.type in acc) { - acc[docApp.spec.type as keyof typeof acc]++; + const enumMember = asEnumMember(docApp.spec.type, OdhDocumentType); + if (enumMember) { + acc[enumMember]++; } return acc; }, @@ -52,26 +54,18 @@ const DocTypeFilters: React.FC = ({ categoryApps }) => { return ( - {Object.keys(OdhDocumentType).map((docType) => { - const value = OdhDocumentType[docType as keyof typeof OdhDocumentType]; - return ( - - onFilterChange( - value, - (e.target as React.AllHTMLAttributes).checked || false, - ) - } - title={docType} - > - {`${docType} (${docCounts[value]})`} - - ); - })} + {enumIterator(OdhDocumentType).map(([docType, value]) => ( + onFilterChange(value, checked)} + title={docType} + > + {`${docType} (${docCounts[value]})`} + + ))} ); }; diff --git a/frontend/src/pages/learningCenter/EnabledFilters.tsx b/frontend/src/pages/learningCenter/EnabledFilters.tsx index de72e83ea7..6c9b1799d2 100644 --- a/frontend/src/pages/learningCenter/EnabledFilters.tsx +++ b/frontend/src/pages/learningCenter/EnabledFilters.tsx @@ -47,12 +47,7 @@ const EnabledFilter: React.FC = ({ categoryApps }) => { data-id="enabled-filter-checkbox" id="enabled-filter-checkbox" checked={enabledFilters.includes('true')} - onClick={(e) => - onFilterChange( - 'true', - (e.target as React.AllHTMLAttributes).checked || false, - ) - } + onChange={(_, checked) => onFilterChange('true', checked)} title="Enabled" > {`Enabled (${enabledCount})`} @@ -61,12 +56,7 @@ const EnabledFilter: React.FC = ({ categoryApps }) => { data-id="not-enabled-filter-checkbox" id="not-enabled-filter-checkbox" checked={enabledFilters.includes('false')} - onClick={(e) => - onFilterChange( - 'false', - (e.target as React.AllHTMLAttributes).checked || false, - ) - } + onChange={(_, checked) => onFilterChange('false', checked)} title="Not enabled" > {`Not enabled (${notEnabledCount})`} diff --git a/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx b/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx index fda12ae799..bf10b8b391 100644 --- a/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx +++ b/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx @@ -113,7 +113,7 @@ const LearningCenterToolbar: React.FC = ({ const onSortTypeSelect = React.useCallback( (e: React.MouseEvent | React.ChangeEvent) => { setIsSortTypeDropdownOpen(false); - const selection = (e.target as Element).getAttribute('data-key') ?? ''; + const selection = e.target instanceof Element ? e.target.getAttribute('data-key') ?? '' : ''; setQueryArgument(navigate, DOC_SORT_KEY, selection); }, [navigate], @@ -133,7 +133,7 @@ const LearningCenterToolbar: React.FC = ({ const onSortOrderSelect = React.useCallback( (e: React.MouseEvent | React.ChangeEvent) => { setIsSortOrderDropdownOpen(false); - const selection = (e.target as Element).getAttribute('data-key') ?? ''; + const selection = e.target instanceof Element ? e.target.getAttribute('data-key') ?? '' : ''; setQueryArgument(navigate, DOC_SORT_ORDER_KEY, selection); }, [navigate], diff --git a/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx b/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx index ffb216ca36..21eb0e1538 100644 --- a/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx +++ b/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx @@ -39,8 +39,7 @@ const ProviderTypeFilters: React.FC = ({ docApps, cate return allTypes; }, [categoryApps, docApps]); - const onFilterChange = (docType: string, e: React.SyntheticEvent): void => { - const { checked } = e.target as React.AllHTMLAttributes; + const onFilterChange = (docType: string, checked: boolean): void => { const updatedQuery = [...providerTypeFilters]; const index = updatedQuery.indexOf(docType); if (checked && index === -1) { @@ -81,7 +80,7 @@ const ProviderTypeFilters: React.FC = ({ docApps, cate id={providerType} key={providerType} checked={providerTypeFilters.includes(providerType)} - onClick={(e) => onFilterChange(providerType, e)} + onChange={(_, checked) => onFilterChange(providerType, checked)} title={providerType} > {`${providerType} (${providerTypes[providerType]})`} diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx index cf00d689ea..80aadb7cca 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx @@ -53,8 +53,10 @@ const ModelVersionSelector: React.FC = ({ const menu = ( { - onSelect(itemId as string); - setOpen(false); + if (typeof itemId === 'string') { + onSelect(itemId); + setOpen(false); + } }} data-id="model-version-selector-menu" ref={menuRef} diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx index 049454ed25..2bad667c01 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx @@ -22,6 +22,7 @@ import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/Em import { filterModelVersions } from '~/pages/modelRegistry/screens/utils'; import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; import { modelVersionArchiveUrl } from '~/pages/modelRegistry/screens/routeUtils'; +import { asEnumMember } from '~/utilities/utils'; import ModelVersionsTable from './ModelVersionsTable'; type ModelVersionListViewProps = { @@ -89,7 +90,10 @@ const ModelVersionListView: React.FC = ({ }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + const enumMember = asEnumMember(newSearchType, SearchType); + if (enumMember !== null) { + setSearchType(enumMember); + } }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx index e7bf2dcc50..800c4a3e21 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx @@ -13,6 +13,7 @@ import { ModelVersion } from '~/concepts/modelRegistry/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import { filterModelVersions } from '~/pages/modelRegistry/screens/utils'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; +import { asEnumMember } from '~/utilities/utils'; import ModelVersionsArchiveTable from './ModelVersionsArchiveTable'; type ModelVersionsArchiveListViewProps = { @@ -63,7 +64,10 @@ const ModelVersionsArchiveListView: React.FC }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + const enumMember = asEnumMember(newSearchType, SearchType); + if (enumMember) { + setSearchType(enumMember); + } }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx index 561cee5da8..3ad9e8be51 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx @@ -9,6 +9,7 @@ import { filterRegisteredModels } from '~/pages/modelRegistry/screens/utils'; import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; import { registeredModelArchiveUrl } from '~/pages/modelRegistry/screens/routeUtils'; +import { asEnumMember } from '~/utilities/utils'; import RegisteredModelTable from './RegisteredModelTable'; import RegisteredModelsTableToolbar from './RegisteredModelsTableToolbar'; @@ -71,7 +72,10 @@ const RegisteredModelListView: React.FC = ({ }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + const newSearchTypeInput = asEnumMember(newSearchType, SearchType); + if (newSearchTypeInput !== null) { + setSearchType(newSearchTypeInput); + } }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx index 7e5de0392c..86a0f2e9db 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx @@ -13,6 +13,7 @@ import { RegisteredModel } from '~/concepts/modelRegistry/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import { filterRegisteredModels } from '~/pages/modelRegistry/screens/utils'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; +import { asEnumMember } from '~/utilities/utils'; import RegisteredModelsArchiveTable from './RegisteredModelsArchiveTable'; type RegisteredModelsArchiveListViewProps = { @@ -68,7 +69,10 @@ const RegisteredModelsArchiveListView: React.FC { - setSearchType(newSearchType as SearchType); + const newSearchTypeInput = asEnumMember(newSearchType, SearchType); + if (newSearchTypeInput !== null) { + setSearchType(newSearchTypeInput); + } }} icon={} /> diff --git a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx index 2b9d485279..b22321b07e 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx +++ b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { FormGroup } from '@patternfly/react-core'; import { ServingRuntimeAPIProtocol, ServingRuntimePlatform } from '~/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import { asEnumMember } from '~/utilities/utils'; type CustomServingRuntimeAPIProtocolSelectorProps = { selectedAPIProtocol: ServingRuntimeAPIProtocol | undefined; @@ -50,7 +51,12 @@ const CustomServingRuntimeAPIProtocolSelector: React.FC< isDisabled={isOnlyModelMesh} options={options} value={selectedAPIProtocol || ''} - onChange={(key) => setSelectedAPIProtocol(key as ServingRuntimeAPIProtocol)} + onChange={(key) => { + const enumValue = asEnumMember(key, ServingRuntimeAPIProtocol); + if (enumValue !== null) { + setSelectedAPIProtocol(enumValue); + } + }} /> ); diff --git a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts index 21330644fc..6e0c34f66c 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts +++ b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts @@ -2,6 +2,7 @@ import { K8sResourceCommon } from '@openshift/dynamic-plugin-sdk-utils'; import { ServingRuntimeKind, TemplateKind } from '~/k8sTypes'; import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { ServingRuntimeAPIProtocol, ServingRuntimePlatform } from '~/types'; +import { asEnumMember } from '~/utilities/utils'; export const getTemplateEnabled = ( template: TemplateKind, @@ -142,8 +143,12 @@ export const getAPIProtocolFromTemplate = ( if (!template.metadata.annotations?.['opendatahub.io/apiProtocol']) { return undefined; } - - return template.metadata.annotations['opendatahub.io/apiProtocol'] as ServingRuntimeAPIProtocol; + return ( + asEnumMember( + template.metadata.annotations['opendatahub.io/apiProtocol'], + ServingRuntimeAPIProtocol, + ) ?? undefined + ); }; export const getAPIProtocolFromServingRuntime = ( @@ -152,5 +157,10 @@ export const getAPIProtocolFromServingRuntime = ( if (!resource.metadata.annotations?.['opendatahub.io/apiProtocol']) { return undefined; } - return resource.metadata.annotations['opendatahub.io/apiProtocol'] as ServingRuntimeAPIProtocol; + return ( + asEnumMember( + resource.metadata.annotations['opendatahub.io/apiProtocol'], + ServingRuntimeAPIProtocol, + ) ?? undefined + ); }; diff --git a/frontend/src/pages/modelServing/screens/global/utils.ts b/frontend/src/pages/modelServing/screens/global/utils.ts index b2728f34a6..d4d4c22111 100644 --- a/frontend/src/pages/modelServing/screens/global/utils.ts +++ b/frontend/src/pages/modelServing/screens/global/utils.ts @@ -2,6 +2,7 @@ import { InferenceServiceKind, ProjectKind, SecretKind, PodKind } from '~/k8sTyp import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { getProjectDisplayName } from '~/concepts/projects/utils'; import { InferenceServiceModelState, ModelStatus } from '~/pages/modelServing/screens/types'; +import { asEnumMember } from '~/utilities/utils'; export const getInferenceServiceDisplayName = (is: InferenceServiceKind): string => getDisplayNameFromK8sResource(is); @@ -12,8 +13,8 @@ export const getTokenDisplayName = (secret: SecretKind): string => export const getInferenceServiceActiveModelState = ( is: InferenceServiceKind, ): InferenceServiceModelState => - is.status?.modelStatus?.states?.activeModelState || - is.status?.modelStatus?.states?.targetModelState || + asEnumMember(is.status?.modelStatus?.states?.activeModelState, InferenceServiceModelState) || + asEnumMember(is.status?.modelStatus?.states?.targetModelState, InferenceServiceModelState) || InferenceServiceModelState.UNKNOWN; export const getInferenceServiceStatusMessage = (is: InferenceServiceKind): string => { diff --git a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx index 85c1684d85..82a4fd840d 100644 --- a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx @@ -28,8 +28,9 @@ const EnsureMetricsAvailable: React.FC = ({ let readyCount = 0; metrics.forEach((metric) => { - if (data[metric].error) { - error = data[metric].error as AxiosError; + const axiosError = data[metric].error; + if (axiosError && axiosError instanceof AxiosError) { + error = axiosError; } if (data[metric].loaded) { readyCount++; diff --git a/frontend/src/pages/modelServing/screens/metrics/ModelServingMetricsContext.tsx b/frontend/src/pages/modelServing/screens/metrics/ModelServingMetricsContext.tsx index d7981dad08..e757df110e 100644 --- a/frontend/src/pages/modelServing/screens/metrics/ModelServingMetricsContext.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/ModelServingMetricsContext.tsx @@ -24,10 +24,16 @@ export enum ModelMetricType { } type ModelServingMetricsContextType = { - data: Record< - ModelMetricType | ServerMetricType, - ContextResourceData - >; + data: { + [ServerMetricType.REQUEST_COUNT]: ContextResourceData; + [ServerMetricType.AVG_RESPONSE_TIME]: ContextResourceData; + [ServerMetricType.CPU_UTILIZATION]: ContextResourceData; + [ServerMetricType.MEMORY_UTILIZATION]: ContextResourceData; + [ModelMetricType.REQUEST_COUNT_FAILED]: ContextResourceData; + [ModelMetricType.REQUEST_COUNT_SUCCESS]: ContextResourceData; + [ModelMetricType.TRUSTY_AI_SPD]: ContextResourceData; + [ModelMetricType.TRUSTY_AI_DIR]: ContextResourceData; + }; refresh: () => void; namespace: string; }; diff --git a/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx b/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx index 89d7018bfe..ade194fdc7 100644 --- a/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx @@ -19,7 +19,9 @@ const BiasChart: React.FC = ({ biasMetricConfig }) => { BIAS_CHART_CONFIGS[metricType]; const metric = React.useMemo(() => { - const metricData = data[modelMetricKey].data as PrometheusQueryRangeResponseDataResult[]; + const metricData = data[modelMetricKey].data.filter( + (x): x is PrometheusQueryRangeResponseDataResult => 'metric' in x, + ); const values = metricData.find((x) => x.metric.request === id)?.values || []; diff --git a/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx b/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx index fd7c60a5f1..e50085df55 100644 --- a/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx @@ -7,6 +7,7 @@ import { } from '~/pages/modelServing/screens/metrics/const'; import { BiasMetricType } from '~/api'; import { isMetricType } from '~/pages/modelServing/screens/metrics/utils'; +import { enumIterator } from '~/utilities/utils'; type MetricTypeFieldProps = { fieldId: string; @@ -16,6 +17,7 @@ type MetricTypeFieldProps = { const MetricTypeField: React.FC = ({ fieldId, value, onChange }) => { const [isOpen, setOpen] = React.useState(false); + return ( diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx index 8f9c511223..ed008ecf21 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx @@ -5,7 +5,6 @@ import { ModelMetricType, ModelServingMetricsContext, } from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; -import { ContextResourceData, PrometheusQueryRangeResultValue } from '~/types'; import EnsureMetricsAvailable from '~/pages/modelServing/screens/metrics/EnsureMetricsAvailable'; const ModelMeshMetrics: React.FC = () => { @@ -22,15 +21,11 @@ const ModelMeshMetrics: React.FC = () => { metrics={[ { name: 'Successful', - metric: data[ - ModelMetricType.REQUEST_COUNT_SUCCESS - ] as ContextResourceData, + metric: data[ModelMetricType.REQUEST_COUNT_SUCCESS], }, { name: 'Failed', - metric: data[ - ModelMetricType.REQUEST_COUNT_FAILED - ] as ContextResourceData, + metric: data[ModelMetricType.REQUEST_COUNT_FAILED], }, ]} color="blue" diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx index bcf76a0b15..d97fc00a2a 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx @@ -9,11 +9,6 @@ import { convertPrometheusNaNToZero, toPercentage, } from '~/pages/modelServing/screens/metrics/utils'; -import { - ContextResourceData, - PrometheusQueryRangeResponseDataResult, - PrometheusQueryRangeResultValue, -} from '~/types'; import { NamedMetricChartLine } from '~/pages/modelServing/screens/metrics/types'; const ServerGraphs: React.FC = () => { @@ -24,9 +19,7 @@ const ServerGraphs: React.FC = () => { , + metric: data[ServerMetricType.REQUEST_COUNT], }} color="blue" title="HTTP requests per 5 minutes" @@ -35,11 +28,7 @@ const ServerGraphs: React.FC = () => { - ).data.map( + metrics={data[ServerMetricType.AVG_RESPONSE_TIME].data.map( (line): NamedMetricChartLine => ({ name: line.metric.pod || '', metric: { @@ -56,9 +45,7 @@ const ServerGraphs: React.FC = () => { , + metric: data[ServerMetricType.CPU_UTILIZATION], translatePoint: toPercentage, }} color="purple" @@ -71,9 +58,7 @@ const ServerGraphs: React.FC = () => { , + metric: data[ServerMetricType.MEMORY_UTILIZATION], translatePoint: toPercentage, }} color="orange" diff --git a/frontend/src/pages/modelServing/screens/metrics/utils.tsx b/frontend/src/pages/modelServing/screens/metrics/utils.tsx index f82f06e592..2f878d30cd 100644 --- a/frontend/src/pages/modelServing/screens/metrics/utils.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/utils.tsx @@ -14,6 +14,7 @@ import { BIAS_THRESHOLD_COLOR, } from '~/pages/modelServing/screens/metrics/const'; import { PROMETHEUS_REQUEST_RESOLUTION } from '~/concepts/metrics/const'; +import { isEnumMember } from '~/utilities/utils'; import { BiasSelectOption, DomainCalculator, @@ -219,8 +220,7 @@ export const checkConfigurationFieldsValid = ( export const isMetricType = ( metricType: string | SelectOptionObject, -): metricType is BiasMetricType => - Object.values(BiasMetricType).includes(metricType as BiasMetricType); +): metricType is BiasMetricType => isEnumMember(metricType.toString(), BiasMetricType); export const byId = (arg: U): ((arg: T) => boolean) => diff --git a/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx b/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx index e85b2193fb..0a76b36591 100644 --- a/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx +++ b/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx @@ -3,6 +3,7 @@ import { Tab, Tabs, TabTitleText } from '@patternfly/react-core'; import { NotebookControllerTabTypes } from '~/pages/notebookController/const'; import NotebookServerRoutes from '~/pages/notebookController/screens/server/NotebookServerRoutes'; import { NotebookControllerContext } from '~/pages/notebookController/NotebookControllerContext'; +import { asEnumMember } from '~/utilities/utils'; import NotebookAdmin from './NotebookAdmin'; const NotebookControllerTabs: React.FC = () => { @@ -16,7 +17,10 @@ const NotebookControllerTabs: React.FC = () => { unmountOnExit onSelect={(e, eventKey) => { setImpersonating(); - setCurrentAdminTab(eventKey as NotebookControllerTabTypes); + const enumValue = asEnumMember(eventKey, NotebookControllerTabTypes); + if (enumValue !== null) { + setCurrentAdminTab(enumValue); + } }} > = ({ user, userProperty }) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const content = user[userProperty as keyof AdminViewUserData]; if (isField(content, userProperty === 'serverStatus')) { diff --git a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx index 7234cad59f..37fc1ab4e2 100644 --- a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx +++ b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx @@ -17,6 +17,7 @@ import { CUSTOM_VARIABLE, EMPTY_KEY } from '~/pages/notebookController/const'; import { EnvVarType, VariableRow } from '~/types'; import '~/pages/notebookController/NotebookController.scss'; +import { asEnumMember } from '~/utilities/utils'; type EnvironmentVariablesFieldProps = { fieldIndex: string; @@ -88,7 +89,7 @@ const EnvironmentVariablesField: React.FC = ({ type={ showPassword && variableType === 'password' ? TextInputTypes.text - : (variable.type as TextInputTypes) + : asEnumMember(variable.type, TextInputTypes) ?? undefined } value={variable.value} onChange={(e, newValue) => diff --git a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx index 84a99a7aca..4b3192712e 100644 --- a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx +++ b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx @@ -82,7 +82,9 @@ const EnvironmentVariablesRow: React.FC = ({ onToggle={() => setTypeDropdownOpen(!typeDropdownOpen)} aria-labelledby="container-size" selections={variableRow.variableType} - onSelect={(e, selection) => updateVariableType(selection as string)} + onSelect={(e, selection) => { + updateVariableType(selection.toString()); + }} > {selectOptions} diff --git a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx index 51037b753f..63a3c4533b 100644 --- a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx +++ b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx @@ -44,10 +44,10 @@ const ExecutionDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, co const [artifactTypes, artifactTypesLoaded] = useGetArtifactTypes(); const allEvents = parseEventsByType(events); - const artifactTypeMap = artifactTypes.reduce((acc, artifactType) => { + const artifactTypeMap = artifactTypes.reduce>((acc, artifactType) => { acc[artifactType.getId()] = artifactType.getName(); return acc; - }, {} as Record); + }, {}); const error = executionError || eventsError; diff --git a/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx b/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx index a2e2e6a45e..62ddbacdfd 100644 --- a/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx +++ b/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx @@ -30,7 +30,12 @@ const GlobalPipelineRunsTab: React.FC = () => { return ( setSearchParams({ runType: tabId as PipelineRunType })} + onSelect={(_event, tabId) => { + const enumValue = asEnumMember(tabId, PipelineRunType); + if (enumValue !== null) { + setSearchParams({ runType: enumValue }); + } + }} aria-label="Pipeline run page tabs" role="region" className="odh-pipeline-runs-page-tabs" diff --git a/frontend/src/pages/projects/ProjectDetailsContext.tsx b/frontend/src/pages/projects/ProjectDetailsContext.tsx index 825fb866ec..2e7ea07e1d 100644 --- a/frontend/src/pages/projects/ProjectDetailsContext.tsx +++ b/frontend/src/pages/projects/ProjectDetailsContext.tsx @@ -52,6 +52,7 @@ type ProjectDetailsContextType = { export const ProjectDetailsContext = React.createContext({ // We never will get into a case without a project, so fudge the default value + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions currentProject: null as unknown as ProjectKind, refreshAllProjectData: () => undefined, filterTokens: () => [], diff --git a/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx b/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx index 6a2b420559..0561d4d81e 100644 --- a/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx +++ b/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx @@ -58,11 +58,14 @@ const DeployedModelsSection: React.FC = ({ isMultiPl return; } if (isMultiPlatform) { - const modelInferenceServices = modelServers.reduce((acc, modelServer) => { - const services = getInferenceServiceFromServingRuntime(inferenceServices, modelServer); - acc.push(...services); - return acc; - }, [] as InferenceServiceKind[]); + const modelInferenceServices = modelServers.reduce( + (acc, modelServer) => { + const services = getInferenceServiceFromServingRuntime(inferenceServices, modelServer); + acc.push(...services); + return acc; + }, + [], + ); setDeployedModels(modelInferenceServices); } else { setDeployedModels(inferenceServices); diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx index 087af37f9d..7d876cb12b 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx @@ -4,6 +4,7 @@ import { EnvironmentVariableType, EnvVariableData, } from '~/pages/projects/types'; +import { asEnumMember } from '~/utilities/utils'; import EnvDataTypeField from './EnvDataTypeField'; import GenericKeyValuePairField from './GenericKeyValuePairField'; import { EMPTY_KEY_VALUE_PAIR } from './const'; @@ -22,7 +23,9 @@ const DEFAULT_ENV: EnvVariableData = { const EnvConfigMap: React.FC = ({ env = DEFAULT_ENV, onUpdate }) => ( onUpdate({ ...env, category: value as ConfigMapCategory, data: [] })} + onSelection={(value) => + onUpdate({ ...env, category: asEnumMember(value, ConfigMapCategory), data: [] }) + } options={{ [ConfigMapCategory.GENERIC]: { label: 'Key / value', diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx index b5abf71d37..aab8fe07c0 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { EnvironmentVariableType, EnvVariableData, SecretCategory } from '~/pages/projects/types'; +import { asEnumMember } from '~/utilities/utils'; import EnvDataTypeField from './EnvDataTypeField'; import GenericKeyValuePairField from './GenericKeyValuePairField'; import { EMPTY_KEY_VALUE_PAIR } from './const'; @@ -18,7 +19,9 @@ const DEFAULT_ENV: EnvVariableData = { const EnvSecret: React.FC = ({ env = DEFAULT_ENV, onUpdate }) => ( onUpdate({ ...env, category: value as SecretCategory, data: [] })} + onSelection={(value) => + onUpdate({ ...env, category: asEnumMember(value, SecretCategory), data: [] }) + } options={{ [SecretCategory.GENERIC]: { label: 'Key / value', diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx index 259f796cb3..a882c76690 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx @@ -4,6 +4,7 @@ import { Select, SelectOption } from '@patternfly/react-core/deprecated'; import { MinusCircleIcon } from '@patternfly/react-icons'; import { EnvironmentVariableType, EnvVariable } from '~/pages/projects/types'; import IndentSection from '~/pages/projects/components/IndentSection'; +import { asEnumMember } from '~/utilities/utils'; import EnvTypeSwitch from './EnvTypeSwitch'; type EnvTypeSelectFieldProps = { @@ -32,10 +33,13 @@ const EnvTypeSelectField: React.FC = ({ aria-label="Select environment variable type" onSelect={(e, value) => { if (typeof value === 'string') { - onUpdate({ - type: value as EnvironmentVariableType, - }); - setOpen(false); + const enumValue = asEnumMember(value, EnvironmentVariableType); + if (enumValue !== null) { + onUpdate({ + type: enumValue, + }); + setOpen(false); + } } }} > diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts b/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts index d49b49aae5..e73f077feb 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts @@ -7,12 +7,14 @@ export const removeArrayItem = (values: T[], index: number): T[] => values.filter((v, i) => i !== index); export const isConfigMapKind = (object: unknown): object is ConfigMapKind => - (object as ConfigMapKind).kind === 'ConfigMap'; + typeof object === 'object' && object !== null && 'kind' in object && object.kind === 'ConfigMap'; export const isSecretKind = (object: unknown): object is SecretKind => - (object as SecretKind).kind === 'Secret'; + typeof object === 'object' && object !== null && 'kind' in object && object.kind === 'Secret'; export const isStringKeyValuePairObject = (object: unknown): object is Record => - Object.entries(object as Record).every( + typeof object === 'object' && + object !== null && + Object.entries(object).every( ([key, value]) => typeof key === 'string' && typeof value === 'string', ); diff --git a/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts b/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts index 54bbf8397f..b68507988d 100644 --- a/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts +++ b/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts @@ -81,7 +81,10 @@ export const getImageVersionSelectOptionObject = ( export const isImageVersionSelectOptionObject = ( object: unknown, ): object is ImageVersionSelectOptionObjectType => - (object as ImageVersionSelectOptionObjectType | undefined)?.imageVersion !== undefined; + typeof object === 'object' && + object !== null && + 'imageVersion' in object && + object.imageVersion !== undefined; /******************* Compare utils for sorting *******************/ const getBuildNumber = (build: BuildKind): number => { const buildNumber = build.metadata.annotations?.['openshift.io/build.number'] || '-1'; diff --git a/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts b/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts index 4c2c3987f3..0e08b4e05f 100644 --- a/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts +++ b/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { getNotebookBuildConfigs, getBuildsForBuildConfig } from '~/api'; import useNotification from '~/utilities/useNotification'; -import { BuildConfigKind, BuildKind, BuildPhase } from '~/k8sTypes'; +import { BuildConfigKind, BuildPhase } from '~/k8sTypes'; import { BuildStatus } from './types'; import { compareBuilds } from './spawnerUtils'; @@ -26,7 +26,7 @@ const useBuildStatuses = (namespace?: string): BuildStatus[] => { imageStreamVersion: buildConfig.spec.output.to.name, }; } - const mostRecent = builds.toSorted(compareBuilds).pop() as BuildKind; + const mostRecent = builds.toSorted(compareBuilds)[builds.length - 1]; return { name: buildNotebookName, status: mostRecent.status.phase, diff --git a/frontend/src/redux/context.ts b/frontend/src/redux/context.ts index 82105b8f35..e93bf2751a 100644 --- a/frontend/src/redux/context.ts +++ b/frontend/src/redux/context.ts @@ -2,5 +2,6 @@ import * as React from 'react'; import { ReactReduxContextValue } from 'react-redux'; export const ReduxContext = React.createContext( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions {} as ReactReduxContextValue, ); diff --git a/frontend/src/redux/store/store.ts b/frontend/src/redux/store/store.ts index 5eb7522efd..aa01b385ce 100644 --- a/frontend/src/redux/store/store.ts +++ b/frontend/src/redux/store/store.ts @@ -5,7 +5,7 @@ import appReducer from '~/redux/reducers/appReducer'; import { ODH_PRODUCT_NAME } from '~/utilities/const'; const composeEnhancers = - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?.({ name: ODH_PRODUCT_NAME, }) || compose; @@ -16,7 +16,7 @@ export const store = configureStore(); // Create a separate for the for the dynamic plugin SDK const sdkComposeEnhancers = - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?.({ name: `${ODH_PRODUCT_NAME} - Dynamic Plugin SDK`, }) || compose; diff --git a/frontend/src/typeHelpers.ts b/frontend/src/typeHelpers.ts index 6df5120c04..2a22a458fe 100644 --- a/frontend/src/typeHelpers.ts +++ b/frontend/src/typeHelpers.ts @@ -1,3 +1,5 @@ +import { isEnumMember } from '~/utilities/utils'; + /** * The type `{}` doesn't mean "any empty object", it means "any non-nullish value". * @@ -162,7 +164,7 @@ export type ExactlyOne = AtMostOne & AtLeastOne; export const isInEnum = (e: T) => (token: unknown): token is T[keyof T] => - Object.values(e).includes(token as T[keyof T]); + isEnumMember(token, e); /** * Pick keys from enum types diff --git a/frontend/src/utilities/NavData.tsx b/frontend/src/utilities/NavData.tsx index 3192d0967c..d8522e0b59 100644 --- a/frontend/src/utilities/NavData.tsx +++ b/frontend/src/utilities/NavData.tsx @@ -28,10 +28,9 @@ export type NavDataGroup = NavDataCommon & { export type NavDataItem = NavDataHref | NavDataGroup; -export const isNavDataHref = (navData: NavDataItem): navData is NavDataHref => - !!(navData as NavDataHref).href; +export const isNavDataHref = (navData: NavDataItem): navData is NavDataHref => 'href' in navData; export const isNavDataGroup = (navData: NavDataItem): navData is NavDataGroup => - !!(navData as NavDataGroup).children; + 'children' in navData; const useAreaCheck = (area: SupportedArea, success: T[]): T[] => useIsAreaAvailable(area).status ? success : []; diff --git a/frontend/src/utilities/__tests__/utils.spec.ts b/frontend/src/utilities/__tests__/utils.spec.ts index 04f4135917..736c302d85 100644 --- a/frontend/src/utilities/__tests__/utils.spec.ts +++ b/frontend/src/utilities/__tests__/utils.spec.ts @@ -1,4 +1,4 @@ -import { asEnumMember, isEnumMember } from '~/utilities/utils'; +import { asEnumMember, enumIterator, isEnumMember } from '~/utilities/utils'; enum Test { first = '1st', @@ -87,3 +87,21 @@ describe('isEnumMember', () => { expect(isEnumMember(null, TestNumeric)).toBe(false); }); }); + +describe('enumIterator', () => { + it('should iterate over enum values', () => { + expect(enumIterator(Test)).toEqual([ + ['first', '1st'], + ['second', '2nd'], + ]); + expect(enumIterator(TestMixed)).toEqual([ + ['first', 1], + ['second', '2nd'], + ['third', '3rd'], + ]); + expect(enumIterator(TestNumeric)).toEqual([ + ['first', 0], + ['second', 1], + ]); + }); +}); diff --git a/frontend/src/utilities/useAcceleratorProfileState.ts b/frontend/src/utilities/useAcceleratorProfileState.ts index f777fc594b..239de80f51 100644 --- a/frontend/src/utilities/useAcceleratorProfileState.ts +++ b/frontend/src/utilities/useAcceleratorProfileState.ts @@ -10,7 +10,7 @@ import { TolerationOperator, } from '~/types'; import useGenericObjectState, { GenericObjectState } from '~/utilities/useGenericObjectState'; -import { getAcceleratorProfileCount } from '~/utilities/utils'; +import { getAcceleratorProfileCount, isEnumMember } from '~/utilities/utils'; export type AcceleratorProfileState = { acceleratorProfile?: AcceleratorProfileKind; @@ -65,9 +65,8 @@ const useAcceleratorProfileState = ( } else { // check if there is accelerator usage in the container // this is to handle the case where the accelerator is disabled, deleted, or empty - const containerResourceAttributes = Object.values(ContainerResourceAttributes) as string[]; const possibleAcceleratorRequests = Object.entries(resources.requests ?? {}) - .filter(([key]) => !containerResourceAttributes.includes(key)) + .filter(([key]) => !isEnumMember(key, ContainerResourceAttributes)) .map(([key, value]) => ({ identifier: key, count: value })); if (possibleAcceleratorRequests.length > 0) { // check if they are just using the nvidia.com/gpu diff --git a/frontend/src/utilities/useDraggableTable.ts b/frontend/src/utilities/useDraggableTable.ts index 6fa8540dcb..9ea56d503f 100644 --- a/frontend/src/utilities/useDraggableTable.ts +++ b/frontend/src/utilities/useDraggableTable.ts @@ -125,7 +125,7 @@ const useDraggableTable = ( return; } - const curListItem = (evt.target as HTMLTableSectionElement).closest('tr'); + const curListItem = evt.target instanceof HTMLElement ? evt.target.closest('tr') : null; if ( !curListItem || !bodyRef.current.contains(curListItem) || @@ -159,7 +159,7 @@ const useDraggableTable = ( ); const onDragEnd = React.useCallback((evt) => { - const target = evt.currentTarget as HTMLTableRowElement; + const target = evt.currentTarget; target.classList.remove(styles.modifiers.ghostRow); target.setAttribute('aria-pressed', 'false'); setDraggedItemId(''); diff --git a/frontend/src/utilities/useWatchNotebooksForUsers.tsx b/frontend/src/utilities/useWatchNotebooksForUsers.tsx index 13a09e4f75..9451a16d0c 100644 --- a/frontend/src/utilities/useWatchNotebooksForUsers.tsx +++ b/frontend/src/utilities/useWatchNotebooksForUsers.tsx @@ -54,10 +54,13 @@ const useWatchNotebooksForUsers = ( setLoadError(undefined); } - const newNotebooks = successes.reduce((acc, { value }) => { - acc[value.name] = value.data; - return acc; - }, {} as UsernameMap); + const newNotebooks = successes.reduce>( + (acc, { value }) => { + acc[value.name] = value.data; + return acc; + }, + {}, + ); setNotebooks((prevState) => ({ ...prevState, ...newNotebooks })); setLoaded(true); }) diff --git a/frontend/src/utilities/utils.ts b/frontend/src/utilities/utils.ts index 2b7def86e2..f29ef82928 100644 --- a/frontend/src/utilities/utils.ts +++ b/frontend/src/utilities/utils.ts @@ -140,6 +140,7 @@ export const getDashboardMainContainer = (): HTMLElement => document.getElementById(DASHBOARD_MAIN_CONTAINER_ID) || document.body; export const isHTMLInputElement = (object: unknown): object is HTMLInputElement => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions (object as Partial).value !== undefined; export const normalizeBetween = (value: number, min?: number, max?: number): number => { @@ -159,13 +160,19 @@ export const getAcceleratorProfileCount = ( resources: ContainerResources, ): number => Number(resources.requests?.[acceleratorProfile.spec.identifier] ?? 0); +export const enumIterator = (e: T): [keyof T, T[keyof T]][] => + Object.entries(e) + .filter(([key]) => Number.isNaN(Number(key))) + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + .map(([key, value]) => [key as keyof T, value]); + export const asEnumMember = ( - member: T[keyof T] | string | number | null, + member: T[keyof T] | string | number | undefined | null, e: T, ): T[keyof T] | null => (isEnumMember(member, e) ? member : null); export const isEnumMember = ( - member: T[keyof T] | string | number | null, + member: T[keyof T] | string | number | undefined | unknown | null, e: T, ): member is T[keyof T] => { if (member != null) { From b94f94b330120ec8690a8a4fe81454a9cc2ed218 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Tue, 18 Jun 2024 16:29:59 -0500 Subject: [PATCH 14/14] [RHOAIENG-8124]: show pipeline group label on hover at low scale fix(pipelineDefaultTaskGroup): format fix(pipelineDefaultTaskGroup): remove unused declarations --- frontend/package-lock.json | 49 ++++++++----------- frontend/package.json | 2 +- .../topology/PipelineDefaultTaskGroup.tsx | 37 ++++++-------- 3 files changed, 38 insertions(+), 50 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 549f0934fa..177a1f4edd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,7 +23,7 @@ "@patternfly/react-styles": "^5.2.1", "@patternfly/react-table": "^5.2.1", "@patternfly/react-tokens": "^5.2.1", - "@patternfly/react-topology": "^5.4.0-prerelease.6", + "@patternfly/react-topology": "^5.4.0-prerelease.9", "@patternfly/react-virtualized-extension": "^5.0.0", "@types/classnames": "^2.3.1", "axios": "^1.6.4", @@ -2239,6 +2239,22 @@ "ms": "^2.1.1" } }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.2.tgz", + "integrity": "sha512-F09dphqvHsbe/6C2t2unbmpr5q41BNPEfJCdn8Z7aEBpVSy/zFQ/b4SWsweQjWNsYMDvE2ffNUN8X0CeFsEGNw==", + "dependencies": { + "@dagrejs/graphlib": "2.2.2" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.2.tgz", + "integrity": "sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==", + "engines": { + "node": ">17.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -4074,19 +4090,18 @@ "integrity": "sha512-VYK0uVP2/2RJ7ZshJCCLeq0Boih5I1bv+9Z/Bg6h12dCkLs85XsxAX9Ve+BGIo5DF54/mzcRHE1RKYap4ISXuw==" }, "node_modules/@patternfly/react-topology": { - "version": "5.4.0-prerelease.6", - "resolved": "https://registry.npmjs.org/@patternfly/react-topology/-/react-topology-5.4.0-prerelease.6.tgz", - "integrity": "sha512-suyY+UOW9GgcDjt0HMHTKYx9UePdHAF3BSZN0Xt1zFtgR3AFMl3DOuAeYva4Yyk18OkA0D9gRe29283kgG536Q==", + "version": "5.4.0-prerelease.9", + "resolved": "https://registry.npmjs.org/@patternfly/react-topology/-/react-topology-5.4.0-prerelease.9.tgz", + "integrity": "sha512-mECBtZ+MsnOwONM56a5x0YGjHg+Z7D6AOIGEq2Tz1DRDTq2hnw+naMLq+oQRs8ijSqhpvaA/IYHY3Bi/CW412A==", "dependencies": { + "@dagrejs/dagre": "1.1.2", "@patternfly/react-core": "^5.1.1", "@patternfly/react-icons": "^5.1.1", "@patternfly/react-styles": "^5.1.1", "@types/d3": "^7.4.0", "@types/d3-force": "^1.2.1", - "@types/dagre": "0.7.42", "@types/react-measure": "^2.0.6", "d3": "^7.8.0", - "dagre": "0.8.2", "mobx": "^6.9.0", "mobx-react": "^7.6.0", "point-in-svg-path": "^1.0.1", @@ -4756,11 +4771,6 @@ "@types/d3-selection": "*" } }, - "node_modules/@types/dagre": { - "version": "0.7.42", - "resolved": "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.42.tgz", - "integrity": "sha512-knVdi1Ul8xYgJ0wdhQ+/2YGJFKJFa/5srcPII9zvOs4KhsHfpnFrSTQXATYmjslglxRMif3Lg+wEZ0beag+94A==" - }, "node_modules/@types/dompurify": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", @@ -9284,15 +9294,6 @@ "node": ">=12" } }, - "node_modules/dagre": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.2.tgz", - "integrity": "sha512-TEOOGZOkCOgCG7AoUIq64sJ3d21SMv8tyoqteLpX+UsUsS9Qw8iap4hhogXY4oB3r0bbZuAjO0atAilgCmsE0Q==", - "dependencies": { - "graphlib": "^2.1.5", - "lodash": "^4.17.4" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -11836,14 +11837,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "devOptional": true }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "dependencies": { - "lodash": "^4.17.15" - } - }, "node_modules/grpc-web": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/grpc-web/-/grpc-web-1.5.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index c6ed706f3d..49d42f0ab1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -66,7 +66,7 @@ "@patternfly/react-styles": "^5.2.1", "@patternfly/react-table": "^5.2.1", "@patternfly/react-tokens": "^5.2.1", - "@patternfly/react-topology": "^5.4.0-prerelease.6", + "@patternfly/react-topology": "^5.4.0-prerelease.9", "@patternfly/react-virtualized-extension": "^5.0.0", "@types/classnames": "^2.3.1", "axios": "^1.6.4", diff --git a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx index a48b905eaf..b877763c03 100644 --- a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx +++ b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx @@ -8,10 +8,7 @@ import { Node, GraphElement, RunStatus, - DEFAULT_LAYER, - Layer, ScaleDetailsLevel, - TOP_LAYER, NodeModel, useHover, PipelineNodeModel, @@ -65,7 +62,7 @@ const DefaultTaskGroupInner: React.FunctionComponent - - {element.isCollapsed() ? ( - - {groupNode} - - ) : ( - groupNode - )} - - + + {element.isCollapsed() ? ( + + {groupNode} + + ) : ( + groupNode + )} + ); }, );