@datarecce/ui 0.1.40 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -133
- package/dist/AuthModal-C8LetZNB.js +23 -0
- package/dist/AuthModal-C8LetZNB.js.map +1 -0
- package/dist/LineageCanvas-CR38SDYr.d.ts +41 -0
- package/dist/LineageCanvas-CR38SDYr.d.ts.map +1 -0
- package/dist/ResultErrorFallback-C7c-TN1p.js +3 -0
- package/dist/ResultErrorFallback-C7c-TN1p.js.map +1 -0
- package/dist/RouteConfigContext-z8YNimdP.d.ts +172 -0
- package/dist/RouteConfigContext-z8YNimdP.d.ts.map +1 -0
- package/dist/RunProgress-DyFIALbI.d.ts +429 -0
- package/dist/RunProgress-DyFIALbI.d.ts.map +1 -0
- package/dist/ScreenshotDataGrid-BfxYUThx.d.ts +64 -0
- package/dist/ScreenshotDataGrid-BfxYUThx.d.ts.map +1 -0
- package/dist/SplitPane-B-BLxZaQ.d.ts +1427 -0
- package/dist/SplitPane-B-BLxZaQ.d.ts.map +1 -0
- package/dist/advanced.d.ts +18 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +3 -0
- package/dist/advanced.js.map +1 -0
- package/dist/api-ZZ4cc9b9.d.ts +255 -0
- package/dist/api-ZZ4cc9b9.d.ts.map +1 -0
- package/dist/api-_i6BZPkM.js +3 -0
- package/dist/api-_i6BZPkM.js.map +1 -0
- package/dist/api.d.ts +3 -2
- package/dist/api.js +2 -100
- package/dist/colors--47Kkns4.js +3 -0
- package/dist/colors--47Kkns4.js.map +1 -0
- package/dist/colors-vY9Yzui0.d.ts +255 -0
- package/dist/colors-vY9Yzui0.d.ts.map +1 -0
- package/dist/components-run.d.ts +8 -0
- package/dist/components-run.js +2 -0
- package/dist/components.d.ts +22 -2
- package/dist/components.js +2 -86
- package/dist/const-DbXBkrxT.js +3 -0
- package/dist/const-DbXBkrxT.js.map +1 -0
- package/dist/constants-DD5vJv2q.js +3 -0
- package/dist/constants-DD5vJv2q.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +2 -0
- package/dist/contexts.d.ts +7 -0
- package/dist/contexts.js +2 -0
- package/dist/flag-CiR2E5oz.d.ts +898 -0
- package/dist/flag-CiR2E5oz.d.ts.map +1 -0
- package/dist/flag-koeDAqr3.js +3 -0
- package/dist/flag-koeDAqr3.js.map +1 -0
- package/dist/hooks.d.ts +15 -2
- package/dist/hooks.js +2 -43
- package/dist/index-BFkbe0aF.d.ts +698 -0
- package/dist/index-BFkbe0aF.d.ts.map +1 -0
- package/dist/index-Ba3hp2Ng.d.ts +471 -0
- package/dist/index-Ba3hp2Ng.d.ts.map +1 -0
- package/dist/index-C_kD4ZQ3.d.ts +1079 -0
- package/dist/index-C_kD4ZQ3.d.ts.map +1 -0
- package/dist/index-CiPcALu4.d.ts +146 -0
- package/dist/index-CiPcALu4.d.ts.map +1 -0
- package/dist/index-CkXLPYZY.d.ts +13 -0
- package/dist/index-CkXLPYZY.d.ts.map +1 -0
- package/dist/index-DTCpHvX_.d.ts +211 -0
- package/dist/index-DTCpHvX_.d.ts.map +1 -0
- package/dist/index-DVoQsx5c.d.ts +349 -0
- package/dist/index-DVoQsx5c.d.ts.map +1 -0
- package/dist/index-DmwYRgDR.d.ts +192 -0
- package/dist/index-DmwYRgDR.d.ts.map +1 -0
- package/dist/index-N8N7XmRj.d.ts +130 -0
- package/dist/index-N8N7XmRj.d.ts.map +1 -0
- package/dist/index-h_fw6R9U.d.ts +1501 -0
- package/dist/index-h_fw6R9U.d.ts.map +1 -0
- package/dist/index-o48TPoFN.d.ts +734 -0
- package/dist/index-o48TPoFN.d.ts.map +1 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.js +2 -198
- package/dist/keepAlive-CEzyrDfg.js +3 -0
- package/dist/keepAlive-CEzyrDfg.js.map +1 -0
- package/dist/lib/api/axiosClient.d.ts +8 -0
- package/dist/lib/api/axiosClient.d.ts.map +1 -0
- package/dist/lib/api/axiosClient.js +3 -0
- package/dist/lib/api/axiosClient.js.map +1 -0
- package/dist/lib/api/track.d.ts +137 -0
- package/dist/lib/api/track.d.ts.map +1 -0
- package/dist/lib/api/track.js +2 -0
- package/dist/lib/api/user.d.ts +16 -0
- package/dist/lib/api/user.d.ts.map +1 -0
- package/dist/lib/api/user.js +2 -0
- package/dist/lib/const.d.ts +8 -0
- package/dist/lib/const.d.ts.map +1 -0
- package/dist/lib/const.js +2 -0
- package/dist/lib/result/ResultErrorFallback.d.ts +8 -0
- package/dist/lib/result/ResultErrorFallback.d.ts.map +1 -0
- package/dist/lib/result/ResultErrorFallback.js +2 -0
- package/dist/primitives-CgGUvwHB.d.ts +914 -0
- package/dist/primitives-CgGUvwHB.d.ts.map +1 -0
- package/dist/primitives.d.ts +12 -0
- package/dist/primitives.js +2 -0
- package/dist/result.d.ts +4 -0
- package/dist/result.js +2 -0
- package/dist/src-BgHSbbHk.js +67 -0
- package/dist/src-BgHSbbHk.js.map +1 -0
- package/dist/styles.css +478 -4
- package/dist/theme-CeWzymUn.js +64 -0
- package/dist/theme-CeWzymUn.js.map +1 -0
- package/dist/theme.d.ts +3 -2
- package/dist/theme.js +2 -9
- package/dist/track-9ZQpBlUK.js +3 -0
- package/dist/track-9ZQpBlUK.js.map +1 -0
- package/dist/types-CFbNxrx2.d.ts +171 -0
- package/dist/types-CFbNxrx2.d.ts.map +1 -0
- package/dist/types-CZre3j02.d.ts +231 -0
- package/dist/types-CZre3j02.d.ts.map +1 -0
- package/dist/types.d.ts +14 -2
- package/dist/types.js +3 -9
- package/dist/types.js.map +1 -0
- package/dist/useRecceServerFlag-Bg5R67J4.js +3 -0
- package/dist/useRecceServerFlag-Bg5R67J4.js.map +1 -0
- package/dist/useThemeColors--prVbMmM.js +3 -0
- package/dist/useThemeColors--prVbMmM.js.map +1 -0
- package/dist/useThemeColors-DHEroo8f.d.ts +104 -0
- package/dist/useThemeColors-DHEroo8f.d.ts.map +1 -0
- package/dist/user-DMT7E0fc.js +3 -0
- package/dist/user-DMT7E0fc.js.map +1 -0
- package/dist/utils-CW2skXm_.js +3 -0
- package/dist/utils-CW2skXm_.js.map +1 -0
- package/dist/utils-CXWhfyxC.js +5 -0
- package/dist/utils-CXWhfyxC.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +2 -0
- package/package.json +115 -107
- package/LICENSE +0 -201
- package/dist/RecceCheckContext-BJprb2xR.js +0 -7968
- package/dist/RecceCheckContext-BJprb2xR.js.map +0 -1
- package/dist/RecceCheckContext-DPnWB_aU.css +0 -215
- package/dist/RecceCheckContext-DPnWB_aU.css.map +0 -1
- package/dist/RecceCheckContext-DbZ7BdRy.mjs +0 -7426
- package/dist/RecceCheckContext-DbZ7BdRy.mjs.map +0 -1
- package/dist/RecceCheckContext-DyxOeUsX.css +0 -215
- package/dist/RecceCheckContext-DyxOeUsX.css.map +0 -1
- package/dist/api.d.mts +0 -3
- package/dist/api.js.map +0 -1
- package/dist/api.mjs +0 -46
- package/dist/api.mjs.map +0 -1
- package/dist/components-B6oaPB5f.mjs +0 -11769
- package/dist/components-B6oaPB5f.mjs.map +0 -1
- package/dist/components-BeAjVBV3.css +0 -70
- package/dist/components-BeAjVBV3.css.map +0 -1
- package/dist/components-DTLQ2djq.js +0 -14110
- package/dist/components-DTLQ2djq.js.map +0 -1
- package/dist/components-iUxcqtUB.css +0 -70
- package/dist/components-iUxcqtUB.css.map +0 -1
- package/dist/components.d.mts +0 -3
- package/dist/components.mjs +0 -9
- package/dist/const-CaIm1Z8g.mjs +0 -12
- package/dist/const-CaIm1Z8g.mjs.map +0 -1
- package/dist/const-CvdZO0FN.js +0 -24
- package/dist/const-CvdZO0FN.js.map +0 -1
- package/dist/hooks-cQsBXBd1.js +0 -40
- package/dist/hooks-cQsBXBd1.js.map +0 -1
- package/dist/hooks-eaHm_yEp.mjs +0 -33
- package/dist/hooks-eaHm_yEp.mjs.map +0 -1
- package/dist/hooks.d.mts +0 -3
- package/dist/hooks.mjs +0 -8
- package/dist/html2canvas-pro.esm-CsuSOHXp.js +0 -7250
- package/dist/html2canvas-pro.esm-CsuSOHXp.js.map +0 -1
- package/dist/html2canvas-pro.esm-E7kpobrC.mjs +0 -7249
- package/dist/html2canvas-pro.esm-E7kpobrC.mjs.map +0 -1
- package/dist/index-B9lSPJTi.d.ts +0 -2170
- package/dist/index-B9lSPJTi.d.ts.map +0 -1
- package/dist/index-CbF0x3kW.d.mts +0 -2172
- package/dist/index-CbF0x3kW.d.mts.map +0 -1
- package/dist/index.d.mts +0 -3
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -20
- package/dist/index.mjs.map +0 -1
- package/dist/mui-theme-CUhybmBq.mjs +0 -696
- package/dist/mui-theme-CUhybmBq.mjs.map +0 -1
- package/dist/mui-theme-iBHkjXJq.js +0 -732
- package/dist/mui-theme-iBHkjXJq.js.map +0 -1
- package/dist/state-CTITyT0R.js +0 -795
- package/dist/state-CTITyT0R.js.map +0 -1
- package/dist/state-Sc2b4jri.mjs +0 -382
- package/dist/state-Sc2b4jri.mjs.map +0 -1
- package/dist/theme.d.mts +0 -3
- package/dist/theme.mjs +0 -4
- package/dist/tooltipMessage-BC5W7H3X.js +0 -13
- package/dist/tooltipMessage-BC5W7H3X.js.map +0 -1
- package/dist/tooltipMessage-B_xMIKWL.mjs +0 -7
- package/dist/tooltipMessage-B_xMIKWL.mjs.map +0 -1
- package/dist/types.d.mts +0 -3
- package/dist/types.mjs +0 -6
- package/dist/urls-BQW5wjg-.js +0 -13
- package/dist/urls-BQW5wjg-.js.map +0 -1
- package/dist/urls-DT7FVEcS.mjs +0 -7
- package/dist/urls-DT7FVEcS.mjs.map +0 -1
- package/dist/version-B9s8yne-.js +0 -300
- package/dist/version-B9s8yne-.js.map +0 -1
- package/dist/version-DP1kU_7v.mjs +0 -162
- package/dist/version-DP1kU_7v.mjs.map +0 -1
- package/recce-source/.editorconfig +0 -26
- package/recce-source/.flake8 +0 -37
- package/recce-source/.github/ISSUE_TEMPLATE/bug_report.yml +0 -67
- package/recce-source/.github/ISSUE_TEMPLATE/custom.md +0 -10
- package/recce-source/.github/ISSUE_TEMPLATE/feature_request.yml +0 -42
- package/recce-source/.github/PULL_REQUEST_TEMPLATE.md +0 -21
- package/recce-source/.github/copilot-instructions.md +0 -331
- package/recce-source/.github/instructions/backend-instructions.md +0 -541
- package/recce-source/.github/instructions/frontend-instructions.md +0 -317
- package/recce-source/.github/workflows/build-statics.yaml +0 -72
- package/recce-source/.github/workflows/bump.yaml +0 -48
- package/recce-source/.github/workflows/integration-tests-cloud.yaml +0 -92
- package/recce-source/.github/workflows/integration-tests-sqlmesh.yaml +0 -33
- package/recce-source/.github/workflows/integration-tests.yaml +0 -52
- package/recce-source/.github/workflows/nightly.yaml +0 -246
- package/recce-source/.github/workflows/release.yaml +0 -196
- package/recce-source/.github/workflows/tests-js.yaml +0 -58
- package/recce-source/.github/workflows/tests-python.yaml +0 -128
- package/recce-source/.pre-commit-config.yaml +0 -26
- package/recce-source/CLAUDE.md +0 -483
- package/recce-source/CODE_OF_CONDUCT.md +0 -128
- package/recce-source/CONTRIBUTING.md +0 -107
- package/recce-source/LICENSE +0 -201
- package/recce-source/Makefile +0 -126
- package/recce-source/README.md +0 -182
- package/recce-source/RECCE_CLOUD.md +0 -81
- package/recce-source/SECURITY.md +0 -25
- package/recce-source/docs/PACKAGING.md +0 -340
- package/recce-source/docs/README.md +0 -1
- package/recce-source/integration_tests/dbt/dbt_project.yml +0 -26
- package/recce-source/integration_tests/dbt/models/customers.sql +0 -69
- package/recce-source/integration_tests/dbt/models/docs.md +0 -14
- package/recce-source/integration_tests/dbt/models/orders.sql +0 -56
- package/recce-source/integration_tests/dbt/models/schema.yml +0 -82
- package/recce-source/integration_tests/dbt/models/staging/schema.yml +0 -31
- package/recce-source/integration_tests/dbt/models/staging/stg_customers.sql +0 -22
- package/recce-source/integration_tests/dbt/models/staging/stg_orders.sql +0 -23
- package/recce-source/integration_tests/dbt/models/staging/stg_payments.sql +0 -25
- package/recce-source/integration_tests/dbt/packages.yml +0 -7
- package/recce-source/integration_tests/dbt/profiles.yml +0 -8
- package/recce-source/integration_tests/dbt/seeds/raw_customers.csv +0 -101
- package/recce-source/integration_tests/dbt/seeds/raw_orders.csv +0 -100
- package/recce-source/integration_tests/dbt/seeds/raw_payments.csv +0 -114
- package/recce-source/integration_tests/dbt/seeds/raw_statuses.csv +0 -5
- package/recce-source/integration_tests/dbt/smoke_test.sh +0 -72
- package/recce-source/integration_tests/dbt/smoke_test_cloud.sh +0 -71
- package/recce-source/integration_tests/sqlmesh/__init__.py +0 -0
- package/recce-source/integration_tests/sqlmesh/audits/assert_item_price_above_zero.sql +0 -9
- package/recce-source/integration_tests/sqlmesh/audits/items.sql +0 -7
- package/recce-source/integration_tests/sqlmesh/audits/order_items.sql +0 -7
- package/recce-source/integration_tests/sqlmesh/config.py +0 -171
- package/recce-source/integration_tests/sqlmesh/helper.py +0 -20
- package/recce-source/integration_tests/sqlmesh/hooks/__init__.py +0 -0
- package/recce-source/integration_tests/sqlmesh/macros/__init__.py +0 -0
- package/recce-source/integration_tests/sqlmesh/macros/macros.py +0 -8
- package/recce-source/integration_tests/sqlmesh/macros/macros.sql +0 -8
- package/recce-source/integration_tests/sqlmesh/macros/utils.py +0 -11
- package/recce-source/integration_tests/sqlmesh/metrics/metrics.sql +0 -25
- package/recce-source/integration_tests/sqlmesh/models/customer_revenue_by_day.sql +0 -41
- package/recce-source/integration_tests/sqlmesh/models/customer_revenue_lifetime.sql +0 -60
- package/recce-source/integration_tests/sqlmesh/models/customers.sql +0 -32
- package/recce-source/integration_tests/sqlmesh/models/items.py +0 -95
- package/recce-source/integration_tests/sqlmesh/models/marketing.sql +0 -15
- package/recce-source/integration_tests/sqlmesh/models/order_items.py +0 -95
- package/recce-source/integration_tests/sqlmesh/models/orders.py +0 -70
- package/recce-source/integration_tests/sqlmesh/models/raw_marketing.py +0 -62
- package/recce-source/integration_tests/sqlmesh/models/top_waiters.sql +0 -23
- package/recce-source/integration_tests/sqlmesh/models/waiter_as_customer_by_day.sql +0 -29
- package/recce-source/integration_tests/sqlmesh/models/waiter_names.sql +0 -10
- package/recce-source/integration_tests/sqlmesh/models/waiter_revenue_by_day.sql +0 -29
- package/recce-source/integration_tests/sqlmesh/models/waiters.py +0 -62
- package/recce-source/integration_tests/sqlmesh/prep_env.sh +0 -16
- package/recce-source/integration_tests/sqlmesh/schema.yaml +0 -5
- package/recce-source/integration_tests/sqlmesh/seeds/waiter_names.csv +0 -11
- package/recce-source/integration_tests/sqlmesh/test_server.sh +0 -29
- package/recce-source/integration_tests/sqlmesh/tests/test_customer_revenue_by_day.yaml +0 -63
- package/recce-source/integration_tests/sqlmesh/tests/test_order_items.yaml +0 -72
- package/recce-source/js/.editorconfig +0 -27
- package/recce-source/js/.env.development +0 -5
- package/recce-source/js/.husky/pre-commit +0 -29
- package/recce-source/js/.nvmrc +0 -1
- package/recce-source/js/README.md +0 -39
- package/recce-source/js/app/(mainComponents)/DisplayModeToggle.tsx +0 -65
- package/recce-source/js/app/(mainComponents)/NavBar.tsx +0 -228
- package/recce-source/js/app/(mainComponents)/RecceVersionBadge.tsx +0 -107
- package/recce-source/js/app/(mainComponents)/TopBar.tsx +0 -252
- package/recce-source/js/app/@lineage/default.tsx +0 -20
- package/recce-source/js/app/@lineage/page.tsx +0 -14
- package/recce-source/js/app/MainLayout.tsx +0 -170
- package/recce-source/js/app/Providers.tsx +0 -49
- package/recce-source/js/app/checks/page.tsx +0 -296
- package/recce-source/js/app/error.tsx +0 -93
- package/recce-source/js/app/favicon.ico +0 -0
- package/recce-source/js/app/global-error.tsx +0 -115
- package/recce-source/js/app/global.css +0 -82
- package/recce-source/js/app/layout.tsx +0 -48
- package/recce-source/js/app/lineage/page.tsx +0 -15
- package/recce-source/js/app/page.tsx +0 -12
- package/recce-source/js/app/query/page.tsx +0 -8
- package/recce-source/js/biome.json +0 -313
- package/recce-source/js/jest.config.js +0 -34
- package/recce-source/js/jest.globals.d.ts +0 -32
- package/recce-source/js/jest.setup.js +0 -91
- package/recce-source/js/next.config.js +0 -16
- package/recce-source/js/package-lock.json +0 -13843
- package/recce-source/js/package.json +0 -123
- package/recce-source/js/pnpm-lock.yaml +0 -9235
- package/recce-source/js/pnpm-workspace.yaml +0 -6
- package/recce-source/js/postcss.config.js +0 -5
- package/recce-source/js/public/auth_callback.html +0 -68
- package/recce-source/js/public/imgs/feedback/thumbs-down.png +0 -0
- package/recce-source/js/public/imgs/feedback/thumbs-up.png +0 -0
- package/recce-source/js/public/imgs/reload-image.svg +0 -4
- package/recce-source/js/public/logo/recce-logo-white.png +0 -0
- package/recce-source/js/src/components/AuthModal/AuthModal.tsx +0 -202
- package/recce-source/js/src/components/app/AvatarDropdown.tsx +0 -159
- package/recce-source/js/src/components/app/EnvInfo.tsx +0 -357
- package/recce-source/js/src/components/app/Filename.tsx +0 -388
- package/recce-source/js/src/components/app/SetupConnectionPopover.tsx +0 -91
- package/recce-source/js/src/components/app/StateExporter.tsx +0 -57
- package/recce-source/js/src/components/app/StateImporter.tsx +0 -198
- package/recce-source/js/src/components/app/StateSharing.tsx +0 -145
- package/recce-source/js/src/components/app/StateSynchronizer.tsx +0 -205
- package/recce-source/js/src/components/charts/HistogramChart.tsx +0 -291
- package/recce-source/js/src/components/charts/SquareIcon.tsx +0 -51
- package/recce-source/js/src/components/charts/TopKSummaryList.tsx +0 -457
- package/recce-source/js/src/components/charts/chartTheme.ts +0 -74
- package/recce-source/js/src/components/check/CheckBreadcrumb.tsx +0 -97
- package/recce-source/js/src/components/check/CheckDescription.tsx +0 -134
- package/recce-source/js/src/components/check/CheckDetail.tsx +0 -797
- package/recce-source/js/src/components/check/CheckEmptyState.tsx +0 -84
- package/recce-source/js/src/components/check/CheckList.tsx +0 -320
- package/recce-source/js/src/components/check/LineageDiffView.tsx +0 -32
- package/recce-source/js/src/components/check/PresetCheckTemplateView.tsx +0 -48
- package/recce-source/js/src/components/check/SchemaDiffView.tsx +0 -290
- package/recce-source/js/src/components/check/check.ts +0 -25
- package/recce-source/js/src/components/check/timeline/CheckTimeline.tsx +0 -163
- package/recce-source/js/src/components/check/timeline/CommentInput.tsx +0 -84
- package/recce-source/js/src/components/check/timeline/TimelineEvent.tsx +0 -468
- package/recce-source/js/src/components/check/timeline/index.ts +0 -12
- package/recce-source/js/src/components/check/utils.ts +0 -12
- package/recce-source/js/src/components/data-grid/ScreenshotDataGrid.tsx +0 -333
- package/recce-source/js/src/components/data-grid/agGridStyles.css +0 -55
- package/recce-source/js/src/components/data-grid/agGridTheme.ts +0 -43
- package/recce-source/js/src/components/editor/CodeEditor.tsx +0 -107
- package/recce-source/js/src/components/editor/DiffEditor.tsx +0 -162
- package/recce-source/js/src/components/editor/index.ts +0 -12
- package/recce-source/js/src/components/errorboundary/ErrorBoundary.tsx +0 -87
- package/recce-source/js/src/components/histogram/HistogramDiffForm.tsx +0 -147
- package/recce-source/js/src/components/histogram/HistogramDiffResultView.tsx +0 -63
- package/recce-source/js/src/components/icons/index.tsx +0 -142
- package/recce-source/js/src/components/lineage/ActionControl.tsx +0 -63
- package/recce-source/js/src/components/lineage/ActionTag.tsx +0 -141
- package/recce-source/js/src/components/lineage/ChangeStatusLegend.tsx +0 -46
- package/recce-source/js/src/components/lineage/ColumnLevelLineageControl.tsx +0 -327
- package/recce-source/js/src/components/lineage/ColumnLevelLineageLegend.tsx +0 -57
- package/recce-source/js/src/components/lineage/GraphColumnNode.tsx +0 -199
- package/recce-source/js/src/components/lineage/GraphEdge.tsx +0 -59
- package/recce-source/js/src/components/lineage/GraphNode.tsx +0 -555
- package/recce-source/js/src/components/lineage/LineagePage.tsx +0 -10
- package/recce-source/js/src/components/lineage/LineageView.tsx +0 -1384
- package/recce-source/js/src/components/lineage/LineageViewContext.tsx +0 -86
- package/recce-source/js/src/components/lineage/LineageViewContextMenu.tsx +0 -637
- package/recce-source/js/src/components/lineage/LineageViewNotification.tsx +0 -64
- package/recce-source/js/src/components/lineage/LineageViewTopBar.tsx +0 -596
- package/recce-source/js/src/components/lineage/NodeSqlView.tsx +0 -136
- package/recce-source/js/src/components/lineage/NodeTag.tsx +0 -278
- package/recce-source/js/src/components/lineage/NodeView.tsx +0 -642
- package/recce-source/js/src/components/lineage/SandboxView.tsx +0 -436
- package/recce-source/js/src/components/lineage/ServerDisconnectedModalContent.tsx +0 -105
- package/recce-source/js/src/components/lineage/SetupConnectionBanner.tsx +0 -52
- package/recce-source/js/src/components/lineage/SingleEnvironmentQueryView.tsx +0 -152
- package/recce-source/js/src/components/lineage/graph.test.ts +0 -31
- package/recce-source/js/src/components/lineage/graph.ts +0 -58
- package/recce-source/js/src/components/lineage/lineage.test.ts +0 -169
- package/recce-source/js/src/components/lineage/lineage.ts +0 -521
- package/recce-source/js/src/components/lineage/styles.css +0 -42
- package/recce-source/js/src/components/lineage/styles.tsx +0 -165
- package/recce-source/js/src/components/lineage/useMultiNodesAction.ts +0 -352
- package/recce-source/js/src/components/lineage/useValueDiffAlertDialog.tsx +0 -108
- package/recce-source/js/src/components/onboarding-guide/Notification.tsx +0 -62
- package/recce-source/js/src/components/profile/ProfileDiffForm.tsx +0 -134
- package/recce-source/js/src/components/profile/ProfileDiffResultView.tsx +0 -243
- package/recce-source/js/src/components/query/ChangedOnlyCheckbox.tsx +0 -29
- package/recce-source/js/src/components/query/DiffText.tsx +0 -120
- package/recce-source/js/src/components/query/QueryDiffResultView.tsx +0 -468
- package/recce-source/js/src/components/query/QueryForm.tsx +0 -80
- package/recce-source/js/src/components/query/QueryPage.tsx +0 -282
- package/recce-source/js/src/components/query/QueryResultView.tsx +0 -180
- package/recce-source/js/src/components/query/SetupConnectionGuide.tsx +0 -57
- package/recce-source/js/src/components/query/SqlEditor.tsx +0 -245
- package/recce-source/js/src/components/query/ToggleSwitch.tsx +0 -84
- package/recce-source/js/src/components/query/styles.css +0 -21
- package/recce-source/js/src/components/routing/DirectUrlAccess.test.tsx +0 -428
- package/recce-source/js/src/components/routing/LineageStatePreservation.test.tsx +0 -311
- package/recce-source/js/src/components/routing/Navigation.test.tsx +0 -256
- package/recce-source/js/src/components/rowcount/RowCountDiffResultView.tsx +0 -108
- package/recce-source/js/src/components/rowcount/delta.test.ts +0 -51
- package/recce-source/js/src/components/rowcount/delta.ts +0 -16
- package/recce-source/js/src/components/run/RunList.tsx +0 -303
- package/recce-source/js/src/components/run/RunModal.tsx +0 -191
- package/recce-source/js/src/components/run/RunPage.tsx +0 -26
- package/recce-source/js/src/components/run/RunResultPane.tsx +0 -454
- package/recce-source/js/src/components/run/RunStatusAndDate.tsx +0 -106
- package/recce-source/js/src/components/run/RunToolbar.tsx +0 -70
- package/recce-source/js/src/components/run/RunView.tsx +0 -196
- package/recce-source/js/src/components/run/registry.ts +0 -214
- package/recce-source/js/src/components/run/types.ts +0 -14
- package/recce-source/js/src/components/schema/ColumnNameCell.test.tsx +0 -169
- package/recce-source/js/src/components/schema/ColumnNameCell.tsx +0 -198
- package/recce-source/js/src/components/schema/SchemaView.tsx +0 -336
- package/recce-source/js/src/components/schema/schemaDiff.ts +0 -32
- package/recce-source/js/src/components/schema/style.css +0 -134
- package/recce-source/js/src/components/screenshot/ScreenshotBox.tsx +0 -39
- package/recce-source/js/src/components/shared/HistoryToggle.tsx +0 -35
- package/recce-source/js/src/components/split/Split.tsx +0 -40
- package/recce-source/js/src/components/split/styles.css +0 -24
- package/recce-source/js/src/components/summary/ChangeSummary.tsx +0 -264
- package/recce-source/js/src/components/summary/SchemaSummary.tsx +0 -123
- package/recce-source/js/src/components/summary/SummaryView.tsx +0 -29
- package/recce-source/js/src/components/timeout/IdleTimeoutBadge.tsx +0 -48
- package/recce-source/js/src/components/top-k/TopKDiffForm.tsx +0 -58
- package/recce-source/js/src/components/top-k/TopKDiffResultView.tsx +0 -73
- package/recce-source/js/src/components/ui/dataGrid/DataFrameColumnGroupHeader.tsx +0 -228
- package/recce-source/js/src/components/ui/dataGrid/DataFrameColumnHeader.tsx +0 -113
- package/recce-source/js/src/components/ui/dataGrid/defaultRenderCell.tsx +0 -72
- package/recce-source/js/src/components/ui/dataGrid/index.ts +0 -23
- package/recce-source/js/src/components/ui/dataGrid/inlineRenderCell.test.tsx +0 -607
- package/recce-source/js/src/components/ui/dataGrid/inlineRenderCell.tsx +0 -211
- package/recce-source/js/src/components/ui/dataGrid/schemaCells.test.tsx +0 -452
- package/recce-source/js/src/components/ui/dataGrid/schemaCells.tsx +0 -142
- package/recce-source/js/src/components/ui/dataGrid/valueDiffCells.test.tsx +0 -178
- package/recce-source/js/src/components/ui/dataGrid/valueDiffCells.tsx +0 -275
- package/recce-source/js/src/components/ui/markdown/ExternalLinkConfirmDialog.tsx +0 -134
- package/recce-source/js/src/components/ui/markdown/MarkdownContent.tsx +0 -364
- package/recce-source/js/src/components/ui/mui/index.ts +0 -13
- package/recce-source/js/src/components/ui/mui-provider.tsx +0 -67
- package/recce-source/js/src/components/ui/mui-theme.ts +0 -1039
- package/recce-source/js/src/components/ui/mui-utils.ts +0 -113
- package/recce-source/js/src/components/ui/toaster.tsx +0 -288
- package/recce-source/js/src/components/valuediff/ValueDiffDetailResultView.tsx +0 -216
- package/recce-source/js/src/components/valuediff/ValueDiffForm.tsx +0 -246
- package/recce-source/js/src/components/valuediff/ValueDiffResultView.tsx +0 -81
- package/recce-source/js/src/components/valuediff/shared.ts +0 -33
- package/recce-source/js/src/constants/tooltipMessage.ts +0 -3
- package/recce-source/js/src/constants/urls.ts +0 -1
- package/recce-source/js/src/lib/UrlHash.ts +0 -12
- package/recce-source/js/src/lib/api/adhocQuery.ts +0 -70
- package/recce-source/js/src/lib/api/axiosClient.ts +0 -9
- package/recce-source/js/src/lib/api/cacheKeys.ts +0 -13
- package/recce-source/js/src/lib/api/checkEvents.ts +0 -252
- package/recce-source/js/src/lib/api/checks.ts +0 -129
- package/recce-source/js/src/lib/api/cll.ts +0 -53
- package/recce-source/js/src/lib/api/connectToCloud.ts +0 -13
- package/recce-source/js/src/lib/api/flag.ts +0 -37
- package/recce-source/js/src/lib/api/info.ts +0 -198
- package/recce-source/js/src/lib/api/instanceInfo.ts +0 -25
- package/recce-source/js/src/lib/api/keepAlive.ts +0 -108
- package/recce-source/js/src/lib/api/lineagecheck.ts +0 -35
- package/recce-source/js/src/lib/api/localStorageKeys.ts +0 -7
- package/recce-source/js/src/lib/api/models.ts +0 -59
- package/recce-source/js/src/lib/api/profile.ts +0 -65
- package/recce-source/js/src/lib/api/rowcount.ts +0 -19
- package/recce-source/js/src/lib/api/runs.ts +0 -174
- package/recce-source/js/src/lib/api/schemacheck.ts +0 -31
- package/recce-source/js/src/lib/api/select.ts +0 -25
- package/recce-source/js/src/lib/api/sessionStorageKeys.ts +0 -8
- package/recce-source/js/src/lib/api/state.ts +0 -117
- package/recce-source/js/src/lib/api/track.ts +0 -281
- package/recce-source/js/src/lib/api/types.ts +0 -284
- package/recce-source/js/src/lib/api/user.ts +0 -42
- package/recce-source/js/src/lib/api/valuediff.ts +0 -46
- package/recce-source/js/src/lib/api/version.ts +0 -40
- package/recce-source/js/src/lib/const.ts +0 -9
- package/recce-source/js/src/lib/dataGrid/crossFunctionConsistency.test.ts +0 -626
- package/recce-source/js/src/lib/dataGrid/dataGridFactory.test.ts +0 -2140
- package/recce-source/js/src/lib/dataGrid/dataGridFactory.ts +0 -397
- package/recce-source/js/src/lib/dataGrid/generators/rowCountUtils.test.ts +0 -132
- package/recce-source/js/src/lib/dataGrid/generators/rowCountUtils.ts +0 -126
- package/recce-source/js/src/lib/dataGrid/generators/toDataDiffGrid.test.ts +0 -1627
- package/recce-source/js/src/lib/dataGrid/generators/toDataDiffGrid.ts +0 -140
- package/recce-source/js/src/lib/dataGrid/generators/toDataGrid.ts +0 -67
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDataGrid.test.ts +0 -142
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDataGrid.ts +0 -71
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDiffDataGrid.test.ts +0 -258
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDiffDataGrid.ts +0 -153
- package/recce-source/js/src/lib/dataGrid/generators/toSchemaDataGrid.test.ts +0 -951
- package/recce-source/js/src/lib/dataGrid/generators/toSchemaDataGrid.ts +0 -221
- package/recce-source/js/src/lib/dataGrid/generators/toValueDataGrid.test.ts +0 -395
- package/recce-source/js/src/lib/dataGrid/generators/toValueDataGrid.ts +0 -184
- package/recce-source/js/src/lib/dataGrid/generators/toValueDiffGrid.test.ts +0 -884
- package/recce-source/js/src/lib/dataGrid/generators/toValueDiffGrid.ts +0 -113
- package/recce-source/js/src/lib/dataGrid/index.ts +0 -51
- package/recce-source/js/src/lib/dataGrid/propertyBased.test.ts +0 -858
- package/recce-source/js/src/lib/dataGrid/shared/columnBuilders.test.ts +0 -482
- package/recce-source/js/src/lib/dataGrid/shared/columnBuilders.ts +0 -345
- package/recce-source/js/src/lib/dataGrid/shared/dataTypeEdgeCases.test.ts +0 -698
- package/recce-source/js/src/lib/dataGrid/shared/diffColumnBuilder.test.tsx +0 -820
- package/recce-source/js/src/lib/dataGrid/shared/diffColumnBuilder.tsx +0 -277
- package/recce-source/js/src/lib/dataGrid/shared/gridUtils.test.ts +0 -785
- package/recce-source/js/src/lib/dataGrid/shared/gridUtils.ts +0 -370
- package/recce-source/js/src/lib/dataGrid/shared/index.ts +0 -81
- package/recce-source/js/src/lib/dataGrid/shared/rowBuilders.test.ts +0 -909
- package/recce-source/js/src/lib/dataGrid/shared/rowBuilders.ts +0 -325
- package/recce-source/js/src/lib/dataGrid/shared/simpleColumnBuilder.tsx +0 -240
- package/recce-source/js/src/lib/dataGrid/shared/toDiffColumn.test.tsx +0 -719
- package/recce-source/js/src/lib/dataGrid/shared/toDiffColumn.tsx +0 -231
- package/recce-source/js/src/lib/dataGrid/shared/validation.test.ts +0 -559
- package/recce-source/js/src/lib/dataGrid/shared/validation.ts +0 -367
- package/recce-source/js/src/lib/dataGrid/warehouseNamingConventions.test.ts +0 -1117
- package/recce-source/js/src/lib/formatSelect.ts +0 -50
- package/recce-source/js/src/lib/hooks/ApiConfigContext.tsx +0 -181
- package/recce-source/js/src/lib/hooks/IdleTimeoutContext.tsx +0 -177
- package/recce-source/js/src/lib/hooks/LineageGraphContext.tsx +0 -512
- package/recce-source/js/src/lib/hooks/RecceActionContext.tsx +0 -269
- package/recce-source/js/src/lib/hooks/RecceCheckContext.tsx +0 -33
- package/recce-source/js/src/lib/hooks/RecceContextProvider.tsx +0 -54
- package/recce-source/js/src/lib/hooks/RecceInstanceContext.tsx +0 -129
- package/recce-source/js/src/lib/hooks/RecceQueryContext.tsx +0 -98
- package/recce-source/js/src/lib/hooks/RecceShareStateContext.tsx +0 -59
- package/recce-source/js/src/lib/hooks/ScreenShot.tsx +0 -399
- package/recce-source/js/src/lib/hooks/useAppRouter.test.ts +0 -211
- package/recce-source/js/src/lib/hooks/useAppRouter.ts +0 -200
- package/recce-source/js/src/lib/hooks/useCheckEvents.ts +0 -99
- package/recce-source/js/src/lib/hooks/useCheckToast.tsx +0 -14
- package/recce-source/js/src/lib/hooks/useClipBoardToast.tsx +0 -27
- package/recce-source/js/src/lib/hooks/useCountdownToast.tsx +0 -102
- package/recce-source/js/src/lib/hooks/useFeedbackCollectionToast.tsx +0 -130
- package/recce-source/js/src/lib/hooks/useGuideToast.tsx +0 -45
- package/recce-source/js/src/lib/hooks/useIdleDetection.tsx +0 -185
- package/recce-source/js/src/lib/hooks/useModelColumns.tsx +0 -113
- package/recce-source/js/src/lib/hooks/useRecceInstanceInfo.tsx +0 -13
- package/recce-source/js/src/lib/hooks/useRecceServerFlag.tsx +0 -13
- package/recce-source/js/src/lib/hooks/useRun.tsx +0 -89
- package/recce-source/js/src/lib/hooks/useThemeColors.ts +0 -115
- package/recce-source/js/src/lib/mergeKeys.test.ts +0 -89
- package/recce-source/js/src/lib/mergeKeys.ts +0 -86
- package/recce-source/js/src/lib/result/ResultErrorFallback.tsx +0 -9
- package/recce-source/js/src/lib/utils/formatTime.ts +0 -84
- package/recce-source/js/src/lib/utils/urls.ts +0 -16
- package/recce-source/js/src/utils/DropdownValuesInput.tsx +0 -297
- package/recce-source/js/src/utils/formatters.tsx +0 -237
- package/recce-source/js/src/utils/transforms.ts +0 -81
- package/recce-source/js/tsconfig.json +0 -47
- package/recce-source/macros/README.md +0 -8
- package/recce-source/macros/recce_athena.sql +0 -73
- package/recce-source/pyproject.toml +0 -109
- package/recce-source/recce/VERSION +0 -1
- package/recce-source/recce/__init__.py +0 -84
- package/recce-source/recce/adapter/__init__.py +0 -0
- package/recce-source/recce/adapter/base.py +0 -109
- package/recce-source/recce/adapter/dbt_adapter/__init__.py +0 -1699
- package/recce-source/recce/adapter/dbt_adapter/dbt_version.py +0 -42
- package/recce-source/recce/adapter/sqlmesh_adapter.py +0 -141
- package/recce-source/recce/apis/__init__.py +0 -0
- package/recce-source/recce/apis/check_api.py +0 -203
- package/recce-source/recce/apis/check_events_api.py +0 -353
- package/recce-source/recce/apis/check_func.py +0 -130
- package/recce-source/recce/apis/run_api.py +0 -130
- package/recce-source/recce/apis/run_func.py +0 -258
- package/recce-source/recce/artifact.py +0 -266
- package/recce-source/recce/cli.py +0 -1846
- package/recce-source/recce/config.py +0 -127
- package/recce-source/recce/connect_to_cloud.py +0 -138
- package/recce-source/recce/core.py +0 -334
- package/recce-source/recce/diff.py +0 -26
- package/recce-source/recce/event/CONFIG +0 -1
- package/recce-source/recce/event/SENTRY_DNS +0 -1
- package/recce-source/recce/event/__init__.py +0 -304
- package/recce-source/recce/event/collector.py +0 -184
- package/recce-source/recce/event/track.py +0 -158
- package/recce-source/recce/exceptions.py +0 -21
- package/recce-source/recce/git.py +0 -77
- package/recce-source/recce/github.py +0 -222
- package/recce-source/recce/mcp_server.py +0 -861
- package/recce-source/recce/models/__init__.py +0 -6
- package/recce-source/recce/models/check.py +0 -473
- package/recce-source/recce/models/run.py +0 -46
- package/recce-source/recce/models/types.py +0 -218
- package/recce-source/recce/pull_request.py +0 -124
- package/recce-source/recce/run.py +0 -390
- package/recce-source/recce/server.py +0 -877
- package/recce-source/recce/state/__init__.py +0 -31
- package/recce-source/recce/state/cloud.py +0 -644
- package/recce-source/recce/state/const.py +0 -26
- package/recce-source/recce/state/local.py +0 -56
- package/recce-source/recce/state/state.py +0 -119
- package/recce-source/recce/state/state_loader.py +0 -174
- package/recce-source/recce/summary.py +0 -575
- package/recce-source/recce/tasks/__init__.py +0 -23
- package/recce-source/recce/tasks/core.py +0 -134
- package/recce-source/recce/tasks/dataframe.py +0 -170
- package/recce-source/recce/tasks/histogram.py +0 -433
- package/recce-source/recce/tasks/lineage.py +0 -19
- package/recce-source/recce/tasks/profile.py +0 -298
- package/recce-source/recce/tasks/query.py +0 -450
- package/recce-source/recce/tasks/rowcount.py +0 -277
- package/recce-source/recce/tasks/schema.py +0 -65
- package/recce-source/recce/tasks/top_k.py +0 -172
- package/recce-source/recce/tasks/utils.py +0 -147
- package/recce-source/recce/tasks/valuediff.py +0 -497
- package/recce-source/recce/util/__init__.py +0 -4
- package/recce-source/recce/util/api_token.py +0 -80
- package/recce-source/recce/util/breaking.py +0 -330
- package/recce-source/recce/util/cache.py +0 -25
- package/recce-source/recce/util/cll.py +0 -355
- package/recce-source/recce/util/cloud/__init__.py +0 -15
- package/recce-source/recce/util/cloud/base.py +0 -115
- package/recce-source/recce/util/cloud/check_events.py +0 -190
- package/recce-source/recce/util/cloud/checks.py +0 -242
- package/recce-source/recce/util/io.py +0 -120
- package/recce-source/recce/util/lineage.py +0 -83
- package/recce-source/recce/util/logger.py +0 -25
- package/recce-source/recce/util/onboarding_state.py +0 -45
- package/recce-source/recce/util/perf_tracking.py +0 -85
- package/recce-source/recce/util/pydantic_model.py +0 -22
- package/recce-source/recce/util/recce_cloud.py +0 -454
- package/recce-source/recce/util/singleton.py +0 -18
- package/recce-source/recce/util/startup_perf.py +0 -121
- package/recce-source/recce/yaml/__init__.py +0 -58
- package/recce-source/recce_cloud/README.md +0 -780
- package/recce-source/recce_cloud/VERSION +0 -1
- package/recce-source/recce_cloud/__init__.py +0 -24
- package/recce-source/recce_cloud/api/__init__.py +0 -17
- package/recce-source/recce_cloud/api/base.py +0 -132
- package/recce-source/recce_cloud/api/client.py +0 -186
- package/recce-source/recce_cloud/api/exceptions.py +0 -26
- package/recce-source/recce_cloud/api/factory.py +0 -63
- package/recce-source/recce_cloud/api/github.py +0 -106
- package/recce-source/recce_cloud/api/gitlab.py +0 -111
- package/recce-source/recce_cloud/artifact.py +0 -57
- package/recce-source/recce_cloud/ci_providers/__init__.py +0 -9
- package/recce-source/recce_cloud/ci_providers/base.py +0 -82
- package/recce-source/recce_cloud/ci_providers/detector.py +0 -147
- package/recce-source/recce_cloud/ci_providers/github_actions.py +0 -136
- package/recce-source/recce_cloud/ci_providers/gitlab_ci.py +0 -130
- package/recce-source/recce_cloud/cli.py +0 -434
- package/recce-source/recce_cloud/download.py +0 -230
- package/recce-source/recce_cloud/hatch_build.py +0 -20
- package/recce-source/recce_cloud/pyproject.toml +0 -49
- package/recce-source/recce_cloud/upload.py +0 -214
- package/recce-source/test.py +0 -0
- package/recce-source/tests/__init__.py +0 -0
- package/recce-source/tests/adapter/__init__.py +0 -0
- package/recce-source/tests/adapter/dbt_adapter/__init__.py +0 -0
- package/recce-source/tests/adapter/dbt_adapter/conftest.py +0 -17
- package/recce-source/tests/adapter/dbt_adapter/dbt_test_helper.py +0 -298
- package/recce-source/tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -25
- package/recce-source/tests/adapter/dbt_adapter/test_dbt_cll.py +0 -717
- package/recce-source/tests/adapter/dbt_adapter/test_proj/dbt_project.yml +0 -4
- package/recce-source/tests/adapter/dbt_adapter/test_proj/manifest.json +0 -1
- package/recce-source/tests/adapter/dbt_adapter/test_proj/package-lock.yml +0 -8
- package/recce-source/tests/adapter/dbt_adapter/test_proj/packages.yml +0 -7
- package/recce-source/tests/adapter/dbt_adapter/test_proj/profiles.yml +0 -6
- package/recce-source/tests/adapter/dbt_adapter/test_selector.py +0 -205
- package/recce-source/tests/apis/__init__.py +0 -0
- package/recce-source/tests/apis/row_count_diff.json +0 -59
- package/recce-source/tests/apis/test_check_events_api.py +0 -615
- package/recce-source/tests/apis/test_run_func.py +0 -433
- package/recce-source/tests/catalog.json +0 -527
- package/recce-source/tests/data/manifest/base/catalog.json +0 -1
- package/recce-source/tests/data/manifest/base/manifest.json +0 -1
- package/recce-source/tests/data/manifest/pr2/catalog.json +0 -1
- package/recce-source/tests/data/manifest/pr2/manifest.json +0 -1
- package/recce-source/tests/manifest.json +0 -10655
- package/recce-source/tests/models/__init__.py +0 -0
- package/recce-source/tests/models/test_check.py +0 -731
- package/recce-source/tests/models/test_run_models.py +0 -295
- package/recce-source/tests/recce_cloud/__init__.py +0 -0
- package/recce-source/tests/recce_cloud/test_ci_providers.py +0 -351
- package/recce-source/tests/recce_cloud/test_cli.py +0 -735
- package/recce-source/tests/recce_cloud/test_client.py +0 -379
- package/recce-source/tests/recce_cloud/test_platform_clients.py +0 -483
- package/recce-source/tests/recce_state.json +0 -1
- package/recce-source/tests/state/test_cloud.py +0 -719
- package/recce-source/tests/state/test_local.py +0 -164
- package/recce-source/tests/state/test_state_loader.py +0 -211
- package/recce-source/tests/tasks/__init__.py +0 -0
- package/recce-source/tests/tasks/conftest.py +0 -4
- package/recce-source/tests/tasks/test_histogram.py +0 -129
- package/recce-source/tests/tasks/test_lineage.py +0 -55
- package/recce-source/tests/tasks/test_preset_checks.py +0 -64
- package/recce-source/tests/tasks/test_profile.py +0 -397
- package/recce-source/tests/tasks/test_query.py +0 -528
- package/recce-source/tests/tasks/test_row_count.py +0 -133
- package/recce-source/tests/tasks/test_schema.py +0 -122
- package/recce-source/tests/tasks/test_top_k.py +0 -77
- package/recce-source/tests/tasks/test_utils.py +0 -439
- package/recce-source/tests/tasks/test_valuediff.py +0 -361
- package/recce-source/tests/test_cli.py +0 -236
- package/recce-source/tests/test_cli_mcp_optional.py +0 -45
- package/recce-source/tests/test_cloud_listing_cli.py +0 -324
- package/recce-source/tests/test_config.py +0 -43
- package/recce-source/tests/test_connect_to_cloud.py +0 -82
- package/recce-source/tests/test_core.py +0 -174
- package/recce-source/tests/test_dbt.py +0 -36
- package/recce-source/tests/test_mcp_server.py +0 -505
- package/recce-source/tests/test_pull_request.py +0 -130
- package/recce-source/tests/test_server.py +0 -202
- package/recce-source/tests/test_server_lifespan.py +0 -138
- package/recce-source/tests/test_summary.py +0 -73
- package/recce-source/tests/util/__init__.py +0 -0
- package/recce-source/tests/util/cloud/__init__.py +0 -0
- package/recce-source/tests/util/cloud/test_check_events.py +0 -255
- package/recce-source/tests/util/cloud/test_checks.py +0 -204
- package/recce-source/tests/util/test_api_token.py +0 -119
- package/recce-source/tests/util/test_breaking.py +0 -1427
- package/recce-source/tests/util/test_cll.py +0 -706
- package/recce-source/tests/util/test_lineage.py +0 -122
- package/recce-source/tests/util/test_onboarding_state.py +0 -84
- package/recce-source/tests/util/test_recce_cloud.py +0 -231
- package/recce-source/tox.ini +0 -40
- package/recce-source/uv.lock +0 -3928
- package/src/api/index.ts +0 -32
- package/src/components/index.ts +0 -154
- package/src/global.d.ts +0 -14
- package/src/hooks/index.ts +0 -56
- package/src/index.ts +0 -17
- package/src/lib/hooks/RouteConfigContext.ts +0 -139
- package/src/lib/hooks/useAppRouter.ts +0 -240
- package/src/mui-augmentation.d.ts +0 -139
- package/src/theme/index.ts +0 -13
- package/src/theme.ts +0 -23
- package/src/types/index.ts +0 -23
|
@@ -1,1699 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
import uuid
|
|
5
|
-
from contextlib import contextmanager
|
|
6
|
-
from copy import deepcopy
|
|
7
|
-
from dataclasses import dataclass, fields
|
|
8
|
-
from errno import ENOENT
|
|
9
|
-
from functools import lru_cache
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import (
|
|
12
|
-
Any,
|
|
13
|
-
Callable,
|
|
14
|
-
Dict,
|
|
15
|
-
Iterator,
|
|
16
|
-
List,
|
|
17
|
-
Literal,
|
|
18
|
-
Optional,
|
|
19
|
-
Set,
|
|
20
|
-
Tuple,
|
|
21
|
-
Type,
|
|
22
|
-
Union,
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
from recce.event import log_performance
|
|
26
|
-
from recce.exceptions import RecceException
|
|
27
|
-
from recce.util.cll import CLLPerformanceTracking, cll
|
|
28
|
-
from recce.util.lineage import (
|
|
29
|
-
build_column_key,
|
|
30
|
-
filter_dependency_maps,
|
|
31
|
-
find_downstream,
|
|
32
|
-
find_upstream,
|
|
33
|
-
)
|
|
34
|
-
from recce.util.perf_tracking import LineagePerfTracker
|
|
35
|
-
from recce.util.startup_perf import track_timing
|
|
36
|
-
|
|
37
|
-
from ...tasks.profile import ProfileTask
|
|
38
|
-
from ...util.breaking import BreakingPerformanceTracking, parse_change_category
|
|
39
|
-
|
|
40
|
-
try:
|
|
41
|
-
import agate
|
|
42
|
-
import dbt.adapters.factory
|
|
43
|
-
from dbt.contracts.state import PreviousState
|
|
44
|
-
except ImportError as e:
|
|
45
|
-
print("Error: dbt module not found. Please install it by running:")
|
|
46
|
-
print("pip install dbt-core dbt-<adapter>")
|
|
47
|
-
raise e
|
|
48
|
-
from watchdog.events import FileSystemEventHandler
|
|
49
|
-
from watchdog.observers import Observer
|
|
50
|
-
|
|
51
|
-
from recce.adapter.base import BaseAdapter
|
|
52
|
-
from recce.state import ArtifactsRoot
|
|
53
|
-
|
|
54
|
-
from ...models import RunType
|
|
55
|
-
from ...models.types import (
|
|
56
|
-
CllColumn,
|
|
57
|
-
CllData,
|
|
58
|
-
CllNode,
|
|
59
|
-
LineageDiff,
|
|
60
|
-
NodeChange,
|
|
61
|
-
NodeDiff,
|
|
62
|
-
)
|
|
63
|
-
from ...tasks import (
|
|
64
|
-
HistogramDiffTask,
|
|
65
|
-
ProfileDiffTask,
|
|
66
|
-
QueryBaseTask,
|
|
67
|
-
QueryDiffTask,
|
|
68
|
-
QueryTask,
|
|
69
|
-
RowCountDiffTask,
|
|
70
|
-
RowCountTask,
|
|
71
|
-
Task,
|
|
72
|
-
TopKDiffTask,
|
|
73
|
-
ValueDiffDetailTask,
|
|
74
|
-
ValueDiffTask,
|
|
75
|
-
)
|
|
76
|
-
from .dbt_version import DbtVersion
|
|
77
|
-
|
|
78
|
-
dbt_supported_registry: Dict[RunType, Type[Task]] = {
|
|
79
|
-
RunType.QUERY: QueryTask,
|
|
80
|
-
RunType.QUERY_BASE: QueryBaseTask,
|
|
81
|
-
RunType.QUERY_DIFF: QueryDiffTask,
|
|
82
|
-
RunType.VALUE_DIFF: ValueDiffTask,
|
|
83
|
-
RunType.VALUE_DIFF_DETAIL: ValueDiffDetailTask,
|
|
84
|
-
RunType.PROFILE: ProfileTask,
|
|
85
|
-
RunType.PROFILE_DIFF: ProfileDiffTask,
|
|
86
|
-
RunType.ROW_COUNT: RowCountTask,
|
|
87
|
-
RunType.ROW_COUNT_DIFF: RowCountDiffTask,
|
|
88
|
-
RunType.TOP_K_DIFF: TopKDiffTask,
|
|
89
|
-
RunType.HISTOGRAM_DIFF: HistogramDiffTask,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
# Reference: https://github.com/AltimateAI/vscode-dbt-power-user/blob/master/dbt_core_integration.py
|
|
93
|
-
|
|
94
|
-
get_adapter_orig = dbt.adapters.factory.get_adapter
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def get_adapter(config):
|
|
98
|
-
if hasattr(config, "adapter"):
|
|
99
|
-
return config.adapter
|
|
100
|
-
else:
|
|
101
|
-
return get_adapter_orig(config)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
dbt.adapters.factory.get_adapter = get_adapter
|
|
105
|
-
|
|
106
|
-
# All dbt import should after overwriting the get_adapter
|
|
107
|
-
from dbt.adapters.base import Column # noqa: E402
|
|
108
|
-
from dbt.adapters.factory import get_adapter_class_by_name # noqa: E402
|
|
109
|
-
from dbt.adapters.sql import SQLAdapter # noqa: E402
|
|
110
|
-
from dbt.config.runtime import RuntimeConfig # noqa: E402
|
|
111
|
-
from dbt.contracts.graph.manifest import ( # noqa: E402
|
|
112
|
-
MacroManifest,
|
|
113
|
-
Manifest,
|
|
114
|
-
ManifestMetadata,
|
|
115
|
-
WritableManifest,
|
|
116
|
-
)
|
|
117
|
-
from dbt.contracts.graph.nodes import ManifestNode # noqa: E402
|
|
118
|
-
from dbt.contracts.results import CatalogArtifact # noqa: E402
|
|
119
|
-
from dbt.flags import set_from_args # noqa: E402
|
|
120
|
-
from dbt.parser.manifest import process_node # noqa: E402
|
|
121
|
-
from dbt.parser.sql import SqlBlockParser # noqa: E402
|
|
122
|
-
|
|
123
|
-
dbt_version = DbtVersion()
|
|
124
|
-
|
|
125
|
-
if dbt_version < "v1.8":
|
|
126
|
-
from dbt.contracts.connection import Connection
|
|
127
|
-
else:
|
|
128
|
-
from dbt.adapters.contracts.connection import Connection
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
@contextmanager
|
|
132
|
-
def silence_no_nodes_warning():
|
|
133
|
-
if dbt_version >= "v1.8":
|
|
134
|
-
from dbt.events.types import NoNodesForSelectionCriteria
|
|
135
|
-
from dbt_common.events.functions import WARN_ERROR_OPTIONS
|
|
136
|
-
|
|
137
|
-
WARN_ERROR_OPTIONS.silence.append(NoNodesForSelectionCriteria.__name__)
|
|
138
|
-
try:
|
|
139
|
-
yield
|
|
140
|
-
finally:
|
|
141
|
-
if dbt_version >= "v1.8":
|
|
142
|
-
from dbt_common.events.functions import WARN_ERROR_OPTIONS
|
|
143
|
-
|
|
144
|
-
WARN_ERROR_OPTIONS.silence.pop()
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
logger = logging.getLogger("uvicorn")
|
|
148
|
-
MIN_DBT_NODE_COMPOSITION = 3
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
class ArtifactsEventHandler(FileSystemEventHandler):
|
|
152
|
-
def __init__(self, watch_files: List[str], callback: Callable = None):
|
|
153
|
-
super().__init__()
|
|
154
|
-
self.watch_files = watch_files
|
|
155
|
-
self.callback = callback
|
|
156
|
-
|
|
157
|
-
def on_modified(self, event):
|
|
158
|
-
if event.is_directory:
|
|
159
|
-
return None
|
|
160
|
-
|
|
161
|
-
if event.src_path in self.watch_files:
|
|
162
|
-
if callable(self.callback):
|
|
163
|
-
self.callback(event)
|
|
164
|
-
|
|
165
|
-
def on_created(self, event):
|
|
166
|
-
if event.is_directory:
|
|
167
|
-
return None
|
|
168
|
-
|
|
169
|
-
if event.src_path in self.watch_files:
|
|
170
|
-
if callable(self.callback):
|
|
171
|
-
self.callback(event)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
class EnvironmentEventHandler(FileSystemEventHandler):
|
|
175
|
-
def __init__(self, observer, watch_files: set[str], callback: Callable = None):
|
|
176
|
-
super().__init__()
|
|
177
|
-
self.observer = observer
|
|
178
|
-
self.watch_files = watch_files
|
|
179
|
-
self.detected_files = set()
|
|
180
|
-
self.callback = callback
|
|
181
|
-
|
|
182
|
-
def on_created(self, event):
|
|
183
|
-
if event.is_directory:
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
if event.src_path in self.watch_files:
|
|
187
|
-
self.detected_files.add(event.src_path)
|
|
188
|
-
|
|
189
|
-
# Check if all target-base files are created
|
|
190
|
-
if self.detected_files == self.watch_files:
|
|
191
|
-
if callable(self.callback):
|
|
192
|
-
self.callback()
|
|
193
|
-
self.observer.stop()
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def merge_tables(tables: List[agate.Table]) -> agate.Table:
|
|
197
|
-
if dbt_version < "v1.8":
|
|
198
|
-
from dbt.clients.agate_helper import merge_tables
|
|
199
|
-
|
|
200
|
-
return merge_tables(tables)
|
|
201
|
-
else:
|
|
202
|
-
from dbt_common.clients.agate_helper import merge_tables
|
|
203
|
-
|
|
204
|
-
return merge_tables(tables)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
def as_manifest(m: WritableManifest) -> Manifest:
|
|
208
|
-
if dbt_version < "v1.8":
|
|
209
|
-
data = m.__dict__
|
|
210
|
-
all_fields = set([x.name for x in fields(Manifest)])
|
|
211
|
-
new_data = {k: v for k, v in data.items() if k in all_fields}
|
|
212
|
-
return Manifest(**new_data)
|
|
213
|
-
else:
|
|
214
|
-
result = Manifest.from_writable_manifest(m)
|
|
215
|
-
result.metadata = ManifestMetadata(**m.metadata.__dict__)
|
|
216
|
-
return result
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
@track_timing(record_size=True)
|
|
220
|
-
def load_manifest(path: str = None, data: dict = None):
|
|
221
|
-
if path is not None:
|
|
222
|
-
if not os.path.isfile(path):
|
|
223
|
-
return None
|
|
224
|
-
return WritableManifest.read_and_check_versions(path)
|
|
225
|
-
if data is not None:
|
|
226
|
-
return WritableManifest.upgrade_schema_version(data)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@track_timing(record_size=True)
|
|
230
|
-
def load_catalog(path: str = None, data: dict = None):
|
|
231
|
-
if path is not None:
|
|
232
|
-
if not os.path.isfile(path):
|
|
233
|
-
return None
|
|
234
|
-
return CatalogArtifact.read_and_check_versions(path)
|
|
235
|
-
if data is not None:
|
|
236
|
-
return CatalogArtifact.upgrade_schema_version(data)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def previous_state(state_path: Path, target_path: Path, project_root: Path) -> PreviousState:
|
|
240
|
-
if dbt_version < "v1.5.2":
|
|
241
|
-
return PreviousState(state_path, target_path)
|
|
242
|
-
else:
|
|
243
|
-
try:
|
|
244
|
-
# Overwrite the level_tag method temporarily to avoid the warning message
|
|
245
|
-
from dbt.events.types import EventLevel, WarnStateTargetEqual
|
|
246
|
-
|
|
247
|
-
original_level_tag_func = WarnStateTargetEqual.level_tag
|
|
248
|
-
WarnStateTargetEqual.level_tag = lambda x: EventLevel.DEBUG
|
|
249
|
-
except ImportError:
|
|
250
|
-
# Skip overwriting the level_tag method if the dbt version not support
|
|
251
|
-
original_level_tag_func = None
|
|
252
|
-
pass
|
|
253
|
-
|
|
254
|
-
state = PreviousState(state_path, target_path, project_root)
|
|
255
|
-
|
|
256
|
-
if original_level_tag_func is not None:
|
|
257
|
-
# Restore the original level_tag method
|
|
258
|
-
WarnStateTargetEqual.level_tag = original_level_tag_func
|
|
259
|
-
|
|
260
|
-
return state
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def default_profiles_dir():
|
|
264
|
-
# Precedence: DBT_PROFILES_DIR > current working directory > ~/.dbt/
|
|
265
|
-
# https://docs.getdbt.com/docs/core/connect-data-platform/connection-profiles#advanced-customizing-a-profile-directory
|
|
266
|
-
if os.getenv("DBT_PROFILES_DIR"):
|
|
267
|
-
return os.getenv("DBT_PROFILES_DIR")
|
|
268
|
-
elif os.path.exists(os.path.join(os.getcwd(), "profiles.yml")):
|
|
269
|
-
return os.getcwd()
|
|
270
|
-
else:
|
|
271
|
-
return os.path.expanduser("~/.dbt/")
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
@dataclass()
|
|
275
|
-
class DbtArgs:
|
|
276
|
-
"""
|
|
277
|
-
Used for RuntimeConfig.from_args
|
|
278
|
-
"""
|
|
279
|
-
|
|
280
|
-
threads: Optional[int] = (1,)
|
|
281
|
-
target: Optional[str] = (None,)
|
|
282
|
-
profiles_dir: Optional[str] = (None,)
|
|
283
|
-
project_dir: Optional[str] = (None,)
|
|
284
|
-
profile: Optional[str] = (None,)
|
|
285
|
-
target_path: Optional[str] = (None,)
|
|
286
|
-
project_only_flags: Optional[Dict[str, Any]] = None
|
|
287
|
-
which: Optional[str] = None
|
|
288
|
-
# Behavior flags - need to be present on args object for set_from_args
|
|
289
|
-
state_modified_compare_more_unrendered_values: Optional[bool] = True # dbt v1.9
|
|
290
|
-
require_unique_project_resource_names: Optional[bool] = False # dbt v1.11
|
|
291
|
-
require_ref_searches_node_package_before_root: Optional[bool] = False # dbt v1.11
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
@dataclass
|
|
295
|
-
class DbtAdapter(BaseAdapter):
|
|
296
|
-
runtime_config: RuntimeConfig = None
|
|
297
|
-
adapter: SQLAdapter = None
|
|
298
|
-
manifest: Manifest = None
|
|
299
|
-
previous_state: PreviousState = None
|
|
300
|
-
target_path: str = None
|
|
301
|
-
curr_manifest: WritableManifest = None
|
|
302
|
-
curr_catalog: CatalogArtifact = None
|
|
303
|
-
base_path: str = None
|
|
304
|
-
base_manifest: WritableManifest = None
|
|
305
|
-
base_catalog: CatalogArtifact = None
|
|
306
|
-
|
|
307
|
-
# Review mode
|
|
308
|
-
review_mode: bool = False
|
|
309
|
-
|
|
310
|
-
# Watch the artifact change
|
|
311
|
-
artifacts_observer = Observer()
|
|
312
|
-
artifacts_files = []
|
|
313
|
-
|
|
314
|
-
# Watch the base environment ready
|
|
315
|
-
base_env_observer = Observer()
|
|
316
|
-
|
|
317
|
-
def support_tasks(self):
|
|
318
|
-
support_map = {run_type.value: True for run_type in dbt_supported_registry}
|
|
319
|
-
|
|
320
|
-
return support_map
|
|
321
|
-
|
|
322
|
-
@classmethod
|
|
323
|
-
def load(cls, no_artifacts=False, review=False, **kwargs):
|
|
324
|
-
target = kwargs.get("target")
|
|
325
|
-
target_path = kwargs.get("target_path", "target")
|
|
326
|
-
target_base_path = kwargs.get("target_base_path", "target-base")
|
|
327
|
-
|
|
328
|
-
profile_name = kwargs.get("profile")
|
|
329
|
-
project_dir = kwargs.get("project_dir")
|
|
330
|
-
profiles_dir = kwargs.get("profiles_dir")
|
|
331
|
-
|
|
332
|
-
if profiles_dir is None:
|
|
333
|
-
profiles_dir = default_profiles_dir()
|
|
334
|
-
|
|
335
|
-
# runtime_config
|
|
336
|
-
args = DbtArgs(
|
|
337
|
-
threads=1,
|
|
338
|
-
target=target,
|
|
339
|
-
target_path=target_path,
|
|
340
|
-
project_dir=project_dir,
|
|
341
|
-
profiles_dir=profiles_dir,
|
|
342
|
-
profile=profile_name,
|
|
343
|
-
project_only_flags={},
|
|
344
|
-
which="list",
|
|
345
|
-
)
|
|
346
|
-
set_from_args(args, args)
|
|
347
|
-
|
|
348
|
-
from dbt.exceptions import DbtProjectError
|
|
349
|
-
|
|
350
|
-
try:
|
|
351
|
-
# adapter
|
|
352
|
-
if dbt_version < "v1.8":
|
|
353
|
-
runtime_config = RuntimeConfig.from_args(args)
|
|
354
|
-
adapter_name = runtime_config.credentials.type
|
|
355
|
-
adapter_cls = get_adapter_class_by_name(adapter_name)
|
|
356
|
-
adapter: SQLAdapter = adapter_cls(runtime_config)
|
|
357
|
-
else:
|
|
358
|
-
from dbt.mp_context import get_mp_context
|
|
359
|
-
from dbt_common.context import (
|
|
360
|
-
get_invocation_context,
|
|
361
|
-
set_invocation_context,
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
set_invocation_context({})
|
|
365
|
-
get_invocation_context()._env = dict(os.environ)
|
|
366
|
-
runtime_config = RuntimeConfig.from_args(args)
|
|
367
|
-
adapter_name = runtime_config.credentials.type
|
|
368
|
-
adapter_cls = get_adapter_class_by_name(adapter_name)
|
|
369
|
-
adapter: SQLAdapter = adapter_cls(runtime_config, get_mp_context())
|
|
370
|
-
from dbt.adapters.factory import FACTORY
|
|
371
|
-
|
|
372
|
-
FACTORY.adapters[adapter_name] = adapter
|
|
373
|
-
|
|
374
|
-
adapter.connections.set_connection_name()
|
|
375
|
-
runtime_config.adapter = adapter
|
|
376
|
-
|
|
377
|
-
dbt_adapter = cls(
|
|
378
|
-
runtime_config=runtime_config,
|
|
379
|
-
adapter=adapter,
|
|
380
|
-
review_mode=review,
|
|
381
|
-
base_path=target_base_path,
|
|
382
|
-
)
|
|
383
|
-
except DbtProjectError as e:
|
|
384
|
-
raise e
|
|
385
|
-
|
|
386
|
-
# Load the artifacts from the state file or dbt target and dbt base directory
|
|
387
|
-
if not no_artifacts and not review:
|
|
388
|
-
dbt_adapter.load_artifacts()
|
|
389
|
-
return dbt_adapter
|
|
390
|
-
|
|
391
|
-
def print_lineage_info(self):
|
|
392
|
-
print("Base:")
|
|
393
|
-
print(f" Manifest: {self.base_manifest.metadata.generated_at}")
|
|
394
|
-
print(f" Catalog: {self.base_catalog.metadata.generated_at if self.base_catalog else 'N/A'}")
|
|
395
|
-
|
|
396
|
-
print("Current:")
|
|
397
|
-
print(f" Manifest: {self.curr_manifest.metadata.generated_at}")
|
|
398
|
-
print(f" Catalog: {self.curr_catalog.metadata.generated_at if self.curr_catalog else 'N/A'}")
|
|
399
|
-
|
|
400
|
-
def get_columns(self, model: str, base=False) -> List[Column]:
|
|
401
|
-
relation = self.create_relation(model, base)
|
|
402
|
-
get_columns_macro = "get_columns_in_relation"
|
|
403
|
-
if self.adapter.connections.TYPE == "databricks":
|
|
404
|
-
get_columns_macro = "get_columns_comments"
|
|
405
|
-
|
|
406
|
-
if dbt_version < "v1.8":
|
|
407
|
-
columns = self.adapter.execute_macro(
|
|
408
|
-
get_columns_macro, kwargs={"relation": relation}, manifest=self.manifest
|
|
409
|
-
)
|
|
410
|
-
else:
|
|
411
|
-
from dbt.context.providers import generate_runtime_macro_context
|
|
412
|
-
|
|
413
|
-
macro_manifest = MacroManifest(self.manifest.macros)
|
|
414
|
-
self.adapter.set_macro_resolver(macro_manifest)
|
|
415
|
-
self.adapter.set_macro_context_generator(generate_runtime_macro_context)
|
|
416
|
-
columns = self.adapter.execute_macro(get_columns_macro, kwargs={"relation": relation})
|
|
417
|
-
|
|
418
|
-
if self.adapter.connections.TYPE == "databricks":
|
|
419
|
-
# reference: get_columns_in_relation (dbt/adapters/databricks/impl.py)
|
|
420
|
-
from dbt.adapters.databricks.column import DatabricksColumn
|
|
421
|
-
|
|
422
|
-
rows = columns
|
|
423
|
-
columns = []
|
|
424
|
-
for row in rows:
|
|
425
|
-
if row["col_name"].startswith("#"):
|
|
426
|
-
break
|
|
427
|
-
columns.append(
|
|
428
|
-
DatabricksColumn(
|
|
429
|
-
column=row["col_name"],
|
|
430
|
-
dtype=row["data_type"],
|
|
431
|
-
comment=row["comment"],
|
|
432
|
-
)
|
|
433
|
-
)
|
|
434
|
-
return columns
|
|
435
|
-
else:
|
|
436
|
-
return columns
|
|
437
|
-
|
|
438
|
-
def get_model(self, model_id: str, base=False):
|
|
439
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
440
|
-
manifest_dict = manifest.to_dict()
|
|
441
|
-
|
|
442
|
-
node = manifest_dict["nodes"].get(model_id)
|
|
443
|
-
if node is None:
|
|
444
|
-
return {}
|
|
445
|
-
|
|
446
|
-
node_name = node["name"]
|
|
447
|
-
with self.adapter.connection_named("model"):
|
|
448
|
-
columns = [column for column in self.get_columns(node_name, base=base)]
|
|
449
|
-
|
|
450
|
-
child_map: List[str] = manifest_dict["child_map"][model_id]
|
|
451
|
-
cols_not_null = []
|
|
452
|
-
cols_unique = []
|
|
453
|
-
|
|
454
|
-
for child in child_map:
|
|
455
|
-
comps = child.split(".")
|
|
456
|
-
child_type = comps[0]
|
|
457
|
-
child_name = comps[2]
|
|
458
|
-
|
|
459
|
-
not_null_prefix = f"not_null_{node_name}_"
|
|
460
|
-
if child_type == "test" and child_name.startswith(not_null_prefix):
|
|
461
|
-
cols_not_null.append(child_name[len(not_null_prefix) :])
|
|
462
|
-
unique_prefix = f"unique_{node_name}_"
|
|
463
|
-
if child_type == "test" and child_name.startswith(unique_prefix):
|
|
464
|
-
cols_unique.append(child_name[len(unique_prefix) :])
|
|
465
|
-
|
|
466
|
-
columns_info = {}
|
|
467
|
-
primary_key = None
|
|
468
|
-
for c in columns:
|
|
469
|
-
col_name = c.column
|
|
470
|
-
col = dict(name=col_name, type=c.dtype)
|
|
471
|
-
if col_name in cols_not_null:
|
|
472
|
-
col["not_null"] = True
|
|
473
|
-
if col_name in cols_unique:
|
|
474
|
-
col["unique"] = True
|
|
475
|
-
if not primary_key:
|
|
476
|
-
primary_key = col_name
|
|
477
|
-
columns_info[col_name] = col
|
|
478
|
-
|
|
479
|
-
result = dict(columns=columns_info)
|
|
480
|
-
if primary_key:
|
|
481
|
-
result["primary_key"] = primary_key
|
|
482
|
-
|
|
483
|
-
return result
|
|
484
|
-
|
|
485
|
-
@track_timing("artifact_load")
|
|
486
|
-
def load_artifacts(self):
|
|
487
|
-
"""
|
|
488
|
-
Load the artifacts from the 'target' and 'target-base' directory
|
|
489
|
-
"""
|
|
490
|
-
if self.runtime_config is None:
|
|
491
|
-
raise Exception("Cannot find the dbt project configuration")
|
|
492
|
-
|
|
493
|
-
project_root = self.runtime_config.project_root
|
|
494
|
-
target_path = self.runtime_config.target_path
|
|
495
|
-
target_base_path = self.base_path
|
|
496
|
-
self.target_path = os.path.join(project_root, target_path)
|
|
497
|
-
self.base_path = os.path.join(project_root, target_base_path)
|
|
498
|
-
|
|
499
|
-
# load the artifacts
|
|
500
|
-
path = os.path.join(project_root, target_path, "manifest.json")
|
|
501
|
-
curr_manifest = load_manifest(path=path, timing_name="curr_manifest")
|
|
502
|
-
if curr_manifest is None:
|
|
503
|
-
raise FileNotFoundError(ENOENT, os.strerror(ENOENT), path)
|
|
504
|
-
path = os.path.join(project_root, target_base_path, "manifest.json")
|
|
505
|
-
base_manifest = load_manifest(path=path, timing_name="base_manifest")
|
|
506
|
-
if base_manifest is None:
|
|
507
|
-
raise FileNotFoundError(ENOENT, os.strerror(ENOENT), path)
|
|
508
|
-
|
|
509
|
-
curr_catalog = load_catalog(
|
|
510
|
-
path=os.path.join(project_root, target_path, "catalog.json"), timing_name="curr_catalog"
|
|
511
|
-
)
|
|
512
|
-
base_catalog = load_catalog(
|
|
513
|
-
path=os.path.join(project_root, target_base_path, "catalog.json"), timing_name="base_catalog"
|
|
514
|
-
)
|
|
515
|
-
|
|
516
|
-
# set the value if all the artifacts are loaded successfully
|
|
517
|
-
self.curr_manifest = curr_manifest
|
|
518
|
-
self.curr_catalog = curr_catalog
|
|
519
|
-
self.base_manifest = base_manifest
|
|
520
|
-
self.base_catalog = base_catalog
|
|
521
|
-
|
|
522
|
-
# set the manifest
|
|
523
|
-
self.manifest = as_manifest(curr_manifest)
|
|
524
|
-
self.previous_state = previous_state(
|
|
525
|
-
Path(target_base_path),
|
|
526
|
-
Path(self.runtime_config.target_path),
|
|
527
|
-
Path(self.runtime_config.project_root),
|
|
528
|
-
)
|
|
529
|
-
|
|
530
|
-
# set the file paths to watch
|
|
531
|
-
self.artifacts_files = [
|
|
532
|
-
os.path.join(project_root, target_path, "manifest.json"),
|
|
533
|
-
os.path.join(project_root, target_path, "catalog.json"),
|
|
534
|
-
os.path.join(project_root, target_base_path, "manifest.json"),
|
|
535
|
-
os.path.join(project_root, target_base_path, "catalog.json"),
|
|
536
|
-
]
|
|
537
|
-
|
|
538
|
-
def is_python_model(self, node_id: str, base: Optional[bool] = False):
|
|
539
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
540
|
-
model = manifest.nodes.get(node_id)
|
|
541
|
-
if hasattr(model, "language"):
|
|
542
|
-
return model.language == "python"
|
|
543
|
-
|
|
544
|
-
return False
|
|
545
|
-
|
|
546
|
-
def find_node_by_name(self, node_name, base=False) -> Optional[ManifestNode]:
|
|
547
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
548
|
-
|
|
549
|
-
for key, node in manifest.nodes.items():
|
|
550
|
-
if node.name == node_name:
|
|
551
|
-
return node
|
|
552
|
-
|
|
553
|
-
return None
|
|
554
|
-
|
|
555
|
-
def get_node_name_by_id(self, unique_id):
|
|
556
|
-
if unique_id.startswith("source."):
|
|
557
|
-
if unique_id in self.curr_manifest.sources:
|
|
558
|
-
return self.curr_manifest.sources[unique_id].name
|
|
559
|
-
elif unique_id in self.base_manifest.sources:
|
|
560
|
-
return self.base_manifest.sources[unique_id].name
|
|
561
|
-
elif unique_id.startswith("metric."):
|
|
562
|
-
if unique_id in self.curr_manifest.metrics:
|
|
563
|
-
return self.curr_manifest.metrics[unique_id].name
|
|
564
|
-
elif unique_id in self.base_manifest.metrics:
|
|
565
|
-
return self.base_manifest.metrics[unique_id].name
|
|
566
|
-
elif unique_id.startswith("exposure."):
|
|
567
|
-
if unique_id in self.curr_manifest.exposures:
|
|
568
|
-
return self.curr_manifest.exposures[unique_id].name
|
|
569
|
-
elif unique_id in self.base_manifest.exposures:
|
|
570
|
-
return self.base_manifest.exposures[unique_id].name
|
|
571
|
-
elif unique_id.startswith("semantic_model."):
|
|
572
|
-
if unique_id in self.curr_manifest.semantic_models:
|
|
573
|
-
return self.curr_manifest.semantic_models[unique_id].name
|
|
574
|
-
elif unique_id in self.base_manifest.semantic_models:
|
|
575
|
-
return self.base_manifest.semantic_models[unique_id].name
|
|
576
|
-
else:
|
|
577
|
-
if unique_id in self.curr_manifest.nodes:
|
|
578
|
-
return self.curr_manifest.nodes[unique_id].name
|
|
579
|
-
elif unique_id in self.base_manifest.nodes:
|
|
580
|
-
return self.base_manifest.nodes[unique_id].name
|
|
581
|
-
return None
|
|
582
|
-
|
|
583
|
-
def get_manifest(self, base: bool):
|
|
584
|
-
return self.curr_manifest if base is False else self.base_manifest
|
|
585
|
-
|
|
586
|
-
def generate_sql(
|
|
587
|
-
self,
|
|
588
|
-
sql_template: str,
|
|
589
|
-
base: bool = False,
|
|
590
|
-
context=None,
|
|
591
|
-
provided_manifest=None,
|
|
592
|
-
):
|
|
593
|
-
if context is None:
|
|
594
|
-
context = {}
|
|
595
|
-
manifest = provided_manifest if provided_manifest is not None else as_manifest(self.get_manifest(base))
|
|
596
|
-
parser = SqlBlockParser(self.runtime_config, manifest, self.runtime_config)
|
|
597
|
-
|
|
598
|
-
if dbt_version >= dbt_version.parse("v1.8"):
|
|
599
|
-
from dbt_common.context import (
|
|
600
|
-
get_invocation_context,
|
|
601
|
-
set_invocation_context,
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
set_invocation_context({})
|
|
605
|
-
get_invocation_context()._env = dict(os.environ)
|
|
606
|
-
|
|
607
|
-
node_id = str("generated_" + uuid.uuid4().hex)
|
|
608
|
-
node = parser.parse_remote(sql_template, node_id)
|
|
609
|
-
process_node(self.runtime_config, manifest, node)
|
|
610
|
-
|
|
611
|
-
if dbt_version < dbt_version.parse("v1.8"):
|
|
612
|
-
compiler = self.adapter.get_compiler()
|
|
613
|
-
compiler.compile_node(node, manifest, context)
|
|
614
|
-
return node.compiled_code
|
|
615
|
-
else:
|
|
616
|
-
from dbt.clients import jinja
|
|
617
|
-
from dbt.context.providers import (
|
|
618
|
-
generate_runtime_macro_context,
|
|
619
|
-
generate_runtime_model_context,
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
# Set up macro resolver for dbt >= 1.8
|
|
623
|
-
macro_manifest = MacroManifest(manifest.macros)
|
|
624
|
-
self.adapter.set_macro_resolver(macro_manifest)
|
|
625
|
-
self.adapter.set_macro_context_generator(generate_runtime_macro_context)
|
|
626
|
-
|
|
627
|
-
jinja_ctx = generate_runtime_model_context(node, self.runtime_config, manifest)
|
|
628
|
-
jinja_ctx.update(context)
|
|
629
|
-
compiled_code = jinja.get_rendered(sql_template, jinja_ctx, node)
|
|
630
|
-
return compiled_code
|
|
631
|
-
|
|
632
|
-
def execute(
|
|
633
|
-
self,
|
|
634
|
-
sql: str,
|
|
635
|
-
auto_begin: bool = False,
|
|
636
|
-
fetch: bool = False,
|
|
637
|
-
limit: Optional[int] = None,
|
|
638
|
-
) -> Tuple[any, agate.Table]:
|
|
639
|
-
if dbt_version < dbt_version.parse("v1.6"):
|
|
640
|
-
return self.adapter.execute(sql, auto_begin=auto_begin, fetch=fetch)
|
|
641
|
-
|
|
642
|
-
return self.adapter.execute(sql, auto_begin=auto_begin, fetch=fetch, limit=limit)
|
|
643
|
-
|
|
644
|
-
def build_parent_map(self, nodes: Dict, base: Optional[bool] = False) -> Dict[str, List[str]]:
|
|
645
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
646
|
-
manifest_dict = manifest.to_dict()
|
|
647
|
-
|
|
648
|
-
node_ids = nodes.keys()
|
|
649
|
-
parent_map = {}
|
|
650
|
-
for k, parents in manifest_dict["parent_map"].items():
|
|
651
|
-
if k not in node_ids:
|
|
652
|
-
continue
|
|
653
|
-
parent_map[k] = [parent for parent in parents if parent in node_ids]
|
|
654
|
-
|
|
655
|
-
return parent_map
|
|
656
|
-
|
|
657
|
-
def build_parent_list_per_node(self, node_id: str, base: Optional[bool] = False) -> List[str]:
|
|
658
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
659
|
-
manifest_dict = manifest.to_dict()
|
|
660
|
-
|
|
661
|
-
if node_id in manifest_dict["parent_map"]:
|
|
662
|
-
return manifest_dict["parent_map"][node_id]
|
|
663
|
-
|
|
664
|
-
def get_lineage(self, base: Optional[bool] = False):
|
|
665
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
666
|
-
catalog = self.curr_catalog if base is False else self.base_catalog
|
|
667
|
-
cache_key = hash((id(manifest), id(catalog)))
|
|
668
|
-
return self.get_lineage_cached(base, cache_key)
|
|
669
|
-
|
|
670
|
-
def get_lineage_diff(self) -> LineageDiff:
|
|
671
|
-
cache_key = hash(
|
|
672
|
-
(
|
|
673
|
-
id(self.base_manifest),
|
|
674
|
-
id(self.base_catalog),
|
|
675
|
-
id(self.curr_manifest),
|
|
676
|
-
id(self.curr_catalog),
|
|
677
|
-
)
|
|
678
|
-
)
|
|
679
|
-
return self._get_lineage_diff_cached(cache_key)
|
|
680
|
-
|
|
681
|
-
@lru_cache(maxsize=2)
|
|
682
|
-
def get_lineage_cached(self, base: Optional[bool] = False, cache_key=0):
|
|
683
|
-
if base is False:
|
|
684
|
-
perf_tracker = LineagePerfTracker()
|
|
685
|
-
perf_tracker.start_lineage()
|
|
686
|
-
|
|
687
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
688
|
-
catalog = self.curr_catalog if base is False else self.base_catalog
|
|
689
|
-
|
|
690
|
-
manifest_metadata = manifest.metadata if manifest is not None else None
|
|
691
|
-
catalog_metadata = catalog.metadata if catalog is not None else None
|
|
692
|
-
|
|
693
|
-
manifest_dict = manifest.to_dict()
|
|
694
|
-
|
|
695
|
-
nodes = {}
|
|
696
|
-
|
|
697
|
-
for node in manifest_dict["nodes"].values():
|
|
698
|
-
unique_id = node["unique_id"]
|
|
699
|
-
resource_type = node["resource_type"]
|
|
700
|
-
|
|
701
|
-
if resource_type not in ["model", "seed", "exposure", "snapshot"]:
|
|
702
|
-
continue
|
|
703
|
-
|
|
704
|
-
nodes[unique_id] = {
|
|
705
|
-
"id": node["unique_id"],
|
|
706
|
-
"name": node["name"],
|
|
707
|
-
"resource_type": node["resource_type"],
|
|
708
|
-
"package_name": node["package_name"],
|
|
709
|
-
"schema": node["schema"],
|
|
710
|
-
"config": node["config"],
|
|
711
|
-
"checksum": node["checksum"],
|
|
712
|
-
"raw_code": node["raw_code"],
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
# List of <type>.<package_name>.<node_name>.<hash>
|
|
716
|
-
# model.jaffle_shop.customer_segments
|
|
717
|
-
# test.jaffle_shop.not_null_customers_customer_id.5c9bf9911d
|
|
718
|
-
# test.jaffle_shop.unique_customers_customer_id.c5af1ff4b1
|
|
719
|
-
child_map: List[str] = manifest_dict["child_map"][unique_id]
|
|
720
|
-
cols_not_null = []
|
|
721
|
-
cols_unique = []
|
|
722
|
-
|
|
723
|
-
for child in child_map:
|
|
724
|
-
node_name = node["name"]
|
|
725
|
-
comps = child.split(".")
|
|
726
|
-
if len(comps) < MIN_DBT_NODE_COMPOSITION:
|
|
727
|
-
# only happens in unittest
|
|
728
|
-
continue
|
|
729
|
-
|
|
730
|
-
child_type = comps[0]
|
|
731
|
-
child_name = comps[2]
|
|
732
|
-
|
|
733
|
-
not_null_prefix = f"not_null_{node_name}_"
|
|
734
|
-
if child_type == "test" and child_name.startswith(not_null_prefix):
|
|
735
|
-
cols_not_null.append(child_name[len(not_null_prefix) :])
|
|
736
|
-
unique_prefix = f"unique_{node_name}_"
|
|
737
|
-
if child_type == "test" and child_name.startswith(unique_prefix):
|
|
738
|
-
cols_unique.append(child_name[len(unique_prefix) :])
|
|
739
|
-
|
|
740
|
-
if catalog is not None and unique_id in catalog.nodes:
|
|
741
|
-
columns = {}
|
|
742
|
-
primary_key = None
|
|
743
|
-
for col_name, col_metadata in catalog.nodes[unique_id].columns.items():
|
|
744
|
-
col = dict(name=col_name, type=col_metadata.type)
|
|
745
|
-
if col_name in cols_not_null:
|
|
746
|
-
col["not_null"] = True
|
|
747
|
-
if col_name in cols_unique:
|
|
748
|
-
col["unique"] = True
|
|
749
|
-
if not primary_key:
|
|
750
|
-
primary_key = col_name
|
|
751
|
-
columns[col_name] = col
|
|
752
|
-
nodes[unique_id]["columns"] = columns
|
|
753
|
-
if primary_key:
|
|
754
|
-
nodes[unique_id]["primary_key"] = primary_key
|
|
755
|
-
|
|
756
|
-
for source in manifest_dict["sources"].values():
|
|
757
|
-
unique_id = source["unique_id"]
|
|
758
|
-
|
|
759
|
-
nodes[unique_id] = {
|
|
760
|
-
"id": source["unique_id"],
|
|
761
|
-
"name": source["name"],
|
|
762
|
-
"source_name": source["source_name"],
|
|
763
|
-
"resource_type": source["resource_type"],
|
|
764
|
-
"package_name": source["package_name"],
|
|
765
|
-
"config": source["config"],
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
if catalog is not None and unique_id in catalog.sources:
|
|
769
|
-
nodes[unique_id]["columns"] = {
|
|
770
|
-
col_name: {"name": col_name, "type": col_metadata.type}
|
|
771
|
-
for col_name, col_metadata in catalog.sources[unique_id].columns.items()
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
for exposure in manifest_dict["exposures"].values():
|
|
775
|
-
nodes[exposure["unique_id"]] = {
|
|
776
|
-
"id": exposure["unique_id"],
|
|
777
|
-
"name": exposure["name"],
|
|
778
|
-
"resource_type": exposure["resource_type"],
|
|
779
|
-
"package_name": exposure["package_name"],
|
|
780
|
-
"config": exposure["config"],
|
|
781
|
-
}
|
|
782
|
-
for metric in manifest_dict["metrics"].values():
|
|
783
|
-
nodes[metric["unique_id"]] = {
|
|
784
|
-
"id": metric["unique_id"],
|
|
785
|
-
"name": metric["name"],
|
|
786
|
-
"resource_type": metric["resource_type"],
|
|
787
|
-
"package_name": metric["package_name"],
|
|
788
|
-
"config": metric["config"],
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
if "semantic_models" in manifest_dict:
|
|
792
|
-
for semantic_models in manifest_dict["semantic_models"].values():
|
|
793
|
-
nodes[semantic_models["unique_id"]] = {
|
|
794
|
-
"id": semantic_models["unique_id"],
|
|
795
|
-
"name": semantic_models["name"],
|
|
796
|
-
"resource_type": semantic_models["resource_type"],
|
|
797
|
-
"package_name": semantic_models["package_name"],
|
|
798
|
-
"config": semantic_models["config"],
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
parent_map = self.build_parent_map(nodes, base)
|
|
802
|
-
|
|
803
|
-
if base is False:
|
|
804
|
-
perf_tracker.end_lineage()
|
|
805
|
-
perf_tracker.set_total_nodes(len(nodes))
|
|
806
|
-
log_performance("model lineage", perf_tracker.to_dict())
|
|
807
|
-
perf_tracker.reset()
|
|
808
|
-
|
|
809
|
-
return dict(
|
|
810
|
-
parent_map=parent_map,
|
|
811
|
-
nodes=nodes,
|
|
812
|
-
manifest_metadata=manifest_metadata,
|
|
813
|
-
catalog_metadata=catalog_metadata,
|
|
814
|
-
)
|
|
815
|
-
|
|
816
|
-
@lru_cache(maxsize=1)
|
|
817
|
-
def _get_lineage_diff_cached(self, cache_key) -> LineageDiff:
|
|
818
|
-
base = self.get_lineage(base=True)
|
|
819
|
-
current = self.get_lineage(base=False)
|
|
820
|
-
|
|
821
|
-
modified_nodes = self.select_nodes(select="state:modified")
|
|
822
|
-
diff = {}
|
|
823
|
-
for node_id in modified_nodes:
|
|
824
|
-
base_node = base.get("nodes", {}).get(node_id)
|
|
825
|
-
curr_node = current.get("nodes", {}).get(node_id)
|
|
826
|
-
if base_node and curr_node:
|
|
827
|
-
diff[node_id] = NodeDiff(change_status="modified")
|
|
828
|
-
elif base_node:
|
|
829
|
-
diff[node_id] = NodeDiff(change_status="removed")
|
|
830
|
-
elif curr_node:
|
|
831
|
-
diff[node_id] = NodeDiff(change_status="added")
|
|
832
|
-
|
|
833
|
-
return LineageDiff(
|
|
834
|
-
base=base,
|
|
835
|
-
current=current,
|
|
836
|
-
diff=diff,
|
|
837
|
-
)
|
|
838
|
-
|
|
839
|
-
@lru_cache(maxsize=128)
|
|
840
|
-
def get_change_analysis_cached(self, node_id: str):
|
|
841
|
-
breaking_perf_tracker = BreakingPerformanceTracking()
|
|
842
|
-
lineage_diff = self.get_lineage_diff()
|
|
843
|
-
diff = lineage_diff.diff
|
|
844
|
-
|
|
845
|
-
if node_id not in diff or diff[node_id].change_status != "modified":
|
|
846
|
-
return diff.get(node_id)
|
|
847
|
-
|
|
848
|
-
breaking_perf_tracker.increment_modified_nodes()
|
|
849
|
-
breaking_perf_tracker.start_lineage_diff()
|
|
850
|
-
|
|
851
|
-
base = lineage_diff.base
|
|
852
|
-
current = lineage_diff.current
|
|
853
|
-
|
|
854
|
-
base_manifest = as_manifest(self.get_manifest(True))
|
|
855
|
-
curr_manifest = as_manifest(self.get_manifest(False))
|
|
856
|
-
breaking_perf_tracker.record_checkpoint("manifest")
|
|
857
|
-
|
|
858
|
-
def ref_func(*args):
|
|
859
|
-
if len(args) == 1:
|
|
860
|
-
node = args[0]
|
|
861
|
-
elif len(args) > 1:
|
|
862
|
-
node = args[1]
|
|
863
|
-
else:
|
|
864
|
-
return None
|
|
865
|
-
return node
|
|
866
|
-
|
|
867
|
-
def source_func(source_name, table_name):
|
|
868
|
-
source_name = source_name.replace("-", "_")
|
|
869
|
-
return f"__{source_name}__{table_name}"
|
|
870
|
-
|
|
871
|
-
jinja_context = dict(
|
|
872
|
-
ref=ref_func,
|
|
873
|
-
source=source_func,
|
|
874
|
-
)
|
|
875
|
-
|
|
876
|
-
base_node = base.get("nodes", {}).get(node_id)
|
|
877
|
-
curr_node = current.get("nodes", {}).get(node_id)
|
|
878
|
-
change = NodeChange(category="unknown")
|
|
879
|
-
if (
|
|
880
|
-
curr_node.get("resource_type") in ["model", "snapshot"]
|
|
881
|
-
and curr_node.get("raw_code") is not None
|
|
882
|
-
and base_node.get("raw_code") is not None
|
|
883
|
-
):
|
|
884
|
-
try:
|
|
885
|
-
|
|
886
|
-
def _get_schema(lineage):
|
|
887
|
-
schema = {}
|
|
888
|
-
nodes = lineage["nodes"]
|
|
889
|
-
parent_list = lineage["parent_map"].get(node_id, [])
|
|
890
|
-
for parent_id in parent_list:
|
|
891
|
-
parent_node = nodes.get(parent_id)
|
|
892
|
-
if parent_node is None:
|
|
893
|
-
continue
|
|
894
|
-
columns = parent_node.get("columns") or {}
|
|
895
|
-
name = parent_node.get("name")
|
|
896
|
-
if parent_node.get("resource_type") == "source":
|
|
897
|
-
parts = parent_id.split(".")
|
|
898
|
-
source = parts[2]
|
|
899
|
-
table = parts[3]
|
|
900
|
-
source = source.replace("-", "_")
|
|
901
|
-
name = f"__{source}__{table}"
|
|
902
|
-
schema[name] = {name: column.get("type") for name, column in columns.items()}
|
|
903
|
-
return schema
|
|
904
|
-
|
|
905
|
-
base_sql = self.generate_sql(
|
|
906
|
-
base_node.get("raw_code"),
|
|
907
|
-
context=jinja_context,
|
|
908
|
-
provided_manifest=base_manifest,
|
|
909
|
-
)
|
|
910
|
-
curr_sql = self.generate_sql(
|
|
911
|
-
curr_node.get("raw_code"),
|
|
912
|
-
context=jinja_context,
|
|
913
|
-
provided_manifest=curr_manifest,
|
|
914
|
-
)
|
|
915
|
-
base_schema = _get_schema(base)
|
|
916
|
-
curr_schema = _get_schema(current)
|
|
917
|
-
dialect = self.adapter.connections.TYPE
|
|
918
|
-
if curr_manifest.metadata.adapter_type is not None:
|
|
919
|
-
dialect = curr_manifest.metadata.adapter_type
|
|
920
|
-
|
|
921
|
-
change = parse_change_category(
|
|
922
|
-
base_sql,
|
|
923
|
-
curr_sql,
|
|
924
|
-
old_schema=base_schema,
|
|
925
|
-
new_schema=curr_schema,
|
|
926
|
-
dialect=dialect,
|
|
927
|
-
perf_tracking=breaking_perf_tracker,
|
|
928
|
-
)
|
|
929
|
-
|
|
930
|
-
# Make sure that the case of the column names are the same
|
|
931
|
-
changed_columns = {
|
|
932
|
-
column.lower(): change_status for column, change_status in (change.columns or {}).items()
|
|
933
|
-
}
|
|
934
|
-
changed_columns_names = set(changed_columns)
|
|
935
|
-
changed_columns_final = {}
|
|
936
|
-
|
|
937
|
-
base_columns = base_node.get("columns") or {}
|
|
938
|
-
curr_columns = curr_node.get("columns") or {}
|
|
939
|
-
columns_names = set(base_columns) | set(curr_columns)
|
|
940
|
-
|
|
941
|
-
for column_name in columns_names:
|
|
942
|
-
if column_name.lower() in changed_columns_names:
|
|
943
|
-
changed_columns_final[column_name] = changed_columns[column_name.lower()]
|
|
944
|
-
|
|
945
|
-
change.columns = changed_columns_final
|
|
946
|
-
except Exception:
|
|
947
|
-
# TODO: telemetry
|
|
948
|
-
pass
|
|
949
|
-
|
|
950
|
-
breaking_perf_tracker.end_lineage_diff()
|
|
951
|
-
log_performance("change analysis per node", breaking_perf_tracker.to_dict())
|
|
952
|
-
breaking_perf_tracker.reset()
|
|
953
|
-
node_diff = diff.get(node_id)
|
|
954
|
-
node_diff.change = change
|
|
955
|
-
return node_diff
|
|
956
|
-
|
|
957
|
-
def get_cll(
|
|
958
|
-
self,
|
|
959
|
-
node_id: Optional[str] = None,
|
|
960
|
-
column: Optional[str] = None,
|
|
961
|
-
change_analysis: Optional[bool] = False,
|
|
962
|
-
no_cll: Optional[bool] = False,
|
|
963
|
-
no_upstream: Optional[bool] = False,
|
|
964
|
-
no_downstream: Optional[bool] = False,
|
|
965
|
-
no_filter: Optional[bool] = False,
|
|
966
|
-
) -> CllData:
|
|
967
|
-
cll_tracker = LineagePerfTracker()
|
|
968
|
-
cll_tracker.set_params(
|
|
969
|
-
has_node=node_id is not None,
|
|
970
|
-
has_column=column is not None,
|
|
971
|
-
change_analysis=change_analysis,
|
|
972
|
-
no_cll=no_cll,
|
|
973
|
-
no_upstream=no_upstream,
|
|
974
|
-
no_downstream=no_downstream,
|
|
975
|
-
)
|
|
976
|
-
cll_tracker.start_column_lineage()
|
|
977
|
-
|
|
978
|
-
manifest = self.curr_manifest
|
|
979
|
-
manifest_dict = manifest.to_dict()
|
|
980
|
-
|
|
981
|
-
# Find related model nodes
|
|
982
|
-
if node_id is not None:
|
|
983
|
-
cll_node_ids = {node_id}
|
|
984
|
-
else:
|
|
985
|
-
lineage_diff = self.get_lineage_diff()
|
|
986
|
-
cll_node_ids = set(lineage_diff.diff.keys())
|
|
987
|
-
|
|
988
|
-
cll_tracker.set_init_nodes(len(cll_node_ids))
|
|
989
|
-
|
|
990
|
-
nodes = {}
|
|
991
|
-
columns = {}
|
|
992
|
-
parent_map = {}
|
|
993
|
-
child_map = {}
|
|
994
|
-
|
|
995
|
-
if not no_upstream:
|
|
996
|
-
cll_node_ids = cll_node_ids.union(find_upstream(cll_node_ids, manifest_dict.get("parent_map")))
|
|
997
|
-
if not no_downstream:
|
|
998
|
-
cll_node_ids = cll_node_ids.union(find_downstream(cll_node_ids, manifest_dict.get("child_map")))
|
|
999
|
-
|
|
1000
|
-
if not no_cll:
|
|
1001
|
-
allowed_related_nodes = set()
|
|
1002
|
-
for key in ["sources", "nodes", "exposures", "metrics"]:
|
|
1003
|
-
attr = getattr(manifest, key)
|
|
1004
|
-
allowed_related_nodes.update(set(attr.keys()))
|
|
1005
|
-
if hasattr(manifest, "semantic_models"):
|
|
1006
|
-
attr = getattr(manifest, "semantic_models")
|
|
1007
|
-
allowed_related_nodes.update(set(attr.keys()))
|
|
1008
|
-
for cll_node_id in cll_node_ids:
|
|
1009
|
-
if cll_node_id not in allowed_related_nodes:
|
|
1010
|
-
continue
|
|
1011
|
-
cll_data_one = deepcopy(self.get_cll_cached(cll_node_id, base=False))
|
|
1012
|
-
cll_tracker.increment_cll_nodes()
|
|
1013
|
-
if cll_data_one is None:
|
|
1014
|
-
continue
|
|
1015
|
-
|
|
1016
|
-
nodes[cll_node_id] = cll_data_one.nodes.get(cll_node_id)
|
|
1017
|
-
node_diff = None
|
|
1018
|
-
if change_analysis:
|
|
1019
|
-
node_diff = self.get_change_analysis_cached(cll_node_id)
|
|
1020
|
-
cll_tracker.increment_change_analysis_nodes()
|
|
1021
|
-
if node_diff is not None:
|
|
1022
|
-
nodes[cll_node_id].change_status = node_diff.change_status
|
|
1023
|
-
if node_diff.change is not None:
|
|
1024
|
-
nodes[cll_node_id].change_category = node_diff.change.category
|
|
1025
|
-
for c_id, c in cll_data_one.columns.items():
|
|
1026
|
-
columns[c_id] = c
|
|
1027
|
-
if node_diff is not None:
|
|
1028
|
-
if node_diff.change_status == "added":
|
|
1029
|
-
c.change_status = "added"
|
|
1030
|
-
elif node_diff.change_status == "removed":
|
|
1031
|
-
c.change_status = "removed"
|
|
1032
|
-
elif node_diff.change is not None and node_diff.change.columns is not None:
|
|
1033
|
-
column_diff = node_diff.change.columns.get(c.name)
|
|
1034
|
-
if column_diff:
|
|
1035
|
-
c.change_status = column_diff
|
|
1036
|
-
|
|
1037
|
-
for p_id, parents in cll_data_one.parent_map.items():
|
|
1038
|
-
parent_map[p_id] = parents
|
|
1039
|
-
else:
|
|
1040
|
-
for cll_node_id in cll_node_ids:
|
|
1041
|
-
cll_node = None
|
|
1042
|
-
cll_node_columns: Dict[str, CllColumn] = {}
|
|
1043
|
-
|
|
1044
|
-
if cll_node_id in manifest.sources:
|
|
1045
|
-
cll_node = CllNode.build_cll_node(manifest, "sources", cll_node_id)
|
|
1046
|
-
if self.curr_catalog and cll_node_id in self.curr_catalog.sources:
|
|
1047
|
-
cll_node_columns = {
|
|
1048
|
-
column.name: CllColumn(
|
|
1049
|
-
id=f"{cll_node_id}_{column.name}",
|
|
1050
|
-
table_id=cll_node_id,
|
|
1051
|
-
name=column.name,
|
|
1052
|
-
type=column.type,
|
|
1053
|
-
)
|
|
1054
|
-
for column in self.curr_catalog.sources[cll_node_id].columns.values()
|
|
1055
|
-
}
|
|
1056
|
-
elif cll_node_id in manifest.nodes:
|
|
1057
|
-
cll_node = CllNode.build_cll_node(manifest, "nodes", cll_node_id)
|
|
1058
|
-
if self.curr_catalog and cll_node_id in self.curr_catalog.nodes:
|
|
1059
|
-
cll_node_columns = {
|
|
1060
|
-
column.name: CllColumn(
|
|
1061
|
-
id=f"{cll_node_id}_{column.name}",
|
|
1062
|
-
table_id=cll_node_id,
|
|
1063
|
-
name=column.name,
|
|
1064
|
-
type=column.type,
|
|
1065
|
-
)
|
|
1066
|
-
for column in self.curr_catalog.nodes[cll_node_id].columns.values()
|
|
1067
|
-
}
|
|
1068
|
-
elif cll_node_id in manifest.exposures:
|
|
1069
|
-
cll_node = CllNode.build_cll_node(manifest, "exposures", cll_node_id)
|
|
1070
|
-
elif hasattr(manifest, "semantic_models") and cll_node_id in manifest.semantic_models:
|
|
1071
|
-
cll_node = CllNode.build_cll_node(manifest, "semantic_models", cll_node_id)
|
|
1072
|
-
elif cll_node_id in manifest.metrics:
|
|
1073
|
-
cll_node = CllNode.build_cll_node(manifest, "metrics", cll_node_id)
|
|
1074
|
-
|
|
1075
|
-
if not cll_node:
|
|
1076
|
-
continue
|
|
1077
|
-
nodes[cll_node_id] = cll_node
|
|
1078
|
-
|
|
1079
|
-
node_diff = None
|
|
1080
|
-
if change_analysis:
|
|
1081
|
-
node_diff = self.get_change_analysis_cached(cll_node_id)
|
|
1082
|
-
cll_tracker.increment_change_analysis_nodes()
|
|
1083
|
-
if node_diff is not None:
|
|
1084
|
-
cll_node.change_status = node_diff.change_status
|
|
1085
|
-
if node_diff.change is not None:
|
|
1086
|
-
cll_node.change_category = node_diff.change.category
|
|
1087
|
-
for c, cll_column in cll_node_columns.items():
|
|
1088
|
-
cll_node.columns[c] = cll_column
|
|
1089
|
-
columns[cll_column.id] = cll_column
|
|
1090
|
-
if node_diff.change.columns and c in node_diff.change.columns:
|
|
1091
|
-
cll_column.change_status = node_diff.change.columns[c]
|
|
1092
|
-
|
|
1093
|
-
parent_map[cll_node_id] = manifest.parent_map.get(cll_node_id, [])
|
|
1094
|
-
|
|
1095
|
-
# build the child map
|
|
1096
|
-
for parent_id, parents in parent_map.items():
|
|
1097
|
-
for parent in parents:
|
|
1098
|
-
if parent not in child_map:
|
|
1099
|
-
child_map[parent] = set()
|
|
1100
|
-
child_map[parent].add(parent_id)
|
|
1101
|
-
|
|
1102
|
-
# Find the anchor nodes
|
|
1103
|
-
anchor_node_ids = set()
|
|
1104
|
-
extra_node_ids = set()
|
|
1105
|
-
if node_id is None and column is None:
|
|
1106
|
-
if change_analysis:
|
|
1107
|
-
# If change analysis is requested, we need to find the nodes that have changes
|
|
1108
|
-
lineage_diff = self.get_lineage_diff()
|
|
1109
|
-
for nid, nd in lineage_diff.diff.items():
|
|
1110
|
-
if nd.change_status == "added":
|
|
1111
|
-
anchor_node_ids.add(nid)
|
|
1112
|
-
n = lineage_diff.current["nodes"].get(nid)
|
|
1113
|
-
n_columns = n.get("columns", {})
|
|
1114
|
-
for c in n_columns:
|
|
1115
|
-
anchor_node_ids.add(build_column_key(nid, c))
|
|
1116
|
-
continue
|
|
1117
|
-
if nd.change_status == "removed":
|
|
1118
|
-
extra_node_ids.add(nid)
|
|
1119
|
-
continue
|
|
1120
|
-
|
|
1121
|
-
node_diff = self.get_change_analysis_cached(nid)
|
|
1122
|
-
if node_diff is not None and node_diff.change is not None:
|
|
1123
|
-
extra_node_ids.add(nid)
|
|
1124
|
-
if no_cll:
|
|
1125
|
-
if node_diff.change.category in ["breaking", "partial_breaking", "unknown"]:
|
|
1126
|
-
anchor_node_ids.add(nid)
|
|
1127
|
-
else:
|
|
1128
|
-
if node_diff.change.category in ["breaking", "unknown"]:
|
|
1129
|
-
anchor_node_ids.add(nid)
|
|
1130
|
-
if node_diff.change.columns is not None:
|
|
1131
|
-
for column_name in node_diff.change.columns:
|
|
1132
|
-
anchor_node_ids.add(f"{nid}_{column_name}")
|
|
1133
|
-
else:
|
|
1134
|
-
lineage_diff = self.get_lineage_diff()
|
|
1135
|
-
anchor_node_ids = lineage_diff.diff.keys()
|
|
1136
|
-
elif node_id is not None and column is None:
|
|
1137
|
-
if change_analysis:
|
|
1138
|
-
# If change analysis is requested, we need to find the nodes that have changes
|
|
1139
|
-
node_diff = self.get_change_analysis_cached(node_id)
|
|
1140
|
-
if node_diff is not None and node_diff.change is not None:
|
|
1141
|
-
extra_node_ids.add(node_id)
|
|
1142
|
-
if no_cll:
|
|
1143
|
-
if node_diff.change.category in ["breaking", "partial_breaking", "unknown"]:
|
|
1144
|
-
anchor_node_ids.add(node_id)
|
|
1145
|
-
else:
|
|
1146
|
-
if node_diff.change.category in ["breaking", "unknown"]:
|
|
1147
|
-
anchor_node_ids.add(node_id)
|
|
1148
|
-
if node_diff.change.columns is not None:
|
|
1149
|
-
for column_name in node_diff.change.columns:
|
|
1150
|
-
anchor_node_ids.add(f"{node_id}_{column_name}")
|
|
1151
|
-
else:
|
|
1152
|
-
anchor_node_ids.add(node_id)
|
|
1153
|
-
else:
|
|
1154
|
-
anchor_node_ids.add(node_id)
|
|
1155
|
-
if not no_cll:
|
|
1156
|
-
node = nodes.get(node_id)
|
|
1157
|
-
if node:
|
|
1158
|
-
for column_name in node.columns:
|
|
1159
|
-
column_key = build_column_key(node_id, column_name)
|
|
1160
|
-
anchor_node_ids.add(column_key)
|
|
1161
|
-
else:
|
|
1162
|
-
anchor_node_ids.add(f"{node_id}_{column}")
|
|
1163
|
-
|
|
1164
|
-
cll_tracker.set_anchor_nodes(len(anchor_node_ids))
|
|
1165
|
-
result_node_ids = set(anchor_node_ids)
|
|
1166
|
-
if not no_upstream:
|
|
1167
|
-
result_node_ids = result_node_ids.union(find_upstream(anchor_node_ids, parent_map))
|
|
1168
|
-
if not no_downstream:
|
|
1169
|
-
result_node_ids = result_node_ids.union(find_downstream(anchor_node_ids, child_map))
|
|
1170
|
-
|
|
1171
|
-
# Filter the nodes and columns based on the anchor nodes
|
|
1172
|
-
if not no_filter:
|
|
1173
|
-
nodes = {k: v for k, v in nodes.items() if k in result_node_ids or k in extra_node_ids}
|
|
1174
|
-
columns = {k: v for k, v in columns.items() if k in result_node_ids or k in extra_node_ids}
|
|
1175
|
-
|
|
1176
|
-
for node in nodes.values():
|
|
1177
|
-
node.columns = {
|
|
1178
|
-
k: v for k, v in node.columns.items() if v.id in result_node_ids or v.id in extra_node_ids
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
if change_analysis:
|
|
1182
|
-
node.impacted = node.id in result_node_ids
|
|
1183
|
-
|
|
1184
|
-
parent_map, child_map = filter_dependency_maps(parent_map, child_map, result_node_ids)
|
|
1185
|
-
|
|
1186
|
-
cll_tracker.end_column_lineage()
|
|
1187
|
-
cll_tracker.set_total_nodes(len(nodes) + len(columns))
|
|
1188
|
-
log_performance("column level lineage", cll_tracker.to_dict())
|
|
1189
|
-
cll_tracker.reset()
|
|
1190
|
-
|
|
1191
|
-
return CllData(
|
|
1192
|
-
nodes=nodes,
|
|
1193
|
-
columns=columns,
|
|
1194
|
-
parent_map=parent_map,
|
|
1195
|
-
child_map=child_map,
|
|
1196
|
-
)
|
|
1197
|
-
|
|
1198
|
-
@lru_cache(maxsize=128)
|
|
1199
|
-
def get_cll_cached(self, node_id: str, base: Optional[bool] = False) -> Optional[CllData]:
|
|
1200
|
-
cll_tracker = CLLPerformanceTracking()
|
|
1201
|
-
|
|
1202
|
-
node, parent_list = self.get_cll_node(node_id, base=base)
|
|
1203
|
-
if node is None:
|
|
1204
|
-
return None
|
|
1205
|
-
|
|
1206
|
-
cll_tracker.set_total_nodes(1)
|
|
1207
|
-
cll_tracker.start_column_lineage()
|
|
1208
|
-
|
|
1209
|
-
def _apply_all_columns(node: CllNode, transformation_type):
|
|
1210
|
-
cll_data = CllData()
|
|
1211
|
-
cll_data.nodes[node.id] = node
|
|
1212
|
-
cll_data.parent_map[node.id] = set(parent_list)
|
|
1213
|
-
for col in node.columns.values():
|
|
1214
|
-
column_id = f"{node.id}_{col.name}"
|
|
1215
|
-
col.transformation_type = transformation_type
|
|
1216
|
-
cll_data.columns[column_id] = col
|
|
1217
|
-
cll_data.parent_map[column_id] = set()
|
|
1218
|
-
return cll_data
|
|
1219
|
-
|
|
1220
|
-
manifest = as_manifest(self.get_manifest(base))
|
|
1221
|
-
catalog = self.curr_catalog if base is False else self.base_catalog
|
|
1222
|
-
resource_type = node.resource_type
|
|
1223
|
-
if resource_type not in {"model", "seed", "source", "snapshot"}:
|
|
1224
|
-
return _apply_all_columns(node, "unknown")
|
|
1225
|
-
|
|
1226
|
-
if resource_type == "source" or resource_type == "seed":
|
|
1227
|
-
return _apply_all_columns(node, "source")
|
|
1228
|
-
|
|
1229
|
-
if node.raw_code is None or self.is_python_model(node.id, base=base):
|
|
1230
|
-
return _apply_all_columns(node, "unknown")
|
|
1231
|
-
|
|
1232
|
-
if node.name == "metricflow_time_spine":
|
|
1233
|
-
return _apply_all_columns(node, "source")
|
|
1234
|
-
|
|
1235
|
-
if not node.columns:
|
|
1236
|
-
return _apply_all_columns(node, "unknown")
|
|
1237
|
-
|
|
1238
|
-
table_id_map = {}
|
|
1239
|
-
|
|
1240
|
-
def ref_func(*args):
|
|
1241
|
-
node_name: str = None
|
|
1242
|
-
project_or_package: str = None
|
|
1243
|
-
|
|
1244
|
-
if len(args) == 1:
|
|
1245
|
-
node_name = args[0]
|
|
1246
|
-
else:
|
|
1247
|
-
project_or_package = args[0]
|
|
1248
|
-
node_name = args[1]
|
|
1249
|
-
|
|
1250
|
-
for key, n in manifest.nodes.items():
|
|
1251
|
-
if n.name != node_name:
|
|
1252
|
-
continue
|
|
1253
|
-
if project_or_package is not None and n.package_name != project_or_package:
|
|
1254
|
-
continue
|
|
1255
|
-
|
|
1256
|
-
# replace id "." to "_"
|
|
1257
|
-
unique_id = n.unique_id
|
|
1258
|
-
table_name = unique_id.replace(".", "_")
|
|
1259
|
-
table_id_map[table_name.lower()] = unique_id
|
|
1260
|
-
return table_name
|
|
1261
|
-
|
|
1262
|
-
raise ValueError(f"Cannot find node {node_name} in the manifest")
|
|
1263
|
-
|
|
1264
|
-
def source_func(source_name, name):
|
|
1265
|
-
for key, n in manifest.sources.items():
|
|
1266
|
-
if n.source_name != source_name:
|
|
1267
|
-
continue
|
|
1268
|
-
if n.name != name:
|
|
1269
|
-
continue
|
|
1270
|
-
|
|
1271
|
-
# replace id "." to "_"
|
|
1272
|
-
unique_id = n.unique_id
|
|
1273
|
-
table_name = unique_id.replace(".", "_")
|
|
1274
|
-
table_id_map[table_name.lower()] = unique_id
|
|
1275
|
-
return table_name
|
|
1276
|
-
|
|
1277
|
-
raise ValueError(f"Cannot find source {source_name}.{name} in the manifest")
|
|
1278
|
-
|
|
1279
|
-
raw_code = node.raw_code
|
|
1280
|
-
jinja_context = dict(
|
|
1281
|
-
ref=ref_func,
|
|
1282
|
-
source=source_func,
|
|
1283
|
-
)
|
|
1284
|
-
|
|
1285
|
-
schema = {}
|
|
1286
|
-
if catalog is not None:
|
|
1287
|
-
for parent_id in parent_list:
|
|
1288
|
-
table_name = parent_id.replace(".", "_")
|
|
1289
|
-
columns = {}
|
|
1290
|
-
if parent_id in catalog.nodes:
|
|
1291
|
-
for col_name, col_metadata in catalog.nodes[parent_id].columns.items():
|
|
1292
|
-
columns[col_name] = col_metadata.type
|
|
1293
|
-
if parent_id in catalog.sources:
|
|
1294
|
-
for col_name, col_metadata in catalog.sources[parent_id].columns.items():
|
|
1295
|
-
columns[col_name] = col_metadata.type
|
|
1296
|
-
schema[table_name] = columns
|
|
1297
|
-
|
|
1298
|
-
try:
|
|
1299
|
-
compiled_sql = self.generate_sql(raw_code, base=base, context=jinja_context, provided_manifest=manifest)
|
|
1300
|
-
dialect = self.adapter.type()
|
|
1301
|
-
if self.get_manifest(base).metadata.adapter_type is not None:
|
|
1302
|
-
dialect = self.get_manifest(base).metadata.adapter_type
|
|
1303
|
-
m2c, c2c_map = cll(compiled_sql, schema=schema, dialect=dialect)
|
|
1304
|
-
except RecceException:
|
|
1305
|
-
cll_tracker.increment_sqlglot_error_nodes()
|
|
1306
|
-
return _apply_all_columns(node, "unknown")
|
|
1307
|
-
except Exception:
|
|
1308
|
-
cll_tracker.increment_other_error_nodes()
|
|
1309
|
-
return _apply_all_columns(node, "unknown")
|
|
1310
|
-
|
|
1311
|
-
# Add cll dependency to the node.
|
|
1312
|
-
cll_data = CllData()
|
|
1313
|
-
cll_data.nodes[node.id] = node
|
|
1314
|
-
cll_data.columns = {f"{node.id}_{col.name}": col for col in node.columns.values()}
|
|
1315
|
-
|
|
1316
|
-
# parent map for node
|
|
1317
|
-
depends_on = set(parent_list)
|
|
1318
|
-
for d in m2c:
|
|
1319
|
-
parent_key = f"{table_id_map[d.node.lower()]}_{d.column}"
|
|
1320
|
-
depends_on.add(parent_key)
|
|
1321
|
-
cll_data.parent_map[node_id] = depends_on
|
|
1322
|
-
|
|
1323
|
-
# parent map for columns
|
|
1324
|
-
for name, column in node.columns.items():
|
|
1325
|
-
depends_on = set()
|
|
1326
|
-
column_id = f"{node.id}_{name}"
|
|
1327
|
-
if name in c2c_map:
|
|
1328
|
-
for d in c2c_map[name].depends_on:
|
|
1329
|
-
parent_key = f"{table_id_map[d.node.lower()]}_{d.column}"
|
|
1330
|
-
depends_on.add(parent_key)
|
|
1331
|
-
column.transformation_type = c2c_map[name].transformation_type
|
|
1332
|
-
cll_data.parent_map[column_id] = set(depends_on)
|
|
1333
|
-
|
|
1334
|
-
cll_tracker.end_column_lineage()
|
|
1335
|
-
log_performance("column level lineage per node", cll_tracker.to_dict())
|
|
1336
|
-
cll_tracker.reset()
|
|
1337
|
-
return cll_data
|
|
1338
|
-
|
|
1339
|
-
def get_cll_node(self, node_id: str, base: Optional[bool] = False) -> Tuple[Optional[CllNode], list[str]]:
|
|
1340
|
-
manifest = self.curr_manifest if base is False else self.base_manifest
|
|
1341
|
-
catalog = self.curr_catalog if base is False else self.base_catalog
|
|
1342
|
-
parent_list = []
|
|
1343
|
-
node = None
|
|
1344
|
-
|
|
1345
|
-
# model, seed, snapshot
|
|
1346
|
-
if node_id in manifest.nodes:
|
|
1347
|
-
found = manifest.nodes[node_id]
|
|
1348
|
-
unique_id = found.unique_id
|
|
1349
|
-
node = CllNode.build_cll_node(manifest, "nodes", node_id)
|
|
1350
|
-
if hasattr(found.depends_on, "nodes"):
|
|
1351
|
-
parent_list = found.depends_on.nodes
|
|
1352
|
-
|
|
1353
|
-
if catalog is not None and node is not None and unique_id in catalog.nodes:
|
|
1354
|
-
columns = {}
|
|
1355
|
-
for col_name, col_metadata in catalog.nodes[unique_id].columns.items():
|
|
1356
|
-
column_id = f"{unique_id}_{col_name}"
|
|
1357
|
-
col = CllColumn(id=column_id, name=col_name, table_id=unique_id, type=col_metadata.type)
|
|
1358
|
-
columns[col_name] = col
|
|
1359
|
-
node.columns = columns
|
|
1360
|
-
|
|
1361
|
-
# source
|
|
1362
|
-
if node_id in manifest.sources:
|
|
1363
|
-
found = manifest.sources[node_id]
|
|
1364
|
-
unique_id = found.unique_id
|
|
1365
|
-
node = CllNode.build_cll_node(manifest, "sources", node_id)
|
|
1366
|
-
parent_list = []
|
|
1367
|
-
|
|
1368
|
-
if catalog is not None and node is not None and unique_id in catalog.sources:
|
|
1369
|
-
columns = {}
|
|
1370
|
-
for col_name, col_metadata in catalog.sources[unique_id].columns.items():
|
|
1371
|
-
column_id = f"{unique_id}_{col_name}"
|
|
1372
|
-
col = CllColumn(id=column_id, name=col_name, table_id=unique_id, type=col_metadata.type)
|
|
1373
|
-
columns[col_name] = col
|
|
1374
|
-
node.columns = columns
|
|
1375
|
-
|
|
1376
|
-
# exposure
|
|
1377
|
-
if node_id in manifest.exposures:
|
|
1378
|
-
found = manifest.exposures[node_id]
|
|
1379
|
-
node = CllNode.build_cll_node(manifest, "exposures", node_id)
|
|
1380
|
-
if hasattr(found.depends_on, "nodes"):
|
|
1381
|
-
parent_list = found.depends_on.nodes
|
|
1382
|
-
|
|
1383
|
-
if hasattr(manifest, "semantic_models") and node_id in manifest.semantic_models:
|
|
1384
|
-
found = manifest.semantic_models[node_id]
|
|
1385
|
-
node = CllNode.build_cll_node(manifest, "semantic_models", node_id)
|
|
1386
|
-
if hasattr(found.depends_on, "nodes"):
|
|
1387
|
-
parent_list = found.depends_on.nodes
|
|
1388
|
-
|
|
1389
|
-
if node_id in manifest.metrics:
|
|
1390
|
-
found = manifest.metrics[node_id]
|
|
1391
|
-
node = CllNode.build_cll_node(manifest, "metrics", node_id)
|
|
1392
|
-
if hasattr(found.depends_on, "nodes"):
|
|
1393
|
-
parent_list = found.depends_on.nodes
|
|
1394
|
-
|
|
1395
|
-
return node, parent_list
|
|
1396
|
-
|
|
1397
|
-
def get_manifests_by_id(self, unique_id: str):
|
|
1398
|
-
curr_manifest = self.get_manifest(base=False)
|
|
1399
|
-
base_manifest = self.get_manifest(base=True)
|
|
1400
|
-
if unique_id in curr_manifest.nodes.keys() or unique_id in base_manifest.nodes.keys():
|
|
1401
|
-
return {
|
|
1402
|
-
"current": curr_manifest.nodes.get(unique_id),
|
|
1403
|
-
"base": base_manifest.nodes.get(unique_id),
|
|
1404
|
-
}
|
|
1405
|
-
return None
|
|
1406
|
-
|
|
1407
|
-
def build_name_to_unique_id_index(self) -> Dict[str, str]:
|
|
1408
|
-
name_to_unique_id = {}
|
|
1409
|
-
curr_manifest = self.get_manifest(base=False)
|
|
1410
|
-
base_manifest = self.get_manifest(base=True)
|
|
1411
|
-
|
|
1412
|
-
for unique_id, node in base_manifest.nodes.items():
|
|
1413
|
-
name_to_unique_id[node.name] = unique_id
|
|
1414
|
-
for unique_id, node in curr_manifest.nodes.items():
|
|
1415
|
-
name_to_unique_id[node.name] = unique_id
|
|
1416
|
-
return name_to_unique_id
|
|
1417
|
-
|
|
1418
|
-
def start_monitor_artifacts(self, callback: Callable = None):
|
|
1419
|
-
if self.artifacts_files:
|
|
1420
|
-
event_handler = ArtifactsEventHandler(self.artifacts_files, callback=callback)
|
|
1421
|
-
if self.target_path:
|
|
1422
|
-
self.artifacts_observer.schedule(event_handler, self.target_path, recursive=False)
|
|
1423
|
-
if self.base_path:
|
|
1424
|
-
self.artifacts_observer.schedule(event_handler, self.base_path, recursive=False)
|
|
1425
|
-
self.artifacts_observer.start()
|
|
1426
|
-
logger.info("Start monitoring dbt artifacts")
|
|
1427
|
-
|
|
1428
|
-
def stop_monitor_artifacts(self):
|
|
1429
|
-
if self.artifacts_files:
|
|
1430
|
-
self.artifacts_observer.stop()
|
|
1431
|
-
self.artifacts_observer.join()
|
|
1432
|
-
logger.info("Stop monitoring artifacts")
|
|
1433
|
-
|
|
1434
|
-
def start_monitor_base_env(self, callback: Callable = None):
|
|
1435
|
-
target_base_dir = os.path.join(self.runtime_config.project_root, "target-base")
|
|
1436
|
-
base_env_files = {
|
|
1437
|
-
os.path.join(target_base_dir, "manifest.json"),
|
|
1438
|
-
os.path.join(target_base_dir, "catalog.json"),
|
|
1439
|
-
}
|
|
1440
|
-
event_handler = EnvironmentEventHandler(self.base_env_observer, base_env_files, callback=callback)
|
|
1441
|
-
self.base_env_observer.schedule(event_handler, self.runtime_config.project_root, recursive=True)
|
|
1442
|
-
self.base_env_observer.start()
|
|
1443
|
-
logger.info("Start monitoring base environment")
|
|
1444
|
-
|
|
1445
|
-
def stop_monitor_base_env(self):
|
|
1446
|
-
if self.base_env_observer.is_alive():
|
|
1447
|
-
self.base_env_observer.stop()
|
|
1448
|
-
self.base_env_observer.join()
|
|
1449
|
-
logger.info("Stop monitoring base environment")
|
|
1450
|
-
|
|
1451
|
-
def set_artifacts(
|
|
1452
|
-
self,
|
|
1453
|
-
base_manifest: WritableManifest,
|
|
1454
|
-
curr_manifest: WritableManifest,
|
|
1455
|
-
manifest: Manifest,
|
|
1456
|
-
previous_manifest: Manifest,
|
|
1457
|
-
base_catalog: CatalogArtifact,
|
|
1458
|
-
curr_catalog: CatalogArtifact,
|
|
1459
|
-
):
|
|
1460
|
-
self.curr_manifest = curr_manifest
|
|
1461
|
-
self.base_manifest = base_manifest
|
|
1462
|
-
self.manifest = manifest
|
|
1463
|
-
self.curr_catalog = curr_catalog
|
|
1464
|
-
self.base_catalog = base_catalog
|
|
1465
|
-
self.previous_state = previous_state(
|
|
1466
|
-
Path(self.base_path),
|
|
1467
|
-
Path(self.runtime_config.target_path),
|
|
1468
|
-
Path(self.runtime_config.project_root),
|
|
1469
|
-
)
|
|
1470
|
-
self.previous_state.manifest = previous_manifest
|
|
1471
|
-
|
|
1472
|
-
# The dependencies of the review mode is derived from manifests.
|
|
1473
|
-
# It is a workaround solution to use macro dispatch
|
|
1474
|
-
# see: https://docs.getdbt.com/reference/dbt-jinja-functions/dispatch
|
|
1475
|
-
dependencies = {}
|
|
1476
|
-
for macro in self.manifest.macros.values():
|
|
1477
|
-
if macro.package_name not in dependencies:
|
|
1478
|
-
dependencies[macro.package_name] = self.runtime_config
|
|
1479
|
-
self.runtime_config.dependencies = dependencies
|
|
1480
|
-
|
|
1481
|
-
def refresh(self, refresh_file_path: str = None):
|
|
1482
|
-
# Refresh the artifacts
|
|
1483
|
-
if refresh_file_path is None:
|
|
1484
|
-
return self.load_artifacts()
|
|
1485
|
-
|
|
1486
|
-
# In single environment mode (target_path is equal to base_path),
|
|
1487
|
-
# we capture the original manifest as base and only update the current
|
|
1488
|
-
target_type = os.path.basename(os.path.dirname(refresh_file_path))
|
|
1489
|
-
if self.target_path and target_type == os.path.basename(self.target_path):
|
|
1490
|
-
if refresh_file_path.endswith("manifest.json"):
|
|
1491
|
-
self.curr_manifest = load_manifest(path=refresh_file_path)
|
|
1492
|
-
self.manifest = as_manifest(self.curr_manifest)
|
|
1493
|
-
self.get_cll_cached.cache_clear()
|
|
1494
|
-
self.get_change_analysis_cached.cache_clear()
|
|
1495
|
-
elif refresh_file_path.endswith("catalog.json"):
|
|
1496
|
-
self.curr_catalog = load_catalog(path=refresh_file_path)
|
|
1497
|
-
self.get_cll_cached.cache_clear()
|
|
1498
|
-
self.get_change_analysis_cached.cache_clear()
|
|
1499
|
-
elif self.base_path and target_type == os.path.basename(self.base_path):
|
|
1500
|
-
if refresh_file_path.endswith("manifest.json"):
|
|
1501
|
-
self.base_manifest = load_manifest(path=refresh_file_path)
|
|
1502
|
-
self.get_change_analysis_cached.cache_clear()
|
|
1503
|
-
elif refresh_file_path.endswith("catalog.json"):
|
|
1504
|
-
self.base_catalog = load_catalog(path=refresh_file_path)
|
|
1505
|
-
self.get_change_analysis_cached.cache_clear()
|
|
1506
|
-
|
|
1507
|
-
def create_relation(self, model, base=False):
|
|
1508
|
-
node = self.find_node_by_name(model, base)
|
|
1509
|
-
if node is None:
|
|
1510
|
-
return None
|
|
1511
|
-
|
|
1512
|
-
return self.adapter.Relation.create_from(self.runtime_config, node)
|
|
1513
|
-
|
|
1514
|
-
def select_nodes(
|
|
1515
|
-
self,
|
|
1516
|
-
select: Optional[str] = None,
|
|
1517
|
-
exclude: Optional[str] = None,
|
|
1518
|
-
packages: Optional[list[str]] = None,
|
|
1519
|
-
view_mode: Optional[Literal["all", "changed_models"]] = None,
|
|
1520
|
-
) -> Set[str]:
|
|
1521
|
-
import dbt.compilation
|
|
1522
|
-
from dbt.compilation import Compiler
|
|
1523
|
-
from dbt.graph import (
|
|
1524
|
-
NodeSelector,
|
|
1525
|
-
SelectionIntersection,
|
|
1526
|
-
SelectionUnion,
|
|
1527
|
-
parse_difference,
|
|
1528
|
-
)
|
|
1529
|
-
|
|
1530
|
-
select_list = [select] if select else None
|
|
1531
|
-
exclude_list = [exclude] if exclude else None
|
|
1532
|
-
|
|
1533
|
-
def _parse_difference(include, exclude):
|
|
1534
|
-
if dbt_version < "v1.8":
|
|
1535
|
-
return parse_difference(include, exclude, "eager")
|
|
1536
|
-
else:
|
|
1537
|
-
return parse_difference(include, exclude)
|
|
1538
|
-
|
|
1539
|
-
specs = [_parse_difference(select_list, exclude_list)]
|
|
1540
|
-
|
|
1541
|
-
# If packages is not provided, use the project name from manifest metadata as default
|
|
1542
|
-
if packages is None:
|
|
1543
|
-
if (
|
|
1544
|
-
self.manifest.metadata
|
|
1545
|
-
and hasattr(self.manifest.metadata, "project_name")
|
|
1546
|
-
and self.manifest.metadata.project_name
|
|
1547
|
-
):
|
|
1548
|
-
packages = [self.manifest.metadata.project_name]
|
|
1549
|
-
|
|
1550
|
-
if packages is not None:
|
|
1551
|
-
package_spec = SelectionUnion([_parse_difference([f"package:{p}"], None) for p in packages])
|
|
1552
|
-
specs.append(package_spec)
|
|
1553
|
-
if view_mode and view_mode == "changed_models":
|
|
1554
|
-
specs.append(_parse_difference(["1+state:modified+"], None))
|
|
1555
|
-
spec = SelectionIntersection(specs)
|
|
1556
|
-
|
|
1557
|
-
manifest = Manifest()
|
|
1558
|
-
manifest.metadata.adapter_type = self.adapter.type()
|
|
1559
|
-
manifest_prev = self.previous_state.manifest
|
|
1560
|
-
manifest_curr = self.manifest
|
|
1561
|
-
|
|
1562
|
-
manifest.nodes = {**manifest_curr.nodes}
|
|
1563
|
-
# # mark a node is removed if the node id is no in the curr nodes
|
|
1564
|
-
for node_id, node in manifest_prev.nodes.items():
|
|
1565
|
-
if node_id not in manifest.nodes:
|
|
1566
|
-
node_dict = node.to_dict()
|
|
1567
|
-
if "raw_code" in node_dict:
|
|
1568
|
-
node_dict["raw_code"] = "__removed__"
|
|
1569
|
-
node_class = type(node)
|
|
1570
|
-
removed_node = node_class.from_dict(node_dict)
|
|
1571
|
-
manifest.nodes[node_id] = removed_node
|
|
1572
|
-
|
|
1573
|
-
manifest.macros = {**manifest_prev.macros, **manifest_curr.macros}
|
|
1574
|
-
manifest.sources = {**manifest_prev.sources, **manifest_curr.sources}
|
|
1575
|
-
manifest.exposures = {**manifest_prev.exposures, **manifest_curr.exposures}
|
|
1576
|
-
manifest.metrics = {**manifest_prev.metrics, **manifest_curr.metrics}
|
|
1577
|
-
if hasattr(manifest_prev, "semantic_models"):
|
|
1578
|
-
manifest.semantic_models = {
|
|
1579
|
-
**manifest_prev.semantic_models,
|
|
1580
|
-
**manifest_curr.semantic_models,
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
compiler = Compiler(self.runtime_config)
|
|
1584
|
-
# disable to print compile states
|
|
1585
|
-
tmp_func = dbt.compilation.print_compile_stats
|
|
1586
|
-
dbt.compilation.print_compile_stats = lambda x: None
|
|
1587
|
-
graph = compiler.compile(manifest, write=False)
|
|
1588
|
-
dbt.compilation.print_compile_stats = tmp_func
|
|
1589
|
-
selector = NodeSelector(graph, manifest, previous_state=self.previous_state)
|
|
1590
|
-
|
|
1591
|
-
# disable "The selection criterion does not match"
|
|
1592
|
-
with silence_no_nodes_warning():
|
|
1593
|
-
return selector.get_selected(spec)
|
|
1594
|
-
|
|
1595
|
-
def export_artifacts(self) -> ArtifactsRoot:
|
|
1596
|
-
"""
|
|
1597
|
-
Export the artifacts from the current state
|
|
1598
|
-
"""
|
|
1599
|
-
artifacts = ArtifactsRoot()
|
|
1600
|
-
|
|
1601
|
-
def _load_artifact(artifact):
|
|
1602
|
-
return artifact.to_dict() if artifact else None
|
|
1603
|
-
|
|
1604
|
-
artifacts.base = {
|
|
1605
|
-
"manifest": _load_artifact(self.base_manifest),
|
|
1606
|
-
"catalog": _load_artifact(self.base_catalog),
|
|
1607
|
-
}
|
|
1608
|
-
artifacts.current = {
|
|
1609
|
-
"manifest": _load_artifact(self.curr_manifest),
|
|
1610
|
-
"catalog": _load_artifact(self.curr_catalog),
|
|
1611
|
-
}
|
|
1612
|
-
return artifacts
|
|
1613
|
-
|
|
1614
|
-
def export_artifacts_from_file(self) -> ArtifactsRoot:
|
|
1615
|
-
"""
|
|
1616
|
-
Export the artifacts from the state file. This is the old implementation
|
|
1617
|
-
"""
|
|
1618
|
-
artifacts = ArtifactsRoot()
|
|
1619
|
-
target_path = self.runtime_config.target_path
|
|
1620
|
-
target_base_path = self.base_path
|
|
1621
|
-
|
|
1622
|
-
def _load_artifact(path):
|
|
1623
|
-
if not os.path.isfile(path):
|
|
1624
|
-
return None
|
|
1625
|
-
|
|
1626
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
1627
|
-
json_content = f.read()
|
|
1628
|
-
return json.loads(json_content)
|
|
1629
|
-
|
|
1630
|
-
project_root = self.runtime_config.project_root
|
|
1631
|
-
artifacts.base = {
|
|
1632
|
-
"manifest": _load_artifact(os.path.join(project_root, target_base_path, "manifest.json")),
|
|
1633
|
-
"catalog": _load_artifact(os.path.join(project_root, target_base_path, "catalog.json")),
|
|
1634
|
-
}
|
|
1635
|
-
artifacts.current = {
|
|
1636
|
-
"manifest": _load_artifact(os.path.join(project_root, target_path, "manifest.json")),
|
|
1637
|
-
"catalog": _load_artifact(os.path.join(project_root, target_path, "catalog.json")),
|
|
1638
|
-
}
|
|
1639
|
-
return artifacts
|
|
1640
|
-
|
|
1641
|
-
def import_artifacts(self, artifacts: ArtifactsRoot, merge=True):
|
|
1642
|
-
# Merge the artifacts from the state file or cloud
|
|
1643
|
-
def _select_artifact(
|
|
1644
|
-
original: Union[WritableManifest, CatalogArtifact],
|
|
1645
|
-
new: Union[WritableManifest, CatalogArtifact],
|
|
1646
|
-
):
|
|
1647
|
-
if merge:
|
|
1648
|
-
if not original:
|
|
1649
|
-
return new
|
|
1650
|
-
if not new:
|
|
1651
|
-
return original
|
|
1652
|
-
return original if original.metadata.generated_at > new.metadata.generated_at else new
|
|
1653
|
-
else:
|
|
1654
|
-
return new
|
|
1655
|
-
|
|
1656
|
-
self.base_manifest = _select_artifact(self.base_manifest, load_manifest(data=artifacts.base.get("manifest")))
|
|
1657
|
-
self.curr_manifest = _select_artifact(self.curr_manifest, load_manifest(data=artifacts.current.get("manifest")))
|
|
1658
|
-
self.base_catalog = _select_artifact(self.base_catalog, load_catalog(data=artifacts.base.get("catalog")))
|
|
1659
|
-
self.curr_catalog = _select_artifact(self.curr_catalog, load_catalog(data=artifacts.current.get("catalog")))
|
|
1660
|
-
|
|
1661
|
-
self.manifest = as_manifest(self.curr_manifest)
|
|
1662
|
-
self.previous_state = previous_state(
|
|
1663
|
-
Path(self.base_path),
|
|
1664
|
-
Path(self.runtime_config.target_path),
|
|
1665
|
-
Path(self.runtime_config.project_root),
|
|
1666
|
-
)
|
|
1667
|
-
self.previous_state.manifest = as_manifest(self.base_manifest)
|
|
1668
|
-
|
|
1669
|
-
# The dependencies of the review mode is derived from manifests.
|
|
1670
|
-
# It is a workaround solution to use macro dispatch
|
|
1671
|
-
# see: https://docs.getdbt.com/reference/dbt-jinja-functions/dispatch
|
|
1672
|
-
dependencies = {}
|
|
1673
|
-
for macro in self.manifest.macros.values():
|
|
1674
|
-
if macro.package_name not in dependencies:
|
|
1675
|
-
dependencies[macro.package_name] = self.runtime_config
|
|
1676
|
-
|
|
1677
|
-
self.runtime_config.dependencies = dependencies
|
|
1678
|
-
|
|
1679
|
-
if not self.curr_manifest or not self.base_manifest:
|
|
1680
|
-
raise Exception(
|
|
1681
|
-
"No enough dbt artifacts in the state file. Please use the latest recce to generate the recce state"
|
|
1682
|
-
)
|
|
1683
|
-
|
|
1684
|
-
@contextmanager
|
|
1685
|
-
def connection_named(self, name: str) -> Iterator[None]:
|
|
1686
|
-
with self.adapter.connection_named(name):
|
|
1687
|
-
yield
|
|
1688
|
-
|
|
1689
|
-
def get_thread_connection(self) -> Connection:
|
|
1690
|
-
return self.adapter.connections.get_thread_connection()
|
|
1691
|
-
|
|
1692
|
-
def cancel(self, connection: Connection):
|
|
1693
|
-
self.adapter.connections.cancel(connection)
|
|
1694
|
-
|
|
1695
|
-
def __hash__(self):
|
|
1696
|
-
return id(self)
|
|
1697
|
-
|
|
1698
|
-
def __eq__(self, other):
|
|
1699
|
-
return self.__hash__() == other.__hash__()
|