diff --git a/docs/content/api/modules.json.gz b/docs/content/api/modules.json.gz index f7cd2bb8e3951..4cd3a4eefdce4 100644 Binary files a/docs/content/api/modules.json.gz and b/docs/content/api/modules.json.gz differ diff --git a/docs/content/api/searchindex.json.gz b/docs/content/api/searchindex.json.gz index d1ac67478eabf..cfb62ce78b37d 100644 Binary files a/docs/content/api/searchindex.json.gz and b/docs/content/api/searchindex.json.gz differ diff --git a/docs/content/api/sections.json.gz b/docs/content/api/sections.json.gz index 342c36a274fd3..88bb67976f532 100644 Binary files a/docs/content/api/sections.json.gz and b/docs/content/api/sections.json.gz differ diff --git a/docs/content/integrations/sigma.mdx b/docs/content/integrations/sigma.mdx index c7e6d5e239ba0..1a0dd25a0d749 100644 --- a/docs/content/integrations/sigma.mdx +++ b/docs/content/integrations/sigma.mdx @@ -102,7 +102,7 @@ from dagster_sigma import ( DagsterSigmaTranslator, SigmaBaseUrl, SigmaOrganization, - SigmaWorkbook, + SigmaWorkbookTranslatorData, load_sigma_asset_specs, ) @@ -117,7 +117,7 @@ sigma_organization = SigmaOrganization( # A translator class lets us customize properties of the built Sigma assets, such as the owners or asset key class MyCustomSigmaTranslator(DagsterSigmaTranslator): - def get_asset_spec(self, data: SigmaWorkbook) -> dg.AssetSpec: + def get_asset_spec(self, data: SigmaWorkbookTranslatorData) -> dg.AssetSpec: # We create the default asset spec using super() default_spec = super().get_asset_spec(data) # we customize the team owner tag for all Sigma assets @@ -125,7 +125,7 @@ class MyCustomSigmaTranslator(DagsterSigmaTranslator): sigma_specs = load_sigma_asset_specs( - sigma_organization, dagster_sigma_translator=MyCustomSigmaTranslator + sigma_organization, dagster_sigma_translator=MyCustomSigmaTranslator() ) defs = dg.Definitions(assets=[*sigma_specs], resources={"sigma": sigma_organization}) ``` diff --git a/examples/docs_snippets/docs_snippets/integrations/sigma/customize-sigma-asset-defs.py b/examples/docs_snippets/docs_snippets/integrations/sigma/customize-sigma-asset-defs.py index c33fcdbce0df0..594fb3fc4cdd9 100644 --- a/examples/docs_snippets/docs_snippets/integrations/sigma/customize-sigma-asset-defs.py +++ b/examples/docs_snippets/docs_snippets/integrations/sigma/customize-sigma-asset-defs.py @@ -2,7 +2,7 @@ DagsterSigmaTranslator, SigmaBaseUrl, SigmaOrganization, - SigmaWorkbook, + SigmaWorkbookTranslatorData, load_sigma_asset_specs, ) @@ -17,14 +17,14 @@ # A translator class lets us customize properties of the built Sigma assets, such as the owners or asset key class MyCustomSigmaTranslator(DagsterSigmaTranslator): - def get_asset_spec(self, data: SigmaWorkbook) -> dg.AssetSpec: + def get_asset_spec(self, data: SigmaWorkbookTranslatorData) -> dg.AssetSpec: # We create the default asset spec using super() - default_spec = super().get_asset_spec(data) # type: ignore + default_spec = super().get_asset_spec(data) # we customize the team owner tag for all Sigma assets return default_spec.replace_attributes(owners=["team:my_team"]) sigma_specs = load_sigma_asset_specs( - sigma_organization, dagster_sigma_translator=MyCustomSigmaTranslator + sigma_organization, dagster_sigma_translator=MyCustomSigmaTranslator() ) defs = dg.Definitions(assets=[*sigma_specs], resources={"sigma": sigma_organization}) diff --git a/js_modules/dagster-ui/packages/ui-components/src/components/CursorControls.tsx b/js_modules/dagster-ui/packages/ui-components/src/components/CursorControls.tsx index 06e85da63b8be..1ae09c970a966 100644 --- a/js_modules/dagster-ui/packages/ui-components/src/components/CursorControls.tsx +++ b/js_modules/dagster-ui/packages/ui-components/src/components/CursorControls.tsx @@ -4,6 +4,7 @@ import {Button} from './Button'; import {Icon} from './Icon'; export interface CursorPaginationProps { + cursor?: string; hasPrevCursor: boolean; hasNextCursor: boolean; popCursor: () => void; diff --git a/js_modules/dagster-ui/packages/ui-core/client.json b/js_modules/dagster-ui/packages/ui-core/client.json index 8e9e8ae0c4b40..62a4e28337304 100644 --- a/js_modules/dagster-ui/packages/ui-core/client.json +++ b/js_modules/dagster-ui/packages/ui-core/client.json @@ -21,7 +21,8 @@ "RunStatusOnlyQuery": "e0000c8f2600dbe29f305febb04cca005e08da6a7ce03ec20476c59d607495c0", "AutomaterializeRunsQuery": "213e0a8e4d88de599b4740ba7d0d4bfac14defebcc8f3813eecc13696b9f17d9", "FullPartitionsQuery": "bfe939600c7396798b3c92b0e8335e639c9d76479c1cecaabc309a83c8f7ca4d", - "GetEvaluationsQuery": "22cc08d87eec75cfec1f054f4d222b5a2478c47fc1a6788024d5902e3b7db197", + "GetEvaluationsQuery": "ffbe804f3dcaaf3923223096d1cd5cdd39773945fe75045d131e0144bee5c3d9", + "GetSlimEvaluationsQuery": "040bf5d6f79ce3c06d75f4410e5386df795c546f10d3180e3ee7cb3ddac949e3", "GetEvaluationsSpecificPartitionQuery": "7c47ec6fee7ebf9edf6c5d57294f5a603e6a599884841eb211a1de9422c3791c", "PartitionSubsetListQuery": "9a560790b6c1828137f31532f5879cfb6611d9ca8c14b7f315464510b6a4bd75", "AutoMaterializeSensorFlag": "961162c030e7e3c35be91db37c1990ad31b53cb8225d216fece2bdc2a6210bce", @@ -35,8 +36,9 @@ "LaunchAssetCheckUpstreamQuery": "afb78499f0bf86942fc7f1ff7261c34caec2bd1e4aabb05c95a2db6acc811aaa", "OverduePopoverQuery": "3c8359e1adfab8237e4b26508489f07c09b24069373064c6c94d645312ae9296", "RunningBackfillsNoticeQuery": "edaaca1d6474672ae342eb3887f2aed16fbb502b704a603986d21f14bc10ee53", + "AssetCheckAutomationListQuery": "b6dffe3883c3c008672d9366595c876d95e0b1672f891695550ab513c93fa3a8", "AssetCheckDetailsQuery": "c448858a73cd66132c2d6f232497570d217ece793897c47aaa1155b15c7ef8e9", - "AssetChecksQuery": "2487c52958999bb33e5daa6c0caa0eea46ab200cb7d5d2294a8290e8e1ad3025", + "AssetChecksQuery": "67252db2bc1bfc878d1568008f7e0698a4515d15b4b68c8e88bd1edef4c1f60f", "AssetDaemonTicksQuery": "399ac77e660d40eba32c2ab06db2a2936a71e660d93ec108364eec1fdfc16788", "AssetColumnLineage": "bcb70460f77b88bbbfaec90982f3e99f522d9a4e270e63832684cfde169fabc7", "GetAutoMaterializePausedQuery": "50f74183f54031274136ab855701d01f26642a6d958d7452ae13aa6c40ca349d", diff --git a/js_modules/dagster-ui/packages/ui-core/jest/mocks/ComputeGraphData.worker.ts b/js_modules/dagster-ui/packages/ui-core/jest/mocks/ComputeGraphData.worker.ts index 53c25b23e987d..8c3da751177b3 100644 --- a/js_modules/dagster-ui/packages/ui-core/jest/mocks/ComputeGraphData.worker.ts +++ b/js_modules/dagster-ui/packages/ui-core/jest/mocks/ComputeGraphData.worker.ts @@ -22,8 +22,8 @@ export default class MockWorker { // mock expects data: { } instead of e: { data: { } } async postMessage(data: ComputeGraphDataWorkerMessageType) { if (data.type === 'computeGraphData') { - if (data.flagAssetSelectionSyntax) { - setFeatureFlagsInternal({flagAssetSelectionSyntax: true}); + if (data.flagSelectionSyntax) { + setFeatureFlagsInternal({flagSelectionSyntax: true}); } const state = await computeGraphData(data); this.onmessage.forEach((onmessage) => onmessage({data: {...state, id: data.id}})); diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/DefaultFeatureFlags.oss.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/DefaultFeatureFlags.oss.tsx index 4dab268bccfc8..f45ebf0d5819c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/app/DefaultFeatureFlags.oss.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/app/DefaultFeatureFlags.oss.tsx @@ -5,12 +5,9 @@ import {FeatureFlag} from 'shared/app/FeatureFlags.oss'; */ export const DEFAULT_FEATURE_FLAG_VALUES: Partial> = { [FeatureFlag.flagAssetSelectionWorker]: true, - [FeatureFlag.flagAssetSelectionSyntax]: new URLSearchParams(global?.location?.search ?? '').has( + [FeatureFlag.flagSelectionSyntax]: new URLSearchParams(global?.location?.search ?? '').has( 'new-asset-selection-syntax', ), - [FeatureFlag.flagRunSelectionSyntax]: new URLSearchParams(global?.location?.search ?? '').has( - 'new-run-selection-syntax', - ), // Flags for tests [FeatureFlag.__TestFlagDefaultTrue]: true, diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/FeatureFlags.oss.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/FeatureFlags.oss.tsx index bb481edf6ea67..cf47e9c647c92 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/app/FeatureFlags.oss.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/app/FeatureFlags.oss.tsx @@ -4,8 +4,7 @@ export enum FeatureFlag { flagSidebarResources = 'flagSidebarResources', flagDisableAutoLoadDefaults = 'flagDisableAutoLoadDefaults', flagLegacyRunsPage = 'flagLegacyRunsPage', - flagAssetSelectionSyntax = 'flagAssetSelectionSyntax', - flagRunSelectionSyntax = 'flagRunSelectionSyntax', + flagSelectionSyntax = 'flagSelectionSyntax', flagAssetSelectionWorker = 'flagAssetSelectionWorker', flagOpSelectionSyntax = 'flagOpSelectionSyntax', diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/Util.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/Util.tsx index f09f482ea1555..580a799d98a70 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/app/Util.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/app/Util.tsx @@ -1,9 +1,7 @@ import {cache} from 'idb-lru-cache'; import memoize from 'lodash/memoize'; import LRU from 'lru-cache'; -import {FeatureFlag} from 'shared/app/FeatureFlags.oss'; -import {featureEnabled} from './Flags'; import {timeByParts} from './timeByParts'; function twoDigit(v: number) { @@ -235,12 +233,6 @@ export function assertUnreachable(value: never): never { throw new Error(`Didn't expect to get here with value: ${JSON.stringify(value)}`); } -export function debugLog(...args: any[]) { - if (featureEnabled(FeatureFlag.flagDebugConsoleLogging)) { - console.log(...args); - } -} - export function colorHash(str: string) { let seed = 0; for (let i = 0; i < str.length; i++) { diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/apolloLinks.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/apolloLinks.tsx index 40ec49199dcc8..415bfcb620773 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/app/apolloLinks.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/app/apolloLinks.tsx @@ -1,4 +1,3 @@ -import {debugLog, formatElapsedTimeWithMsec} from './Util'; import {ApolloLink} from '../apollo-client'; const getCalls = (response: any) => { @@ -15,11 +14,6 @@ export const logLink = new ApolloLink((operation, forward) => const elapsedTime = performance.now() - context.start; const calls = getCalls(context.response); operation.setContext({elapsedTime, calls}); - debugLog(`${operation.operationName} took ${formatElapsedTimeWithMsec(elapsedTime)}`, { - operation, - data, - calls, - }); return data; }), ); diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/useVisibleFeatureFlagRows.oss.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/useVisibleFeatureFlagRows.oss.tsx index 2f5f6f09ad5a0..b42f648a08286 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/app/useVisibleFeatureFlagRows.oss.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/app/useVisibleFeatureFlagRows.oss.tsx @@ -16,10 +16,6 @@ export const useVisibleFeatureFlagRows = () => [ key: 'Disable automatically loading default config in launchpad', flagType: FeatureFlag.flagDisableAutoLoadDefaults, }, - { - key: 'Debug console logging', - flagType: FeatureFlag.flagDebugConsoleLogging, - }, { key: 'Revert to legacy Runs page', flagType: FeatureFlag.flagLegacyRunsPage, diff --git a/js_modules/dagster-ui/packages/ui-core/src/asset-graph/AssetGraphExplorer.tsx b/js_modules/dagster-ui/packages/ui-core/src/asset-graph/AssetGraphExplorer.tsx index 7faacf10d2e73..f578ed1623219 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/asset-graph/AssetGraphExplorer.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/asset-graph/AssetGraphExplorer.tsx @@ -754,7 +754,7 @@ const AssetGraphExplorerWithData = ({ )}
{filterButton}
- {featureEnabled(FeatureFlag.flagAssetSelectionSyntax) ? ( + {featureEnabled(FeatureFlag.flagSelectionSyntax) ? ( { const data: WorkerMessageData = event.data; - if (data.flagAssetSelectionSyntax) { - setFeatureFlags({[FeatureFlag.flagAssetSelectionSyntax]: true}); + if (data.flagSelectionSyntax) { + setFeatureFlags({[FeatureFlag.flagSelectionSyntax]: true}); } if (data.type === 'computeGraphData') { diff --git a/js_modules/dagster-ui/packages/ui-core/src/asset-graph/useAssetGraphData.tsx b/js_modules/dagster-ui/packages/ui-core/src/asset-graph/useAssetGraphData.tsx index 31bd11ae44999..81bda55273e03 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/asset-graph/useAssetGraphData.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/asset-graph/useAssetGraphData.tsx @@ -79,7 +79,7 @@ export function useFullAssetGraphData(options: AssetGraphFetchScope) { const requestId = ++currentRequestRef.current; buildGraphData({ nodes: queryItems, - flagAssetSelectionSyntax: featureEnabled(FeatureFlag.flagAssetSelectionSyntax), + flagSelectionSyntax: featureEnabled(FeatureFlag.flagSelectionSyntax), }) ?.then((data) => { if (lastProcessedRequestRef.current < requestId) { @@ -172,7 +172,7 @@ export function useAssetGraphData(opsQuery: string, options: AssetGraphFetchScop opsQuery, kinds, hideEdgesToNodesOutsideQuery, - flagAssetSelectionSyntax: featureEnabled(FeatureFlag.flagAssetSelectionSyntax), + flagSelectionSyntax: featureEnabled(FeatureFlag.flagSelectionSyntax), }) ?.then((data) => { if (lastProcessedRequestRef.current < requestId) { diff --git a/js_modules/dagster-ui/packages/ui-core/src/asset-selection/AntlrAssetSelection.ts b/js_modules/dagster-ui/packages/ui-core/src/asset-selection/AntlrAssetSelection.ts index 9a32b9ba87f68..18cf3ba18b104 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/asset-selection/AntlrAssetSelection.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/asset-selection/AntlrAssetSelection.ts @@ -68,7 +68,7 @@ export const parseAssetSelectionQuery = ( export const filterAssetSelectionByQuery = weakMapMemoize( (all_assets: AssetGraphQueryItem[], query: string): AssetSelectionQueryResult => { - if (featureEnabled(FeatureFlag.flagAssetSelectionSyntax)) { + if (featureEnabled(FeatureFlag.flagSelectionSyntax)) { const result = parseAssetSelectionQuery(all_assets, query); if (result instanceof Error) { // fall back to old behavior diff --git a/js_modules/dagster-ui/packages/ui-core/src/asset-selection/useAssetSelectionInput.tsx b/js_modules/dagster-ui/packages/ui-core/src/asset-selection/useAssetSelectionInput.tsx index bab62530c31cf..e9256d2d00f5d 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/asset-selection/useAssetSelectionInput.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/asset-selection/useAssetSelectionInput.tsx @@ -38,7 +38,7 @@ export const useAssetSelectionInput = < /> ); - if (featureEnabled(FeatureFlag.flagAssetSelectionSyntax)) { + if (featureEnabled(FeatureFlag.flagSelectionSyntax)) { filterInput = ( ; } - return ; + + return ; }; const renderChecksTab = () => { diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomaterializePolicyPage.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomaterializePolicyPage.tsx index 70b3570890991..dbb5f4833e14c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomaterializePolicyPage.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomaterializePolicyPage.tsx @@ -22,24 +22,10 @@ export const AssetAutomaterializePolicyPage = ({ assetKey: AssetKey; definition?: AssetViewDefinitionNodeFragment | null; }) => { - const {queryResult, paginationProps} = useEvaluationsQueryResult({assetKey}); + const {queryResult, evaluations, paginationProps} = useEvaluationsQueryResult({assetKey}); useQueryRefreshAtInterval(queryResult, FIFTEEN_SECONDS); - const evaluations = useMemo(() => { - if ( - queryResult.data?.assetConditionEvaluationRecordsOrError?.__typename === - 'AssetConditionEvaluationRecords' && - queryResult.data?.assetNodeOrError?.__typename === 'AssetNode' - ) { - return queryResult.data?.assetConditionEvaluationRecordsOrError.records; - } - return []; - }, [ - queryResult.data?.assetConditionEvaluationRecordsOrError, - queryResult.data?.assetNodeOrError, - ]); - const isFirstPage = !paginationProps.hasPrevCursor; const [selectedEvaluationId, setSelectedEvaluationId] = useQueryPersistedState< diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomationRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomationRoot.tsx new file mode 100644 index 0000000000000..4052d069a05b8 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/AssetAutomationRoot.tsx @@ -0,0 +1,72 @@ +import { + Box, + CursorHistoryControls, + NonIdealState, + SpinnerWithText, +} from '@dagster-io/ui-components'; +import {useEffect, useRef} from 'react'; + +import {useEvaluationsQueryResult} from './useEvaluationsQueryResult'; +import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../../app/QueryRefresh'; +import {AssetKey} from '../types'; +import {EvaluationList} from './EvaluationList'; +import {AssetViewDefinitionNodeFragment} from '../types/AssetView.types'; + +interface Props { + assetKey: AssetKey; + definition: AssetViewDefinitionNodeFragment | null; +} + +export const AssetAutomationRoot = ({assetKey, definition}: Props) => { + const {queryResult, evaluations, paginationProps} = useEvaluationsQueryResult({assetKey}); + const {data, loading} = queryResult; + + const scrollRef = useRef(null); + + // When paginating, reset scroll to top. + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTo({top: 0, behavior: 'instant'}); + } + }, [paginationProps.cursor]); + + useQueryRefreshAtInterval(queryResult, FIFTEEN_SECONDS); + + if (loading && !data) { + return ( + + + + ); + } + + if (!definition) { + return ( + + + + ); + } + + return ( + <> + + + +
+ +
+ + ); +}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationDetailDialog.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationDetailDialog.tsx index b5008856ce22a..a2f9651e00bb0 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationDetailDialog.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationDetailDialog.tsx @@ -10,10 +10,13 @@ import { } from '@dagster-io/ui-components'; import {ReactNode, useState} from 'react'; -import {GET_EVALUATIONS_QUERY} from './GetEvaluationsQuery'; +import {GET_SLIM_EVALUATIONS_QUERY} from './GetEvaluationsQuery'; import {PartitionTagSelector} from './PartitionTagSelector'; import {QueryfulEvaluationDetailTable} from './QueryfulEvaluationDetailTable'; -import {GetEvaluationsQuery, GetEvaluationsQueryVariables} from './types/GetEvaluationsQuery.types'; +import { + GetSlimEvaluationsQuery, + GetSlimEvaluationsQueryVariables, +} from './types/GetEvaluationsQuery.types'; import {usePartitionsForAssetKey} from './usePartitionsForAssetKey'; import {useQuery} from '../../apollo-client'; import {DEFAULT_TIME_FORMAT} from '../../app/time/TimestampFormat'; @@ -21,18 +24,26 @@ import {TimestampDisplay} from '../../schedules/TimestampDisplay'; interface Props { isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; + onClose: () => void; assetKeyPath: string[]; + assetCheckName?: string; evaluationID: string; } -export const EvaluationDetailDialog = ({isOpen, setIsOpen, evaluationID, assetKeyPath}: Props) => { +export const EvaluationDetailDialog = ({ + isOpen, + onClose, + evaluationID, + assetKeyPath, + assetCheckName, +}: Props) => { return ( - setIsOpen(false)} style={EvaluationDetailDialogStyle}> + ); @@ -41,17 +52,26 @@ export const EvaluationDetailDialog = ({isOpen, setIsOpen, evaluationID, assetKe interface ContentProps { evaluationID: string; assetKeyPath: string[]; - setIsOpen: (isOpen: boolean) => void; + assetCheckName?: string; + onClose: () => void; } -const EvaluationDetailDialogContents = ({evaluationID, assetKeyPath, setIsOpen}: ContentProps) => { +const EvaluationDetailDialogContents = ({ + evaluationID, + assetKeyPath, + assetCheckName, + onClose, +}: ContentProps) => { const [selectedPartition, setSelectedPartition] = useState(null); - const {data, loading} = useQuery( - GET_EVALUATIONS_QUERY, + const {data, loading} = useQuery( + GET_SLIM_EVALUATIONS_QUERY, { variables: { - assetKey: {path: assetKeyPath}, + assetKey: assetCheckName ? null : {path: assetKeyPath}, + assetCheckKey: assetCheckName + ? {assetKey: {path: assetKeyPath}, name: assetCheckName} + : null, cursor: `${BigInt(evaluationID) + 1n}`, limit: 2, }, @@ -70,7 +90,7 @@ const EvaluationDetailDialogContents = ({evaluationID, assetKeyPath, setIsOpen}: } - onDone={() => setIsOpen(false)} + onDone={onClose} /> ); } @@ -90,12 +110,12 @@ const EvaluationDetailDialogContents = ({evaluationID, assetKeyPath, setIsOpen}: /> } - onDone={() => setIsOpen(false)} + onDone={onClose} /> ); } - const evaluation = record?.records[0]; + const evaluation = record?.records.find((r) => r.evaluationId === evaluationID); if (!evaluation) { return ( @@ -114,7 +134,7 @@ const EvaluationDetailDialogContents = ({evaluationID, assetKeyPath, setIsOpen}: /> } - onDone={() => setIsOpen(false)} + onDone={onClose} /> ); } @@ -135,7 +155,7 @@ const EvaluationDetailDialogContents = ({evaluationID, assetKeyPath, setIsOpen}: } /> - {allPartitions.length > 0 ? ( + {allPartitions.length > 0 && evaluation.isLegacy ? ( } - onDone={() => setIsOpen(false)} + onDone={onClose} /> ); }; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationList.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationList.tsx index 25daf6c8e841f..e27e6ed34e199 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationList.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationList.tsx @@ -1,4 +1,4 @@ -import {Table} from '@dagster-io/ui-components'; +import {Colors, Table} from '@dagster-io/ui-components'; import {AssetKey} from '../types'; import {EvaluationListRow} from './EvaluationListRow'; @@ -7,13 +7,22 @@ import {AssetConditionEvaluationRecordFragment} from './types/GetEvaluationsQuer interface Props { assetKey: AssetKey; isPartitioned: boolean; + assetCheckName?: string; evaluations: AssetConditionEvaluationRecordFragment[]; } -export const EvaluationList = ({assetKey, isPartitioned, evaluations}: Props) => { +export const EvaluationList = ({assetKey, isPartitioned, assetCheckName, evaluations}: Props) => { return ( - + @@ -27,6 +36,7 @@ export const EvaluationList = ({assetKey, isPartitioned, evaluations}: Props) => key={evaluation.id} evaluation={evaluation} assetKey={assetKey} + assetCheckName={assetCheckName} isPartitioned={isPartitioned} /> ); diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationListRow.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationListRow.tsx index c22f39cde199d..e918a49b255d9 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationListRow.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationListRow.tsx @@ -9,6 +9,7 @@ import { Mono, } from '@dagster-io/ui-components'; import {useState} from 'react'; +import {Link} from 'react-router-dom'; import {AssetKey} from '../types'; import {EvaluationDetailDialog} from './EvaluationDetailDialog'; @@ -20,11 +21,12 @@ import {TimestampDisplay} from '../../schedules/TimestampDisplay'; interface Props { assetKey: AssetKey; + assetCheckName?: string; isPartitioned: boolean; evaluation: AssetConditionEvaluationRecordFragment; } -export const EvaluationListRow = ({evaluation, assetKey, isPartitioned}: Props) => { +export const EvaluationListRow = ({evaluation, assetKey, assetCheckName, isPartitioned}: Props) => { const [isOpen, setIsOpen] = useState(false); return ( @@ -52,9 +54,10 @@ export const EvaluationListRow = ({evaluation, assetKey, isPartitioned}: Props) setIsOpen(false)} + evaluationID={evaluation.evaluationId} assetKeyPath={assetKey.path} + assetCheckName={assetCheckName} /> ); @@ -67,16 +70,28 @@ interface EvaluationRunInfoProps { const EvaluationRunInfo = ({runIds, timestamp}: EvaluationRunInfoProps) => { const [isOpen, setIsOpen] = useState(false); + const firstRun = runIds[0]; - if (runIds.length === 0) { + if (!firstRun) { return None; } if (runIds.length === 1) { + const truncated = firstRun.slice(0, 8); + + // This looks like a backfill ID. Link there. + if (truncated === firstRun) { + return ( + + {firstRun} + + ); + } + return ( - - {runIds[0]} - + + {truncated} + ); } diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationStatusTag.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationStatusTag.tsx index 2e2b21a794ff0..21b4f43e3d34b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationStatusTag.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/EvaluationStatusTag.tsx @@ -44,7 +44,7 @@ export const EvaluationStatusTag = ({ hoverCloseDelay={50} content={ }; + assetKey: {__typename: 'AssetKey'; path: Array} | null; evaluation: { __typename: 'AssetConditionEvaluation'; rootUniqueId: string; @@ -731,6 +731,7 @@ export type AssetConditionEvaluationRecordFragment = { export type GetEvaluationsQueryVariables = Types.Exact<{ assetKey: Types.AssetKeyInput; + assetCheckKey?: Types.InputMaybe; limit: Types.Scalars['Int']['input']; cursor?: Types.InputMaybe; }>; @@ -765,7 +766,434 @@ export type GetEvaluationsQuery = { startTimestamp: number | null; endTimestamp: number | null; isLegacy: boolean; - assetKey: {__typename: 'AssetKey'; path: Array}; + assetKey: {__typename: 'AssetKey'; path: Array} | null; + evaluation: { + __typename: 'AssetConditionEvaluation'; + rootUniqueId: string; + evaluationNodes: Array< + | { + __typename: 'PartitionedAssetConditionEvaluationNode'; + description: string; + startTimestamp: number | null; + endTimestamp: number | null; + numTrue: number; + uniqueId: string; + childUniqueIds: Array; + numCandidates: number | null; + } + | { + __typename: 'SpecificPartitionAssetConditionEvaluationNode'; + description: string; + status: Types.AssetConditionEvaluationStatus; + uniqueId: string; + childUniqueIds: Array; + metadataEntries: Array< + | { + __typename: 'AssetMetadataEntry'; + label: string; + description: string | null; + assetKey: {__typename: 'AssetKey'; path: Array}; + } + | { + __typename: 'BoolMetadataEntry'; + boolValue: boolean | null; + label: string; + description: string | null; + } + | { + __typename: 'CodeReferencesMetadataEntry'; + label: string; + description: string | null; + codeReferences: Array< + | { + __typename: 'LocalFileCodeReference'; + filePath: string; + lineNumber: number | null; + label: string | null; + } + | {__typename: 'UrlCodeReference'; url: string; label: string | null} + >; + } + | { + __typename: 'FloatMetadataEntry'; + floatValue: number | null; + label: string; + description: string | null; + } + | { + __typename: 'IntMetadataEntry'; + intValue: number | null; + intRepr: string; + label: string; + description: string | null; + } + | { + __typename: 'JobMetadataEntry'; + jobName: string; + repositoryName: string | null; + locationName: string; + label: string; + description: string | null; + } + | { + __typename: 'JsonMetadataEntry'; + jsonString: string; + label: string; + description: string | null; + } + | { + __typename: 'MarkdownMetadataEntry'; + mdStr: string; + label: string; + description: string | null; + } + | { + __typename: 'NotebookMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | {__typename: 'NullMetadataEntry'; label: string; description: string | null} + | { + __typename: 'PathMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | { + __typename: 'PipelineRunMetadataEntry'; + runId: string; + label: string; + description: string | null; + } + | { + __typename: 'PythonArtifactMetadataEntry'; + module: string; + name: string; + label: string; + description: string | null; + } + | { + __typename: 'TableColumnLineageMetadataEntry'; + label: string; + description: string | null; + lineage: Array<{ + __typename: 'TableColumnLineageEntry'; + columnName: string; + columnDeps: Array<{ + __typename: 'TableColumnDep'; + columnName: string; + assetKey: {__typename: 'AssetKey'; path: Array}; + }>; + }>; + } + | { + __typename: 'TableMetadataEntry'; + label: string; + description: string | null; + table: { + __typename: 'Table'; + records: Array; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{ + __typename: 'DefinitionTag'; + key: string; + value: string; + }>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + }; + } + | { + __typename: 'TableSchemaMetadataEntry'; + label: string; + description: string | null; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{__typename: 'DefinitionTag'; key: string; value: string}>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + } + | { + __typename: 'TextMetadataEntry'; + text: string; + label: string; + description: string | null; + } + | { + __typename: 'TimestampMetadataEntry'; + timestamp: number; + label: string; + description: string | null; + } + | { + __typename: 'UrlMetadataEntry'; + url: string; + label: string; + description: string | null; + } + >; + } + | { + __typename: 'UnpartitionedAssetConditionEvaluationNode'; + description: string; + startTimestamp: number | null; + endTimestamp: number | null; + status: Types.AssetConditionEvaluationStatus; + uniqueId: string; + childUniqueIds: Array; + metadataEntries: Array< + | { + __typename: 'AssetMetadataEntry'; + label: string; + description: string | null; + assetKey: {__typename: 'AssetKey'; path: Array}; + } + | { + __typename: 'BoolMetadataEntry'; + boolValue: boolean | null; + label: string; + description: string | null; + } + | { + __typename: 'CodeReferencesMetadataEntry'; + label: string; + description: string | null; + codeReferences: Array< + | { + __typename: 'LocalFileCodeReference'; + filePath: string; + lineNumber: number | null; + label: string | null; + } + | {__typename: 'UrlCodeReference'; url: string; label: string | null} + >; + } + | { + __typename: 'FloatMetadataEntry'; + floatValue: number | null; + label: string; + description: string | null; + } + | { + __typename: 'IntMetadataEntry'; + intValue: number | null; + intRepr: string; + label: string; + description: string | null; + } + | { + __typename: 'JobMetadataEntry'; + jobName: string; + repositoryName: string | null; + locationName: string; + label: string; + description: string | null; + } + | { + __typename: 'JsonMetadataEntry'; + jsonString: string; + label: string; + description: string | null; + } + | { + __typename: 'MarkdownMetadataEntry'; + mdStr: string; + label: string; + description: string | null; + } + | { + __typename: 'NotebookMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | {__typename: 'NullMetadataEntry'; label: string; description: string | null} + | { + __typename: 'PathMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | { + __typename: 'PipelineRunMetadataEntry'; + runId: string; + label: string; + description: string | null; + } + | { + __typename: 'PythonArtifactMetadataEntry'; + module: string; + name: string; + label: string; + description: string | null; + } + | { + __typename: 'TableColumnLineageMetadataEntry'; + label: string; + description: string | null; + lineage: Array<{ + __typename: 'TableColumnLineageEntry'; + columnName: string; + columnDeps: Array<{ + __typename: 'TableColumnDep'; + columnName: string; + assetKey: {__typename: 'AssetKey'; path: Array}; + }>; + }>; + } + | { + __typename: 'TableMetadataEntry'; + label: string; + description: string | null; + table: { + __typename: 'Table'; + records: Array; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{ + __typename: 'DefinitionTag'; + key: string; + value: string; + }>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + }; + } + | { + __typename: 'TableSchemaMetadataEntry'; + label: string; + description: string | null; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{__typename: 'DefinitionTag'; key: string; value: string}>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + } + | { + __typename: 'TextMetadataEntry'; + text: string; + label: string; + description: string | null; + } + | { + __typename: 'TimestampMetadataEntry'; + timestamp: number; + label: string; + description: string | null; + } + | { + __typename: 'UrlMetadataEntry'; + url: string; + label: string; + description: string | null; + } + >; + } + >; + }; + evaluationNodes: Array<{ + __typename: 'AutomationConditionEvaluationNode'; + uniqueId: string; + expandedLabel: Array; + userLabel: string | null; + startTimestamp: number | null; + endTimestamp: number | null; + numCandidates: number | null; + numTrue: number; + isPartitioned: boolean; + childUniqueIds: Array; + }>; + }>; + } + | {__typename: 'AutoMaterializeAssetEvaluationNeedsMigrationError'; message: string} + | null; +}; + +export type GetSlimEvaluationsQueryVariables = Types.Exact<{ + assetKey?: Types.InputMaybe; + assetCheckKey?: Types.InputMaybe; + limit: Types.Scalars['Int']['input']; + cursor?: Types.InputMaybe; +}>; + +export type GetSlimEvaluationsQuery = { + __typename: 'Query'; + assetConditionEvaluationRecordsOrError: + | { + __typename: 'AssetConditionEvaluationRecords'; + records: Array<{ + __typename: 'AssetConditionEvaluationRecord'; + id: string; + evaluationId: string; + numRequested: number; + runIds: Array; + timestamp: number; + startTimestamp: number | null; + endTimestamp: number | null; + isLegacy: boolean; + assetKey: {__typename: 'AssetKey'; path: Array} | null; evaluation: { __typename: 'AssetConditionEvaluation'; rootUniqueId: string; @@ -1546,6 +1974,8 @@ export type GetEvaluationsSpecificPartitionQuery = { } | null; }; -export const GetEvaluationsQueryVersion = '22cc08d87eec75cfec1f054f4d222b5a2478c47fc1a6788024d5902e3b7db197'; +export const GetEvaluationsQueryVersion = 'ffbe804f3dcaaf3923223096d1cd5cdd39773945fe75045d131e0144bee5c3d9'; + +export const GetSlimEvaluationsQueryVersion = '040bf5d6f79ce3c06d75f4410e5386df795c546f10d3180e3ee7cb3ddac949e3'; export const GetEvaluationsSpecificPartitionQueryVersion = '7c47ec6fee7ebf9edf6c5d57294f5a603e6a599884841eb211a1de9422c3791c'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/useEvaluationsQueryResult.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/useEvaluationsQueryResult.tsx index c99e35f78995a..acdccd1de1406 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/useEvaluationsQueryResult.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AutoMaterializePolicyPage/useEvaluationsQueryResult.tsx @@ -1,3 +1,5 @@ +import {useMemo} from 'react'; + import {GET_EVALUATIONS_QUERY} from './GetEvaluationsQuery'; import {GetEvaluationsQuery, GetEvaluationsQueryVariables} from './types/GetEvaluationsQuery.types'; import {useCursorPaginatedQuery} from '../../runs/useCursorPaginatedQuery'; @@ -7,7 +9,10 @@ export const PAGE_SIZE = 30; // This function exists mostly to use the return type later export function useEvaluationsQueryResult({assetKey}: {assetKey: AssetKey}) { - const result = useCursorPaginatedQuery({ + const {queryResult, paginationProps} = useCursorPaginatedQuery< + GetEvaluationsQuery, + GetEvaluationsQueryVariables + >({ nextCursorForResult: (data) => { if ( data.assetConditionEvaluationRecordsOrError?.__typename === @@ -32,5 +37,18 @@ export function useEvaluationsQueryResult({assetKey}: {assetKey: AssetKey}) { query: GET_EVALUATIONS_QUERY, pageSize: PAGE_SIZE, }); - return result; + + const {data} = queryResult; + const evaluations = useMemo(() => { + if ( + data?.assetConditionEvaluationRecordsOrError?.__typename === + 'AssetConditionEvaluationRecords' && + data?.assetNodeOrError?.__typename === 'AssetNode' + ) { + return data.assetConditionEvaluationRecordsOrError.records; + } + return []; + }, [data]); + + return {evaluations, queryResult, paginationProps}; } diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetCheckAutomationList.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetCheckAutomationList.tsx new file mode 100644 index 0000000000000..4315bd6464114 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetCheckAutomationList.tsx @@ -0,0 +1,121 @@ +import {Box, CursorHistoryControls} from '@dagster-io/ui-components'; +import {useMemo} from 'react'; + +import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../../app/QueryRefresh'; +import {EvaluationList} from '../AutoMaterializePolicyPage/EvaluationList'; +import {AssetKey} from '../types'; +import { + AssetCheckAutomationListQuery, + AssetCheckAutomationListQueryVariables, +} from './types/AssetCheckAutomationList.types'; +import {gql} from '../../apollo-client'; +import {useCursorPaginatedQuery} from '../../runs/useCursorPaginatedQuery'; +import {ASSET_CONDITION_EVALUATION_RECORD_FRAGMENT} from '../AutoMaterializePolicyPage/GetEvaluationsQuery'; +import {AssetCheckKeyFragment} from './types/AssetChecksQuery.types'; + +interface Props { + assetCheck: AssetCheckKeyFragment; + checkName: string; +} + +export const AssetCheckAutomationList = ({assetCheck, checkName}: Props) => { + const {queryResult, evaluations, paginationProps} = useAssetCheckEvaluationsQueryResult({ + assetKey: assetCheck.assetKey, + checkName, + }); + + useQueryRefreshAtInterval(queryResult, FIFTEEN_SECONDS); + + return ( + <> + + + + + + ); +}; + +export const PAGE_SIZE = 30; + +export function useAssetCheckEvaluationsQueryResult({ + assetKey, + checkName, +}: { + assetKey: AssetKey; + checkName: string; +}) { + const {queryResult, paginationProps} = useCursorPaginatedQuery< + AssetCheckAutomationListQuery, + AssetCheckAutomationListQueryVariables + >({ + nextCursorForResult: (data) => { + if ( + data.assetConditionEvaluationRecordsOrError?.__typename === + 'AssetConditionEvaluationRecords' + ) { + return data.assetConditionEvaluationRecordsOrError.records[PAGE_SIZE - 1]?.evaluationId; + } + return undefined; + }, + getResultArray: (data) => { + if ( + data?.assetConditionEvaluationRecordsOrError?.__typename === + 'AssetConditionEvaluationRecords' + ) { + return data.assetConditionEvaluationRecordsOrError.records; + } + return []; + }, + variables: { + assetCheckKey: { + assetKey: {path: assetKey.path}, + name: checkName, + }, + }, + query: ASSET_CHECK_AUTOMATION_LIST_QUERY, + pageSize: PAGE_SIZE, + }); + + const {data} = queryResult; + const evaluations = useMemo(() => { + if ( + data?.assetConditionEvaluationRecordsOrError?.__typename === 'AssetConditionEvaluationRecords' + ) { + return data.assetConditionEvaluationRecordsOrError.records; + } + return []; + }, [data]); + + return {queryResult, evaluations, paginationProps}; +} + +const ASSET_CHECK_AUTOMATION_LIST_QUERY = gql` + query AssetCheckAutomationListQuery( + $assetCheckKey: AssetCheckHandleInput! + $limit: Int! + $cursor: String + ) { + assetConditionEvaluationRecordsOrError( + assetKey: null + assetCheckKey: $assetCheckKey + limit: $limit + cursor: $cursor + ) { + ... on AssetConditionEvaluationRecords { + records { + ...AssetConditionEvaluationRecordFragment + } + } + } + } + ${ASSET_CONDITION_EVALUATION_RECORD_FRAGMENT} +`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecks.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecks.tsx index b4ac9bcd1ee8f..2b9e6b661729b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecks.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecks.tsx @@ -15,6 +15,7 @@ import {useVirtualizer} from '@tanstack/react-virtual'; import React, {useMemo, useState} from 'react'; import styled from 'styled-components'; +import {AssetCheckAutomationList} from './AssetCheckAutomationList'; import { ASSET_CHECK_DETAILS_QUERY, AgentUpgradeRequired, @@ -52,6 +53,7 @@ export const AssetChecks = ({ const queryResult = useQuery(ASSET_CHECKS_QUERY, { variables: {assetKey}, }); + const {data} = queryResult; useQueryRefreshAtInterval(queryResult, FIFTEEN_SECONDS); @@ -101,6 +103,8 @@ export const AssetChecks = ({ return checks.find((check) => check.name === selectedCheckName) ?? checks[0]; }, [selectedCheckName, checks]); + const isSelectedCheckAutomated = !!selectedCheck?.automationCondition; + const {paginationProps, executions, executionsLoading} = useHistoricalCheckExecutions( selectedCheck ? {assetKey, checkName: selectedCheck.name} : null, ); @@ -189,6 +193,7 @@ export const AssetChecks = ({ $selected={selectedCheck === check} onClick={() => { setSelectedCheckName(check.name); + setActiveTab('overview'); }} > @@ -237,6 +242,7 @@ export const AssetChecks = ({ { setActiveTab(tab); }} @@ -257,6 +263,9 @@ export const AssetChecks = ({ paginationProps={paginationProps} /> ) : null} + {activeTab === 'automation-history' ? ( + + ) : null} diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksQuery.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksQuery.tsx index 9331e729d1cf3..1be396c818b8c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksQuery.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksQuery.tsx @@ -5,6 +5,15 @@ import { import {ASSET_CHECK_TABLE_FRAGMENT} from './VirtualizedAssetCheckTable'; import {gql} from '../../apollo-client'; +const ASSET_CHECK_KEY_FRAGMENT = gql` + fragment AssetCheckKeyFragment on AssetCheck { + name + assetKey { + path + } + } +`; + export const ASSET_CHECKS_QUERY = gql` query AssetChecksQuery($assetKey: AssetKeyInput!) { assetNodeOrError(assetKey: $assetKey) { @@ -18,6 +27,7 @@ export const ASSET_CHECKS_QUERY = gql` } ... on AssetChecks { checks { + ...AssetCheckKeyFragment ...ExecuteChecksButtonCheckFragment ...AssetCheckTableFragment } @@ -26,7 +36,9 @@ export const ASSET_CHECKS_QUERY = gql` } } } + ${EXECUTE_CHECKS_BUTTON_ASSET_NODE_FRAGMENT} ${EXECUTE_CHECKS_BUTTON_CHECK_FRAGMENT} ${ASSET_CHECK_TABLE_FRAGMENT} + ${ASSET_CHECK_KEY_FRAGMENT} `; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksTabs.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksTabs.tsx index bd5380d6d1933..d7264c45d3cdd 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksTabs.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksTabs.tsx @@ -1,17 +1,31 @@ -import {Tab, Tabs} from '@dagster-io/ui-components'; +import {Tab, Tabs, Tooltip} from '@dagster-io/ui-components'; -export type AssetChecksTabType = 'overview' | 'execution-history'; +export type AssetChecksTabType = 'overview' | 'execution-history' | 'automation-history'; interface Props { activeTab: AssetChecksTabType; + enableAutomationHistory: boolean; onChange: (tabId: AssetChecksTabType) => void; } -export const AssetChecksTabs = ({activeTab, onChange}: Props) => { +export const AssetChecksTabs = ({activeTab, enableAutomationHistory, onChange}: Props) => { return ( + + Automation history + + } + disabled={!enableAutomationHistory} + /> ); }; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/VirtualizedAssetCheckTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/VirtualizedAssetCheckTable.tsx index a08f2801d8b89..593dde9e4284b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/VirtualizedAssetCheckTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/VirtualizedAssetCheckTable.tsx @@ -151,6 +151,10 @@ export const ASSET_CHECK_TABLE_FRAGMENT = gql` name description canExecuteIndividually + automationCondition { + label + expandedLabel + } ...ExecuteChecksButtonCheckFragment executionForLatestMaterialization { ...AssetCheckExecutionFragment diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetCheckAutomationList.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetCheckAutomationList.types.ts new file mode 100644 index 0000000000000..9c97126f8d517 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetCheckAutomationList.types.ts @@ -0,0 +1,431 @@ +// Generated GraphQL types, do not edit manually. + +import * as Types from '../../../graphql/types'; + +export type AssetCheckAutomationListQueryVariables = Types.Exact<{ + assetCheckKey: Types.AssetCheckHandleInput; + limit: Types.Scalars['Int']['input']; + cursor?: Types.InputMaybe; +}>; + +export type AssetCheckAutomationListQuery = { + __typename: 'Query'; + assetConditionEvaluationRecordsOrError: + | { + __typename: 'AssetConditionEvaluationRecords'; + records: Array<{ + __typename: 'AssetConditionEvaluationRecord'; + id: string; + evaluationId: string; + numRequested: number; + runIds: Array; + timestamp: number; + startTimestamp: number | null; + endTimestamp: number | null; + isLegacy: boolean; + assetKey: {__typename: 'AssetKey'; path: Array} | null; + evaluation: { + __typename: 'AssetConditionEvaluation'; + rootUniqueId: string; + evaluationNodes: Array< + | { + __typename: 'PartitionedAssetConditionEvaluationNode'; + description: string; + startTimestamp: number | null; + endTimestamp: number | null; + numTrue: number; + uniqueId: string; + childUniqueIds: Array; + numCandidates: number | null; + } + | { + __typename: 'SpecificPartitionAssetConditionEvaluationNode'; + description: string; + status: Types.AssetConditionEvaluationStatus; + uniqueId: string; + childUniqueIds: Array; + metadataEntries: Array< + | { + __typename: 'AssetMetadataEntry'; + label: string; + description: string | null; + assetKey: {__typename: 'AssetKey'; path: Array}; + } + | { + __typename: 'BoolMetadataEntry'; + boolValue: boolean | null; + label: string; + description: string | null; + } + | { + __typename: 'CodeReferencesMetadataEntry'; + label: string; + description: string | null; + codeReferences: Array< + | { + __typename: 'LocalFileCodeReference'; + filePath: string; + lineNumber: number | null; + label: string | null; + } + | {__typename: 'UrlCodeReference'; url: string; label: string | null} + >; + } + | { + __typename: 'FloatMetadataEntry'; + floatValue: number | null; + label: string; + description: string | null; + } + | { + __typename: 'IntMetadataEntry'; + intValue: number | null; + intRepr: string; + label: string; + description: string | null; + } + | { + __typename: 'JobMetadataEntry'; + jobName: string; + repositoryName: string | null; + locationName: string; + label: string; + description: string | null; + } + | { + __typename: 'JsonMetadataEntry'; + jsonString: string; + label: string; + description: string | null; + } + | { + __typename: 'MarkdownMetadataEntry'; + mdStr: string; + label: string; + description: string | null; + } + | { + __typename: 'NotebookMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | {__typename: 'NullMetadataEntry'; label: string; description: string | null} + | { + __typename: 'PathMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | { + __typename: 'PipelineRunMetadataEntry'; + runId: string; + label: string; + description: string | null; + } + | { + __typename: 'PythonArtifactMetadataEntry'; + module: string; + name: string; + label: string; + description: string | null; + } + | { + __typename: 'TableColumnLineageMetadataEntry'; + label: string; + description: string | null; + lineage: Array<{ + __typename: 'TableColumnLineageEntry'; + columnName: string; + columnDeps: Array<{ + __typename: 'TableColumnDep'; + columnName: string; + assetKey: {__typename: 'AssetKey'; path: Array}; + }>; + }>; + } + | { + __typename: 'TableMetadataEntry'; + label: string; + description: string | null; + table: { + __typename: 'Table'; + records: Array; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{ + __typename: 'DefinitionTag'; + key: string; + value: string; + }>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + }; + } + | { + __typename: 'TableSchemaMetadataEntry'; + label: string; + description: string | null; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{__typename: 'DefinitionTag'; key: string; value: string}>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + } + | { + __typename: 'TextMetadataEntry'; + text: string; + label: string; + description: string | null; + } + | { + __typename: 'TimestampMetadataEntry'; + timestamp: number; + label: string; + description: string | null; + } + | { + __typename: 'UrlMetadataEntry'; + url: string; + label: string; + description: string | null; + } + >; + } + | { + __typename: 'UnpartitionedAssetConditionEvaluationNode'; + description: string; + startTimestamp: number | null; + endTimestamp: number | null; + status: Types.AssetConditionEvaluationStatus; + uniqueId: string; + childUniqueIds: Array; + metadataEntries: Array< + | { + __typename: 'AssetMetadataEntry'; + label: string; + description: string | null; + assetKey: {__typename: 'AssetKey'; path: Array}; + } + | { + __typename: 'BoolMetadataEntry'; + boolValue: boolean | null; + label: string; + description: string | null; + } + | { + __typename: 'CodeReferencesMetadataEntry'; + label: string; + description: string | null; + codeReferences: Array< + | { + __typename: 'LocalFileCodeReference'; + filePath: string; + lineNumber: number | null; + label: string | null; + } + | {__typename: 'UrlCodeReference'; url: string; label: string | null} + >; + } + | { + __typename: 'FloatMetadataEntry'; + floatValue: number | null; + label: string; + description: string | null; + } + | { + __typename: 'IntMetadataEntry'; + intValue: number | null; + intRepr: string; + label: string; + description: string | null; + } + | { + __typename: 'JobMetadataEntry'; + jobName: string; + repositoryName: string | null; + locationName: string; + label: string; + description: string | null; + } + | { + __typename: 'JsonMetadataEntry'; + jsonString: string; + label: string; + description: string | null; + } + | { + __typename: 'MarkdownMetadataEntry'; + mdStr: string; + label: string; + description: string | null; + } + | { + __typename: 'NotebookMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | {__typename: 'NullMetadataEntry'; label: string; description: string | null} + | { + __typename: 'PathMetadataEntry'; + path: string; + label: string; + description: string | null; + } + | { + __typename: 'PipelineRunMetadataEntry'; + runId: string; + label: string; + description: string | null; + } + | { + __typename: 'PythonArtifactMetadataEntry'; + module: string; + name: string; + label: string; + description: string | null; + } + | { + __typename: 'TableColumnLineageMetadataEntry'; + label: string; + description: string | null; + lineage: Array<{ + __typename: 'TableColumnLineageEntry'; + columnName: string; + columnDeps: Array<{ + __typename: 'TableColumnDep'; + columnName: string; + assetKey: {__typename: 'AssetKey'; path: Array}; + }>; + }>; + } + | { + __typename: 'TableMetadataEntry'; + label: string; + description: string | null; + table: { + __typename: 'Table'; + records: Array; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{ + __typename: 'DefinitionTag'; + key: string; + value: string; + }>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + }; + } + | { + __typename: 'TableSchemaMetadataEntry'; + label: string; + description: string | null; + schema: { + __typename: 'TableSchema'; + columns: Array<{ + __typename: 'TableColumn'; + name: string; + description: string | null; + type: string; + tags: Array<{__typename: 'DefinitionTag'; key: string; value: string}>; + constraints: { + __typename: 'TableColumnConstraints'; + nullable: boolean; + unique: boolean; + other: Array; + }; + }>; + constraints: { + __typename: 'TableConstraints'; + other: Array; + } | null; + }; + } + | { + __typename: 'TextMetadataEntry'; + text: string; + label: string; + description: string | null; + } + | { + __typename: 'TimestampMetadataEntry'; + timestamp: number; + label: string; + description: string | null; + } + | { + __typename: 'UrlMetadataEntry'; + url: string; + label: string; + description: string | null; + } + >; + } + >; + }; + evaluationNodes: Array<{ + __typename: 'AutomationConditionEvaluationNode'; + uniqueId: string; + expandedLabel: Array; + userLabel: string | null; + startTimestamp: number | null; + endTimestamp: number | null; + numCandidates: number | null; + numTrue: number; + isPartitioned: boolean; + childUniqueIds: Array; + }>; + }>; + } + | {__typename: 'AutoMaterializeAssetEvaluationNeedsMigrationError'} + | null; +}; + +export const AssetCheckAutomationListQueryVersion = 'b6dffe3883c3c008672d9366595c876d95e0b1672f891695550ab513c93fa3a8'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetChecksQuery.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetChecksQuery.types.ts index 0a0802d4b3760..e7d0eff4b81a9 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetChecksQuery.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/AssetChecksQuery.types.ts @@ -2,6 +2,12 @@ import * as Types from '../../../graphql/types'; +export type AssetCheckKeyFragment = { + __typename: 'AssetCheck'; + name: string; + assetKey: {__typename: 'AssetKey'; path: Array}; +}; + export type AssetChecksQueryVariables = Types.Exact<{ assetKey: Types.AssetKeyInput; }>; @@ -25,6 +31,12 @@ export type AssetChecksQuery = { canExecuteIndividually: Types.AssetCheckCanExecuteIndividually; jobNames: Array; description: string | null; + assetKey: {__typename: 'AssetKey'; path: Array}; + automationCondition: { + __typename: 'AutomationCondition'; + label: string | null; + expandedLabel: Array; + } | null; executionForLatestMaterialization: { __typename: 'AssetCheckExecution'; id: string; @@ -238,4 +250,4 @@ export type AssetChecksQuery = { | {__typename: 'AssetNotFoundError'}; }; -export const AssetChecksQueryVersion = '2487c52958999bb33e5daa6c0caa0eea46ab200cb7d5d2294a8290e8e1ad3025'; +export const AssetChecksQueryVersion = '67252db2bc1bfc878d1568008f7e0698a4515d15b4b68c8e88bd1edef4c1f60f'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/VirtualizedAssetCheckTable.types.ts b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/VirtualizedAssetCheckTable.types.ts index b5cd73766262a..96fe6efd4673c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/VirtualizedAssetCheckTable.types.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/types/VirtualizedAssetCheckTable.types.ts @@ -8,6 +8,11 @@ export type AssetCheckTableFragment = { description: string | null; canExecuteIndividually: Types.AssetCheckCanExecuteIndividually; jobNames: Array; + automationCondition: { + __typename: 'AutomationCondition'; + label: string | null; + expandedLabel: Array; + } | null; executionForLatestMaterialization: { __typename: 'AssetCheckExecution'; id: string; diff --git a/js_modules/dagster-ui/packages/ui-core/src/gantt/GanttChart.tsx b/js_modules/dagster-ui/packages/ui-core/src/gantt/GanttChart.tsx index 179e15ac0d2ef..57081b9d63b98 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/gantt/GanttChart.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/gantt/GanttChart.tsx @@ -414,7 +414,7 @@ const GanttChartInner = React.memo((props: GanttChartInnerProps) => { ) : null} - {featureEnabled(FeatureFlag.flagRunSelectionSyntax) ? ( + {featureEnabled(FeatureFlag.flagSelectionSyntax) ? ( ; assetKey: AssetKey; + automationCondition: Maybe; blocking: Scalars['Boolean']['output']; canExecuteIndividually: AssetCheckCanExecuteIndividually; description: Maybe; @@ -299,8 +300,9 @@ export type AssetConditionEvaluationNode = export type AssetConditionEvaluationRecord = { __typename: 'AssetConditionEvaluationRecord'; - assetKey: AssetKey; + assetKey: Maybe; endTimestamp: Maybe; + entityKey: EntityKey; evaluation: AssetConditionEvaluation; evaluationId: Scalars['ID']['output']; evaluationNodes: Array; @@ -1257,6 +1259,8 @@ export type EngineEvent = DisplayableEvent & timestamp: Scalars['String']['output']; }; +export type EntityKey = AssetCheckhandle | AssetKey; + export type EnumConfigType = ConfigType & { __typename: 'EnumConfigType'; description: Maybe; @@ -3813,13 +3817,14 @@ export type QueryAssetCheckExecutionsArgs = { }; export type QueryAssetConditionEvaluationForPartitionArgs = { - assetKey: AssetKeyInput; + assetKey?: InputMaybe; evaluationId: Scalars['ID']['input']; partition: Scalars['String']['input']; }; export type QueryAssetConditionEvaluationRecordsOrErrorArgs = { - assetKey: AssetKeyInput; + assetCheckKey?: InputMaybe; + assetKey?: InputMaybe; cursor?: InputMaybe; limit: Scalars['Int']['input']; }; @@ -4040,7 +4045,7 @@ export type QueryTopLevelResourceDetailsOrErrorArgs = { }; export type QueryTruePartitionsForAutomationConditionEvaluationNodeArgs = { - assetKey: AssetKeyInput; + assetKey?: InputMaybe; evaluationId: Scalars['ID']['input']; nodeUniqueId?: InputMaybe; }; @@ -6001,6 +6006,12 @@ export const buildAssetCheck = ( : relationshipsToOmit.has('AssetKey') ? ({} as AssetKey) : buildAssetKey({}, relationshipsToOmit), + automationCondition: + overrides && overrides.hasOwnProperty('automationCondition') + ? overrides.automationCondition! + : relationshipsToOmit.has('AutomationCondition') + ? ({} as AutomationCondition) + : buildAutomationCondition({}, relationshipsToOmit), blocking: overrides && overrides.hasOwnProperty('blocking') ? overrides.blocking! : true, canExecuteIndividually: overrides && overrides.hasOwnProperty('canExecuteIndividually') @@ -6266,6 +6277,12 @@ export const buildAssetConditionEvaluationRecord = ( : buildAssetKey({}, relationshipsToOmit), endTimestamp: overrides && overrides.hasOwnProperty('endTimestamp') ? overrides.endTimestamp! : 4.33, + entityKey: + overrides && overrides.hasOwnProperty('entityKey') + ? overrides.entityKey! + : relationshipsToOmit.has('AssetCheckhandle') + ? ({} as AssetCheckhandle) + : buildAssetCheckhandle({}, relationshipsToOmit), evaluation: overrides && overrides.hasOwnProperty('evaluation') ? overrides.evaluation! diff --git a/js_modules/dagster-ui/packages/ui-core/src/instigation/TickMaterializationsTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/instigation/TickMaterializationsTable.tsx index 3b52656d83bc5..1c903bfd21528 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/instigation/TickMaterializationsTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/instigation/TickMaterializationsTable.tsx @@ -204,7 +204,7 @@ const AssetDetailRow = ({ setIsOpen(false)} evaluationID={evaluationId} assetKeyPath={assetKey.path} /> diff --git a/js_modules/dagster-ui/packages/ui-core/src/run-selection/AntlrRunSelection.ts b/js_modules/dagster-ui/packages/ui-core/src/run-selection/AntlrRunSelection.ts index d93abfb242675..24c986e69c848 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/run-selection/AntlrRunSelection.ts +++ b/js_modules/dagster-ui/packages/ui-core/src/run-selection/AntlrRunSelection.ts @@ -47,7 +47,7 @@ export const parseRunSelectionQuery = ( export const filterRunSelectionByQuery = weakMapMemoize( (all_runs: RunGraphQueryItem[], query: string): RunSelectionQueryResult => { - if (featureEnabled(FeatureFlag.flagRunSelectionSyntax)) { + if (featureEnabled(FeatureFlag.flagSelectionSyntax)) { const result = parseRunSelectionQuery(all_runs, query); if (result instanceof Error) { // fall back to old behavior diff --git a/js_modules/dagster-ui/packages/ui-core/src/runs/Run.tsx b/js_modules/dagster-ui/packages/ui-core/src/runs/Run.tsx index 4fd391633cf5c..830e16d64e32c 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/runs/Run.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/runs/Run.tsx @@ -189,7 +189,7 @@ const RunWithData = ({ onSetLogsFilter, onSetSelectionQuery, }: RunWithDataProps) => { - const newRunSelectionSyntax = featureEnabled(FeatureFlag.flagRunSelectionSyntax); + const newRunSelectionSyntax = featureEnabled(FeatureFlag.flagSelectionSyntax); const [queryLogType, setQueryLogType] = useQueryPersistedState({ queryKey: 'logType', diff --git a/js_modules/dagster-ui/packages/ui-core/src/runs/RunActionButtons.tsx b/js_modules/dagster-ui/packages/ui-core/src/runs/RunActionButtons.tsx index 5d520b7d0bc5a..d0fa1cfb16de2 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/runs/RunActionButtons.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/runs/RunActionButtons.tsx @@ -188,7 +188,7 @@ export const RunActionButtons = (props: RunActionButtonsProps) => { console.warn('Run execution plan must be present to launch from-selected execution'); return Promise.resolve(); } - const selectionAndDownstreamQuery = featureEnabled(FeatureFlag.flagRunSelectionSyntax) + const selectionAndDownstreamQuery = featureEnabled(FeatureFlag.flagSelectionSyntax) ? selection.keys.map((k) => `name:"${k}"*`).join(' or ') : selection.keys.map((k) => `${k}*`).join(','); diff --git a/js_modules/dagster-ui/packages/ui-core/src/runs/useCursorPaginatedQuery.tsx b/js_modules/dagster-ui/packages/ui-core/src/runs/useCursorPaginatedQuery.tsx index 736b147175069..9566bf88b15c4 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/runs/useCursorPaginatedQuery.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/runs/useCursorPaginatedQuery.tsx @@ -63,6 +63,7 @@ export function useCursorPaginatedQuery { diff --git a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_checks.py b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_checks.py index 799f6bd5ec3d7..0a0a224947ac7 100644 --- a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_checks.py +++ b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_checks.py @@ -10,10 +10,9 @@ from dagster._core.storage.asset_check_execution_record import AssetCheckExecutionRecordStatus from dagster._core.storage.dagster_run import RunRecord -from dagster_graphql.schema.asset_checks import GrapheneAssetCheckExecution - if TYPE_CHECKING: - from dagster_graphql.schema.pipelines.pipeline import GrapheneAssetCheckHandle + from dagster_graphql.schema.asset_checks import GrapheneAssetCheckExecution + from dagster_graphql.schema.entity_key import GrapheneAssetCheckHandle from dagster_graphql.schema.util import ResolveInfo @@ -32,7 +31,9 @@ def fetch_asset_check_executions( asset_check_key: AssetCheckKey, limit: int, cursor: Optional[str], -) -> List[GrapheneAssetCheckExecution]: +) -> List["GrapheneAssetCheckExecution"]: + from dagster_graphql.schema.asset_checks import GrapheneAssetCheckExecution + check_records = loading_context.instance.event_log_storage.get_asset_check_execution_history( check_key=asset_check_key, limit=limit, @@ -50,7 +51,7 @@ def fetch_asset_check_executions( def get_asset_checks_for_run_id( graphene_info: "ResolveInfo", run_id: str ) -> Sequence["GrapheneAssetCheckHandle"]: - from dagster_graphql.schema.pipelines.pipeline import GrapheneAssetCheckHandle + from dagster_graphql.schema.entity_key import GrapheneAssetCheckHandle check.str_param(run_id, "run_id") diff --git a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_condition_evaluations.py b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_condition_evaluations.py index 29d3eb286355d..e754b67658f43 100644 --- a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_condition_evaluations.py +++ b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_asset_condition_evaluations.py @@ -1,7 +1,7 @@ -from typing import TYPE_CHECKING, Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence, Union import dagster._check as check -from dagster import AssetKey +from dagster._core.definitions.asset_key import AssetCheckKey, AssetKey, EntityKey from dagster._core.scheduler.instigation import AutoMaterializeAssetEvaluationRecord from dagster_graphql.schema.asset_condition_evaluations import ( @@ -13,7 +13,7 @@ from dagster_graphql.schema.auto_materialize_asset_evaluations import ( GrapheneAutoMaterializeAssetEvaluationNeedsMigrationError, ) -from dagster_graphql.schema.inputs import GrapheneAssetKeyInput +from dagster_graphql.schema.inputs import GrapheneAssetCheckHandleInput, GrapheneAssetKeyInput if TYPE_CHECKING: from dagster_graphql.schema.util import ResolveInfo @@ -67,20 +67,29 @@ def fetch_asset_condition_evaluation_record_for_partition( ) +def entity_key_from_graphql_input( + graphene_input: Union[GrapheneAssetKeyInput, GrapheneAssetCheckHandleInput], +) -> EntityKey: + if "path" in graphene_input: + return AssetKey.from_graphql_input(graphene_input) + else: + return AssetCheckKey.from_graphql_input(graphene_input) + + def fetch_true_partitions_for_evaluation_node( graphene_info: "ResolveInfo", - graphene_asset_key: GrapheneAssetKeyInput, + graphene_entity_key: Union[GrapheneAssetKeyInput, GrapheneAssetCheckHandleInput], evaluation_id: int, node_unique_id: str, ) -> Sequence[str]: - asset_key = AssetKey.from_graphql_input(graphene_asset_key) + key = entity_key_from_graphql_input(graphene_entity_key) schedule_storage = check.not_none(graphene_info.context.instance.schedule_storage) record = next( iter( schedule_storage.get_auto_materialize_asset_evaluations( # there is no method to get a specific evaluation by id, so instead get the first # evaluation before evaluation_id + 1 - key=asset_key, + key=key, cursor=evaluation_id + 1, limit=1, ) @@ -104,7 +113,7 @@ def fetch_true_partitions_for_evaluation_node( def fetch_asset_condition_evaluation_records_for_asset_key( graphene_info: "ResolveInfo", - graphene_asset_key: GrapheneAssetKeyInput, + graphene_entity_key: Union[GrapheneAssetKeyInput, GrapheneAssetCheckHandleInput], limit: int, cursor: Optional[str], ) -> GrapheneAssetConditionEvaluationRecordsOrError: @@ -113,13 +122,13 @@ def fetch_asset_condition_evaluation_records_for_asset_key( if migration_error: return migration_error - asset_key = AssetKey.from_graphql_input(graphene_asset_key) + key = entity_key_from_graphql_input(graphene_entity_key) schedule_storage = check.not_none(graphene_info.context.instance.schedule_storage) return _get_graphene_records_from_evaluations( graphene_info, schedule_storage.get_auto_materialize_asset_evaluations( - key=asset_key, + key=key, limit=limit, cursor=int(cursor) if cursor else None, ), diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/__init__.py b/python_modules/dagster-graphql/dagster_graphql/schema/__init__.py index 7ad606e5f4c8c..b84cddd321d9e 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/__init__.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/__init__.py @@ -6,7 +6,6 @@ def types(): - from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.backfill import ( GrapheneLaunchBackfillResult, GrapheneLaunchBackfillSuccess, @@ -14,6 +13,7 @@ def types(): from dagster_graphql.schema.config_type_or_error import GrapheneConfigTypeOrError from dagster_graphql.schema.config_types import types as config_types from dagster_graphql.schema.dagster_types import types as dagster_types_types + from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import types as errors_types from dagster_graphql.schema.execution import types as execution_types from dagster_graphql.schema.external import types as external_types diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/asset_checks.py b/python_modules/dagster-graphql/dagster_graphql/schema/asset_checks.py index ca864bc4fa98b..ac14762b2ccfc 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/asset_checks.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/asset_checks.py @@ -7,8 +7,11 @@ AssetCheckEvaluation, AssetCheckEvaluationTargetMaterializationData, ) -from dagster._core.definitions.asset_check_spec import AssetCheckKey, AssetCheckSeverity +from dagster._core.definitions.asset_check_spec import AssetCheckSeverity from dagster._core.definitions.asset_key import AssetKey +from dagster._core.definitions.declarative_automation.serialized_objects import ( + AutomationConditionSnapshot, +) from dagster._core.definitions.remote_asset_graph import RemoteAssetCheckNode from dagster._core.events import DagsterEventType from dagster._core.storage.asset_check_execution_record import ( @@ -17,7 +20,9 @@ ) from dagster_graphql.implementation.events import iterate_metadata_entries -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.auto_materialize_policy import GrapheneAutoMaterializePolicy +from dagster_graphql.schema.automation_condition import GrapheneAutomationCondition +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import GrapheneError from dagster_graphql.schema.metadata import GrapheneMetadataEntry from dagster_graphql.schema.util import ResolveInfo, non_null_list @@ -134,6 +139,7 @@ class GrapheneAssetCheck(graphene.ObjectType): canExecuteIndividually = graphene.NonNull(GrapheneAssetCheckCanExecuteIndividually) blocking = graphene.NonNull(graphene.Boolean) additionalAssetKeys = non_null_list(GrapheneAssetKey) + automationCondition = graphene.Field(GrapheneAutomationCondition) class Meta: name = "AssetCheck" @@ -185,6 +191,22 @@ def resolve_additionalAssetKeys(self, _) -> Sequence[GrapheneAssetKey]: for asset_key in self._asset_check.additional_asset_keys ] + def resolve_automationCondition( + self, _graphene_info: ResolveInfo + ) -> Optional[GrapheneAutoMaterializePolicy]: + automation_condition = ( + self._asset_check.automation_condition_snapshot + or self._asset_check.automation_condition + ) + if automation_condition: + return GrapheneAutomationCondition( + # we only store one of automation_condition or automation_condition_snapshot + automation_condition + if isinstance(automation_condition, AutomationConditionSnapshot) + else automation_condition.get_snapshot() + ) + return None + class GrapheneAssetChecks(graphene.ObjectType): checks = non_null_list(GrapheneAssetCheck) @@ -234,14 +256,3 @@ class Meta: GrapheneAssetCheckNeedsAgentUpgradeError, ) name = "AssetChecksOrError" - - -class GrapheneAssetCheckHandle(graphene.ObjectType): - name = graphene.NonNull(graphene.String) - assetKey = graphene.NonNull(GrapheneAssetKey) - - class Meta: - name = "AssetCheckhandle" - - def __init__(self, handle: AssetCheckKey): - super().__init__(name=handle.name, assetKey=GrapheneAssetKey(path=handle.asset_key.path)) diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/asset_condition_evaluations.py b/python_modules/dagster-graphql/dagster_graphql/schema/asset_condition_evaluations.py index d5626cc10c1e4..5e522a5b8077f 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/asset_condition_evaluations.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/asset_condition_evaluations.py @@ -4,6 +4,7 @@ import graphene from dagster._core.asset_graph_view.serializable_entity_subset import SerializableEntitySubset +from dagster._core.definitions.asset_key import AssetKey from dagster._core.definitions.declarative_automation.serialized_objects import ( AutomationConditionEvaluation, AutomationConditionSnapshot, @@ -11,10 +12,10 @@ from dagster._core.scheduler.instigation import AutoMaterializeAssetEvaluationRecord from dagster_graphql.implementation.events import iterate_metadata_entries -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.auto_materialize_asset_evaluations import ( GrapheneAutoMaterializeAssetEvaluationNeedsMigrationError, ) +from dagster_graphql.schema.entity_key import GrapheneAssetKey, GrapheneEntityKey from dagster_graphql.schema.metadata import GrapheneMetadataEntry from dagster_graphql.schema.util import ResolveInfo, non_null_list @@ -251,7 +252,8 @@ class GrapheneAssetConditionEvaluationRecord(graphene.ObjectType): runIds = non_null_list(graphene.String) timestamp = graphene.NonNull(graphene.Float) - assetKey = graphene.NonNull(GrapheneAssetKey) + assetKey = graphene.Field(GrapheneAssetKey) + entityKey = graphene.NonNull(GrapheneEntityKey) numRequested = graphene.NonNull(graphene.Int) startTimestamp = graphene.Field(graphene.Float) @@ -282,7 +284,10 @@ def __init__( evaluationId=record.evaluation_id, timestamp=record.timestamp, runIds=evaluation_with_run_ids.run_ids, - assetKey=GrapheneAssetKey(path=record.key.path), + assetKey=GrapheneEntityKey.from_entity_key(record.key) + if isinstance(record.key, AssetKey) + else None, + entityKey=GrapheneEntityKey.from_entity_key(record.key), numRequested=root_evaluation.true_subset.size, startTimestamp=root_evaluation.start_timestamp, endTimestamp=root_evaluation.end_timestamp, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/asset_graph.py b/python_modules/dagster-graphql/dagster_graphql/schema/asset_graph.py index 4b59f18050e30..6473b7e1bbed3 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/asset_graph.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/asset_graph.py @@ -61,7 +61,6 @@ GrapheneAssetChecks, GrapheneAssetChecksOrError, ) -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.auto_materialize_policy import GrapheneAutoMaterializePolicy from dagster_graphql.schema.automation_condition import GrapheneAutomationCondition from dagster_graphql.schema.backfill import GrapheneBackfillPolicy @@ -73,6 +72,7 @@ GrapheneRegularDagsterType, to_dagster_type, ) +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import GrapheneAssetNotFoundError from dagster_graphql.schema.freshness_policy import ( GrapheneAssetFreshnessInfo, @@ -839,7 +839,7 @@ def resolve_autoMaterializePolicy( def resolve_automationCondition( self, _graphene_info: ResolveInfo - ) -> Optional[GrapheneAutoMaterializePolicy]: + ) -> Optional[GrapheneAutomationCondition]: automation_condition = ( self._asset_node_snap.automation_condition_snapshot or self._asset_node_snap.automation_condition diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/asset_key.py b/python_modules/dagster-graphql/dagster_graphql/schema/asset_key.py deleted file mode 100644 index d0945dc669b24..0000000000000 --- a/python_modules/dagster-graphql/dagster_graphql/schema/asset_key.py +++ /dev/null @@ -1,18 +0,0 @@ -import graphene - -from dagster_graphql.schema.util import non_null_list - - -class GrapheneAssetKey(graphene.ObjectType): - path = non_null_list(graphene.String) - - class Meta: - name = "AssetKey" - - -class GrapheneAssetLineageInfo(graphene.ObjectType): - assetKey = graphene.NonNull(GrapheneAssetKey) - partitions = non_null_list(graphene.String) - - class Meta: - name = "AssetLineageInfo" diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/asset_selections.py b/python_modules/dagster-graphql/dagster_graphql/schema/asset_selections.py index d380a35922e03..eb913c1692829 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/asset_selections.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/asset_selections.py @@ -7,7 +7,7 @@ from dagster_graphql.implementation.fetch_assets import get_asset from dagster_graphql.implementation.utils import capture_error -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.util import ResolveInfo, non_null_list if TYPE_CHECKING: diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/auto_materialize_asset_evaluations.py b/python_modules/dagster-graphql/dagster_graphql/schema/auto_materialize_asset_evaluations.py index d1e9c4d7f0025..a52bc5d44d4b1 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/auto_materialize_asset_evaluations.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/auto_materialize_asset_evaluations.py @@ -10,8 +10,8 @@ from dagster._core.definitions.metadata import DagsterAssetMetadataValue from dagster._core.scheduler.instigation import AutoMaterializeAssetEvaluationRecord -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.auto_materialize_policy import GrapheneAutoMaterializeRule +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import GrapheneError from dagster_graphql.schema.partition_keys import ( GraphenePartitionKeys, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/backfill.py b/python_modules/dagster-graphql/dagster_graphql/schema/backfill.py index 81aba8d6a52e6..56549667aab45 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/backfill.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/backfill.py @@ -33,7 +33,7 @@ partition_statuses_from_run_partition_data, ) from dagster_graphql.implementation.utils import has_permission_for_asset_graph -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GrapheneError, GrapheneInvalidOutputError, @@ -383,7 +383,7 @@ class Meta: description="Included to comply with RunsFeedEntry interface.", ) assetCheckSelection = graphene.List( - graphene.NonNull("dagster_graphql.schema.asset_checks.GrapheneAssetCheckHandle") + graphene.NonNull("dagster_graphql.schema.entity_key.GrapheneAssetCheckHandle") ) def __init__(self, backfill_job: PartitionBackfill): diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/entity_key.py b/python_modules/dagster-graphql/dagster_graphql/schema/entity_key.py new file mode 100644 index 0000000000000..a1e9696511eb8 --- /dev/null +++ b/python_modules/dagster-graphql/dagster_graphql/schema/entity_key.py @@ -0,0 +1,44 @@ +import graphene +from dagster._core.definitions.asset_check_spec import AssetCheckKey +from dagster._core.definitions.asset_key import AssetKey, EntityKey + +from dagster_graphql.schema.util import non_null_list + + +class GrapheneAssetKey(graphene.ObjectType): + path = non_null_list(graphene.String) + + class Meta: + name = "AssetKey" + + +class GrapheneAssetCheckHandle(graphene.ObjectType): + name = graphene.NonNull(graphene.String) + assetKey = graphene.NonNull(GrapheneAssetKey) + + class Meta: + name = "AssetCheckhandle" + + def __init__(self, handle: AssetCheckKey): + super().__init__(name=handle.name, assetKey=GrapheneAssetKey(path=handle.asset_key.path)) + + +class GrapheneEntityKey(graphene.Union): + class Meta: + name = "EntityKey" + types = (GrapheneAssetKey, GrapheneAssetCheckHandle) + + @staticmethod + def from_entity_key(key: EntityKey) -> "GrapheneEntityKey": + if isinstance(key, AssetKey): + return GrapheneAssetKey(path=key.path) + else: + return GrapheneAssetCheckHandle(handle=key) + + +class GrapheneAssetLineageInfo(graphene.ObjectType): + assetKey = graphene.NonNull(GrapheneAssetKey) + partitions = non_null_list(graphene.String) + + class Meta: + name = "AssetLineageInfo" diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/instigation.py b/python_modules/dagster-graphql/dagster_graphql/schema/instigation.py index e58cc9b863cae..26005c6708c6a 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/instigation.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/instigation.py @@ -33,7 +33,7 @@ from dagster_graphql.implementation.fetch_ticks import get_instigation_ticks from dagster_graphql.implementation.loader import RepositoryScopedBatchLoader from dagster_graphql.implementation.utils import UserFacingGraphQLError -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GrapheneError, GraphenePythonError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/logs/events.py b/python_modules/dagster-graphql/dagster_graphql/schema/logs/events.py index 46a1dbc8bf0e2..0014e2051980a 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/logs/events.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/logs/events.py @@ -11,7 +11,7 @@ from dagster_graphql.implementation.events import construct_basic_params from dagster_graphql.implementation.fetch_runs import gen_run_by_id, get_step_stats from dagster_graphql.schema.asset_checks import GrapheneAssetCheckEvaluation -from dagster_graphql.schema.asset_key import GrapheneAssetKey, GrapheneAssetLineageInfo +from dagster_graphql.schema.entity_key import GrapheneAssetKey, GrapheneAssetLineageInfo from dagster_graphql.schema.errors import GraphenePythonError, GrapheneRunNotFoundError from dagster_graphql.schema.logs.log_level import GrapheneLogLevel from dagster_graphql.schema.metadata import GrapheneMetadataEntry diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/metadata.py b/python_modules/dagster-graphql/dagster_graphql/schema/metadata.py index dfcec93939e13..2e5fa989f7f0d 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/metadata.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/metadata.py @@ -1,6 +1,6 @@ import graphene -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.table import ( GrapheneTable, GrapheneTableColumnLineageEntry, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/partition_sets.py b/python_modules/dagster-graphql/dagster_graphql/schema/partition_sets.py index 6610edfd4fcf4..3b20c41fbf330 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/partition_sets.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/partition_sets.py @@ -29,8 +29,8 @@ ) from dagster_graphql.implementation.fetch_runs import get_runs from dagster_graphql.implementation.utils import capture_error -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.backfill import GraphenePartitionBackfill +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GrapheneDuplicateDynamicPartitionError, GraphenePartitionSetNotFoundError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py b/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py index be0b522e2da82..22f4e32623dda 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py @@ -36,14 +36,13 @@ apply_cursor_limit_reverse, capture_error, ) -from dagster_graphql.schema.asset_checks import GrapheneAssetCheckHandle -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.dagster_types import ( GrapheneDagsterType, GrapheneDagsterTypeOrError, GrapheneDagsterTypeUnion, to_dagster_type, ) +from dagster_graphql.schema.entity_key import GrapheneAssetCheckHandle, GrapheneAssetKey from dagster_graphql.schema.errors import ( GrapheneDagsterTypeNotFoundError, GraphenePythonError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/resources.py b/python_modules/dagster-graphql/dagster_graphql/schema/resources.py index b6b942fe17458..43946513601dd 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/resources.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/resources.py @@ -11,8 +11,8 @@ ResourceValueSnap, ) -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.config_types import GrapheneConfigTypeField +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GraphenePythonError, GrapheneRepositoryNotFoundError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/roots/mutation.py b/python_modules/dagster-graphql/dagster_graphql/schema/roots/mutation.py index 31d51fc9be512..24d67095948e4 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/roots/mutation.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/roots/mutation.py @@ -44,13 +44,13 @@ pipeline_selector_from_graphql, require_permission_check, ) -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.backfill import ( GrapheneAssetPartitionRange, GrapheneCancelBackfillResult, GrapheneLaunchBackfillResult, GrapheneResumeBackfillResult, ) +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GrapheneAssetNotFoundError, GrapheneConflictingExecutionParamsError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py b/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py index 48a244bee6457..d50d810915e62 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py @@ -103,7 +103,6 @@ GrapheneAssetConditionEvaluationRecordsOrError, ) from dagster_graphql.schema.asset_graph import ( - GrapheneAssetKey, GrapheneAssetLatestInfo, GrapheneAssetNode, GrapheneAssetNodeDefinitionCollision, @@ -118,6 +117,7 @@ GraphenePartitionBackfillOrError, GraphenePartitionBackfillsOrError, ) +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.env_vars import GrapheneEnvVarWithConsumersListOrError from dagster_graphql.schema.external import ( GrapheneRepositoriesOrError, @@ -129,6 +129,7 @@ ) from dagster_graphql.schema.inputs import ( GrapheneAssetBackfillPreviewParams, + GrapheneAssetCheckHandleInput, GrapheneAssetGroupSelector, GrapheneAssetKeyInput, GrapheneBulkActionsFilter, @@ -578,7 +579,7 @@ class Meta: truePartitionsForAutomationConditionEvaluationNode = graphene.Field( non_null_list(graphene.String), - assetKey=graphene.Argument(graphene.NonNull(GrapheneAssetKeyInput)), + assetKey=graphene.Argument(GrapheneAssetKeyInput), evaluationId=graphene.Argument(graphene.NonNull(graphene.ID)), nodeUniqueId=graphene.Argument(graphene.String), description="Retrieve the partition keys which were true for a specific automation condition evaluation node.", @@ -594,7 +595,7 @@ class Meta: assetConditionEvaluationForPartition = graphene.Field( GrapheneAssetConditionEvaluation, - assetKey=graphene.Argument(graphene.NonNull(GrapheneAssetKeyInput)), + assetKey=graphene.Argument(GrapheneAssetKeyInput), evaluationId=graphene.Argument(graphene.NonNull(graphene.ID)), partition=graphene.Argument(graphene.NonNull(graphene.String)), description="Retrieve the condition evaluation for an asset and partition.", @@ -602,7 +603,8 @@ class Meta: assetConditionEvaluationRecordsOrError = graphene.Field( GrapheneAssetConditionEvaluationRecordsOrError, - assetKey=graphene.Argument(graphene.NonNull(GrapheneAssetKeyInput)), + assetKey=graphene.Argument(GrapheneAssetKeyInput), + assetCheckKey=graphene.Argument(GrapheneAssetCheckHandleInput, required=False), limit=graphene.Argument(graphene.NonNull(graphene.Int)), cursor=graphene.Argument(graphene.String), description="Retrieve the condition evaluation records for an asset.", @@ -1269,7 +1271,7 @@ def resolve_autoMaterializeEvaluationsForEvaluationId( def resolve_assetConditionEvaluationForPartition( self, graphene_info: ResolveInfo, - assetKey: GrapheneAssetKeyInput, + assetKey: Optional[GrapheneAssetKeyInput], evaluationId: str, partition: str, ): @@ -1283,13 +1285,14 @@ def resolve_assetConditionEvaluationForPartition( def resolve_assetConditionEvaluationRecordsOrError( self, graphene_info: ResolveInfo, - assetKey: GrapheneAssetKeyInput, + assetKey: Optional[GrapheneAssetKeyInput], limit: int, cursor: Optional[str] = None, + assetCheckKey: Optional[GrapheneAssetCheckHandleInput] = None, ): return fetch_asset_condition_evaluation_records_for_asset_key( graphene_info=graphene_info, - graphene_asset_key=assetKey, + graphene_entity_key=check.not_none(assetKey or assetCheckKey), cursor=cursor, limit=limit, ) @@ -1297,13 +1300,13 @@ def resolve_assetConditionEvaluationRecordsOrError( def resolve_truePartitionsForAutomationConditionEvaluationNode( self, graphene_info: ResolveInfo, - assetKey: GrapheneAssetKeyInput, + assetKey: Optional[GrapheneAssetKeyInput], evaluationId: str, nodeUniqueId: str, ): return fetch_true_partitions_for_evaluation_node( graphene_info=graphene_info, - graphene_asset_key=assetKey, + graphene_entity_key=assetKey, evaluation_id=int(evaluationId), node_unique_id=nodeUniqueId, ) diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/runs_feed.py b/python_modules/dagster-graphql/dagster_graphql/schema/runs_feed.py index 112ee46f89a03..73dfee2e3e8eb 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/runs_feed.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/runs_feed.py @@ -1,6 +1,6 @@ import graphene -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import GraphenePythonError from dagster_graphql.schema.util import non_null_list @@ -15,7 +15,7 @@ class GrapheneRunsFeedEntry(graphene.Interface): jobName = graphene.String() assetSelection = graphene.List(graphene.NonNull(GrapheneAssetKey)) assetCheckSelection = graphene.List( - graphene.NonNull("dagster_graphql.schema.asset_checks.GrapheneAssetCheckHandle") + graphene.NonNull("dagster_graphql.schema.entity_key.GrapheneAssetCheckHandle") ) class Meta: diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/sensors.py b/python_modules/dagster-graphql/dagster_graphql/schema/sensors.py index 627565bfb80c7..8cefee63c4a79 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/sensors.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/sensors.py @@ -25,8 +25,8 @@ capture_error, require_permission_check, ) -from dagster_graphql.schema.asset_key import GrapheneAssetKey from dagster_graphql.schema.asset_selections import GrapheneAssetSelection +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.errors import ( GraphenePythonError, GrapheneRepositoryNotFoundError, diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/table.py b/python_modules/dagster-graphql/dagster_graphql/schema/table.py index 08d1e6e22b645..472071dbe7aa3 100644 --- a/python_modules/dagster-graphql/dagster_graphql/schema/table.py +++ b/python_modules/dagster-graphql/dagster_graphql/schema/table.py @@ -3,7 +3,7 @@ import graphene from dagster._core.definitions.metadata.table import TableColumn -from dagster_graphql.schema.asset_key import GrapheneAssetKey +from dagster_graphql.schema.entity_key import GrapheneAssetKey from dagster_graphql.schema.tags import GrapheneDefinitionTag from dagster_graphql.schema.util import non_null_list diff --git a/python_modules/dagster/dagster/_annotations.py b/python_modules/dagster/dagster/_annotations.py index 96d906aff0c3e..6966f4e208fed 100644 --- a/python_modules/dagster/dagster/_annotations.py +++ b/python_modules/dagster/dagster/_annotations.py @@ -11,6 +11,7 @@ is_resource_def, ) from dagster._utils.warnings import ( + beta_warning, deprecation_warning, experimental_warning, preview_warning, @@ -177,6 +178,120 @@ def get_preview_info(obj: Annotatable) -> PreviewInfo: return getattr(target, _PREVIEW_ATTR_NAME) +# ######################## +# ##### BETA +# ######################## + + +_BETA_ATTR_NAME: Final[str] = "_beta" + + +@dataclass +class BetaInfo: + additional_warn_text: Optional[str] = None + subject: Optional[str] = None + + +@overload +def beta( + __obj: T_Annotatable, + *, + additional_warn_text: Optional[str] = ..., + subject: Optional[str] = ..., + emit_runtime_warning: bool = ..., +) -> T_Annotatable: ... + + +@overload +def beta( + __obj: None = ..., + *, + additional_warn_text: Optional[str] = ..., + subject: Optional[str] = ..., + emit_runtime_warning: bool = ..., +) -> Callable[[T_Annotatable], T_Annotatable]: ... + + +def beta( + __obj: Optional[T_Annotatable] = None, + *, + additional_warn_text: Optional[str] = None, + subject: Optional[str] = None, + emit_runtime_warning: bool = True, +) -> Union[T_Annotatable, Callable[[T_Annotatable], T_Annotatable]]: + """Mark an object as beta. This appends some metadata to the object that causes it to be + rendered with a "beta" tag and associated warning in the docs. + + If `emit_runtime_warning` is True, a warning will also be emitted when the function is called, + having the same text as is displayed in the docs. For consistency between docs and runtime + warnings, this decorator is preferred to manual calls to `beta_warning`. + + Args: + additional_warn_text (Optional[str]): Additional text to display after the beta warning. + subject (Optional[str]): The subject of the beta warning. Defaults to a string + representation of the decorated object. This is useful when marking usage of + a beta API inside an otherwise non-beta function, so + that it can be easily cleaned up later. It should only be used with + `emit_runtime_warning=False`, as we don't want to warn users when a + beta API is used internally. + emit_runtime_warning (bool): Whether to emit a warning when the function is called. + + Usage: + + .. code-block:: python + + @beta + def my_beta_function(my_arg): + ... + + @beta + class MyBetaClass: + ... + + @beta(subject="some_beta_function", emit_runtime_warning=False) + def not_beta_function(): + ... + some_beta_function() + ... + """ + if __obj is None: + return lambda obj: beta( + obj, + subject=subject, + emit_runtime_warning=emit_runtime_warning, + additional_warn_text=additional_warn_text, + ) + else: + target = _get_annotation_target(__obj) + setattr( + target, + _BETA_ATTR_NAME, + BetaInfo(additional_warn_text, subject), + ) + + if emit_runtime_warning: + stack_level = _get_warning_stacklevel(__obj) + subject = subject or _get_subject(__obj) + warning_fn = lambda: beta_warning( + subject, + additional_warn_text=additional_warn_text, + stacklevel=stack_level, + ) + return apply_pre_call_decorator(__obj, warning_fn) + else: + return __obj + + +def is_beta(obj: Annotatable) -> bool: + target = _get_annotation_target(obj) + return hasattr(target, _BETA_ATTR_NAME) + + +def get_beta_info(obj: Annotatable) -> BetaInfo: + target = _get_annotation_target(obj) + return getattr(target, _BETA_ATTR_NAME) + + # ######################## # ##### SUPERSEDED # ######################## diff --git a/python_modules/dagster/dagster/_utils/warnings.py b/python_modules/dagster/dagster/_utils/warnings.py index 31084f633de9c..b6d63bc1418f2 100644 --- a/python_modules/dagster/dagster/_utils/warnings.py +++ b/python_modules/dagster/dagster/_utils/warnings.py @@ -36,6 +36,32 @@ def preview_warning( ) +# ######################## +# ##### BETA +# ######################## + + +class BetaWarning(Warning): + pass + + +def beta_warning( + subject: str, + additional_warn_text: Optional[str] = None, + stacklevel: int = 3, +): + if not _warnings_on.get(): + return + + warnings.warn( + f"{subject} is currently in beta, and may have breaking changes in minor version releases, " + f"with behavior changes in patch releases." + + ((" " + additional_warn_text) if additional_warn_text else ""), + category=BetaWarning, + stacklevel=stacklevel, + ) + + # ######################## # ##### SUPERSEDED # ######################## diff --git a/python_modules/libraries/dagster-components/dagster_components/core/component_generator.py b/python_modules/libraries/dagster-components/dagster_components/core/component_generator.py new file mode 100644 index 0000000000000..7535c24cf46b9 --- /dev/null +++ b/python_modules/libraries/dagster-components/dagster_components/core/component_generator.py @@ -0,0 +1 @@ +class ComponentGenerator: ... diff --git a/python_modules/libraries/dagster-looker/dagster_looker/api/dagster_looker_api_translator.py b/python_modules/libraries/dagster-looker/dagster_looker/api/dagster_looker_api_translator.py index 03733e9bb99d5..d451e6d8c8b18 100644 --- a/python_modules/libraries/dagster-looker/dagster_looker/api/dagster_looker_api_translator.py +++ b/python_modules/libraries/dagster-looker/dagster_looker/api/dagster_looker_api_translator.py @@ -110,7 +110,7 @@ class LookerStructureData: @record class LookerApiTranslatorStructureData: """A record representing a structure in Looker and the Looker instance data. - Includes the content's type and data as returned from the API. + Includes the structure's type and data as returned from the API. """ structure_data: "LookerStructureData" diff --git a/python_modules/libraries/dagster-sigma/dagster_sigma/__init__.py b/python_modules/libraries/dagster-sigma/dagster_sigma/__init__.py index 944e51de4e4c2..456c5b7b0bb9b 100644 --- a/python_modules/libraries/dagster-sigma/dagster_sigma/__init__.py +++ b/python_modules/libraries/dagster-sigma/dagster_sigma/__init__.py @@ -12,7 +12,9 @@ from dagster_sigma.translator import ( DagsterSigmaTranslator as DagsterSigmaTranslator, SigmaDataset as SigmaDataset, + SigmaDatasetTranslatorData as SigmaDatasetTranslatorData, SigmaWorkbook as SigmaWorkbook, + SigmaWorkbookTranslatorData as SigmaWorkbookTranslatorData, ) from dagster_sigma.version import __version__ diff --git a/python_modules/libraries/dagster-sigma/dagster_sigma/resource.py b/python_modules/libraries/dagster-sigma/dagster_sigma/resource.py index b6d5ae249c099..b07e0b4015c8a 100644 --- a/python_modules/libraries/dagster-sigma/dagster_sigma/resource.py +++ b/python_modules/libraries/dagster-sigma/dagster_sigma/resource.py @@ -26,6 +26,7 @@ from dagster._serdes.serdes import deserialize_value from dagster._utils.cached_method import cached_method from dagster._utils.log import get_dagster_logger +from dagster._utils.warnings import deprecation_warning from pydantic import Field, PrivateAttr from sqlglot import exp, parse_one @@ -709,7 +710,9 @@ def build_defs( @experimental def load_sigma_asset_specs( organization: SigmaOrganization, - dagster_sigma_translator: Type[DagsterSigmaTranslator] = DagsterSigmaTranslator, + dagster_sigma_translator: Optional[ + Union[DagsterSigmaTranslator, Type[DagsterSigmaTranslator]] + ] = None, sigma_filter: Optional[SigmaFilter] = None, fetch_column_data: bool = True, fetch_lineage_data: bool = True, @@ -719,8 +722,9 @@ def load_sigma_asset_specs( Args: organization (SigmaOrganization): The Sigma organization to fetch assets from. - dagster_sigma_translator (Type[DagsterSigmaTranslator]): The translator to use - to convert Sigma content into AssetSpecs. Defaults to DagsterSigmaTranslator. + dagster_sigma_translator (Optional[Union[DagsterSigmaTranslator, Type[DagsterSigmaTranslatorr]]]): + The translator to use to convert Sigma content into :py:class:`dagster.AssetSpec`. + Defaults to :py:class:`DagsterSigmaTranslator`. sigma_filter (Optional[SigmaFilter]): Filters the set of Sigma objects to fetch. fetch_column_data (bool): Whether to fetch column data for datasets, which can be slow. fetch_lineage_data (bool): Whether to fetch any lineage data for workbooks and datasets. @@ -730,6 +734,16 @@ def load_sigma_asset_specs( Returns: List[AssetSpec]: The set of assets representing the Sigma content in the organization. """ + if isinstance(dagster_sigma_translator, type): + deprecation_warning( + subject="Support of `dagster_sigma_translator` as a Type[DagsterSigmaTranslator]", + breaking_version="1.10", + additional_warn_text=( + "Pass an instance of DagsterSigmaTranslator or subclass to `dagster_sigma_translator` instead." + ), + ) + dagster_sigma_translator = dagster_sigma_translator() + snapshot = None if snapshot_path and not os.getenv(SNAPSHOT_ENV_VAR_NAME): snapshot = deserialize_value(Path(snapshot_path).read_text(), RepositoryLoadData) @@ -738,7 +752,7 @@ def load_sigma_asset_specs( return check.is_list( SigmaOrganizationDefsLoader( organization=initialized_organization, - translator_cls=dagster_sigma_translator, + translator=dagster_sigma_translator or DagsterSigmaTranslator(), sigma_filter=sigma_filter, fetch_column_data=fetch_column_data, fetch_lineage_data=fetch_lineage_data, @@ -767,7 +781,7 @@ def _get_translator_spec_assert_keys_match( @dataclass class SigmaOrganizationDefsLoader(StateBackedDefinitionsLoader[SigmaOrganizationData]): organization: SigmaOrganization - translator_cls: Type[DagsterSigmaTranslator] + translator: DagsterSigmaTranslator snapshot: Optional[RepositoryLoadData] sigma_filter: Optional[SigmaFilter] = None fetch_column_data: bool = True @@ -790,7 +804,6 @@ def fetch_state(self) -> SigmaOrganizationData: ) def defs_from_state(self, state: SigmaOrganizationData) -> Definitions: - translator = self.translator_cls() translator_data_workbooks = [ SigmaWorkbookTranslatorData(workbook=workbook, organization_data=state) for workbook in state.workbooks @@ -800,7 +813,7 @@ def defs_from_state(self, state: SigmaOrganizationData) -> Definitions: for dataset in state.datasets ] asset_specs = [ - _get_translator_spec_assert_keys_match(translator, obj) + _get_translator_spec_assert_keys_match(self.translator, obj) for obj in [*translator_data_workbooks, *translator_data_datasets] ] return Definitions(assets=asset_specs) diff --git a/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator.py b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator.py index d3c48e3169250..d0e9981d4f4d1 100644 --- a/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator.py +++ b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator.py @@ -28,5 +28,5 @@ def get_asset_spec(self, data) -> AssetSpec: client_secret=EnvVar("SIGMA_CLIENT_SECRET"), ) - sigma_specs = load_sigma_asset_specs(resource, dagster_sigma_translator=MyCoolTranslator) + sigma_specs = load_sigma_asset_specs(resource, dagster_sigma_translator=MyCoolTranslator()) defs = Definitions(assets=[*sigma_specs], jobs=[define_asset_job("all_asset_job")]) diff --git a/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator_legacy.py b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator_legacy.py new file mode 100644 index 0000000000000..a36174297f946 --- /dev/null +++ b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/pending_repo_with_translator_legacy.py @@ -0,0 +1,33 @@ +from dagster import EnvVar, define_asset_job +from dagster._core.definitions.asset_spec import AssetSpec +from dagster._core.definitions.definitions_class import Definitions +from dagster._utils.env import environ +from dagster_sigma import ( + DagsterSigmaTranslator, + SigmaBaseUrl, + SigmaOrganization, + load_sigma_asset_specs, +) + +fake_client_id = "fake_client_id" +fake_client_secret = "fake_client_secret" + +with environ({"SIGMA_CLIENT_ID": fake_client_id, "SIGMA_CLIENT_SECRET": fake_client_secret}): + fake_token = "fake_token" + + class MyCoolTranslator(DagsterSigmaTranslator): + def get_asset_spec(self, data) -> AssetSpec: + spec = super().get_asset_spec(data) + return spec.replace_attributes( + key=spec.key.with_prefix("my_prefix"), + ) + + resource = SigmaOrganization( + base_url=SigmaBaseUrl.AWS_US, + client_id=EnvVar("SIGMA_CLIENT_ID"), + client_secret=EnvVar("SIGMA_CLIENT_SECRET"), + ) + + # Pass the translator type + sigma_specs = load_sigma_asset_specs(resource, dagster_sigma_translator=MyCoolTranslator) + defs = Definitions(assets=[*sigma_specs], jobs=[define_asset_job("all_asset_job")]) diff --git a/python_modules/libraries/dagster-sigma/dagster_sigma_tests/test_asset_specs.py b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/test_asset_specs.py index c3cc5a383c885..4624133674c5b 100644 --- a/python_modules/libraries/dagster-sigma/dagster_sigma_tests/test_asset_specs.py +++ b/python_modules/libraries/dagster-sigma/dagster_sigma_tests/test_asset_specs.py @@ -1,6 +1,7 @@ from pathlib import Path from tempfile import TemporaryDirectory +import pytest import responses from click.testing import CliRunner from dagster._core.code_pointer import CodePointer @@ -117,3 +118,26 @@ def test_load_assets_organization_data_translator( assert all( key.path[0] == "my_prefix" for key in repository_def.assets_defs_by_key.keys() ), repository_def.assets_defs_by_key + + +@responses.activate +def test_load_assets_organization_data_translator_legacy( + sigma_auth_token: str, sigma_sample_data: None +) -> None: + with instance_for_test() as _instance: + with pytest.warns( + DeprecationWarning, + match=r"Support of `dagster_sigma_translator` as a Type\[DagsterSigmaTranslator\]", + ): + repository_def = initialize_repository_def_from_pointer( + CodePointer.from_python_file( + str(Path(__file__).parent / "pending_repo_with_translator_legacy.py"), + "defs", + None, + ), + ) + + assert len(repository_def.assets_defs_by_key) == 2 + assert all( + key.path[0] == "my_prefix" for key in repository_def.assets_defs_by_key.keys() + ), repository_def.assets_defs_by_key
Timestamp Evaluation result