Skip to content

Commit

Permalink
Merge pull request opendatahub-io#2811 from jpuzz0/RHOAIENG-2989-arti…
Browse files Browse the repository at this point in the history
…fact-run-node-artifact-details

[RHOAIENG-2989] artifact node drawer details section
  • Loading branch information
openshift-merge-bot[bot] authored May 15, 2024
2 parents 8645fe6 + c76698a commit 5fcd504
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 49 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 @@ -12,7 +12,7 @@ export interface ArtifactsListResponse {

export const useGetArtifactsList = (
refreshRate?: number,
): FetchState<ArtifactsListResponse | null> => {
): FetchState<ArtifactsListResponse | undefined> => {
const { pageToken, maxResultSize, filterQuery } = useMlmdListContext();
const { metadataStoreServiceClient } = usePipelinesAPI();

Expand All @@ -39,7 +39,7 @@ export const useGetArtifactsList = (
};
}, [filterQuery, pageToken, maxResultSize, metadataStoreServiceClient]);

return useFetchState(fetchArtifactsList, null, {
return useFetchState(fetchArtifactsList, undefined, {
refreshRate,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ import useFetchState, { FetchState, FetchStateCallbackPromise } from '~/utilitie

export const useGetEventsByExecutionId = (
executionId?: string,
): FetchState<GetEventsByExecutionIDsResponse | null> =>
useGetEventsByExecutionIds([Number(executionId)]);

export const useGetEventsByExecutionIds = (
executionIds: number[],
): FetchState<GetEventsByExecutionIDsResponse | null> => {
const { metadataStoreServiceClient } = usePipelinesAPI();

const call = React.useCallback<
FetchStateCallbackPromise<GetEventsByExecutionIDsResponse | null>
>(async () => {
const numberId = Number(executionId);
const request = new GetEventsByExecutionIDsRequest();

if (!executionId || Number.isNaN(numberId)) {
return null;
}

request.setExecutionIdsList([numberId]);
request.setExecutionIdsList(executionIds);

const response = await metadataStoreServiceClient.getEventsByExecutionIDs(request);

return response;
}, [executionId, metadataStoreServiceClient]);
}, [executionIds, metadataStoreServiceClient]);

return useFetchState(call, null);
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,40 @@ 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 { useGetEventsByExecutionIds } from '~/concepts/pipelines/apiHooks/mlmd/useGetEventsByExecutionId';
import { parseEventsByType } from '~/pages/pipelines/global/experiments/executions/utils';
import { Event } from '~/third_party/mlmd';
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] = usePipelineRunArtifacts(run);
const [eventsResponse] = useGetEventsByExecutionIds(
React.useMemo(() => executions.map((execution) => execution.getId()), [executions]),
);
const events = parseEventsByType(eventsResponse);
const nodes = usePipelineTaskTopology(
pipelineSpec,
run?.run_details,
executions,
events[Event.Type.OUTPUT],
artifacts,
);

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

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

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

if (error) {
Expand Down Expand Up @@ -95,6 +110,7 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
panelContent={
<PipelineRunDrawerRightContent
task={selectedNode?.data.pipelineTask}
upstreamTaskName={selectedNode?.runAfterTasks?.[0]}
onClose={() => setSelectedId(null)}
executions={executions}
/>
Expand All @@ -110,29 +126,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 +159,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 +193,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>
);
};
Loading

0 comments on commit 5fcd504

Please sign in to comment.