From dfeac61fd61fcd298e68597d7bfd84b8d550746d Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Mon, 19 Aug 2024 09:47:41 -0500 Subject: [PATCH] [ui] Allow deleting dynamic partitions from asset pages (#23462) ## Summary & Motivation Fixes https://github.com/dagster-io/dagster/issues/14026 This PR adds a new "delete partitions" modal in both OSS and Plus that allows you to remove dynamic partitions from the UI. The UI immediately updates to reflect the change. During testing I also found that our current UI for adding dynamic partitions didn't work quite right in cases where the dynamic partition was one of the dimensions of a multi-dimensional partition def, because the `dimension.name` and the `dimension.dynamicPartitionsDefinitionName` are not the same, and the Add Partition mutation requires the latter. The new dropdown option in these screenshots only appears if the asset has a dynamic partition dimension, so non-dynamic assets are unimpacted. image image image ## How I Tested These Changes There's a new test file covering this modal! I also tested this manually using both assets that have dynamic partitions and assets that have multi-dimensional partition defs where one axis is dynamic. Also verified the changes in the report materialization events modal and the wipe assets implementation on the asset table didn't have any impact. --------- Co-authored-by: bengotow --- .../ui-core/src/assets/AssetActionMenu.tsx | 51 ++--- .../ui-core/src/assets/AssetPartitions.tsx | 2 +- .../ui-core/src/assets/AssetTable.tsx | 12 +- .../ui-core/src/assets/AssetTableFragment.tsx | 4 + .../packages/ui-core/src/assets/AssetView.tsx | 40 ++-- .../assets/DeleteDynamicPartitionsDialog.tsx | 189 ++++++++++++++++++ .../__fixtures__/PartitionHealth.fixtures.ts | 22 ++ .../DeleteDynamicPartitionsDialog.test.tsx | 78 ++++++++ ...LaunchAssetChoosePartitionsDialog.test.tsx | 15 +- .../assets/types/AssetTableFragment.types.ts | 20 +- .../src/assets/types/AssetView.types.ts | 20 +- .../assets/types/AssetsCatalogTable.types.ts | 30 ++- .../DeleteDynamicPartitionsDialog.types.ts | 26 +++ .../types/useReportEventsModal.types.ts | 25 +++ .../useDeleteDynamicPartitionsDialog.tsx | 70 +++++++ .../src/assets/useReportEventsModal.tsx | 94 ++++----- .../ui-core/src/assets/useWipeModal.tsx | 2 +- .../launchpad/ConfigEditorConfigPicker.tsx | 8 +- .../src/partitions/CreatePartitionDialog.tsx | 10 +- .../src/partitions/DimensionRangeWizard.tsx | 6 +- .../src/partitions/DimensionRangeWizards.tsx | 3 +- .../__tests__/CreatePartitionDialog.test.tsx | 2 +- .../src/workspace/VirtualizedAssetRow.tsx | 6 +- .../src/workspace/VirtualizedAssetTable.tsx | 10 +- .../workspace/VirtualizedRepoAssetTable.tsx | 2 +- .../types/VirtualizedRepoAssetTable.types.ts | 10 +- .../types/WorkspaceAssetsRoot.types.ts | 10 +- 27 files changed, 633 insertions(+), 134 deletions(-) create mode 100644 js_modules/dagster-ui/packages/ui-core/src/assets/DeleteDynamicPartitionsDialog.tsx create mode 100644 js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/DeleteDynamicPartitionsDialog.test.tsx create mode 100644 js_modules/dagster-ui/packages/ui-core/src/assets/types/DeleteDynamicPartitionsDialog.types.ts create mode 100644 js_modules/dagster-ui/packages/ui-core/src/assets/useDeleteDynamicPartitionsDialog.tsx diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx index c26b766d67ad3..7cda959c100f8 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx @@ -1,13 +1,4 @@ -import { - Button, - Icon, - Menu, - MenuDivider, - MenuItem, - Popover, - Spinner, - Tooltip, -} from '@dagster-io/ui-components'; +import {Button, Icon, Menu, MenuItem, Popover, Spinner, Tooltip} from '@dagster-io/ui-components'; import {useContext} from 'react'; import { @@ -17,11 +8,11 @@ import { import {useObserveAction} from './LaunchAssetObservationButton'; import {assetDetailsPathForKey} from './assetDetailsPathForKey'; import {AssetTableDefinitionFragment} from './types/AssetTableFragment.types'; +import {useDeleteDynamicPartitionsDialog} from './useDeleteDynamicPartitionsDialog'; import {useReportEventsModal} from './useReportEventsModal'; +import {useWipeModal} from './useWipeModal'; import {CloudOSSContext} from '../app/CloudOSSContext'; import {showSharedToaster} from '../app/DomUtils'; -import {usePermissionsForLocation} from '../app/Permissions'; -import {AssetKeyInput} from '../graphql/types'; import {MenuLink} from '../ui/MenuLink'; import {RepoAddress} from '../workspace/types'; import {workspacePathFromAddress} from '../workspace/workspacePath'; @@ -30,34 +21,42 @@ interface Props { path: string[]; definition: AssetTableDefinitionFragment | null; repoAddress: RepoAddress | null; - onWipe?: (assets: AssetKeyInput[]) => void; + onRefresh?: () => void; } export const AssetActionMenu = (props: Props) => { - const {repoAddress, path, definition, onWipe} = props; - const { - permissions: {canWipeAssets}, - } = usePermissionsForLocation(repoAddress?.location); - + const {repoAddress, path, definition, onRefresh} = props; const { - featureContext: {canSeeWipeMaterializationAction, canSeeMaterializeAction}, + featureContext: {canSeeMaterializeAction}, } = useContext(CloudOSSContext); const {executeItem, launchpadElement} = useExecuteAssetMenuItem(path, definition); + const deletePartitions = useDeleteDynamicPartitionsDialog( + repoAddress && definition ? {repoAddress, assetKey: {path}, definition} : null, + onRefresh, + ); + + const wipe = useWipeModal( + repoAddress && definition ? {repository: definition.repository, assetKey: {path}} : null, + onRefresh, + ); + const reportEvents = useReportEventsModal( repoAddress ? { assetKey: {path}, isPartitioned: !!definition?.partitionDefinition, - repository: {name: repoAddress.name, location: {name: repoAddress.location}}, + repoAddress, } : null, ); return ( <> {launchpadElement} + {wipe.element} {reportEvents.element} + {deletePartitions.element} { /> )) : undefined} - {canSeeWipeMaterializationAction ? : undefined} - {canSeeWipeMaterializationAction ? ( - canWipeAssets && onWipe && onWipe([{path}])} - /> - ) : null} + {wipe.dropdownOptions} + {deletePartitions.dropdownOptions} } > diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetPartitions.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetPartitions.tsx index 48472c4b4c050..19ce38ca6c06f 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetPartitions.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetPartitions.tsx @@ -203,6 +203,7 @@ export const AssetPartitions = ({ {timeDimensionIdx !== -1 && ( (idx === timeDimensionIdx ? {...r, selectedKeys} : r)), ) } - dimensionType={selections[timeDimensionIdx]!.dimension.type} /> )} diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTable.tsx index 7625aefbf2aa4..686734271d84e 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTable.tsx @@ -22,7 +22,7 @@ import {AssetViewType} from './useAssetView'; import {CloudOSSContext} from '../app/CloudOSSContext'; import {useUnscopedPermissions} from '../app/Permissions'; import {QueryRefreshCountdown, RefreshState} from '../app/QueryRefresh'; -import {AssetKeyInput, DefinitionTag} from '../graphql/types'; +import {DefinitionTag} from '../graphql/types'; import {useSelectionReducer} from '../hooks/useSelectionReducer'; import {testId} from '../testing/testId'; import {StaticSetFilter} from '../ui/BaseFilters/useStaticSetFilter'; @@ -57,8 +57,6 @@ export const AssetTable = ({ computeKindFilter, storageKindFilter, }: Props) => { - const [toWipe, setToWipe] = React.useState(); - const groupedByDisplayKey = useMemo( () => groupBy(assets, (a) => JSON.stringify(displayPathForAsset(a))), [assets, displayPathForAsset], @@ -139,9 +137,9 @@ export const AssetTable = ({ groups={groupedByDisplayKey} checkedDisplayKeys={checkedDisplayKeys} onToggleFactory={onToggleFactory} + onRefresh={() => refreshState.refetch()} showRepoColumn view={view} - onWipe={(assetKeys: AssetKeyInput[]) => setToWipe(assetKeys)} computeKindFilter={computeKindFilter} storageKindFilter={storageKindFilter} /> @@ -188,12 +186,6 @@ export const AssetTable = ({ {belowActionBarComponents} {content()} - setToWipe(undefined)} - onComplete={() => refreshState.refetch()} - /> ); }; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTableFragment.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTableFragment.tsx index d40a626b00151..e3dbb48636906 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTableFragment.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetTableFragment.tsx @@ -13,6 +13,10 @@ export const ASSET_TABLE_DEFINITION_FRAGMENT = gql` hasMaterializePermission partitionDefinition { description + dimensionTypes { + type + dynamicPartitionsDefinitionName + } } description owners { diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetView.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetView.tsx index 2d55d87079f6f..fddeeb93a64de 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetView.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetView.tsx @@ -29,6 +29,7 @@ import { AssetViewDefinitionQuery, AssetViewDefinitionQueryVariables, } from './types/AssetView.types'; +import {useDeleteDynamicPartitionsDialog} from './useDeleteDynamicPartitionsDialog'; import {healthRefreshHintFromLiveData} from './usePartitionHealthData'; import {useReportEventsModal} from './useReportEventsModal'; import {useWipeModal} from './useWipeModal'; @@ -46,6 +47,7 @@ import {useAssetGraphData} from '../asset-graph/useAssetGraphData'; import {StaleReasonsTag} from '../assets/Stale'; import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; import {useBlockTraceOnQueryResult} from '../performance/TraceContext'; +import {buildRepoAddress} from '../workspace/buildRepoAddress'; interface Props { assetKey: AssetKey; @@ -257,6 +259,14 @@ export const AssetView = ({assetKey, headerBreadcrumbs, writeAssetVisit, current } }; + const repoAddress = useMemo( + () => + definition + ? buildRepoAddress(definition.repository.name, definition.repository.location.name) + : null, + [definition], + ); + const setCurrentPage = useSetRecoilState(currentPageAtom); const {path} = useRouteMatch(); useEffect(() => { @@ -264,21 +274,21 @@ export const AssetView = ({assetKey, headerBreadcrumbs, writeAssetVisit, current }, [path, selectedTab, setCurrentPage]); const wipe = useWipeModal( - definition - ? { - assetKey: definition.assetKey, - repository: definition.repository, - } - : null, + definition ? {assetKey: definition.assetKey, repository: definition.repository} : null, refresh, ); + + const dynamicPartitionsDelete = useDeleteDynamicPartitionsDialog( + definition && repoAddress ? {assetKey: definition.assetKey, definition, repoAddress} : null, + () => { + definitionQueryResult.refetch(); + refresh(); + }, + ); + const reportEvents = useReportEventsModal( - definition - ? { - assetKey: definition.assetKey, - isPartitioned: definition.isPartitioned, - repository: definition.repository, - } + definition && repoAddress + ? {assetKey: definition.assetKey, isPartitioned: definition.isPartitioned, repoAddress} : null, refresh, ); @@ -326,11 +336,13 @@ export const AssetView = ({assetKey, headerBreadcrumbs, writeAssetVisit, current additionalDropdownOptions={[ ...reportEvents.dropdownOptions, ...wipe.dropdownOptions, + ...dynamicPartitionsDelete.dropdownOptions, ]} /> ) : undefined} {reportEvents.element} {wipe.element} + {dynamicPartitionsDelete.element} } /> @@ -470,6 +482,10 @@ export const ASSET_VIEW_DEFINITION_QUERY = gql` groupName partitionDefinition { description + dimensionTypes { + type + dynamicPartitionsDefinitionName + } } partitionKeysByDimension { name diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/DeleteDynamicPartitionsDialog.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/DeleteDynamicPartitionsDialog.tsx new file mode 100644 index 0000000000000..73fe1f5f83d9e --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/DeleteDynamicPartitionsDialog.tsx @@ -0,0 +1,189 @@ +import {RefetchQueriesFunction, gql, useMutation} from '@apollo/client'; +// eslint-disable-next-line no-restricted-imports +import { + Body2, + Box, + Button, + Dialog, + DialogBody, + DialogFooter, + Spinner, +} from '@dagster-io/ui-components'; +import {memo, useCallback, useMemo, useState} from 'react'; + +import { + DeleteDynamicPartitionsMutation, + DeleteDynamicPartitionsMutationVariables, +} from './types/DeleteDynamicPartitionsDialog.types'; +import {usePartitionHealthData} from './usePartitionHealthData'; +import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; +import {PythonErrorInfo} from '../app/PythonErrorInfo'; +import {AssetKeyInput, PartitionDefinitionType} from '../graphql/types'; +import {OrdinalPartitionSelector} from '../partitions/OrdinalPartitionSelector'; +import {RepoAddress} from '../workspace/types'; + +export interface DeleteDynamicPartitionsDialogProps { + assetKey: AssetKeyInput; + repoAddress: RepoAddress; + partitionsDefName: string; + isOpen: boolean; + onClose: () => void; + onComplete?: () => void; + requery?: RefetchQueriesFunction; +} + +export const DeleteDynamicPartitionsDialog = memo((props: DeleteDynamicPartitionsDialogProps) => { + return ( + + + + ); +}); + +export const DeleteDynamicPartitionsModalInner = memo( + ({ + repoAddress, + assetKey, + partitionsDefName, + onClose, + onComplete, + requery, + }: DeleteDynamicPartitionsDialogProps) => { + const [deleting, setDeleting] = useState(false); + const [result, setResult] = useState< + DeleteDynamicPartitionsMutation['deleteDynamicPartitions'] | undefined + >(); + const [selectedPartitions, setSelectedPartitions] = useState([]); + const [health] = usePartitionHealthData([assetKey]); + + const dynamicHealth = health?.dimensions.find( + (d) => d.type === PartitionDefinitionType.DYNAMIC, + ); + + const [deletePartitions] = useMutation< + DeleteDynamicPartitionsMutation, + DeleteDynamicPartitionsMutationVariables + >(DELETE_DYNAMIC_PARTITIONS_MUTATION, {refetchQueries: requery}); + + const onDelete = useCallback( + async (partitionKeys: string[]) => { + setDeleting(true); + const resp = await deletePartitions({ + variables: { + repositorySelector: { + repositoryLocationName: repoAddress.location, + repositoryName: repoAddress.name, + }, + partitionsDefName, + partitionKeys, + }, + }); + setResult(resp.data?.deleteDynamicPartitions); + setDeleting(false); + onComplete?.(); + }, + [deletePartitions, onComplete, partitionsDefName, repoAddress.location, repoAddress.name], + ); + + const content = useMemo(() => { + if (result) { + return ( + + {result.__typename === 'DeleteDynamicPartitionsSuccess' ? ( + + The selected partitions of {partitionsDefName} and associated + materializations have been deleted. + + ) : ( + + )} + + ); + } + if (deleting) { + return ( + +
Wiping...
+
+ ); + } + return ( + + + Select partition keys of the {partitionsDefName} partition definition + to delete. + + {health && dynamicHealth ? ( + + ) : ( + + )} + + Deleting partitions impacts all assets that share this partition definition. + Materialization events for these partitions will be wiped.{' '} + This action cannot be undone. + + + ); + }, [deleting, dynamicHealth, health, partitionsDefName, result, selectedPartitions]); + + return ( + <> + {content} + + + {result ? null : ( + + )} + + + ); + }, +); + +export const DELETE_DYNAMIC_PARTITIONS_MUTATION = gql` + mutation DeleteDynamicPartitionsMutation( + $partitionKeys: [String!]! + $partitionsDefName: String! + $repositorySelector: RepositorySelector! + ) { + deleteDynamicPartitions( + partitionKeys: $partitionKeys + partitionsDefName: $partitionsDefName + repositorySelector: $repositorySelector + ) { + ... on DeleteDynamicPartitionsSuccess { + __typename + } + ... on UnauthorizedError { + message + __typename + } + ...PythonErrorFragment + } + } + + ${PYTHON_ERROR_FRAGMENT} +`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/__fixtures__/PartitionHealth.fixtures.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/__fixtures__/PartitionHealth.fixtures.ts index 309630af16bcf..e1032e49c34c1 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/__fixtures__/PartitionHealth.fixtures.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/__fixtures__/PartitionHealth.fixtures.ts @@ -304,3 +304,25 @@ export const TWO_DIMENSIONAL_ASSET_EMPTY: PartitionHealthQuery = { }, }, }; + +export const ONE_DIMENSIONAL_DYNAMIC_ASSET: PartitionHealthQuery = { + __typename: 'Query', + assetNodeOrError: { + __typename: 'AssetNode', + id: '1234', + partitionKeysByDimension: [ + { + __typename: 'DimensionPartitionKeys', + name: 'default', + partitionKeys: ['apple', 'pear', 'fig'], + type: PartitionDefinitionType.DYNAMIC, + }, + ], + assetPartitionStatuses: { + __typename: 'DefaultPartitionStatuses', + materializedPartitions: ['apple', 'pear', 'fig'], + materializingPartitions: [], + failedPartitions: [], + }, + }, +}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/DeleteDynamicPartitionsDialog.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/DeleteDynamicPartitionsDialog.test.tsx new file mode 100644 index 0000000000000..24f5527a2c4ad --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/DeleteDynamicPartitionsDialog.test.tsx @@ -0,0 +1,78 @@ +import {MockedProvider} from '@apollo/client/testing'; +import {waitFor} from '@testing-library/dom'; +import {render} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { + buildQueryMock, + mockViewportClientRect, + restoreViewportClientRect, +} from '../../testing/mocking'; +import { + DELETE_DYNAMIC_PARTITIONS_MUTATION, + DeleteDynamicPartitionsDialog, +} from '../DeleteDynamicPartitionsDialog'; +import {ONE_DIMENSIONAL_DYNAMIC_ASSET} from '../__fixtures__/PartitionHealth.fixtures'; +import {PARTITION_HEALTH_QUERY} from '../usePartitionHealthData'; + +describe('DeleteDynamicPartitionsDialog', () => { + beforeAll(() => { + mockViewportClientRect(); + }); + afterAll(() => { + restoreViewportClientRect(); + }); + + it('should show a partition selector and delete selected partitions', async () => { + const deletePartitionsMock = { + request: { + query: DELETE_DYNAMIC_PARTITIONS_MUTATION, + variables: { + repositorySelector: {repositoryLocationName: 'location', repositoryName: 'repo.py'}, + partitionsDefName: 'fruits', + partitionKeys: ['apple', 'fig'], + }, + }, + result: jest.fn(() => ({ + data: { + __typename: 'Mutation', + deleteDynamicPartitions: {}, + }, + })), + }; + + const {getByText, getByTestId} = render( + + {}} + /> + , + ); + await waitFor(() => { + expect(getByText('Delete fruits partitions')).toBeVisible(); + }); + const user = userEvent.setup(); + await waitFor(async () => { + await user.click(getByText('Select a partition')); + }); + await user.click(getByTestId(`menu-item-apple`)); + await user.click(getByTestId(`menu-item-fig`)); + + await user.click(getByText('Delete 2 partitions')); + + expect(deletePartitionsMock.result).toHaveBeenCalled(); + }); +}); diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/LaunchAssetChoosePartitionsDialog.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/LaunchAssetChoosePartitionsDialog.test.tsx index b001d6fb59497..d790f9317e470 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/LaunchAssetChoosePartitionsDialog.test.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/LaunchAssetChoosePartitionsDialog.test.tsx @@ -9,6 +9,7 @@ import { buildAddDynamicPartitionSuccess, buildAssetKey, buildAssetNode, + buildDimensionDefinitionType, buildDimensionPartitionKeys, buildMultiPartitionStatuses, buildPartitionDefinition, @@ -127,6 +128,7 @@ describe('launchAssetChoosePartitionsDialog', () => { const savePartitionButton = screen.getByTestId('save-partition-button'); userEvent.click(savePartitionButton); + // Verify that it refreshes asset health after partition is added await waitFor(() => { expect(assetASecondQueryMockResult).toHaveBeenCalled(); }); @@ -150,7 +152,18 @@ function buildAsset(name: string, dynamicPartitionKeys: string[]) { }), ], partitionDefinition: buildPartitionDefinition({ - name: 'foo', + name: 'not-foo', + dimensionTypes: [ + buildDimensionDefinitionType({ + name: 'a', + type: PartitionDefinitionType.DYNAMIC, + dynamicPartitionsDefinitionName: 'foo', + }), + buildDimensionDefinitionType({ + name: 'b', + type: PartitionDefinitionType.TIME_WINDOW, + }), + ], }), assetPartitionStatuses: buildMultiPartitionStatuses({ primaryDimensionName: 'b', diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetTableFragment.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetTableFragment.types.ts index a761450e07853..629a6d663e639 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetTableFragment.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetTableFragment.types.ts @@ -14,7 +14,15 @@ export type AssetTableDefinitionFragment = { computeKind: string | null; hasMaterializePermission: boolean; description: string | null; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} >; @@ -43,7 +51,15 @@ export type AssetTableFragment = { computeKind: string | null; hasMaterializePermission: boolean; description: string | null; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} >; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetView.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetView.types.ts index d74dd6f1500a9..8979f4e15fafa 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetView.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetView.types.ts @@ -34,7 +34,15 @@ export type AssetViewDefinitionQuery = { computeKind: string | null; isPartitioned: boolean; isObservable: boolean; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; partitionKeysByDimension: Array<{__typename: 'DimensionPartitionKeys'; name: string}>; repository: { __typename: 'Repository'; @@ -16305,7 +16313,15 @@ export type AssetViewDefinitionNodeFragment = { computeKind: string | null; isPartitioned: boolean; isObservable: boolean; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; partitionKeysByDimension: Array<{__typename: 'DimensionPartitionKeys'; name: string}>; repository: { __typename: 'Repository'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetsCatalogTable.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetsCatalogTable.types.ts index 7345f291fb5b9..65d02719286ca 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetsCatalogTable.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/types/AssetsCatalogTable.types.ts @@ -29,7 +29,15 @@ export type AssetCatalogTableQuery = { computeKind: string | null; hasMaterializePermission: boolean; description: string | null; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< | {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} @@ -75,7 +83,15 @@ export type AssetCatalogGroupTableQuery = { hasMaterializePermission: boolean; description: string | null; assetKey: {__typename: 'AssetKey'; path: Array}; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} >; @@ -102,7 +118,15 @@ export type AssetCatalogGroupTableNodeFragment = { hasMaterializePermission: boolean; description: string | null; assetKey: {__typename: 'AssetKey'; path: Array}; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} >; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/types/DeleteDynamicPartitionsDialog.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/types/DeleteDynamicPartitionsDialog.types.ts new file mode 100644 index 0000000000000..8094cff7036b5 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/types/DeleteDynamicPartitionsDialog.types.ts @@ -0,0 +1,26 @@ +// Generated GraphQL types, do not edit manually. + +import * as Types from '../../graphql/types'; + +export type DeleteDynamicPartitionsMutationVariables = Types.Exact<{ + partitionKeys: Array | Types.Scalars['String']['input']; + partitionsDefName: Types.Scalars['String']['input']; + repositorySelector: Types.RepositorySelector; +}>; + +export type DeleteDynamicPartitionsMutation = { + __typename: 'Mutation'; + deleteDynamicPartitions: + | {__typename: 'DeleteDynamicPartitionsSuccess'} + | { + __typename: 'PythonError'; + message: string; + stack: Array; + errorChain: Array<{ + __typename: 'ErrorChainLink'; + isExplicitLink: boolean; + error: {__typename: 'PythonError'; message: string; stack: Array}; + }>; + } + | {__typename: 'UnauthorizedError'; message: string}; +}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/types/useReportEventsModal.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/types/useReportEventsModal.types.ts index 565511aeb7beb..4d98fca0ac7ab 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/types/useReportEventsModal.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/types/useReportEventsModal.types.ts @@ -2,6 +2,31 @@ import * as Types from '../../graphql/types'; +export type ReportEventPartitionDefinitionQueryVariables = Types.Exact<{ + assetKey: Types.AssetKeyInput; +}>; + +export type ReportEventPartitionDefinitionQuery = { + __typename: 'Query'; + assetNodeOrError: + | { + __typename: 'AssetNode'; + id: string; + partitionDefinition: { + __typename: 'PartitionDefinition'; + type: Types.PartitionDefinitionType; + name: string | null; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + name: string; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; + } + | {__typename: 'AssetNotFoundError'}; +}; + export type ReportEventMutationVariables = Types.Exact<{ eventParams: Types.ReportRunlessAssetEventsParams; }>; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/useDeleteDynamicPartitionsDialog.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/useDeleteDynamicPartitionsDialog.tsx new file mode 100644 index 0000000000000..bdbc95e3b1c8b --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/useDeleteDynamicPartitionsDialog.tsx @@ -0,0 +1,70 @@ +import {Colors, Icon, MenuItem} from '@dagster-io/ui-components'; +import {useContext, useState} from 'react'; + +import {DeleteDynamicPartitionsDialog} from './DeleteDynamicPartitionsDialog'; +import {CloudOSSContext} from '../app/CloudOSSContext'; +import {usePermissionsForLocation} from '../app/Permissions'; +import {AssetKeyInput, PartitionDefinitionType} from '../graphql/types'; +import {RepoAddress} from '../workspace/types'; + +export function useDeleteDynamicPartitionsDialog( + opts: { + repoAddress: RepoAddress; + assetKey: AssetKeyInput; + definition: { + partitionDefinition: { + dimensionTypes: + | {type: PartitionDefinitionType; dynamicPartitionsDefinitionName: string | null}[] + | null; + } | null; + }; + } | null, + refresh?: () => void, +) { + const [showing, setShowing] = useState(false); + const { + permissions: {canWipeAssets}, + } = usePermissionsForLocation(opts ? opts.repoAddress.location : null); + + const { + featureContext: {canSeeWipeMaterializationAction}, + } = useContext(CloudOSSContext); + + const dynamicDimension = opts?.definition.partitionDefinition?.dimensionTypes?.find( + (d) => d.type === PartitionDefinitionType.DYNAMIC, + ); + + if ( + !opts || + !dynamicDimension?.dynamicPartitionsDefinitionName || + !canSeeWipeMaterializationAction + ) { + return { + element: , + dropdownOptions: [] as JSX.Element[], + }; + } + + return { + element: ( + setShowing(false)} + onComplete={refresh} + /> + ), + dropdownOptions: [ + } + disabled={!canWipeAssets} + intent="danger" + onClick={() => setShowing(true)} + />, + ], + }; +} diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx index 5e6391b86c948..f7509cbab7a8b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx @@ -1,4 +1,4 @@ -import {gql, useMutation} from '@apollo/client'; +import {gql, useMutation, useQuery} from '@apollo/client'; import { Body2, Box, @@ -19,9 +19,12 @@ import { explodePartitionKeysInSelectionMatching, mergedAssetHealth, } from './MultipartitioningSupport'; +import {asAssetKeyInput} from './asInput'; import { ReportEventMutation, ReportEventMutationVariables, + ReportEventPartitionDefinitionQuery, + ReportEventPartitionDefinitionQueryVariables, } from './types/useReportEventsModal.types'; import {usePartitionDimensionSelections} from './usePartitionDimensionSelections'; import {keyCountInSelections, usePartitionHealthData} from './usePartitionHealthData'; @@ -31,15 +34,14 @@ import {usePermissionsForLocation} from '../app/Permissions'; import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; import {PythonErrorInfo} from '../app/PythonErrorInfo'; import {AssetEventType, AssetKeyInput, PartitionDefinitionType} from '../graphql/types'; -import {DimensionRangeWizard} from '../partitions/DimensionRangeWizard'; +import {DimensionRangeWizards} from '../partitions/DimensionRangeWizards'; import {ToggleableSection} from '../ui/ToggleableSection'; -import {buildRepoAddress} from '../workspace/buildRepoAddress'; import {RepoAddress} from '../workspace/types'; type Asset = { isPartitioned: boolean; assetKey: AssetKeyInput; - repository: {name: string; location: {name: string}}; + repoAddress: RepoAddress; }; export function useReportEventsModal(asset: Asset | null, onEventReported?: () => void) { @@ -63,7 +65,7 @@ export function useReportEventsModal(asset: Asset | null, onEventReported?: () = asset={asset} isOpen={isOpen} setIsOpen={setIsOpen} - repoAddress={buildRepoAddress(asset.repository?.name, asset.repository?.location?.name)} + repoAddress={asset.repoAddress} onEventReported={onEventReported} /> ) : undefined; @@ -122,6 +124,20 @@ const ReportEventDialogBody = ({ disabledReasons, } = usePermissionsForLocation(repoAddress.location); + const assetPartitionDefResult = useQuery< + ReportEventPartitionDefinitionQuery, + ReportEventPartitionDefinitionQueryVariables + >(REPORT_EVENT_PARTITION_DEFINITION_QUERY, { + variables: { + assetKey: asAssetKeyInput(asset.assetKey), + }, + }); + + const assetPartitionDef = + assetPartitionDefResult.data?.assetNodeOrError.__typename === 'AssetNode' + ? assetPartitionDefResult.data?.assetNodeOrError.partitionDefinition + : null; + const [mutation] = useMutation( REPORT_EVENT_MUTATION, ); @@ -222,46 +238,14 @@ const ReportEventDialogBody = ({ } > - {selections.map((range, idx) => ( - - - - {range.dimension.name} - - - Select partitions to materialize.{' '} - {range.dimension.type === PartitionDefinitionType.TIME_WINDOW - ? 'Click and drag to select a range on the timeline.' - : null} - - - - setSelections((selections) => - selections.map((r) => - r.dimension === range.dimension ? {...r, selectedKeys} : r, - ), - ) - } - partitionDefinitionName={range.dimension.name} - repoAddress={repoAddress} - refetch={async () => setLastRefresh(Date.now())} - /> - - ))} + setLastRefresh(Date.now())} + selections={selections} + setSelections={setSelections} + displayedHealth={assetHealth} + displayedPartitionDefinition={assetPartitionDef} + /> ) : undefined} @@ -295,6 +279,26 @@ const ReportEventDialogBody = ({ ); }; +const REPORT_EVENT_PARTITION_DEFINITION_QUERY = gql` + query ReportEventPartitionDefinitionQuery($assetKey: AssetKeyInput!) { + assetNodeOrError(assetKey: $assetKey) { + __typename + ... on AssetNode { + id + partitionDefinition { + type + name + dimensionTypes { + type + name + dynamicPartitionsDefinitionName + } + } + } + } + } +`; + const REPORT_EVENT_MUTATION = gql` mutation ReportEventMutation($eventParams: ReportRunlessAssetEventsParams!) { reportRunlessAssetEvents(eventParams: $eventParams) { diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/useWipeModal.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/useWipeModal.tsx index a416f3bf84551..02e9206de3016 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/useWipeModal.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/useWipeModal.tsx @@ -8,7 +8,7 @@ import {AssetKeyInput} from '../graphql/types'; export function useWipeModal( opts: {assetKey: AssetKeyInput; repository: {location: {name: string}}} | null, - refresh: () => void, + refresh?: () => void, ) { const [showing, setShowing] = useState(false); const { diff --git a/js_modules/dagster-ui/packages/ui-core/src/launchpad/ConfigEditorConfigPicker.tsx b/js_modules/dagster-ui/packages/ui-core/src/launchpad/ConfigEditorConfigPicker.tsx index 16edc13ffed94..b570ad9b84927 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/launchpad/ConfigEditorConfigPicker.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/launchpad/ConfigEditorConfigPicker.tsx @@ -224,7 +224,7 @@ const ConfigEditorPartitionPicker = React.memo((props: ConfigEditorPartitionPick rightElement, }; - const {isDynamicPartition, partitionDefinitionName} = React.useMemo(() => { + const {isDynamicPartition, dynamicPartitionsDefinitionName} = React.useMemo(() => { const assetNodes = data?.assetNodes; const definition = assetNodes?.find((a) => !!a.partitionDefinition)?.partitionDefinition; if ( @@ -234,11 +234,11 @@ const ConfigEditorPartitionPicker = React.memo((props: ConfigEditorPartitionPick node?.partitionDefinition?.name && node?.partitionDefinition?.name !== definition?.name, ) ) { - return {isDynamicPartition: false, partitionDefinitionName: undefined}; + return {isDynamicPartition: false, dynamicPartitionsDefinitionName: undefined}; } return { isDynamicPartition: definition.type === PartitionDefinitionType.DYNAMIC, - partitionDefinitionName: definition.name, + dynamicPartitionsDefinitionName: definition.name, }; }, [data?.assetNodes]); @@ -323,7 +323,7 @@ const ConfigEditorPartitionPicker = React.memo((props: ConfigEditorPartitionPick { setShowCreatePartition(false); diff --git a/js_modules/dagster-ui/packages/ui-core/src/partitions/CreatePartitionDialog.tsx b/js_modules/dagster-ui/packages/ui-core/src/partitions/CreatePartitionDialog.tsx index 3ff5668b6f421..6645fd89ad8f0 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/partitions/CreatePartitionDialog.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/partitions/CreatePartitionDialog.tsx @@ -64,14 +64,14 @@ const INVALID_PARTITION_SUBSTRINGS_READABLE = [ export const CreatePartitionDialog = ({ isOpen, - partitionDefinitionName, + dynamicPartitionsDefinitionName, close, repoAddress, refetch, onCreated, }: { isOpen: boolean; - partitionDefinitionName?: string | null; + dynamicPartitionsDefinitionName?: string | null; close: () => void; repoAddress: RepoAddress; refetch?: () => Promise; @@ -117,7 +117,7 @@ export const CreatePartitionDialog = ({ const result = await createPartition({ variables: { repositorySelector: repoAddressToSelector(repoAddress), - partitionsDefName: partitionDefinitionName || '', + partitionsDefName: dynamicPartitionsDefinitionName || '', partitionKey: partitionName, }, @@ -175,10 +175,10 @@ export const CreatePartitionDialog = ({
Add a partition - {partitionDefinitionName ? ( + {dynamicPartitionsDefinitionName ? ( <> {' '} - for {partitionDefinitionName} + for {dynamicPartitionsDefinitionName} ) : ( '' diff --git a/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizard.tsx b/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizard.tsx index 0f11d997249fb..5b70794fb5427 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizard.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizard.tsx @@ -16,7 +16,7 @@ export const DimensionRangeWizard = ({ partitionKeys, health, dimensionType, - partitionDefinitionName, + dynamicPartitionsDefinitionName, repoAddress, refetch, }: { @@ -25,7 +25,7 @@ export const DimensionRangeWizard = ({ partitionKeys: string[]; health: PartitionStatusHealthSource; dimensionType: PartitionDefinitionType; - partitionDefinitionName?: string | null; + dynamicPartitionsDefinitionName?: string | null; repoAddress?: RepoAddress; refetch?: () => Promise; }) => { @@ -96,7 +96,7 @@ export const DimensionRangeWizard = ({ { setShowCreatePartition(false); diff --git a/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizards.tsx b/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizards.tsx index 21206d7230bcd..41438c7a29a3c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizards.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/partitions/DimensionRangeWizards.tsx @@ -65,8 +65,7 @@ export const DimensionRangeWizards = ({ selections.map((r) => (r.dimension === range.dimension ? {...r, selectedKeys} : r)), ) } - partitionDefinitionName={ - displayedPartitionDefinition?.name || + dynamicPartitionsDefinitionName={ displayedPartitionDefinition?.dimensionTypes.find( (d) => d.name === range.dimension.name, )?.dynamicPartitionsDefinitionName diff --git a/js_modules/dagster-ui/packages/ui-core/src/partitions/__tests__/CreatePartitionDialog.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/partitions/__tests__/CreatePartitionDialog.test.tsx index 5f0dfa98752e2..05a08199e4952 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/partitions/__tests__/CreatePartitionDialog.test.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/partitions/__tests__/CreatePartitionDialog.test.tsx @@ -30,7 +30,7 @@ function Test({mocks}: {mocks?: MockedResponse[]}) { name: 'testing', location: 'testing', }} - partitionDefinitionName="testPartitionDef" + dynamicPartitionsDefinitionName="testPartitionDef" onCreated={onCreatedMock} /> diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetRow.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetRow.tsx index 821e226f8156e..168042d12f4c3 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetRow.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetRow.tsx @@ -50,7 +50,7 @@ interface AssetRowProps { repoAddress: RepoAddress | null; height: number; start: number; - onWipe: (assets: AssetKeyInput[]) => void; + onRefresh: () => void; computeKindFilter?: StaticSetFilter; storageKindFilter?: StaticSetFilter; } @@ -65,7 +65,7 @@ export const VirtualizedAssetRow = (props: AssetRowProps) => { height, checked, onToggleChecked, - onWipe, + onRefresh, showCheckboxColumn = false, showRepoColumn, view = 'flat', @@ -220,7 +220,7 @@ export const VirtualizedAssetRow = (props: AssetRowProps) => { path={path} definition={definition} repoAddress={repoAddress} - onWipe={onWipe} + onRefresh={onRefresh} /> ) : null} diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetTable.tsx index 9e4ff039a9290..536198d988daa 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedAssetTable.tsx @@ -5,7 +5,7 @@ import {VirtualizedAssetCatalogHeader, VirtualizedAssetRow} from './VirtualizedA import {buildRepoAddress} from './buildRepoAddress'; import {AssetTableFragment} from '../assets/types/AssetTableFragment.types'; import {AssetViewType} from '../assets/useAssetView'; -import {AssetKeyInput, DefinitionTag} from '../graphql/types'; +import {DefinitionTag} from '../graphql/types'; import {StaticSetFilter} from '../ui/BaseFilters/useStaticSetFilter'; import {Container, Inner} from '../ui/VirtualizedTable'; @@ -19,7 +19,7 @@ interface Props { groups: {[displayKey: string]: AssetTableFragment[]}; checkedDisplayKeys: Set; onToggleFactory: (path: string) => (values: {checked: boolean; shiftKey: boolean}) => void; - onWipe: (assets: AssetKeyInput[]) => void; + onRefresh: () => void; showRepoColumn: boolean; view?: AssetViewType; computeKindFilter?: StaticSetFilter; @@ -33,7 +33,7 @@ export const VirtualizedAssetTable = (props: Props) => { groups, checkedDisplayKeys, onToggleFactory, - onWipe, + onRefresh, showRepoColumn, view = 'flat', computeKindFilter, @@ -84,8 +84,6 @@ export const VirtualizedAssetTable = (props: Props) => { return buildRepoAddress(repository.name, repository.location.name); }; - const wipeableAssets = row.type === 'folder' ? row.assets : [row.asset]; - return ( { start={start} checked={checkedDisplayKeys.has(row.displayKey)} onToggleChecked={onToggleFactory(row.displayKey)} - onWipe={() => onWipe(wipeableAssets.map((a) => a.key))} + onRefresh={onRefresh} computeKindFilter={computeKindFilter} storageKindFilter={storageKindFilter} /> diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx index cea14b32e8351..70d86e3c704f6 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx @@ -103,7 +103,7 @@ export const VirtualizedRepoAssetTable = ({repoAddress, assets}: Props) => { start={start} checked={false} onToggleChecked={() => {}} - onWipe={() => {}} + onRefresh={() => {}} /> ); })} diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedRepoAssetTable.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedRepoAssetTable.types.ts index a5cfbe3997037..e67ef9ab7afef 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedRepoAssetTable.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedRepoAssetTable.types.ts @@ -15,7 +15,15 @@ export type RepoAssetTableFragment = { hasMaterializePermission: boolean; description: string | null; assetKey: {__typename: 'AssetKey'; path: Array}; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string} >; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceAssetsRoot.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceAssetsRoot.types.ts index 363b99fe8efad..4c69eddec2704 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceAssetsRoot.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceAssetsRoot.types.ts @@ -36,7 +36,15 @@ export type WorkspaceAssetsQuery = { hasMaterializePermission: boolean; description: string | null; assetKey: {__typename: 'AssetKey'; path: Array}; - partitionDefinition: {__typename: 'PartitionDefinition'; description: string} | null; + partitionDefinition: { + __typename: 'PartitionDefinition'; + description: string; + dimensionTypes: Array<{ + __typename: 'DimensionDefinitionType'; + type: Types.PartitionDefinitionType; + dynamicPartitionsDefinitionName: string | null; + }>; + } | null; owners: Array< | {__typename: 'TeamAssetOwner'; team: string} | {__typename: 'UserAssetOwner'; email: string}