Skip to content

Commit

Permalink
[RHOAIENG-2989] artifact node drawer details section
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuzz0 committed May 13, 2024
1 parent 22dc16b commit 045a2dc
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { MlmdContext } from '~/concepts/pipelines/apiHooks/mlmd/types';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { Artifact } from '~/third_party/mlmd';
import { GetArtifactsByContextRequest } from '~/third_party/mlmd/generated/ml_metadata/proto/metadata_store_service_pb';
import useFetchState, {
FetchState,
FetchStateCallbackPromise,
NotReadyError,
} from '~/utilities/useFetchState';

export const useArtifactsFromMlmdContext = (
context: MlmdContext | null,
refreshRate?: number,
): FetchState<Artifact[]> => {
const { metadataStoreServiceClient } = usePipelinesAPI();

const getArtifactsList = React.useCallback<FetchStateCallbackPromise<Artifact[]>>(async () => {
if (!context) {
return Promise.reject(new NotReadyError('No context'));
}

const request = new GetArtifactsByContextRequest();
request.setContextId(context.getId());
const res = await metadataStoreServiceClient.getArtifactsByContext(request);
return res.getArtifactsList();
}, [metadataStoreServiceClient, context]);

return useFetchState(getArtifactsList, [], {
refreshRate,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,27 @@ import { PipelineRunType } from '~/pages/pipelines/global/runs/types';
import { routePipelineRunsNamespace } from '~/routes';
import PipelineJobReferenceName from '~/concepts/pipelines/content/PipelineJobReferenceName';
import useExecutionsForPipelineRun from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/useExecutionsForPipelineRun';
import { usePipelineRunArtifacts } from './artifacts';

const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, contextPath }) => {
const { runId } = useParams();
const navigate = useNavigate();
const { namespace } = usePipelinesAPI();
const [runResource, runLoaded, runError] = usePipelineRunById(runId, true);
const [run, runLoaded, runError] = usePipelineRunById(runId, true);
const [version, versionLoaded, versionError] = usePipelineVersionById(
runResource?.pipeline_version_reference?.pipeline_id,
runResource?.pipeline_version_reference?.pipeline_version_id,
run?.pipeline_version_reference?.pipeline_id,
run?.pipeline_version_reference?.pipeline_version_id,
);
const pipelineSpec = version?.pipeline_spec ?? runResource?.pipeline_spec;
const pipelineSpec = version?.pipeline_spec ?? run?.pipeline_spec;
const [deleting, setDeleting] = React.useState(false);
const [detailsTab, setDetailsTab] = React.useState<RunDetailsTabSelection>(
RunDetailsTabs.DETAILS,
);
const [selectedId, setSelectedId] = React.useState<string | null>(null);

const [executions, executionsLoaded, executionsError] = useExecutionsForPipelineRun(runResource);
const nodes = usePipelineTaskTopology(pipelineSpec, runResource?.run_details, executions);
const [executions, executionsLoaded, executionsError] = useExecutionsForPipelineRun(run);
const [artifacts, isArtifactsLoaded, artifactsError] = usePipelineRunArtifacts(run);
const nodes = usePipelineTaskTopology(pipelineSpec, run?.run_details, executions, artifacts);

const selectedNode = React.useMemo(
() => nodes.find((n) => n.id === selectedId),
Expand All @@ -64,8 +66,8 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,

const getFirstNode = (firstId: string) => nodes.find((n) => n.id === firstId);

const loaded = runLoaded && (versionLoaded || !!runResource?.pipeline_spec);
const error = versionError || runError;
const loaded = runLoaded && (versionLoaded || !!run?.pipeline_spec);
const error = versionError || runError || executionsError || artifactsError;

if (error) {
return (
Expand All @@ -80,7 +82,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
);
}

if (!loaded || (!executionsLoaded && !executionsError)) {
if (!loaded || !executionsLoaded || !isArtifactsLoaded) {
return (
<Bullseye>
<Spinner />
Expand All @@ -95,6 +97,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
panelContent={
<PipelineRunDrawerRightContent
task={selectedNode?.data.pipelineTask}
upstreamTaskName={selectedNode?.runAfterTasks?.[0]}
onClose={() => setSelectedId(null)}
executions={executions}
/>
Expand All @@ -110,29 +113,29 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
setDetailsTab(selection);
setSelectedId(null);
}}
pipelineRunDetails={runResource && pipelineSpec ? runResource : undefined}
pipelineRunDetails={run && pipelineSpec ? run : undefined}
/>
}
>
<ApplicationsPage
title={
runResource ? (
<PipelineDetailsTitle run={runResource} statusIcon pipelineRunLabel />
run ? (
<PipelineDetailsTitle run={run} statusIcon pipelineRunLabel />
) : (
'Error loading run'
)
}
subtext={
runResource && (
run && (
<PipelineJobReferenceName
runName={runResource.display_name}
recurringRunId={runResource.recurring_run_id}
runName={run.display_name}
recurringRunId={run.recurring_run_id}
/>
)
}
description={
runResource?.description ? (
<MarkdownView conciseDisplay markdown={runResource.description} />
run?.description ? (
<MarkdownView conciseDisplay markdown={run.description} />
) : (
''
)
Expand All @@ -143,15 +146,12 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
<Breadcrumb>
{breadcrumbPath}
<BreadcrumbItem isActive style={{ maxWidth: 300 }}>
<Truncate content={runResource?.display_name ?? 'Loading...'} />
<Truncate content={run?.display_name ?? 'Loading...'} />
</BreadcrumbItem>
</Breadcrumb>
}
headerAction={
<PipelineRunDetailsActions
run={runResource}
onDelete={() => setDeleting(true)}
/>
<PipelineRunDetailsActions run={run} onDelete={() => setDeleting(true)} />
}
empty={false}
>
Expand Down Expand Up @@ -180,7 +180,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
</Drawer>
<DeletePipelineRunsModal
type={PipelineRunType.Archived}
toDeleteResources={deleting && runResource ? [runResource] : []}
toDeleteResources={deleting && run ? [run] : []}
onClose={(deleteComplete) => {
if (deleteComplete) {
navigate(contextPath ?? routePipelineRunsNamespace(namespace));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@ import PipelineRunDrawerRightTabs from '~/concepts/pipelines/content/pipelinesDe
import './PipelineRunDrawer.scss';
import { PipelineTask } from '~/concepts/pipelines/topology';
import { Execution } from '~/third_party/mlmd';
import { ArtifactNodeDrawerContent } from './artifacts';

type PipelineRunDrawerRightContentProps = {
task?: PipelineTask;
executions: Execution[];
upstreamTaskName?: string;
onClose: () => void;
};

const PipelineRunDrawerRightContent: React.FC<PipelineRunDrawerRightContentProps> = ({
task,
executions,
upstreamTaskName,
onClose,
}) => {
if (!task) {
Expand All @@ -35,18 +38,28 @@ const PipelineRunDrawerRightContent: React.FC<PipelineRunDrawerRightContentProps
minSize="500px"
data-testid="pipeline-run-drawer-right-content"
>
<DrawerHead>
<Title headingLevel="h2" size="xl">
{task.name} {task.type === 'artifact' ? 'Artifact details' : ''}
</Title>
{task.status?.podName && <Text component="small">{task.status.podName}</Text>}
<DrawerActions>
<DrawerCloseButton onClick={onClose} />
</DrawerActions>
</DrawerHead>
<DrawerPanelBody className="pipeline-run__drawer-panel-body pf-v5-u-pr-sm">
<PipelineRunDrawerRightTabs task={task} executions={executions} />
</DrawerPanelBody>
{task.type === 'artifact' ? (
<ArtifactNodeDrawerContent
upstreamTaskName={upstreamTaskName}
task={task}
onClose={onClose}
/>
) : (
<>
<DrawerHead>
<Title headingLevel="h2" size="xl">
{task.name}
</Title>
{task.status?.podName && <Text component="small">{task.status.podName}</Text>}
<DrawerActions>
<DrawerCloseButton onClick={onClose} />
</DrawerActions>
</DrawerHead>
<DrawerPanelBody className="pipeline-run__drawer-panel-body pf-v5-u-pr-sm">
<PipelineRunDrawerRightTabs task={task} executions={executions} />
</DrawerPanelBody>
</>
)}
</DrawerPanelContent>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';
import { Link } from 'react-router-dom';

import {
Title,
Flex,
FlexItem,
Stack,
DescriptionList,
DescriptionListGroup,
DescriptionListTerm,
DescriptionListDescription,
} from '@patternfly/react-core';

import { Artifact } from '~/third_party/mlmd';
import { artifactsDetailsRoute } from '~/routes';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { getArtifactName } from '~/pages/pipelines/global/experiments/artifacts/utils';
import PipelinesTableRowTime from '~/concepts/pipelines/content/tables/PipelinesTableRowTime';
import PipelineRunDrawerRightContent from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent';

type ArtifactNodeDetailsProps = Pick<
React.ComponentProps<typeof PipelineRunDrawerRightContent>,
'upstreamTaskName'
> & {
artifact: Artifact.AsObject;
};

export const ArtifactNodeDetails: React.FC<ArtifactNodeDetailsProps> = ({
artifact,
upstreamTaskName,
}) => {
const { namespace } = usePipelinesAPI();
const artifactName = getArtifactName(artifact);

return (
<Flex
spaceItems={{ default: 'spaceItems2xl' }}
direction={{ default: 'column' }}
className="pf-v5-u-pt-lg pf-v5-u-pb-lg"
>
<FlexItem>
<Stack hasGutter>
<Title headingLevel="h3">Artifact details</Title>
<DescriptionList
isHorizontal
horizontalTermWidthModifier={{ default: '15ch' }}
data-testid="artifact-details-description-list"
>
<DescriptionListGroup>
<DescriptionListTerm>Upstream task</DescriptionListTerm>
<DescriptionListDescription>{upstreamTaskName}</DescriptionListDescription>

<DescriptionListTerm>Artifact name</DescriptionListTerm>
<DescriptionListDescription>
<Link to={artifactsDetailsRoute(namespace, artifact.id)}>{artifactName}</Link>
</DescriptionListDescription>

<DescriptionListTerm>Artifact type</DescriptionListTerm>
<DescriptionListDescription>{artifact.type}</DescriptionListDescription>

<DescriptionListTerm>Created at</DescriptionListTerm>
<DescriptionListDescription>
<PipelinesTableRowTime date={new Date(artifact.createTimeSinceEpoch)} />
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</Stack>
</FlexItem>

<FlexItem>
<Stack hasGutter>
<Title headingLevel="h3">Artifact URI</Title>
<DescriptionList
isHorizontal
horizontalTermWidthModifier={{ default: '15ch' }}
data-testid="artifact-uri-description-list"
>
<DescriptionListGroup>
<DescriptionListTerm>{artifactName}</DescriptionListTerm>
<DescriptionListDescription>{artifact.uri}</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</Stack>
</FlexItem>
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';

import {
DrawerHead,
Title,
Text,
DrawerActions,
DrawerCloseButton,
DrawerPanelBody,
Tabs,
Tab,
TabTitleText,
EmptyState,
EmptyStateHeader,
EmptyStateVariant,
} from '@patternfly/react-core';

import PipelineRunDrawerRightContent from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent';
import { ArtifactNodeDetails } from './ArtifactNodeDetails';

type ArtifactNodeDrawerContentProps = Omit<
React.ComponentProps<typeof PipelineRunDrawerRightContent>,
'executions'
>;

export const ArtifactNodeDrawerContent: React.FC<ArtifactNodeDrawerContentProps> = ({
task,
upstreamTaskName,
onClose,
}) => {
const artifact = task?.metadata?.toObject();

return task ? (
<>
<DrawerHead>
<Title headingLevel="h2" size="xl">
{task.name}
</Title>
{task.status?.podName && <Text component="small">{task.status.podName}</Text>}
<DrawerActions>
<DrawerCloseButton onClick={onClose} />
</DrawerActions>
</DrawerHead>
<DrawerPanelBody className="pipeline-run__drawer-panel-body pf-v5-u-pr-sm">
{artifact ? (
<Tabs aria-label="Artifact node detail tabs" activeKey="details">
<Tab
eventKey="details"
title={<TabTitleText>Artifact details</TabTitleText>}
aria-label="Overview"
>
<ArtifactNodeDetails artifact={artifact} upstreamTaskName={upstreamTaskName} />
</Tab>
<Tab
eventKey="visualization"
title={<TabTitleText>Visualization</TabTitleText>}
aria-label="Visualization"
/>
</Tabs>
) : (
<EmptyState variant={EmptyStateVariant.xs}>
<EmptyStateHeader titleText="Content is not available yet." headingLevel="h4" />
</EmptyState>
)}
</DrawerPanelBody>
</>
) : null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './ArtifactNodeDetails';
export * from './ArtifactNodeDrawerContent';
export * from './usePipelineRunArtifacts';
Loading

0 comments on commit 045a2dc

Please sign in to comment.