From 55a735f5f9b086e11c6789032e6ad98371c26a0b Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Fri, 19 Apr 2024 12:04:36 -0400 Subject: [PATCH] Use project display names in DW, move related utils from ~/pages/projects to ~/concepts/projects and ~/concepts/k8s Signed-off-by: Mike Turley --- .../src/__mocks__/mockProjectK8sResource.ts | 18 +++-- .../k8s/__tests__/inferenceServices.spec.ts | 2 +- frontend/src/api/k8s/inferenceServices.ts | 2 +- frontend/src/api/k8s/notebooks.ts | 2 +- frontend/src/api/k8s/projects.ts | 2 +- frontend/src/api/k8s/pvcs.ts | 2 +- frontend/src/api/k8s/secrets.ts | 2 +- frontend/src/api/k8s/servingRuntimes.ts | 2 +- .../DistributedWorkloadsContext.tsx | 17 ++++- .../src/concepts/k8s/NameDescriptionField.tsx | 2 +- .../src/concepts/k8s/__tests__/utils.spec.ts | 63 +++++++++++++++++ frontend/src/concepts/k8s/utils.ts | 16 +++++ .../content/DeletePipelineServerModal.tsx | 2 +- .../pipelines/content/createRun/RunForm.tsx | 2 +- .../experiment/CreateExperimentModal.tsx | 2 +- .../content/import/PipelineImportModal.tsx | 2 +- .../import/PipelineVersionImportModal.tsx | 2 +- .../pipelineRun/PipelineRunTabDetails.tsx | 2 +- .../src/concepts/projects/ProjectSelector.tsx | 2 +- .../concepts/projects/__tests__/utils.spec.ts | 68 ++++++++++++++++++- frontend/src/concepts/projects/utils.ts | 12 ++++ .../global/GlobalDistributedWorkloads.tsx | 2 +- .../sections/RequestedResources.tsx | 4 +- .../customServingRuntimes/utils.ts | 2 +- .../screens/global/EmptyModelServing.tsx | 2 +- .../global/InferenceServiceProject.tsx | 2 +- .../pages/modelServing/screens/global/data.ts | 2 +- .../modelServing/screens/global/utils.ts | 3 +- .../InferenceServiceServingRuntimeSection.tsx | 2 +- .../ManageInferenceServiceModal.tsx | 3 +- .../ProjectModelMetricsConfigurationPage.tsx | 2 +- .../projects/ProjectModelMetricsPage.tsx | 2 +- .../projects/ProjectServerMetricsWrapper.tsx | 2 +- .../ServingRuntimeTokenInput.tsx | 2 +- .../kServeModal/ManageKServeModal.tsx | 3 +- .../modelServing/screens/projects/utils.ts | 2 +- frontend/src/pages/modelServing/utils.ts | 2 +- .../global/GlobalPipelineCoreDetails.tsx | 2 +- .../compareRuns/ManageRunsPage.tsx | 2 +- .../pages/projects/projectSharing/utils.ts | 2 +- .../screens/detail/ProjectDetails.tsx | 2 +- .../ProjectPipelineBreadcrumbPage.tsx | 2 +- .../screens/projects/DeleteProjectModal.tsx | 2 +- .../screens/projects/ManageProjectModal.tsx | 7 +- .../projects/screens/projects/ProjectLink.tsx | 2 +- .../screens/projects/ProjectListView.tsx | 2 +- .../screens/projects/ProjectTableRow.tsx | 3 +- .../projects/screens/projects/tableData.tsx | 2 +- .../screens/spawner/EditSpawnerPage.tsx | 2 +- .../projects/screens/spawner/SpawnerPage.tsx | 7 +- frontend/src/pages/projects/utils.ts | 32 +-------- 51 files changed, 238 insertions(+), 92 deletions(-) create mode 100644 frontend/src/concepts/k8s/__tests__/utils.spec.ts create mode 100644 frontend/src/concepts/k8s/utils.ts diff --git a/frontend/src/__mocks__/mockProjectK8sResource.ts b/frontend/src/__mocks__/mockProjectK8sResource.ts index 4c00b35ffe..4ea79943e6 100644 --- a/frontend/src/__mocks__/mockProjectK8sResource.ts +++ b/frontend/src/__mocks__/mockProjectK8sResource.ts @@ -3,19 +3,23 @@ import { genUID } from '~/__mocks__/mockUtils'; import { KnownLabels, ProjectKind } from '~/k8sTypes'; type MockResourceConfigType = { + hasAnnotations?: boolean; username?: string; displayName?: string; description?: string; k8sName?: string; + creationTimestamp?: string; enableModelMesh?: boolean; isDSProject?: boolean; phase?: 'Active' | 'Terminating'; }; export const mockProjectK8sResource = ({ + hasAnnotations = true, username = 'test-user', displayName = 'Test Project', k8sName = 'test-project', + creationTimestamp = '2023-02-14T21:43:59Z', enableModelMesh, description = '', isDSProject = true, @@ -26,7 +30,7 @@ export const mockProjectK8sResource = ({ metadata: { name: k8sName, uid: genUID('project'), - creationTimestamp: '2023-02-14T21:43:59Z', + creationTimestamp, labels: { 'kubernetes.io/metadata.name': k8sName, ...(enableModelMesh !== undefined && { @@ -34,11 +38,13 @@ export const mockProjectK8sResource = ({ }), ...(isDSProject && { [KnownLabels.DASHBOARD_RESOURCE]: 'true' }), }, - annotations: { - 'openshift.io/description': description, - 'openshift.io/display-name': displayName, - 'openshift.io/requester': username, - }, + ...(hasAnnotations && { + annotations: { + ...(description && { 'openshift.io/description': description }), + ...(displayName && { 'openshift.io/display-name': displayName }), + ...(username && { 'openshift.io/requester': username }), + }, + }), resourceVersion: '1', }, status: { diff --git a/frontend/src/api/k8s/__tests__/inferenceServices.spec.ts b/frontend/src/api/k8s/__tests__/inferenceServices.spec.ts index ac45f952d9..811411af9a 100644 --- a/frontend/src/api/k8s/__tests__/inferenceServices.spec.ts +++ b/frontend/src/api/k8s/__tests__/inferenceServices.spec.ts @@ -25,7 +25,7 @@ import { } from '~/api/k8s/inferenceServices'; import { InferenceServiceModel, ProjectModel } from '~/api/models'; import { InferenceServiceKind, ProjectKind } from '~/k8sTypes'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { AcceleratorProfileState } from '~/utilities/useAcceleratorProfileState'; jest.mock('@openshift/dynamic-plugin-sdk-utils', () => ({ diff --git a/frontend/src/api/k8s/inferenceServices.ts b/frontend/src/api/k8s/inferenceServices.ts index 022b030953..370313c0c4 100644 --- a/frontend/src/api/k8s/inferenceServices.ts +++ b/frontend/src/api/k8s/inferenceServices.ts @@ -10,7 +10,7 @@ import { import { InferenceServiceModel } from '~/api/models'; import { InferenceServiceKind, K8sAPIOptions, KnownLabels } from '~/k8sTypes'; import { CreatingInferenceServiceObject } from '~/pages/modelServing/screens/types'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { applyK8sAPIOptions } from '~/api/apiMergeUtils'; import { AcceleratorProfileState } from '~/utilities/useAcceleratorProfileState'; import { getModelServingProjects } from './projects'; diff --git a/frontend/src/api/k8s/notebooks.ts b/frontend/src/api/k8s/notebooks.ts index 552098b131..85c496facf 100644 --- a/frontend/src/api/k8s/notebooks.ts +++ b/frontend/src/api/k8s/notebooks.ts @@ -14,7 +14,7 @@ import { K8sAPIOptions, KnownLabels, NotebookKind } from '~/k8sTypes'; import { usernameTranslate } from '~/utilities/notebookControllerUtils'; import { EnvironmentFromVariable, StartNotebookData } from '~/pages/projects/types'; import { ROOT_MOUNT_PATH } from '~/pages/projects/pvc/const'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { getTolerationPatch, TolerationChanges } from '~/utilities/tolerations'; import { applyK8sAPIOptions } from '~/api/apiMergeUtils'; import { diff --git a/frontend/src/api/k8s/projects.ts b/frontend/src/api/k8s/projects.ts index 84e25a2040..c0616f43be 100644 --- a/frontend/src/api/k8s/projects.ts +++ b/frontend/src/api/k8s/projects.ts @@ -11,7 +11,7 @@ import { import { K8sAPIOptions, ProjectKind } from '~/k8sTypes'; import { ProjectModel, ProjectRequestModel } from '~/api/models'; import { throwErrorFromAxios } from '~/api/errorUtils'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { ODH_PRODUCT_NAME } from '~/utilities/const'; import { LABEL_SELECTOR_DASHBOARD_RESOURCE, LABEL_SELECTOR_MODEL_SERVING_PROJECT } from '~/const'; import { NamespaceApplicationCase } from '~/pages/projects/types'; diff --git a/frontend/src/api/k8s/pvcs.ts b/frontend/src/api/k8s/pvcs.ts index bf1dd8339d..6dac9c27c5 100644 --- a/frontend/src/api/k8s/pvcs.ts +++ b/frontend/src/api/k8s/pvcs.ts @@ -10,7 +10,7 @@ import { } from '@openshift/dynamic-plugin-sdk-utils'; import { K8sAPIOptions, KnownLabels, PersistentVolumeClaimKind } from '~/k8sTypes'; import { PVCModel } from '~/api/models'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { LABEL_SELECTOR_DASHBOARD_RESOURCE } from '~/const'; import { applyK8sAPIOptions } from '~/api/apiMergeUtils'; import { CreatingStorageObject } from '~/pages/projects/types'; diff --git a/frontend/src/api/k8s/secrets.ts b/frontend/src/api/k8s/secrets.ts index 416b4f0f17..26ee8d84e6 100644 --- a/frontend/src/api/k8s/secrets.ts +++ b/frontend/src/api/k8s/secrets.ts @@ -9,7 +9,7 @@ import { import { K8sAPIOptions, KnownLabels, SecretKind } from '~/k8sTypes'; import { SecretModel } from '~/api/models'; import { genRandomChars } from '~/utilities/string'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { applyK8sAPIOptions } from '~/api/apiMergeUtils'; export const DATA_CONNECTION_PREFIX = 'aws-connection'; diff --git a/frontend/src/api/k8s/servingRuntimes.ts b/frontend/src/api/k8s/servingRuntimes.ts index 4a045ad4ba..057d7d0c55 100644 --- a/frontend/src/api/k8s/servingRuntimes.ts +++ b/frontend/src/api/k8s/servingRuntimes.ts @@ -16,7 +16,7 @@ import { import { CreatingServingRuntimeObject } from '~/pages/modelServing/screens/types'; import { ContainerResources } from '~/types'; import { getModelServingRuntimeName } from '~/pages/modelServing/utils'; -import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { applyK8sAPIOptions } from '~/api/apiMergeUtils'; import { AcceleratorProfileState } from '~/utilities/useAcceleratorProfileState'; import { getModelServingProjects } from './projects'; diff --git a/frontend/src/concepts/distributedWorkloads/DistributedWorkloadsContext.tsx b/frontend/src/concepts/distributedWorkloads/DistributedWorkloadsContext.tsx index 6eb7abab5c..dbaaa98bc3 100644 --- a/frontend/src/concepts/distributedWorkloads/DistributedWorkloadsContext.tsx +++ b/frontend/src/concepts/distributedWorkloads/DistributedWorkloadsContext.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; -import { Bullseye, Alert } from '@patternfly/react-core'; +import { Bullseye, Alert, Spinner } from '@patternfly/react-core'; import { ClusterQueueKind, LocalQueueKind, WorkloadKind } from '~/k8sTypes'; import { FetchStateObject } from '~/types'; import { DEFAULT_LIST_FETCH_STATE, DEFAULT_VALUE_FETCH_STATE } from '~/utilities/const'; import { SupportedArea, conditionalArea } from '~/concepts/areas'; import useSyncPreferredProject from '~/concepts/projects/useSyncPreferredProject'; import { ProjectsContext, byName } from '~/concepts/projects/ProjectsContext'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { useMakeFetchObject } from '~/utilities/useMakeFetchObject'; import { DEFAULT_DW_PROJECT_CURRENT_METRICS, @@ -24,7 +25,8 @@ type DistributedWorkloadsContextType = { workloads: FetchStateObject; projectCurrentMetrics: DWProjectCurrentMetrics; refreshAllData: () => void; - namespace?: string; + namespace: string; + projectDisplayName: string; }; type DistributedWorkloadsContextProviderProps = { @@ -38,6 +40,8 @@ export const DistributedWorkloadsContext = React.createContext undefined, + namespace: '', + projectDisplayName: '', }); export const DistributedWorkloadsContextProvider = @@ -100,6 +104,14 @@ export const DistributedWorkloadsContextProvider = ); } + if (!project) { + return ( + + + + ); + } + return ( {children} diff --git a/frontend/src/concepts/k8s/NameDescriptionField.tsx b/frontend/src/concepts/k8s/NameDescriptionField.tsx index 847f9e45b0..3f5f3b5a10 100644 --- a/frontend/src/concepts/k8s/NameDescriptionField.tsx +++ b/frontend/src/concepts/k8s/NameDescriptionField.tsx @@ -12,7 +12,7 @@ import { } from '@patternfly/react-core'; import { ExclamationCircleIcon, HelpIcon } from '@patternfly/react-icons'; import { NameDescType } from '~/pages/projects/types'; -import { isValidK8sName, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { isValidK8sName, translateDisplayNameForK8s } from '~/concepts/k8s/utils'; type NameDescriptionFieldProps = { nameFieldId: string; diff --git a/frontend/src/concepts/k8s/__tests__/utils.spec.ts b/frontend/src/concepts/k8s/__tests__/utils.spec.ts new file mode 100644 index 0000000000..d4c288f028 --- /dev/null +++ b/frontend/src/concepts/k8s/__tests__/utils.spec.ts @@ -0,0 +1,63 @@ +import { mockProjectK8sResource } from '~/__mocks__'; +import { + getDescriptionFromK8sResource, + getDisplayNameFromK8sResource, + isValidK8sName, + translateDisplayNameForK8s, +} from '~/concepts/k8s/utils'; + +describe('getDisplayNameFromK8sResource', () => { + it('gets the display name when present', () => { + const mockProject = mockProjectK8sResource({ + k8sName: 'my-project', + displayName: 'My Project', + }); + expect(getDisplayNameFromK8sResource(mockProject)).toBe('My Project'); + }); + + it('uses the resource name if no display name is present', () => { + const mockProject = mockProjectK8sResource({ + k8sName: 'my-project', + displayName: '', + }); + expect(getDisplayNameFromK8sResource(mockProject)).toBe('my-project'); + }); +}); + +describe('getDescriptionFromK8sResource', () => { + it('gets the description', () => { + const mockProject = mockProjectK8sResource({ description: 'This is a test project' }); + expect(getDescriptionFromK8sResource(mockProject)).toBe('This is a test project'); + }); + + it('returns empty string if no description', () => { + const mockProject = mockProjectK8sResource({ description: '' }); + expect(getDescriptionFromK8sResource(mockProject)).toBe(''); + }); +}); + +describe('translateDisplayNameForK8s', () => { + it('translates a string into a valid k8s name', () => { + expect(translateDisplayNameForK8s('Test Project 1')).toBe('test-project-1'); + expect(translateDisplayNameForK8s("John Doe's Cool Project!")).toBe('john-does-cool-project'); + expect(translateDisplayNameForK8s('$ymbols & Capitals and Spaces! (These are invalid!)')).toBe( + 'ymbols--capitals-and-spaces-these-are-invalid', + ); + }); +}); + +describe('isValidK8sName', () => { + it('identifies invalid names', () => { + expect(isValidK8sName('')).toBe(false); + expect(isValidK8sName('Test Project 1')).toBe(false); + expect(isValidK8sName("John Doe's Cool Project!")).toBe(false); + expect(isValidK8sName('$ymbols & Capitals and Spaces! (These are invalid!)')).toBe(false); + }); + + it('identifies valid names', () => { + expect(isValidK8sName(undefined)).toBe(true); + expect(isValidK8sName('test-project-1')).toBe(true); + expect(isValidK8sName('john-does-cool-project')).toBe(true); + expect(isValidK8sName('ymbols--capitals-and-spaces-these-are-invalid')).toBe(true); + }); +}); diff --git a/frontend/src/concepts/k8s/utils.ts b/frontend/src/concepts/k8s/utils.ts new file mode 100644 index 0000000000..ed4d410678 --- /dev/null +++ b/frontend/src/concepts/k8s/utils.ts @@ -0,0 +1,16 @@ +import { K8sDSGResource } from '~/k8sTypes'; + +export const getDisplayNameFromK8sResource = (resource: K8sDSGResource): string => + resource.metadata.annotations?.['openshift.io/display-name'] || resource.metadata.name; +export const getDescriptionFromK8sResource = (resource: K8sDSGResource): string => + resource.metadata.annotations?.['openshift.io/description'] || ''; + +export const translateDisplayNameForK8s = (name: string): string => + name + .trim() + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^A-Za-z0-9-]/g, ''); + +export const isValidK8sName = (name?: string): boolean => + name === undefined || (name.length > 0 && /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name)); diff --git a/frontend/src/concepts/pipelines/content/DeletePipelineServerModal.tsx b/frontend/src/concepts/pipelines/content/DeletePipelineServerModal.tsx index feac4b4b5f..27113a9404 100644 --- a/frontend/src/concepts/pipelines/content/DeletePipelineServerModal.tsx +++ b/frontend/src/concepts/pipelines/content/DeletePipelineServerModal.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import DeleteModal from '~/pages/projects/components/DeleteModal'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { deleteServer } from '~/concepts/pipelines/utils'; diff --git a/frontend/src/concepts/pipelines/content/createRun/RunForm.tsx b/frontend/src/concepts/pipelines/content/createRun/RunForm.tsx index d7e2906fde..e04b565aa2 100644 --- a/frontend/src/concepts/pipelines/content/createRun/RunForm.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/RunForm.tsx @@ -4,7 +4,7 @@ import NameDescriptionField from '~/concepts/k8s/NameDescriptionField'; import { RunFormData, RunTypeOption } from '~/concepts/pipelines/content/createRun/types'; import { ValueOf } from '~/typeHelpers'; import { ParamsSection } from '~/concepts/pipelines/content/createRun/contentSections/ParamsSection'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { useLatestPipelineVersion } from '~/concepts/pipelines/apiHooks/useLatestPipelineVersion'; import RunTypeSectionScheduled from '~/concepts/pipelines/content/createRun/contentSections/RunTypeSectionScheduled'; import { PipelineVersionKFv2, RuntimeConfigParameters } from '~/concepts/pipelines/kfTypes'; diff --git a/frontend/src/concepts/pipelines/content/experiment/CreateExperimentModal.tsx b/frontend/src/concepts/pipelines/content/experiment/CreateExperimentModal.tsx index 2d6c78648b..41b6128d75 100644 --- a/frontend/src/concepts/pipelines/content/experiment/CreateExperimentModal.tsx +++ b/frontend/src/concepts/pipelines/content/experiment/CreateExperimentModal.tsx @@ -10,7 +10,7 @@ import { TextInput, } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import useCreateExperimentData from '~/concepts/pipelines/content/experiment/useCreateExperimentData'; import { ExperimentKFv2 } from '~/concepts/pipelines/kfTypes'; diff --git a/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx b/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx index 8c56652a9b..8e33696a6e 100644 --- a/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx +++ b/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx @@ -12,7 +12,7 @@ import { } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { usePipelineImportModalData } from '~/concepts/pipelines/content/import/useImportModalData'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes'; import PipelineUploadRadio from './PipelineUploadRadio'; import { PipelineUploadOption } from './utils'; diff --git a/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx b/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx index 2d74cdb5bd..fc5c2a88dc 100644 --- a/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx +++ b/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx @@ -12,7 +12,7 @@ import { } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { usePipelineVersionImportModalData } from '~/concepts/pipelines/content/import/useImportModalData'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PipelineKFv2, PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes'; import PipelineSelector from '~/concepts/pipelines/content/pipelineSelector/PipelineSelector'; import { PipelineUploadOption, generatePipelineVersionName } from './utils'; diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunTabDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunTabDetails.tsx index 3cbb90490f..c0acf78813 100644 --- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunTabDetails.tsx +++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunTabDetails.tsx @@ -10,7 +10,7 @@ import { Link } from 'react-router-dom'; import { PipelineRunJobKFv2, PipelineRunKFv2 } from '~/concepts/pipelines/kfTypes'; import { getRunDuration } from '~/concepts/pipelines/content/tables/utils'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { relativeDuration } from '~/utilities/time'; import { asTimestamp, diff --git a/frontend/src/concepts/projects/ProjectSelector.tsx b/frontend/src/concepts/projects/ProjectSelector.tsx index ce57146206..75e48e809a 100644 --- a/frontend/src/concepts/projects/ProjectSelector.tsx +++ b/frontend/src/concepts/projects/ProjectSelector.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Bullseye, Flex, FlexItem } from '@patternfly/react-core'; import { Dropdown, DropdownItem, DropdownToggle } from '@patternfly/react-core/deprecated'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { byName, ProjectsContext } from '~/concepts/projects/ProjectsContext'; import { ProjectObjectType, typedObjectImage } from '~/concepts/design/utils'; diff --git a/frontend/src/concepts/projects/__tests__/utils.spec.ts b/frontend/src/concepts/projects/__tests__/utils.spec.ts index 73df4f0c8d..3a4eb6c9d7 100644 --- a/frontend/src/concepts/projects/__tests__/utils.spec.ts +++ b/frontend/src/concepts/projects/__tests__/utils.spec.ts @@ -1,4 +1,11 @@ -import { isAvailableProject } from '~/concepts/projects/utils'; +import { mockProjectK8sResource } from '~/__mocks__'; +import { + isAvailableProject, + getProjectDisplayName, + getProjectDescription, + getProjectOwner, + getProjectCreationTime, +} from '~/concepts/projects/utils'; const mockDashboardNamespace = 'mock-opendatahub'; @@ -31,3 +38,62 @@ describe('isAvailableProject', () => { expect(isAvailableProject('odh-not-dashboard', mockDashboardNamespace)).toBe(true); }); }); + +describe('getProjectDisplayName', () => { + it('gets the display name when present', () => { + const mockProject = mockProjectK8sResource({ + k8sName: 'my-project', + displayName: 'My Project', + }); + expect(getProjectDisplayName(mockProject)).toBe('My Project'); + }); + + it('uses the resource name if no display name is present', () => { + const mockProject = mockProjectK8sResource({ + k8sName: 'my-project', + displayName: '', + }); + expect(getProjectDisplayName(mockProject)).toBe('my-project'); + }); +}); + +describe('getProjectDescription', () => { + it('gets the description', () => { + const mockProject = mockProjectK8sResource({ description: 'This is a test project' }); + expect(getProjectDescription(mockProject)).toBe('This is a test project'); + }); + + it('returns empty string if no description', () => { + const mockProject = mockProjectK8sResource({ description: '' }); + expect(getProjectDescription(mockProject)).toBe(''); + }); +}); + +describe('getProjectOwner', () => { + it('gets the requester if present', () => { + const mockProject = mockProjectK8sResource({ username: 'john-doe' }); + expect(getProjectOwner(mockProject)).toBe('john-doe'); + }); + + it('returns empty string if no annotations', () => { + const mockProject = mockProjectK8sResource({ hasAnnotations: false }); + expect(getProjectOwner(mockProject)).toBe(''); + }); + + it('returns empty string if no requester', () => { + const mockProject = mockProjectK8sResource({ username: '' }); + expect(getProjectOwner(mockProject)).toBe(''); + }); +}); + +describe('getProjectCreationTime', () => { + it('returns creation timestamp as unix time integer if present', () => { + const mockProject = mockProjectK8sResource({ creationTimestamp: '2024-04-19T16:36:37.104Z' }); + expect(getProjectCreationTime(mockProject)).toBe(1713544597104); + }); + + it('returns 0 if no timestamp present', () => { + const mockProject = mockProjectK8sResource({ creationTimestamp: '' }); + expect(getProjectCreationTime(mockProject)).toBe(0); + }); +}); diff --git a/frontend/src/concepts/projects/utils.ts b/frontend/src/concepts/projects/utils.ts index f29d3db0a4..50b19c9564 100644 --- a/frontend/src/concepts/projects/utils.ts +++ b/frontend/src/concepts/projects/utils.ts @@ -1,3 +1,6 @@ +import { ProjectKind } from '~/k8sTypes'; +import { getDescriptionFromK8sResource, getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; + export const isAvailableProject = (projectName: string, dashboardNamespace: string): boolean => !( projectName.startsWith('openshift-') || @@ -7,3 +10,12 @@ export const isAvailableProject = (projectName: string, dashboardNamespace: stri projectName === 'openshift' || projectName === dashboardNamespace ); + +export const getProjectDisplayName = (project: ProjectKind): string => + getDisplayNameFromK8sResource(project); +export const getProjectDescription = (project: ProjectKind): string => + getDescriptionFromK8sResource(project); +export const getProjectOwner = (project: ProjectKind): string => + project.metadata.annotations?.['openshift.io/requester'] || ''; +export const getProjectCreationTime = (project: ProjectKind): number => + project.metadata.creationTimestamp ? new Date(project.metadata.creationTimestamp).getTime() : 0; diff --git a/frontend/src/pages/distributedWorkloads/global/GlobalDistributedWorkloads.tsx b/frontend/src/pages/distributedWorkloads/global/GlobalDistributedWorkloads.tsx index 153eefeb45..0bacde518a 100644 --- a/frontend/src/pages/distributedWorkloads/global/GlobalDistributedWorkloads.tsx +++ b/frontend/src/pages/distributedWorkloads/global/GlobalDistributedWorkloads.tsx @@ -54,7 +54,7 @@ const GlobalDistributedWorkloads: React.FC = ({ ); } - // We're all good, either no namespace is required or we have a valid one + // We're all good, we have a namespace matching a known project return ( { - const { namespace } = React.useContext(DistributedWorkloadsContext); + const { projectDisplayName } = React.useContext(DistributedWorkloadsContext); // Cap things at 110% total quota for display, but show real values in tooltips const maxDomain = numTotalSharedQuota * 1.1; @@ -58,7 +58,7 @@ const RequestedResourcesBulletChart: React.FC = { diff --git a/frontend/src/pages/modelServing/screens/global/utils.ts b/frontend/src/pages/modelServing/screens/global/utils.ts index ec06218ecd..f4098f7c8c 100644 --- a/frontend/src/pages/modelServing/screens/global/utils.ts +++ b/frontend/src/pages/modelServing/screens/global/utils.ts @@ -1,5 +1,6 @@ import { InferenceServiceKind, ProjectKind, SecretKind, PodKind } from '~/k8sTypes'; -import { getDisplayNameFromK8sResource, getProjectDisplayName } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { InferenceServiceModelState, ModelStatus } from '~/pages/modelServing/screens/types'; export const getInferenceServiceDisplayName = (is: InferenceServiceKind): string => diff --git a/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceServingRuntimeSection.tsx b/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceServingRuntimeSection.tsx index faf29daa62..ee8c3da01e 100644 --- a/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceServingRuntimeSection.tsx +++ b/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceServingRuntimeSection.tsx @@ -5,7 +5,7 @@ import { UpdateObjectAtPropAndValue } from '~/pages/projects/types'; import { CreatingInferenceServiceObject } from '~/pages/modelServing/screens/types'; import { ServingRuntimeKind } from '~/k8sTypes'; import useServingRuntimes from '~/pages/modelServing/useServingRuntimes'; -import { getDisplayNameFromK8sResource } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; type InferenceServiceServingRuntimeSectionProps = { data: CreatingInferenceServiceObject; diff --git a/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/ManageInferenceServiceModal.tsx b/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/ManageInferenceServiceModal.tsx index 65106b9f61..9f0525f12f 100644 --- a/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/ManageInferenceServiceModal.tsx +++ b/frontend/src/pages/modelServing/screens/projects/InferenceServiceModal/ManageInferenceServiceModal.tsx @@ -11,7 +11,8 @@ import DashboardModalFooter from '~/concepts/dashboard/DashboardModalFooter'; import { InferenceServiceStorageType } from '~/pages/modelServing/screens/types'; import { isAWSValid } from '~/pages/projects/screens/spawner/spawnerUtils'; import { AwsKeys } from '~/pages/projects/dataConnections/const'; -import { getProjectDisplayName, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { containsOnlySlashes, isS3PathValid } from '~/utilities/string'; import DataConnectionSection from './DataConnectionSection'; import ProjectSection from './ProjectSection'; diff --git a/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsConfigurationPage.tsx b/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsConfigurationPage.tsx index e89c9eb22f..b4e16eb2dc 100644 --- a/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsConfigurationPage.tsx +++ b/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsConfigurationPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useOutletContext } from 'react-router'; import { getInferenceServiceDisplayName } from '~/pages/modelServing/screens/global/utils'; import BiasConfigurationPage from '~/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationPage'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { ProjectModelMetricsOutletContextProps } from './ProjectModelMetricsWrapper'; const ProjectModelMetricsConfigurationPage: React.FC = () => { diff --git a/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsPage.tsx b/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsPage.tsx index d7b18fd8f2..e96e8324b7 100644 --- a/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsPage.tsx +++ b/frontend/src/pages/modelServing/screens/projects/ProjectModelMetricsPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useOutletContext } from 'react-router-dom'; import { getInferenceServiceDisplayName } from '~/pages/modelServing/screens/global/utils'; import MetricsPage from '~/pages/modelServing/screens/metrics/MetricsPage'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PerformanceMetricType } from '~/pages/modelServing/screens/types'; import { ProjectModelMetricsOutletContextProps } from './ProjectModelMetricsWrapper'; diff --git a/frontend/src/pages/modelServing/screens/projects/ProjectServerMetricsWrapper.tsx b/frontend/src/pages/modelServing/screens/projects/ProjectServerMetricsWrapper.tsx index 3f541eaf57..4b68c96574 100644 --- a/frontend/src/pages/modelServing/screens/projects/ProjectServerMetricsWrapper.tsx +++ b/frontend/src/pages/modelServing/screens/projects/ProjectServerMetricsWrapper.tsx @@ -3,7 +3,7 @@ import MetricsPage from '~/pages/modelServing/screens/metrics/MetricsPage'; import { MetricsCommonContextProvider } from '~/concepts/metrics/MetricsCommonContext'; import { ModelServingMetricsProvider } from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; import { getServerMetricsQueries } from '~/pages/modelServing/screens/metrics/utils'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PerformanceMetricType } from '~/pages/modelServing/screens/types'; import ProjectServerMetricsPathWrapper from './ProjectServerMetricsPathWrapper'; import { getModelServerDisplayName } from './utils'; diff --git a/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTokenInput.tsx b/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTokenInput.tsx index 46e7556c2c..4e6cb115ad 100644 --- a/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTokenInput.tsx +++ b/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTokenInput.tsx @@ -17,7 +17,7 @@ import { CreatingServingRuntimeObject, ServingRuntimeToken, } from '~/pages/modelServing/screens/types'; -import { translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; type ServingRuntimeTokenInputProps = { data: CreatingServingRuntimeObject | CreatingInferenceServiceObject; diff --git a/frontend/src/pages/modelServing/screens/projects/kServeModal/ManageKServeModal.tsx b/frontend/src/pages/modelServing/screens/projects/kServeModal/ManageKServeModal.tsx index 5c95fc07e0..ea6a9141dd 100644 --- a/frontend/src/pages/modelServing/screens/projects/kServeModal/ManageKServeModal.tsx +++ b/frontend/src/pages/modelServing/screens/projects/kServeModal/ManageKServeModal.tsx @@ -40,7 +40,8 @@ import { isAWSValid } from '~/pages/projects/screens/spawner/spawnerUtils'; import InferenceServiceNameSection from '~/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceNameSection'; import InferenceServiceFrameworkSection from '~/pages/modelServing/screens/projects/InferenceServiceModal/InferenceServiceFrameworkSection'; import DataConnectionSection from '~/pages/modelServing/screens/projects/InferenceServiceModal/DataConnectionSection'; -import { getProjectDisplayName, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; +import { translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { containsOnlySlashes, isS3PathValid } from '~/utilities/string'; import AuthServingRuntimeSection from '~/pages/modelServing/screens/projects/ServingRuntimeModal/AuthServingRuntimeSection'; import { useAccessReview } from '~/api'; diff --git a/frontend/src/pages/modelServing/screens/projects/utils.ts b/frontend/src/pages/modelServing/screens/projects/utils.ts index d121d23fd3..d618937ac3 100644 --- a/frontend/src/pages/modelServing/screens/projects/utils.ts +++ b/frontend/src/pages/modelServing/screens/projects/utils.ts @@ -26,7 +26,7 @@ import { DEFAULT_MODEL_SERVER_SIZES } from '~/pages/modelServing/screens/const'; import { useAppContext } from '~/app/AppContext'; import { useDeepCompareMemoize } from '~/utilities/useDeepCompareMemoize'; import { EMPTY_AWS_SECRET_DATA } from '~/pages/projects/dataConnections/const'; -import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { getDisplayNameFromServingRuntimeTemplate } from '~/pages/modelServing/customServingRuntimes/utils'; import { getServingRuntimeSize, diff --git a/frontend/src/pages/modelServing/utils.ts b/frontend/src/pages/modelServing/utils.ts index ab3491b01f..1b789ba65a 100644 --- a/frontend/src/pages/modelServing/utils.ts +++ b/frontend/src/pages/modelServing/utils.ts @@ -29,7 +29,7 @@ import { ServiceAccountKind, } from '~/k8sTypes'; import { ContainerResources } from '~/types'; -import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import { CreatingInferenceServiceObject, CreatingServingRuntimeObject, diff --git a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx index fb10e77b73..ee38646e8f 100644 --- a/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx +++ b/frontend/src/pages/pipelines/global/GlobalPipelineCoreDetails.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; import { BreadcrumbItem } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PipelineCoreDetailsPageComponent } from '~/concepts/pipelines/content/types'; import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability'; import { experimentRunsRoute, experimentSchedulesRoute, experimentsBaseRoute } from '~/routes'; diff --git a/frontend/src/pages/pipelines/global/experiments/compareRuns/ManageRunsPage.tsx b/frontend/src/pages/pipelines/global/experiments/compareRuns/ManageRunsPage.tsx index b9df7a92a3..95c92e5c68 100644 --- a/frontend/src/pages/pipelines/global/experiments/compareRuns/ManageRunsPage.tsx +++ b/frontend/src/pages/pipelines/global/experiments/compareRuns/ManageRunsPage.tsx @@ -26,7 +26,7 @@ import { } from '~/routes'; import ApplicationsPage from '~/pages/ApplicationsPage'; import { useExperimentByParams } from '~/pages/pipelines/global/experiments/useExperimentByParams'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import usePipelineFilter, { FilterOptions, diff --git a/frontend/src/pages/projects/projectSharing/utils.ts b/frontend/src/pages/projects/projectSharing/utils.ts index 8caf40a990..ed00244657 100644 --- a/frontend/src/pages/projects/projectSharing/utils.ts +++ b/frontend/src/pages/projects/projectSharing/utils.ts @@ -1,5 +1,5 @@ import { RoleBindingKind } from '~/k8sTypes'; -import { getDisplayNameFromK8sResource } from '~/pages/projects/utils'; +import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { ProjectSharingRBType, ProjectSharingRoleType } from './types'; export const getRoleBindingResourceName = (roleBinding: RoleBindingKind): string => diff --git a/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx b/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx index 37e92e9440..d2acc5f464 100644 --- a/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx +++ b/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx @@ -3,7 +3,7 @@ import { Breadcrumb, BreadcrumbItem, Flex, FlexItem } from '@patternfly/react-co import { Link } from 'react-router-dom'; import ApplicationsPage from '~/pages/ApplicationsPage'; import { ProjectDetailsContext } from '~/pages/projects/ProjectDetailsContext'; -import { getProjectDescription, getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDescription, getProjectDisplayName } from '~/concepts/projects/utils'; import GenericHorizontalBar from '~/pages/projects/components/GenericHorizontalBar'; import ProjectSharing from '~/pages/projects/projectSharing/ProjectSharing'; import ProjectSettingsPage from '~/pages/projects/projectSettings/ProjectSettingsPage'; diff --git a/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx b/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx index 1e5d362a71..37c751d9b6 100644 --- a/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx +++ b/frontend/src/pages/projects/screens/detail/pipelines/ProjectPipelineBreadcrumbPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; import { BreadcrumbItem } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { PipelineCoreDetailsPageComponent } from '~/concepts/pipelines/content/types'; import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability'; diff --git a/frontend/src/pages/projects/screens/projects/DeleteProjectModal.tsx b/frontend/src/pages/projects/screens/projects/DeleteProjectModal.tsx index 22d09032e0..7172f8f4b2 100644 --- a/frontend/src/pages/projects/screens/projects/DeleteProjectModal.tsx +++ b/frontend/src/pages/projects/screens/projects/DeleteProjectModal.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ProjectKind } from '~/k8sTypes'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { deleteProject } from '~/api'; import DeleteModal from '~/pages/projects/components/DeleteModal'; import { fireTrackingEvent } from '~/utilities/segmentIOUtils'; diff --git a/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx b/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx index 116fe79d27..795e1c4093 100644 --- a/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx +++ b/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx @@ -3,11 +3,8 @@ import { Alert, Button, Form, Modal, Stack, StackItem } from '@patternfly/react- import { createProject, updateProject } from '~/api'; import { useUser } from '~/redux/selectors'; import { ProjectKind } from '~/k8sTypes'; -import { - getProjectDescription, - getProjectDisplayName, - isValidK8sName, -} from '~/pages/projects/utils'; +import { getProjectDescription, getProjectDisplayName } from '~/concepts/projects/utils'; +import { isValidK8sName } from '~/concepts/k8s/utils'; import NameDescriptionField from '~/concepts/k8s/NameDescriptionField'; import { NameDescType } from '~/pages/projects/types'; import { ProjectsContext } from '~/concepts/projects/ProjectsContext'; diff --git a/frontend/src/pages/projects/screens/projects/ProjectLink.tsx b/frontend/src/pages/projects/screens/projects/ProjectLink.tsx index 962dbbd93c..f47a8ca06a 100644 --- a/frontend/src/pages/projects/screens/projects/ProjectLink.tsx +++ b/frontend/src/pages/projects/screens/projects/ProjectLink.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; import { ProjectKind } from '~/k8sTypes'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; type ProjectLinkProps = { project: ProjectKind; diff --git a/frontend/src/pages/projects/screens/projects/ProjectListView.tsx b/frontend/src/pages/projects/screens/projects/ProjectListView.tsx index 3e06b6e14c..9d0e30da7e 100644 --- a/frontend/src/pages/projects/screens/projects/ProjectListView.tsx +++ b/frontend/src/pages/projects/screens/projects/ProjectListView.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom'; import { Table } from '~/components/table'; import DashboardSearchField, { SearchType } from '~/concepts/dashboard/DashboardSearchField'; import { ProjectKind } from '~/k8sTypes'; -import { getProjectDisplayName, getProjectOwner } from '~/pages/projects/utils'; +import { getProjectDisplayName, getProjectOwner } from '~/concepts/projects/utils'; import { useAppContext } from '~/app/AppContext'; import LaunchJupyterButton from '~/pages/projects/screens/projects/LaunchJupyterButton'; import { ProjectsContext } from '~/concepts/projects/ProjectsContext'; diff --git a/frontend/src/pages/projects/screens/projects/ProjectTableRow.tsx b/frontend/src/pages/projects/screens/projects/ProjectTableRow.tsx index cf22ce7cd5..acd4bfe136 100644 --- a/frontend/src/pages/projects/screens/projects/ProjectTableRow.tsx +++ b/frontend/src/pages/projects/screens/projects/ProjectTableRow.tsx @@ -13,7 +13,8 @@ import { useNavigate } from 'react-router-dom'; import { KnownLabels, ProjectKind } from '~/k8sTypes'; import useProjectTableRowItems from '~/pages/projects/screens/projects/useProjectTableRowItems'; import ResourceNameTooltip from '~/components/ResourceNameTooltip'; -import { getNotebookDisplayName, getProjectOwner } from '~/pages/projects/utils'; +import { getNotebookDisplayName } from '~/pages/projects/utils'; +import { getProjectOwner } from '~/concepts/projects/utils'; import useProjectNotebookStates from '~/pages/projects/notebook/useProjectNotebookStates'; import NotebookRouteLink from '~/pages/projects/notebook/NotebookRouteLink'; import CanEnableElyraPipelinesCheck from '~/concepts/pipelines/elyra/CanEnableElyraPipelinesCheck'; diff --git a/frontend/src/pages/projects/screens/projects/tableData.tsx b/frontend/src/pages/projects/screens/projects/tableData.tsx index 9e8f09a7a9..5efcf1706b 100644 --- a/frontend/src/pages/projects/screens/projects/tableData.tsx +++ b/frontend/src/pages/projects/screens/projects/tableData.tsx @@ -1,6 +1,6 @@ import { SortableData } from '~/components/table'; import { ProjectKind } from '~/k8sTypes'; -import { getProjectCreationTime, getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectCreationTime, getProjectDisplayName } from '~/concepts/projects/utils'; export const columns: SortableData[] = [ { diff --git a/frontend/src/pages/projects/screens/spawner/EditSpawnerPage.tsx b/frontend/src/pages/projects/screens/spawner/EditSpawnerPage.tsx index 8a935ce3ea..5f582ea29c 100644 --- a/frontend/src/pages/projects/screens/spawner/EditSpawnerPage.tsx +++ b/frontend/src/pages/projects/screens/spawner/EditSpawnerPage.tsx @@ -13,7 +13,7 @@ import { import { ExclamationCircleIcon } from '@patternfly/react-icons'; import { useNavigate } from 'react-router-dom'; import { ProjectDetailsContext } from '~/pages/projects/ProjectDetailsContext'; -import { getProjectDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { NotebookState } from '~/pages/projects/notebook/types'; import SpawnerPage from './SpawnerPage'; diff --git a/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx b/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx index 34256e271b..d942915538 100644 --- a/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx +++ b/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx @@ -16,11 +16,8 @@ import GenericSidebar from '~/components/GenericSidebar'; import NameDescriptionField from '~/concepts/k8s/NameDescriptionField'; import { ProjectDetailsContext } from '~/pages/projects/ProjectDetailsContext'; import { NameDescType } from '~/pages/projects/types'; -import { - getNotebookDescription, - getNotebookDisplayName, - getProjectDisplayName, -} from '~/pages/projects/utils'; +import { getNotebookDescription, getNotebookDisplayName } from '~/pages/projects/utils'; +import { getProjectDisplayName } from '~/concepts/projects/utils'; import { NotebookKind } from '~/k8sTypes'; import useNotebookImageData from '~/pages/projects/screens/detail/notebooks/useNotebookImageData'; import useNotebookDeploymentSize from '~/pages/projects/screens/detail/notebooks/useNotebookDeploymentSize'; diff --git a/frontend/src/pages/projects/utils.ts b/frontend/src/pages/projects/utils.ts index d9d8eec43d..40964e7dbd 100644 --- a/frontend/src/pages/projects/utils.ts +++ b/frontend/src/pages/projects/utils.ts @@ -1,35 +1,7 @@ -import { - K8sDSGResource, - NotebookKind, - PersistentVolumeClaimKind, - ProjectKind, - SecretKind, -} from '~/k8sTypes'; +import { NotebookKind, PersistentVolumeClaimKind, SecretKind } from '~/k8sTypes'; +import { getDescriptionFromK8sResource, getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { NotebookState } from './notebook/types'; -export const getDisplayNameFromK8sResource = (resource: K8sDSGResource): string => - resource.metadata.annotations?.['openshift.io/display-name'] || resource.metadata.name; -export const getDescriptionFromK8sResource = (resource: K8sDSGResource): string => - resource.metadata.annotations?.['openshift.io/description'] || ''; - -export const translateDisplayNameForK8s = (name: string): string => - name - .trim() - .toLowerCase() - .replace(/\s/g, '-') - .replace(/[^A-Za-z0-9-]/g, ''); -export const isValidK8sName = (name?: string): boolean => - name === undefined || (name.length > 0 && /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name)); - -export const getProjectDisplayName = (project: ProjectKind): string => - getDisplayNameFromK8sResource(project); -export const getProjectDescription = (project: ProjectKind): string => - getDescriptionFromK8sResource(project); -export const getProjectOwner = (project: ProjectKind): string => - project.metadata.annotations?.['openshift.io/requester'] || ''; -export const getProjectCreationTime = (project: ProjectKind): number => - project.metadata.creationTimestamp ? new Date(project.metadata.creationTimestamp).getTime() : 0; - export const getNotebookDisplayName = (notebook: NotebookKind): string => getDisplayNameFromK8sResource(notebook); export const getNotebookDescription = (notebook: NotebookKind): string =>