Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ui] Ability to report asset materializations from the actions dropdown #22691

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import email from '../icon-svgs/email.svg';
import error from '../icon-svgs/error.svg';
import error_outline from '../icon-svgs/error_outline.svg';
import execute from '../icon-svgs/execute.svg';
import executing from '../icon-svgs/executing.svg';
import expand from '../icon-svgs/expand.svg';
import expand_arrows from '../icon-svgs/expand_arrows.svg';
import expand_less from '../icon-svgs/expand_less.svg';
Expand Down Expand Up @@ -205,6 +206,7 @@ export const Icons = {
datatype_number,
expectation,
execute,
executing,
materialization,
observation,
job,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import {Button, Icon, Menu, MenuItem, Popover, Spinner, Tooltip} from '@dagster-io/ui-components';
import {
Button,
Icon,
Menu,
MenuDivider,
MenuItem,
Popover,
Spinner,
Tooltip,
} from '@dagster-io/ui-components';
import {useContext} from 'react';

import {
Expand All @@ -8,6 +17,7 @@ import {
import {useObserveAction} from './LaunchAssetObservationButton';
import {assetDetailsPathForKey} from './assetDetailsPathForKey';
import {AssetTableDefinitionFragment} from './types/AssetTableFragment.types';
import {useReportEventsModal} from './useReportEventsModal';
import {CloudOSSContext} from '../app/CloudOSSContext';
import {showSharedToaster} from '../app/DomUtils';
import {usePermissionsForLocation} from '../app/Permissions';
Expand All @@ -30,14 +40,24 @@ export const AssetActionMenu = (props: Props) => {
} = usePermissionsForLocation(repoAddress?.location);

const {
featureContext: {canSeeWipeMaterializationAction},
featureContext: {canSeeWipeMaterializationAction, canSeeMaterializeAction},
} = useContext(CloudOSSContext);

const {executeItem, launchpadElement} = useExecuteAssetMenuItem(path, definition);

const reportEvents = useReportEventsModal(
repoAddress
? {
assetKey: {path},
isPartitioned: !!definition?.partitionDefinition,
repository: {name: repoAddress.name, location: {name: repoAddress.location}},
}
: null,
);
return (
<>
{launchpadElement}
{reportEvents.element}
<Popover
position="bottom-right"
content={
Expand All @@ -55,7 +75,13 @@ export const AssetActionMenu = (props: Props) => {
icon="asset_group"
/>
<MenuLink
text="View neighbors"
text="View checks"
to={assetDetailsPathForKey({path}, {view: 'checks'})}
disabled={!definition}
icon="asset_check"
/>
<MenuLink
text="View lineage"
to={assetDetailsPathForKey({path}, {view: 'lineage', lineageScope: 'neighbors'})}
disabled={!definition}
icon="graph_neighbors"
Expand All @@ -72,6 +98,17 @@ export const AssetActionMenu = (props: Props) => {
disabled={!definition}
icon="graph_downstream"
/>
{canSeeMaterializeAction && definition?.hasMaterializePermission
? reportEvents.dropdownOptions.map((option) => (
<MenuItem
key={option.label}
text={option.label}
icon={option.icon}
onClick={option.onClick}
/>
))
: undefined}
{canSeeWipeMaterializationAction ? <MenuDivider /> : undefined}
{canSeeWipeMaterializationAction ? (
<MenuItem
text="Wipe materializations"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import {
Box,
Colors,
Icon,
MiddleTruncate,
Popover,
Spinner,
Tag,
intentToFillColor,
} from '@dagster-io/ui-components';
import {Link} from 'react-router-dom';

import {ChecksSummaryPopover} from './AssetChecksStatusSummary';
import {assertUnreachable} from '../../app/Util';
import {AssetCheckLiveFragment} from '../../asset-data/types/AssetBaseDataProvider.types';
import {
AssetCheckEvaluation,
AssetCheckExecution,
AssetCheckExecutionResolvedStatus,
AssetCheckSeverity,
AssetKey,
AssetKeyInput,
} from '../../graphql/types';
import {linkToRunEvent} from '../../runs/RunUtils';
import {TimestampDisplay} from '../../schedules/TimestampDisplay';
Expand All @@ -31,7 +34,7 @@ const CheckRow = ({
}: {
icon: JSX.Element;
checkName: string;
assetKey: AssetKey;
assetKey: AssetKeyInput;
timestamp?: number;
}) => (
<Box
Expand All @@ -56,7 +59,7 @@ export const CheckStatusRow = ({
assetKey,
}: {
assetCheck: AssetCheckLiveFragment;
assetKey: AssetKey;
assetKey: AssetKeyInput;
}) => {
const {executionForLatestMaterialization: execution} = assetCheck;

Expand Down Expand Up @@ -228,29 +231,56 @@ export const AssetCheckStatusTag = ({
export const AssetCheckErrorsTag = ({
checks,
severity,
assetKey,
}: {
checks: AssetCheckLiveFragment[];
severity: AssetCheckSeverity;
assetKey: AssetKeyInput;
}) => {
const actions: TagAction[] = [];
const execution = checks[0]?.executionForLatestMaterialization;
if (execution) {
const tagIcon = severity === AssetCheckSeverity.ERROR ? 'cancel' : 'warning_outline';
const tagIntent = severity === AssetCheckSeverity.ERROR ? 'danger' : 'warning';

if (checks.length === 1) {
const actions: TagAction[] = [];
const execution = checks[0]!.executionForLatestMaterialization;

actions.push({
label: 'View in run logs',
to: linkToRunEvent(
{id: execution.runId},
{stepKey: execution.stepKey, timestamp: execution.timestamp},
),
label: 'View details',
to: assetDetailsPathForAssetCheck({assetKey, name: checks[0]!.name}),
});
if (execution) {
actions.push({
label: 'View in run logs',
to: linkToRunEvent(
{id: execution.runId},
{stepKey: execution.stepKey, timestamp: execution.timestamp},
),
});
}

return (
<TagActionsPopover
data={{key: '', value: ''}}
actions={actions}
childrenMiddleTruncate={checks.length === 1}
>
<Tag icon={tagIcon} intent={tagIntent}>
<MiddleTruncate text={checks[0]!.name} />
</Tag>
</TagActionsPopover>
);
}

return (
<TagActionsPopover data={{key: '', value: ''}} actions={actions}>
<Tag
icon={severity === AssetCheckSeverity.ERROR ? 'cancel' : 'warning_outline'}
intent={severity === AssetCheckSeverity.ERROR ? 'danger' : 'warning'}
>
{checks.length === 1 ? checks[0]!.name : `${checks.length} failed`}
<Popover
content={<ChecksSummaryPopover type={severity} assetKey={assetKey} assetChecks={checks} />}
position="top-left"
interactionKind="hover"
className="chunk-popover-target"
>
<Tag icon={tagIcon} intent={tagIntent}>
{`${checks.length} failed`}
</Tag>
</TagActionsPopover>
</Popover>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {CheckStatusRow} from './AssetCheckStatusTag';
import {assertUnreachable} from '../../app/Util';
import {AssetCheckLiveFragment} from '../../asset-data/types/AssetBaseDataProvider.types';
import {LiveDataForNode} from '../../asset-graph/Utils';
import {AssetCheckExecutionResolvedStatus, AssetCheckSeverity, AssetKey} from '../../graphql/types';
import {
AssetCheckExecutionResolvedStatus,
AssetCheckSeverity,
AssetKeyInput,
} from '../../graphql/types';

type AssetCheckIconType =
| Exclude<
Expand Down Expand Up @@ -82,13 +86,13 @@ const titlePerCheckType = (type: AssetCheckIconType) => {
}
};

const ChecksSummaryPopover = ({
export const ChecksSummaryPopover = ({
type,
assetKey,
assetChecks,
}: {
type: AssetCheckIconType;
assetKey: AssetKey;
assetKey: AssetKeyInput;
assetChecks: AssetCheckLiveFragment[];
}) => {
return (
Expand Down Expand Up @@ -123,7 +127,7 @@ export const AssetChecksStatusSummary = ({
}: {
liveData: LiveDataForNode;
rendering: 'dag' | 'tags';
assetKey: AssetKey;
assetKey: AssetKeyInput;
}) => {
const byIconType = countBy(liveData.assetChecks, iconTypeFromCheck);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Asset = {
repository: {name: string; location: {name: string}};
};

export function useReportEventsModal(asset: Asset | null, onEventReported: () => void) {
export function useReportEventsModal(asset: Asset | null, onEventReported?: () => void) {
const [isOpen, setIsOpen] = useState(false);

const dropdownOptions = useMemo(
Expand Down Expand Up @@ -85,7 +85,7 @@ const ReportEventDialogBody = ({
repoAddress: RepoAddress;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
onEventReported: () => void;
onEventReported?: () => void;
}) => {
const [description, setDescription] = useState('');
const {
Expand Down Expand Up @@ -159,7 +159,7 @@ const ReportEventDialogBody = ({
icon: 'materialization',
intent: 'success',
});
onEventReported();
onEventReported?.();
setIsOpen(false);
}
};
Expand Down
3 changes: 3 additions & 0 deletions js_modules/dagster-ui/packages/ui-core/src/ui/TagActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ export const TagActionsPopover = ({
data,
actions,
children,
childrenMiddleTruncate,
}: {
data: TagType;
actions: TagAction[];
children: React.ReactNode;
childrenMiddleTruncate?: boolean;
}) => {
return (
<Popover
content={<TagActions actions={actions} data={data} />}
hoverOpenDelay={100}
hoverCloseDelay={100}
targetProps={childrenMiddleTruncate ? {style: {minWidth: 0, maxWidth: '100%'}} : {}}
placement="top"
interactionKind="hover"
>
Expand Down