@datarecce/ui 0.1.30 → 0.1.31
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/dist/api.d.mts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/components.d.mts +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/hooks.d.mts +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/{index-BNUP2V_N.d.ts → index-B9lSPJTi.d.ts} +184 -2
- package/dist/index-B9lSPJTi.d.ts.map +1 -0
- package/dist/{index-DOPZuhD8.d.mts → index-IIXVIoOL.d.mts} +253 -71
- package/dist/index-IIXVIoOL.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +4 -0
- package/dist/theme.d.mts +2 -185
- package/dist/theme.d.ts +2 -185
- package/dist/types.d.mts +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +4 -2
- package/recce-source/.editorconfig +26 -0
- package/recce-source/.flake8 +37 -0
- package/recce-source/.github/ISSUE_TEMPLATE/bug_report.yml +67 -0
- package/recce-source/.github/ISSUE_TEMPLATE/custom.md +10 -0
- package/recce-source/.github/ISSUE_TEMPLATE/feature_request.yml +42 -0
- package/recce-source/.github/PULL_REQUEST_TEMPLATE.md +21 -0
- package/recce-source/.github/copilot-instructions.md +331 -0
- package/recce-source/.github/instructions/backend-instructions.md +541 -0
- package/recce-source/.github/instructions/frontend-instructions.md +317 -0
- package/recce-source/.github/workflows/build-statics.yaml +72 -0
- package/recce-source/.github/workflows/bump.yaml +48 -0
- package/recce-source/.github/workflows/integration-tests-cloud.yaml +92 -0
- package/recce-source/.github/workflows/integration-tests-sqlmesh.yaml +33 -0
- package/recce-source/.github/workflows/integration-tests.yaml +52 -0
- package/recce-source/.github/workflows/nightly.yaml +246 -0
- package/recce-source/.github/workflows/release.yaml +196 -0
- package/recce-source/.github/workflows/tests-js.yaml +58 -0
- package/recce-source/.github/workflows/tests-python.yaml +128 -0
- package/recce-source/.pre-commit-config.yaml +26 -0
- package/recce-source/CLAUDE.md +483 -0
- package/recce-source/CODE_OF_CONDUCT.md +128 -0
- package/recce-source/CONTRIBUTING.md +107 -0
- package/recce-source/LICENSE +201 -0
- package/recce-source/Makefile +126 -0
- package/recce-source/README.md +182 -0
- package/recce-source/RECCE_CLOUD.md +81 -0
- package/recce-source/SECURITY.md +25 -0
- package/recce-source/docs/PACKAGING.md +340 -0
- package/recce-source/docs/README.md +1 -0
- package/recce-source/integration_tests/dbt/dbt_project.yml +26 -0
- package/recce-source/integration_tests/dbt/models/customers.sql +69 -0
- package/recce-source/integration_tests/dbt/models/docs.md +14 -0
- package/recce-source/integration_tests/dbt/models/orders.sql +56 -0
- package/recce-source/integration_tests/dbt/models/schema.yml +82 -0
- package/recce-source/integration_tests/dbt/models/staging/schema.yml +31 -0
- package/recce-source/integration_tests/dbt/models/staging/stg_customers.sql +22 -0
- package/recce-source/integration_tests/dbt/models/staging/stg_orders.sql +23 -0
- package/recce-source/integration_tests/dbt/models/staging/stg_payments.sql +25 -0
- package/recce-source/integration_tests/dbt/packages.yml +7 -0
- package/recce-source/integration_tests/dbt/profiles.yml +8 -0
- package/recce-source/integration_tests/dbt/seeds/raw_customers.csv +101 -0
- package/recce-source/integration_tests/dbt/seeds/raw_orders.csv +100 -0
- package/recce-source/integration_tests/dbt/seeds/raw_payments.csv +114 -0
- package/recce-source/integration_tests/dbt/seeds/raw_statuses.csv +5 -0
- package/recce-source/integration_tests/dbt/smoke_test.sh +72 -0
- package/recce-source/integration_tests/dbt/smoke_test_cloud.sh +71 -0
- package/recce-source/integration_tests/sqlmesh/__init__.py +0 -0
- package/recce-source/integration_tests/sqlmesh/audits/assert_item_price_above_zero.sql +9 -0
- package/recce-source/integration_tests/sqlmesh/audits/items.sql +7 -0
- package/recce-source/integration_tests/sqlmesh/audits/order_items.sql +7 -0
- package/recce-source/integration_tests/sqlmesh/config.py +171 -0
- package/recce-source/integration_tests/sqlmesh/helper.py +20 -0
- 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 +8 -0
- package/recce-source/integration_tests/sqlmesh/macros/macros.sql +8 -0
- package/recce-source/integration_tests/sqlmesh/macros/utils.py +11 -0
- package/recce-source/integration_tests/sqlmesh/metrics/metrics.sql +25 -0
- package/recce-source/integration_tests/sqlmesh/models/customer_revenue_by_day.sql +41 -0
- package/recce-source/integration_tests/sqlmesh/models/customer_revenue_lifetime.sql +60 -0
- package/recce-source/integration_tests/sqlmesh/models/customers.sql +32 -0
- package/recce-source/integration_tests/sqlmesh/models/items.py +95 -0
- package/recce-source/integration_tests/sqlmesh/models/marketing.sql +15 -0
- package/recce-source/integration_tests/sqlmesh/models/order_items.py +95 -0
- package/recce-source/integration_tests/sqlmesh/models/orders.py +70 -0
- package/recce-source/integration_tests/sqlmesh/models/raw_marketing.py +62 -0
- package/recce-source/integration_tests/sqlmesh/models/top_waiters.sql +23 -0
- package/recce-source/integration_tests/sqlmesh/models/waiter_as_customer_by_day.sql +29 -0
- package/recce-source/integration_tests/sqlmesh/models/waiter_names.sql +10 -0
- package/recce-source/integration_tests/sqlmesh/models/waiter_revenue_by_day.sql +29 -0
- package/recce-source/integration_tests/sqlmesh/models/waiters.py +62 -0
- package/recce-source/integration_tests/sqlmesh/prep_env.sh +16 -0
- package/recce-source/integration_tests/sqlmesh/schema.yaml +5 -0
- package/recce-source/integration_tests/sqlmesh/seeds/waiter_names.csv +11 -0
- package/recce-source/integration_tests/sqlmesh/test_server.sh +29 -0
- package/recce-source/integration_tests/sqlmesh/tests/test_customer_revenue_by_day.yaml +63 -0
- package/recce-source/integration_tests/sqlmesh/tests/test_order_items.yaml +72 -0
- package/recce-source/js/.editorconfig +27 -0
- package/recce-source/js/.env.development +5 -0
- package/recce-source/js/.husky/pre-commit +29 -0
- package/recce-source/js/.nvmrc +1 -0
- package/recce-source/js/README.md +39 -0
- package/recce-source/js/app/(mainComponents)/DisplayModeToggle.tsx +65 -0
- package/recce-source/js/app/(mainComponents)/NavBar.tsx +228 -0
- package/recce-source/js/app/(mainComponents)/RecceVersionBadge.tsx +107 -0
- package/recce-source/js/app/(mainComponents)/TopBar.tsx +252 -0
- package/recce-source/js/app/@lineage/default.tsx +20 -0
- package/recce-source/js/app/@lineage/page.tsx +14 -0
- package/recce-source/js/app/MainLayout.tsx +170 -0
- package/recce-source/js/app/Providers.tsx +49 -0
- package/recce-source/js/app/checks/page.tsx +296 -0
- package/recce-source/js/app/error.tsx +93 -0
- package/recce-source/js/app/favicon.ico +0 -0
- package/recce-source/js/app/global-error.tsx +115 -0
- package/recce-source/js/app/global.css +82 -0
- package/recce-source/js/app/layout.tsx +48 -0
- package/recce-source/js/app/lineage/page.tsx +15 -0
- package/recce-source/js/app/page.tsx +12 -0
- package/recce-source/js/app/query/page.tsx +8 -0
- package/recce-source/js/biome.json +313 -0
- package/recce-source/js/jest.config.js +34 -0
- package/recce-source/js/jest.globals.d.ts +32 -0
- package/recce-source/js/jest.setup.js +91 -0
- package/recce-source/js/next.config.js +16 -0
- package/recce-source/js/package-lock.json +13843 -0
- package/recce-source/js/package.json +123 -0
- package/recce-source/js/pnpm-lock.yaml +9235 -0
- package/recce-source/js/pnpm-workspace.yaml +6 -0
- package/recce-source/js/postcss.config.js +5 -0
- package/recce-source/js/public/auth_callback.html +68 -0
- 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 +4 -0
- package/recce-source/js/public/logo/recce-logo-white.png +0 -0
- package/recce-source/js/src/components/AuthModal/AuthModal.tsx +202 -0
- package/recce-source/js/src/components/app/AvatarDropdown.tsx +159 -0
- package/recce-source/js/src/components/app/EnvInfo.tsx +357 -0
- package/recce-source/js/src/components/app/Filename.tsx +388 -0
- package/recce-source/js/src/components/app/SetupConnectionPopover.tsx +91 -0
- package/recce-source/js/src/components/app/StateExporter.tsx +57 -0
- package/recce-source/js/src/components/app/StateImporter.tsx +198 -0
- package/recce-source/js/src/components/app/StateSharing.tsx +145 -0
- package/recce-source/js/src/components/app/StateSynchronizer.tsx +205 -0
- package/recce-source/js/src/components/charts/HistogramChart.tsx +291 -0
- package/recce-source/js/src/components/charts/SquareIcon.tsx +51 -0
- package/recce-source/js/src/components/charts/TopKSummaryList.tsx +457 -0
- package/recce-source/js/src/components/charts/chartTheme.ts +74 -0
- package/recce-source/js/src/components/check/CheckBreadcrumb.tsx +97 -0
- package/recce-source/js/src/components/check/CheckDescription.tsx +134 -0
- package/recce-source/js/src/components/check/CheckDetail.tsx +797 -0
- package/recce-source/js/src/components/check/CheckEmptyState.tsx +84 -0
- package/recce-source/js/src/components/check/CheckList.tsx +320 -0
- package/recce-source/js/src/components/check/LineageDiffView.tsx +32 -0
- package/recce-source/js/src/components/check/PresetCheckTemplateView.tsx +48 -0
- package/recce-source/js/src/components/check/SchemaDiffView.tsx +290 -0
- package/recce-source/js/src/components/check/check.ts +25 -0
- package/recce-source/js/src/components/check/timeline/CheckTimeline.tsx +163 -0
- package/recce-source/js/src/components/check/timeline/CommentInput.tsx +84 -0
- package/recce-source/js/src/components/check/timeline/TimelineEvent.tsx +468 -0
- package/recce-source/js/src/components/check/timeline/index.ts +12 -0
- package/recce-source/js/src/components/check/utils.ts +12 -0
- package/recce-source/js/src/components/data-grid/ScreenshotDataGrid.tsx +333 -0
- package/recce-source/js/src/components/data-grid/agGridStyles.css +55 -0
- package/recce-source/js/src/components/data-grid/agGridTheme.ts +43 -0
- package/recce-source/js/src/components/editor/CodeEditor.tsx +107 -0
- package/recce-source/js/src/components/editor/DiffEditor.tsx +162 -0
- package/recce-source/js/src/components/editor/index.ts +12 -0
- package/recce-source/js/src/components/errorboundary/ErrorBoundary.tsx +87 -0
- package/recce-source/js/src/components/histogram/HistogramDiffForm.tsx +147 -0
- package/recce-source/js/src/components/histogram/HistogramDiffResultView.tsx +63 -0
- package/recce-source/js/src/components/icons/index.tsx +142 -0
- package/recce-source/js/src/components/lineage/ActionControl.tsx +63 -0
- package/recce-source/js/src/components/lineage/ActionTag.tsx +141 -0
- package/recce-source/js/src/components/lineage/ChangeStatusLegend.tsx +46 -0
- package/recce-source/js/src/components/lineage/ColumnLevelLineageControl.tsx +327 -0
- package/recce-source/js/src/components/lineage/ColumnLevelLineageLegend.tsx +57 -0
- package/recce-source/js/src/components/lineage/GraphColumnNode.tsx +199 -0
- package/recce-source/js/src/components/lineage/GraphEdge.tsx +59 -0
- package/recce-source/js/src/components/lineage/GraphNode.tsx +555 -0
- package/recce-source/js/src/components/lineage/LineagePage.tsx +10 -0
- package/recce-source/js/src/components/lineage/LineageView.tsx +1384 -0
- package/recce-source/js/src/components/lineage/LineageViewContext.tsx +86 -0
- package/recce-source/js/src/components/lineage/LineageViewContextMenu.tsx +637 -0
- package/recce-source/js/src/components/lineage/LineageViewNotification.tsx +64 -0
- package/recce-source/js/src/components/lineage/LineageViewTopBar.tsx +596 -0
- package/recce-source/js/src/components/lineage/NodeSqlView.tsx +136 -0
- package/recce-source/js/src/components/lineage/NodeTag.tsx +278 -0
- package/recce-source/js/src/components/lineage/NodeView.tsx +642 -0
- package/recce-source/js/src/components/lineage/SandboxView.tsx +436 -0
- package/recce-source/js/src/components/lineage/ServerDisconnectedModalContent.tsx +105 -0
- package/recce-source/js/src/components/lineage/SetupConnectionBanner.tsx +52 -0
- package/recce-source/js/src/components/lineage/SingleEnvironmentQueryView.tsx +152 -0
- package/recce-source/js/src/components/lineage/graph.test.ts +31 -0
- package/recce-source/js/src/components/lineage/graph.ts +58 -0
- package/recce-source/js/src/components/lineage/lineage.test.ts +169 -0
- package/recce-source/js/src/components/lineage/lineage.ts +521 -0
- package/recce-source/js/src/components/lineage/styles.css +42 -0
- package/recce-source/js/src/components/lineage/styles.tsx +165 -0
- package/recce-source/js/src/components/lineage/useMultiNodesAction.ts +352 -0
- package/recce-source/js/src/components/lineage/useValueDiffAlertDialog.tsx +108 -0
- package/recce-source/js/src/components/onboarding-guide/Notification.tsx +62 -0
- package/recce-source/js/src/components/profile/ProfileDiffForm.tsx +134 -0
- package/recce-source/js/src/components/profile/ProfileDiffResultView.tsx +245 -0
- package/recce-source/js/src/components/query/ChangedOnlyCheckbox.tsx +29 -0
- package/recce-source/js/src/components/query/DiffText.tsx +120 -0
- package/recce-source/js/src/components/query/QueryDiffResultView.tsx +470 -0
- package/recce-source/js/src/components/query/QueryForm.tsx +80 -0
- package/recce-source/js/src/components/query/QueryPage.tsx +282 -0
- package/recce-source/js/src/components/query/QueryResultView.tsx +180 -0
- package/recce-source/js/src/components/query/SetupConnectionGuide.tsx +57 -0
- package/recce-source/js/src/components/query/SqlEditor.tsx +245 -0
- package/recce-source/js/src/components/query/ToggleSwitch.tsx +84 -0
- package/recce-source/js/src/components/query/styles.css +21 -0
- package/recce-source/js/src/components/routing/DirectUrlAccess.test.tsx +428 -0
- package/recce-source/js/src/components/routing/LineageStatePreservation.test.tsx +311 -0
- package/recce-source/js/src/components/routing/Navigation.test.tsx +256 -0
- package/recce-source/js/src/components/rowcount/RowCountDiffResultView.tsx +109 -0
- package/recce-source/js/src/components/rowcount/delta.ts +11 -0
- package/recce-source/js/src/components/run/RunList.tsx +303 -0
- package/recce-source/js/src/components/run/RunModal.tsx +191 -0
- package/recce-source/js/src/components/run/RunPage.tsx +26 -0
- package/recce-source/js/src/components/run/RunResultPane.tsx +454 -0
- package/recce-source/js/src/components/run/RunStatusAndDate.tsx +106 -0
- package/recce-source/js/src/components/run/RunToolbar.tsx +70 -0
- package/recce-source/js/src/components/run/RunView.tsx +196 -0
- package/recce-source/js/src/components/run/registry.ts +214 -0
- package/recce-source/js/src/components/run/types.ts +14 -0
- package/recce-source/js/src/components/schema/ColumnNameCell.test.tsx +169 -0
- package/recce-source/js/src/components/schema/ColumnNameCell.tsx +198 -0
- package/recce-source/js/src/components/schema/SchemaView.tsx +337 -0
- package/recce-source/js/src/components/schema/schemaDiff.ts +32 -0
- package/recce-source/js/src/components/schema/style.css +134 -0
- package/recce-source/js/src/components/screenshot/ScreenshotBox.tsx +39 -0
- package/recce-source/js/src/components/shared/HistoryToggle.tsx +35 -0
- package/recce-source/js/src/components/split/Split.tsx +40 -0
- package/recce-source/js/src/components/split/styles.css +24 -0
- package/recce-source/js/src/components/summary/ChangeSummary.tsx +264 -0
- package/recce-source/js/src/components/summary/SchemaSummary.tsx +123 -0
- package/recce-source/js/src/components/summary/SummaryView.tsx +29 -0
- package/recce-source/js/src/components/timeout/IdleTimeoutBadge.tsx +48 -0
- package/recce-source/js/src/components/top-k/TopKDiffForm.tsx +58 -0
- package/recce-source/js/src/components/top-k/TopKDiffResultView.tsx +73 -0
- package/recce-source/js/src/components/ui/dataGrid/DataFrameColumnGroupHeader.tsx +228 -0
- package/recce-source/js/src/components/ui/dataGrid/DataFrameColumnHeader.tsx +113 -0
- package/recce-source/js/src/components/ui/dataGrid/defaultRenderCell.tsx +72 -0
- package/recce-source/js/src/components/ui/dataGrid/index.ts +23 -0
- package/recce-source/js/src/components/ui/dataGrid/inlineRenderCell.test.tsx +607 -0
- package/recce-source/js/src/components/ui/dataGrid/inlineRenderCell.tsx +211 -0
- package/recce-source/js/src/components/ui/dataGrid/schemaCells.test.tsx +452 -0
- package/recce-source/js/src/components/ui/dataGrid/schemaCells.tsx +142 -0
- package/recce-source/js/src/components/ui/dataGrid/valueDiffCells.test.tsx +178 -0
- package/recce-source/js/src/components/ui/dataGrid/valueDiffCells.tsx +275 -0
- package/recce-source/js/src/components/ui/markdown/ExternalLinkConfirmDialog.tsx +134 -0
- package/recce-source/js/src/components/ui/markdown/MarkdownContent.tsx +364 -0
- package/recce-source/js/src/components/ui/mui/index.ts +13 -0
- package/recce-source/js/src/components/ui/mui-provider.tsx +67 -0
- package/recce-source/js/src/components/ui/mui-theme.ts +1039 -0
- package/recce-source/js/src/components/ui/mui-utils.ts +113 -0
- package/recce-source/js/src/components/ui/toaster.tsx +288 -0
- package/recce-source/js/src/components/valuediff/ValueDiffDetailResultView.tsx +217 -0
- package/recce-source/js/src/components/valuediff/ValueDiffForm.tsx +246 -0
- package/recce-source/js/src/components/valuediff/ValueDiffResultView.tsx +82 -0
- package/recce-source/js/src/components/valuediff/shared.ts +33 -0
- package/recce-source/js/src/constants/tooltipMessage.ts +3 -0
- package/recce-source/js/src/constants/urls.ts +1 -0
- package/recce-source/js/src/lib/UrlHash.ts +12 -0
- package/recce-source/js/src/lib/api/adhocQuery.ts +70 -0
- package/recce-source/js/src/lib/api/axiosClient.ts +9 -0
- package/recce-source/js/src/lib/api/cacheKeys.ts +13 -0
- package/recce-source/js/src/lib/api/checkEvents.ts +252 -0
- package/recce-source/js/src/lib/api/checks.ts +129 -0
- package/recce-source/js/src/lib/api/cll.ts +53 -0
- package/recce-source/js/src/lib/api/connectToCloud.ts +13 -0
- package/recce-source/js/src/lib/api/flag.ts +37 -0
- package/recce-source/js/src/lib/api/info.ts +198 -0
- package/recce-source/js/src/lib/api/instanceInfo.ts +25 -0
- package/recce-source/js/src/lib/api/keepAlive.ts +108 -0
- package/recce-source/js/src/lib/api/lineagecheck.ts +35 -0
- package/recce-source/js/src/lib/api/localStorageKeys.ts +7 -0
- package/recce-source/js/src/lib/api/models.ts +59 -0
- package/recce-source/js/src/lib/api/profile.ts +65 -0
- package/recce-source/js/src/lib/api/rowcount.ts +19 -0
- package/recce-source/js/src/lib/api/runs.ts +174 -0
- package/recce-source/js/src/lib/api/schemacheck.ts +31 -0
- package/recce-source/js/src/lib/api/select.ts +25 -0
- package/recce-source/js/src/lib/api/sessionStorageKeys.ts +8 -0
- package/recce-source/js/src/lib/api/state.ts +117 -0
- package/recce-source/js/src/lib/api/track.ts +281 -0
- package/recce-source/js/src/lib/api/types.ts +284 -0
- package/recce-source/js/src/lib/api/user.ts +42 -0
- package/recce-source/js/src/lib/api/valuediff.ts +46 -0
- package/recce-source/js/src/lib/api/version.ts +40 -0
- package/recce-source/js/src/lib/const.ts +9 -0
- package/recce-source/js/src/lib/dataGrid/crossFunctionConsistency.test.ts +626 -0
- package/recce-source/js/src/lib/dataGrid/dataGridFactory.test.ts +2140 -0
- package/recce-source/js/src/lib/dataGrid/dataGridFactory.ts +397 -0
- package/recce-source/js/src/lib/dataGrid/generators/rowCountUtils.test.ts +132 -0
- package/recce-source/js/src/lib/dataGrid/generators/rowCountUtils.ts +126 -0
- package/recce-source/js/src/lib/dataGrid/generators/toDataDiffGrid.test.ts +1627 -0
- package/recce-source/js/src/lib/dataGrid/generators/toDataDiffGrid.ts +140 -0
- package/recce-source/js/src/lib/dataGrid/generators/toDataGrid.ts +67 -0
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDataGrid.test.ts +142 -0
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDataGrid.ts +71 -0
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDiffDataGrid.test.ts +258 -0
- package/recce-source/js/src/lib/dataGrid/generators/toRowCountDiffDataGrid.ts +153 -0
- package/recce-source/js/src/lib/dataGrid/generators/toSchemaDataGrid.test.ts +951 -0
- package/recce-source/js/src/lib/dataGrid/generators/toSchemaDataGrid.ts +221 -0
- package/recce-source/js/src/lib/dataGrid/generators/toValueDataGrid.test.ts +395 -0
- package/recce-source/js/src/lib/dataGrid/generators/toValueDataGrid.ts +184 -0
- package/recce-source/js/src/lib/dataGrid/generators/toValueDiffGrid.test.ts +884 -0
- package/recce-source/js/src/lib/dataGrid/generators/toValueDiffGrid.ts +113 -0
- package/recce-source/js/src/lib/dataGrid/index.ts +51 -0
- package/recce-source/js/src/lib/dataGrid/propertyBased.test.ts +858 -0
- package/recce-source/js/src/lib/dataGrid/shared/columnBuilders.test.ts +482 -0
- package/recce-source/js/src/lib/dataGrid/shared/columnBuilders.ts +345 -0
- package/recce-source/js/src/lib/dataGrid/shared/dataTypeEdgeCases.test.ts +698 -0
- package/recce-source/js/src/lib/dataGrid/shared/diffColumnBuilder.test.tsx +820 -0
- package/recce-source/js/src/lib/dataGrid/shared/diffColumnBuilder.tsx +277 -0
- package/recce-source/js/src/lib/dataGrid/shared/gridUtils.test.ts +785 -0
- package/recce-source/js/src/lib/dataGrid/shared/gridUtils.ts +370 -0
- package/recce-source/js/src/lib/dataGrid/shared/index.ts +81 -0
- package/recce-source/js/src/lib/dataGrid/shared/rowBuilders.test.ts +909 -0
- package/recce-source/js/src/lib/dataGrid/shared/rowBuilders.ts +325 -0
- package/recce-source/js/src/lib/dataGrid/shared/simpleColumnBuilder.tsx +240 -0
- package/recce-source/js/src/lib/dataGrid/shared/toDiffColumn.test.tsx +719 -0
- package/recce-source/js/src/lib/dataGrid/shared/toDiffColumn.tsx +231 -0
- package/recce-source/js/src/lib/dataGrid/shared/validation.test.ts +559 -0
- package/recce-source/js/src/lib/dataGrid/shared/validation.ts +367 -0
- package/recce-source/js/src/lib/dataGrid/warehouseNamingConventions.test.ts +1117 -0
- package/recce-source/js/src/lib/formatSelect.ts +50 -0
- package/recce-source/js/src/lib/hooks/ApiConfigContext.tsx +181 -0
- package/recce-source/js/src/lib/hooks/IdleTimeoutContext.tsx +177 -0
- package/recce-source/js/src/lib/hooks/LineageGraphContext.tsx +512 -0
- package/recce-source/js/src/lib/hooks/RecceActionContext.tsx +269 -0
- package/recce-source/js/src/lib/hooks/RecceCheckContext.tsx +33 -0
- package/recce-source/js/src/lib/hooks/RecceContextProvider.tsx +54 -0
- package/recce-source/js/src/lib/hooks/RecceInstanceContext.tsx +129 -0
- package/recce-source/js/src/lib/hooks/RecceQueryContext.tsx +98 -0
- package/recce-source/js/src/lib/hooks/RecceShareStateContext.tsx +59 -0
- package/recce-source/js/src/lib/hooks/ScreenShot.tsx +399 -0
- package/recce-source/js/src/lib/hooks/useAppRouter.test.ts +211 -0
- package/recce-source/js/src/lib/hooks/useAppRouter.ts +200 -0
- package/recce-source/js/src/lib/hooks/useCheckEvents.ts +99 -0
- package/recce-source/js/src/lib/hooks/useCheckToast.tsx +14 -0
- package/recce-source/js/src/lib/hooks/useClipBoardToast.tsx +27 -0
- package/recce-source/js/src/lib/hooks/useCountdownToast.tsx +102 -0
- package/recce-source/js/src/lib/hooks/useFeedbackCollectionToast.tsx +130 -0
- package/recce-source/js/src/lib/hooks/useGuideToast.tsx +45 -0
- package/recce-source/js/src/lib/hooks/useIdleDetection.tsx +185 -0
- package/recce-source/js/src/lib/hooks/useModelColumns.tsx +113 -0
- package/recce-source/js/src/lib/hooks/useRecceInstanceInfo.tsx +13 -0
- package/recce-source/js/src/lib/hooks/useRecceServerFlag.tsx +13 -0
- package/recce-source/js/src/lib/hooks/useRun.tsx +89 -0
- package/recce-source/js/src/lib/hooks/useThemeColors.ts +115 -0
- package/recce-source/js/src/lib/mergeKeys.test.ts +89 -0
- package/recce-source/js/src/lib/mergeKeys.ts +86 -0
- package/recce-source/js/src/lib/result/ResultErrorFallback.tsx +9 -0
- package/recce-source/js/src/lib/utils/formatTime.ts +84 -0
- package/recce-source/js/src/lib/utils/urls.ts +16 -0
- package/recce-source/js/src/utils/DropdownValuesInput.tsx +297 -0
- package/recce-source/js/src/utils/formatters.tsx +237 -0
- package/recce-source/js/src/utils/transforms.ts +81 -0
- package/recce-source/js/tsconfig.json +47 -0
- package/recce-source/macros/README.md +8 -0
- package/recce-source/macros/recce_athena.sql +73 -0
- package/recce-source/pyproject.toml +109 -0
- package/recce-source/recce/VERSION +1 -0
- package/recce-source/recce/__init__.py +84 -0
- package/recce-source/recce/adapter/__init__.py +0 -0
- package/recce-source/recce/adapter/base.py +109 -0
- package/recce-source/recce/adapter/dbt_adapter/__init__.py +1699 -0
- package/recce-source/recce/adapter/dbt_adapter/dbt_version.py +42 -0
- package/recce-source/recce/adapter/sqlmesh_adapter.py +141 -0
- package/recce-source/recce/apis/__init__.py +0 -0
- package/recce-source/recce/apis/check_api.py +203 -0
- package/recce-source/recce/apis/check_events_api.py +353 -0
- package/recce-source/recce/apis/check_func.py +130 -0
- package/recce-source/recce/apis/run_api.py +130 -0
- package/recce-source/recce/apis/run_func.py +258 -0
- package/recce-source/recce/artifact.py +266 -0
- package/recce-source/recce/cli.py +1846 -0
- package/recce-source/recce/config.py +127 -0
- package/recce-source/recce/connect_to_cloud.py +138 -0
- package/recce-source/recce/core.py +334 -0
- package/recce-source/recce/diff.py +26 -0
- package/recce-source/recce/event/CONFIG +1 -0
- package/recce-source/recce/event/SENTRY_DNS +1 -0
- package/recce-source/recce/event/__init__.py +304 -0
- package/recce-source/recce/event/collector.py +184 -0
- package/recce-source/recce/event/track.py +158 -0
- package/recce-source/recce/exceptions.py +21 -0
- package/recce-source/recce/git.py +77 -0
- package/recce-source/recce/github.py +222 -0
- package/recce-source/recce/mcp_server.py +861 -0
- package/recce-source/recce/models/__init__.py +6 -0
- package/recce-source/recce/models/check.py +473 -0
- package/recce-source/recce/models/run.py +46 -0
- package/recce-source/recce/models/types.py +218 -0
- package/recce-source/recce/pull_request.py +124 -0
- package/recce-source/recce/run.py +390 -0
- package/recce-source/recce/server.py +877 -0
- package/recce-source/recce/state/__init__.py +31 -0
- package/recce-source/recce/state/cloud.py +644 -0
- package/recce-source/recce/state/const.py +26 -0
- package/recce-source/recce/state/local.py +56 -0
- package/recce-source/recce/state/state.py +119 -0
- package/recce-source/recce/state/state_loader.py +174 -0
- package/recce-source/recce/summary.py +575 -0
- package/recce-source/recce/tasks/__init__.py +23 -0
- package/recce-source/recce/tasks/core.py +134 -0
- package/recce-source/recce/tasks/dataframe.py +170 -0
- package/recce-source/recce/tasks/histogram.py +433 -0
- package/recce-source/recce/tasks/lineage.py +19 -0
- package/recce-source/recce/tasks/profile.py +298 -0
- package/recce-source/recce/tasks/query.py +450 -0
- package/recce-source/recce/tasks/rowcount.py +277 -0
- package/recce-source/recce/tasks/schema.py +65 -0
- package/recce-source/recce/tasks/top_k.py +172 -0
- package/recce-source/recce/tasks/utils.py +147 -0
- package/recce-source/recce/tasks/valuediff.py +497 -0
- package/recce-source/recce/util/__init__.py +4 -0
- package/recce-source/recce/util/api_token.py +80 -0
- package/recce-source/recce/util/breaking.py +330 -0
- package/recce-source/recce/util/cache.py +25 -0
- package/recce-source/recce/util/cll.py +355 -0
- package/recce-source/recce/util/cloud/__init__.py +15 -0
- package/recce-source/recce/util/cloud/base.py +115 -0
- package/recce-source/recce/util/cloud/check_events.py +190 -0
- package/recce-source/recce/util/cloud/checks.py +242 -0
- package/recce-source/recce/util/io.py +120 -0
- package/recce-source/recce/util/lineage.py +83 -0
- package/recce-source/recce/util/logger.py +25 -0
- package/recce-source/recce/util/onboarding_state.py +45 -0
- package/recce-source/recce/util/perf_tracking.py +85 -0
- package/recce-source/recce/util/pydantic_model.py +22 -0
- package/recce-source/recce/util/recce_cloud.py +454 -0
- package/recce-source/recce/util/singleton.py +18 -0
- package/recce-source/recce/util/startup_perf.py +121 -0
- package/recce-source/recce/yaml/__init__.py +58 -0
- package/recce-source/recce_cloud/README.md +780 -0
- package/recce-source/recce_cloud/VERSION +1 -0
- package/recce-source/recce_cloud/__init__.py +24 -0
- package/recce-source/recce_cloud/api/__init__.py +17 -0
- package/recce-source/recce_cloud/api/base.py +132 -0
- package/recce-source/recce_cloud/api/client.py +186 -0
- package/recce-source/recce_cloud/api/exceptions.py +26 -0
- package/recce-source/recce_cloud/api/factory.py +63 -0
- package/recce-source/recce_cloud/api/github.py +106 -0
- package/recce-source/recce_cloud/api/gitlab.py +111 -0
- package/recce-source/recce_cloud/artifact.py +57 -0
- package/recce-source/recce_cloud/ci_providers/__init__.py +9 -0
- package/recce-source/recce_cloud/ci_providers/base.py +82 -0
- package/recce-source/recce_cloud/ci_providers/detector.py +147 -0
- package/recce-source/recce_cloud/ci_providers/github_actions.py +136 -0
- package/recce-source/recce_cloud/ci_providers/gitlab_ci.py +130 -0
- package/recce-source/recce_cloud/cli.py +434 -0
- package/recce-source/recce_cloud/download.py +230 -0
- package/recce-source/recce_cloud/hatch_build.py +20 -0
- package/recce-source/recce_cloud/pyproject.toml +49 -0
- package/recce-source/recce_cloud/upload.py +214 -0
- 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 +17 -0
- package/recce-source/tests/adapter/dbt_adapter/dbt_test_helper.py +298 -0
- package/recce-source/tests/adapter/dbt_adapter/test_dbt_adapter.py +25 -0
- package/recce-source/tests/adapter/dbt_adapter/test_dbt_cll.py +717 -0
- package/recce-source/tests/adapter/dbt_adapter/test_proj/dbt_project.yml +4 -0
- package/recce-source/tests/adapter/dbt_adapter/test_proj/manifest.json +1 -0
- package/recce-source/tests/adapter/dbt_adapter/test_proj/package-lock.yml +8 -0
- package/recce-source/tests/adapter/dbt_adapter/test_proj/packages.yml +7 -0
- package/recce-source/tests/adapter/dbt_adapter/test_proj/profiles.yml +6 -0
- package/recce-source/tests/adapter/dbt_adapter/test_selector.py +205 -0
- package/recce-source/tests/apis/__init__.py +0 -0
- package/recce-source/tests/apis/row_count_diff.json +59 -0
- package/recce-source/tests/apis/test_check_events_api.py +615 -0
- package/recce-source/tests/apis/test_run_func.py +433 -0
- package/recce-source/tests/catalog.json +527 -0
- package/recce-source/tests/data/manifest/base/catalog.json +1 -0
- package/recce-source/tests/data/manifest/base/manifest.json +1 -0
- package/recce-source/tests/data/manifest/pr2/catalog.json +1 -0
- package/recce-source/tests/data/manifest/pr2/manifest.json +1 -0
- package/recce-source/tests/manifest.json +10655 -0
- package/recce-source/tests/models/__init__.py +0 -0
- package/recce-source/tests/models/test_check.py +731 -0
- package/recce-source/tests/models/test_run_models.py +295 -0
- package/recce-source/tests/recce_cloud/__init__.py +0 -0
- package/recce-source/tests/recce_cloud/test_ci_providers.py +351 -0
- package/recce-source/tests/recce_cloud/test_cli.py +735 -0
- package/recce-source/tests/recce_cloud/test_client.py +379 -0
- package/recce-source/tests/recce_cloud/test_platform_clients.py +483 -0
- package/recce-source/tests/recce_state.json +1 -0
- package/recce-source/tests/state/test_cloud.py +719 -0
- package/recce-source/tests/state/test_local.py +164 -0
- package/recce-source/tests/state/test_state_loader.py +211 -0
- package/recce-source/tests/tasks/__init__.py +0 -0
- package/recce-source/tests/tasks/conftest.py +4 -0
- package/recce-source/tests/tasks/test_histogram.py +129 -0
- package/recce-source/tests/tasks/test_lineage.py +55 -0
- package/recce-source/tests/tasks/test_preset_checks.py +64 -0
- package/recce-source/tests/tasks/test_profile.py +397 -0
- package/recce-source/tests/tasks/test_query.py +528 -0
- package/recce-source/tests/tasks/test_row_count.py +133 -0
- package/recce-source/tests/tasks/test_schema.py +122 -0
- package/recce-source/tests/tasks/test_top_k.py +77 -0
- package/recce-source/tests/tasks/test_utils.py +439 -0
- package/recce-source/tests/tasks/test_valuediff.py +361 -0
- package/recce-source/tests/test_cli.py +236 -0
- package/recce-source/tests/test_cli_mcp_optional.py +45 -0
- package/recce-source/tests/test_cloud_listing_cli.py +324 -0
- package/recce-source/tests/test_config.py +43 -0
- package/recce-source/tests/test_connect_to_cloud.py +82 -0
- package/recce-source/tests/test_core.py +174 -0
- package/recce-source/tests/test_dbt.py +36 -0
- package/recce-source/tests/test_mcp_server.py +505 -0
- package/recce-source/tests/test_pull_request.py +130 -0
- package/recce-source/tests/test_server.py +202 -0
- package/recce-source/tests/test_server_lifespan.py +138 -0
- package/recce-source/tests/test_summary.py +73 -0
- 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 +255 -0
- package/recce-source/tests/util/cloud/test_checks.py +204 -0
- package/recce-source/tests/util/test_api_token.py +119 -0
- package/recce-source/tests/util/test_breaking.py +1427 -0
- package/recce-source/tests/util/test_cll.py +706 -0
- package/recce-source/tests/util/test_lineage.py +122 -0
- package/recce-source/tests/util/test_onboarding_state.py +84 -0
- package/recce-source/tests/util/test_recce_cloud.py +231 -0
- package/recce-source/tox.ini +40 -0
- package/recce-source/uv.lock +3928 -0
- package/src/api/index.ts +32 -0
- package/src/components/index.ts +154 -0
- package/src/global.d.ts +14 -0
- package/src/hooks/index.ts +56 -0
- package/src/index.ts +17 -0
- package/src/lib/hooks/RouteConfigContext.ts +139 -0
- package/src/lib/hooks/useAppRouter.ts +240 -0
- package/src/mui-augmentation.d.ts +139 -0
- package/src/theme/index.ts +13 -0
- package/src/theme.ts +23 -0
- package/src/types/index.ts +23 -0
- package/dist/index-BNUP2V_N.d.ts.map +0 -1
- package/dist/index-DOPZuhD8.d.mts.map +0 -1
- package/dist/theme.d.mts.map +0 -1
- package/dist/theme.d.ts.map +0 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import sqlglot.expressions as exp
|
|
6
|
+
from sqlglot import Dialect, parse_one
|
|
7
|
+
from sqlglot.errors import OptimizeError, SqlglotError
|
|
8
|
+
from sqlglot.optimizer import Scope, traverse_scope
|
|
9
|
+
from sqlglot.optimizer.qualify import qualify
|
|
10
|
+
|
|
11
|
+
from recce.exceptions import RecceException
|
|
12
|
+
from recce.models.types import CllColumn, CllColumnDep
|
|
13
|
+
|
|
14
|
+
CllResult = Tuple[
|
|
15
|
+
List[CllColumnDep], # Model to column dependencies
|
|
16
|
+
Dict[str, CllColumn], # Column to column dependencies
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class CLLPerformanceTracking:
|
|
22
|
+
lineage_start = None
|
|
23
|
+
lineage_elapsed = None
|
|
24
|
+
column_lineage_start = None
|
|
25
|
+
column_lineage_elapsed = None
|
|
26
|
+
|
|
27
|
+
total_nodes = None
|
|
28
|
+
sqlglot_error_nodes = 0
|
|
29
|
+
other_error_nodes = 0
|
|
30
|
+
|
|
31
|
+
def start_lineage(self):
|
|
32
|
+
self.lineage_start = time.perf_counter_ns()
|
|
33
|
+
|
|
34
|
+
def end_lineage(self):
|
|
35
|
+
if self.lineage_start is None:
|
|
36
|
+
return
|
|
37
|
+
self.lineage_elapsed = (time.perf_counter_ns() - self.lineage_start) / 1000000
|
|
38
|
+
|
|
39
|
+
def start_column_lineage(self):
|
|
40
|
+
self.column_lineage_start = time.perf_counter_ns()
|
|
41
|
+
|
|
42
|
+
def end_column_lineage(self):
|
|
43
|
+
if self.column_lineage_start is None:
|
|
44
|
+
return
|
|
45
|
+
self.column_lineage_elapsed = (time.perf_counter_ns() - self.column_lineage_start) / 1000000
|
|
46
|
+
|
|
47
|
+
def set_total_nodes(self, total_nodes):
|
|
48
|
+
self.total_nodes = total_nodes
|
|
49
|
+
|
|
50
|
+
def increment_sqlglot_error_nodes(self):
|
|
51
|
+
self.sqlglot_error_nodes += 1
|
|
52
|
+
|
|
53
|
+
def increment_other_error_nodes(self):
|
|
54
|
+
self.other_error_nodes += 1
|
|
55
|
+
|
|
56
|
+
def to_dict(self):
|
|
57
|
+
return {
|
|
58
|
+
"lineage_elapsed_ms": self.lineage_elapsed,
|
|
59
|
+
"column_lineage_elapsed_ms": self.column_lineage_elapsed,
|
|
60
|
+
"total_nodes": self.total_nodes,
|
|
61
|
+
"sqlglot_error_nodes": self.sqlglot_error_nodes,
|
|
62
|
+
"other_error_nodes": self.other_error_nodes,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
def reset(self):
|
|
66
|
+
self.lineage_start = None
|
|
67
|
+
self.lineage_elapsed = None
|
|
68
|
+
self.column_lineage_start = None
|
|
69
|
+
self.column_lineage_elapsed = None
|
|
70
|
+
|
|
71
|
+
self.total_nodes = None
|
|
72
|
+
self.sqlglot_error_nodes = 0
|
|
73
|
+
self.other_error_nodes = 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _cll_column(proj, table_alias_map) -> CllColumn:
|
|
77
|
+
# given an expression, return the columns depends on
|
|
78
|
+
# [{node: table, column: column}, ...]
|
|
79
|
+
type = "source"
|
|
80
|
+
depends_on: List[CllColumnDep] = []
|
|
81
|
+
|
|
82
|
+
# instance of Column
|
|
83
|
+
if isinstance(proj, exp.Alias):
|
|
84
|
+
# 'select a as b'
|
|
85
|
+
# 'select CURRENT_TIMESTAMP() as create_at'
|
|
86
|
+
root = proj.this
|
|
87
|
+
|
|
88
|
+
for expression in root.walk(bfs=False):
|
|
89
|
+
if isinstance(expression, exp.Column):
|
|
90
|
+
column = expression
|
|
91
|
+
alias = column.table
|
|
92
|
+
|
|
93
|
+
if alias is None:
|
|
94
|
+
table = next(iter(table_alias_map.values()))
|
|
95
|
+
else:
|
|
96
|
+
table = table_alias_map.get(alias, alias)
|
|
97
|
+
depends_on.append(CllColumnDep(table, column.name))
|
|
98
|
+
if type == "source":
|
|
99
|
+
type = "passthrough"
|
|
100
|
+
elif isinstance(expression, (exp.Paren, exp.Identifier)):
|
|
101
|
+
pass
|
|
102
|
+
else:
|
|
103
|
+
type = "derived"
|
|
104
|
+
|
|
105
|
+
depends_on = _dedeup_depends_on(depends_on)
|
|
106
|
+
|
|
107
|
+
if len(depends_on) == 0:
|
|
108
|
+
type = "source"
|
|
109
|
+
|
|
110
|
+
if isinstance(proj, exp.Alias):
|
|
111
|
+
alias = proj
|
|
112
|
+
if type == "passthrough" and depends_on[0].column != alias.alias_or_name:
|
|
113
|
+
type = "renamed"
|
|
114
|
+
|
|
115
|
+
return CllColumn(type=type, depends_on=depends_on)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _dedeup_depends_on(depends_on: List[CllColumnDep]) -> List[CllColumnDep]:
|
|
119
|
+
# deduplicate the depends_on list
|
|
120
|
+
dedup_set = set()
|
|
121
|
+
dedup_list = []
|
|
122
|
+
for col_dep in depends_on:
|
|
123
|
+
node_col = col_dep.node + "." + col_dep.column
|
|
124
|
+
if node_col not in dedup_set:
|
|
125
|
+
dedup_list.append(col_dep)
|
|
126
|
+
dedup_set.add(node_col)
|
|
127
|
+
return dedup_list
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _cll_set_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> CllResult:
|
|
131
|
+
# model-to-column
|
|
132
|
+
m2c: List[CllColumnDep] = []
|
|
133
|
+
# column-to-column
|
|
134
|
+
c2c_map: Dict[str, CllColumn] = {}
|
|
135
|
+
|
|
136
|
+
for union_scope in scope.union_scopes:
|
|
137
|
+
sub_scope_result = scope_cll_map.get(union_scope)
|
|
138
|
+
if sub_scope_result is None:
|
|
139
|
+
raise RecceException(f"Scope {union_scope} not found in scope_cll_map")
|
|
140
|
+
sub_m2c, sub_c2c_map = sub_scope_result
|
|
141
|
+
|
|
142
|
+
for k, v in sub_c2c_map.items():
|
|
143
|
+
if k not in c2c_map:
|
|
144
|
+
c2c_map[k] = v
|
|
145
|
+
else:
|
|
146
|
+
c2c_map[k].depends_on.extend(v.depends_on)
|
|
147
|
+
c2c_map[k].transformation_type = "derived"
|
|
148
|
+
|
|
149
|
+
m2c.extend(sub_m2c)
|
|
150
|
+
return m2c, c2c_map
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> CllResult:
|
|
154
|
+
assert scope.expression.key == "select"
|
|
155
|
+
|
|
156
|
+
# model-to-column
|
|
157
|
+
m2c: List[CllColumnDep] = []
|
|
158
|
+
# column-to-column
|
|
159
|
+
c2c_map: Dict[str, CllColumn] = {}
|
|
160
|
+
|
|
161
|
+
table_alias_map = {t.alias_or_name: t.name for t in scope.tables}
|
|
162
|
+
select = scope.expression
|
|
163
|
+
|
|
164
|
+
def source_column_dependency(ref_column: exp.Column) -> Optional[CllColumn]:
|
|
165
|
+
column_name = ref_column.name
|
|
166
|
+
table_name = ref_column.table if ref_column.table != "" else next(iter(table_alias_map.values()))
|
|
167
|
+
source = scope.sources.get(table_name, None) # transformation_type: exp.Table | Scope
|
|
168
|
+
if isinstance(source, Scope):
|
|
169
|
+
ref_cll_result = scope_cll_map.get(source)
|
|
170
|
+
if ref_cll_result is None:
|
|
171
|
+
return None
|
|
172
|
+
_, sub_c2c_map = ref_cll_result
|
|
173
|
+
return sub_c2c_map.get(column_name)
|
|
174
|
+
elif isinstance(source, exp.Table):
|
|
175
|
+
return CllColumn(
|
|
176
|
+
name=column_name,
|
|
177
|
+
transformation_type="passthrough",
|
|
178
|
+
depends_on=[CllColumnDep(node=source.name, column=column_name)],
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def subquery_cll(subquery: exp.Subquery) -> Optional[CllResult]:
|
|
184
|
+
select = subquery.find(exp.Select)
|
|
185
|
+
if select is None:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
matched_scope = None
|
|
189
|
+
for sub_scope in scope.subquery_scopes:
|
|
190
|
+
if sub_scope.expression == select:
|
|
191
|
+
matched_scope = sub_scope
|
|
192
|
+
break
|
|
193
|
+
if matched_scope is None:
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
return scope_cll_map.get(matched_scope)
|
|
197
|
+
|
|
198
|
+
for proj in scope.expression.selects:
|
|
199
|
+
transformation_type = "source"
|
|
200
|
+
column_depends_on: List[CllColumnDep] = []
|
|
201
|
+
root = proj.this if isinstance(proj, exp.Alias) else proj
|
|
202
|
+
for expression in root.walk(bfs=False):
|
|
203
|
+
if isinstance(expression, exp.Column):
|
|
204
|
+
ref_column_dependency = source_column_dependency(expression)
|
|
205
|
+
if ref_column_dependency is not None:
|
|
206
|
+
column_depends_on.extend(ref_column_dependency.depends_on)
|
|
207
|
+
if ref_column_dependency.transformation_type == "derived":
|
|
208
|
+
transformation_type = "derived"
|
|
209
|
+
elif ref_column_dependency.transformation_type == "renamed":
|
|
210
|
+
if transformation_type == "source" or transformation_type == "passthrough":
|
|
211
|
+
transformation_type = "renamed"
|
|
212
|
+
elif ref_column_dependency.transformation_type == "passthrough":
|
|
213
|
+
if transformation_type == "source":
|
|
214
|
+
transformation_type = "passthrough"
|
|
215
|
+
else:
|
|
216
|
+
column_depends_on.append(CllColumnDep(expression.table, expression.name))
|
|
217
|
+
if transformation_type == "source":
|
|
218
|
+
transformation_type = "passthrough"
|
|
219
|
+
|
|
220
|
+
elif isinstance(expression, (exp.Paren, exp.Identifier)):
|
|
221
|
+
pass
|
|
222
|
+
else:
|
|
223
|
+
transformation_type = "derived"
|
|
224
|
+
|
|
225
|
+
column_depends_on = _dedeup_depends_on(column_depends_on)
|
|
226
|
+
|
|
227
|
+
if len(column_depends_on) == 0 and transformation_type != "source":
|
|
228
|
+
transformation_type = "source"
|
|
229
|
+
|
|
230
|
+
if isinstance(proj, exp.Alias):
|
|
231
|
+
alias = proj
|
|
232
|
+
if transformation_type == "passthrough" and column_depends_on[0].column != alias.alias_or_name:
|
|
233
|
+
transformation_type = "renamed"
|
|
234
|
+
|
|
235
|
+
c2c_map[proj.alias_or_name] = CllColumn(
|
|
236
|
+
name=proj.alias_or_name, transformation_type=transformation_type, depends_on=column_depends_on
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def selected_column_dependency(ref_column: exp.Column) -> Optional[CllColumn]:
|
|
240
|
+
column_name = ref_column.name
|
|
241
|
+
return c2c_map.get(column_name)
|
|
242
|
+
|
|
243
|
+
# joins clause: Reference the source columns
|
|
244
|
+
if select.args.get("joins"):
|
|
245
|
+
joins = select.args.get("joins")
|
|
246
|
+
for join in joins:
|
|
247
|
+
if isinstance(join, exp.Join):
|
|
248
|
+
for ref_column in join.find_all(exp.Column):
|
|
249
|
+
if source_column_dependency(ref_column) is not None:
|
|
250
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
251
|
+
|
|
252
|
+
# where clauses: Reference the source columns
|
|
253
|
+
if select.args.get("where"):
|
|
254
|
+
where = select.args.get("where")
|
|
255
|
+
if isinstance(where, exp.Where):
|
|
256
|
+
for ref_column in where.find_all(exp.Column):
|
|
257
|
+
if source_column_dependency(ref_column) is not None:
|
|
258
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
259
|
+
for subquery in where.find_all(exp.Subquery):
|
|
260
|
+
sub_cll = subquery_cll(subquery)
|
|
261
|
+
if sub_cll is not None:
|
|
262
|
+
sub_m2c, sub_c2c_map = sub_cll
|
|
263
|
+
m2c.extend(sub_m2c)
|
|
264
|
+
for sub_c in sub_c2c_map.values():
|
|
265
|
+
m2c.extend(sub_c.depends_on)
|
|
266
|
+
|
|
267
|
+
# group by clause: Reference the source columns, column index
|
|
268
|
+
if select.args.get("group"):
|
|
269
|
+
group = select.args.get("group")
|
|
270
|
+
if isinstance(group, exp.Group):
|
|
271
|
+
for ref_column in group.find_all(exp.Column):
|
|
272
|
+
if source_column_dependency(ref_column) is not None:
|
|
273
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
274
|
+
|
|
275
|
+
# having clause: Reference the source columns, selected columns
|
|
276
|
+
if select.args.get("having"):
|
|
277
|
+
having = select.args.get("having")
|
|
278
|
+
if isinstance(having, exp.Having):
|
|
279
|
+
for ref_column in having.find_all(exp.Column):
|
|
280
|
+
if source_column_dependency(ref_column) is not None:
|
|
281
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
282
|
+
elif selected_column_dependency(ref_column) is not None:
|
|
283
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
284
|
+
for subquery in having.find_all(exp.Subquery):
|
|
285
|
+
sub_cll = subquery_cll(subquery)
|
|
286
|
+
if sub_cll is not None:
|
|
287
|
+
sub_m2c, sub_c2c_map = sub_cll
|
|
288
|
+
m2c.extend(sub_m2c)
|
|
289
|
+
for sub_c in sub_c2c_map.values():
|
|
290
|
+
m2c.extend(sub_c.depends_on)
|
|
291
|
+
|
|
292
|
+
# order by clause: Reference the source columns, selected columns, column index
|
|
293
|
+
if select.args.get("order"):
|
|
294
|
+
order = select.args.get("order")
|
|
295
|
+
if isinstance(order, exp.Order):
|
|
296
|
+
for ref_column in order.find_all(exp.Column):
|
|
297
|
+
if source_column_dependency(ref_column) is not None:
|
|
298
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
299
|
+
elif selected_column_dependency(ref_column) is not None:
|
|
300
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
301
|
+
|
|
302
|
+
for source in scope.sources.values():
|
|
303
|
+
scope_cll_result = scope_cll_map.get(source)
|
|
304
|
+
if scope_cll_result is None:
|
|
305
|
+
continue
|
|
306
|
+
sub_m2c, _ = scope_cll_result
|
|
307
|
+
m2c.extend(sub_m2c)
|
|
308
|
+
|
|
309
|
+
m2c = _dedeup_depends_on(m2c)
|
|
310
|
+
|
|
311
|
+
return m2c, c2c_map
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
315
|
+
# given a sql, return the cll for the sql
|
|
316
|
+
# {
|
|
317
|
+
# 'depends_on': [{'node': 'model_id', 'column': 'column'}],
|
|
318
|
+
# 'columns': {
|
|
319
|
+
# 'column1': {
|
|
320
|
+
# 'type': 'derived',
|
|
321
|
+
# 'depends_on': [{'node': 'model_id', 'column': 'column'}],
|
|
322
|
+
# }
|
|
323
|
+
# }
|
|
324
|
+
# }
|
|
325
|
+
|
|
326
|
+
dialect = Dialect.get(dialect) if dialect is not None else None
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
expression = parse_one(sql, dialect=dialect)
|
|
330
|
+
except SqlglotError as e:
|
|
331
|
+
raise RecceException(f"Failed to parse SQL: {str(e)}")
|
|
332
|
+
|
|
333
|
+
try:
|
|
334
|
+
expression = qualify(expression, schema=schema, dialect=dialect)
|
|
335
|
+
except OptimizeError as e:
|
|
336
|
+
raise RecceException(f"Failed to optimize SQL: {str(e)}")
|
|
337
|
+
except SqlglotError as e:
|
|
338
|
+
raise RecceException(f"Failed to qualify SQL: {str(e)}")
|
|
339
|
+
|
|
340
|
+
result = None
|
|
341
|
+
scope_cll_map = {}
|
|
342
|
+
for scope in traverse_scope(expression):
|
|
343
|
+
scope_type = scope.expression.key
|
|
344
|
+
if scope_type == "union" or scope_type == "intersect" or scope_type == "except":
|
|
345
|
+
result = _cll_set_scope(scope, scope_cll_map)
|
|
346
|
+
elif scope_type == "select":
|
|
347
|
+
result = _cll_select_scope(scope, scope_cll_map)
|
|
348
|
+
else:
|
|
349
|
+
continue
|
|
350
|
+
|
|
351
|
+
scope_cll_map[scope] = result
|
|
352
|
+
|
|
353
|
+
if result is None:
|
|
354
|
+
raise RecceException("Failed to extract CLL from SQL")
|
|
355
|
+
return result
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Recce Cloud API client modules.
|
|
3
|
+
|
|
4
|
+
This package provides modular access to Recce Cloud API endpoints.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from recce.util.cloud.base import CloudBase
|
|
8
|
+
from recce.util.cloud.check_events import CheckEventsCloud
|
|
9
|
+
from recce.util.cloud.checks import ChecksCloud
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"CloudBase",
|
|
13
|
+
"CheckEventsCloud",
|
|
14
|
+
"ChecksCloud",
|
|
15
|
+
]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for Recce Cloud API clients.
|
|
3
|
+
|
|
4
|
+
This module provides the common functionality shared across all cloud API clients.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Dict, Optional
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from recce.util.recce_cloud import (
|
|
13
|
+
DOCKER_INTERNAL_URL_PREFIX,
|
|
14
|
+
LOCALHOST_URL_PREFIX,
|
|
15
|
+
RECCE_CLOUD_API_HOST,
|
|
16
|
+
RecceCloudException,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CloudBase:
|
|
21
|
+
"""
|
|
22
|
+
Base class for Recce Cloud API operations.
|
|
23
|
+
|
|
24
|
+
Provides common functionality for making authenticated requests to the Recce Cloud API,
|
|
25
|
+
including request handling, error management, and Docker environment URL conversion.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
token: Authentication token (API token or GitHub token)
|
|
29
|
+
token_type: Type of token ("api_token" or "github_token")
|
|
30
|
+
base_url: Base URL for API v1 endpoints
|
|
31
|
+
base_url_v2: Base URL for API v2 endpoints
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, token: str):
|
|
35
|
+
"""
|
|
36
|
+
Initialize the CloudBase client.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
token: Authentication token for Recce Cloud API
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If token is None
|
|
43
|
+
"""
|
|
44
|
+
if token is None:
|
|
45
|
+
raise ValueError("Token cannot be None.")
|
|
46
|
+
|
|
47
|
+
self.token = token
|
|
48
|
+
self.token_type = "github_token" if token.startswith(("ghp_", "gho_", "ghu_", "ghs_", "ghr_")) else "api_token"
|
|
49
|
+
self.base_url = f"{RECCE_CLOUD_API_HOST}/api/v1"
|
|
50
|
+
self.base_url_v2 = f"{RECCE_CLOUD_API_HOST}/api/v2"
|
|
51
|
+
|
|
52
|
+
def _request(self, method: str, url: str, headers: Optional[Dict] = None, **kwargs):
|
|
53
|
+
"""
|
|
54
|
+
Make an authenticated HTTP request to Recce Cloud API.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
method: HTTP method (GET, POST, PATCH, DELETE, etc.)
|
|
58
|
+
url: Full URL for the request
|
|
59
|
+
headers: Optional additional headers
|
|
60
|
+
**kwargs: Additional arguments passed to requests.request
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Response object from requests library
|
|
64
|
+
"""
|
|
65
|
+
headers = {
|
|
66
|
+
**(headers or {}),
|
|
67
|
+
"Authorization": f"Bearer {self.token}",
|
|
68
|
+
}
|
|
69
|
+
url = self._replace_localhost_with_docker_internal(url)
|
|
70
|
+
return requests.request(method, url, headers=headers, **kwargs)
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _replace_localhost_with_docker_internal(url: str) -> Optional[str]:
|
|
74
|
+
"""
|
|
75
|
+
Convert localhost URLs to docker internal URLs if running in Docker.
|
|
76
|
+
|
|
77
|
+
This is useful for local development when Recce is running inside a Docker container
|
|
78
|
+
and needs to access localhost services on the host machine.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
url: URL that might contain localhost
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
URL with localhost replaced by host.docker.internal if in Docker, otherwise original URL
|
|
85
|
+
"""
|
|
86
|
+
if url is None:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
if (
|
|
90
|
+
os.environ.get("RECCE_SHARE_INSTANCE_ENV") == "docker"
|
|
91
|
+
or os.environ.get("RECCE_TASK_INSTANCE_ENV") == "docker"
|
|
92
|
+
or os.environ.get("RECCE_INSTANCE_ENV") == "docker"
|
|
93
|
+
):
|
|
94
|
+
if url.startswith(LOCALHOST_URL_PREFIX):
|
|
95
|
+
return url.replace(LOCALHOST_URL_PREFIX, DOCKER_INTERNAL_URL_PREFIX)
|
|
96
|
+
|
|
97
|
+
return url
|
|
98
|
+
|
|
99
|
+
def _raise_for_status(self, response, message: str):
|
|
100
|
+
"""
|
|
101
|
+
Raise RecceCloudException if the response status is not successful.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
response: Response object from requests
|
|
105
|
+
message: Error message to include in the exception
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
RecceCloudException: If response status code is not 2xx
|
|
109
|
+
"""
|
|
110
|
+
if not response.ok:
|
|
111
|
+
raise RecceCloudException(
|
|
112
|
+
message=message,
|
|
113
|
+
reason=response.text,
|
|
114
|
+
status_code=response.status_code,
|
|
115
|
+
)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Recce Cloud API client for check event operations.
|
|
3
|
+
|
|
4
|
+
This module provides methods for managing check events (timeline/conversation) in Recce Cloud,
|
|
5
|
+
including CRUD operations for comments and retrieving state change events.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, List
|
|
9
|
+
|
|
10
|
+
from recce.util.cloud.base import CloudBase
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CheckEventsCloud(CloudBase):
|
|
14
|
+
"""
|
|
15
|
+
Client for Recce Cloud check event operations.
|
|
16
|
+
|
|
17
|
+
Provides methods to list, create, update, and delete check events
|
|
18
|
+
(comments and state changes) within Recce Cloud sessions.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
>>> client = CheckEventsCloud(token="your-api-token")
|
|
22
|
+
>>> events = client.list_events(
|
|
23
|
+
... org_id="org123",
|
|
24
|
+
... project_id="proj456",
|
|
25
|
+
... session_id="sess789",
|
|
26
|
+
... check_id="check001"
|
|
27
|
+
... )
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def _build_events_url(self, org_id: str, project_id: str, session_id: str, check_id: str) -> str:
|
|
31
|
+
"""Build the base URL for check events endpoints."""
|
|
32
|
+
return f"{self.base_url_v2}/organizations/{org_id}/projects/{project_id}/sessions/{session_id}/checks/{check_id}/events"
|
|
33
|
+
|
|
34
|
+
def list_events(self, org_id: str, project_id: str, session_id: str, check_id: str) -> List[Dict]:
|
|
35
|
+
"""
|
|
36
|
+
List all events for a check in chronological order.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
org_id: Organization ID
|
|
40
|
+
project_id: Project ID
|
|
41
|
+
session_id: Session ID
|
|
42
|
+
check_id: Check ID
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
List of event dictionaries
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
RecceCloudException: If the request fails
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> events = client.list_events("org123", "proj456", "sess789", "check001")
|
|
52
|
+
>>> for event in events:
|
|
53
|
+
... print(f"{event['event_type']}: {event['content']}")
|
|
54
|
+
"""
|
|
55
|
+
api_url = self._build_events_url(org_id, project_id, session_id, check_id)
|
|
56
|
+
query_params = {"include_deleted": True}
|
|
57
|
+
response = self._request("GET", api_url, params=query_params)
|
|
58
|
+
|
|
59
|
+
self._raise_for_status(
|
|
60
|
+
response,
|
|
61
|
+
"Failed to list check events from Recce Cloud.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
data = response.json()
|
|
65
|
+
# Response is wrapped: {"events": [...]}
|
|
66
|
+
return data.get("events", [])
|
|
67
|
+
|
|
68
|
+
def get_event(self, org_id: str, project_id: str, session_id: str, check_id: str, event_id: str) -> Dict:
|
|
69
|
+
"""
|
|
70
|
+
Get a specific event by ID.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
org_id: Organization ID
|
|
74
|
+
project_id: Project ID
|
|
75
|
+
session_id: Session ID
|
|
76
|
+
check_id: Check ID
|
|
77
|
+
event_id: Event ID
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Event dictionary
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
RecceCloudException: If the request fails or event not found
|
|
84
|
+
"""
|
|
85
|
+
api_url = f"{self._build_events_url(org_id, project_id, session_id, check_id)}/{event_id}"
|
|
86
|
+
response = self._request("GET", api_url)
|
|
87
|
+
|
|
88
|
+
self._raise_for_status(
|
|
89
|
+
response,
|
|
90
|
+
f"Failed to get check event {event_id} from Recce Cloud.",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
data = response.json()
|
|
94
|
+
# Response is wrapped: {"event": {...}}
|
|
95
|
+
return data.get("event", {})
|
|
96
|
+
|
|
97
|
+
def create_comment(self, org_id: str, project_id: str, session_id: str, check_id: str, content: str) -> Dict:
|
|
98
|
+
"""
|
|
99
|
+
Create a new comment on a check.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
org_id: Organization ID
|
|
103
|
+
project_id: Project ID
|
|
104
|
+
session_id: Session ID
|
|
105
|
+
check_id: Check ID
|
|
106
|
+
content: Comment content (plain text or markdown)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Created event dictionary
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
RecceCloudException: If the request fails
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
>>> event = client.create_comment(
|
|
116
|
+
... "org123", "proj456", "sess789", "check001",
|
|
117
|
+
... "This looks good to me!"
|
|
118
|
+
... )
|
|
119
|
+
>>> print(f"Created comment with ID: {event['id']}")
|
|
120
|
+
"""
|
|
121
|
+
api_url = self._build_events_url(org_id, project_id, session_id, check_id)
|
|
122
|
+
response = self._request("POST", api_url, json={"content": content})
|
|
123
|
+
|
|
124
|
+
self._raise_for_status(
|
|
125
|
+
response,
|
|
126
|
+
"Failed to create comment in Recce Cloud.",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
data = response.json()
|
|
130
|
+
# Response is wrapped: {"event": {...}}
|
|
131
|
+
return data.get("event", {})
|
|
132
|
+
|
|
133
|
+
def update_comment(
|
|
134
|
+
self, org_id: str, project_id: str, session_id: str, check_id: str, event_id: str, content: str
|
|
135
|
+
) -> Dict:
|
|
136
|
+
"""
|
|
137
|
+
Update an existing comment.
|
|
138
|
+
|
|
139
|
+
Only the author or an admin can update a comment.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
org_id: Organization ID
|
|
143
|
+
project_id: Project ID
|
|
144
|
+
session_id: Session ID
|
|
145
|
+
check_id: Check ID
|
|
146
|
+
event_id: Event ID of the comment to update
|
|
147
|
+
content: New comment content
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Updated event dictionary
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
RecceCloudException: If the request fails or user is not authorized
|
|
154
|
+
"""
|
|
155
|
+
api_url = f"{self._build_events_url(org_id, project_id, session_id, check_id)}/{event_id}"
|
|
156
|
+
response = self._request("PATCH", api_url, json={"content": content})
|
|
157
|
+
|
|
158
|
+
self._raise_for_status(
|
|
159
|
+
response,
|
|
160
|
+
f"Failed to update comment {event_id} in Recce Cloud.",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
data = response.json()
|
|
164
|
+
# Response is wrapped: {"event": {...}}
|
|
165
|
+
return data.get("event", {})
|
|
166
|
+
|
|
167
|
+
def delete_comment(self, org_id: str, project_id: str, session_id: str, check_id: str, event_id: str) -> None:
|
|
168
|
+
"""
|
|
169
|
+
Delete a comment (soft delete).
|
|
170
|
+
|
|
171
|
+
Only the author or an admin can delete a comment.
|
|
172
|
+
The comment will be marked as deleted but remain in the timeline.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
org_id: Organization ID
|
|
176
|
+
project_id: Project ID
|
|
177
|
+
session_id: Session ID
|
|
178
|
+
check_id: Check ID
|
|
179
|
+
event_id: Event ID of the comment to delete
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
RecceCloudException: If the request fails or user is not authorized
|
|
183
|
+
"""
|
|
184
|
+
api_url = f"{self._build_events_url(org_id, project_id, session_id, check_id)}/{event_id}"
|
|
185
|
+
response = self._request("DELETE", api_url)
|
|
186
|
+
|
|
187
|
+
self._raise_for_status(
|
|
188
|
+
response,
|
|
189
|
+
f"Failed to delete comment {event_id} in Recce Cloud.",
|
|
190
|
+
)
|