Skip to content

Commit

Permalink
[dagit] Add top level Asset Group pages, Asset Groups in left nav (#8203
Browse files Browse the repository at this point in the history
)

* BG Asset Groups UI 1

* Tags in the top nav of Asset Group + Asset Details for corss linking

* Polish

* Add subheaders for Asset Groups and Jobs in the left nav

# Conflicts:
#	js_modules/dagit/packages/core/src/nav/FlatContentList.tsx
#	js_modules/dagit/packages/core/src/ui/SectionedLeftNav.tsx

* Add asset group to the “defined in” column of the asset catalog

* PR feedback

Co-authored-by: bengotow <bgotow@elementl.com>
  • Loading branch information
bengotow and bengotow committed Jun 7, 2022
1 parent a34abb7 commit 7fa711e
Show file tree
Hide file tree
Showing 33 changed files with 658 additions and 322 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@ import {
import {ExplorerPath} from '../pipelines/PipelinePathUtils';
import {SidebarPipelineOrJobOverview} from '../pipelines/SidebarPipelineOrJobOverview';
import {useDidLaunchEvent} from '../runs/RunUtils';
import {PipelineSelector} from '../types/globalTypes';
import {GraphQueryInput} from '../ui/GraphQueryInput';
import {Loading} from '../ui/Loading';

import {AssetConnectedEdges} from './AssetEdges';
import {AssetNode, AssetNodeMinimal} from './AssetNode';
import {ForeignNode} from './ForeignNode';
import {OmittedAssetsNotice} from './OmittedAssetsNotice';
import {SidebarAssetInfo} from './SidebarAssetInfo';
import {
GraphData,
Expand All @@ -57,7 +55,7 @@ import {
} from './Utils';
import {AssetGraphLayout} from './layout';
import {AssetGraphQuery_assetNodes} from './types/AssetGraphQuery';
import {useAssetGraphData} from './useAssetGraphData';
import {AssetGraphFetchScope, useAssetGraphData} from './useAssetGraphData';
import {useFindJobForAsset} from './useFindJobForAsset';
import {useLiveDataForAssetKeys} from './useLiveDataForAssetKeys';

Expand All @@ -67,8 +65,7 @@ interface Props {
options: GraphExplorerOptions;
setOptions?: (options: GraphExplorerOptions) => void;

pipelineSelector?: PipelineSelector;
filterNodes?: (assetNode: AssetGraphQuery_assetNodes) => boolean;
fetchOptions: AssetGraphFetchScope;

explorerPath: ExplorerPath;
onChangeExplorerPath: (path: ExplorerPath, mode: 'replace' | 'push') => void;
Expand All @@ -84,7 +81,7 @@ export const AssetGraphExplorer: React.FC<Props> = (props) => {
graphAssetKeys,
allAssetKeys,
applyingEmptyDefault,
} = useAssetGraphData(props.explorerPath.opsQuery, {pipelineSelector: props.pipelineSelector});
} = useAssetGraphData(props.explorerPath.opsQuery, props.fetchOptions);

const {liveResult, liveDataByNode} = useLiveDataForAssetKeys(
assetGraphData?.nodes,
Expand Down Expand Up @@ -150,7 +147,7 @@ export const AssetGraphExplorerWithData: React.FC<
assetGraphData,
graphQueryItems,
applyingEmptyDefault,
pipelineSelector,
fetchOptions,
} = props;

const history = useHistory();
Expand Down Expand Up @@ -401,52 +398,45 @@ export const AssetGraphExplorerWithData: React.FC<
</OptionsOverlay>
)}

{setOptions && (
<Box
flex={{direction: 'column', alignItems: 'flex-end', gap: 8}}
style={{position: 'absolute', right: 12, top: 12}}
>
<Box flex={{alignItems: 'center', gap: 12}}>
<QueryRefreshCountdown
refreshState={liveDataRefreshState}
dataDescription="materializations"
/>

<LaunchAssetExecutionButton
title={titleForLaunch(selectedGraphNodes, liveDataByNode)}
preferredJobName={explorerPath.pipelineName}
assets={launchGraphNodes.map((n) => n.definition)}
upstreamAssetKeys={uniqBy(
flatMap(launchGraphNodes.map((n) => n.definition.dependencyKeys)),
(key) => JSON.stringify(key),
).filter(
(key) =>
!launchGraphNodes.some(
(n) => JSON.stringify(n.assetKey) === JSON.stringify(key),
),
)}
/>
</Box>
{!props.pipelineSelector && <OmittedAssetsNotice assetKeys={props.allAssetKeys} />}
</Box>
)}
{setOptions && (
<QueryOverlay>
<GraphQueryInput
items={graphQueryItems}
value={explorerPath.opsQuery}
placeholder="Type an asset subset…"
onChange={(opsQuery) =>
onChangeExplorerPath({...explorerPath, opsQuery}, 'replace')
}
popoverPosition="bottom-left"
<Box
flex={{direction: 'column', alignItems: 'flex-end', gap: 8}}
style={{position: 'absolute', right: 12, top: 12}}
>
<Box flex={{alignItems: 'center', gap: 12}}>
<QueryRefreshCountdown
refreshState={liveDataRefreshState}
dataDescription="materializations"
/>
</QueryOverlay>
)}

<LaunchAssetExecutionButton
title={titleForLaunch(selectedGraphNodes, liveDataByNode)}
preferredJobName={explorerPath.pipelineName}
assets={launchGraphNodes.map((n) => n.definition)}
upstreamAssetKeys={uniqBy(
flatMap(launchGraphNodes.map((n) => n.definition.dependencyKeys)),
(key) => JSON.stringify(key),
).filter(
(key) =>
!launchGraphNodes.some(
(n) => JSON.stringify(n.assetKey) === JSON.stringify(key),
),
)}
/>
</Box>
</Box>
<QueryOverlay>
<GraphQueryInput
items={graphQueryItems}
value={explorerPath.opsQuery}
placeholder="Type an asset subset…"
onChange={(opsQuery) => onChangeExplorerPath({...explorerPath, opsQuery}, 'replace')}
popoverPosition="bottom-left"
/>
</QueryOverlay>
</>
}
second={
!options.enableSidebar ? null : selectedGraphNodes.length === 1 && selectedGraphNodes[0] ? (
selectedGraphNodes.length === 1 && selectedGraphNodes[0] ? (
<RightInfoPanel>
<RightInfoPanelContent>
<SidebarAssetInfo
Expand All @@ -455,10 +445,10 @@ export const AssetGraphExplorerWithData: React.FC<
/>
</RightInfoPanelContent>
</RightInfoPanel>
) : pipelineSelector ? (
) : fetchOptions.pipelineSelector ? (
<RightInfoPanel>
<RightInfoPanelContent>
<SidebarPipelineOrJobOverview pipelineSelector={pipelineSelector} />
<SidebarPipelineOrJobOverview pipelineSelector={fetchOptions.pipelineSelector} />
</RightInfoPanelContent>
</RightInfoPanel>
) : null
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {gql, useQuery} from '@apollo/client';
import React from 'react';

import {filterByQuery, GraphQueryItem} from '../app/GraphQueryImpl';
import {PipelineSelector} from '../types/globalTypes';
import {AssetGroupSelector, PipelineSelector} from '../types/globalTypes';

import {ASSET_NODE_FRAGMENT} from './AssetNode';
import {buildGraphData, GraphData, tokenForAssetKey} from './Utils';
Expand All @@ -12,6 +12,11 @@ import {
AssetGraphQuery_assetNodes,
} from './types/AssetGraphQuery';

export interface AssetGraphFetchScope {
hideEdgesToNodesOutsideQuery?: boolean;
pipelineSelector?: PipelineSelector;
groupSelector?: AssetGroupSelector;
}
/** Fetches data for rendering an asset graph:
*
* @param pipelineSelector: Optionally scope to an asset job, or pass null for the global graph
Expand All @@ -21,16 +26,13 @@ import {
* @param filterNodes: filter the returned graph using the provided function. The global graph
* uses this option to implement the "3 of 4 repositories" picker.
*/
export function useAssetGraphData(
opsQuery: string,
options: {
hideEdgesToNodesOutsideQuery?: boolean;
pipelineSelector?: PipelineSelector;
},
) {
export function useAssetGraphData(opsQuery: string, options: AssetGraphFetchScope) {
const fetchResult = useQuery<AssetGraphQuery, AssetGraphQueryVariables>(ASSET_GRAPH_QUERY, {
variables: {pipelineSelector: options.pipelineSelector},
notifyOnNetworkStatusChange: true,
variables: {
pipelineSelector: options.pipelineSelector,
groupSelector: options.groupSelector,
},
});

const nodes = fetchResult.data?.assetNodes;
Expand Down Expand Up @@ -129,8 +131,8 @@ const removeEdgesToHiddenAssets = (graphData: GraphData) => {
};

const ASSET_GRAPH_QUERY = gql`
query AssetGraphQuery($pipelineSelector: PipelineSelector) {
assetNodes(pipeline: $pipelineSelector) {
query AssetGraphQuery($pipelineSelector: PipelineSelector, $groupSelector: AssetGroupSelector) {
assetNodes(pipeline: $pipelineSelector, group: $groupSelector) {
id
dependencyKeys {
path
Expand Down
58 changes: 0 additions & 58 deletions js_modules/dagit/packages/core/src/assets/AssetEntryRoot.tsx

This file was deleted.

76 changes: 76 additions & 0 deletions js_modules/dagit/packages/core/src/assets/AssetGroupRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {Box, Heading, Page, PageHeader, Tabs, Tag} from '@dagster-io/ui';
import * as React from 'react';
import {useHistory, useParams} from 'react-router-dom';

import {AssetGraphExplorer} from '../asset-graph/AssetGraphExplorer';
import {useDocumentTitle} from '../hooks/useDocumentTitle';
import {useQueryPersistedState} from '../hooks/useQueryPersistedState';
import {RepositoryLink} from '../nav/RepositoryLink';
import {explorerPathFromString, explorerPathToString} from '../pipelines/PipelinePathUtils';
import {TabLink} from '../ui/TabLink';
import {RepoAddress} from '../workspace/types';
import {workspacePathFromAddress} from '../workspace/workspacePath';

import {AssetsCatalogTable} from './AssetsCatalogTable';

export const AssetGroupRoot: React.FC<{repoAddress: RepoAddress}> = ({repoAddress}) => {
const params = useParams();
const history = useHistory();
const explorerPath = explorerPathFromString(params[0]);
const {pipelineName: groupName, opNames: prefixPath} = explorerPath;

useDocumentTitle(`Asset Group: ${groupName}`);

const [tab = 'lineage'] = useQueryPersistedState<'lineage' | 'list'>({queryKey: 'tab'});
const groupSelector = React.useMemo(
() => ({
groupName,
repositoryLocationName: repoAddress.location,
repositoryName: repoAddress.name,
}),
[groupName, repoAddress],
);

return (
<Page style={{display: 'flex', flexDirection: 'column', paddingBottom: 0}}>
<PageHeader
title={<Heading>{groupName}</Heading>}
tags={
<Tag icon="asset_group">
Asset Group in <RepositoryLink repoAddress={repoAddress} />
</Tag>
}
tabs={
<Box flex={{direction: 'row', justifyContent: 'space-between', alignItems: 'flex-end'}}>
<Tabs selectedTabId={tab}>
<TabLink
id="lineage"
title="Lineage"
to={workspacePathFromAddress(repoAddress, `/asset-groups/${groupName}?tab=lineage`)}
/>
<TabLink
id="list"
title="List"
to={workspacePathFromAddress(repoAddress, `/asset-groups/${groupName}?tab=list`)}
/>
</Tabs>
</Box>
}
/>
{tab === 'lineage' ? (
<AssetGraphExplorer
fetchOptions={{groupSelector}}
options={{preferAssetRendering: true, explodeComposites: true}}
explorerPath={explorerPath}
onChangeExplorerPath={(path, mode) => {
history[mode](
workspacePathFromAddress(repoAddress, `/asset-groups/${explorerPathToString(path)}`),
);
}}
/>
) : (
<AssetsCatalogTable prefixPath={prefixPath.filter(Boolean)} groupSelector={groupSelector} />
)}
</Page>
);
};

1 comment on commit 7fa711e

@vercel
Copy link

@vercel vercel bot commented on 7fa711e Jun 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

dagit-storybook – ./js_modules/dagit/packages/ui

dagit-storybook.vercel.app
dagit-storybook-git-master-elementl.vercel.app
dagit-storybook-elementl.vercel.app

Please sign in to comment.