Skip to content

Commit

Permalink
[dagit] Add last materialization, latest run columns to the asset tab…
Browse files Browse the repository at this point in the history
…le (#7996)

* [dagit] Add last materialization, latest run columns to the asset table

* [dagit] Remove the Asset Grid prototype that was behind a feature flag (#8109)

Co-authored-by: bengotow <[email protected]>

Co-authored-by: bengotow <[email protected]>
  • Loading branch information
bengotow and bengotow authored May 31, 2022
1 parent 6b8f907 commit 35e8a66
Show file tree
Hide file tree
Showing 17 changed files with 425 additions and 1,006 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import {
Checkbox,
Colors,
Icon,
MenuItem,
Mono,
NonIdealState,
SplitPanelContainer,
Suggest,
} from '@dagster-io/ui';
import flatMap from 'lodash/flatMap';
import isEqual from 'lodash/isEqual';
Expand Down Expand Up @@ -68,7 +66,7 @@ import {
isSourceAsset,
tokenForAssetKey,
displayNameForAssetKey,
identifyBundles,
buildComputeStatusData,
} from './Utils';
import {AssetGraphLayout} from './layout';
import {AssetGraphQuery_assetNodes} from './types/AssetGraphQuery';
Expand Down Expand Up @@ -102,11 +100,9 @@ export const AssetGraphExplorer: React.FC<Props> = (props) => {
} = useAssetGraphData(props.pipelineSelector, props.explorerPath.opsQuery);

const {liveResult, liveDataByNode} = useLiveDataForAssetKeys(
props.pipelineSelector,
assetGraphData,
assetGraphData?.nodes,
graphAssetKeys,
);

const liveDataRefreshState = useQueryRefreshAtInterval(liveResult, FIFTEEN_SECONDS);

useDocumentTitle('Assets');
Expand Down Expand Up @@ -323,9 +319,10 @@ const AssetGraphExplorerWithData: React.FC<
}
};

const allBundleNames = React.useMemo(() => {
return Object.keys(identifyBundles(props.allAssetKeys.map((a) => JSON.stringify(a.path))));
}, [props.allAssetKeys]);
const computeStatuses = React.useMemo(
() => (assetGraphData ? buildComputeStatusData(assetGraphData, liveDataByNode) : {}),
[assetGraphData, liveDataByNode],
);

return (
<SplitPanelContainer
Expand Down Expand Up @@ -509,6 +506,7 @@ const AssetGraphExplorerWithData: React.FC<
<AssetNode
definition={graphNode.definition}
liveData={liveDataByNode[graphNode.id]}
computeStatus={computeStatuses[graphNode.id]}
selected={selectedGraphNodes.includes(graphNode)}
/>
)}
Expand Down Expand Up @@ -580,37 +578,6 @@ const AssetGraphExplorerWithData: React.FC<
onChange={(opsQuery) => onChangeExplorerPath({...explorerPath, opsQuery}, 'replace')}
popoverPosition="bottom-left"
/>
{allBundleNames.length > 0 && (
<Suggest<string>
inputProps={{placeholder: 'View a folder'}}
items={allBundleNames}
itemRenderer={(folder, props) => (
<MenuItem
text={displayNameForAssetKey({path: JSON.parse(folder)})}
active={props.modifiers.active}
onClick={props.handleClick}
key={folder}
/>
)}
noResults={<MenuItem disabled={true} text="Loading..." />}
inputValueRenderer={(str) => str}
selectedItem=""
onItemSelect={(item) => {
onChangeExplorerPath(
{
...explorerPath,
opsQuery: [
explorerPath.opsQuery,
`${displayNameForAssetKey({path: JSON.parse(item)})}/`,
]
.filter(Boolean)
.join(','),
},
'replace',
);
}}
/>
)}
</QueryOverlay>
</>
}
Expand Down
167 changes: 81 additions & 86 deletions js_modules/dagit/packages/core/src/asset-graph/AssetNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ import {linkToRunEvent, titleForRun} from '../runs/RunUtils';
import {TimestampDisplay} from '../schedules/TimestampDisplay';
import {markdownToPlaintext} from '../ui/markdownToPlaintext';

import {displayNameForAssetKey, LiveDataForNode} from './Utils';
import {
assetNameMaxlengthForWidth,
ASSET_NODE_ANNOTATIONS_MAX_WIDTH,
ASSET_NODE_NAME_MAX_LENGTH,
} from './layout';
import {ComputeStatus, displayNameForAssetKey, LiveDataForNode} from './Utils';
import {ASSET_NODE_ANNOTATIONS_MAX_WIDTH, ASSET_NODE_NAME_MAX_LENGTH} from './layout';
import {AssetNodeFragment} from './types/AssetNodeFragment';

const MISSING_LIVE_DATA = {
Expand All @@ -30,24 +26,25 @@ const MISSING_LIVE_DATA = {
export const AssetNode: React.FC<{
definition: AssetNodeFragment;
liveData?: LiveDataForNode;
computeStatus?: ComputeStatus;
selected: boolean;
width?: number;
padded?: boolean;
inAssetCatalog?: boolean;
}> = React.memo(({definition, selected, liveData, inAssetCatalog, width, padded = true}) => {
const stepKey = definition.opNames[0] || '';
}> = React.memo(({definition, selected, liveData, inAssetCatalog, computeStatus}) => {
const firstOp = definition.opNames.length ? definition.opNames[0] : null;
const computeName = definition.graphName || definition.opNames[0] || null;

// Used for linking to the run with this step highlighted. We only support highlighting
// a single step, so just use the first one.
const computeName = definition.graphName || definition.opNames[0] || null;
const stepKey = firstOp || '';

const displayName = withMiddleTruncation(displayNameForAssetKey(definition.assetKey), {
maxLength: width ? assetNameMaxlengthForWidth(width) : ASSET_NODE_NAME_MAX_LENGTH,
maxLength: ASSET_NODE_NAME_MAX_LENGTH,
});

const {lastMaterialization} = liveData || MISSING_LIVE_DATA;

return (
<AssetNodeContainer $selected={selected} $padded={padded}>
<AssetNodeContainer $selected={selected}>
<AssetNodeBox $selected={selected}>
<Name>
<span style={{marginTop: 1}}>
Expand All @@ -58,7 +55,7 @@ export const AssetNode: React.FC<{
</div>
<div style={{flex: 1}} />
<div style={{maxWidth: ASSET_NODE_ANNOTATIONS_MAX_WIDTH}}>
{liveData?.computeStatus === 'old' && (
{computeStatus === 'old' && (
<UpstreamNotice>
upstream
<br />
Expand Down Expand Up @@ -88,15 +85,17 @@ export const AssetNode: React.FC<{
{lastMaterialization ? (
<StatsRow>
<span>Materialized</span>
<AssetRunLink
runId={lastMaterialization.runId}
event={{stepKey, timestamp: lastMaterialization.timestamp}}
>
<TimestampDisplay
timestamp={Number(lastMaterialization.timestamp) / 1000}
timeFormat={{showSeconds: false, showTimezone: false}}
/>
</AssetRunLink>
<CaptionMono>
<AssetRunLink
runId={lastMaterialization.runId}
event={{stepKey, timestamp: lastMaterialization.timestamp}}
>
<TimestampDisplay
timestamp={Number(lastMaterialization.timestamp) / 1000}
timeFormat={{showSeconds: false, showTimezone: false}}
/>
</AssetRunLink>
</CaptionMono>
</StatsRow>
) : (
<>
Expand All @@ -108,7 +107,9 @@ export const AssetNode: React.FC<{
)}
<StatsRow>
<span>Latest Run</span>
<AssetLatestRunWithNotices liveData={liveData} stepKey={stepKey} />
<CaptionMono>
<AssetLatestRunWithNotices liveData={liveData} stepKey={stepKey} />
</CaptionMono>
</StatsRow>
</Stats>
{definition.computeKind && (
Expand All @@ -132,57 +133,12 @@ export const AssetNode: React.FC<{
);
}, isEqual);

export const AssetLatestRunWithNotices: React.FC<{
liveData: LiveDataForNode | undefined;
stepKey: string | null;
}> = ({liveData, stepKey}) => {
const {lastMaterialization, unstartedRunIds, inProgressRunIds, runWhichFailedToMaterialize} =
liveData || MISSING_LIVE_DATA;

return inProgressRunIds?.length > 0 ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip content="A run is currently rematerializing this asset.">
<Spinner purpose="body-text" />
</Tooltip>
<AssetRunLink runId={inProgressRunIds[0]} />
</Box>
) : unstartedRunIds?.length > 0 ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip content="A run has started that will rematerialize this asset soon.">
<Spinner purpose="body-text" stopped />
</Tooltip>
<AssetRunLink runId={unstartedRunIds[0]} />
</Box>
) : runWhichFailedToMaterialize?.__typename === 'Run' ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip
content={`Run ${titleForRun({
runId: runWhichFailedToMaterialize.id,
})} failed to materialize this asset`}
>
<Icon name="warning" color={Colors.Red500} />
</Tooltip>
<AssetRunLink runId={runWhichFailedToMaterialize.id} />
</Box>
) : lastMaterialization ? (
<AssetRunLink
runId={lastMaterialization.runId}
event={{stepKey, timestamp: lastMaterialization.timestamp}}
/>
) : (
<span></span>
);
};
export const AssetNodeMinimal: React.FC<{
selected: boolean;
style?: CSSProperties;
}> = ({selected, style, children}) => {
return (
<AssetNodeContainer
$padded={true}
$selected={selected}
style={{position: 'absolute', borderRadius: 12}}
>
<AssetNodeContainer $selected={selected} style={{position: 'absolute', borderRadius: 12}}>
<AssetNodeBox
$selected={selected}
style={{
Expand All @@ -208,7 +164,7 @@ export const AssetRunLink: React.FC<{
target="_blank"
rel="noreferrer"
>
{children || <CaptionMono>{titleForRun({runId})}</CaptionMono>}
{children || titleForRun({runId})}
</Link>
);

Expand Down Expand Up @@ -257,20 +213,17 @@ const BoxColors = {
Stats: 'rgba(236, 236, 248, 1)',
};

export const AssetNodeContainer = styled.div<{$selected: boolean; $padded: boolean}>`
const AssetNodeContainer = styled.div<{$selected: boolean}>`
outline: ${(p) => (p.$selected ? `2px dashed ${NodeHighlightColors.Border}` : 'none')};
border-radius: 6px;
outline-offset: -1px;
background: ${(p) => (p.$selected ? NodeHighlightColors.Background : 'white')};
inset: 0;
${(p) =>
p.$padded &&
`
padding: 4px;
margin-top: 10px;
margin-right: 4px;
margin-left: 4px;
margin-bottom: 2px;`}
margin-bottom: 2px;
`;

export const AssetNodeBox = styled.div<{$selected: boolean}>`
Expand All @@ -295,6 +248,16 @@ const Name = styled.div`
gap: 4px;
`;

export const NameMinimal = styled(Name)`
font-weight: 600;
white-space: nowrap;
position: absolute;
background: none;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;

const Description = styled.div`
background: ${BoxColors.Description};
padding: 4px 8px;
Expand Down Expand Up @@ -335,12 +298,44 @@ const UpstreamNotice = styled.div`
border-top-right-radius: 3px;
`;

export const NameMinimal = styled(Name)`
font-weight: 600;
white-space: nowrap;
position: absolute;
background: none;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;
export const AssetLatestRunWithNotices: React.FC<{
liveData?: LiveDataForNode;
stepKey: string;
}> = ({liveData, stepKey}) => {
const {lastMaterialization, unstartedRunIds, inProgressRunIds, runWhichFailedToMaterialize} =
liveData || MISSING_LIVE_DATA;

return inProgressRunIds?.length > 0 ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip content="A run is currently rematerializing this asset.">
<Spinner purpose="body-text" />
</Tooltip>
<AssetRunLink runId={inProgressRunIds[0]} />
</Box>
) : unstartedRunIds?.length > 0 ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip content="A run has started that will rematerialize this asset soon.">
<Spinner purpose="body-text" stopped />
</Tooltip>
<AssetRunLink runId={unstartedRunIds[0]} />
</Box>
) : runWhichFailedToMaterialize?.__typename === 'Run' ? (
<Box flex={{gap: 4, alignItems: 'center'}}>
<Tooltip
content={`Run ${titleForRun({
runId: runWhichFailedToMaterialize.id,
})} failed to materialize this asset`}
>
<Icon name="warning" color={Colors.Red500} />
</Tooltip>
<AssetRunLink runId={runWhichFailedToMaterialize.id} />
</Box>
) : lastMaterialization ? (
<AssetRunLink
runId={lastMaterialization.runId}
event={{stepKey, timestamp: lastMaterialization.timestamp}}
/>
) : (
<span></span>
);
};
Loading

0 comments on commit 35e8a66

Please sign in to comment.