Skip to content

Commit

Permalink
[RHOAIENG-2986] Artifacts - Initial Infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuzz0 committed Apr 17, 2024
1 parent 99fe109 commit bd833fd
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 22 deletions.
10 changes: 9 additions & 1 deletion frontend/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { InvalidArgoDeploymentAlert } from '~/concepts/pipelines/content/Invalid
import ApplicationsPage from '~/pages/ApplicationsPage';
import UnauthorizedError from '~/pages/UnauthorizedError';
import { useUser } from '~/redux/selectors';
import { globExperimentsAll, globPipelineRunsAll, globPipelinesAll } from '~/routes';
import {
globArtifactsAll,
globExperimentsAll,
globPipelineRunsAll,
globPipelinesAll,
} from '~/routes';
import { useCheckJupyterEnabled } from '~/utilities/notebookControllerUtils';

const InstalledApplications = React.lazy(
Expand All @@ -30,6 +35,8 @@ const GlobalPipelineExperimentRoutes = React.lazy(
() => import('../pages/pipelines/GlobalPipelineExperimentsRoutes'),
);

const GlobalArtifactsRoutes = React.lazy(() => import('../pages/pipelines/GlobalArtifactsRoutes'));

const GlobalDistributedWorkloadsRoutes = React.lazy(
() => import('../pages/distributedWorkloads/GlobalDistributedWorkloadsRoutes'),
);
Expand Down Expand Up @@ -91,6 +98,7 @@ const AppRoutes: React.FC = () => {
<Route path={globPipelinesAll} element={<GlobalPipelinesRoutes />} />
<Route path={globPipelineRunsAll} element={<GlobalPipelineRunsRoutes />} />
<Route path={globExperimentsAll} element={<GlobalPipelineExperimentRoutes />} />
<Route path={globArtifactsAll} element={<GlobalArtifactsRoutes />} />

<Route path="/distributedWorkloads/*" element={<GlobalDistributedWorkloadsRoutes />} />

Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/table/TableBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,11 @@ const TableBase = <T,>({
<ToolbarContent>
{toolbarContent}
{showPagination && (
<ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
<ToolbarItem
variant="pagination"
align={{ default: 'alignRight' }}
className="pf-v5-u-pr-lg"
>
{pagination('top')}
</ToolbarItem>
)}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/ApplicationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@patternfly/react-core';

type ApplicationsPageProps = {
title: React.ReactNode;
title?: React.ReactNode;
breadcrumb?: React.ReactNode;
description?: React.ReactNode;
loaded: boolean;
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/pages/pipelines/GlobalArtifactsRoutes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Navigate, Route } from 'react-router-dom';

import ProjectsRoutes from '~/concepts/projects/ProjectsRoutes';
import GlobalPipelineCoreLoader from '~/pages/pipelines/global/GlobalPipelineCoreLoader';
import { artifactsBaseRoute } from '~/routes';
import { GlobalArtifactsPage } from './global/experiments/artifacts';

const GlobalArtifactsRoutes: React.FC = () => (
<ProjectsRoutes>
<Route
path="/:namespace?/*"
element={<GlobalPipelineCoreLoader getInvalidRedirectPath={artifactsBaseRoute} />}
>
<Route index element={<GlobalArtifactsPage />} />
<Route path="*" element={<Navigate to="." />} />
</Route>
</ProjectsRoutes>
);

export default GlobalArtifactsRoutes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';

import {
Bullseye,
EmptyState,
EmptyStateBody,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
Spinner,
} from '@patternfly/react-core';
import { ExclamationCircleIcon, PlusCircleIcon } from '@patternfly/react-icons';

import { useGetArtifactsList } from './useGetArtifactsList';

export const ArtifactsListTable: React.FC = () => {
const [artifacts, isArtifactsLoaded, artifactsError] = useGetArtifactsList();

if (artifactsError) {
return (
<Bullseye>
<EmptyState variant={EmptyStateVariant.lg}>
<EmptyStateHeader
titleText="There was an issue loading artifacts"
icon={<EmptyStateIcon icon={ExclamationCircleIcon} />}
headingLevel="h2"
/>
<EmptyStateBody>{artifactsError.message}</EmptyStateBody>
</EmptyState>
</Bullseye>
);
}

if (!isArtifactsLoaded) {
return (
<Bullseye>
<Spinner />
</Bullseye>
);
}

if (!artifacts?.length) {
return (
<EmptyState data-testid="artifacts-list-empty-state" variant={EmptyStateVariant.lg}>
<EmptyStateHeader
titleText="No artifacts"
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
headingLevel="h4"
/>
<EmptyStateBody>
No artifacts have been generated from experiments within this project. Select a different
project, or execute an experiment from the <b>Experiments and runs</b> page.
</EmptyStateBody>
</EmptyState>
);
}

return <></>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

import { usePipelinesAPI } from '~/concepts/pipelines/context';
import PipelineServerActions from '~/concepts/pipelines/content/PipelineServerActions';
import PipelineCoreApplicationPage from '~/pages/pipelines/global/PipelineCoreApplicationPage';
import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability';
import EnsureCompatiblePipelineServer from '~/concepts/pipelines/EnsureCompatiblePipelineServer';
import { artifactsBaseRoute } from '~/routes';
import { ArtifactsListTable } from './ArtifactsListTable';

export const GlobalArtifactsPage: React.FC = () => {
const pipelinesAPI = usePipelinesAPI();

return (
<PipelineCoreApplicationPage
title="Artifacts"
description="View your artifacts and their metadata."
headerAction={<PipelineServerActions isDisabled={!pipelinesAPI.pipelinesServer.installed} />}
getRedirectPath={artifactsBaseRoute}
overrideChildPadding
>
<EnsureAPIAvailability>
<EnsureCompatiblePipelineServer>
<ArtifactsListTable />
</EnsureCompatiblePipelineServer>
</EnsureAPIAvailability>
</PipelineCoreApplicationPage>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GlobalArtifactsPage } from './GlobalArtifactsPage';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { Artifact, GetArtifactsRequest } from '~/third_party/mlmd';
import useFetchState, { FetchState } from '~/utilities/useFetchState';

export const useGetArtifactsList = (
refreshRate?: number,
): FetchState<Artifact.AsObject[] | null> => {
const { metadataStoreServiceClient } = usePipelinesAPI();

const fetchArtifactsList = React.useCallback(async () => {
const response = await metadataStoreServiceClient.getArtifacts(new GetArtifactsRequest());
return response.toObject().artifactsList;
}, [metadataStoreServiceClient]);

return useFetchState(fetchArtifactsList, null, {
refreshRate,
});
};
5 changes: 5 additions & 0 deletions frontend/src/routes/pipelines/artifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const artifactsRootPath = '/artifacts';
export const globArtifactsAll = `${artifactsRootPath}/*`;

export const artifactsBaseRoute = (namespace: string | undefined): string =>
!namespace ? artifactsRootPath : `${artifactsRootPath}/${namespace}`;
1 change: 1 addition & 0 deletions frontend/src/routes/pipelines/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './global';
export * from './project';
export * from './experiments';
export * from './artifacts';
export * from './runs';
46 changes: 27 additions & 19 deletions frontend/src/utilities/NavData.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as React from 'react';
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';
import { useUser } from '~/redux/selectors';
import { experimentsRootPath, routePipelineRuns, routePipelines } from '~/routes';
import {
artifactsRootPath,
experimentsRootPath,
routePipelineRuns,
routePipelines,
} from '~/routes';

type NavDataCommon = {
id: string;
Expand Down Expand Up @@ -54,7 +59,7 @@ const useDSPipelinesNav = (): NavDataItem[] => {
return [];
}

const pipelinesNav: NavDataItem[] = [
return [
{
id: 'pipelines',
group: { id: 'pipelines', title: 'Data Science Pipelines' },
Expand All @@ -63,24 +68,27 @@ const useDSPipelinesNav = (): NavDataItem[] => {
{ id: 'global-pipeline-runs', label: 'Runs', href: routePipelineRuns() },
],
},
...(isExperimentsAvailable
? [
{
id: 'experiments',
group: { id: 'experiments', title: 'Experiments' },
children: [
{
id: 'experiments-and-runs',
label: 'Experiments and runs',
href: experimentsRootPath,
},
{
id: 'artifacts',
label: 'Artifacts',
href: artifactsRootPath,
},
],
},
]
: []),
];

// TODO temporary solution to switch between layout options - remove with https://issues.redhat.com/browse/RHOAIENG-3826
if (isExperimentsAvailable) {
pipelinesNav.push({
id: 'experiments',
group: { id: 'experiments', title: 'Experiments' },
children: [
{
id: 'experiments-and-runs',
label: 'Experiments and runs',
href: experimentsRootPath,
},
],
});
}

return pipelinesNav;
};

const useDistributedWorkloadsNav = (): NavDataItem[] =>
Expand Down

0 comments on commit bd833fd

Please sign in to comment.