Skip to content

Commit

Permalink
[dagit] Add a Materialize All button to Asset Details > Lineage (#8248)
Browse files Browse the repository at this point in the history
Co-authored-by: bengotow <bgotow@elementl.com>
  • Loading branch information
bengotow and bengotow committed Jun 8, 2022
1 parent ff3392a commit ec409c2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,6 @@ export const AssetGraphExplorerWithData: React.FC<
selectedAssetValues.includes(tokenForAssetKey(node.definition.assetKey)),
);
const lastSelectedNode = selectedGraphNodes[selectedGraphNodes.length - 1];
const launchGraphNodes = selectedGraphNodes.length
? selectedGraphNodes
: Object.values(assetGraphData.nodes).filter((a) => !isSourceAsset(a.definition));

const {layout, loading, async} = useAssetLayout(assetGraphData);

Expand Down Expand Up @@ -404,7 +401,11 @@ export const AssetGraphExplorerWithData: React.FC<
/>

<LaunchAssetExecutionButton
assetKeys={launchGraphNodes.map((n) => n.assetKey)}
context={selectedGraphNodes.length ? 'selected' : 'all'}
assetKeys={(selectedGraphNodes.length
? selectedGraphNodes
: Object.values(assetGraphData.nodes).filter((a) => !isSourceAsset(a.definition))
).map((n) => n.assetKey)}
liveDataByNode={liveDataByNode}
preferredJobName={explorerPath.pipelineName}
/>
Expand Down
66 changes: 66 additions & 0 deletions js_modules/dagit/packages/core/src/assets/AssetNodeLineage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {Box, Button, ButtonGroup, Checkbox, Colors, Icon} from '@dagster-io/ui';
import * as React from 'react';

import {GraphData, LiveData} from '../asset-graph/Utils';

import {AssetLineageScope, AssetNodeLineageGraph} from './AssetNodeLineageGraph';
import {AssetViewParams} from './AssetView';
import {LaunchAssetExecutionButton} from './LaunchAssetExecutionButton';
import {AssetNodeDefinitionFragment} from './types/AssetNodeDefinitionFragment';

export const AssetNodeLineage: React.FC<{
params: AssetViewParams;
setParams: (params: AssetViewParams) => void;
assetNode: AssetNodeDefinitionFragment;
assetGraphData: GraphData;
liveDataByNode: LiveData;
}> = ({params, setParams, assetNode, liveDataByNode, assetGraphData}) => {
return (
<>
<Box
flex={{justifyContent: 'space-between', alignItems: 'center', gap: 12}}
padding={{left: 24, right: 12, vertical: 12}}
border={{side: 'bottom', color: Colors.KeylineGray, width: 1}}
>
<ButtonGroup<AssetLineageScope>
activeItems={new Set([params.lineageScope || 'neighbors'])}
buttons={[
{id: 'neighbors', label: 'Nearest Neighbors', icon: 'graph_neighbors'},
{id: 'upstream', label: 'Upstream', icon: 'graph_upstream'},
{id: 'downstream', label: 'Downstream', icon: 'graph_downstream'},
]}
onClick={(lineageScope) => setParams({...params, lineageScope})}
/>
<Checkbox
format="switch"
label="Show secondary edges"
checked={!!params.lineageShowSecondaryEdges}
onChange={() =>
setParams({
...params,
lineageShowSecondaryEdges: params.lineageShowSecondaryEdges ? undefined : true,
})
}
/>
<div style={{flex: 1}} />
{Object.values(assetGraphData.nodes).length > 1 ? (
<LaunchAssetExecutionButton
assetKeys={Object.values(assetGraphData.nodes).map((n) => n.assetKey)}
liveDataByNode={liveDataByNode}
intent="none"
context="all"
/>
) : (
<Button icon={<Icon name="materialization" />} disabled>
Materialize all
</Button>
)}
</Box>
<AssetNodeLineageGraph
assetNode={assetNode}
liveDataByNode={liveDataByNode}
assetGraphData={assetGraphData}
/>
</>
);
};
4 changes: 2 additions & 2 deletions js_modules/dagit/packages/core/src/assets/AssetTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ const AssetEntryRow: React.FC<{
<td>
{asset ? (
<Box flex={{gap: 8, alignItems: 'center'}}>
<AnchorButton to={`/instance/assets/${path.join('/')}`}>View Details</AnchorButton>
<AnchorButton to={`/instance/assets/${path.join('/')}`}>View details</AnchorButton>
<Popover
position="bottom-right"
content={
Expand All @@ -288,7 +288,7 @@ const AssetEntryRow: React.FC<{
repoAddress && asset.definition?.groupName
? workspacePathFromAddress(
repoAddress,
`/asset_groups/${asset.definition.groupName}`,
`/asset-groups/${asset.definition.groupName}`,
)
: ''
}
Expand Down
49 changes: 10 additions & 39 deletions js_modules/dagit/packages/core/src/assets/AssetView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import {gql, useQuery} from '@apollo/client';
import {
Alert,
Box,
ButtonGroup,
ButtonLink,
Checkbox,
Colors,
NonIdealState,
Spinner,
Expand Down Expand Up @@ -34,7 +32,8 @@ import {workspacePathFromAddress} from '../workspace/workspacePath';
import {AssetEvents} from './AssetEvents';
import {AssetNodeDefinition, ASSET_NODE_DEFINITION_FRAGMENT} from './AssetNodeDefinition';
import {AssetNodeInstigatorTag, ASSET_NODE_INSTIGATORS_FRAGMENT} from './AssetNodeInstigatorTag';
import {AssetLineageScope, AssetNodeLineageGraph} from './AssetNodeLineageGraph';
import {AssetNodeLineage} from './AssetNodeLineage';
import {AssetLineageScope} from './AssetNodeLineageGraph';
import {AssetPageHeader} from './AssetPageHeader';
import {LaunchAssetExecutionButton} from './LaunchAssetExecutionButton';
import {AssetKey} from './types';
Expand Down Expand Up @@ -148,7 +147,7 @@ export const AssetView: React.FC<Props> = ({assetKey}) => {
</Tabs>
}
right={
<Box style={{margin: '-4px 0'}} flex={{gap: 8, alignItems: 'baseline'}}>
<Box style={{margin: '-4px 0'}} flex={{gap: 12, alignItems: 'baseline'}}>
<Box margin={{top: 4}}>
<QueryRefreshCountdown refreshState={refreshState} />
</Box>
Expand Down Expand Up @@ -198,41 +197,13 @@ export const AssetView: React.FC<Props> = ({assetKey}) => {
) : params.view === 'lineage' ? (
definition ? (
assetGraphData ? (
<>
<Box
flex={{justifyContent: 'space-between', alignItems: 'center'}}
padding={{horizontal: 24, vertical: 12}}
border={{side: 'bottom', color: Colors.KeylineGray, width: 1}}
>
<ButtonGroup<AssetLineageScope>
activeItems={new Set([params.lineageScope || 'neighbors'])}
buttons={[
{id: 'neighbors', label: 'Nearest Neighbors', icon: 'graph_neighbors'},
{id: 'upstream', label: 'Upstream', icon: 'graph_upstream'},
{id: 'downstream', label: 'Downstream', icon: 'graph_downstream'},
]}
onClick={(lineageScope) => setParams({...params, lineageScope})}
/>
<Checkbox
format="switch"
label="Show secondary edges"
checked={params.lineageShowSecondaryEdges === true}
onChange={() =>
setParams({
...params,
lineageShowSecondaryEdges: params.lineageShowSecondaryEdges
? undefined
: true,
})
}
/>
</Box>
<AssetNodeLineageGraph
assetNode={definition}
liveDataByNode={liveDataByNode}
assetGraphData={assetGraphData}
/>
</>
<AssetNodeLineage
params={params}
setParams={setParams}
assetNode={definition}
liveDataByNode={liveDataByNode}
assetGraphData={assetGraphData}
/>
) : (
<Box style={{flex: 1}} flex={{alignItems: 'center', justifyContent: 'center'}}>
<Spinner purpose="page" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,26 @@ type LaunchAssetsState =
export const LaunchAssetExecutionButton: React.FC<{
assetKeys: AssetKey[]; // Memoization not required
liveDataByNode: LiveData;
context?: 'all' | 'selected';
intent?: 'primary' | 'none';
preferredJobName?: string;
}> = ({assetKeys, liveDataByNode, preferredJobName}) => {
}> = ({assetKeys, liveDataByNode, preferredJobName, context, intent = 'primary'}) => {
const {canLaunchPipelineExecution} = usePermissions();
const launchWithTelemetry = useLaunchWithTelemetry();

const [state, setState] = React.useState<LaunchAssetsState>({type: 'none'});
const client = useApolloClient();

const count = assetKeys.length > 1 ? ` (${assetKeys.length})` : '';
const isRematerializeForAll = (assetKeys.length
? assetKeys.map((n) => liveDataByNode[toGraphId(n)])
: Object.values(liveDataByNode)
).every((e) => !!e?.lastMaterialization);

const label = `${isRematerializeForAll ? 'Rematerialize' : 'Materialize'}${
context === 'all' ? ` all${count}` : context === 'selected' ? ` selected${count}` : count
}`;

if (!assetKeys.length || !canLaunchPipelineExecution) {
return (
<Tooltip
Expand All @@ -65,8 +77,8 @@ export const LaunchAssetExecutionButton: React.FC<{
: 'Select one or more assets to materialize.'
}
>
<Button intent="primary" icon={<Icon name="materialization" />} disabled>
Materialize
<Button intent={intent} icon={<Icon name="materialization" />} disabled>
{label}
</Button>
</Tooltip>
);
Expand Down Expand Up @@ -102,7 +114,7 @@ export const LaunchAssetExecutionButton: React.FC<{
return (
<>
<Button
intent="primary"
intent={intent}
onClick={onClick}
icon={
state.type === 'loading' ? (
Expand All @@ -112,7 +124,7 @@ export const LaunchAssetExecutionButton: React.FC<{
)
}
>
{titleForLaunch(assetKeys, liveDataByNode)}
{label}
</Button>
{state.type === 'launchpad' && (
<AssetLaunchpad
Expand Down Expand Up @@ -249,21 +261,6 @@ function stateForLaunchingAssets(
};
}

const titleForLaunch = (assetKeys: AssetKey[], liveDataByNode: LiveData) => {
const isRematerializeForAll = (assetKeys.length
? assetKeys.map((n) => liveDataByNode[toGraphId(n)])
: Object.values(liveDataByNode)
).every((e) => !!e?.lastMaterialization);

const count = assetKeys.length !== 1 ? ` (${assetKeys.length})` : '';

return `${isRematerializeForAll ? 'Rematerialize' : 'Materialize'} ${
assetKeys.length === 0 || assetKeys.length === Object.keys(liveDataByNode).length
? `All${count}`
: `Selected${count}`
}`;
};

export const LAUNCH_ASSET_EXECUTION_ASSET_NODE_FRAGMENT = gql`
fragment LaunchAssetExecutionAssetNodeFragment on AssetNode {
id
Expand Down

0 comments on commit ec409c2

Please sign in to comment.