Skip to content

Commit

Permalink
Refactor pipelines context and add useSafePipelines hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Gkrumbach07 committed Apr 17, 2024
1 parent a0e5d7f commit ae17703
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 145 deletions.
24 changes: 23 additions & 1 deletion frontend/src/concepts/pipelines/apiHooks/usePipelines.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { FetchState } from '~/utilities/useFetchState';
import { FetchState, NotReadyError } from '~/utilities/useFetchState';
import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes';
import usePipelineQuery from '~/concepts/pipelines/apiHooks/usePipelineQuery';
import { PipelineListPaged, PipelineOptions } from '~/concepts/pipelines/types';
Expand All @@ -21,4 +21,26 @@ const usePipelines = (
);
};

export const useSafePipelines = (
options?: PipelineOptions,
refreshRate?: number,
): FetchState<PipelineListPaged<PipelineKFv2>> => {
const { api, pipelinesServer, apiAvailable } = usePipelinesAPI();
return usePipelineQuery<PipelineKFv2>(
React.useCallback(
(opts, params) => {
if (!apiAvailable || !pipelinesServer.compatible) {
throw new NotReadyError('Pipelines is not available');
}
return api
.listPipelines(opts, params)
.then((result) => ({ ...result, items: result.pipelines }));
},
[api, apiAvailable, pipelinesServer.compatible],
),
options,
refreshRate,
);
};

export default usePipelines;
3 changes: 2 additions & 1 deletion frontend/src/concepts/pipelines/context/PipelinesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export const PipelineContextProvider = conditionalArea<PipelineContextProviderPr
);

const routeHost = routeLoaded && pipelineAPIRouteHost ? pipelineAPIRouteHost : null;
const hostPath = namespace && dspaName ? `/api/service/pipelines/${namespace}/${dspaName}` : null;
const hostPath =
routeLoaded && namespace && dspaName ? `/api/service/pipelines/${namespace}/${dspaName}` : null;
useManageElyraSecret(namespace, pipelineNamespaceCR, routeHost);

const refreshState = React.useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,111 +13,26 @@ import {
Text,
TextContent,
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { ProjectDetailsContext } from '~/pages/projects/ProjectDetailsContext';
import usePipelineRunJobs from '~/concepts/pipelines/apiHooks/usePipelineRunJobs';
import {
usePipelineActiveRuns,
usePipelineArchivedRuns,
} from '~/concepts/pipelines/apiHooks/usePipelineRuns';
import { CreatePipelineServerButton, usePipelinesAPI } from '~/concepts/pipelines/context';
import useExperiments from '~/concepts/pipelines/apiHooks/useExperiments';
import usePipelines from '~/concepts/pipelines/apiHooks/usePipelines';
import { useSafePipelines } from '~/concepts/pipelines/apiHooks/usePipelines';
import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability';
import EnsureCompatiblePipelineServer from '~/concepts/pipelines/EnsureCompatiblePipelineServer';
import ImportPipelineButton from '~/concepts/pipelines/content/import/ImportPipelineButton';
import { ProjectObjectType, SectionType } from '~/concepts/design/utils';
import OverviewCard from '~/pages/projects/screens/detail/overview/components/OverviewCard';
import PipelinesCardItems from '~/pages/projects/screens/detail/overview/trainModels/PipelinesCardItems';
import MetricsContents from './MetricsContents';
import PipelinesOverviewCard from './PipelinesOverviewCard';
import PipelinesCardMetrics from './PipelinesCardMetrics';

const PipelinesCard: React.FC = () => {
const { pipelinesServer } = usePipelinesAPI();
const {
currentProject,
notebooks: { data: notebooks, loaded: notebooksLoaded, error: notebooksError },
} = React.useContext(ProjectDetailsContext);

const [{ items: pipelines, totalSize: pipelinesCount }, pipelinesLoaded, pipelinesError] =
usePipelines({
pageSize: 5,
});
const [{ totalSize: activeRunsCount }, activeRunsLoaded, activeRunsError] = usePipelineActiveRuns(
{
pageSize: 1,
},
);
const [{ totalSize: archivedRunsCount }, archivedRunsLoaded, archivedRunsError] =
usePipelineArchivedRuns({
pageSize: 1,
});
const [{ totalSize: scheduledCount }, scheduledLoaded, scheduledError] = usePipelineRunJobs({
pageSize: 1,
});
const [{ totalSize: experimentsCount }, experimentsLoaded, experimentsError] = useExperiments({
const [{ totalSize: pipelinesCount }] = useSafePipelines({
pageSize: 1,
});

const loaded =
!pipelinesServer.initializing &&
pipelinesLoaded &&
activeRunsLoaded &&
archivedRunsLoaded &&
scheduledLoaded &&
experimentsLoaded;

const loadError =
pipelinesError || activeRunsError || archivedRunsError || scheduledError || experimentsError;

const triggeredCount = activeRunsCount + archivedRunsCount;

const statistics = React.useMemo(
() => [
{
count: pipelinesCount,
text: pipelinesCount === 1 ? 'Pipeline' : 'Pipelines',
},
{
count: scheduledCount,
text: scheduledCount === 1 ? 'Schedule' : 'Schedules',
},
{
count: triggeredCount,
text: triggeredCount === 1 ? 'Run' : 'Runs',
},
{
count: experimentsCount,
text: experimentsCount === 1 ? 'Experiment' : 'Experiments',
},
],
[experimentsCount, pipelinesCount, scheduledCount, triggeredCount],
);

const renderContent = () => {
if (loadError) {
return (
<EmptyState variant="xs">
<EmptyStateHeader
icon={
<EmptyStateIcon
icon={() => (
<ExclamationCircleIcon
style={{
color: 'var(--pf-v5-global--danger-color--100)',
width: '32px',
height: '32px',
}}
/>
)}
/>
}
headingLevel="h3"
/>
<EmptyStateBody>{loadError.message}</EmptyStateBody>
</EmptyState>
);
}

if (!loaded) {
if (pipelinesServer.initializing) {
return (
<EmptyState variant="xs">
<EmptyStateHeader
Expand All @@ -128,7 +43,6 @@ const PipelinesCard: React.FC = () => {
</EmptyState>
);
}

if (!pipelinesServer.installed) {
return (
<>
Expand Down Expand Up @@ -167,61 +81,16 @@ const PipelinesCard: React.FC = () => {
}

return (
<EnsureCompatiblePipelineServer>
{pipelinesCount ? (
<>
<MetricsContents
title="Pipelines"
createButton={<ImportPipelineButton variant="link" />}
createText="Import pipeline"
statistics={statistics}
listItems={
<PipelinesCardItems
pipelines={pipelines}
loaded={loaded}
error={loadError}
totalCount={pipelinesCount}
currentProject={currentProject}
/>
}
/>
</>
) : (
<>
<CardBody>
<TextContent>
<Text component="small">
Pipelines are platforms for building and deploying portable and scalable
machine-learning (ML) workflows. You can import a pipeline or create one in a
workbench.
</Text>
</TextContent>
</CardBody>
<CardFooter>
<ImportPipelineButton variant="link" isInline />
</CardFooter>
</>
)}
</EnsureCompatiblePipelineServer>
<EnsureAPIAvailability>
<EnsureCompatiblePipelineServer>
<PipelinesCardMetrics />
</EnsureCompatiblePipelineServer>
</EnsureAPIAvailability>
);
};

return (
<OverviewCard
id="Pipelines"
objectType={ProjectObjectType.pipeline}
sectionType={pipelinesCount ? SectionType.training : SectionType.organize}
title="Pipelines"
popoverHeaderContent={pipelinesCount ? 'About pipelines' : undefined}
popoverBodyContent={
!pipelinesCount
? 'Pipelines are platforms for building and deploying portable and scalable machine-learning (ML) workflows. You can import a pipeline or create one in a workbench.'
: undefined
}
data-testid="section-pipelines"
>
{renderContent()}
</OverviewCard>
<PipelinesOverviewCard pipelinesCount={pipelinesCount}>{renderContent()}</PipelinesOverviewCard>
);
};

Expand Down
Loading

0 comments on commit ae17703

Please sign in to comment.