Compare commits

..
Author SHA1 Message Date
Elizabeth ThompsonandClaude Opus 4.8 c18c91a89d fix(chart): catch JSONDecodeError when parsing params on chart create
CreateChartCommand.__init__ called json.loads on the client-supplied
params string without guarding it. A malformed params value in
POST /api/v1/chart raised a raw JSONDecodeError out of __init__ --
before run()'s @transaction or validate() ran -- which the api.py
create() handler does not catch, surfacing as an opaque 500.

Wrap the parse and raise ChartInvalidError(exceptions=[...]) instead,
following the existing *ValidationError idiom, so the existing
except ChartInvalidError branch returns a 422. Adds a unit test
covering both the invalid-JSON and valid-JSON (happy path) cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-29 16:40:26 +00:00
767 changed files with 55015 additions and 105265 deletions
@@ -0,0 +1,23 @@
name: Label Draft PRs
on:
pull_request:
types:
- opened
- converted_to_draft
jobs:
label-draft:
runs-on: ubuntu-latest
steps:
- name: Check if the PR is a draft
id: check-draft
uses: actions/github-script@v8
with:
script: |
const isDraft = context.payload.pull_request.draft;
core.setOutput('isDraft', isDraft);
- name: Add `review:draft` Label
if: steps.check-draft.outputs.isDraft == 'true'
uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: "review:draft"
+3 -3
View File
@@ -26,7 +26,7 @@ runs:
- name: Set up QEMU
if: ${{ inputs.build == 'true' }}
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
with:
# Pin the binfmt image to a specific QEMU release. The default
# (`tonistiigi/binfmt:latest`) is a moving target, and drift across
@@ -39,12 +39,12 @@ runs:
- name: Set up Docker Buildx
if: ${{ inputs.build == 'true' }}
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Try to login to DockerHub
if: ${{ inputs.login-to-dockerhub == 'true' }}
continue-on-error: true
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ inputs.dockerhub-user }}
password: ${{ inputs.dockerhub-token }}
-30
View File
@@ -84,18 +84,6 @@ updates:
- "@swc/core"
- "@swc/plugin-emotion"
- "@swc/plugin-transform-imports"
jsonforms:
patterns:
- "@jsonforms/*"
visx:
patterns:
- "@visx/*"
emotion:
patterns:
- "@emotion/*"
fontsource:
patterns:
- "@fontsource/*"
open-pull-requests-limit: 30
versioning-strategy: increase
cooldown:
@@ -192,21 +180,3 @@ updates:
versioning-strategy: increase
cooldown:
default-days: 7
- package-ecosystem: "npm"
directory: "/superset-embedded-sdk/"
schedule:
interval: "daily"
labels:
- npm
- dependabot
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
babel:
patterns:
- "@babel/*"
versioning-strategy: increase
cooldown:
default-days: 7
-46
View File
@@ -1,46 +0,0 @@
# Verifies that every `uses:` ref under .github/ is on the ASF Infrastructure
# GitHub Actions allowlist (apache/infrastructure-actions). An action that is
# not allowlisted fails at "Set up job" with no logs and no notification, so
# this check surfaces the problem at PR time instead. It also warns (without
# failing) when a pinned SHA's allowlist entry is about to expire.
name: ASF Allowlist Check
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- ".github/**"
push:
branches:
- "master"
- "[0-9].[0-9]*"
paths:
- ".github/**"
schedule:
# Weekly, so allowlist expirations are surfaced even when nothing under
# .github/ has changed.
- cron: "0 6 * * 1"
permissions:
contents: read
# cancel previous workflow jobs for PRs
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
asf-allowlist-check:
runs-on: ubuntu-26.04
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check action refs against the ASF allowlist
uses: apache/infrastructure-actions/allowlist-check@df54e48ff76152790f317934c691cfa7fd7a1a46 # allowlist-check/v1.0.1
with:
# Default scan-glob is .github/**/*.yml, which misses .yaml files.
scan-glob: ".github/**/*.y*ml"
@@ -1,99 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: Check OpenAPI spec drift
on:
push:
branches:
- "master"
- "[0-9].[0-9]*"
pull_request:
types: [synchronize, opened, reopened, ready_for_review]
# Deliberately unfiltered by `paths`: a required check that does not run on a
# PR blocks it from merging forever.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
check-openapi-spec-drift:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Python
uses: ./.github/actions/setup-backend/
with:
# The generated output depends on the pinned apispec version.
requirements-type: base
- name: Regenerate the spec
env:
# No config file: the spec documents what a default deployment
# registers, so feature flags must stay off.
SUPERSET__SQLALCHEMY_DATABASE_URI: "sqlite:///:memory:"
FLASK_APP: "superset.app:create_app()"
run: superset update-api-docs
- name: Assert the published spec is up to date
env:
SPEC: docs/static/resources/openapi.json
run: |
if git diff --quiet -- "$SPEC"; then
exit 0
fi
# Staged to a file, not piped: `head` closing the pipe would
# SIGPIPE-kill `git diff` under pipefail and abort this step.
diff_file="$RUNNER_TEMP/openapi.diff"
git diff -- "$SPEC" > "$diff_file"
regen="SUPERSET__SQLALCHEMY_DATABASE_URI='sqlite:///:memory:' FLASK_APP='superset.app:create_app()' superset update-api-docs"
echo "::error::$SPEC is stale. Regenerate it on the pinned requirements:"
echo "$regen"
git diff --stat -- "$SPEC"
# Summaries cap at 1 MiB, well under a full regeneration.
{
echo '### OpenAPI spec is stale'
echo
git diff --stat -- "$SPEC"
echo
echo 'Regenerate with:'
echo
echo '```bash'
echo "$regen"
echo '```'
echo
echo '```diff'
head -300 "$diff_file"
echo '```'
if [ "$(wc -l < "$diff_file")" -gt 300 ]; then
echo
echo '_Truncated at 300 lines; see the job log for the full diff._'
fi
} >> "$GITHUB_STEP_SUMMARY"
echo "::group::Full diff"
cat "$diff_file"
echo "::endgroup::"
exit 1
+2 -2
View File
@@ -67,7 +67,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -78,6 +78,6 @@ jobs:
# queries: security-extended,security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
category: "/language:${{matrix.language}}"
+1 -24
View File
@@ -213,29 +213,6 @@ jobs:
docker images $IMAGE_TAG
docker history $IMAGE_TAG
- name: WebSocket server smoke test
if: contains(fromJson('["lean", "dev"]'), matrix.build_preset)
shell: bash
run: |
# The realtime WebSocket server is bundled in the official image and
# launched via an alternate entrypoint; verify the bundled Node runtime
# starts it and it serves /health. (A JWT secret >= 32 bytes is required
# or the server refuses to start; no Redis is needed for /health.)
# Both presets are checked because docker-compose-non-dev.yml runs the
# websocket service from the dev target.
docker run -d --name superset-ws \
-e JWT_SECRET="ci-smoke-test-secret-ci-smoke-test-secret" \
-e PORT=8080 -p 8080:8080 \
"$IMAGE_TAG" /app/docker/entrypoints/run-websocket.sh
ok=""
for _ in $(seq 1 20); do
if curl -sf http://localhost:8080/health; then echo "ws /health OK"; ok=1; break; fi
sleep 2
done
docker logs superset-ws || true
docker rm -f superset-ws || true
[ "$ok" = "1" ] || { echo "::error::websocket /health did not come up"; exit 1; }
- name: docker-compose sanity check
if: matrix.build_preset == 'dev'
shell: bash
@@ -289,7 +266,7 @@ jobs:
actions-timeline:
needs: [docker-build, docker-compose-image-tag]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: "temurin"
java-version: "11"
@@ -19,7 +19,7 @@ concurrency:
jobs:
validate-all-ghas:
runs-on: ubuntu-26.04 # Don't switch to ubuntu-slim as zizmore-action requires Docker
runs-on: ubuntu-26.04
permissions:
contents: read
# Required for the zizmor action to upload its SARIF results to
@@ -34,7 +34,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: "./superset-frontend/.nvmrc"
node-version: "20"
- name: Install Dependencies
# Versions are pinned to avoid ad-hoc, unpinned package installs.
@@ -49,4 +49,4 @@ jobs:
run: bash .github/workflows/scripts/check-docs-deploy-freshness.test.sh
- name: Check for security issues on GHA workflows
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
cancel-on-hold:
name: Cancel CI runs when hold label applied
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'hold')
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: write
pull-requests: read
@@ -69,7 +69,7 @@ jobs:
rerun-on-unhold:
name: Re-run CI when hold label removed
if: github.event.action == 'unlabeled' && startsWith(github.event.label.name, 'hold')
runs-on: ubuntu-slim
runs-on: ubuntu-24.04
permissions:
actions: write
pull-requests: read
+42
View File
@@ -0,0 +1,42 @@
name: Tags
on:
release:
types: [published] # This makes it run only when a new released is published
permissions:
contents: read
jobs:
latest-release:
name: Add/update tag to new release
runs-on: ubuntu-slim
permissions:
contents: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
- name: Check for latest tag
id: latest-tag
env:
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
run: |
source ./scripts/tag_latest_release.sh "$RELEASE_TAG_NAME" --dry-run
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Run latest-tag
uses: ./.github/actions/latest-tag
if: steps.latest-tag.outputs.SKIP_TAG != 'true'
with:
description: Superset latest release
tag-name: latest
env:
GITHUB_TOKEN: ${{ github.token }}
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: "temurin"
java-version: "11"
+1 -1
View File
@@ -202,7 +202,7 @@ jobs:
actions-timeline:
needs: pre-commit
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
steps:
- uses: Kesin11/actions-timeline@57fc93f20c6da7fbc14063c6d24a2a5627c799ad # v3.2.0
with:
+1 -1
View File
@@ -118,7 +118,7 @@ jobs:
node-version-file: "./docs/.nvmrc"
- name: Setup Python
uses: ./.github/actions/setup-backend/
- uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: "zulu"
java-version: "21"
+8 -25
View File
@@ -168,10 +168,7 @@ jobs:
needs: changes
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-26.04
# Embedded Tests below adds its own gunicorn boot + spec run on top of
# Required and Soft-delete; 30m was tight even for the two-step shadow
# job this replaced.
timeout-minutes: 40
timeout-minutes: 30
permissions:
contents: read
pull-requests: read
@@ -179,10 +176,7 @@ jobs:
fail-fast: false
matrix:
browser: ["chromium"]
# Subdirectory deployment (APPLICATION_ROOT) is a required-to-pass
# dimension, not an optional one, so it runs on every event —
# unlike cypress-matrix above, which only widens on push.
app_root: ["", "/app/prefix"]
app_root: ${{ github.event_name == 'push' && fromJSON('["", "/app/prefix"]') || fromJSON('[""]') }}
env:
SUPERSET_ENV: development
SUPERSET_CONFIG: tests.integration_tests.superset_test_config
@@ -272,25 +266,14 @@ jobs:
# Scoped to this step: each playwright-run boots its own gunicorn
# with the step's env, so the Required Tests server above keeps
# master's Flask configuration while this one runs with SOFT_DELETE
# on — the same isolation pattern as the Embedded Tests step below.
# Without a flag-on server the recently-archived specs skip
# themselves everywhere and ship zero executed E2E coverage; in the
# Required run above they are collected and skipped, which is
# expected.
# on — the same isolation pattern as the Embedded step in
# superset-playwright.yml. Without a flag-on server the
# recently-archived specs skip themselves everywhere and ship zero
# executed E2E coverage; in the Required run above they are
# collected and skipped, which is expected.
SUPERSET_FEATURE_SOFT_DELETE: "true"
with:
run: playwright-run "${{ matrix.app_root }}" recently-archived/
- name: Run Playwright (Embedded Tests)
uses: ./.github/actions/cached-dependencies
env:
NODE_OPTIONS: "--max-old-space-size=4096"
# Scoped to this step for the same reason as Soft-delete above:
# embedding is a real, required feature, so its Playwright coverage
# now gates merges instead of running only in shadow mode.
SUPERSET_FEATURE_EMBEDDED_SUPERSET: "true"
INCLUDE_EMBEDDED: "true"
with:
run: playwright-run "${{ matrix.app_root }}" embedded
- name: Set safe app root
if: failure()
id: set-safe-app-root
@@ -371,7 +354,7 @@ jobs:
actions-timeline:
needs: [cypress-matrix, playwright-tests, cypress-matrix-required, playwright-tests-required]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
@@ -74,7 +74,7 @@ jobs:
actions-timeline:
needs: test-superset-extensions-cli-package
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
steps:
- uses: Kesin11/actions-timeline@57fc93f20c6da7fbc14063c6d24a2a5627c799ad # v3.2.0
with:
+2 -3
View File
@@ -97,7 +97,6 @@ jobs:
mkdir -p ${{ github.workspace }}/superset-frontend/coverage
docker run \
-v ${{ github.workspace }}/superset-frontend/coverage:/app/superset-frontend/coverage \
-e CI=true \
--rm $TAG \
bash -c \
"npm run test -- --coverage --shard=${{ matrix.shard }}/8 --coverageReporters=json"
@@ -111,7 +110,7 @@ jobs:
report-coverage:
needs: [sharded-jest-tests]
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
timeout-minutes: 15
permissions:
id-token: write
@@ -206,7 +205,7 @@ jobs:
actions-timeline:
needs: [report-coverage, lint-frontend, validate-frontend, test-storybook]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
+24 -8
View File
@@ -46,10 +46,8 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
# NOTE: Required Playwright tests are in superset-e2e.yml (E2E / playwright-tests),
# including Embedded — it moved out of this workflow because embedding is a
# required feature, not an experimental one. This workflow now contains
# only experimental and mobile tests, which run in shadow mode.
# NOTE: Required Playwright tests are in superset-e2e.yml (E2E / playwright-tests)
# This workflow contains only experimental tests that run in shadow mode
playwright-tests-experimental:
needs: changes
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
@@ -132,6 +130,10 @@ jobs:
uses: ./.github/actions/cached-dependencies
with:
run: build-instrumented-assets
- name: Build embedded SDK
uses: ./.github/actions/cached-dependencies
with:
run: build-embedded-sdk
- name: Install Playwright
uses: ./.github/actions/cached-dependencies
with:
@@ -142,13 +144,27 @@ jobs:
NODE_OPTIONS: "--max-old-space-size=4096"
with:
run: playwright-run "${{ matrix.app_root }}" experimental/
- name: Run Playwright (Embedded Tests)
uses: ./.github/actions/cached-dependencies
env:
NODE_OPTIONS: "--max-old-space-size=4096"
# Scope embedded-only env vars to this step. Setting them at the job
# level enabled the EMBEDDED_SUPERSET feature flag inside Flask for
# the preceding "Required Tests" and "Experimental Tests" steps too,
# which loads extra handlers and destabilizes the werkzeug dev
# server under the 2-worker Playwright load. Required Tests should
# match master's Flask configuration.
SUPERSET_FEATURE_EMBEDDED_SUPERSET: "true"
INCLUDE_EMBEDDED: "true"
with:
run: playwright-run "${{ matrix.app_root }}" embedded
- name: Run Playwright (Mobile Tests)
uses: ./.github/actions/cached-dependencies
env:
NODE_OPTIONS: "--max-old-space-size=4096"
# Scoped to this step: setting feature flags at the job level would
# alter Flask's configuration for the preceding Experimental step
# too — the mobile consumption mode should not do that.
# Scoped to this step for the same reason as the embedded flags
# above: the mobile consumption mode should not alter Flask's
# configuration for the required desktop test steps.
SUPERSET_FEATURE_MOBILE_CONSUMPTION_MODE: "true"
INCLUDE_MOBILE: "true"
with:
@@ -172,7 +188,7 @@ jobs:
actions-timeline:
needs: playwright-tests-experimental
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
@@ -164,13 +164,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Setup Postgres
# cached-dependencies is a git submodule (not a plain directory), and
# the $/ self-repository syntax resolves action files directly from
# the repository without performing a real (submodule-aware)
# checkout, so it can't see into a submodule's gitlink. Keep this one
# on the workspace-relative ./ form, consistent with every other
# workflow in the repo that references this action.
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
uses: ./.github/actions/cached-dependencies
with:
run: |
setup-postgres
@@ -216,7 +210,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Python
uses: $/.github/actions/setup-backend/
uses: ./.github/actions/setup-backend/
- name: Install dependencies
uses: ./.github/actions/cached-dependencies
with:
@@ -263,7 +257,7 @@ jobs:
actions-timeline:
needs: [test-mysql, test-postgres, test-sqlite, test-postgres-required]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
@@ -33,7 +33,7 @@ jobs:
persist-credentials: false
- name: Check for file changes
id: check
uses: $/.github/actions/change-detector/
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -80,15 +80,9 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Python
uses: $/.github/actions/setup-backend/
uses: ./.github/actions/setup-backend/
- name: Setup Postgres
# cached-dependencies is a submodule (not a plain directory), and
# the $/ self-repository syntax resolves action files directly from
# the repository without performing a real (submodule-aware)
# checkout, so it can't see into a submodule's link. Keep this one
# on the workspace-relative ./ form, consistent with every other
# workflow in the repo that references this action.
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
uses: ./.github/actions/cached-dependencies
with:
run: setup-postgres
- name: Start Celery worker
@@ -147,19 +141,13 @@ jobs:
- name: Start hadoop and hive
run: docker compose -f scripts/databases/hive/docker-compose.yml up -d
- name: Setup Python
uses: $/.github/actions/setup-backend/
uses: ./.github/actions/setup-backend/
- name: Setup Postgres
uses: ./.github/actions/cached-dependencies
with:
run: setup-postgres
- name: Start Celery worker
# cached-dependencies is a submodule (not a plain directory), and
# the $/ self-repository syntax resolves action files directly from
# the repository without performing a real (submodule-aware)
# checkout, so it can't see into a submodule's link. Keep this one
# on the workspace-relative ./ form, consistent with every other
# workflow in the repo that references this action.
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
uses: ./.github/actions/cached-dependencies
with:
run: celery-worker
- name: Python unit tests (PostgreSQL)
@@ -177,7 +165,7 @@ jobs:
actions-timeline:
needs: [test-postgres-presto, test-postgres-hive]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
@@ -35,7 +35,7 @@ jobs:
persist-credentials: false
- name: Check for file changes
id: check
uses: $/.github/actions/change-detector/
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -60,7 +60,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Python
uses: $/.github/actions/setup-backend/
uses: ./.github/actions/setup-backend/
with:
python-version: ${{ matrix.python-version }}
- name: Python unit tests
@@ -125,7 +125,7 @@ jobs:
unit-tests-required:
needs: [changes, unit-tests]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
timeout-minutes: 5
permissions:
contents: read
@@ -20,7 +20,7 @@ permissions:
jobs:
post-comment:
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
# Only act when the Translations workflow failed (which means a regression
# was detected — the workflow exits 1 on regression).
if: github.event.workflow_run.conclusion == 'failure'
+5 -11
View File
@@ -32,7 +32,7 @@ jobs:
- name: Check for file changes
id: check
uses: $/.github/actions/change-detector/
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -45,13 +45,7 @@ jobs:
cache-dependency-path: "superset-frontend/package-lock.json"
- name: Install dependencies
if: steps.check.outputs.frontend
# cached-dependencies is a git submodule (not a plain directory), and
# the $/ self-repository syntax resolves action files directly from
# the repository without performing a real (submodule-aware)
# checkout, so it can't see into a submodule's gitlink. Keep this one
# on the workspace-relative ./ form, consistent with every other
# workflow in the repo that references this action.
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
uses: ./.github/actions/cached-dependencies
with:
run: npm-install
- name: lint
@@ -74,13 +68,13 @@ jobs:
- name: Check for file changes
id: check
uses: $/.github/actions/change-detector/
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Python
if: steps.check.outputs.python == 'true' || steps.check.outputs.frontend == 'true'
uses: $/.github/actions/setup-backend/
uses: ./.github/actions/setup-backend/
- name: Install gettext tools
if: steps.check.outputs.python == 'true' || steps.check.outputs.frontend == 'true'
@@ -166,7 +160,7 @@ jobs:
actions-timeline:
needs: [frontend-check-translations, babel-extract]
if: always()
runs-on: ubuntu-slim
runs-on: ubuntu-26.04
permissions:
actions: read
steps:
+3 -3
View File
@@ -35,10 +35,10 @@ jobs:
- name: Install dependencies
working-directory: ./superset-websocket
run: npm ci
- name: lint
- name: eslint
working-directory: ./superset-websocket
run: npm run lint-check
- name: typescript check
run: npm run eslint -- . --quiet
- name: typescript checks
working-directory: ./superset-websocket
run: npm run type
- name: code format check
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
persist-credentials: false
- name: Setup supersetbot
uses: $/.github/actions/setup-supersetbot/
uses: ./.github/actions/setup-supersetbot/
- name: Execute custom Node.js script
env:
+3 -3
View File
@@ -66,7 +66,7 @@ jobs:
fetch-depth: 0
- name: Setup Docker Environment
uses: $/.github/actions/setup-docker
uses: ./.github/actions/setup-docker
with:
dockerhub-user: ${{ secrets.DOCKERHUB_USER }}
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -81,7 +81,7 @@ jobs:
package-manager-cache: false
- name: Setup supersetbot
uses: $/.github/actions/setup-supersetbot
uses: ./.github/actions/setup-supersetbot/
- name: Execute custom Node.js script
env:
@@ -139,7 +139,7 @@ jobs:
package-manager-cache: false
- name: Setup supersetbot
uses: $/.github/actions/setup-supersetbot/
uses: ./.github/actions/setup-supersetbot/
- name: Label the PRs with the right release-related labels
env:
+6
View File
@@ -15,12 +15,18 @@
# limitations under the License.
#
[submodule ".github/actions/latest-tag"]
path = .github/actions/latest-tag
url = https://github.com/EndBug/latest-tag
[submodule ".github/actions/pr-lint-action"]
path = .github/actions/pr-lint-action
url = https://github.com/morrisoncole/pr-lint-action
[submodule ".github/actions/cached-dependencies"]
path = .github/actions/cached-dependencies
url = https://github.com/apache-superset/cached-dependencies
[submodule ".github/actions/comment-on-pr"]
path = .github/actions/comment-on-pr
url = https://github.com/unsplash/comment-on-pr
[submodule ".github/actions/chart-testing-action"]
path = .github/actions/chart-testing-action
url = https://github.com/helm/chart-testing-action
+5 -14
View File
@@ -64,19 +64,10 @@ repos:
hooks:
- id: oxfmt-frontend
name: oxfmt (frontend)
entry: ./scripts/oxfmt.sh superset-frontend
entry: bash -c 'cd superset-frontend && files=(); for f in "$@"; do files+=("${f#superset-frontend/}"); done; npx oxfmt --write --no-error-on-unmatched-pattern -- "${files[@]}"' --
language: system
pass_filenames: true
files: ^superset-frontend/.*\.(js|jsx|ts|tsx|css|scss|sass|json)$
- id: oxfmt-websocket
name: oxfmt (websocket)
entry: ./scripts/oxfmt.sh superset-websocket
language: system
pass_filenames: true
# JSON is excluded: superset-websocket/.oxfmtrc.json ignores *.json, so
# passing them here would only ever be a no-op (notably for the tracked
# package-lock.json).
files: ^superset-websocket/.*\.(js|ts)$
- repo: local
hooks:
- id: oxlint-frontend
@@ -97,9 +88,9 @@ repos:
language: system
pass_filenames: true
files: ^superset-frontend/.*\.(js|jsx|ts|tsx)$
- id: oxlint-docs
name: oxlint (docs)
entry: bash -c 'cd docs && FILES=$(printf "%s\n" "$@" | sed "s|^docs/||" | tr "\n" " ") && yarn lint --fix --quiet $FILES'
- id: eslint-docs
name: eslint (docs)
entry: bash -c 'cd docs && FILES=$(printf "%s\n" "$@" | sed "s|^docs/||" | tr "\n" " ") && yarn eslint --fix --quiet $FILES'
language: system
pass_filenames: true
files: ^docs/.*\.(js|jsx|ts|tsx)$
@@ -178,7 +169,7 @@ repos:
name: zizmor (GHA security audit)
entry: zizmor
language: python
additional_dependencies: [zizmor==1.30.0]
additional_dependencies: [zizmor==1.25.2]
files: ^\.github/
types: [yaml]
pass_filenames: false
-37
View File
@@ -104,30 +104,6 @@ RUN if [ "${BUILD_TRANSLATIONS}" = "true" ]; then \
rm -rf /app/superset/translations/*/*/*.[po,mo];
######################################################################
# superset-websocket builds the realtime WebSocket (Node) server that
# ships in the official image, launched via docker/entrypoints/run-websocket.sh
######################################################################
FROM node:24-trixie-slim AS superset-websocket
# Harden `npm ci` against transient npm-registry network blips (e.g. ECONNRESET).
ENV npm_config_fetch_retries=5 \
npm_config_fetch_retry_mintimeout=20000 \
npm_config_fetch_retry_maxtimeout=120000 \
npm_config_fetch_timeout=600000
WORKDIR /app/superset-websocket
# Install against the lockfile first (cached until it changes), then bundle the
# TypeScript server into a single self-contained CJS file (esbuild inlines every
# dependency), so the runtime image needs only the Node binary and dist/ — no
# node_modules to ship.
COPY superset-websocket/package.json superset-websocket/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY superset-websocket/ ./
RUN npm run build
######################################################################
# Base python layer
######################################################################
@@ -245,19 +221,6 @@ RUN rm superset/translations/*/*/*.po
COPY --from=superset-node /app/superset/translations superset/translations
COPY --from=python-translation-compiler /app/translations_mo superset/translations
# --- Realtime WebSocket server (part of the official image) ---------------
# The realtime transport (superset-websocket) is a Node service, bundled by
# esbuild into a single self-contained file. Copy the Node runtime plus that
# bundle so every image built from this stage can launch it via an alternate
# entrypoint (docker/entrypoints/run-websocket.sh) rather than needing a separate
# image. This lives here rather than in a single downstream stage so the lean and
# dev images both ship it — docker-compose-non-dev.yml runs the websocket service
# from the dev target.
RUN /app/docker/apt-install.sh libstdc++6
COPY --from=superset-websocket /usr/local/bin/node /usr/local/bin/node
COPY --from=superset-websocket --chown=superset:superset \
/app/superset-websocket/dist /app/superset-websocket/dist
HEALTHCHECK CMD /app/docker/docker-healthcheck.sh
CMD ["/app/docker/entrypoints/run-server.sh"]
EXPOSE ${SUPERSET_PORT}
-5
View File
@@ -441,11 +441,6 @@ categories:
url: https://bestpair.info/
contributors: ["@stevensuting"]
- name: Veremes
url: https://www.veremes.com/
logo: veremes.svg
contributors: ["@verdier"]
- name: Virtuoso QA
url: https://www.virtuosoqa.com
-10
View File
@@ -84,16 +84,6 @@ The `sql_lab` role is *additive*: it grants the SQL Lab permission set on top of
Deployments may grant or revoke individual view-menu permissions, which shifts the boundary for that deployment but does not redefine the model. Any custom role created by an operator inherits the same principle: its capabilities are whatever the operator has explicitly granted it. The Public principal follows the same rule: operators may grant the Public role read access to specific datasets or dashboards (typically for anonymous reporting use cases), which shifts the boundary for that deployment without redefining the model.
### Async Execution and Realtime Notifications
Asynchronous execution paths do not create a separate data-access capability. A background task is a continuation of an already-authorized action, such as reading chart data or executing SQL through SQL Lab. The initiating route, command, or scheduler must enforce the same route-level and object-level checks the synchronous path would enforce before it creates the task, and the worker must execute under the initiating principal's effective identity when row-level security, impersonation, embedded guest-token scope, or similar controls affect the result.
Task metadata is itself a request-scoped resource. Non-admin users and embedded guests may read or cancel only tasks they are subscribed to or that otherwise represent work they are entitled to observe; Admin may observe and manage tasks as part of the trusted operational boundary. A bug that lets a principal create, read, join, cancel, or receive task state for work outside the role and capability matrix is in scope.
Realtime transports, including WebSocket delivery backed by Redis or Valkey Pub/Sub, are notification mechanisms rather than authorization sources. WebSocket notification access is controlled by `can_read` on the `Realtime` resource. The broadcast scope is authenticated-global, not public: it reaches every authenticated realtime socket, and an anonymous request receives no realtime principal, no JWT cookie, and therefore no socket, so it never receives these messages (true anonymous/Public-role realtime is not offered and would require a separate, restricted model). Broadcast Pub/Sub messages, such as list-view entity-change events, must be context-free nudges; sensitive or authoritative state must not be published on the broadcast scope. Targeted Pub/Sub messages may carry task state only when the producer names routing keys derived from an authorized identity, such as a task subscriber's principal channel (or a per-tab channel derived from it); the producer validates every routing key against the task's own subscriber principals before publishing, and the websocket server forwards the payload only to sockets bound to those keys. Full data and result payloads must still be fetched through the normal protected REST API or cache-read path. Redis Streams used for task completion, dependency, and lock-release signalling are likewise coordination signals; the metastore or cache entry they wake a consumer to read remains the source of truth.
The realtime notification permission is distinct from the permission to read the underlying object. It controls whether a principal receives push notifications, not whether they may read the object once they call the protected REST API. Existing websocket connections are authorized by the JWT accepted at upgrade time; permission revocation after token minting is bounded by `WEBSOCKET_JWT_EXPIRATION_SECONDS` plus the websocket server's socket-check interval. Redis Streams are internal server-to-server coordination primitives and should not be directly exposed as an end-user subscription surface.
### Vulnerability Scope
The test for whether a finding is in scope is a single question:
+2 -214
View File
@@ -24,219 +24,9 @@ assists people when migrating to a new version.
## Next
### Tagging is on by default
`TAGGING_SYSTEM` now ships **on**. The Tags menu entry, the tag columns and
filters on the chart, dashboard and saved-query lists, and the Tags field in the
chart and dashboard property modals are all visible without configuration, and
tags are included in asset export and import.
**What operators should expect:**
- **Implicit tags accrue.** Saving a chart, dashboard, dataset or saved query,
and favoriting an asset, write rows to `tag` and `tagged_object` (`type:chart`,
`editor:<user id>`, `favorited_by:<user id>`). These have always been created
when the flag was on; they are simply no longer opt-in.
- **Exports gain a `tags` key and a `tags.yaml` file.** Chart and dashboard
export bundles carry custom tags. Importers on 6.0 and later understand both;
older importers skip the unrecognized `tags.yaml` file but reject chart and
dashboard YAML that contains a `tags` key, so strip that key before importing
a bundle into Superset 5.x or earlier.
- **The flag is honored at write time.** The tagging SQLA event listeners are
always attached at startup; the ones that create tags check `TAGGING_SYSTEM`
when they fire, so the flag, including a runtime override through
`GET_FEATURE_FLAGS_FUNC` or `IS_FEATURE_ENABLED_FUNC`, takes effect without a
restart. The cleanup listeners run regardless of the flag, so deleting an
asset never leaves orphaned `tagged_object` rows behind.
Set `FEATURE_FLAGS = {"TAGGING_SYSTEM": False}` to restore the previous
behavior. Existing tag rows are left untouched.
### Global Async Queries re-platformed onto the Global Task Framework (breaking)
Global Async Queries (GAQ) no longer runs on its own bespoke async-events
plumbing. Async chart data is now executed as Global Task Framework (GTF) tasks
(one task per `QueryObject`), the browser learns of completion by polling
`GET /api/v1/task/status_changes` (optionally accelerated by the WebSocket
transport below) and re-issuing the original `/chart/data` request against the
now-warm per-query cache, and the realtime WebSocket server is a generic,
feature-agnostic task push transport rather than a GAQ-specific event tail.
Breaking removals (no deprecation window):
- The `/api/v1/async_event/` REST API, `AsyncQueryManager`, and the
`qc-<hash>` query-context descriptor replay endpoint
(`GET /api/v1/chart/data/<cache_key>`) are removed. Any client that consumed a
`result_url` from a `202` response must move to the re-request model (the
built-in frontend already does).
- The following config keys are removed: `GLOBAL_ASYNC_QUERIES_CACHE_BACKEND`,
`GLOBAL_ASYNC_QUERIES_TRANSPORT`, `GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL`,
`GLOBAL_ASYNC_QUERIES_REDIS_STREAM_PREFIX`,
`GLOBAL_ASYNC_QUERIES_REDIS_STREAM_LIMIT`,
`GLOBAL_ASYNC_QUERIES_REDIS_STREAM_LIMIT_FIREHOSE`,
`GLOBAL_ASYNC_QUERIES_REGISTER_REQUEST_HANDLERS`,
`GLOBAL_ASYNC_QUERIES_JWT_*`, and
`GLOBAL_ASYNC_QUERY_MANAGER_CLASS`. The coordinator (locks, GTF, and now GAQ)
uses `DISTRIBUTED_COORDINATION_CONFIG` exclusively.
Enabling async chart data in the new flow:
```python
# feature flag: makes async chart data available (auto-enables GLOBAL_TASK_FRAMEWORK)
FEATURE_FLAGS = {"GLOBAL_ASYNC_QUERIES": True}
# a Redis connection for distributed coordination (locks, GTF signalling,
# and the realtime pub/sub); required for async execution in production
DISTRIBUTED_COORDINATION_CONFIG = {
"CACHE_TYPE": "RedisCache",
"CACHE_REDIS_HOST": "localhost",
"CACHE_REDIS_PORT": 6379,
"CACHE_REDIS_DB": 0,
}
```
Async is now **opt-in per request**: `GLOBAL_ASYNC_QUERIES` only makes async
*available*; whether a given `/chart/data` request runs async is decided by an
`async_mode` request flag (endpoint default `false`, so programmatic API clients
keep the synchronous `200` flow unless they opt in). The built-in frontend
resolves the `async_mode` it sends from a policy chain — per-dashboard override →
deployment default `GLOBAL_ASYNC_QUERIES_DEFAULT` (default `true`) → the feature
flag — so the UI keeps its existing async behavior by default.
**Embedded (guest token) async requires explicit role grants.** Async chart-data
completion is observed through `GET /api/v1/task/status_changes` (gated by
`can_read Task`) and, when the WebSocket transport is enabled, over the socket
(gated by `can_read Realtime`). An authenticated Gamma user has `can_read Task` by
default; the default guest role (`Public`) does **not**. So an embedded guest only
runs async when the operator grants its role `can_read Task` (and `can_read
Realtime` for the socket) — otherwise the request transparently falls back to the
synchronous `200` flow rather than returning a `202` the guest could never resolve.
Enabling the realtime WebSocket transport (optional; when enabled it becomes the
completion transport for async chart-data — see the note on the interval poll):
> **Note:** the realtime WebSocket transport is opt-in (`WEBSOCKET_ENABLE`
> defaults to `False`). When it is **disabled**, async chart-data completion is
> driven entirely by the `status_changes` interval poll (the source of truth).
> When it is **enabled**, completion is delivered over the socket and the
> recurring interval poll does not run; a one-shot `status_changes` catch-up on
> waiter registration and on socket reconnect reconciles anything missed while
> disconnected. The socket accelerates delivery over the authoritative
> `status_changes` API rather than replacing it: Redis Pub/Sub is best-effort
> (at-most-once, no replay), so a disconnect is reconciled by the catch-up on
> reconnect/registration. In the rare case a `task.status` is missed while the
> socket stays open, the request's give-up runs one final `status_changes` read
> before timing out — so a chart whose query actually finished still resolves; only
> if that read can't confirm completion does the request end in a bounded error (a
> page reload re-establishes state).
```python
WEBSOCKET_ENABLE = True
WEBSOCKET_URL = "ws://<same-host>:8080/"
WEBSOCKET_JWT_SECRET = "<output of: openssl rand -base64 42>"
```
The built-in Gamma role receives `can_read Realtime`; grant that permission to
custom roles that should receive websocket notifications.
Run the `superset-websocket` Node server on the **same browser-visible host**
(so its JWT channel cookie is shared) and point its `redis` config at the same
instance as `DISTRIBUTED_COORDINATION_CONFIG`, plus `jwtSecret` /
`jwtCookieName` matching the Flask config (`WEBSOCKET_JWT_SECRET` /
`WEBSOCKET_JWT_COOKIE_NAME`, default `superset-ws-token`). During websocket JWT
secret rotation, set the websocket server's `previousJwtSecret` /
`PREVIOUS_JWT_SECRET` to the old key while Flask continues minting cookies with
`WEBSOCKET_JWT_SECRET`. The server is bundled in the official Superset image
and launched via an alternate entrypoint — no separate image is required:
`docker run <superset-image> /app/docker/entrypoints/run-websocket.sh` (or the
opt-in `websocket` profile in `docker compose`). It **subscribes** to a single
Redis Pub/Sub channel, `realtime`, which carries a self-describing
`{topic, scope, routes, payload}` envelope (both the broadcast `entity.changed`
nudges and the targeted `task.status` messages), and forwards `{topic, payload}`
to browsers after routing — so a Redis ACL for the websocket server must allow
subscribing to `realtime` (this replaces the earlier `entity-changes:*` /
`task-status` channels); see `superset-websocket/README.md`.
Orphaned GTF tasks (a worker killed mid-execution) are now detected and cleaned
up server-side. While a worker holds a task it writes a liveness heartbeat
(`tasks.last_heartbeat`, every `GTF_TASK_HEARTBEAT_INTERVAL` seconds, default
`15`); a dedicated `reap_orphaned_tasks` Celery beat job reaps any active task
whose heartbeat is older than `GTF_ORPHAN_TASK_TIMEOUT` (default `60`) — revoking
its Celery job, marking it `FAILURE` so waiters unblock, and (on engines that
support query cancellation) cancelling the abandoned warehouse query out-of-band.
Enable the `reap_orphaned_tasks` beat schedule on a short interval (e.g. every
minute); it is separate from `prune_tasks` (a heavier retention delete run
infrequently). The heartbeat write is issued out-of-band and deliberately does
not advance `changed_on`.
Async chart-data query tasks are now cancellable: a per-query timeout
(`GLOBAL_ASYNC_QUERIES_QUERY_TIMEOUT`, default `None` = unbounded) or a user
cancel aborts the task, and on database engines that support query cancellation
(e.g. PostgreSQL, MySQL, Snowflake, Redshift) the abort also cancels the running
warehouse query over a fresh connection — including when the worker died (the
reaper cancels it). Engines without cancel support are unaffected — the task is
still freed, but the query runs to completion.
- Calculated (expression) dataset columns are now wrapped in parentheses when
compiled to SQL (`(<expression>)`), in `SELECT`, `GROUP BY`, `ORDER BY`,
`COUNT(DISTINCT ...)`, and the series-limit (top-N) prequery/JOIN paths. This
fixes a correctness bug where a bare boolean operator (e.g. `OR`) inside a
calculated column used as a series dimension leaked into the surrounding
operator precedence (`state = 'CA' OR state = 'NY' = 1` mis-parsing as
`state = 'CA' OR (state = 'NY' = 1)`). Query results are otherwise unchanged,
but the generated SQL text for calculated-column queries differs; deployments
that key on the exact compiled SQL (custom result-cache keys, logging, or SQL
diffing) may observe the added parentheses. Physical columns are unaffected,
as are calculated columns used as a temporal (time/x-axis) dimension, which
resolve through a separate time-grain path (`get_timestamp_expression`).
- **[BREAKING] `SemanticLayer` and `SemanticView` are now classified in the
Flask-AppBuilder role sets**, so `sync_role_definitions` (run on
`superset init` and on startup) stops granting the built-in **Gamma** role
write access to them. `SemanticLayer` is treated like `Database`
(`READ_ONLY_MODEL_VIEWS`): create/edit/delete become **admin-only**, while
read stays broadly available (its configuration is returned masked).
`SemanticView` is treated like `Dataset` (`GAMMA_READ_ONLY_MODEL_VIEWS`):
writes are Alpha-tier, reads Gamma-tier. Its custom read endpoints
(`views`, `connections`) are mapped to `can_read` so they remain
accessible under the read-only classification. A deployment relying on
Gamma users creating or editing semantic layers/views must grant those
permissions through a custom role. A migration retires the now-unused
`can_views` / `can_connections` permissions left on the `SemanticLayer`
view menu by earlier builds. Two upgrade-time notes on that migration:
it seeds the `SemanticLayer` view menu and its `can_read` PVM if absent, so
even a fresh or flag-off install gains that permission (harmless — the
endpoints 404 while `SEMANTIC_LAYERS` is off); and retiring the stale
permissions remaps any role that held them onto `can_read`, a small
widening — a custom role granted only `can_views` or `can_connections` gains
`can_read` (the semantic-layer list and its masked-configuration detail),
which it could not previously reach. Operators who hand-rolled semantic-layer
roles should re-audit them after upgrading. The feature remains gated behind
the default-off `SEMANTIC_LAYERS` flag.
### Archived dataset purge requires impact confirmation
`GET /api/v1/dataset/<uuid>/purge-impact` returns the charts and distinct
dashboards affected by permanently deleting an archived dataset, together with
an opaque `impact_token`. The dataset purge endpoint now requires that token in
the JSON body as `confirmed_impact_token`. API clients that call
`POST /api/v1/dataset/<uuid>/purge` must fetch and display the impact first;
requests with a missing or malformed token are rejected with 400.
The server rechecks the dependency identities immediately before mutation. If
they changed, purge performs no deletion and returns 409 with a refreshed impact
payload. Clients must display the new impact and obtain renewed confirmation
before retrying. Preview or recheck failures fail closed rather than treating
unknown impact as zero. Chart and dashboard purge endpoints are unchanged.
- The dashboard datasource-based visibility fallback now fails closed: a dashboard whose member charts datasources cannot be resolved (deleted datasource rows, missing `datasource_id`, or unsupported datasource types) is no longer accessible to users without explicit editor/viewer rights, and a dashboard composed of semantic-view charts now requires `datasource_access` on (at least one of) its semantic views or their parent semantic layer — previously any authenticated user could open such a dashboards shell. Because the fallback now considers every member chart rather than only table-backed ones, a user holding `datasource_access` on any single member datasource — including a semantic view or its parent layer — can open a mixed dashboard that previously denied them. Dashboards with no charts remain accessible, and dashboards with explicit viewers are unaffected. Conversely, holders of `all_datasource_access` now see every published no-viewer dashboard in the dashboard list — including chart-less ones previously hidden by the inner joins — matching what the object-level gate already allowed them to open.
- `SAMPLES_ROW_LIMIT` is now the default for `/datasource/samples` requests without a valid explicit `per_page`, rather than a hard per-request ceiling; explicit limits are honored up to the existing global row-limit ceiling, matching `/chart/data` SAMPLES requests.
- The `cockroachdb` extra (`pip install apache-superset[cockroachdb]`) now installs `sqlalchemy-cockroachdb` instead of the abandoned `cockroachdb` package, whose SQLAlchemy dialect could not be imported under SQLAlchemy 2.0. Existing environments with the old package installed should `pip uninstall cockroachdb && pip install sqlalchemy-cockroachdb` (or simply reinstall the extra) to restore CockroachDB connectivity.
### Native Value filter "Select all" always targets the whole column
The native "Value" filter's bulk "Select all" / "Clear" controls now operate on the entire loaded set of column values regardless of any text typed into the filter's search box. Previously the "Select all (N)" count briefly flickered to the search-scoped count before settling on the full-column count, and clicking "Select all" while searching could select only the currently matching subset. Search-scoped bulk selection was never a supported feature; the count is now stable and always matches what "Select all" selects (the full column). No configuration change is required.
### MCP tool results preserve stored string values
Structured MCP tool results no longer add `<UNTRUSTED-CONTENT>` wrappers or
@@ -440,10 +230,8 @@ Behavior changes to be aware of:
fail fast at the first phase check rather than erroring at setup.
- Dashboard reports whose charts have not mounted are no longer captured
blank: readiness is polled until the deadline, and the report fails loudly
if charts never mount. Large tiled reports also retry Chromium screenshot
stalls and suspicious uniform tiles, while persistent screenshot timeouts
fail loudly. Large tiled thumbnails use the same bounded retries, but retain
their previous failure contract after a persistent timeout.
if charts never mount. Thumbnails and non-report screenshots keep their
previous behavior.
### Embedded (guest token) API responses no longer echo database errors
-35
View File
@@ -137,41 +137,6 @@ services:
healthcheck:
disable: true
# Realtime WebSocket transport, launched from the official image via its
# alternate entrypoint (no separate image needed). Opt-in — start it with
# `docker compose --profile websocket up`. To actually use it, the Superset
# app must also set WEBSOCKET_ENABLE=true, WEBSOCKET_URL, and a matching
# WEBSOCKET_JWT_SECRET (== the JWT_SECRET below) in docker/.env-local.
superset-websocket:
build:
<<: *common-build
container_name: superset_websocket
profiles:
- websocket
# Neither a volume mount nor the root user is needed: the entrypoint and the
# Node bundle it runs are both baked into the image, and the server is
# configured entirely through the environment below.
command: ["/app/docker/entrypoints/run-websocket.sh"]
environment:
REDIS_HOST: redis
REDIS_PORT: 6379
PORT: 8080
JWT_COOKIE_NAME: superset-ws-token
# Dev-only default; must match the app's WEBSOCKET_JWT_SECRET and be
# replaced with a strong secret (>= 32 bytes) outside local development.
JWT_SECRET: ${WEBSOCKET_JWT_SECRET:-dev-only-websocket-secret-change-me!}
# Optional verify-only old key for websocket JWT secret rotation.
PREVIOUS_JWT_SECRET: ${WEBSOCKET_PREVIOUS_JWT_SECRET:-}
restart: unless-stopped
ports:
- 8080:8080
depends_on:
redis:
condition: service_started
# Overrides the image-level HEALTHCHECK, which probes the Superset app.
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health"]
volumes:
superset_home:
external: false
-9
View File
@@ -19,14 +19,6 @@
#
HYPHEN_SYMBOL='-'
STATSD_ARGS=()
STATSD_HOST="${SERVER_STATSD_HOST//[[:space:]]/}"
if [ -n "${STATSD_HOST}" ]; then
STATSD_PORT="${SERVER_STATSD_PORT//[[:space:]]/}"
STATSD_PORT="${STATSD_PORT:-8125}"
STATSD_ARGS=(--statsd-host "${STATSD_HOST}:${STATSD_PORT}" --statsd-prefix "${SERVER_STATSD_PREFIX:-superset}")
fi
exec gunicorn \
--bind "${SUPERSET_BIND_ADDRESS:-0.0.0.0}:${SUPERSET_PORT:-8088}" \
--access-logfile "${ACCESS_LOG_FILE:-$HYPHEN_SYMBOL}" \
@@ -41,5 +33,4 @@ exec gunicorn \
--max-requests-jitter ${WORKER_MAX_REQUESTS_JITTER:-0} \
--limit-request-line ${SERVER_LIMIT_REQUEST_LINE:-0} \
--limit-request-field_size ${SERVER_LIMIT_REQUEST_FIELD_SIZE:-0} \
"${STATSD_ARGS[@]}" \
"${FLASK_APP}"
-44
View File
@@ -1,44 +0,0 @@
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Launch the realtime WebSocket server (superset-websocket) bundled in the
# official image. Run it with:
#
# docker run <superset-image> /app/docker/entrypoints/run-websocket.sh
#
# Configure via environment variables — see superset-websocket/src/config.ts for
# the authoritative, complete set (Redis connection, logging, connection limits,
# StatsD, etc.). The values that MUST match the Flask app's config are:
# JWT_SECRET == WEBSOCKET_JWT_SECRET
# JWT_COOKIE_NAME == WEBSOCKET_JWT_COOKIE_NAME (default superset-ws-token)
# REALTIME_CHANNEL_PREFIX == Flask REALTIME_CHANNEL_PREFIX (default empty; set a
# per-deployment value on both sides to isolate a shared Redis/Valkey)
# Optional rotation setting:
# PREVIOUS_JWT_SECRET == old WEBSOCKET_JWT_SECRET accepted for verification
# and the Redis connection (REDIS_HOST/REDIS_PORT/...) must point at the same
# instance as the app's DISTRIBUTED_COORDINATION_CONFIG.
set -e
# Run from a writable directory so that opting into file logging with the
# default relative LOG_FILENAME (LOG_TO_FILE=true) writes somewhere the
# unprivileged `superset` user can create files, rather than the read-only /app.
# The config.json lookup is unaffected (it resolves relative to the bundle).
cd "${SUPERSET_HOME:-/app/superset_home}"
exec node /app/superset-websocket/dist/index.cjs start
@@ -15,8 +15,8 @@
"db": 0,
"ssl": false
},
"redisStreamPrefix": "async-events-",
"jwtAlgorithms": ["HS256"],
"jwtSecret": "CHANGE-ME-IN-PRODUCTION-GOTTA-BE-LONG-AND-SECRET",
"previousJwtSecret": "",
"jwtCookieName": "superset-ws-token"
"jwtCookieName": "async-token"
}
+3 -3
View File
@@ -62,8 +62,8 @@ yarn version:remove:developer_docs <version> # Remove developer docs version
yarn version:remove:components <version> # Remove components version
# Quality Checks
yarn typecheck # TypeScript validation
yarn lint # Lint TypeScript/JavaScript files
yarn typecheck # TypeScript validation
yarn eslint # Lint TypeScript/JavaScript files
```
## 📁 Documentation Structure
@@ -431,7 +431,7 @@ yarn build
yarn typecheck
# Linting issues
yarn lint
yarn eslint
```
### Version Issues
@@ -486,39 +486,6 @@ Log in as an admin user to ensure you have adequate permissions.
This is the best source of information about the problem. In a docker compose deployment, you can do this with a command like `docker logs superset_worker --since 1h`.
### CSV and Excel chart-data request failures
The worker uses the saved query context to POST to the chart-data export endpoint,
falling back to the legacy GET export when a query context cannot be generated.
`ALERT_REPORTS_CSV_REQUEST_TIMEOUT` (60 seconds by default) limits socket operations;
the report execution budget and its delivery/cleanup reserves also cap the request.
Connection and read timeouts are reported as CSV/Excel generation timeouts.
These attachment timeouts are logged at error level and explicitly mark the report
task as failed, while the report execution retains its ERROR state and separate
error-notification history. Other HTTP 408 exception handling is unchanged.
To tolerate short-lived transport failures, operators can opt in with
`ALERT_REPORTS_CSV_REQUEST_RETRY = True` (default: `False`). This permits **one** retry
for transient connection/read failures and HTTP 429, 500, 502, 503, or 504. Other
HTTP statuses are not retried. Backoff is 0.5 seconds, extended to at most 2 seconds
for a numeric `Retry-After`; longer, invalid, or date-based delays are not retried
inline. Both attempts and backoff share the initial request timeout allowance and
respect the remaining execution budget. Unbounded requests are not retried.
A request that consumes its entire timeout does **not** get another full timeout.
Socket timeouts are not wall-clock cancellation: existing report task limits still
interrupt in-flight work. A timed-out server query can continue running, so enabling
retries can increase database load. Leave retries disabled unless appropriate for
your deployment; disable the setting to roll back retry behavior.
Worker diagnostics include schedule/chart identifiers, a fixed endpoint path (no
query string), error category, HTTP status, timeout, elapsed duration, and attempt.
For HTTP errors, at most 4097 response bytes are read to enforce a 4096-byte limit.
Only recognized Superset error types from up to four JSON errors are retained;
free-form messages, extra fields, and non-JSON or oversized bodies are redacted or
omitted. Cookies, authentication headers, URLs, SQL, and query payloads are not
included in these transport diagnostics. HTTP 400 therefore remains a failure to
investigate, not a reason to repeat the same request.
### Check web browser and webdriver installation
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV, XLSX, or text but can't send as PNG, your problem may lie with the browser.
+7 -48
View File
@@ -97,37 +97,6 @@ This setting only applies to requests detected as native filter option queries.
over the per-chart/dataset/database timeouts, but not over an explicit per-request
`custom_cache_timeout` override (e.g. "Force refresh").
## Async Query Result Cache TTL
When [Global Async Queries](/admin-docs/configuration/configuring-superset#feature-flags) is
enabled, a chart-data request that runs asynchronously does not return the result inline. Instead the
query executes on a background task that **writes the result to the data cache**, and the browser
then re-issues the same request to read that result back out of the cache once the task succeeds.
This read-back is what makes the result-cache TTL matter for correctness, not just performance: if
the effective TTL is shorter than the full async round trip (task execution + the client's poll
interval + the re-fetch), the entry can be **evicted before the client reads it**, leaving the chart
stuck re-running instead of loading. To prevent this, async requests floor their result-cache TTL to
`GLOBAL_ASYNC_QUERIES_MIN_CACHE_TTL` (seconds, default `300` — five minutes):
```python
GLOBAL_ASYNC_QUERIES_MIN_CACHE_TTL = 300 # seconds
```
How the floor interacts with the timeouts above:
- It applies **only to async execution**. Synchronous `/chart/data` requests keep their normal
chart/dataset/database/`DATA_CACHE_CONFIG` timeout even when Global Async Queries is enabled.
- A **longer** effective TTL from that chain is kept as-is — the floor only raises TTLs that are
shorter than it.
- A TTL of `0` ("cache forever") is left untouched.
Tuning guidance: raise this value if your workload's async round trip can exceed five minutes (very
long-running queries or slow warehouses), otherwise those charts may intermittently fail to load. Be
aware of the trade-off — because the floor can raise an async result's TTL above a shorter cache
retention policy, it keeps async results in the cache longer and modestly increases cache
(Redis/Valkey) usage. Do not lower it below your worst-case async round trip.
## Limiting Cached Result Size
Very large chart or SQL query results can flood the cache backend (Redis/Memcached), evicting many
@@ -347,25 +316,14 @@ high-performance distributed operations. This configuration enables:
- **Distributed locking**: Moves lock operations from the metadata database to Redis, improving
performance and reducing metastore load
- **Event-driven notifications**: Task completion and abort signals are delivered over Redis
**Streams**, so waiters (sync join-and-wait, task-dependency DAGs, abort listeners) wake when a
signal lands instead of polling the metadata database. Because stream entries are persisted, a
waiter that reads slightly late, reconnects, or fails over still receives the signal. Without this
backend, these operations poll the metadata database instead.
- **Real-time event notifications**: Enables instant pub/sub messaging for task abort signals and
completion notifications instead of polling-based approaches
:::note
This requires Redis or Valkey specifically—it uses Redis-specific features (Streams, pub/sub,
`SET NX EX`) that are not available in general Flask-Caching backends.
This requires Redis or Valkey specifically—it uses Redis-specific features (pub/sub, `SET NX EX`)
that are not available in general Flask-Caching backends.
:::
Each signal stream keeps only its latest entry and is given a TTL, so signal streams for tasks that
are never awaited do not accumulate in Redis/Valkey. Set the retention window with
`DISTRIBUTED_COORDINATION_SIGNAL_TTL` (seconds, default 24 hours):
```python
DISTRIBUTED_COORDINATION_SIGNAL_TTL = 24 * 60 * 60
```
### Configuration
The distributed coordination uses Flask-Caching style configuration for consistency with other cache
@@ -408,8 +366,9 @@ DISTRIBUTED_COORDINATION_CONFIG = {
}
```
By default, connections opened for `DISTRIBUTED_COORDINATION_CONFIG` have no socket
timeout. This can be overridden with `CACHE_REDIS_SOCKET_TIMEOUT` and
By default, connections opened for `DISTRIBUTED_COORDINATION_CONFIG` (as well as
`GLOBAL_ASYNC_QUERIES_CACHE_BACKEND`, which uses the same `RedisCache`/`RedisSentinelCache`
backend) have no socket timeout. This can be overridden with `CACHE_REDIS_SOCKET_TIMEOUT` and
`CACHE_REDIS_SOCKET_CONNECT_TIMEOUT`, both in seconds:
```python
@@ -566,55 +566,6 @@ def FLASK_APP_MUTATOR(app: Flask) -> None:
app.before_request_funcs.setdefault(None, []).append(make_session_permanent)
```
## Carrying extra data through chart and dashboard exports
Deployments often attach their own metadata to charts and dashboards — an owning
team, a catalogue entry, a cost centre — and need it to survive an export/import
round trip between environments. `EXTRA_ASSET_EXPORT_FIELDS` and
`EXTRA_ASSET_IMPORT_HANDLER` let you do that without forking the export commands.
The export hook receives the model and the asset type (`"chart"` or `"dashboard"`)
and returns a mapping, which is serialised under the `extra` key of the asset's
YAML. The import hook receives the model, the asset type and that same mapping,
once the asset exists and has an id:
```python
# superset_config.py
def _export_fields(model, asset_type):
return {"owning_team": lookup_team(model)}
def _import_handler(model, asset_type, extra):
if team := extra.get("owning_team"):
assign_team(model, team)
EXTRA_ASSET_EXPORT_FIELDS = _export_fields
EXTRA_ASSET_IMPORT_HANDLER = _import_handler
```
The exported YAML then carries:
```yaml
slice_name: Revenue by region
...
extra:
owning_team: analytics-platform
```
A few things worth knowing:
- **Both hooks are optional and default to `None`.** With neither configured,
exported files are byte-for-byte what they were before, and imports behave
identically.
- **Everything lives under the single `extra` key.** The import schemas reject
unknown top-level fields, so namespacing under `extra` keeps that strictness
while leaving you free to change the shape of your own payload later.
- **An export hook returning `None` or an empty mapping writes nothing**, so
assets without your metadata do not gain an empty `extra` block.
- **The import handler runs after the asset is created or updated**, which means
you can rely on `model.id`. Raising from it will fail the import.
## Customizing the landing page (index view)
The page served at `/` is rendered by an index view. By default Superset registers
@@ -50,34 +50,13 @@ Superset can be configured to log events to [StatsD](https://github.com/statsd/s
if desired. Most endpoints hit are logged as
well as key events like query start and end in SQL Lab.
Superset can also collect gunicorn [metrics](https://gunicorn.org/instrumentation/).
To enable these, the following environment variables should be set:
```bash
SERVER_STATSD_HOST=localhost
SERVER_STATSD_PORT=8125
SERVER_STATSD_PREFIX=superset
```
To setup StatsD logging for Superset, its a matter of configuring the logger in your `superset_config.py`.
To setup StatsD logging, its a matter of configuring the logger in your `superset_config.py`.
If not already present, you need to ensure that the `statsd`-package is installed in Superset's python environment.
```python
import os
from superset.stats_logger import StatsdStatsLogger
try:
STATSD_PORT = int(os.environ.get("SERVER_STATSD_PORT", "8125"))
except ValueError:
STATSD_PORT = 8125
STATS_LOGGER = StatsdStatsLogger(
host=os.environ.get("SERVER_STATSD_HOST", "localhost"),
port=STATSD_PORT,
prefix=os.environ.get("SERVER_STATSD_PREFIX", "superset"),
)
STATS_LOGGER = StatsdStatsLogger(host='localhost', port=8125, prefix='superset')
```
[statsd](https://pypi.org/project/statsd/) in version ~3.3.0 must be installed.
Note that its also possible to implement your own logger by deriving
`superset.stats_logger.BaseStatsLogger`.
@@ -253,58 +253,6 @@ def my_custom_auth_factory(app):
MCP_AUTH_FACTORY = my_custom_auth_factory
```
### Embedded Guest Authentication
Superset's [embedded dashboards](/user-docs/using-superset/embedding) feature mints short-lived **guest tokens** for anonymous/embedded viewers. The MCP server can accept these same guest tokens, so an embedded guest (e.g. an in-app chatbot next to an embedded dashboard) can call MCP tools scoped to the dashboards/resources named in its token.
This is opt-in and reuses the existing core guest-token configuration -- there is no MCP-specific guest secret or audience.
```python
# superset_config.py
FEATURE_FLAGS = {"EMBEDDED_SUPERSET": True} # required -- guest tokens only exist when this is on
MCP_EMBEDDED_GUEST_AUTH_ENABLED = True # opt-in for the MCP transport (default False)
```
Present the guest token the same way as any other bearer token:
```bash
curl -X POST http://localhost:5008/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_GUEST_TOKEN' \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
```
**How it works**
- A dedicated guest-token verifier validates the token against the same `GUEST_TOKEN_JWT_SECRET` / `GUEST_TOKEN_JWT_ALGO` / `GUEST_TOKEN_JWT_AUDIENCE` config used by embedded dashboards, replays the embedded structural checks, and enforces revocation (global version bumps and per-dashboard `guest_token_revoked_before` cutoffs). It runs *before* the JWT verifier described above, since guest tokens are signed with a different key/algorithm and would otherwise be rejected at the transport.
- A verified guest resolves to a Superset guest user as the highest-priority identity, so it's never downgraded to API-key / `MCP_DEV_USERNAME` / dev-mode resolution. Data access is scoped by the same checks (dataset allowlist, dashboard access, row-level security) that apply to embedded dashboard views.
- Guests are restricted to a default-deny allow-list, `MCP_GUEST_ALLOWED_TOOLS`, regardless of `MCP_RBAC_ENABLED`. Sensitive enumeration tools like `find_users` and `get_instance_info` are denied simply by being absent from the default list.
- Setting `MCP_AUTH_FACTORY` bypasses this whole path: a configured factory is tried first, and the default factory that wires up the guest-token verifier is never reached. If you rely on a custom auth factory (e.g. your own OIDC provider) alongside guest auth, that factory must verify guest tokens itself -- otherwise they're rejected regardless of `MCP_EMBEDDED_GUEST_AUTH_ENABLED`.
```python
# superset_config.py
MCP_GUEST_ALLOWED_TOOLS = {
"get_dashboard_info",
"get_dashboard_layout",
"list_dashboards",
"list_charts",
"get_chart_info",
"get_chart_data",
"get_chart_preview",
} # default
```
**Deployment requirements**
- The MCP server and the service that mints guest tokens (the Superset web app) must share `GUEST_TOKEN_JWT_SECRET` and `GUEST_TOKEN_JWT_AUDIENCE`. Set `GUEST_TOKEN_JWT_AUDIENCE` explicitly -- if it's unset, audience validation falls back to the URL host, which can differ between the two services and cause every guest token to fail validation.
- The `GUEST_ROLE_NAME` role (default `Public`) must exist -- a guest token is rejected if it does not.
- Don't set `MCP_DEV_USERNAME` on a deployment that also serves embedded guests.
- Restart the MCP process after toggling `EMBEDDED_SUPERSET` or `MCP_EMBEDDED_GUEST_AUTH_ENABLED` -- guest auth is wired up once at startup.
:::warning
`GUEST_TOKEN_JWT_SECRET` guards both the web embedding and MCP guest-auth surfaces. With `MCP_EMBEDDED_GUEST_AUTH_ENABLED` on, leaving it at its insecure default isn't just a forgery risk -- the MCP server refuses to start (`MCPAuthConfigError`) until you set a real secret shared with the guest-token minting service.
:::
---
## Connecting AI Clients
@@ -540,8 +488,6 @@ MCP_STORE_CONFIG = {
When `CACHE_REDIS_URL` is set, the MCP server uses a Redis-backed EventStore for session management, allowing replicas to share state. Without Redis, each pod manages its own in-memory sessions and stateful MCP interactions may fail when requests hit different replicas.
`MCP_STATELESS_HTTP` (default `True`) controls whether requests get a fresh, ephemeral transport per HTTP round trip or a transport that stays alive for the session's lifetime. The default suits multi-pod deployments because it doesn't require session affinity -- any pod can handle any request. Its tradeoff: a client disconnecting mid-tool-call can crash not just its own session but other concurrent sessions on the same worker. Setting it to `False` avoids that, but it requires session-affinity (sticky session) routing on `Mcp-Session-Id` at the mesh/ingress layer, since a session's follow-up requests must land on the same pod that created it. See [`MCP_STATELESS_HTTP`](#core) below.
---
## Configuration Reference
@@ -557,7 +503,6 @@ All MCP settings go in `superset_config.py`. Defaults are defined in `superset/m
| `MCP_SERVICE_URL` | `None` | Public base URL for MCP-generated links (set this when behind a reverse proxy) |
| `MCP_DEBUG` | `False` | Enable debug logging |
| `MCP_DEV_USERNAME` | -- | Superset username for development mode (no auth) |
| `MCP_STATELESS_HTTP` | `True` | Streamable-HTTP session mode. `True` gives each request a fresh, ephemeral transport, torn down as soon as that request completes; a client disconnecting mid-tool-call can crash not just its own session but other concurrent sessions on the same worker. `False` keeps the transport alive for the session's lifetime, avoiding that crash, but requires session-affinity routing on `Mcp-Session-Id` for multi-pod deployments (see [Multi-Pod (Kubernetes)](#multi-pod-kubernetes)). |
| `MCP_RBAC_ENABLED` | `True` | Enforce Superset's role-based access control on MCP tool calls. When `True`, each tool checks that the authenticated user has the required FAB permission before executing. Disable only for testing or trusted-network deployments. |
| `MCP_DISABLED_TOOLS` | `set()` | Set of tool names to remove from the MCP server at startup. Disabled tools are never advertised to AI clients during tool discovery. Useful when a custom extension tool should replace a built-in Superset tool. See [Disabling built-in tools](#disabling-built-in-tools). |
| `MCP_DISABLED_CHART_PLUGINS` | `frozenset()` | Set of chart type plugin names (e.g. `"handlebars"`) to hide from `generate_chart`. Does not affect `get_chart_type_schema`. See [Disabling chart type plugins](#disabling-chart-type-plugins). |
@@ -578,8 +523,6 @@ All MCP settings go in `superset_config.py`. Defaults are defined in `superset/m
| `MCP_JWT_DEBUG_ERRORS` | `False` | Log detailed JWT errors server-side (never exposed in HTTP responses per RFC 6750) |
| `MCP_AUTH_FACTORY` | `None` | Custom auth provider factory `(flask_app) -> auth_provider`. Takes precedence over built-in JWT |
| `MCP_USER_RESOLVER` | `None` | Custom function `(app, access_token) -> username` to extract a Superset username from a validated JWT token. When `None`, the default resolver checks `preferred_username`, `username`, `email`, and `sub` claims in that order. |
| `MCP_EMBEDDED_GUEST_AUTH_ENABLED` | `False` | Accept embedded [guest tokens](#embedded-guest-authentication) as Bearer auth. Also requires the `EMBEDDED_SUPERSET` feature flag. |
| `MCP_GUEST_ALLOWED_TOOLS` | see [default list](#embedded-guest-authentication) | The only tool names callable by embedded guests (default-deny), regardless of `MCP_RBAC_ENABLED`. |
### Response Size Guard
-157
View File
@@ -138,18 +138,6 @@ The existing `APP_NAME` Python config key continues to work for backward compati
Email and alert/report notification subjects are driven by backend settings such as
`EMAIL_REPORTS_SUBJECT_PREFIX` and `APP_NAME`, not by this theme token.
To hide the entire brand area in the navbar (both the logo image and the
brand text), set `HIDE_NAVBAR_LOGO` in `superset_config.py`:
```python
# Hide the entire brand area in the navbar, including the logo image and the
# brand text (brandAppName / APP_NAME). Defaults to False.
HIDE_NAVBAR_LOGO = True
```
`HIDE_NAVBAR_LOGO` is a Python config flag rather than a theme token, so it
cannot be set through the theme CRUD UI or `THEME_DEFAULT`/`THEME_DARK`.
### Migration from Configuration to UI
When `ENABLE_UI_THEME_ADMINISTRATION = True`:
@@ -252,39 +240,6 @@ Font URLs are validated against a configurable allowlist. By default, fonts from
This feature works with the stock Docker image - no custom build required!
## Results Grid Configuration Overrides
Superset exposes a handful of opt-in tokens that customize the appearance of
the results grid in SQL Lab. These tokens have no effect unless explicitly
set, since the results grid otherwise falls back to its built-in defaults.
```python
THEME_DEFAULT = {
"token": {
"colorPrimary": "#2893B3",
# ... other Ant Design tokens
# Results grid overrides
"resultsGridRowHeight": 32,
"resultsGridHeaderFontSize": 13,
"resultsGridHeaderFontWeight": 600,
"resultsGridBorderRadius": 4,
"resultsGridNoStriping": True,
}
}
```
| Token | Type | Description |
| --- | --- | --- |
| `resultsGridRowHeight` | `number` | Row and header height, in pixels. |
| `resultsGridHeaderFontSize` | `number` | Header cell font size, in pixels. |
| `resultsGridHeaderFontWeight` | `number` | Header cell font weight. |
| `resultsGridBorderRadius` | `number` | Border radius applied to the grid and its wrapper, in pixels. |
| `resultsGridNoStriping` | `boolean` | When `true`, disables alternating row background striping. |
These tokens can also be set through the theme CRUD interface's JSON editor,
alongside any other Superset-specific tokens.
## ECharts Configuration Overrides
:::note
@@ -499,118 +454,6 @@ THEME_DEFAULT = {
This feature provides powerful theming capabilities while maintaining the flexibility of ECharts' extensive configuration options.
## Component Sizing & Style Tokens
:::note
These tokens landed after the Superset 6.1 release and are only available on
`master`; they are not present in any tagged release yet.
:::
Beyond colors and fonts, a handful of Superset-specific tokens let you tune the
sizing, radius, and outline behavior of individual UI components. All of these
tokens are optional — omit them and components fall back to their existing
defaults, so applying them is a zero-visual-change operation until you opt in.
### Button & DropdownButton Sizing
```python
THEME_DEFAULT = {
"token": {
# ... other tokens
"buttonControlHeight": 32, # default button height, in px
"buttonControlHeightSM": 30, # small/dropdown button height, in px
"buttonControlHeightXS": 22, # xsmall button height, in px
"buttonPaddingInline": 18, # horizontal padding, in px
"buttonPaddingInlineSM": 10, # horizontal padding for small buttons, in px
"buttonFontSize": 14,
"buttonBorderRadius": 4,
}
}
```
`buttonControlHeight` and `buttonBorderRadius` also drive the sizing of the
menu-trigger button used by `PageHeaderWithActions`, so a single pair of tokens
keeps page-header icon buttons visually consistent with regular buttons.
For one-off overrides that shouldn't apply to every button in the app, pass a
`styleConfig` prop directly to `Button` or `DropdownButton` instead of setting
a theme token:
```tsx
<Button
styleConfig={{
controlHeight: 40,
paddingInline: 20,
fontSize: 16,
fontWeight: 700,
borderRadius: 8,
ctaMinWidth: 120,
ctaMinHeight: 40,
iconGap: 8,
}}
>
Click me
</Button>
<DropdownButton
styleConfig={{
controlHeight: 32,
fontSize: 14,
fontWeight: 500,
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
}}
menu={menuProps}
>
Options
</DropdownButton>
```
`styleConfig` values take precedence over the equivalent theme tokens, which in
turn take precedence over the built-in defaults.
### Label Border Radius
```python
THEME_DEFAULT = {
"token": {
"labelBorderRadius": 4, # defaults to 8px
}
}
```
### Select Option Outline
By default, hovering or navigating to an option in a `Select` dropdown draws a
2px outline in `colorPrimary`. Set `selectOptionActiveOutline` to `False` for a
more subtle hover style with no outline:
```python
THEME_DEFAULT = {
"token": {
"selectOptionActiveOutline": False,
}
}
```
### Dashboard Tile Appearance
Chart tiles on a dashboard (not text/markdown tiles) can be restyled via
`dashboardTile*` tokens. All fall back to the existing look — a
`colorBgContainer` background, a `1px solid colorBorder` border, and a
hairline `box-shadow` while the tile is fading out (e.g. when a filter
makes it irrelevant):
```python
THEME_DEFAULT = {
"token": {
"dashboardTileBg": "#ffffff",
"dashboardTileBorder": "1px solid #e0e0e0",
"dashboardTileBorderRadius": 8,
"dashboardTileBoxShadow": "0 1px 2px rgba(0, 0, 0, 0.08)",
}
}
```
## Advanced Features
- **System Themes**: Manage system-wide default and dark themes via UI or configuration
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
:::
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
data. Knowing the installation counts for different Superset versions informs the project's
decisions about patching and long-term support. Scarf purges personally identifiable information
(PII) and provides only aggregated statistics.
+1 -49
View File
@@ -87,7 +87,6 @@ The chart will publish appropriate services to expose the Superset UI internally
- Configure the Service as a `LoadBalancer` or `NodePort`
- Set up an `Ingress` for it - the chart includes a definition, but will need to be tuned to your needs (hostname, tls, annotations etc...)
- Set up a Gateway API `HTTPRoute` for it - see [Exposing Superset via Gateway API (HTTPRoute)](#exposing-superset-via-gateway-api-httproute) below
- Run `kubectl port-forward superset-xxxx-yyyy :8088` to directly tunnel one pod's port into your localhost
Depending how you configured external access, the URL will vary. Once you've identified the appropriate URL you can log in with:
@@ -136,7 +135,7 @@ init:
```
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
There are two independent telemetry channels:
@@ -320,53 +319,6 @@ configOverrides:
AUTH_USER_REGISTRATION_ROLE = "Admin"
```
### Exposing Superset via Gateway API (HTTPRoute)
As an alternative to `Ingress`, the chart can create a [Gateway API](https://gateway-api.sigs.k8s.io/)
`HTTPRoute` that attaches to a Gateway already running in your cluster. This requires the Gateway
API CRDs serving the configured `httproute.apiVersion` (`gateway.networking.k8s.io/v1` by default)
to be installed, along with a Gateway resource for the route to attach to. If the Gateway lives in
a different namespace than the `HTTPRoute` (as in the
example below), its listener's `allowedRoutes` must explicitly permit routes from this release's
namespace, or the `HTTPRoute` will install successfully but never attach.
```yaml
httproute:
enabled: true
parentRefs:
- name: my-gateway
namespace: gateway-system
hostnames:
- superset.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
```
- `httproute.parentRefs` lists the Gateway(s) the route attaches to.
- `httproute.hostnames` matches against the HTTP `Host` header; it's templated, so values like
`{{ .Release.Name }}` can be used.
- `httproute.rules` are routing rules backed by the Superset service; each rule accepts standard
`matches`, `filters`, and `timeouts` fields, and an optional `weight` (defaults to `1`) applied to
its single backend reference. Since each rule maps to one backend, `weight` has no traffic-splitting
effect here; it only matters if you fork the template to add multiple `backendRefs` to a rule.
`timeouts` only joined the Gateway API Standard channel in v1.2, so it requires both v1.2+ CRDs
and a supporting controller; drop it if either predates that.
- If `supersetWebsockets.enabled` is set, an extra rule routing `supersetWebsockets.ingress.path`
(default `/ws`) to the `-ws` service is appended automatically, mirroring the `Ingress` behavior.
WebSocket upgrade support is controller-dependent under Gateway API; check your Gateway
implementation's docs in case it needs an explicit protocol opt-in for global async queries to
keep working behind a Gateway.
- If `supersetMcp.enabled` and `supersetMcp.httproute.enabled` are both set, an extra rule routing
`supersetMcp.httproute.path` to the `-mcp` service is appended as well. Don't expose this route
without first enabling MCP authentication — see the
[MCP Server Deployment & Authentication](/admin-docs/configuration/mcp-server#authentication) doc;
by default the MCP server runs in dev mode with auth disabled.
- Set `httproute.apiVersion` to `gateway.networking.k8s.io/v1beta1` if your cluster's Gateway API
installation hasn't promoted `HTTPRoute` to `v1` yet.
### Enable Alerts and Reports
For this, as per the [Alerts and Reports doc](/admin-docs/configuration/alerts-reports), you will need to:
@@ -183,14 +183,13 @@ https://superset.apache.org/admin-docs/configuration/configuring-superset/#rotat
| --------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `SUPERSET_SECRET_KEY` | Signs session cookies; key material for encrypting stored DB credentials (Fernet/AES) | Forged sessions (auth bypass / privilege escalation); decryption of exfiltrated metadata-DB secrets | Quarterly + post-incident |
| `GUEST_TOKEN_JWT_SECRET` | Signs embedded-dashboard guest tokens | Forged guest tokens → unauthorized dashboard/data access | Quarterly + post-incident |
| `WEBSOCKET_JWT_SECRET` | Signs the realtime websocket channel JWT cookie | Forged websocket tokens → unauthorized realtime notifications | Quarterly + post-incident |
| `GLOBAL_ASYNC_QUERIES_JWT_SECRET` | Signs the async-query channel JWT | Forged async-query tokens | Quarterly + post-incident |
| SMTP password | Outbound email for alerts & reports | Email relay abuse / spoofing | Per organizational policy + post-incident |
| Database connection passwords | Access to analytical databases and the metadata DB | Direct database access | Per organizational policy + post-incident |
Notes:
- Rotating `GUEST_TOKEN_JWT_SECRET` or `WEBSOCKET_JWT_SECRET` invalidates outstanding tokens of that type; schedule rotations accordingly.
- `WEBSOCKET_JWT_SECRET` can be rotated without disconnecting live sockets: set the outgoing value as `PREVIOUS_JWT_SECRET` on the websocket server so it keeps verifying old cookies, then remove it once they have aged out.
- Rotating `GUEST_TOKEN_JWT_SECRET` or `GLOBAL_ASYNC_QUERIES_JWT_SECRET` invalidates outstanding tokens of that type; schedule rotations accordingly.
- After a suspected compromise, rotate **all** of the above, not only `SUPERSET_SECRET_KEY`.
- Keep the register under change control so new secrets introduced by future features are added to the rotation schedule.
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
:::
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
data. Knowing the installation counts for different Superset versions informs the project's
decisions about patching and long-term support. Scarf purges personally identifiable information
(PII) and provides only aggregated statistics.
@@ -135,7 +135,7 @@ init:
```
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
To opt-out of this data collection in your Helm-based installation, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
:::
+9
View File
@@ -570,6 +570,15 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
</details>
<details>
<summary><strong>AsyncEventsRestApi</strong> (1 endpoints) — Real-time event streaming via Server-Sent Events (SSE).</summary>
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | [Read off of the Redis events stream](/developer-docs/api/read-off-of-the-redis-events-stream) | `/api/v1/async_event/` |
</details>
<details>
<summary><strong>OpenApi</strong> (1 endpoints) — Access the OpenAPI specification.</summary>
@@ -114,8 +114,8 @@ function MyExtension() {
## Source Links
- [Story file](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/components/Alert/Alert.stories.tsx)
- [Component source](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/components/Alert/index.tsx)
- [Story file](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/ui/components/Alert/Alert.stories.tsx)
- [Component source](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/ui/components/Alert/index.tsx)
---
@@ -47,8 +47,8 @@ export function MyExtensionPanel() {
Components in `@apache-superset/core/components` are automatically documented here. To add a new extension component:
1. Add the component to `superset-frontend/packages/superset-core/src/components/`
2. Export it from `superset-frontend/packages/superset-core/src/components/index.ts`
1. Add the component to `superset-frontend/packages/superset-core/src/ui/components/`
2. Export it from `superset-frontend/packages/superset-core/src/ui/components/index.ts`
3. Create a Storybook story with an `Interactive` export:
```tsx
+6 -188
View File
@@ -107,7 +107,7 @@ PENDING ──→ IN_PROGRESS ────→ SUCCESS
| `IN_PROGRESS` | Executing |
| `ABORTING` | Abort/timeout triggered, abort handlers running |
| `SUCCESS` | Completed successfully |
| `FAILURE` | Failed with error, abort/cleanup handler exception, orphan reaping, or worker self-fence |
| `FAILURE` | Failed with error or abort/cleanup handler exception |
| `ABORTED` | Cancelled by user/admin |
| `TIMED_OUT` | Exceeded configured timeout |
@@ -152,57 +152,10 @@ Use the tuple format `(current, total)` whenever possible. It provides the riche
#### Payload
The `payload` parameter stores custom metadata that can help users understand what the task is doing. Each call to `update_task()` merges into the existing payload (top-level keys are added or overwritten; keys you don't pass are preserved), so a task can build up its payload incrementally across calls.
The `payload` parameter stores custom metadata that can help users understand what the task is doing. Each call to `update_task()` replaces the previous payload completely.
In the Task List UI, when a payload is defined, an info icon appears in the **Details** column. Users can hover over it to see the JSON content.
#### Forcing an Immediate Write
By default `update_task()` throttles database writes (batching frequent updates to limit metastore load, at most one write per `TASK_PROGRESS_UPDATE_THROTTLE_INTERVAL` seconds, default 2). Pass `immediate=True` to bypass throttling and write synchronously:
```python
ctx.update_task(payload={"result_cache_key": key}, immediate=True)
```
Use this only when another consumer must observe the update as soon as the task finishes — for example, a dependent task that reads a prerequisite's payload the moment the dependency gate releases. For ordinary progress reporting, prefer the default throttled behavior.
#### Task state: public properties, private state, and results
A task's state lives in three tiers:
1. **Public `properties`** — named runtime state and execution config
(`is_abortable`, `progress_*`, `dedupe_count`, `execution_mode`, `timeout`,
`error_message`). Returned by the Task REST API and shown in the Task List UI.
2. **Private properties** — internal state that is surfaced to API consumers
**only in debug mode** (otherwise the whole `private` key is stripped). It has
two structurally isolated namespaces so a task type's freeform key can never
collide with a framework key:
- `private.framework` — framework-owned named keys common to every task: the
Celery job id the orphan reaper revokes (`celery_task_id`) plus error debug
(`exception_type`, `stack_trace`). Written only by the framework via
`task.update_framework_private({...})`.
- `private.task` — freeform, task-type-specific internal handles (e.g. the
chart-data query task's engine cancel handle,
`cancel_query_id`/`cancel_database_id`). Written by task/execution code via
`task.update_task_private({...})`.
- `private.subscription` — a
[subscription policy](#per-client-subscriptions-subscription-policies)'s
per-client bookkeeping (e.g. chart-data's per-tab consumer list). Written
only from the policy hooks via `TaskDAO.merge_subscription_state(task, {...})`;
the executor never writes it, and its whole-blob property writes carry the
row's current value through instead of overwriting it.
All namespaces merge independently (a write to one never clobbers another).
3. **Results (`payload`)** — end-user-facing task output (intermediate/final):
e.g. a `cache_key` or an engine tracking URL. Set via
`ctx.update_task(payload=...)` and rendered in the Task List info bubble. In
debug mode the bubble shows the `private` state in a separate section below.
Rule of thumb: user-facing status → top-level `properties`; user-facing output →
`payload`; framework plumbing → `private.framework`; task-specific internal
handles → `private.task`; subscription-policy bookkeeping →
`private.subscription`.
### Handlers
Register handlers to run cleanup logic or respond to abort requests:
@@ -291,64 +244,6 @@ The framework automatically skips execution if a task was aborted while pending:
Always implement an abort handler for long-running tasks. This allows users to cancel unneeded tasks and free up worker capacity for other operations.
:::
### Per-client subscriptions (subscription policies)
The framework subscribes tasks at **principal grain**: one subscriber row per
authenticated user (or embedded guest). The abort-vs-unsubscribe decision above
counts principals. For most task types that is exactly right.
Some task types need a finer grain than the principal. The canonical case is
async chart-data: a single `SHARED` task is deduplicated across every request
for the same query, so one user viewing the same chart in **two browser tabs** is
a single principal with a single subscriber row. If either tab's cancel (an
explicit cancel, or the navigate-away teardown) were treated as *the* principal
leaving, it would abort the shared task and kill the other tab's still-pending
query.
A **subscription policy** lets a task type refine this without the framework
knowing anything about tabs (or any other per-client grain). Register one on the
`@task` decorator:
```python
from superset_core.tasks.subscription import TaskSubscriptionPolicy
class MyConsumerPolicy(TaskSubscriptionPolicy):
def on_subscribe(self, task, *, principal, client_ref):
# Record this client (e.g. append f"{principal}:{client_ref}" to a list
# via TaskDAO.merge_subscription_state(task, {...})). Called after the
# framework has ensured the principal's subscriber row.
...
def on_unsubscribe(self, task, *, principal, client_ref) -> bool:
# Drop this client. Return True if the principal now has no client left
# (the framework then proceeds with its normal principal-grain rule:
# unsubscribe the principal, and abort if it was the last subscriber);
# return False to keep the principal subscribed because another of its
# clients is still watching.
...
@task(name="my_task", scope=TaskScope.SHARED, subscription_policy=MyConsumerPolicy())
def my_task() -> None:
...
```
Both hooks run in the web request process, inside the lock that serializes
concurrent submit/cancel for the task, so an implementation can safely
read-modify-write its bookkeeping without extra locking against other
submits/cancels. Keep that bookkeeping under `private.subscription` and write it
with `TaskDAO.merge_subscription_state(task, {...})`: the executor does not hold
the submit/cancel lock and keeps writing the task's properties while it runs, so
the helper merges under a row lock and the executor's own writes preserve that
namespace, where a plain `task.update_task_private({...})` would be overwritten
by the executor's next write and silently drop a client that joined
mid-execution. `client_ref` is the caller's
opaque per-client id (for chart-data, the browser tab id sent as `tab_id` on the
request); it is **not** an authorization token — the framework authorizes the
calling principal before the policy runs, and the policy only ever records or
removes entries scoped to that principal. A task type with no policy, or a
request with no `client_ref`, keeps plain principal-grain behavior. An admin
**Force abort** always aborts, bypassing the policy.
## Timeouts
Set a timeout to automatically abort tasks that run too long:
@@ -438,48 +333,6 @@ assert task.uuid == task2.uuid # True
print(task2.status) # "success" (terminal status)
```
## Task Dependencies
Tasks can declare prerequisite tasks, forming a directed acyclic graph (DAG). Pass the prerequisite `Task` objects (returned by `.schedule()`) via `depends_on`:
```python
from superset_core.tasks.types import TaskOptions
totals = totals_task.schedule(options=TaskOptions(task_key="totals_123"))
# `dependent` only runs once `totals` has finished successfully.
dependent = dependent_task.schedule(
options=TaskOptions(depends_on=[totals])
)
```
Passing the `Task` object is the canonical pattern. For convenience, a prerequisite's `UUID` (or UUID string) is also accepted where you don't hold the `Task` itself.
**Semantics (`all_success`).** A task runs only once **every** direct prerequisite has reached a terminal `SUCCESS`. If **any** prerequisite ends in a non-`SUCCESS` terminal state (`FAILURE`, `ABORTED`, or `TIMED_OUT`), the dependent does **not** run and is transitioned to `FAILURE`. This propagates transitively: because a failed dependent is itself non-`SUCCESS`, its own dependents fail in turn, so a failure anywhere short-circuits everything downstream.
**Scheduling model (non-blocking defer).** All tasks in a DAG are enqueued immediately. When a dependent is dequeued before its prerequisites are terminal, it does **not** hold its worker slot: it is re-enqueued via a Celery retry with a short, growing backoff (roughly 1s, 3s, 5s… capped) and the worker moves on to other work. While waiting, the task remains `PENDING` (shown as "waiting on N prerequisites" in the Task List). Each defer emits the `gtf.task.dag_deferred` metric.
:::note
A deferred dependent carries no heartbeat and no Celery job id until it is actually claimed (its prerequisites met), so the orphan reaper never mistakes a waiting task for abandoned work.
:::
Cycles (including self-dependencies) are rejected at schedule time. Dependency edges are removed automatically when either endpoint task is pruned.
**Reading a prerequisite's output.** A dependent reads the payloads its prerequisites published via `ctx.get_dependency_payloads()`, which returns the prerequisites' payloads in dependency-edge order. Pair it with the prerequisite writing its result with `ctx.update_task(payload=..., immediate=True)` so the value is flushed (not held in the write-throttle buffer) by the time the dependency gate releases the dependent:
```python
@task
def totals_task() -> None:
ctx = get_context()
# immediate=True so the dependent observes this the moment the gate releases.
ctx.update_task(payload={"result_cache_key": key}, immediate=True)
@task
def dependent_task() -> None:
ctx = get_context()
upstream = ctx.get_dependency_payloads() # [{"result_cache_key": ...}, ...]
```
## Task Scopes
```python
@@ -502,10 +355,6 @@ def system_task(): ...
| `SHARED` | All subscribers | Last subscriber cancels; others unsubscribe |
| `SYSTEM` | Admins only | Admin cancels |
For `SHARED` tasks, "last subscriber" is at principal grain by default; a task
type can refine cancel to a finer per-client (e.g. per browser tab) grain with a
[subscription policy](#per-client-subscriptions-subscription-policies).
## Task Cleanup
Completed tasks accumulate in the database over time. Configure a scheduled prune job to automatically remove old tasks:
@@ -526,32 +375,8 @@ The prune job only removes tasks in terminal states (`SUCCESS`, `FAILURE`, `ABOR
See `superset/config.py` for a complete example configuration.
### Orphan Reaping
A task whose worker dies mid-execution (OOM kill, crash, lost broker message) would otherwise stay `IN_PROGRESS` forever. To prevent this, a worker writes a liveness heartbeat while it holds a task, and a dedicated `reap_orphaned_tasks` beat job reaps orphans:
- **Heartbeat** — every `GTF_TASK_HEARTBEAT_INTERVAL` seconds (default 15) the executing worker refreshes `tasks.last_heartbeat`. This write is deliberately out-of-band and does not update `changed_on`.
- **Reaping**`reap_orphaned_tasks` marks any active task whose heartbeat is older than `GTF_ORPHAN_TASK_TIMEOUT` (default 60) as `FAILURE` so waiters and dependents unblock, revokes its Celery job so a redelivered copy (with `task_acks_late`) will not run, and — on engines that support query cancellation, when the dead worker had captured a cancel handle — cancels the abandoned warehouse query out-of-band. A task still being worked on keeps a fresh heartbeat and is never reaped, so this never interferes with a live worker's cooperative abort/cleanup.
- **Self-fencing** — the reaper handles a *dead* worker, but a worker that is alive yet cut off from the metastore (network partition, metastore outage) would keep running a query the reaper has already marked `FAILURE`. To avoid that wasted work, if a worker's heartbeat writes keep failing for longer than `GTF_ORPHAN_TASK_TIMEOUT` — the same window the reaper uses — the worker fails the task from the inside, cancelling any in-flight query. A single failed write is tolerated; only a sustained outage spanning the orphan window fences, so a transient blip never kills a healthy task. There is no handover to another worker: the task simply fails.
Enable the `reap_orphaned_tasks` beat schedule on a short interval (e.g. every minute) so orphaned tasks — and their warehouse queries — do not linger; it is separate from `prune_tasks` (a heavier retention delete that runs infrequently). Keep `GTF_ORPHAN_TASK_TIMEOUT` comfortably larger than the heartbeat interval (≥ ~3×) so a brief pause or CPU-bound stretch is not mistaken for a dead worker.
```python
# In your superset_config.py, add to your Celery beat schedule:
CELERY_CONFIG.beat_schedule["reap_orphaned_tasks"] = {
"task": "reap_orphaned_tasks",
"schedule": crontab(minute="*", hour="*"), # Run every minute
}
```
Unlike `prune_tasks`, the reaper takes no kwargs — it reads `GTF_ORPHAN_TASK_TIMEOUT` from config.
:::note Cancelling the underlying query
For long-running work backed by an external query, register an `on_abort` handler that cancels it (this is how async chart-data query tasks cancel the warehouse query on engines that support cancellation). Without such a handler an abort/timeout frees the task but cannot stop the external work.
:::
:::tip Distributed Coordination for Faster Notifications
By default, abort detection and sync join-and-wait poll the task row in the metadata database (every `TASK_ABORT_POLLING_DEFAULT_INTERVAL` seconds, default 10). Configure `DISTRIBUTED_COORDINATION_CONFIG` (Redis/Valkey) and these become event-driven: completion and abort are signalled over Redis **Streams**, so a waiter wakes when the signal lands instead of polling the database. Because stream entries are persisted, a waiter that reads slightly late, reconnects, or fails over still receives the signal. Each signal stream keeps only its latest entry and is given a TTL, so streams for tasks that are never awaited do not accumulate; set the retention window with `DISTRIBUTED_COORDINATION_SIGNAL_TTL` (default 24h). See [Distributed Coordination Backend](/admin-docs/configuration/cache#signal-cache-backend) for configuration details.
By default, abort detection and sync join-and-wait use database polling. Configure `DISTRIBUTED_COORDINATION_CONFIG` to enable Redis pub/sub for real-time notifications. See [Distributed Coordination Backend](/admin-docs/configuration/cache#signal-cache-backend) for configuration details.
:::
## API Reference
@@ -562,24 +387,19 @@ By default, abort detection and sync join-and-wait poll the task row in the meta
@task(
name: str | None = None,
scope: TaskScope = TaskScope.PRIVATE,
timeout: int | None = None,
subscription_policy: TaskSubscriptionPolicy | None = None,
timeout: int | None = None
)
```
- `name`: Task identifier (defaults to function name)
- `scope`: `PRIVATE`, `SHARED`, or `SYSTEM`
- `timeout`: Default timeout in seconds (can be overridden via `TaskOptions`)
- `subscription_policy`: Optional per-client subscription policy that refines the
principal-grain cancel decision (see
[Per-client subscriptions](#per-client-subscriptions-subscription-policies))
### TaskContext Methods
| Method | Description |
| -------------------------------- | --------------------------------------------- |
| `update_task(progress, payload, immediate=False)` | Update progress and/or custom payload (`immediate=True` bypasses write throttling) |
| `get_dependency_payloads()` | Return prerequisite tasks' payloads, in dependency-edge order |
| `update_task(progress, payload)` | Update progress and/or custom payload |
| `on_cleanup(handler)` | Register cleanup handler |
| `on_abort(handler)` | Register abort handler (makes task abortable) |
@@ -589,15 +409,13 @@ By default, abort detection and sync join-and-wait poll the task row in the meta
TaskOptions(
task_key: str | None = None,
task_name: str | None = None,
timeout: int | None = None,
depends_on: list[Task | UUID | str] | None = None
timeout: int | None = None
)
```
- `task_key`: Deduplication key (also used as display name if `task_name` is not set)
- `task_name`: Human-readable display name for the Task List UI
- `timeout`: Timeout in seconds (overrides decorator default)
- `depends_on`: Prerequisite tasks to wait for before running. Pass the scheduled `Task` objects (canonical); a `UUID` or UUID string is also accepted (see [Task Dependencies](#task-dependencies))
:::tip
Provide a descriptive `task_name` for better readability in the Task List UI. While `task_key` is used for deduplication and may be technical (e.g., `chart_export_123`), `task_name` can be user-friendly (e.g., `"Export Sales Chart 123"`).
@@ -49,10 +49,10 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------- | ------------------------------- |
| `GET` | [Get the CSRF token](/developer-docs/6.1.0/api/get-the-csrf-token) | `/api/v1/security/csrf_token/` |
| `POST` | [Get a guest token](/developer-docs/6.1.0/api/get-a-guest-token) | `/api/v1/security/guest_token/` |
| `POST` | [Create security login](/developer-docs/6.1.0/api/create-security-login) | `/api/v1/security/login` |
| `POST` | [Create security refresh](/developer-docs/6.1.0/api/create-security-refresh) | `/api/v1/security/refresh` |
| `GET` | [Get the CSRF token](/developer-docs/api/get-the-csrf-token) | `/api/v1/security/csrf_token/` |
| `POST` | [Get a guest token](/developer-docs/api/get-a-guest-token) | `/api/v1/security/guest_token/` |
| `POST` | [Create security login](/developer-docs/api/create-security-login) | `/api/v1/security/login` |
| `POST` | [Create security refresh](/developer-docs/api/create-security-refresh) | `/api/v1/security/refresh` |
---
@@ -65,34 +65,34 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `DELETE` | [Bulk delete dashboards](/developer-docs/6.1.0/api/bulk-delete-dashboards) | `/api/v1/dashboard/` |
| `GET` | [Get a list of dashboards](/developer-docs/6.1.0/api/get-a-list-of-dashboards) | `/api/v1/dashboard/` |
| `POST` | [Create a new dashboard](/developer-docs/6.1.0/api/create-a-new-dashboard) | `/api/v1/dashboard/` |
| `GET` | [Get metadata information about this API resource (dashboard--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-dashboard-info) | `/api/v1/dashboard/_info` |
| `GET` | [Get a dashboard detail information](/developer-docs/6.1.0/api/get-a-dashboard-detail-information) | `/api/v1/dashboard/{id_or_slug}` |
| `GET` | [Get a dashboard's chart definitions.](/developer-docs/6.1.0/api/get-a-dashboards-chart-definitions) | `/api/v1/dashboard/{id_or_slug}/charts` |
| `POST` | [Create a copy of an existing dashboard](/developer-docs/6.1.0/api/create-a-copy-of-an-existing-dashboard) | `/api/v1/dashboard/{id_or_slug}/copy/` |
| `GET` | [Get dashboard's datasets](/developer-docs/6.1.0/api/get-dashboards-datasets) | `/api/v1/dashboard/{id_or_slug}/datasets` |
| `DELETE` | [Delete a dashboard's embedded configuration](/developer-docs/6.1.0/api/delete-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `GET` | [Get the dashboard's embedded configuration](/developer-docs/6.1.0/api/get-the-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `POST` | [Set a dashboard's embedded configuration](/developer-docs/6.1.0/api/set-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `PUT` | [Update dashboard by id_or_slug embedded](/developer-docs/6.1.0/api/update-dashboard-by-id-or-slug-embedded) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `GET` | [Get dashboard's tabs](/developer-docs/6.1.0/api/get-dashboards-tabs) | `/api/v1/dashboard/{id_or_slug}/tabs` |
| `DELETE` | [Delete a dashboard](/developer-docs/6.1.0/api/delete-a-dashboard) | `/api/v1/dashboard/{pk}` |
| `PUT` | [Update a dashboard](/developer-docs/6.1.0/api/update-a-dashboard) | `/api/v1/dashboard/{pk}` |
| `POST` | [Compute and cache a screenshot (dashboard-pk-cache-dashboard-screenshot)](/developer-docs/6.1.0/api/compute-and-cache-a-screenshot-dashboard-pk-cache-dashboard-screenshot) | `/api/v1/dashboard/{pk}/cache_dashboard_screenshot/` |
| `PUT` | [Update chart customizations configuration for a dashboard.](/developer-docs/6.1.0/api/update-chart-customizations-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/chart_customizations` |
| `PUT` | [Update colors configuration for a dashboard.](/developer-docs/6.1.0/api/update-colors-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/colors` |
| `GET` | [Export dashboard as example bundle](/developer-docs/6.1.0/api/export-dashboard-as-example-bundle) | `/api/v1/dashboard/{pk}/export_as_example/` |
| `DELETE` | [Remove the dashboard from the user favorite list](/developer-docs/6.1.0/api/remove-the-dashboard-from-the-user-favorite-list) | `/api/v1/dashboard/{pk}/favorites/` |
| `POST` | [Mark the dashboard as favorite for the current user](/developer-docs/6.1.0/api/mark-the-dashboard-as-favorite-for-the-current-user) | `/api/v1/dashboard/{pk}/favorites/` |
| `PUT` | [Update native filters configuration for a dashboard.](/developer-docs/6.1.0/api/update-native-filters-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/filters` |
| `GET` | [Get a computed screenshot from cache (dashboard-pk-screenshot-digest)](/developer-docs/6.1.0/api/get-a-computed-screenshot-from-cache-dashboard-pk-screenshot-digest) | `/api/v1/dashboard/{pk}/screenshot/{digest}/` |
| `GET` | [Get dashboard's thumbnail](/developer-docs/6.1.0/api/get-dashboards-thumbnail) | `/api/v1/dashboard/{pk}/thumbnail/{digest}/` |
| `GET` | [Download multiple dashboards as YAML files](/developer-docs/6.1.0/api/download-multiple-dashboards-as-yaml-files) | `/api/v1/dashboard/export/` |
| `GET` | [Check favorited dashboards for current user](/developer-docs/6.1.0/api/check-favorited-dashboards-for-current-user) | `/api/v1/dashboard/favorite_status/` |
| `POST` | [Import dashboard(s) with associated charts/datasets/databases](/developer-docs/6.1.0/api/import-dashboard-s-with-associated-charts-datasets-databases) | `/api/v1/dashboard/import/` |
| `GET` | [Get related fields data (dashboard-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-dashboard-related-column-name) | `/api/v1/dashboard/related/{column_name}` |
| `DELETE` | [Bulk delete dashboards](/developer-docs/api/bulk-delete-dashboards) | `/api/v1/dashboard/` |
| `GET` | [Get a list of dashboards](/developer-docs/api/get-a-list-of-dashboards) | `/api/v1/dashboard/` |
| `POST` | [Create a new dashboard](/developer-docs/api/create-a-new-dashboard) | `/api/v1/dashboard/` |
| `GET` | [Get metadata information about this API resource (dashboard--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-dashboard-info) | `/api/v1/dashboard/_info` |
| `GET` | [Get a dashboard detail information](/developer-docs/api/get-a-dashboard-detail-information) | `/api/v1/dashboard/{id_or_slug}` |
| `GET` | [Get a dashboard's chart definitions.](/developer-docs/api/get-a-dashboards-chart-definitions) | `/api/v1/dashboard/{id_or_slug}/charts` |
| `POST` | [Create a copy of an existing dashboard](/developer-docs/api/create-a-copy-of-an-existing-dashboard) | `/api/v1/dashboard/{id_or_slug}/copy/` |
| `GET` | [Get dashboard's datasets](/developer-docs/api/get-dashboards-datasets) | `/api/v1/dashboard/{id_or_slug}/datasets` |
| `DELETE` | [Delete a dashboard's embedded configuration](/developer-docs/api/delete-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `GET` | [Get the dashboard's embedded configuration](/developer-docs/api/get-the-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `POST` | [Set a dashboard's embedded configuration](/developer-docs/api/set-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `PUT` | [Update dashboard by id_or_slug embedded](/developer-docs/api/update-dashboard-by-id-or-slug-embedded) | `/api/v1/dashboard/{id_or_slug}/embedded` |
| `GET` | [Get dashboard's tabs](/developer-docs/api/get-dashboards-tabs) | `/api/v1/dashboard/{id_or_slug}/tabs` |
| `DELETE` | [Delete a dashboard](/developer-docs/api/delete-a-dashboard) | `/api/v1/dashboard/{pk}` |
| `PUT` | [Update a dashboard](/developer-docs/api/update-a-dashboard) | `/api/v1/dashboard/{pk}` |
| `POST` | [Compute and cache a screenshot (dashboard-pk-cache-dashboard-screenshot)](/developer-docs/api/compute-and-cache-a-screenshot-dashboard-pk-cache-dashboard-screenshot) | `/api/v1/dashboard/{pk}/cache_dashboard_screenshot/` |
| `PUT` | [Update chart customizations configuration for a dashboard.](/developer-docs/api/update-chart-customizations-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/chart_customizations` |
| `PUT` | [Update colors configuration for a dashboard.](/developer-docs/api/update-colors-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/colors` |
| `GET` | [Export dashboard as example bundle](/developer-docs/api/export-dashboard-as-example-bundle) | `/api/v1/dashboard/{pk}/export_as_example/` |
| `DELETE` | [Remove the dashboard from the user favorite list](/developer-docs/api/remove-the-dashboard-from-the-user-favorite-list) | `/api/v1/dashboard/{pk}/favorites/` |
| `POST` | [Mark the dashboard as favorite for the current user](/developer-docs/api/mark-the-dashboard-as-favorite-for-the-current-user) | `/api/v1/dashboard/{pk}/favorites/` |
| `PUT` | [Update native filters configuration for a dashboard.](/developer-docs/api/update-native-filters-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/filters` |
| `GET` | [Get a computed screenshot from cache (dashboard-pk-screenshot-digest)](/developer-docs/api/get-a-computed-screenshot-from-cache-dashboard-pk-screenshot-digest) | `/api/v1/dashboard/{pk}/screenshot/{digest}/` |
| `GET` | [Get dashboard's thumbnail](/developer-docs/api/get-dashboards-thumbnail) | `/api/v1/dashboard/{pk}/thumbnail/{digest}/` |
| `GET` | [Download multiple dashboards as YAML files](/developer-docs/api/download-multiple-dashboards-as-yaml-files) | `/api/v1/dashboard/export/` |
| `GET` | [Check favorited dashboards for current user](/developer-docs/api/check-favorited-dashboards-for-current-user) | `/api/v1/dashboard/favorite_status/` |
| `POST` | [Import dashboard(s) with associated charts/datasets/databases](/developer-docs/api/import-dashboard-s-with-associated-charts-datasets-databases) | `/api/v1/dashboard/import/` |
| `GET` | [Get related fields data (dashboard-related-column-name)](/developer-docs/api/get-related-fields-data-dashboard-related-column-name) | `/api/v1/dashboard/related/{column_name}` |
</details>
@@ -101,26 +101,26 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `DELETE` | [Bulk delete charts](/developer-docs/6.1.0/api/bulk-delete-charts) | `/api/v1/chart/` |
| `GET` | [Get a list of charts](/developer-docs/6.1.0/api/get-a-list-of-charts) | `/api/v1/chart/` |
| `POST` | [Create a new chart](/developer-docs/6.1.0/api/create-a-new-chart) | `/api/v1/chart/` |
| `GET` | [Get metadata information about this API resource (chart--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-chart-info) | `/api/v1/chart/_info` |
| `GET` | [Get a chart detail information](/developer-docs/6.1.0/api/get-a-chart-detail-information) | `/api/v1/chart/{id_or_uuid}` |
| `DELETE` | [Delete a chart](/developer-docs/6.1.0/api/delete-a-chart) | `/api/v1/chart/{pk}` |
| `PUT` | [Update a chart](/developer-docs/6.1.0/api/update-a-chart) | `/api/v1/chart/{pk}` |
| `GET` | [Compute and cache a screenshot (chart-pk-cache-screenshot)](/developer-docs/6.1.0/api/compute-and-cache-a-screenshot-chart-pk-cache-screenshot) | `/api/v1/chart/{pk}/cache_screenshot/` |
| `GET` | [Return payload data response for a chart](/developer-docs/6.1.0/api/return-payload-data-response-for-a-chart) | `/api/v1/chart/{pk}/data/` |
| `DELETE` | [Remove the chart from the user favorite list](/developer-docs/6.1.0/api/remove-the-chart-from-the-user-favorite-list) | `/api/v1/chart/{pk}/favorites/` |
| `POST` | [Mark the chart as favorite for the current user](/developer-docs/6.1.0/api/mark-the-chart-as-favorite-for-the-current-user) | `/api/v1/chart/{pk}/favorites/` |
| `GET` | [Get a computed screenshot from cache (chart-pk-screenshot-digest)](/developer-docs/6.1.0/api/get-a-computed-screenshot-from-cache-chart-pk-screenshot-digest) | `/api/v1/chart/{pk}/screenshot/{digest}/` |
| `GET` | [Get chart thumbnail](/developer-docs/6.1.0/api/get-chart-thumbnail) | `/api/v1/chart/{pk}/thumbnail/{digest}/` |
| `POST` | [Return payload data response for the given query (chart-data)](/developer-docs/6.1.0/api/return-payload-data-response-for-the-given-query-chart-data) | `/api/v1/chart/data` |
| `GET` | [Return payload data response for the given query (chart-data-cache-key)](/developer-docs/6.1.0/api/return-payload-data-response-for-the-given-query-chart-data-cache-key) | `/api/v1/chart/data/{cache_key}` |
| `GET` | [Download multiple charts as YAML files](/developer-docs/6.1.0/api/download-multiple-charts-as-yaml-files) | `/api/v1/chart/export/` |
| `GET` | [Check favorited charts for current user](/developer-docs/6.1.0/api/check-favorited-charts-for-current-user) | `/api/v1/chart/favorite_status/` |
| `POST` | [Import chart(s) with associated datasets and databases](/developer-docs/6.1.0/api/import-chart-s-with-associated-datasets-and-databases) | `/api/v1/chart/import/` |
| `GET` | [Get related fields data (chart-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-chart-related-column-name) | `/api/v1/chart/related/{column_name}` |
| `PUT` | [Warm up the cache for the chart](/developer-docs/6.1.0/api/warm-up-the-cache-for-the-chart) | `/api/v1/chart/warm_up_cache` |
| `DELETE` | [Bulk delete charts](/developer-docs/api/bulk-delete-charts) | `/api/v1/chart/` |
| `GET` | [Get a list of charts](/developer-docs/api/get-a-list-of-charts) | `/api/v1/chart/` |
| `POST` | [Create a new chart](/developer-docs/api/create-a-new-chart) | `/api/v1/chart/` |
| `GET` | [Get metadata information about this API resource (chart--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-chart-info) | `/api/v1/chart/_info` |
| `GET` | [Get a chart detail information](/developer-docs/api/get-a-chart-detail-information) | `/api/v1/chart/{id_or_uuid}` |
| `DELETE` | [Delete a chart](/developer-docs/api/delete-a-chart) | `/api/v1/chart/{pk}` |
| `PUT` | [Update a chart](/developer-docs/api/update-a-chart) | `/api/v1/chart/{pk}` |
| `GET` | [Compute and cache a screenshot (chart-pk-cache-screenshot)](/developer-docs/api/compute-and-cache-a-screenshot-chart-pk-cache-screenshot) | `/api/v1/chart/{pk}/cache_screenshot/` |
| `GET` | [Return payload data response for a chart](/developer-docs/api/return-payload-data-response-for-a-chart) | `/api/v1/chart/{pk}/data/` |
| `DELETE` | [Remove the chart from the user favorite list](/developer-docs/api/remove-the-chart-from-the-user-favorite-list) | `/api/v1/chart/{pk}/favorites/` |
| `POST` | [Mark the chart as favorite for the current user](/developer-docs/api/mark-the-chart-as-favorite-for-the-current-user) | `/api/v1/chart/{pk}/favorites/` |
| `GET` | [Get a computed screenshot from cache (chart-pk-screenshot-digest)](/developer-docs/api/get-a-computed-screenshot-from-cache-chart-pk-screenshot-digest) | `/api/v1/chart/{pk}/screenshot/{digest}/` |
| `GET` | [Get chart thumbnail](/developer-docs/api/get-chart-thumbnail) | `/api/v1/chart/{pk}/thumbnail/{digest}/` |
| `POST` | [Return payload data response for the given query (chart-data)](/developer-docs/api/return-payload-data-response-for-the-given-query-chart-data) | `/api/v1/chart/data` |
| `GET` | [Return payload data response for the given query (chart-data-cache-key)](/developer-docs/api/return-payload-data-response-for-the-given-query-chart-data-cache-key) | `/api/v1/chart/data/{cache_key}` |
| `GET` | [Download multiple charts as YAML files](/developer-docs/api/download-multiple-charts-as-yaml-files) | `/api/v1/chart/export/` |
| `GET` | [Check favorited charts for current user](/developer-docs/api/check-favorited-charts-for-current-user) | `/api/v1/chart/favorite_status/` |
| `POST` | [Import chart(s) with associated datasets and databases](/developer-docs/api/import-chart-s-with-associated-datasets-and-databases) | `/api/v1/chart/import/` |
| `GET` | [Get related fields data (chart-related-column-name)](/developer-docs/api/get-related-fields-data-chart-related-column-name) | `/api/v1/chart/related/{column_name}` |
| `PUT` | [Warm up the cache for the chart](/developer-docs/api/warm-up-the-cache-for-the-chart) | `/api/v1/chart/warm_up_cache` |
</details>
@@ -129,25 +129,25 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `DELETE` | [Bulk delete datasets](/developer-docs/6.1.0/api/bulk-delete-datasets) | `/api/v1/dataset/` |
| `GET` | [Get a list of datasets](/developer-docs/6.1.0/api/get-a-list-of-datasets) | `/api/v1/dataset/` |
| `POST` | [Create a new dataset](/developer-docs/6.1.0/api/create-a-new-dataset) | `/api/v1/dataset/` |
| `GET` | [Get metadata information about this API resource (dataset--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-dataset-info) | `/api/v1/dataset/_info` |
| `GET` | [Get a dataset](/developer-docs/6.1.0/api/get-a-dataset) | `/api/v1/dataset/{id_or_uuid}` |
| `GET` | [Get charts and dashboards count associated to a dataset](/developer-docs/6.1.0/api/get-charts-and-dashboards-count-associated-to-a-dataset) | `/api/v1/dataset/{id_or_uuid}/related_objects` |
| `DELETE` | [Delete a dataset](/developer-docs/6.1.0/api/delete-a-dataset) | `/api/v1/dataset/{pk}` |
| `PUT` | [Update a dataset](/developer-docs/6.1.0/api/update-a-dataset) | `/api/v1/dataset/{pk}` |
| `DELETE` | [Delete a dataset column](/developer-docs/6.1.0/api/delete-a-dataset-column) | `/api/v1/dataset/{pk}/column/{column_id}` |
| `GET` | [Get dataset drill info](/developer-docs/6.1.0/api/get-dataset-drill-info) | `/api/v1/dataset/{pk}/drill_info/` |
| `DELETE` | [Delete a dataset metric](/developer-docs/6.1.0/api/delete-a-dataset-metric) | `/api/v1/dataset/{pk}/metric/{metric_id}` |
| `PUT` | [Refresh and update columns of a dataset](/developer-docs/6.1.0/api/refresh-and-update-columns-of-a-dataset) | `/api/v1/dataset/{pk}/refresh` |
| `GET` | [Get distinct values from field data (dataset-distinct-column-name)](/developer-docs/6.1.0/api/get-distinct-values-from-field-data-dataset-distinct-column-name) | `/api/v1/dataset/distinct/{column_name}` |
| `POST` | [Duplicate a dataset](/developer-docs/6.1.0/api/duplicate-a-dataset) | `/api/v1/dataset/duplicate` |
| `GET` | [Download multiple datasets as YAML files](/developer-docs/6.1.0/api/download-multiple-datasets-as-yaml-files) | `/api/v1/dataset/export/` |
| `POST` | [Retrieve a table by name, or create it if it does not exist](/developer-docs/6.1.0/api/retrieve-a-table-by-name-or-create-it-if-it-does-not-exist) | `/api/v1/dataset/get_or_create/` |
| `POST` | [Import dataset(s) with associated databases](/developer-docs/6.1.0/api/import-dataset-s-with-associated-databases) | `/api/v1/dataset/import/` |
| `GET` | [Get related fields data (dataset-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-dataset-related-column-name) | `/api/v1/dataset/related/{column_name}` |
| `PUT` | [Warm up the cache for each chart powered by the given table](/developer-docs/6.1.0/api/warm-up-the-cache-for-each-chart-powered-by-the-given-table) | `/api/v1/dataset/warm_up_cache` |
| `DELETE` | [Bulk delete datasets](/developer-docs/api/bulk-delete-datasets) | `/api/v1/dataset/` |
| `GET` | [Get a list of datasets](/developer-docs/api/get-a-list-of-datasets) | `/api/v1/dataset/` |
| `POST` | [Create a new dataset](/developer-docs/api/create-a-new-dataset) | `/api/v1/dataset/` |
| `GET` | [Get metadata information about this API resource (dataset--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-dataset-info) | `/api/v1/dataset/_info` |
| `GET` | [Get a dataset](/developer-docs/api/get-a-dataset) | `/api/v1/dataset/{id_or_uuid}` |
| `GET` | [Get charts and dashboards count associated to a dataset](/developer-docs/api/get-charts-and-dashboards-count-associated-to-a-dataset) | `/api/v1/dataset/{id_or_uuid}/related_objects` |
| `DELETE` | [Delete a dataset](/developer-docs/api/delete-a-dataset) | `/api/v1/dataset/{pk}` |
| `PUT` | [Update a dataset](/developer-docs/api/update-a-dataset) | `/api/v1/dataset/{pk}` |
| `DELETE` | [Delete a dataset column](/developer-docs/api/delete-a-dataset-column) | `/api/v1/dataset/{pk}/column/{column_id}` |
| `GET` | [Get dataset drill info](/developer-docs/api/get-dataset-drill-info) | `/api/v1/dataset/{pk}/drill_info/` |
| `DELETE` | [Delete a dataset metric](/developer-docs/api/delete-a-dataset-metric) | `/api/v1/dataset/{pk}/metric/{metric_id}` |
| `PUT` | [Refresh and update columns of a dataset](/developer-docs/api/refresh-and-update-columns-of-a-dataset) | `/api/v1/dataset/{pk}/refresh` |
| `GET` | [Get distinct values from field data (dataset-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-dataset-distinct-column-name) | `/api/v1/dataset/distinct/{column_name}` |
| `POST` | [Duplicate a dataset](/developer-docs/api/duplicate-a-dataset) | `/api/v1/dataset/duplicate` |
| `GET` | [Download multiple datasets as YAML files](/developer-docs/api/download-multiple-datasets-as-yaml-files) | `/api/v1/dataset/export/` |
| `POST` | [Retrieve a table by name, or create it if it does not exist](/developer-docs/api/retrieve-a-table-by-name-or-create-it-if-it-does-not-exist) | `/api/v1/dataset/get_or_create/` |
| `POST` | [Import dataset(s) with associated databases](/developer-docs/api/import-dataset-s-with-associated-databases) | `/api/v1/dataset/import/` |
| `GET` | [Get related fields data (dataset-related-column-name)](/developer-docs/api/get-related-fields-data-dataset-related-column-name) | `/api/v1/dataset/related/{column_name}` |
| `PUT` | [Warm up the cache for each chart powered by the given table](/developer-docs/api/warm-up-the-cache-for-each-chart-powered-by-the-given-table) | `/api/v1/dataset/warm_up_cache` |
</details>
@@ -156,36 +156,36 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `GET` | [Get a list of databases](/developer-docs/6.1.0/api/get-a-list-of-databases) | `/api/v1/database/` |
| `POST` | [Create a new database](/developer-docs/6.1.0/api/create-a-new-database) | `/api/v1/database/` |
| `GET` | [Get metadata information about this API resource (database--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-database-info) | `/api/v1/database/_info` |
| `DELETE` | [Delete a database](/developer-docs/6.1.0/api/delete-a-database) | `/api/v1/database/{pk}` |
| `GET` | [Get a database](/developer-docs/6.1.0/api/get-a-database) | `/api/v1/database/{pk}` |
| `PUT` | [Change a database](/developer-docs/6.1.0/api/change-a-database) | `/api/v1/database/{pk}` |
| `GET` | [Get all catalogs from a database](/developer-docs/6.1.0/api/get-all-catalogs-from-a-database) | `/api/v1/database/{pk}/catalogs/` |
| `GET` | [Get a database connection info](/developer-docs/6.1.0/api/get-a-database-connection-info) | `/api/v1/database/{pk}/connection` |
| `GET` | [Get function names supported by a database](/developer-docs/6.1.0/api/get-function-names-supported-by-a-database) | `/api/v1/database/{pk}/function_names/` |
| `GET` | [Get charts and dashboards count associated to a database](/developer-docs/6.1.0/api/get-charts-and-dashboards-count-associated-to-a-database) | `/api/v1/database/{pk}/related_objects/` |
| `GET` | [The list of the database schemas where to upload information](/developer-docs/6.1.0/api/the-list-of-the-database-schemas-where-to-upload-information) | `/api/v1/database/{pk}/schemas_access_for_file_upload/` |
| `GET` | [Get all schemas from a database](/developer-docs/6.1.0/api/get-all-schemas-from-a-database) | `/api/v1/database/{pk}/schemas/` |
| `GET` | [Get database select star for table (database-pk-select-star-table-name)](/developer-docs/6.1.0/api/get-database-select-star-for-table-database-pk-select-star-table-name) | `/api/v1/database/{pk}/select_star/{table_name}/` |
| `GET` | [Get database select star for table (database-pk-select-star-table-name-schema-name)](/developer-docs/6.1.0/api/get-database-select-star-for-table-database-pk-select-star-table-name-schema-name) | `/api/v1/database/{pk}/select_star/{table_name}/{schema_name}/` |
| `POST` | [Re-sync all permissions for a database connection](/developer-docs/6.1.0/api/re-sync-all-permissions-for-a-database-connection) | `/api/v1/database/{pk}/sync_permissions/` |
| `GET` | [Get table extra metadata (database-pk-table-extra-table-name-schema-name)](/developer-docs/6.1.0/api/get-table-extra-metadata-database-pk-table-extra-table-name-schema-name) | `/api/v1/database/{pk}/table_extra/{table_name}/{schema_name}/` |
| `GET` | [Get table metadata](/developer-docs/6.1.0/api/get-table-metadata) | `/api/v1/database/{pk}/table_metadata/` |
| `GET` | [Get table extra metadata (database-pk-table-metadata-extra)](/developer-docs/6.1.0/api/get-table-extra-metadata-database-pk-table-metadata-extra) | `/api/v1/database/{pk}/table_metadata/extra/` |
| `GET` | [Get database table metadata](/developer-docs/6.1.0/api/get-database-table-metadata) | `/api/v1/database/{pk}/table/{table_name}/{schema_name}/` |
| `GET` | [Get a list of tables for given database](/developer-docs/6.1.0/api/get-a-list-of-tables-for-given-database) | `/api/v1/database/{pk}/tables/` |
| `POST` | [Upload a file to a database table](/developer-docs/6.1.0/api/upload-a-file-to-a-database-table) | `/api/v1/database/{pk}/upload/` |
| `POST` | [Validate arbitrary SQL](/developer-docs/6.1.0/api/validate-arbitrary-sql) | `/api/v1/database/{pk}/validate_sql/` |
| `GET` | [Get names of databases currently available](/developer-docs/6.1.0/api/get-names-of-databases-currently-available) | `/api/v1/database/available/` |
| `GET` | [Download database(s) and associated dataset(s) as a zip file](/developer-docs/6.1.0/api/download-database-s-and-associated-dataset-s-as-a-zip-file) | `/api/v1/database/export/` |
| `POST` | [Import database(s) with associated datasets](/developer-docs/6.1.0/api/import-database-s-with-associated-datasets) | `/api/v1/database/import/` |
| `GET` | [Receive personal access tokens from OAuth2](/developer-docs/6.1.0/api/receive-personal-access-tokens-from-o-auth-2) | `/api/v1/database/oauth2/` |
| `GET` | [Get related fields data (database-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-database-related-column-name) | `/api/v1/database/related/{column_name}` |
| `POST` | [Test a database connection](/developer-docs/6.1.0/api/test-a-database-connection) | `/api/v1/database/test_connection/` |
| `POST` | [Upload a file and returns file metadata](/developer-docs/6.1.0/api/upload-a-file-and-returns-file-metadata) | `/api/v1/database/upload_metadata/` |
| `POST` | [Validate database connection parameters](/developer-docs/6.1.0/api/validate-database-connection-parameters) | `/api/v1/database/validate_parameters/` |
| `GET` | [Get a list of databases](/developer-docs/api/get-a-list-of-databases) | `/api/v1/database/` |
| `POST` | [Create a new database](/developer-docs/api/create-a-new-database) | `/api/v1/database/` |
| `GET` | [Get metadata information about this API resource (database--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-database-info) | `/api/v1/database/_info` |
| `DELETE` | [Delete a database](/developer-docs/api/delete-a-database) | `/api/v1/database/{pk}` |
| `GET` | [Get a database](/developer-docs/api/get-a-database) | `/api/v1/database/{pk}` |
| `PUT` | [Change a database](/developer-docs/api/change-a-database) | `/api/v1/database/{pk}` |
| `GET` | [Get all catalogs from a database](/developer-docs/api/get-all-catalogs-from-a-database) | `/api/v1/database/{pk}/catalogs/` |
| `GET` | [Get a database connection info](/developer-docs/api/get-a-database-connection-info) | `/api/v1/database/{pk}/connection` |
| `GET` | [Get function names supported by a database](/developer-docs/api/get-function-names-supported-by-a-database) | `/api/v1/database/{pk}/function_names/` |
| `GET` | [Get charts and dashboards count associated to a database](/developer-docs/api/get-charts-and-dashboards-count-associated-to-a-database) | `/api/v1/database/{pk}/related_objects/` |
| `GET` | [The list of the database schemas where to upload information](/developer-docs/api/the-list-of-the-database-schemas-where-to-upload-information) | `/api/v1/database/{pk}/schemas_access_for_file_upload/` |
| `GET` | [Get all schemas from a database](/developer-docs/api/get-all-schemas-from-a-database) | `/api/v1/database/{pk}/schemas/` |
| `GET` | [Get database select star for table (database-pk-select-star-table-name)](/developer-docs/api/get-database-select-star-for-table-database-pk-select-star-table-name) | `/api/v1/database/{pk}/select_star/{table_name}/` |
| `GET` | [Get database select star for table (database-pk-select-star-table-name-schema-name)](/developer-docs/api/get-database-select-star-for-table-database-pk-select-star-table-name-schema-name) | `/api/v1/database/{pk}/select_star/{table_name}/{schema_name}/` |
| `POST` | [Re-sync all permissions for a database connection](/developer-docs/api/re-sync-all-permissions-for-a-database-connection) | `/api/v1/database/{pk}/sync_permissions/` |
| `GET` | [Get table extra metadata (database-pk-table-extra-table-name-schema-name)](/developer-docs/api/get-table-extra-metadata-database-pk-table-extra-table-name-schema-name) | `/api/v1/database/{pk}/table_extra/{table_name}/{schema_name}/` |
| `GET` | [Get table metadata](/developer-docs/api/get-table-metadata) | `/api/v1/database/{pk}/table_metadata/` |
| `GET` | [Get table extra metadata (database-pk-table-metadata-extra)](/developer-docs/api/get-table-extra-metadata-database-pk-table-metadata-extra) | `/api/v1/database/{pk}/table_metadata/extra/` |
| `GET` | [Get database table metadata](/developer-docs/api/get-database-table-metadata) | `/api/v1/database/{pk}/table/{table_name}/{schema_name}/` |
| `GET` | [Get a list of tables for given database](/developer-docs/api/get-a-list-of-tables-for-given-database) | `/api/v1/database/{pk}/tables/` |
| `POST` | [Upload a file to a database table](/developer-docs/api/upload-a-file-to-a-database-table) | `/api/v1/database/{pk}/upload/` |
| `POST` | [Validate arbitrary SQL](/developer-docs/api/validate-arbitrary-sql) | `/api/v1/database/{pk}/validate_sql/` |
| `GET` | [Get names of databases currently available](/developer-docs/api/get-names-of-databases-currently-available) | `/api/v1/database/available/` |
| `GET` | [Download database(s) and associated dataset(s) as a zip file](/developer-docs/api/download-database-s-and-associated-dataset-s-as-a-zip-file) | `/api/v1/database/export/` |
| `POST` | [Import database(s) with associated datasets](/developer-docs/api/import-database-s-with-associated-datasets) | `/api/v1/database/import/` |
| `GET` | [Receive personal access tokens from OAuth2](/developer-docs/api/receive-personal-access-tokens-from-o-auth-2) | `/api/v1/database/oauth2/` |
| `GET` | [Get related fields data (database-related-column-name)](/developer-docs/api/get-related-fields-data-database-related-column-name) | `/api/v1/database/related/{column_name}` |
| `POST` | [Test a database connection](/developer-docs/api/test-a-database-connection) | `/api/v1/database/test_connection/` |
| `POST` | [Upload a file and returns file metadata](/developer-docs/api/upload-a-file-and-returns-file-metadata) | `/api/v1/database/upload_metadata/` |
| `POST` | [Validate database connection parameters](/developer-docs/api/validate-database-connection-parameters) | `/api/v1/database/validate_parameters/` |
</details>
@@ -196,7 +196,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ |
| `GET` | [Assemble Explore related information in a single endpoint](/developer-docs/6.1.0/api/assemble-explore-related-information-in-a-single-endpoint) | `/api/v1/explore/` |
| `GET` | [Assemble Explore related information in a single endpoint](/developer-docs/api/assemble-explore-related-information-in-a-single-endpoint) | `/api/v1/explore/` |
</details>
@@ -205,13 +205,13 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `GET` | [Get the bootstrap data for SqlLab page](/developer-docs/6.1.0/api/get-the-bootstrap-data-for-sql-lab-page) | `/api/v1/sqllab/` |
| `POST` | [Estimate the SQL query execution cost](/developer-docs/6.1.0/api/estimate-the-sql-query-execution-cost) | `/api/v1/sqllab/estimate/` |
| `POST` | [Execute a SQL query](/developer-docs/6.1.0/api/execute-a-sql-query) | `/api/v1/sqllab/execute/` |
| `POST` | [Export SQL query results to CSV with streaming](/developer-docs/6.1.0/api/export-sql-query-results-to-csv-with-streaming) | `/api/v1/sqllab/export_streaming/` |
| `GET` | [Export the SQL query results to a CSV](/developer-docs/6.1.0/api/export-the-sql-query-results-to-a-csv) | `/api/v1/sqllab/export/{client_id}/` |
| `POST` | [Format SQL code](/developer-docs/6.1.0/api/format-sql-code) | `/api/v1/sqllab/format_sql/` |
| `GET` | [Get the result of a SQL query execution](/developer-docs/6.1.0/api/get-the-result-of-a-sql-query-execution) | `/api/v1/sqllab/results/` |
| `GET` | [Get the bootstrap data for SqlLab page](/developer-docs/api/get-the-bootstrap-data-for-sql-lab-page) | `/api/v1/sqllab/` |
| `POST` | [Estimate the SQL query execution cost](/developer-docs/api/estimate-the-sql-query-execution-cost) | `/api/v1/sqllab/estimate/` |
| `POST` | [Execute a SQL query](/developer-docs/api/execute-a-sql-query) | `/api/v1/sqllab/execute/` |
| `POST` | [Export SQL query results to CSV with streaming](/developer-docs/api/export-sql-query-results-to-csv-with-streaming) | `/api/v1/sqllab/export_streaming/` |
| `GET` | [Export the SQL query results to a CSV](/developer-docs/api/export-the-sql-query-results-to-a-csv) | `/api/v1/sqllab/export/{client_id}/` |
| `POST` | [Format SQL code](/developer-docs/api/format-sql-code) | `/api/v1/sqllab/format_sql/` |
| `GET` | [Get the result of a SQL query execution](/developer-docs/api/get-the-result-of-a-sql-query-execution) | `/api/v1/sqllab/results/` |
</details>
@@ -220,23 +220,23 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
| `GET` | [Get a list of queries](/developer-docs/6.1.0/api/get-a-list-of-queries) | `/api/v1/query/` |
| `GET` | [Get query detail information](/developer-docs/6.1.0/api/get-query-detail-information) | `/api/v1/query/{pk}` |
| `GET` | [Get distinct values from field data (query-distinct-column-name)](/developer-docs/6.1.0/api/get-distinct-values-from-field-data-query-distinct-column-name) | `/api/v1/query/distinct/{column_name}` |
| `GET` | [Get related fields data (query-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-query-related-column-name) | `/api/v1/query/related/{column_name}` |
| `POST` | [Manually stop a query with client_id](/developer-docs/6.1.0/api/manually-stop-a-query-with-client-id) | `/api/v1/query/stop` |
| `GET` | [Get a list of queries that changed after last_updated_ms](/developer-docs/6.1.0/api/get-a-list-of-queries-that-changed-after-last-updated-ms) | `/api/v1/query/updated_since` |
| `DELETE` | [Bulk delete saved queries](/developer-docs/6.1.0/api/bulk-delete-saved-queries) | `/api/v1/saved_query/` |
| `GET` | [Get a list of saved queries](/developer-docs/6.1.0/api/get-a-list-of-saved-queries) | `/api/v1/saved_query/` |
| `POST` | [Create a saved query](/developer-docs/6.1.0/api/create-a-saved-query) | `/api/v1/saved_query/` |
| `GET` | [Get metadata information about this API resource (saved-query--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-saved-query-info) | `/api/v1/saved_query/_info` |
| `DELETE` | [Delete a saved query](/developer-docs/6.1.0/api/delete-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `GET` | [Get a saved query](/developer-docs/6.1.0/api/get-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `PUT` | [Update a saved query](/developer-docs/6.1.0/api/update-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `GET` | [Get distinct values from field data (saved-query-distinct-column-name)](/developer-docs/6.1.0/api/get-distinct-values-from-field-data-saved-query-distinct-column-name) | `/api/v1/saved_query/distinct/{column_name}` |
| `GET` | [Download multiple saved queries as YAML files](/developer-docs/6.1.0/api/download-multiple-saved-queries-as-yaml-files) | `/api/v1/saved_query/export/` |
| `POST` | [Import saved queries with associated databases](/developer-docs/6.1.0/api/import-saved-queries-with-associated-databases) | `/api/v1/saved_query/import/` |
| `GET` | [Get related fields data (saved-query-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-saved-query-related-column-name) | `/api/v1/saved_query/related/{column_name}` |
| `GET` | [Get a list of queries](/developer-docs/api/get-a-list-of-queries) | `/api/v1/query/` |
| `GET` | [Get query detail information](/developer-docs/api/get-query-detail-information) | `/api/v1/query/{pk}` |
| `GET` | [Get distinct values from field data (query-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-query-distinct-column-name) | `/api/v1/query/distinct/{column_name}` |
| `GET` | [Get related fields data (query-related-column-name)](/developer-docs/api/get-related-fields-data-query-related-column-name) | `/api/v1/query/related/{column_name}` |
| `POST` | [Manually stop a query with client_id](/developer-docs/api/manually-stop-a-query-with-client-id) | `/api/v1/query/stop` |
| `GET` | [Get a list of queries that changed after last_updated_ms](/developer-docs/api/get-a-list-of-queries-that-changed-after-last-updated-ms) | `/api/v1/query/updated_since` |
| `DELETE` | [Bulk delete saved queries](/developer-docs/api/bulk-delete-saved-queries) | `/api/v1/saved_query/` |
| `GET` | [Get a list of saved queries](/developer-docs/api/get-a-list-of-saved-queries) | `/api/v1/saved_query/` |
| `POST` | [Create a saved query](/developer-docs/api/create-a-saved-query) | `/api/v1/saved_query/` |
| `GET` | [Get metadata information about this API resource (saved-query--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-saved-query-info) | `/api/v1/saved_query/_info` |
| `DELETE` | [Delete a saved query](/developer-docs/api/delete-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `GET` | [Get a saved query](/developer-docs/api/get-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `PUT` | [Update a saved query](/developer-docs/api/update-a-saved-query) | `/api/v1/saved_query/{pk}` |
| `GET` | [Get distinct values from field data (saved-query-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-saved-query-distinct-column-name) | `/api/v1/saved_query/distinct/{column_name}` |
| `GET` | [Download multiple saved queries as YAML files](/developer-docs/api/download-multiple-saved-queries-as-yaml-files) | `/api/v1/saved_query/export/` |
| `POST` | [Import saved queries with associated databases](/developer-docs/api/import-saved-queries-with-associated-databases) | `/api/v1/saved_query/import/` |
| `GET` | [Get related fields data (saved-query-related-column-name)](/developer-docs/api/get-related-fields-data-saved-query-related-column-name) | `/api/v1/saved_query/related/{column_name}` |
</details>
@@ -245,8 +245,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `GET` | [Get possible values for a datasource column](/developer-docs/6.1.0/api/get-possible-values-for-a-datasource-column) | `/api/v1/datasource/{datasource_type}/{datasource_id}/column/{column_name}/values/` |
| `POST` | [Validate a SQL expression against a datasource](/developer-docs/6.1.0/api/validate-a-sql-expression-against-a-datasource) | `/api/v1/datasource/{datasource_type}/{datasource_id}/validate_expression/` |
| `GET` | [Get possible values for a datasource column](/developer-docs/api/get-possible-values-for-a-datasource-column) | `/api/v1/datasource/{datasource_type}/{datasource_id}/column/{column_name}/values/` |
| `POST` | [Validate a SQL expression against a datasource](/developer-docs/api/validate-a-sql-expression-against-a-datasource) | `/api/v1/datasource/{datasource_type}/{datasource_id}/validate_expression/` |
</details>
@@ -255,8 +255,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `GET` | [Return an AdvancedDataTypeResponse](/developer-docs/6.1.0/api/return-an-advanced-data-type-response) | `/api/v1/advanced_data_type/convert` |
| `GET` | [Return a list of available advanced data types](/developer-docs/6.1.0/api/return-a-list-of-available-advanced-data-types) | `/api/v1/advanced_data_type/types` |
| `GET` | [Return an AdvancedDataTypeResponse](/developer-docs/api/return-an-advanced-data-type-response) | `/api/v1/advanced_data_type/convert` |
| `GET` | [Return a list of available advanced data types](/developer-docs/api/return-a-list-of-available-advanced-data-types) | `/api/v1/advanced_data_type/types` |
</details>
@@ -267,21 +267,21 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- |
| `DELETE` | [Bulk delete tags](/developer-docs/6.1.0/api/bulk-delete-tags) | `/api/v1/tag/` |
| `GET` | [Get a list of tags](/developer-docs/6.1.0/api/get-a-list-of-tags) | `/api/v1/tag/` |
| `POST` | [Create a tag](/developer-docs/6.1.0/api/create-a-tag) | `/api/v1/tag/` |
| `GET` | [Get metadata information about tag API endpoints](/developer-docs/6.1.0/api/get-metadata-information-about-tag-api-endpoints) | `/api/v1/tag/_info` |
| `POST` | [Add tags to an object](/developer-docs/6.1.0/api/add-tags-to-an-object) | `/api/v1/tag/{object_type}/{object_id}/` |
| `DELETE` | [Delete a tagged object](/developer-docs/6.1.0/api/delete-a-tagged-object) | `/api/v1/tag/{object_type}/{object_id}/{tag}/` |
| `DELETE` | [Delete a tag](/developer-docs/6.1.0/api/delete-a-tag) | `/api/v1/tag/{pk}` |
| `GET` | [Get a tag detail information](/developer-docs/6.1.0/api/get-a-tag-detail-information) | `/api/v1/tag/{pk}` |
| `PUT` | [Update a tag](/developer-docs/6.1.0/api/update-a-tag) | `/api/v1/tag/{pk}` |
| `DELETE` | [Delete tag by pk favorites](/developer-docs/6.1.0/api/delete-tag-by-pk-favorites) | `/api/v1/tag/{pk}/favorites/` |
| `POST` | [Create tag by pk favorites](/developer-docs/6.1.0/api/create-tag-by-pk-favorites) | `/api/v1/tag/{pk}/favorites/` |
| `POST` | [Bulk create tags and tagged objects](/developer-docs/6.1.0/api/bulk-create-tags-and-tagged-objects) | `/api/v1/tag/bulk_create` |
| `GET` | [Get tag favorite status](/developer-docs/6.1.0/api/get-tag-favorite-status) | `/api/v1/tag/favorite_status/` |
| `GET` | [Get all objects associated with a tag](/developer-docs/6.1.0/api/get-all-objects-associated-with-a-tag) | `/api/v1/tag/get_objects/` |
| `GET` | [Get related fields data (tag-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-tag-related-column-name) | `/api/v1/tag/related/{column_name}` |
| `DELETE` | [Bulk delete tags](/developer-docs/api/bulk-delete-tags) | `/api/v1/tag/` |
| `GET` | [Get a list of tags](/developer-docs/api/get-a-list-of-tags) | `/api/v1/tag/` |
| `POST` | [Create a tag](/developer-docs/api/create-a-tag) | `/api/v1/tag/` |
| `GET` | [Get metadata information about tag API endpoints](/developer-docs/api/get-metadata-information-about-tag-api-endpoints) | `/api/v1/tag/_info` |
| `POST` | [Add tags to an object](/developer-docs/api/add-tags-to-an-object) | `/api/v1/tag/{object_type}/{object_id}/` |
| `DELETE` | [Delete a tagged object](/developer-docs/api/delete-a-tagged-object) | `/api/v1/tag/{object_type}/{object_id}/{tag}/` |
| `DELETE` | [Delete a tag](/developer-docs/api/delete-a-tag) | `/api/v1/tag/{pk}` |
| `GET` | [Get a tag detail information](/developer-docs/api/get-a-tag-detail-information) | `/api/v1/tag/{pk}` |
| `PUT` | [Update a tag](/developer-docs/api/update-a-tag) | `/api/v1/tag/{pk}` |
| `DELETE` | [Delete tag by pk favorites](/developer-docs/api/delete-tag-by-pk-favorites) | `/api/v1/tag/{pk}/favorites/` |
| `POST` | [Create tag by pk favorites](/developer-docs/api/create-tag-by-pk-favorites) | `/api/v1/tag/{pk}/favorites/` |
| `POST` | [Bulk create tags and tagged objects](/developer-docs/api/bulk-create-tags-and-tagged-objects) | `/api/v1/tag/bulk_create` |
| `GET` | [Get tag favorite status](/developer-docs/api/get-tag-favorite-status) | `/api/v1/tag/favorite_status/` |
| `GET` | [Get all objects associated with a tag](/developer-docs/api/get-all-objects-associated-with-a-tag) | `/api/v1/tag/get_objects/` |
| `GET` | [Get related fields data (tag-related-column-name)](/developer-docs/api/get-related-fields-data-tag-related-column-name) | `/api/v1/tag/related/{column_name}` |
</details>
@@ -290,20 +290,20 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- |
| `DELETE` | [Delete multiple annotation layers in a bulk operation](/developer-docs/6.1.0/api/delete-multiple-annotation-layers-in-a-bulk-operation) | `/api/v1/annotation_layer/` |
| `GET` | [Get a list of annotation layers (annotation-layer)](/developer-docs/6.1.0/api/get-a-list-of-annotation-layers-annotation-layer) | `/api/v1/annotation_layer/` |
| `POST` | [Create an annotation layer (annotation-layer)](/developer-docs/6.1.0/api/create-an-annotation-layer-annotation-layer) | `/api/v1/annotation_layer/` |
| `GET` | [Get metadata information about this API resource (annotation-layer--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-annotation-layer-info) | `/api/v1/annotation_layer/_info` |
| `DELETE` | [Delete annotation layer (annotation-layer-pk)](/developer-docs/6.1.0/api/delete-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `GET` | [Get an annotation layer (annotation-layer-pk)](/developer-docs/6.1.0/api/get-an-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `PUT` | [Update an annotation layer (annotation-layer-pk)](/developer-docs/6.1.0/api/update-an-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `DELETE` | [Bulk delete annotation layers](/developer-docs/6.1.0/api/bulk-delete-annotation-layers) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `GET` | [Get a list of annotation layers (annotation-layer-pk-annotation)](/developer-docs/6.1.0/api/get-a-list-of-annotation-layers-annotation-layer-pk-annotation) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `POST` | [Create an annotation layer (annotation-layer-pk-annotation)](/developer-docs/6.1.0/api/create-an-annotation-layer-annotation-layer-pk-annotation) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `DELETE` | [Delete annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/6.1.0/api/delete-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `GET` | [Get an annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/6.1.0/api/get-an-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `PUT` | [Update an annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/6.1.0/api/update-an-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `GET` | [Get related fields data (annotation-layer-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-annotation-layer-related-column-name) | `/api/v1/annotation_layer/related/{column_name}` |
| `DELETE` | [Delete multiple annotation layers in a bulk operation](/developer-docs/api/delete-multiple-annotation-layers-in-a-bulk-operation) | `/api/v1/annotation_layer/` |
| `GET` | [Get a list of annotation layers (annotation-layer)](/developer-docs/api/get-a-list-of-annotation-layers-annotation-layer) | `/api/v1/annotation_layer/` |
| `POST` | [Create an annotation layer (annotation-layer)](/developer-docs/api/create-an-annotation-layer-annotation-layer) | `/api/v1/annotation_layer/` |
| `GET` | [Get metadata information about this API resource (annotation-layer--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-annotation-layer-info) | `/api/v1/annotation_layer/_info` |
| `DELETE` | [Delete annotation layer (annotation-layer-pk)](/developer-docs/api/delete-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `GET` | [Get an annotation layer (annotation-layer-pk)](/developer-docs/api/get-an-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `PUT` | [Update an annotation layer (annotation-layer-pk)](/developer-docs/api/update-an-annotation-layer-annotation-layer-pk) | `/api/v1/annotation_layer/{pk}` |
| `DELETE` | [Bulk delete annotation layers](/developer-docs/api/bulk-delete-annotation-layers) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `GET` | [Get a list of annotation layers (annotation-layer-pk-annotation)](/developer-docs/api/get-a-list-of-annotation-layers-annotation-layer-pk-annotation) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `POST` | [Create an annotation layer (annotation-layer-pk-annotation)](/developer-docs/api/create-an-annotation-layer-annotation-layer-pk-annotation) | `/api/v1/annotation_layer/{pk}/annotation/` |
| `DELETE` | [Delete annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/api/delete-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `GET` | [Get an annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/api/get-an-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `PUT` | [Update an annotation layer (annotation-layer-pk-annotation-annotation-id)](/developer-docs/api/update-an-annotation-layer-annotation-layer-pk-annotation-annotation-id) | `/api/v1/annotation_layer/{pk}/annotation/{annotation_id}` |
| `GET` | [Get related fields data (annotation-layer-related-column-name)](/developer-docs/api/get-related-fields-data-annotation-layer-related-column-name) | `/api/v1/annotation_layer/related/{column_name}` |
</details>
@@ -312,14 +312,14 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `DELETE` | [Bulk delete CSS templates](/developer-docs/6.1.0/api/bulk-delete-css-templates) | `/api/v1/css_template/` |
| `GET` | [Get a list of CSS templates](/developer-docs/6.1.0/api/get-a-list-of-css-templates) | `/api/v1/css_template/` |
| `POST` | [Create a CSS template](/developer-docs/6.1.0/api/create-a-css-template) | `/api/v1/css_template/` |
| `GET` | [Get metadata information about this API resource (css-template--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-css-template-info) | `/api/v1/css_template/_info` |
| `DELETE` | [Delete a CSS template](/developer-docs/6.1.0/api/delete-a-css-template) | `/api/v1/css_template/{pk}` |
| `GET` | [Get a CSS template](/developer-docs/6.1.0/api/get-a-css-template) | `/api/v1/css_template/{pk}` |
| `PUT` | [Update a CSS template](/developer-docs/6.1.0/api/update-a-css-template) | `/api/v1/css_template/{pk}` |
| `GET` | [Get related fields data (css-template-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-css-template-related-column-name) | `/api/v1/css_template/related/{column_name}` |
| `DELETE` | [Bulk delete CSS templates](/developer-docs/api/bulk-delete-css-templates) | `/api/v1/css_template/` |
| `GET` | [Get a list of CSS templates](/developer-docs/api/get-a-list-of-css-templates) | `/api/v1/css_template/` |
| `POST` | [Create a CSS template](/developer-docs/api/create-a-css-template) | `/api/v1/css_template/` |
| `GET` | [Get metadata information about this API resource (css-template--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-css-template-info) | `/api/v1/css_template/_info` |
| `DELETE` | [Delete a CSS template](/developer-docs/api/delete-a-css-template) | `/api/v1/css_template/{pk}` |
| `GET` | [Get a CSS template](/developer-docs/api/get-a-css-template) | `/api/v1/css_template/{pk}` |
| `PUT` | [Update a CSS template](/developer-docs/api/update-a-css-template) | `/api/v1/css_template/{pk}` |
| `GET` | [Get related fields data (css-template-related-column-name)](/developer-docs/api/get-related-fields-data-css-template-related-column-name) | `/api/v1/css_template/related/{column_name}` |
</details>
@@ -330,8 +330,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ----------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `POST` | [Create a new dashboard's permanent link](/developer-docs/6.1.0/api/create-a-new-dashboards-permanent-link) | `/api/v1/dashboard/{pk}/permalink` |
| `GET` | [Get dashboard's permanent link state](/developer-docs/6.1.0/api/get-dashboards-permanent-link-state) | `/api/v1/dashboard/permalink/{key}` |
| `POST` | [Create a new dashboard's permanent link](/developer-docs/api/create-a-new-dashboards-permanent-link) | `/api/v1/dashboard/{pk}/permalink` |
| `GET` | [Get dashboard's permanent link state](/developer-docs/api/get-dashboards-permanent-link-state) | `/api/v1/dashboard/permalink/{key}` |
</details>
@@ -340,8 +340,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `POST` | [Create a new permanent link (explore-permalink)](/developer-docs/6.1.0/api/create-a-new-permanent-link-explore-permalink) | `/api/v1/explore/permalink` |
| `GET` | [Get chart's permanent link state](/developer-docs/6.1.0/api/get-charts-permanent-link-state) | `/api/v1/explore/permalink/{key}` |
| `POST` | [Create a new permanent link (explore-permalink)](/developer-docs/api/create-a-new-permanent-link-explore-permalink) | `/api/v1/explore/permalink` |
| `GET` | [Get chart's permanent link state](/developer-docs/api/get-charts-permanent-link-state) | `/api/v1/explore/permalink/{key}` |
</details>
@@ -350,8 +350,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `POST` | [Create a new permanent link (sqllab-permalink)](/developer-docs/6.1.0/api/create-a-new-permanent-link-sqllab-permalink) | `/api/v1/sqllab/permalink` |
| `GET` | [Get permanent link state for SQLLab editor.](/developer-docs/6.1.0/api/get-permanent-link-state-for-sql-lab-editor) | `/api/v1/sqllab/permalink/{key}` |
| `POST` | [Create a new permanent link (sqllab-permalink)](/developer-docs/api/create-a-new-permanent-link-sqllab-permalink) | `/api/v1/sqllab/permalink` |
| `GET` | [Get permanent link state for SQLLab editor.](/developer-docs/api/get-permanent-link-state-for-sql-lab-editor) | `/api/v1/sqllab/permalink/{key}` |
</details>
@@ -360,7 +360,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `GET` | [Get a report schedule log (embedded-dashboard-uuid)](/developer-docs/6.1.0/api/get-a-report-schedule-log-embedded-dashboard-uuid) | `/api/v1/embedded_dashboard/{uuid}` |
| `GET` | [Get a report schedule log (embedded-dashboard-uuid)](/developer-docs/api/get-a-report-schedule-log-embedded-dashboard-uuid) | `/api/v1/embedded_dashboard/{uuid}` |
</details>
@@ -369,10 +369,10 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `POST` | [Create a dashboard's filter state](/developer-docs/6.1.0/api/create-a-dashboards-filter-state) | `/api/v1/dashboard/{pk}/filter_state` |
| `DELETE` | [Delete a dashboard's filter state value](/developer-docs/6.1.0/api/delete-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
| `GET` | [Get a dashboard's filter state value](/developer-docs/6.1.0/api/get-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
| `PUT` | [Update a dashboard's filter state value](/developer-docs/6.1.0/api/update-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
| `POST` | [Create a dashboard's filter state](/developer-docs/api/create-a-dashboards-filter-state) | `/api/v1/dashboard/{pk}/filter_state` |
| `DELETE` | [Delete a dashboard's filter state value](/developer-docs/api/delete-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
| `GET` | [Get a dashboard's filter state value](/developer-docs/api/get-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
| `PUT` | [Update a dashboard's filter state value](/developer-docs/api/update-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
</details>
@@ -381,10 +381,10 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------- | --------------------------------- |
| `POST` | [Create a new form_data](/developer-docs/6.1.0/api/create-a-new-form-data) | `/api/v1/explore/form_data` |
| `DELETE` | [Delete a form_data](/developer-docs/6.1.0/api/delete-a-form-data) | `/api/v1/explore/form_data/{key}` |
| `GET` | [Get a form_data](/developer-docs/6.1.0/api/get-a-form-data) | `/api/v1/explore/form_data/{key}` |
| `PUT` | [Update an existing form_data](/developer-docs/6.1.0/api/update-an-existing-form-data) | `/api/v1/explore/form_data/{key}` |
| `POST` | [Create a new form_data](/developer-docs/api/create-a-new-form-data) | `/api/v1/explore/form_data` |
| `DELETE` | [Delete a form_data](/developer-docs/api/delete-a-form-data) | `/api/v1/explore/form_data/{key}` |
| `GET` | [Get a form_data](/developer-docs/api/get-a-form-data) | `/api/v1/explore/form_data/{key}` |
| `PUT` | [Update an existing form_data](/developer-docs/api/update-an-existing-form-data) | `/api/v1/explore/form_data/{key}` |
</details>
@@ -395,17 +395,17 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `DELETE` | [Bulk delete report schedules](/developer-docs/6.1.0/api/bulk-delete-report-schedules) | `/api/v1/report/` |
| `GET` | [Get a list of report schedules](/developer-docs/6.1.0/api/get-a-list-of-report-schedules) | `/api/v1/report/` |
| `POST` | [Create a report schedule](/developer-docs/6.1.0/api/create-a-report-schedule) | `/api/v1/report/` |
| `GET` | [Get metadata information about this API resource (report--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-report-info) | `/api/v1/report/_info` |
| `DELETE` | [Delete a report schedule](/developer-docs/6.1.0/api/delete-a-report-schedule) | `/api/v1/report/{pk}` |
| `GET` | [Get a report schedule](/developer-docs/6.1.0/api/get-a-report-schedule) | `/api/v1/report/{pk}` |
| `PUT` | [Update a report schedule](/developer-docs/6.1.0/api/update-a-report-schedule) | `/api/v1/report/{pk}` |
| `GET` | [Get a list of report schedule logs](/developer-docs/6.1.0/api/get-a-list-of-report-schedule-logs) | `/api/v1/report/{pk}/log/` |
| `GET` | [Get a report schedule log (report-pk-log-log-id)](/developer-docs/6.1.0/api/get-a-report-schedule-log-report-pk-log-log-id) | `/api/v1/report/{pk}/log/{log_id}` |
| `GET` | [Get related fields data (report-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-report-related-column-name) | `/api/v1/report/related/{column_name}` |
| `GET` | [Get slack channels](/developer-docs/6.1.0/api/get-slack-channels) | `/api/v1/report/slack_channels/` |
| `DELETE` | [Bulk delete report schedules](/developer-docs/api/bulk-delete-report-schedules) | `/api/v1/report/` |
| `GET` | [Get a list of report schedules](/developer-docs/api/get-a-list-of-report-schedules) | `/api/v1/report/` |
| `POST` | [Create a report schedule](/developer-docs/api/create-a-report-schedule) | `/api/v1/report/` |
| `GET` | [Get metadata information about this API resource (report--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-report-info) | `/api/v1/report/_info` |
| `DELETE` | [Delete a report schedule](/developer-docs/api/delete-a-report-schedule) | `/api/v1/report/{pk}` |
| `GET` | [Get a report schedule](/developer-docs/api/get-a-report-schedule) | `/api/v1/report/{pk}` |
| `PUT` | [Update a report schedule](/developer-docs/api/update-a-report-schedule) | `/api/v1/report/{pk}` |
| `GET` | [Get a list of report schedule logs](/developer-docs/api/get-a-list-of-report-schedule-logs) | `/api/v1/report/{pk}/log/` |
| `GET` | [Get a report schedule log (report-pk-log-log-id)](/developer-docs/api/get-a-report-schedule-log-report-pk-log-log-id) | `/api/v1/report/{pk}/log/{log_id}` |
| `GET` | [Get related fields data (report-related-column-name)](/developer-docs/api/get-related-fields-data-report-related-column-name) | `/api/v1/report/related/{column_name}` |
| `GET` | [Get slack channels](/developer-docs/api/get-slack-channels) | `/api/v1/report/slack_channels/` |
</details>
@@ -416,17 +416,17 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `GET` | [Get security roles](/developer-docs/6.1.0/api/get-security-roles) | `/api/v1/security/roles/` |
| `POST` | [Create security roles](/developer-docs/6.1.0/api/create-security-roles) | `/api/v1/security/roles/` |
| `GET` | [Get security roles info](/developer-docs/6.1.0/api/get-security-roles-info) | `/api/v1/security/roles/_info` |
| `DELETE` | [Delete security roles by pk](/developer-docs/6.1.0/api/delete-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `GET` | [Get security roles by pk](/developer-docs/6.1.0/api/get-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `PUT` | [Update security roles by pk](/developer-docs/6.1.0/api/update-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `PUT` | [Update security roles by role_id groups](/developer-docs/6.1.0/api/update-security-roles-by-role-id-groups) | `/api/v1/security/roles/{role_id}/groups` |
| `POST` | [Create security roles by role_id permissions](/developer-docs/6.1.0/api/create-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions` |
| `GET` | [Get security roles by role_id permissions](/developer-docs/6.1.0/api/get-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions/` |
| `PUT` | [Update security roles by role_id users](/developer-docs/6.1.0/api/update-security-roles-by-role-id-users) | `/api/v1/security/roles/{role_id}/users` |
| `GET` | [List roles](/developer-docs/6.1.0/api/list-roles) | `/api/v1/security/roles/search/` |
| `GET` | [Get security roles](/developer-docs/api/get-security-roles) | `/api/v1/security/roles/` |
| `POST` | [Create security roles](/developer-docs/api/create-security-roles) | `/api/v1/security/roles/` |
| `GET` | [Get security roles info](/developer-docs/api/get-security-roles-info) | `/api/v1/security/roles/_info` |
| `DELETE` | [Delete security roles by pk](/developer-docs/api/delete-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `GET` | [Get security roles by pk](/developer-docs/api/get-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `PUT` | [Update security roles by pk](/developer-docs/api/update-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
| `PUT` | [Update security roles by role_id groups](/developer-docs/api/update-security-roles-by-role-id-groups) | `/api/v1/security/roles/{role_id}/groups` |
| `POST` | [Create security roles by role_id permissions](/developer-docs/api/create-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions` |
| `GET` | [Get security roles by role_id permissions](/developer-docs/api/get-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions/` |
| `PUT` | [Update security roles by role_id users](/developer-docs/api/update-security-roles-by-role-id-users) | `/api/v1/security/roles/{role_id}/users` |
| `GET` | [List roles](/developer-docs/api/list-roles) | `/api/v1/security/roles/search/` |
</details>
@@ -435,12 +435,12 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------------------------------ | ------------------------------ |
| `GET` | [Get security users](/developer-docs/6.1.0/api/get-security-users) | `/api/v1/security/users/` |
| `POST` | [Create security users](/developer-docs/6.1.0/api/create-security-users) | `/api/v1/security/users/` |
| `GET` | [Get security users info](/developer-docs/6.1.0/api/get-security-users-info) | `/api/v1/security/users/_info` |
| `DELETE` | [Delete security users by pk](/developer-docs/6.1.0/api/delete-security-users-by-pk) | `/api/v1/security/users/{pk}` |
| `GET` | [Get security users by pk](/developer-docs/6.1.0/api/get-security-users-by-pk) | `/api/v1/security/users/{pk}` |
| `PUT` | [Update security users by pk](/developer-docs/6.1.0/api/update-security-users-by-pk) | `/api/v1/security/users/{pk}` |
| `GET` | [Get security users](/developer-docs/api/get-security-users) | `/api/v1/security/users/` |
| `POST` | [Create security users](/developer-docs/api/create-security-users) | `/api/v1/security/users/` |
| `GET` | [Get security users info](/developer-docs/api/get-security-users-info) | `/api/v1/security/users/_info` |
| `DELETE` | [Delete security users by pk](/developer-docs/api/delete-security-users-by-pk) | `/api/v1/security/users/{pk}` |
| `GET` | [Get security users by pk](/developer-docs/api/get-security-users-by-pk) | `/api/v1/security/users/{pk}` |
| `PUT` | [Update security users by pk](/developer-docs/api/update-security-users-by-pk) | `/api/v1/security/users/{pk}` |
</details>
@@ -449,9 +449,9 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ------------------------------------------------------------------------------------ | ------------------------------------ |
| `GET` | [Get security permissions](/developer-docs/6.1.0/api/get-security-permissions) | `/api/v1/security/permissions/` |
| `GET` | [Get security permissions info](/developer-docs/6.1.0/api/get-security-permissions-info) | `/api/v1/security/permissions/_info` |
| `GET` | [Get security permissions by pk](/developer-docs/6.1.0/api/get-security-permissions-by-pk) | `/api/v1/security/permissions/{pk}` |
| `GET` | [Get security permissions](/developer-docs/api/get-security-permissions) | `/api/v1/security/permissions/` |
| `GET` | [Get security permissions info](/developer-docs/api/get-security-permissions-info) | `/api/v1/security/permissions/_info` |
| `GET` | [Get security permissions by pk](/developer-docs/api/get-security-permissions-by-pk) | `/api/v1/security/permissions/{pk}` |
</details>
@@ -460,12 +460,12 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------------- | ---------------------------------- |
| `GET` | [Get security resources](/developer-docs/6.1.0/api/get-security-resources) | `/api/v1/security/resources/` |
| `POST` | [Create security resources](/developer-docs/6.1.0/api/create-security-resources) | `/api/v1/security/resources/` |
| `GET` | [Get security resources info](/developer-docs/6.1.0/api/get-security-resources-info) | `/api/v1/security/resources/_info` |
| `DELETE` | [Delete security resources by pk](/developer-docs/6.1.0/api/delete-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
| `GET` | [Get security resources by pk](/developer-docs/6.1.0/api/get-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
| `PUT` | [Update security resources by pk](/developer-docs/6.1.0/api/update-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
| `GET` | [Get security resources](/developer-docs/api/get-security-resources) | `/api/v1/security/resources/` |
| `POST` | [Create security resources](/developer-docs/api/create-security-resources) | `/api/v1/security/resources/` |
| `GET` | [Get security resources info](/developer-docs/api/get-security-resources-info) | `/api/v1/security/resources/_info` |
| `DELETE` | [Delete security resources by pk](/developer-docs/api/delete-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
| `GET` | [Get security resources by pk](/developer-docs/api/get-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
| `PUT` | [Update security resources by pk](/developer-docs/api/update-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
</details>
@@ -474,12 +474,12 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `GET` | [Get security permissions resources](/developer-docs/6.1.0/api/get-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
| `POST` | [Create security permissions resources](/developer-docs/6.1.0/api/create-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
| `GET` | [Get security permissions resources info](/developer-docs/6.1.0/api/get-security-permissions-resources-info) | `/api/v1/security/permissions-resources/_info` |
| `DELETE` | [Delete security permissions resources by pk](/developer-docs/6.1.0/api/delete-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
| `GET` | [Get security permissions resources by pk](/developer-docs/6.1.0/api/get-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
| `PUT` | [Update security permissions resources by pk](/developer-docs/6.1.0/api/update-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
| `GET` | [Get security permissions resources](/developer-docs/api/get-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
| `POST` | [Create security permissions resources](/developer-docs/api/create-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
| `GET` | [Get security permissions resources info](/developer-docs/api/get-security-permissions-resources-info) | `/api/v1/security/permissions-resources/_info` |
| `DELETE` | [Delete security permissions resources by pk](/developer-docs/api/delete-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
| `GET` | [Get security permissions resources by pk](/developer-docs/api/get-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
| `PUT` | [Update security permissions resources by pk](/developer-docs/api/update-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
</details>
@@ -488,14 +488,14 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `DELETE` | [Bulk delete RLS rules](/developer-docs/6.1.0/api/bulk-delete-rls-rules) | `/api/v1/rowlevelsecurity/` |
| `GET` | [Get a list of RLS](/developer-docs/6.1.0/api/get-a-list-of-rls) | `/api/v1/rowlevelsecurity/` |
| `POST` | [Create a new RLS rule](/developer-docs/6.1.0/api/create-a-new-rls-rule) | `/api/v1/rowlevelsecurity/` |
| `GET` | [Get metadata information about this API resource (rowlevelsecurity--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-rowlevelsecurity-info) | `/api/v1/rowlevelsecurity/_info` |
| `DELETE` | [Delete an RLS](/developer-docs/6.1.0/api/delete-an-rls) | `/api/v1/rowlevelsecurity/{pk}` |
| `GET` | [Get an RLS](/developer-docs/6.1.0/api/get-an-rls) | `/api/v1/rowlevelsecurity/{pk}` |
| `PUT` | [Update an RLS rule](/developer-docs/6.1.0/api/update-an-rls-rule) | `/api/v1/rowlevelsecurity/{pk}` |
| `GET` | [Get related fields data (rowlevelsecurity-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-rowlevelsecurity-related-column-name) | `/api/v1/rowlevelsecurity/related/{column_name}` |
| `DELETE` | [Bulk delete RLS rules](/developer-docs/api/bulk-delete-rls-rules) | `/api/v1/rowlevelsecurity/` |
| `GET` | [Get a list of RLS](/developer-docs/api/get-a-list-of-rls) | `/api/v1/rowlevelsecurity/` |
| `POST` | [Create a new RLS rule](/developer-docs/api/create-a-new-rls-rule) | `/api/v1/rowlevelsecurity/` |
| `GET` | [Get metadata information about this API resource (rowlevelsecurity--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-rowlevelsecurity-info) | `/api/v1/rowlevelsecurity/_info` |
| `DELETE` | [Delete an RLS](/developer-docs/api/delete-an-rls) | `/api/v1/rowlevelsecurity/{pk}` |
| `GET` | [Get an RLS](/developer-docs/api/get-an-rls) | `/api/v1/rowlevelsecurity/{pk}` |
| `PUT` | [Update an RLS rule](/developer-docs/api/update-an-rls-rule) | `/api/v1/rowlevelsecurity/{pk}` |
| `GET` | [Get related fields data (rowlevelsecurity-related-column-name)](/developer-docs/api/get-related-fields-data-rowlevelsecurity-related-column-name) | `/api/v1/rowlevelsecurity/related/{column_name}` |
</details>
@@ -506,8 +506,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------- | ------------------------ |
| `GET` | [Export all assets](/developer-docs/6.1.0/api/export-all-assets) | `/api/v1/assets/export/` |
| `POST` | [Import multiple assets](/developer-docs/6.1.0/api/import-multiple-assets) | `/api/v1/assets/import/` |
| `GET` | [Export all assets](/developer-docs/api/export-all-assets) | `/api/v1/assets/export/` |
| `POST` | [Import multiple assets](/developer-docs/api/import-multiple-assets) | `/api/v1/assets/import/` |
</details>
@@ -516,7 +516,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `POST` | [Invalidate cache records and remove the database records](/developer-docs/6.1.0/api/invalidate-cache-records-and-remove-the-database-records) | `/api/v1/cachekey/invalidate` |
| `POST` | [Invalidate cache records and remove the database records](/developer-docs/api/invalidate-cache-records-and-remove-the-database-records) | `/api/v1/cachekey/invalidate` |
</details>
@@ -525,10 +525,10 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------------------------------- | ------------------------------ |
| `GET` | [Get a list of logs](/developer-docs/6.1.0/api/get-a-list-of-logs) | `/api/v1/log/` |
| `POST` | [Create log](/developer-docs/6.1.0/api/create-log) | `/api/v1/log/` |
| `GET` | [Get a log detail information](/developer-docs/6.1.0/api/get-a-log-detail-information) | `/api/v1/log/{pk}` |
| `GET` | [Get recent activity data for a user](/developer-docs/6.1.0/api/get-recent-activity-data-for-a-user) | `/api/v1/log/recent_activity/` |
| `GET` | [Get a list of logs](/developer-docs/api/get-a-list-of-logs) | `/api/v1/log/` |
| `POST` | [Create log](/developer-docs/api/create-log) | `/api/v1/log/` |
| `GET` | [Get a log detail information](/developer-docs/api/get-a-log-detail-information) | `/api/v1/log/{pk}` |
| `GET` | [Get recent activity data for a user](/developer-docs/api/get-recent-activity-data-for-a-user) | `/api/v1/log/recent_activity/` |
</details>
@@ -539,9 +539,9 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------- | ------------------- |
| `GET` | [Get the user object](/developer-docs/6.1.0/api/get-the-user-object) | `/api/v1/me/` |
| `PUT` | [Update the current user](/developer-docs/6.1.0/api/update-the-current-user) | `/api/v1/me/` |
| `GET` | [Get the user roles](/developer-docs/6.1.0/api/get-the-user-roles) | `/api/v1/me/roles/` |
| `GET` | [Get the user object](/developer-docs/api/get-the-user-object) | `/api/v1/me/` |
| `PUT` | [Update the current user](/developer-docs/api/update-the-current-user) | `/api/v1/me/` |
| `GET` | [Get the user roles](/developer-docs/api/get-the-user-roles) | `/api/v1/me/roles/` |
</details>
@@ -550,7 +550,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------- | ----------------------------------- |
| `GET` | [Get the user avatar](/developer-docs/6.1.0/api/get-the-user-avatar) | `/api/v1/user/{user_id}/avatar.png` |
| `GET` | [Get the user avatar](/developer-docs/api/get-the-user-avatar) | `/api/v1/user/{user_id}/avatar.png` |
</details>
@@ -559,7 +559,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------- | --------------- |
| `GET` | [Get menu](/developer-docs/6.1.0/api/get-menu) | `/api/v1/menu/` |
| `GET` | [Get menu](/developer-docs/api/get-menu) | `/api/v1/menu/` |
</details>
@@ -568,7 +568,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | -------------------------------------------------------------------------- | ---------------------------- |
| `GET` | [Get all available domains](/developer-docs/6.1.0/api/get-all-available-domains) | `/api/v1/available_domains/` |
| `GET` | [Get all available domains](/developer-docs/api/get-all-available-domains) | `/api/v1/available_domains/` |
</details>
@@ -577,7 +577,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------------------------------- | ---------------------- |
| `GET` | [Read off of the Redis events stream](/developer-docs/6.1.0/api/read-off-of-the-redis-events-stream) | `/api/v1/async_event/` |
| `GET` | [Read off of the Redis events stream](/developer-docs/api/read-off-of-the-redis-events-stream) | `/api/v1/async_event/` |
</details>
@@ -586,7 +586,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| ------ | ---------------------------------------------------------------------------- | ------------------------- |
| `GET` | [Get api by version openapi](/developer-docs/6.1.0/api/get-api-by-version-openapi) | `/api/{version}/_openapi` |
| `GET` | [Get api by version openapi](/developer-docs/api/get-api-by-version-openapi) | `/api/{version}/_openapi` |
</details>
@@ -597,12 +597,12 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------- | ------------------------------- |
| `GET` | [Get security groups](/developer-docs/6.1.0/api/get-security-groups) | `/api/v1/security/groups/` |
| `POST` | [Create security groups](/developer-docs/6.1.0/api/create-security-groups) | `/api/v1/security/groups/` |
| `GET` | [Get security groups info](/developer-docs/6.1.0/api/get-security-groups-info) | `/api/v1/security/groups/_info` |
| `DELETE` | [Delete security groups by pk](/developer-docs/6.1.0/api/delete-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
| `GET` | [Get security groups by pk](/developer-docs/6.1.0/api/get-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
| `PUT` | [Update security groups by pk](/developer-docs/6.1.0/api/update-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
| `GET` | [Get security groups](/developer-docs/api/get-security-groups) | `/api/v1/security/groups/` |
| `POST` | [Create security groups](/developer-docs/api/create-security-groups) | `/api/v1/security/groups/` |
| `GET` | [Get security groups info](/developer-docs/api/get-security-groups-info) | `/api/v1/security/groups/_info` |
| `DELETE` | [Delete security groups by pk](/developer-docs/api/delete-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
| `GET` | [Get security groups by pk](/developer-docs/api/get-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
| `PUT` | [Update security groups by pk](/developer-docs/api/update-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
</details>
@@ -611,20 +611,20 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `DELETE` | [Bulk delete themes](/developer-docs/6.1.0/api/bulk-delete-themes) | `/api/v1/theme/` |
| `GET` | [Get a list of themes](/developer-docs/6.1.0/api/get-a-list-of-themes) | `/api/v1/theme/` |
| `POST` | [Create a theme](/developer-docs/6.1.0/api/create-a-theme) | `/api/v1/theme/` |
| `GET` | [Get metadata information about this API resource (theme--info)](/developer-docs/6.1.0/api/get-metadata-information-about-this-api-resource-theme-info) | `/api/v1/theme/_info` |
| `DELETE` | [Delete a theme](/developer-docs/6.1.0/api/delete-a-theme) | `/api/v1/theme/{pk}` |
| `GET` | [Get a theme](/developer-docs/6.1.0/api/get-a-theme) | `/api/v1/theme/{pk}` |
| `PUT` | [Update a theme](/developer-docs/6.1.0/api/update-a-theme) | `/api/v1/theme/{pk}` |
| `PUT` | [Set a theme as the system dark theme](/developer-docs/6.1.0/api/set-a-theme-as-the-system-dark-theme) | `/api/v1/theme/{pk}/set_system_dark` |
| `PUT` | [Set a theme as the system default theme](/developer-docs/6.1.0/api/set-a-theme-as-the-system-default-theme) | `/api/v1/theme/{pk}/set_system_default` |
| `GET` | [Download multiple themes as YAML files](/developer-docs/6.1.0/api/download-multiple-themes-as-yaml-files) | `/api/v1/theme/export/` |
| `POST` | [Import themes from a ZIP file](/developer-docs/6.1.0/api/import-themes-from-a-zip-file) | `/api/v1/theme/import/` |
| `GET` | [Get related fields data (theme-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-theme-related-column-name) | `/api/v1/theme/related/{column_name}` |
| `DELETE` | [Clear the system dark theme](/developer-docs/6.1.0/api/clear-the-system-dark-theme) | `/api/v1/theme/unset_system_dark` |
| `DELETE` | [Clear the system default theme](/developer-docs/6.1.0/api/clear-the-system-default-theme) | `/api/v1/theme/unset_system_default` |
| `DELETE` | [Bulk delete themes](/developer-docs/api/bulk-delete-themes) | `/api/v1/theme/` |
| `GET` | [Get a list of themes](/developer-docs/api/get-a-list-of-themes) | `/api/v1/theme/` |
| `POST` | [Create a theme](/developer-docs/api/create-a-theme) | `/api/v1/theme/` |
| `GET` | [Get metadata information about this API resource (theme--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-theme-info) | `/api/v1/theme/_info` |
| `DELETE` | [Delete a theme](/developer-docs/api/delete-a-theme) | `/api/v1/theme/{pk}` |
| `GET` | [Get a theme](/developer-docs/api/get-a-theme) | `/api/v1/theme/{pk}` |
| `PUT` | [Update a theme](/developer-docs/api/update-a-theme) | `/api/v1/theme/{pk}` |
| `PUT` | [Set a theme as the system dark theme](/developer-docs/api/set-a-theme-as-the-system-dark-theme) | `/api/v1/theme/{pk}/set_system_dark` |
| `PUT` | [Set a theme as the system default theme](/developer-docs/api/set-a-theme-as-the-system-default-theme) | `/api/v1/theme/{pk}/set_system_default` |
| `GET` | [Download multiple themes as YAML files](/developer-docs/api/download-multiple-themes-as-yaml-files) | `/api/v1/theme/export/` |
| `POST` | [Import themes from a ZIP file](/developer-docs/api/import-themes-from-a-zip-file) | `/api/v1/theme/import/` |
| `GET` | [Get related fields data (theme-related-column-name)](/developer-docs/api/get-related-fields-data-theme-related-column-name) | `/api/v1/theme/related/{column_name}` |
| `DELETE` | [Clear the system dark theme](/developer-docs/api/clear-the-system-dark-theme) | `/api/v1/theme/unset_system_dark` |
| `DELETE` | [Clear the system default theme](/developer-docs/api/clear-the-system-default-theme) | `/api/v1/theme/unset_system_default` |
</details>
@@ -633,14 +633,14 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
| Method | Endpoint | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `GET` | [Get security user registrations](/developer-docs/6.1.0/api/get-security-user-registrations) | `/api/v1/security/user_registrations/` |
| `POST` | [Create security user registrations](/developer-docs/6.1.0/api/create-security-user-registrations) | `/api/v1/security/user_registrations/` |
| `GET` | [Get security user registrations info](/developer-docs/6.1.0/api/get-security-user-registrations-info) | `/api/v1/security/user_registrations/_info` |
| `DELETE` | [Delete security user registrations by pk](/developer-docs/6.1.0/api/delete-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `GET` | [Get security user registrations by pk](/developer-docs/6.1.0/api/get-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `PUT` | [Update security user registrations by pk](/developer-docs/6.1.0/api/update-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `GET` | [Get distinct values from field data (security-user-registrations-distinct-column-name)](/developer-docs/6.1.0/api/get-distinct-values-from-field-data-security-user-registrations-distinct-column-name) | `/api/v1/security/user_registrations/distinct/{column_name}` |
| `GET` | [Get related fields data (security-user-registrations-related-column-name)](/developer-docs/6.1.0/api/get-related-fields-data-security-user-registrations-related-column-name) | `/api/v1/security/user_registrations/related/{column_name}` |
| `GET` | [Get security user registrations](/developer-docs/api/get-security-user-registrations) | `/api/v1/security/user_registrations/` |
| `POST` | [Create security user registrations](/developer-docs/api/create-security-user-registrations) | `/api/v1/security/user_registrations/` |
| `GET` | [Get security user registrations info](/developer-docs/api/get-security-user-registrations-info) | `/api/v1/security/user_registrations/_info` |
| `DELETE` | [Delete security user registrations by pk](/developer-docs/api/delete-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `GET` | [Get security user registrations by pk](/developer-docs/api/get-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `PUT` | [Update security user registrations by pk](/developer-docs/api/update-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
| `GET` | [Get distinct values from field data (security-user-registrations-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-security-user-registrations-distinct-column-name) | `/api/v1/security/user_registrations/distinct/{column_name}` |
| `GET` | [Get related fields data (security-user-registrations-related-column-name)](/developer-docs/api/get-related-fields-data-security-user-registrations-related-column-name) | `/api/v1/security/user_registrations/related/{column_name}` |
</details>
+1 -1
View File
@@ -58,7 +58,7 @@ A modern, enterprise-ready business intelligence web application.
[**Why Superset?**](#why-superset) |
[**Supported Databases**](#supported-databases) |
[**Installation and Configuration**](#installation-and-configuration) |
[**Release Notes**](https://github.com/apache/superset/releases) |
[**Release Notes**](https://github.com/apache/superset/blob/master/RELEASING/README.md#release-notes-for-recent-releases) |
[**Get Involved**](#get-involved) |
[**Contributor Guide**](#contributor-guide) |
[**Resources**](#resources) |
@@ -328,12 +328,6 @@ Conditional formatting rules highlight cells based on their values. Rules can be
Each rule has a **"Use gradient"** toggle: enabled applies a varying opacity (lighter = further from threshold), disabled applies a solid fill at full opacity regardless of value.
For numeric rules, the optional **"Min bound"** / **"Max bound"** fields let you override the auto-detected color range with fixed values instead of relying on the minimum/maximum found in the data — useful when you want consistent coloring across dashboards or data refreshes.
Set **"Bound unit"** to **"% of column"** to express the bounds as percentages of either the column maximum or **"Column sum"**, which adds the absolute values in the column so positive and negative values do not cancel into an unstable denominator. A non-positive maximum or zero sum falls back to the automatic data range. A percentage center based on **"Column sum"** must still resolve inside the color range to activate diverging colors. This option is unavailable with server pagination because the browser only receives one page of rows and cannot calculate a stable full-column denominator; existing percentage rules also use the automatic range while server pagination is enabled.
For a diverging scale, set a **"Center value"** together with **"Low color"**, **"Mid color"**, and **"High color"**. Values below and above the center interpolate toward the corresponding endpoint color. Turning off **"Use gradient"** disables this interpolation and applies the base color as a solid fill.
Each rule's color is set with a full color picker rather than a fixed dropdown of presets. Pick any custom color, or use the **Colors** preset swatches, which reference theme tokens (success, warning, error, and their background variants) so a rule's color updates automatically if the active theme changes, including switching between light and dark mode.
When a rule targets a column with an active time comparison, a **Trend colors** preset also appears, letting you color cells green for an increase and red for a decrease (or the reverse).
@@ -379,15 +373,6 @@ AG Grid supports server-side column filters that query the full dataset — not
AG Grid Interactive Table supports **Time Shift** (time comparison), matching the behavior of the standard Table chart. In the **Advanced Analytics** → **Time Comparison** section of the chart configuration, enter a shift expression (e.g., `1 year ago`, `minus 7 days`) to add comparison columns showing values from the offset period. Dashboard-level time range overrides apply to both the base and comparison periods.
#### Show Summary
The **Show summary** checkbox lives at the top of the **Visual formatting** section in the **Customize** tab, for both **Aggregate** and **Raw Records** query modes. Enabling it pins a summary row to the bottom of the grid whenever there is something to summarize: at least one metric in **Aggregate** mode, or at least one eligible numeric column in **Raw Records** mode. Otherwise no summary row is added.
- In **Aggregate** mode, the summary row applies each metric's own aggregation (or the **Summary aggregation** override, where available) across the full filtered dataset.
- In **Raw Records** mode, the summary row defaults to a server-side `SUM` for each numeric column that's backed by a physical or calculated dataset column; the **Summary aggregation** control can override this to `AVG` as well. Non-numeric cells and columns built from free-form SQL expressions stay blank.
In both modes, the summary is computed across the full result set, independent of the chart's row limit and pagination, and it reflects dashboard and chart-level filters. It does not reflect AG Grid's own server-side column filters (the per-column filter UI in the grid header), which are excluded from the summary query.
### Dynamic Currency Formatting
Chart metric values can display currencies dynamically rather than using a fixed currency code. To enable:
-48
View File
@@ -145,51 +145,3 @@ The following URL parameters can be passed through the `urlParams` option in `da
- **Row-level security** — pass `rls` rules in the guest token request to restrict which rows are visible to the embedded user.
- **Allowed domains** — restrict which host origins can embed a dashboard by setting **Allowed Domains** per-dashboard in the _Embed_ settings modal. Superset checks the request's `Referer` header against this list before serving the embedded view; an empty list allows any origin, so configure this explicitly for production.
- **Redacted errors** — API responses to a guest token report a generic `An error occurred while fetching the data.` instead of the underlying error, since engine errors quote catalog, schema, table and column names. Errors Superset raises itself — access denials, timeouts, payload validation — keep their message, and the full error is always available in the server logs.
## Guest-token request-header size diagnostics
A successful guest-token mint does not guarantee the token can pass through your
deployment's proxies. Limits apply to the **encoded JWT bytes plus header
overhead**, not the number of RLS rules or identifiers. A proxy can reject the
subsequent authentication request before it reaches Superset, including an HTTP
400 HTML response instead of JSON. A 400 alone does not establish a size problem.
Operators can set a deployment-specific diagnostic budget in `superset_config.py`:
```python
# Example only: choose a budget for your complete proxy path.
GUEST_TOKEN_HEADER_MAX_BYTES = 16 * 1024
```
The default is `None` (no budget warnings). Positive integer budgets count UTF-8
bytes of `GUEST_TOKEN_HEADER_NAME`, `: `, the encoded token, and `\r\n`
(four framing bytes). Only sizes **strictly greater** than the budget warn;
equality does not. This is consistent diagnostic accounting, not a prediction of
every proxy's wire-level accounting, HTTP/2 compression, or total-header limits.
Leave a safety margin and validate your actual deployment, including custom
header names. Zero, negative, non-integral, or non-numeric values (including strings and
booleans) disable budget warnings, as do values above JavaScript's maximum safe
integer (2^53 1). Whole-number floats are accepted. Convert environment-variable
strings to integers in deployment configuration to enable the budget.
Issuance audit metadata includes `token_bytes`, `header_bytes`,
`header_budget_bytes`, and `header_budget_exceeded`. Issuance remains HTTP 200
with the same token and response shape. The embedded bootstrap exposes the budget
and configured header name; reload the iframe after changing deployment config.
The embedded client measures initial and refreshed tokens and warns in the
developer console with sizes only. Initial authentication failures get a targeted
suggestion only when the request's token exceeds the budget and the failure has
no status or HTTP 400/431/494; other statuses and ambiguous in-flight
refreshes use the generic error. Refresh warnings do not restart authentication.
These diagnostics do not record JWTs, decoded claims, RLS SQL, or request headers.
[AWS Application Load Balancer quotas](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-limits.html)
list a non-adjustable 16 K single-header limit. Increasing a Superset diagnostic
budget does not increase that limit or add large-token support.
To reduce payload size, replace large inline RLS ID lists with a compact
entitlements-table subquery where supported by your database. Keep the same
tenant/user restrictions, derive identity from your trusted token-issuing
backend, and verify equivalent row access and query performance before rollout.
Do not remove RLS or broaden entitlements to make a token smaller.
@@ -151,20 +151,6 @@ see some data!
You should see months in the rows and Department and Travel Class in the columns. Publish this chart
to your existing Tutorial Dashboard you created earlier.
:::note
Row and column totals/subtotals for the Pivot Table are correct even for non-additive metrics,
such as ratios (`SUM(a)/SUM(b)`), `COUNT_DISTINCT`, `AVG`, and percentiles, not just additive ones
like `SUM` or `COUNT`. Totals derive client-side, by reducing the same full-detail query results
used to build the table, only when every selected metric is additive; if any selected metric is
non-additive, Superset instead issues a database query at each total's own granularity for all
metrics, so the total reflects each metric's own definition evaluated at that level rather than an
incorrect combination of the displayed cells. Because of this, there's no separate "Aggregation
function" control for totals in the Pivot Table: a total always reflects the metric's own
definition. The Table chart's **Show summary** row is different: its **Summary aggregation**
control can override a simple metric's own aggregation (to Sum or Average) for the summary row
only — metrics built from custom SQL keep their own aggregation regardless.
:::
### Line Chart
In this section, we are going to create a line chart to understand the average price of a ticket by
+20 -56
View File
@@ -71,17 +71,17 @@ Parses a JSON string into an object that can be used in your template.
---
#### `group`
#### `groupBy`
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by). The key is passed as a `by` hash argument.
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by).
```handlebars
{{#group data by="department"}}
{{#groupBy data 'department'}}
<h3>{{value}}</h3>
{{#each items}}
<p>{{this.name}}</p>
{{/each}}
{{/group}}
{{/groupBy}}
```
---
@@ -90,14 +90,6 @@ Groups an array of objects by a key, powered by [handlebars-group-by](https://gi
Superset also registers all helpers from the [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) library. These include a wide range of comparison, math, string, and conditional helpers. Commonly used ones include:
:::note
These names are specific to `just-handlebars-helpers` and differ from other
Handlebars helper libraries — notably `handlebars-helpers`, which spells the
math helpers `add`, `subtract`, `multiply` and `divide`. Calling a helper that
is not registered raises `Missing helper: "..."`, which renders the chart blank,
so it is worth checking a name against the tables below before using it.
:::
#### Comparison
| Helper | Description | Example |
@@ -105,7 +97,6 @@ so it is worth checking a name against the tables below before using it.
| `eq` | Strict equality | `{{#if (eq status "active")}}` |
| `eqw` | Weak equality | `{{#if (eqw count "5")}}` |
| `neq` | Strict inequality | `{{#if (neq role "admin")}}` |
| `neqw` | Weak inequality | `{{#if (neqw count "5")}}` |
| `lt` | Less than | `{{#if (lt score 50)}}` |
| `lte` | Less than or equal | `{{#if (lte score 100)}}` |
| `gt` | Greater than | `{{#if (gt price 0)}}` |
@@ -123,52 +114,25 @@ so it is worth checking a name against the tables below before using it.
#### String
| Helper | Description | Example |
| ----------------- | ----------------------------------------------- | ------------------------------ |
| `capitalizeFirst` | Capitalizes the first letter | `{{capitalizeFirst name}}` |
| `capitalizeEach` | Capitalizes the first letter of each word | `{{capitalizeEach title}}` |
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
| `excerpt` | Truncates to a length and appends an ellipsis | `{{excerpt description 100}}` |
| `sprintf` | printf-style formatting | `{{sprintf "%.1f" score}}` |
| `concat` | Concatenates values | `{{concat first " " last}}` |
| `join` | Joins an array with a separator | `{{join tags ", "}}` |
| `first` / `last` | First or last element of an array | `{{first items}}` |
| `newLineToBr` | Converts newlines to `<br>` (needs `{{{ }}}`) | `{{{newLineToBr notes}}}` |
| Helper | Description | Example |
| ------------ | ----------------------------------- | --------------------------------- |
| `capitalize` | Capitalizes first letter | `{{capitalize name}}` |
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
| `truncate` | Truncates a string | `{{truncate description 100}}` |
| `contains` | Checks if string contains substring | `{{#if (contains tag "urgent")}}` |
#### Math
| Helper | Description | Example |
| ---------------- | ----------------------- | ------------------------------------ |
| `sum` | Addition | `{{sum a b}}` |
| `difference` | Subtraction | `{{difference total discount}}` |
| `multiplication` | Multiplication | `{{multiplication price quantity}}` |
| `division` | Division | `{{division total count}}` |
| `remainder` | Modulo | `{{remainder index 2}}` |
| `abs` | Absolute value | `{{abs delta}}` |
| `ceil` | Ceiling | `{{ceil value}}` |
| `floor` | Floor | `{{floor value}}` |
`sum` takes exactly two arguments — it adds a pair of numbers and does not total
an array. There is no `round` helper; use `{{sprintf "%.0f" value}}` to round to
a given number of decimal places.
#### Arrays
| Helper | Description | Example |
| ---------- | ---------------------------------- | --------------------------------- |
| `includes` | Whether an array contains a value | `{{#if (includes tags "urgent")}}` |
| `empty` | Whether an array is empty | `{{#if (empty rows)}}` |
| `count` | Number of items in an array | `{{count rows}}` |
`includes` tests array membership. It returns `false` for a string, so it cannot
be used to check for a substring.
#### Formatting
| Helper | Description | Example |
| ---------------- | ---------------------------- | -------------------------------- |
| `formatCurrency` | Formats a number as currency | `{{formatCurrency revenue "$"}}` |
| Helper | Description | Example |
| ---------- | -------------- | ----------------------------- |
| `add` | Addition | `{{add a b}}` |
| `subtract` | Subtraction | `{{subtract total discount}}` |
| `multiply` | Multiplication | `{{multiply price quantity}}` |
| `divide` | Division | `{{divide total count}}` |
| `ceil` | Ceiling | `{{ceil value}}` |
| `floor` | Floor | `{{floor value}}` |
| `round` | Round | `{{round value}}` |
For the full list of available helpers, see the [just-handlebars-helpers documentation](https://github.com/leapfrogtechnology/just-handlebars-helpers).
@@ -70,24 +70,5 @@ alert or report is removed, and the reason is shown. Charts that belong to
dashboards are removed from those dashboards as part of the deletion; the
dashboards themselves are left in place.
Before an archived dataset is deleted permanently, Superset checks which
charts still use it and which dashboards contain those charts. The confirmation
shows the total number of affected charts and dashboards, identifies the ones
you are allowed to access, and reports the remaining objects only as restricted
counts. Restricted names, identifiers, and links are not displayed. Archived
dependents are included because they can still be recovered after the dataset
is gone.
Deleting the dataset does not delete those charts or dashboards. They remain
in place without a usable dataset and may therefore be broken. If there are no
dependents, the confirmation explicitly reports zero affected charts and
dashboards.
The dependency check fails closed. While it is loading, or if its result is
unavailable, permanent deletion is disabled; cancel or retry the check. Superset
checks again when you submit. If dependencies changed while the confirmation
was open, the refreshed impact replaces the previous result and you must type
DELETE again before proceeding.
Objects are also deleted permanently on their own once they have been in the
archive longer than the retention window, without anyone acting.
@@ -78,18 +78,6 @@ Charts are **not saved by default**. The workflow is intentionally iterative:
To skip the preview and save immediately, include "and save it" in your prompt.
:::
:::info Deployment-specific chart types
Use `get_chart_type_schema` before generating a chart to discover the types
available on your Superset instance. Some deployments expose additional
feature-gated visualizations. For example, a deployment with an AG Grid pivot
extension enabled can expose `interactive_pivot`, which supports interactive
row groups, pivot columns, totals, and period-over-period comparisons. Pair
`comparison_period` (for example, `1 year ago`) with `comparison_type`
(`values`, `difference`, `percentage`, or `ratio`). It is distinct from the
built-in `pivot_table` chart type and is not offered when the host visualization
is unavailable.
:::
### Create Dashboards
Build dashboards from a collection of charts:
@@ -305,14 +293,6 @@ Ask your admin for the MCP server URL and any authentication tokens you need.
| `list_databases` | List configured database connections |
| `get_database_info` | Get details about a specific database connection |
### Themes
| Tool | Description |
| ---------------- | ------------------------------------------------------------------------- |
| `list_themes` | Discover themes (antd design-token configurations) with filters |
| `get_theme_info` | Get a theme's tokens (`json_data`) by ID or UUID |
| `create_theme` | Create a reusable theme from antd design tokens (requires write access) |
---
## Troubleshooting
+71
View File
@@ -0,0 +1,71 @@
/* eslint-env node */
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const typescriptEslintParser = require('@typescript-eslint/parser');
const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin');
const js = require('@eslint/js');
const ts = require('typescript-eslint');
const react = require('eslint-plugin-react');
const globals = require('globals');
const { defineConfig, globalIgnores } = require('eslint/config');
module.exports = defineConfig([
{
files: ['**/*.{js,jsx,ts,tsx}'],
},
globalIgnores(['build/**/*', '.docusaurus/**/*', 'node_modules/**/*']),
js.configs.recommended,
...ts.configs.recommended,
{
files: ['eslint.config.js'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
},
},
{
languageOptions: {
parser: typescriptEslintParser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2020,
sourceType: 'module',
},
globals: {
...globals.browser,
...globals.node,
},
},
plugins: {
typescript: typescriptEslintPlugin,
react,
},
rules: {
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
settings: {
react: {
version: 'detect',
},
},
},
]);
+2 -5
View File
@@ -43,11 +43,8 @@ publish = "build"
ignore = 'if [ -n "$CACHED_COMMIT_REF" ]; then git diff --quiet "$CACHED_COMMIT_REF" HEAD -- . ../README.md; else git fetch --no-tags origin master >/dev/null 2>&1 || true; i=0; while [ "$i" -lt 10 ] && ! git merge-base origin/master HEAD >/dev/null 2>&1; do git fetch --deepen=200 origin master >/dev/null 2>&1 || break; i=$((i+1)); done; BASE="$(git merge-base origin/master HEAD 2>/dev/null || true)"; if [ -z "$BASE" ]; then exit 1; fi; git diff --quiet "$BASE" HEAD -- . ../README.md; fi'
[build.environment]
# Node version is intentionally not pinned here: Netlify auto-detects it
# from docs/.nvmrc, which is a symlink to the repo's single source of truth
# at superset-frontend/.nvmrc. Duplicating the version here previously let
# it drift out of sync (stuck on Node 20 after the repo moved to Node 24),
# breaking installs once a dependency required a newer Node engine.
# Node version matching docs/.nvmrc
NODE_VERSION = "20"
# Yarn version
YARN_VERSION = "1.22.22"
# Increase heap size for webpack bundling of Superset UI components
-139
View File
@@ -1,139 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"typescript",
"react"
],
"categories": {
"correctness": "off"
},
"env": {
"builtin": true,
"browser": true,
"node": true
},
"ignorePatterns": [
"build/**/*",
".docusaurus/**/*",
"node_modules/**/*"
],
"settings": {
"react": {
"version": "18.3.1"
}
},
"options": {
"typeAware": true
},
"rules": {
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-binary-expression": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-empty-static-block": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-new-native-nonconstructor": "error",
"no-nonoctal-decimal-escape": "error",
"no-obj-calls": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": "error",
"no-unused-labels": "error",
"no-unused-private-class-members": "error",
"no-unused-vars": "error",
"no-useless-backreference": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-with": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error",
"no-array-constructor": "error",
"no-unused-expressions": "error",
"typescript/ban-ts-comment": "error",
"typescript/no-duplicate-enum-values": "error",
"typescript/no-empty-object-type": "error",
"typescript/no-explicit-any": "error",
"typescript/no-extra-non-null-assertion": "error",
"typescript/no-misused-new": "error",
"typescript/no-namespace": "error",
"typescript/no-non-null-asserted-optional-chain": "error",
"typescript/no-require-imports": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-constraint": "error",
"typescript/no-unsafe-declaration-merging": "error",
"typescript/no-unsafe-function-type": "error",
"typescript/no-wrapper-object-types": "error",
"typescript/prefer-as-const": "error",
"typescript/prefer-namespace-keyword": "error",
"typescript/triple-slash-reference": "error"
},
"overrides": [
{
"files": [
"**/*.ts",
"**/*.tsx",
"**/*.mts",
"**/*.cts"
],
"rules": {
"constructor-super": "off",
"getter-return": "off",
"no-class-assign": "off",
"no-const-assign": "off",
"no-dupe-class-members": "off",
"no-dupe-keys": "off",
"no-func-assign": "off",
"no-import-assign": "off",
"no-new-native-nonconstructor": "off",
"no-obj-calls": "off",
"no-redeclare": "off",
"no-setter-return": "off",
"no-this-before-super": "off",
"no-unreachable": "off",
"no-unsafe-negation": "off",
"no-var": "error",
"no-with": "off",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error"
}
}
]
}
+17 -11
View File
@@ -29,7 +29,7 @@
"lint:db-metadata": "python3 ../superset/db_engine_specs/lint_metadata.py",
"lint:db-metadata:report": "python3 ../superset/db_engine_specs/lint_metadata.py --markdown -o ../superset/db_engine_specs/METADATA_STATUS.md",
"update:readme-db-logos": "node scripts/generate-database-docs.mjs --update-readme",
"lint": "oxlint --config oxlint.json",
"eslint": "eslint .",
"lint:docs-links": "node scripts/lint-docs-links.mjs",
"version:add": "node scripts/manage-versions.mjs add",
"version:remove": "node scripts/manage-versions.mjs remove",
@@ -43,7 +43,7 @@
"version:remove:components": "node scripts/manage-versions.mjs remove components"
},
"dependencies": {
"@ant-design/icons": "^6.3.4",
"@ant-design/icons": "^6.2.5",
"@docusaurus/core": "^3.10.2",
"@docusaurus/faster": "^3.10.2",
"@docusaurus/plugin-client-redirects": "^3.10.2",
@@ -61,12 +61,12 @@
"@storybook/addon-docs": "^10.5.10",
"@superset-ui/core": "^0.20.4",
"@swc/core": "^1.16.1",
"antd": "^6.6.2",
"baseline-browser-mapping": "^2.11.20",
"caniuse-lite": "^1.0.30001810",
"antd": "^6.6.1",
"baseline-browser-mapping": "^2.11.17",
"caniuse-lite": "^1.0.30001809",
"docusaurus-plugin-openapi-docs": "^5.2.0",
"docusaurus-theme-openapi-docs": "^5.2.0",
"js-yaml": "^5.4.1",
"js-yaml": "^5.3.0",
"json-bigint": "^1.0.0",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
@@ -85,13 +85,19 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.10.2",
"@docusaurus/tsconfig": "^3.10.2",
"@eslint/js": "^9.39.2",
"@types/js-yaml": "^4.0.9",
"@types/react": "^19.1.8",
"oxfmt": "^0.65.0",
"oxlint": "^1.80.0",
"oxlint-tsgolint": "^7.0.2001",
"typescript": "7.0.2",
"webpack": "^5.110.2"
"@typescript-eslint/eslint-plugin": "^8.67.0",
"@typescript-eslint/parser": "^8.67.0",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"globals": "^17.11.0",
"oxfmt": "^0.64.0",
"typescript": "~6.0.3",
"typescript-eslint": "^8.67.0",
"webpack": "^5.109.2"
},
"browserslist": {
"production": [
+1
View File
@@ -287,6 +287,7 @@ def add_missing_operation_ids(spec: dict[str, Any]) -> int:
TAG_DESCRIPTIONS = {
"Advanced Data Type": "Advanced data type operations and conversions.",
"Annotation Layers": "Manage annotation layers and annotations for charts.",
"AsyncEventsRestApi": "Real-time event streaming via Server-Sent Events (SSE).",
"Available Domains": "Get available domains for the Superset instance.",
"CSS Templates": "Manage CSS templates for custom dashboard styling.",
"CacheRestApi": "Cache management and invalidation operations.",
+1
View File
@@ -93,6 +93,7 @@ const CATEGORY_GROUPS = {
'User',
'Menu',
'Available Domains',
'AsyncEventsRestApi',
'OpenApi',
],
};
+6 -6
View File
@@ -98,6 +98,12 @@
"default": false,
"lifecycle": "development",
"description": "Enable Table V2 time comparison feature"
},
{
"name": "TAGGING_SYSTEM",
"default": false,
"lifecycle": "development",
"description": "Enables the tagging system for organizing assets"
}
],
"testing": [
@@ -234,12 +240,6 @@
"description": "Allow users to enable SSH tunneling when creating a DB connection. DB engine must support SSH Tunnels.",
"docs": "https://superset.apache.org/docs/configuration/setup-ssh-tunneling"
},
{
"name": "TAGGING_SYSTEM",
"default": true,
"lifecycle": "testing",
"description": "Enables the tagging system for organizing assets"
},
{
"name": "USE_ANALOGOUS_COLORS",
"default": false,
-51
View File
@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 238 55" style="enable-background:new 0 0 238 55;" xml:space="preserve">
<path d="M234.9,34.8c-0.9-1.3-2.2-2.2-3.6-2.8c-0.4-0.2-1.2-0.4-2.5-0.8c-1-0.3-1.9-0.6-2.8-1.1c-0.6-0.3-1.1-0.8-1.5-1.4
c-0.3-0.6-0.5-1.3-0.5-1.9c0-1,0.4-2,1.1-2.7c0.7-0.7,1.7-1.1,2.8-1.1c1.1-0.1,2.1,0.3,2.9,1c0.8,0.8,1.2,1.8,1.2,2.9h3.6
c0-2-0.7-3.9-2.1-5.3c-1.5-1.3-3.4-2-5.4-1.9c-2,0-4,0.7-5.5,2.1c-1.5,1.3-2.3,3.2-2.3,5.2c-0.1,1.5,0.4,3.1,1.3,4.3
c0.9,1.1,2.8,2.1,5.8,3.1c1.4,0.4,2.7,1.2,3.8,2.1c0.7,0.8,1.1,1.9,1.1,3c0,1.2-0.5,2.3-1.3,3.1c-0.9,0.8-2.1,1.3-3.3,1.2
c-1.4,0-2.6-0.6-3.5-1.6c-1-1.1-1.5-2.6-1.4-4.1V38h-3.5c-0.1,2.4,0.8,4.7,2.4,6.5c1.5,1.6,3.7,2.5,6,2.4c2.2,0.1,4.4-0.7,6.1-2.2
c1.6-1.4,2.5-3.5,2.4-5.6C236.2,37.6,235.8,36.1,234.9,34.8z"/>
<path d="M117,31.4c0.3-2.4,1.5-4.7,3.3-6.4c1.8-1.6,4.1-2.4,6.5-2.4c2.4-0.1,4.8,0.7,6.7,2.2c1.9,1.8,3.1,4.1,3.6,6.6L117,31.4z
M140.9,34.1c0-4.3-1.3-7.9-3.8-10.6s-6.1-4.2-9.8-4.1c-3.7-0.1-7.4,1.3-10,3.9s-4,6.2-3.9,9.9c-0.1,3.7,1.4,7.2,4,9.8
c2.5,2.7,6.1,4.1,9.8,4.1c2.7,0.1,5.4-0.8,7.7-2.3c2.3-1.7,4.1-4,5.2-6.7h-3.8c-0.8,1.7-2.1,3.2-3.7,4.2c-1.6,1-3.5,1.6-5.4,1.5
c-2.6,0.1-5.1-0.9-7-2.6c-1.9-1.8-3-4.3-3.1-6.9h24L140.9,34.1z"/>
<path d="M175.1,19.6c-1.9,0-3.7,0.4-5.4,1.2c-1.6,0.8-3,2-4,3.5c-0.9-1.5-2.2-2.6-3.8-3.4c-1.7-0.8-3.6-1.3-5.5-1.3
c-1.6,0-3.2,0.3-4.7,0.9c-1.4,0.6-2.6,1.5-3.6,2.6v-2.9h-3.4v26.2h3.3V33.6c0-1.6,0-3.2,0.2-4.8c0.2-0.9,0.4-1.7,0.9-2.4
c0.6-1.1,1.6-2,2.8-2.6c1.3-0.6,2.7-1,4.1-0.9c2.6,0,4.5,0.7,5.7,2.2c1.2,1.4,1.9,3.7,1.9,6.7v14.7h3.3V33.6c0-1.6,0-3.2,0.3-4.8
c0.2-0.9,0.5-1.7,0.9-2.4c0.6-1.1,1.6-2,2.7-2.6c1.2-0.6,2.6-1,4-0.9c2.6,0,4.5,0.7,5.7,2.2s1.7,3.9,1.7,7.4v13.9h3.3V33.1
c0-4.7-0.8-8.2-2.5-10.3S178.7,19.6,175.1,19.6z"/>
<path d="M193.1,31.4c0.3-2.4,1.5-4.7,3.3-6.4c1.8-1.6,4.1-2.4,6.5-2.4c2.4-0.1,4.9,0.7,6.8,2.2c1.9,1.8,3.1,4.1,3.5,6.6L193.1,31.4z
M216.9,34.1c0-4.3-1.3-7.9-3.8-10.6s-6.1-4.2-9.8-4.1c-3.7-0.1-7.4,1.3-10,3.9s-4,6.2-3.9,9.9c-0.1,3.7,1.4,7.2,4,9.8
c2.5,2.7,6.1,4.2,9.8,4.1c2.7,0,5.4-0.8,7.7-2.3c2.3-1.7,4.1-4,5.2-6.7h-3.8c-0.8,1.7-2.1,3.2-3.8,4.2c-1.6,1-3.5,1.5-5.4,1.5
c-2.6,0.1-5.2-0.9-7.1-2.6c-1.9-1.8-3-4.3-3.1-6.9h24L216.9,34.1L216.9,34.1z"/>
<path d="M108.4,20.7c-1.1,0.7-2,1.5-2.7,2.6v-3.1h-3.2v26.2h3.5V30.6c0-2.4,0.5-4.1,1.4-5.2c0.9-1.1,2.4-1.7,4.6-1.9v-3.7
C110.6,19.8,109.4,20.1,108.4,20.7z"/>
<path d="M74.6,31.4c0.3-2.4,1.5-4.7,3.3-6.4c1.8-1.6,4.1-2.4,6.5-2.4c2.4-0.1,4.8,0.7,6.7,2.2c1.9,1.8,3.1,4.1,3.5,6.6L74.6,31.4z
M98.5,34.1c0-4.3-1.3-7.9-3.8-10.6s-6.1-4.2-9.8-4.1c-3.7-0.1-7.4,1.3-10,3.9s-4,6.2-3.9,9.9c-0.1,3.7,1.4,7.2,4,9.8
c2.5,2.7,6.1,4.1,9.8,4.1c2.7,0,5.4-0.8,7.7-2.3c2.3-1.7,4.1-4,5.1-6.7h-3.8c-0.8,1.7-2.1,3.2-3.7,4.2s-3.5,1.5-5.4,1.5
c-2.6,0.1-5.1-0.9-7-2.6c-1.9-1.8-3-4.3-3.1-6.9h24L98.5,34.1z"/>
<path d="M47.4,11.7c-1.7-4-4.2-8.4-8.5-10.1C34.2,0,29.1,1,25.4,4.2c-2.7,2.4-4.3,6.1-4.8,11L20.4,16c-0.1,1.3-0.3,2.6-0.3,3.8
c-0.8-3.5-0.5-5.9-0.7-9.7c-0.8,0.4-1.2,1.2-1.2,2.1c0,0.2-1-0.4-1.2-0.3c-0.8,0.4-1.6-1-2-1.5c-0.2,0.4-0.4,0.7-0.7,1.1
C14,11,13.5,11,13,10.5c-0.4,1-1.6,0.5-2.7,0.5c0.6,1,0.8,2-0.1,2.4c0.1,0.1,0.9,0.3,0.9,0.5c-0.3,0.4-1.3,0-1.8-0.1v0.8
c-1.4-0.8-3.7-2.3-2.9,0.9c-1,0.2-0.5,0-0.5,0.9c-0.4,0.1-0.4,0.1-0.4,0.6c-1.2-0.6-2.2,4.9-2.1,6.5c0.9,0.2,1,0.5,1.5,1.4
c-0.7,0.3-1.1,1-1.6,1.3L4,26.6c-0.6,0.3-1.1,0.7-1.5,1.3c0.2-0.3,0.6,0.4,0.5,0.3L2.6,28c0.1,0.4,0.3,0.8,0.4,1.2
c-0.8,0.3-0.6,0.6-1.1,1.3c0.2,0,0.5,0.1,0.7,0.1c-0.4,0-0.5,1.9-0.4,2.2c0.2-0.4,0.6-0.9,0.8-1.4l0.5,0.5l-0.7,0.6
c1.8,0.5-0.2,1.7,0.6,3.1C3.6,34.8,4,34.7,4,33.8l0.4,0.3c-0.2,0.3-1.4,2.6-0.3,2.6c0.1,0,0.1,3.8,0.3,4.8C4.6,41.3,4.8,41,5,40.6
c0,0.6,0.3,0.8,0.1,1.5c1.8-0.5,1.1,0.5,0.4,1.4c0.6-0.1,1.2-0.3,1.8-0.5c0.8-0.3,0.1,1.1,0.4,1.1c0.3,0,1.2-1.8,1.4-2.1v0.6
c0.4-0.2,1.8-2,2-2c0.2,0.3,0.3,0.7,0.2,1.1c1.6-2,3-4.2,4-6.6c0.1,0.1,0.2,0.2,0.4,0.2c-0.2,0-4.1,7.7-4.1,8.1l0.9-0.2
c-0.3,0.6-0.5,1.2-0.6,1.8c1.6-0.2,0.9,1.2,0.9,2.5c1.3-0.8,3.2-1.3,2.5,0.9c0.4-0.2,0.9-0.4,1.3-0.6c-1.1,0.4,0.5,2.6,0.7,3.2
c0.2,0.7,2.3,0.2,3,0.4c0.7-1.1,1,0.2,1.2,1.3s1.4-0.6,1.9-0.4c0.4,0.1-0.3,2.7,1.2,1.6c0.6-0.5,0.8,0.4,1.7-0.5
c0,0,2.7,0.4,2.6,0.4c0.5-2.6,2.5-0.1,2.2-2.4h0.6c-0.1-2,2,0.9,2-2c0-0.7-1.6-1.5,0.4-0.9c-0.2-0.9,0.2-1.1-0.8-1.3
c-0.1-0.2-0.1-0.4,0.1-0.5c0.9,0,2.5,1.7,3.1,0.9c0.2-0.2-0.8-2-0.9-2.5c0.5,0.1,1.1-0.1,1.6,0c-0.9-0.5-0.3-0.6-1.4-0.9
c0.9-1.7,2.6-0.1,3.4-1.4c-1.7,0.3-2.6-3.3-1.5-3.6c-0.9-0.8-1.6-1-2.3-1.9c-1.8-2.3,1.7,0.4,2.3,0.9c0-0.4,0.2-0.9,0.2-1.3l0.9,0.8
c0-0.2,0.1-0.4,0.1-0.6c0.8,0.7,1.9,1,2.9,0.9c-0.2-0.5,0-0.6-0.2-1.1c0.8,0.2,1.3,0,2.2,0.1c-0.6-1.8,1.5-1.7,2.9-2.1
c2.3-0.7-1.4-1-1.6-1.2c-0.4-0.4,0.5-0.9,0.6-0.9s-0.8-1.1-0.6-0.8c-0.3-1-2.6-0.3-0.6-1.6c-0.9-0.2-2.1-0.4-2.1-1.6
c0.7-0.1,1.5-0.2,2.2-0.4c-0.5-0.4-0.9-1.1-1.5-1.4l0.5-0.2c-2.8-0.5,0.3-2.7,1-4c-1.9,0.4-2-1.1-3.7-1.3l0.8-0.7
c-0.9,0-1.9-0.1-2.8-0.3c0.2-1,0.9-1.9,1.9-2.2c-3.1-1.5-2.4-3.8-5.6-4.7l0.7-0.7c-0.9-1.3-1.6-0.4-2.6-0.7
c-2.1-0.6-1.9,2.3-1.9-1.1c0,0.4-0.5-0.4-0.6-0.6c-0.8,0.5-3.4,2.6-3.9,2.1C26.1,12,26,12.2,25.5,13c-0.3,0.3-1.3,0.9-1.1,0.5
c-0.6,0.9-0.6,4.3-1.3,6.6c0-1.2,0.2-2.4,0.3-3.6l0.1-0.9c0.4-4.1,1.7-7.1,3.8-8.9c2.8-2.4,6.7-3.1,10.3-2c3.4,1.2,5.5,5.4,7,9.1
L57,46.5h2.7l13.2-34.7h-3.5L58.5,41.2L47.4,11.7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

+9317 -7367
View File
File diff suppressed because it is too large Load Diff
+6 -23
View File
@@ -1,30 +1,14 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
// "extends": "@docusaurus/tsconfig",
// First compilerOptions section comes from above commented @docusaurus/tsconfig
// We moved them here to help with TS v7 migration so whenever Docusaurus readily supports TS v7,
// re-install @docusaurus/tsconfig and remove said section.
// Commented options are overriden in the next section.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"allowJs": true,
// "esModuleInterop": true,
// "jsx": "preserve",
"target": "ES2022",
"lib": ["ES2022", "DOM"],
// "moduleResolution": "bundler",
"module": "esnext",
"noEmit": true,
// "paths": {
// "@site/*": ["./*"]
// },
// "skipLibCheck": true,
"baseUrl": ".",
"ignoreDeprecations": "6.0",
"skipLibCheck": true,
"noImplicitAny": false,
"strict": false,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"moduleResolution": "node",
"resolveJsonModule": true,
"esModuleInterop": true,
"types": ["@docusaurus/module-type-aliases"],
@@ -39,10 +23,9 @@
// Runtime resolution uses webpack alias pointing to actual source (see src/webpack.extend.ts)
// Using /ui path matches the established pattern used throughout the Superset codebase
"@apache-superset/core/components": ["./src/types/apache-superset-core"],
"@site/*": ["./*"],
"*": ["./src/*", "./node_modules/*"]
"*": ["src/*", "node_modules/*"]
}
},
"include": ["./src/**/*.ts", "./src/**/*.tsx", "./src/**/*.d.ts"],
"exclude": ["./node_modules", "../superset-frontend/**/*", "src/shims/**"]
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"],
"exclude": ["node_modules", "../superset-frontend/**/*", "src/shims/**"]
}
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
:::
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
data. Knowing the installation counts for different Superset versions informs the project's
decisions about patching and long-term support. Scarf purges personally identifiable information
(PII) and provides only aggregated statistics.
@@ -135,7 +135,7 @@ init:
```
:::note
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
To opt-out of this data collection in your Helm-based installation, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
:::
@@ -71,17 +71,17 @@ Parses a JSON string into an object that can be used in your template.
---
#### `group`
#### `groupBy`
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by). The key is passed as a `by` hash argument.
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by).
```handlebars
{{#group data by="department"}}
{{#groupBy data 'department'}}
<h3>{{value}}</h3>
{{#each items}}
<p>{{this.name}}</p>
{{/each}}
{{/group}}
{{/groupBy}}
```
---
@@ -90,14 +90,6 @@ Groups an array of objects by a key, powered by [handlebars-group-by](https://gi
Superset also registers all helpers from the [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) library. These include a wide range of comparison, math, string, and conditional helpers. Commonly used ones include:
:::note
These names are specific to `just-handlebars-helpers` and differ from other
Handlebars helper libraries — notably `handlebars-helpers`, which spells the
math helpers `add`, `subtract`, `multiply` and `divide`. Calling a helper that
is not registered raises `Missing helper: "..."`, which renders the chart blank,
so it is worth checking a name against the tables below before using it.
:::
#### Comparison
| Helper | Description | Example |
@@ -105,7 +97,6 @@ so it is worth checking a name against the tables below before using it.
| `eq` | Strict equality | `{{#if (eq status "active")}}` |
| `eqw` | Weak equality | `{{#if (eqw count "5")}}` |
| `neq` | Strict inequality | `{{#if (neq role "admin")}}` |
| `neqw` | Weak inequality | `{{#if (neqw count "5")}}` |
| `lt` | Less than | `{{#if (lt score 50)}}` |
| `lte` | Less than or equal | `{{#if (lte score 100)}}` |
| `gt` | Greater than | `{{#if (gt price 0)}}` |
@@ -123,52 +114,25 @@ so it is worth checking a name against the tables below before using it.
#### String
| Helper | Description | Example |
| ----------------- | ----------------------------------------------- | ------------------------------ |
| `capitalizeFirst` | Capitalizes the first letter | `{{capitalizeFirst name}}` |
| `capitalizeEach` | Capitalizes the first letter of each word | `{{capitalizeEach title}}` |
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
| `excerpt` | Truncates to a length and appends an ellipsis | `{{excerpt description 100}}` |
| `sprintf` | printf-style formatting | `{{sprintf "%.1f" score}}` |
| `concat` | Concatenates values | `{{concat first " " last}}` |
| `join` | Joins an array with a separator | `{{join tags ", "}}` |
| `first` / `last` | First or last element of an array | `{{first items}}` |
| `newLineToBr` | Converts newlines to `<br>` (needs `{{{ }}}`) | `{{{newLineToBr notes}}}` |
| Helper | Description | Example |
| ------------ | ----------------------------------- | --------------------------------- |
| `capitalize` | Capitalizes first letter | `{{capitalize name}}` |
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
| `truncate` | Truncates a string | `{{truncate description 100}}` |
| `contains` | Checks if string contains substring | `{{#if (contains tag "urgent")}}` |
#### Math
| Helper | Description | Example |
| ---------------- | ----------------------- | ------------------------------------ |
| `sum` | Addition | `{{sum a b}}` |
| `difference` | Subtraction | `{{difference total discount}}` |
| `multiplication` | Multiplication | `{{multiplication price quantity}}` |
| `division` | Division | `{{division total count}}` |
| `remainder` | Modulo | `{{remainder index 2}}` |
| `abs` | Absolute value | `{{abs delta}}` |
| `ceil` | Ceiling | `{{ceil value}}` |
| `floor` | Floor | `{{floor value}}` |
`sum` takes exactly two arguments — it adds a pair of numbers and does not total
an array. There is no `round` helper; use `{{sprintf "%.0f" value}}` to round to
a given number of decimal places.
#### Arrays
| Helper | Description | Example |
| ---------- | ---------------------------------- | --------------------------------- |
| `includes` | Whether an array contains a value | `{{#if (includes tags "urgent")}}` |
| `empty` | Whether an array is empty | `{{#if (empty rows)}}` |
| `count` | Number of items in an array | `{{count rows}}` |
`includes` tests array membership. It returns `false` for a string, so it cannot
be used to check for a substring.
#### Formatting
| Helper | Description | Example |
| ---------------- | ---------------------------- | -------------------------------- |
| `formatCurrency` | Formats a number as currency | `{{formatCurrency revenue "$"}}` |
| Helper | Description | Example |
| ---------- | -------------- | ----------------------------- |
| `add` | Addition | `{{add a b}}` |
| `subtract` | Subtraction | `{{subtract total discount}}` |
| `multiply` | Multiplication | `{{multiply price quantity}}` |
| `divide` | Division | `{{divide total count}}` |
| `ceil` | Ceiling | `{{ceil value}}` |
| `floor` | Floor | `{{floor value}}` |
| `round` | Round | `{{round value}}` |
For the full list of available helpers, see the [just-handlebars-helpers documentation](https://github.com/leapfrogtechnology/just-handlebars-helpers).
+1539 -583
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -29,7 +29,7 @@ maintainers:
- name: craig-rueda
email: craig@craigrueda.com
url: https://github.com/craig-rueda
version: 0.22.7 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
version: 0.22.6 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
dependencies:
- name: postgresql
version: 16.7.27
+1 -1
View File
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
# superset
![Version: 0.22.7](https://img.shields.io/badge/Version-0.22.7-informational?style=flat-square)
![Version: 0.22.6](https://img.shields.io/badge/Version-0.22.6-informational?style=flat-square)
Apache Superset is a modern, enterprise-ready business intelligence web application
+1 -4
View File
@@ -112,10 +112,7 @@ extraEnv: {}
# GUNICORN_KEEPALIVE: 2
# SERVER_LIMIT_REQUEST_LINE: 0
# SERVER_LIMIT_REQUEST_FIELD_SIZE: 0
# See: https://superset.apache.org/docs/configuration/event-logging/#statsd-logging
# SERVER_STATSD_HOST: localhost
# SERVER_STATSD_PORT: 8125
# SERVER_STATSD_PREFIX: superset
# OAUTH_HOME_DOMAIN: ..
# # If a whitelist is not set, any address that can use your OAuth2 endpoint will be able to login.
# # this includes any random Gmail address if your OAuth2 Web App is set to External.
+13 -16
View File
@@ -44,17 +44,17 @@ dependencies = [
# without the ``base.txt`` lock file (#40962).
"cachetools>=7.1.7, <8",
"celery>=5.6.3, <6.0.0",
"click>=8.5.0",
"click>=8.4.2",
"click-option-group",
"colorama",
"flask-cors>=6.0.5, <7.0",
"croniter>=6.2.4",
"cron-descriptor",
"cryptography>=50.0.1, <51.0.0",
"cryptography>=50.0.0, <51.0.0",
"deprecation>=2.1.0, <2.2.0",
"flask>=2.2.5, <4.0.0",
"flask-appbuilder>=5.2.2, <6.0.0",
"flask-caching>=2.5.0, <3",
"flask-caching>=2.4.1, <3",
"flask-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0",
"flask-login>=0.6.0, < 1.0",
@@ -82,8 +82,8 @@ dependencies = [
# https://github.com/apache/superset/issues/33162
"marshmallow>=3.0, <5",
"marshmallow-union>=0.1.15.post1",
"msgpack>=1.2.2, <1.3",
"nh3>=0.3.7, <0.4",
"msgpack>=1.2.0, <1.3",
"nh3>=0.3.5, <0.4",
"numpy>=1.23.5, <2.5",
"packaging",
# --------------------------
@@ -96,7 +96,7 @@ dependencies = [
"pgsanity",
"Pillow>=12.3.0, <13", # raise floor to match resolved pin; closes SCA false-positive on 11.x-range CVEs already fixed in 12.3.0
"polyline>=2.0.4, <3.0",
"pydantic>=2.13.5",
"pydantic>=2.8.0",
"pyparsing>=3.3.2, <4",
"python-dateutil",
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
@@ -109,14 +109,11 @@ dependencies = [
"shillelagh[gsheetsapi]>=1.4.5, <2.0",
"sshtunnel>=0.4.0, <0.5",
"simplejson>=4.1.2",
"slack_sdk>=3.44.0, <4",
"simplejson>=4.1.1",
"slack_sdk>=3.43.0, <4",
"sqlalchemy>=2.0.52, <2.1",
"sqlalchemy-continuum>=1.6.0, <2.0.0",
"sqlalchemy-utils>=0.42.1, <0.43", # expanding lowerbound to work with pydoris
# Dialect-specific gaps/bugs against this pin are worked around in
# superset/sql/dialects/ (e.g. starrocks.py); check there for anything
# that can be cleaned up when bumping
"sqlglot>=30.17.0, <31", # 30.16.0 adds Trino inline UDF IF/CASE routine statement parsing
# newer pandas needs 0.9+
"tabulate>=0.10.0, <1.0",
@@ -138,11 +135,11 @@ athena = ["pyathena[pandas]>=3.35.4, <4"]
# superset/db_engine_specs/aurora.py's known_incompatibilities metadata.
aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
bigquery = [
"pandas-gbq>=0.35.2",
"pandas-gbq>=0.35.1",
# 1.17.1 is likely the final release: googleapis/python-bigquery-sqlalchemy
# was archived 2026-05-16. Both 1.17.0 and 1.17.1 support SQLAlchemy 1.4/2.0.
"sqlalchemy-bigquery>=1.17.2",
"google-cloud-bigquery>=3.44.0",
"google-cloud-bigquery>=3.42.3",
]
clickhouse = ["clickhouse-connect>=1.7.2, <2.0"]
# The `cockroachdb` PyPI package (last released 2021) is abandoned and its
@@ -213,7 +210,7 @@ firebird = ["sqlalchemy-firebird>=2.2.0"]
firebolt = ["firebolt-sqlalchemy>=1.1.2, <2"]
gevent = ["gevent>=26.8.0"]
gsheets = ["shillelagh[gsheetsapi]>=1.4.5, <2"]
hana = ["hdbcli==2.29.27", "sqlalchemy_hana==3.0.3"]
hana = ["hdbcli==2.29.25", "sqlalchemy_hana==3.0.3"]
hive = [
"pyhive[hive_pure_sasl]>=0.7.0",
"tableschema",
@@ -273,7 +270,7 @@ tdengine = [
"taospy>=2.8.10",
"taos-ws-py>=0.7.0"
]
teradata = ["teradatasql>=20.0.0.67"]
teradata = ["teradatasql>=20.0.0.66"]
thumbnails = [] # deprecated, will be removed in 7.0
vertica = ["sqlalchemy-vertica-python>= 0.6.3, < 0.7"]
netezza = ["nzalchemy>= 11.1.2, < 11.2"]
@@ -288,7 +285,7 @@ development = [
"docker",
"flask-testing",
"freezegun",
"grpcio>=1.83.1",
"grpcio>=1.82.1",
"openapi-spec-validator",
"parameterized",
"pip",
+1 -1
View File
@@ -26,7 +26,7 @@ filelock>=3.20.3,<4.0.0
brotli>=1.2.0,<2.0.0
numexpr>=2.9.0
# Security: CVE-2026-34073 (MEDIUM) - Improper Certificate Validation
cryptography>=50.0.1,<51.0.0
cryptography>=50.0.0,<51.0.0
# Security: Snyk - XSS vulnerability in Mako templates
mako>=1.4.1,<2.0.0
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
+12 -13
View File
@@ -40,7 +40,7 @@ brotli==1.2.0
# via
# -r requirements/base.in
# flask-compress
cachelib==0.17.0
cachelib==0.13.0
# via
# flask-caching
# flask-session
@@ -58,7 +58,7 @@ cffi==2.0.0
# pynacl
charset-normalizer==3.4.2
# via requests
click==8.5.0
click==8.4.2
# via
# apache-superset (pyproject.toml)
# celery
@@ -84,7 +84,7 @@ cron-descriptor==1.4.5
# via apache-superset (pyproject.toml)
croniter==6.2.4
# via apache-superset (pyproject.toml)
cryptography==50.0.1
cryptography==50.0.0
# via
# -r requirements/base.in
# apache-superset (pyproject.toml)
@@ -105,7 +105,7 @@ et-xmlfile==2.0.0
# via openpyxl
filelock==3.20.3
# via -r requirements/base.in
flask==3.1.3
flask==2.3.3
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
@@ -124,9 +124,9 @@ flask-appbuilder==5.2.2
# via
# apache-superset (pyproject.toml)
# apache-superset-core
flask-babel==4.0.0
flask-babel==3.1.0
# via flask-appbuilder
flask-caching==2.5.0
flask-caching==2.4.1
# via apache-superset (pyproject.toml)
flask-compress==1.24
# via apache-superset (pyproject.toml)
@@ -218,7 +218,6 @@ markdown-it-py==3.0.0
# via rich
markupsafe==3.0.2
# via
# flask
# jinja2
# mako
# werkzeug
@@ -237,11 +236,11 @@ marshmallow-union==0.1.15.post1
# via apache-superset (pyproject.toml)
mdurl==0.1.2
# via markdown-it-py
msgpack==1.2.2
msgpack==1.2.1
# via apache-superset (pyproject.toml)
msgspec==0.19.0
# via flask-session
nh3==0.3.7
nh3==0.3.6
# via apache-superset (pyproject.toml)
numexpr==2.10.2
# via -r requirements/base.in
@@ -298,11 +297,11 @@ pyasn1-modules==0.4.2
# via google-auth
pycparser==2.22
# via cffi
pydantic==2.13.5
pydantic==2.13.4
# via
# apache-superset (pyproject.toml)
# apache-superset-core
pydantic-core==2.46.5
pydantic-core==2.46.4
# via pydantic
pygeohash==3.2.2
# via apache-superset (pyproject.toml)
@@ -371,7 +370,7 @@ setuptools==84.0.0
# via -r requirements/base.in
shillelagh==1.4.5
# via apache-superset (pyproject.toml)
simplejson==4.1.2
simplejson==4.1.1
# via apache-superset (pyproject.toml)
six==1.17.0
# via
@@ -379,7 +378,7 @@ six==1.17.0
# python-dateutil
# rfc3339-validator
# wtforms-json
slack-sdk==3.44.1
slack-sdk==3.43.0
# via apache-superset (pyproject.toml)
sqlalchemy==2.0.52
# via
+15 -16
View File
@@ -94,7 +94,7 @@ brotli==1.2.0
# via
# -c requirements/base-constraint.txt
# flask-compress
cachelib==0.17.0
cachelib==0.13.0
# via
# -c requirements/base-constraint.txt
# flask-caching
@@ -131,7 +131,7 @@ charset-normalizer==3.4.2
# via
# -c requirements/base-constraint.txt
# requests
click==8.5.0
click==8.4.2
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -179,7 +179,7 @@ croniter==6.2.4
# via
# -c requirements/base-constraint.txt
# apache-superset
cryptography==50.0.1
cryptography==50.0.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -247,7 +247,7 @@ filelock==3.20.3
# via
# -c requirements/base-constraint.txt
# virtualenv
flask==3.1.3
flask==2.3.3
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -269,11 +269,11 @@ flask-appbuilder==5.2.2
# -c requirements/base-constraint.txt
# apache-superset
# apache-superset-core
flask-babel==4.0.0
flask-babel==3.1.0
# via
# -c requirements/base-constraint.txt
# flask-appbuilder
flask-caching==2.5.0
flask-caching==2.4.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -360,7 +360,7 @@ google-auth-oauthlib==1.2.1
# via
# pandas-gbq
# pydata-google-auth
google-cloud-bigquery==3.45.0
google-cloud-bigquery==3.43.0
# via
# apache-superset
# pandas-gbq
@@ -384,7 +384,7 @@ greenlet==3.5.5
# sqlalchemy
griffelib==2.0.2
# via fastmcp-slim
grpcio==1.83.1
grpcio==1.83.0
# via
# apache-superset
# google-api-core
@@ -524,7 +524,6 @@ markdown-it-py==3.0.0
markupsafe==3.0.2
# via
# -c requirements/base-constraint.txt
# flask
# jinja2
# mako
# werkzeug
@@ -560,7 +559,7 @@ more-itertools==10.8.0
# via
# jaraco-classes
# jaraco-functools
msgpack==1.2.2
msgpack==1.2.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -570,7 +569,7 @@ msgspec==0.19.0
# flask-session
mysqlclient==2.2.8
# via apache-superset
nh3==0.3.7
nh3==0.3.6
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -640,7 +639,7 @@ pandas==2.3.3
# db-dtypes
# pandas-gbq
# prophet
pandas-gbq==0.35.2
pandas-gbq==0.35.1
# via apache-superset
parameterized==0.9.0
# via apache-superset
@@ -732,7 +731,7 @@ pycparser==2.22
# via
# -c requirements/base-constraint.txt
# cffi
pydantic==2.13.5
pydantic==2.13.4
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -741,7 +740,7 @@ pydantic==2.13.5
# mcp
# openapi-pydantic
# pydantic-settings
pydantic-core==2.46.5
pydantic-core==2.46.4
# via
# -c requirements/base-constraint.txt
# pydantic
@@ -933,7 +932,7 @@ shillelagh==1.4.5
# via
# -c requirements/base-constraint.txt
# apache-superset
simplejson==4.1.2
simplejson==4.1.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -944,7 +943,7 @@ six==1.17.0
# python-dateutil
# rfc3339-validator
# wtforms-json
slack-sdk==3.44.1
slack-sdk==3.43.0
# via
# -c requirements/base-constraint.txt
# apache-superset
+3 -3
View File
@@ -289,11 +289,11 @@ function extractArgs(args, regexes) {
* For example: `superset-frontend/foo/bar.ts` -> `foo/bar.ts`
*
* @param {string[]} args
* @param {string} packageName
* @param {string} package
* @returns {string[]}
*/
function removePackageSegment(args, packageName) {
const packageSegment = packageName.concat(sep);
function removePackageSegment(args, package) {
const packageSegment = package.concat(sep);
return args.map((arg) => {
const normalizedPath = normalize(arg);
-56
View File
@@ -1,56 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Format the passed files with oxfmt, from within an npm workspace.
#
# Usage: scripts/oxfmt.sh <workspace-dir> [file...]
#
# Paths are passed in repo-relative (as pre-commit provides them) and rewritten
# relative to the workspace, since oxfmt resolves its config from the working
# directory.
set -e
workspace_dir="$1"
shift
if [[ -z "$workspace_dir" ]]; then
echo "Error: no workspace directory given" >&2
exit 1
fi
script_dir="$(dirname "$(realpath "$0")")"
root_dir="$(dirname "$script_dir")"
if [[ ! -d "$root_dir/$workspace_dir" ]]; then
echo "Error: $workspace_dir directory not found in $root_dir" >&2
exit 1
fi
cd "$root_dir/$workspace_dir"
files=()
for file in "$@"; do
files+=("${file#$workspace_dir/}")
done
if [ ${#files[@]} -eq 0 ]; then
echo "No files to format"
exit 0
fi
npx oxfmt --write --no-error-on-unmatched-pattern -- "${files[@]}"
@@ -146,8 +146,6 @@ class Operator(str, enum.Enum):
NOT_IN = "NOT IN"
LIKE = "LIKE"
NOT_LIKE = "NOT LIKE"
ILIKE = "ILIKE"
NOT_ILIKE = "NOT ILIKE"
IS_NULL = "IS NULL"
IS_NOT_NULL = "IS NOT NULL"
ADHOC = "ADHOC"
@@ -23,7 +23,6 @@ from superset_core.tasks.types import TaskContext, TaskScope
if TYPE_CHECKING:
from superset_core.tasks.models import Task
from superset_core.tasks.subscription import TaskSubscriptionPolicy
P = ParamSpec("P")
R = TypeVar("R")
@@ -33,7 +32,6 @@ def task(
name: str | None = None,
scope: TaskScope = TaskScope.PRIVATE,
timeout: int | None = None,
subscription_policy: "TaskSubscriptionPolicy | None" = None,
) -> Callable[[Callable[P, R]], "TaskWrapper[P]"]:
"""
Decorator to register a task.
@@ -48,13 +46,6 @@ def task(
:param timeout: Optional timeout in seconds. When the timeout is reached,
abort handlers are triggered if registered. Can be overridden
at call time via TaskOptions(timeout=...).
:param subscription_policy: Optional per-client subscription policy. The
framework subscribes tasks at principal grain (one row per
user/guest); a policy refines that with a finer per-client
grain (e.g. one browser tab) so a cancel from one client does
not abort a SHARED task another client of the same principal
is still awaiting. See
``superset_core.tasks.subscription.TaskSubscriptionPolicy``.
:returns: TaskWrapper with .schedule() method
Note:
@@ -120,29 +120,11 @@ class Task(CoreModel):
"""
raise NotImplementedError("Property will be replaced during initialization")
@property
def properties_dict(self) -> "TaskProperties":
"""
Get the parsed properties as a sparse ``TaskProperties`` dict.
The canonical read accessor for runtime state and execution config
(progress, error info, the internal ``private`` bucket). Always use
``.get()`` since only explicitly-set keys are present.
Host implementations will replace this property during initialization.
:returns: Parsed ``TaskProperties`` dict
"""
raise NotImplementedError("Property will be replaced during initialization")
def update_properties(self, updates: "TaskProperties") -> None:
"""
Update specific properties fields (merge semantics).
Only updates fields present in the updates dict. The ``private`` subtree
is merged recursively (its ``framework``, ``task`` and ``subscription``
namespaces merge independently), so a write to one namespace never
clobbers the others.
Only updates fields present in the updates dict.
Host implementations will replace this method during initialization.
@@ -153,23 +135,6 @@ class Task(CoreModel):
"""
raise NotImplementedError("Method will be replaced during initialization")
def update_task_private(self, updates: dict[str, Any]) -> None:
"""
Merge keys into the task-owned ``private["task"]`` namespace.
The freeform, task-type-specific internal namespace (isolated from the
framework-owned ``private["framework"]`` keys) for handles a task type
needs to persist but that are not task output e.g. an engine query
cancel handle. A subscription policy's per-client bookkeeping belongs in
the separate ``private["subscription"]`` namespace instead. Never
surfaced to user-facing API payloads except in debug mode.
Host implementations will replace this method during initialization.
:param updates: Keys to merge into ``private["task"]``
"""
raise NotImplementedError("Method will be replaced during initialization")
class TaskSubscriber(CoreModel):
"""
@@ -180,9 +145,7 @@ class TaskSubscriber(CoreModel):
This model tracks task subscriptions for multi-user shared tasks. When a user
schedules a shared task with the same parameters as an existing task,
they are subscribed to that task instead of creating a duplicate. A subscriber
is identified by exactly one of ``user_id`` (authenticated) or ``guest_key``
(an embedded guest, which has no ``ab_user`` row).
they are subscribed to that task instead of creating a duplicate.
"""
__abstract__ = True
@@ -190,8 +153,7 @@ class TaskSubscriber(CoreModel):
# Type hints for expected attributes (no actual field definitions)
id: int
task_id: int
user_id: int | None
guest_key: str | None
user_id: int
subscribed_at: datetime
# Audit fields from AuditMixinNullable
@@ -199,30 +161,3 @@ class TaskSubscriber(CoreModel):
changed_on: datetime | None
created_by_fk: int | None
changed_by_fk: int | None
class TaskDependency(CoreModel):
"""
Abstract TaskDependency model interface.
Host implementations will replace this class during initialization
with concrete implementation providing actual functionality.
This model represents a directed edge in the task dependency graph (DAG):
the task identified by ``task_id`` depends on the prerequisite task
identified by ``depends_on_task_id``. A task only runs once all of its
prerequisites have reached a terminal SUCCESS.
"""
__abstract__ = True
# Type hints for expected attributes (no actual field definitions)
id: int
task_id: int # The dependent task
depends_on_task_id: int # The prerequisite task
# Audit fields from AuditMixinNullable
created_on: datetime | None
changed_on: datetime | None
created_by_fk: int | None
changed_by_fk: int | None
@@ -1,140 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Task-type subscription policies for the Global Task Framework (GTF).
The framework's own subscription model is **principal-oriented**: a task has one
subscriber row per principal (an authenticated user, or an embedded guest keyed
by a token-derived identity), and cancel/abort decisions are made from that
principal-grain subscriber count. That model and everything built on it
(``TaskFilter`` visibility, ``subscriber_count``, ``raise_for_access``) is
intentionally kept free of any finer notion of "who exactly is watching".
Some task types need a finer grain than the principal. The canonical case is
async chart-data: a single ``SHARED`` task is deduplicated across every request
for the same ``query_cache_key``, so one user watching it from **two browser
tabs** is still a single principal. If either tab's "cancel" (an explicit cancel
or a navigate-away teardown) were treated as *the* principal leaving, it would
abort the shared task and kill the other tab's still-pending query.
A **subscription policy** lets a task type refine this without the framework
knowing anything about tabs (or any other per-client grain). A task registers a
policy on its :func:`superset_core.tasks.decorators.task` decorator; the
framework invokes it, under the same lock that serializes submit/cancel, at two
points:
- **on subscribe** after the framework has ensured the principal's subscriber
row (create or dedup-join). The policy records the calling client.
- **on unsubscribe** when a principal cancels. The policy drops the calling
client and returns whether the principal has *any client left*. ``False`` means
"one client detached, keep the principal subscribed and the task running";
``True`` means "the principal's last client is gone" and the framework then
applies its normal principal-grain rule (unsubscribe the principal, and abort
if it was the last subscriber).
A task type with no policy behaves exactly as before (principal-grain). The
policy owns its own bookkeeping the chart-data policy, for instance, stores
its per-tab set in the task's ``private["subscription"]`` namespace (see
:class:`superset_core.tasks.types.PrivateProperties`), which the framework never
inspects. ``client_ref`` is an opaque, client-supplied identifier (e.g. a
browser-tab id); it is **not** an authorization token the framework has
already authorized the calling principal before the policy runs, and the policy
only ever records/removes entries scoped to that principal.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from superset_core.tasks.models import Task
class TaskSubscriptionPolicy(ABC):
"""Per-client subscription refinement for a task type (see module docstring).
Register an instance on the ``@task`` decorator
(``@task(..., subscription_policy=MyPolicy())``). Both hooks run in the web
request process, inside the distributed lock that serializes concurrent
submit/cancel for the task, so an implementation may safely read-modify-write
task state (e.g. a list in ``private["subscription"]``) without additional locking.
"""
@abstractmethod
def on_subscribe(
self,
task: "Task",
*,
principal: str,
client_ref: str | None,
) -> None:
"""Record that ``client_ref`` (a client of ``principal``) joined ``task``.
Called after the framework has ensured ``principal``'s subscriber row.
Should be idempotent: the same ``(principal, client_ref)`` may be
submitted more than once (e.g. a resubmit from the same tab).
:param task: the task being subscribed to
:param principal: the calling principal's stable routing id
(``user:<id>`` for a user, the guest key for an embedded guest)
:param client_ref: the opaque per-client id (e.g. a browser-tab id), or
``None`` when the caller supplied none (the policy should then no-op,
preserving principal-grain behavior)
"""
@abstractmethod
def on_unsubscribe(
self,
task: "Task",
*,
principal: str,
client_ref: str | None,
) -> bool:
"""Drop ``client_ref`` and report whether ``principal`` has any client left.
Called when ``principal`` cancels the task.
:param task: the task being cancelled
:param principal: the calling principal's stable routing id
:param client_ref: the opaque per-client id being removed, or ``None``
:returns: ``True`` if the framework should proceed to unsubscribe
``principal`` (its last client is gone, or the caller supplied no
``client_ref``); ``False`` to keep ``principal`` subscribed because it
still has other clients on this task (a single client detached).
"""
def routing_channels(self, task: "Task") -> list[str] | None:
"""Realtime websocket routing keys for this task's status fanout.
Lets a task type deliver ``task-status`` at a finer grain than the
principal e.g. only to the specific browser tab watching the task,
rather than every tab the principal has open. Returns the list of opaque
routing keys the realtime transport should target (it prefixes each with
``realtime:`` and never parses them); the caller delivers to exactly those
keys.
Return ``None`` (the default) to keep principal-grain fanout the
framework then derives one key per subscriber principal. A concrete policy
that manages per-client keys should also return ``None`` (not an empty
list) when it currently has no keys, so fanout falls back to
principal-grain rather than silently delivering to no one.
:param task: the task whose status is being published
:returns: the routing keys to target, or ``None`` for principal-grain
"""
return None
+2 -85
View File
@@ -20,11 +20,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Literal, TYPE_CHECKING, TypedDict, Union
from uuid import UUID
if TYPE_CHECKING:
from superset_core.tasks.models import Task
from typing import Any, Callable, Literal, TypedDict
class TaskStatus(str, Enum):
@@ -81,60 +77,13 @@ class TaskProperties(TypedDict, total=False):
progress_percent: float
progress_current: int
progress_total: int
dedupe_count: int
# Error info - set when task fails. ``error_message`` is the consumer-facing
# failure reason (public); the exception class and traceback are internal
# debug detail and live under ``private["framework"]`` instead.
# Error info - set when task fails
error_message: str
# Internal runtime state, surfaced to user-facing API payloads only in debug
# mode (the Task REST API strips this key otherwise). Holds framework/task
# plumbing rather than task output. See ``PrivateProperties``.
private: "PrivateProperties"
class FrameworkPrivateProperties(TypedDict, total=False):
"""Framework-owned internal task state, under ``private["framework"]``.
Named keys written *only* by the framework, common to every task type: the
Celery job id the orphan reaper revokes, and error-debug detail (exception
class + traceback). Isolated from task-owned keys so a task type can never
clobber them. Task-execution handles specific to one kind of task (e.g. a
warehouse-query cancel handle) belong in the freeform ``task`` namespace, not
here.
"""
celery_task_id: str
exception_type: str
stack_trace: str
class PrivateProperties(TypedDict, total=False):
"""Internal task runtime state, stored under ``TaskProperties["private"]``.
Never surfaced to user-facing API payloads except in debug mode; distinct
from task output, which belongs in the task's ``payload``. Split into three
structurally isolated namespaces so a task type's freeform key can never
collide with a framework orchestration key or a subscription policy's
bookkeeping:
- ``framework``: named framework-owned keys, common to all tasks (see
``FrameworkPrivateProperties``).
- ``task``: freeform, task-type-specific internal handles, written only by
task/execution code. E.g. the chart-data query task stores its engine
cancel handle here (``cancel_query_id`` / ``cancel_database_id``).
- ``subscription``: freeform bookkeeping owned by the task type's
``SubscriptionPolicy`` (see ``superset_core.tasks.subscription``), written
only through the policy hooks. E.g. the chart-data policy stores its
per-client consumer list here. The framework never inspects it.
"""
framework: "FrameworkPrivateProperties"
task: dict[str, Any]
subscription: dict[str, Any]
@dataclass(frozen=True)
class TaskOptions:
"""
@@ -173,24 +122,11 @@ class TaskOptions:
task = long_task.schedule(
options=TaskOptions(timeout=600) # 10 minute timeout
)
# Task that waits for prerequisite tasks to succeed before running.
# Pass the scheduled Task objects (canonical); UUIDs are also accepted.
parent = parent_task.schedule()
task = dependent_task.schedule(
options=TaskOptions(depends_on=[parent])
)
"""
task_key: str | None = None
task_name: str | None = None
timeout: int | None = None # Timeout in seconds
# Prerequisite tasks this task depends on. Each entry may be a scheduled
# Task, its UUID, or a UUID string. The task only runs once every
# prerequisite has reached a terminal SUCCESS; if any prerequisite ends in a
# non-SUCCESS terminal state the task fails without running (all_success
# semantics).
depends_on: list[Union["Task", UUID, str]] | None = None
class TaskContext(ABC):
@@ -210,8 +146,6 @@ class TaskContext(ABC):
self,
progress: float | int | tuple[int, int] | None = None,
payload: dict[str, Any] | None = None,
*,
immediate: bool = False,
) -> None:
"""
Update task progress and/or payload atomically.
@@ -219,11 +153,6 @@ class TaskContext(ABC):
All parameters are optional. Payload is merged with existing data,
not replaced. All updates occur in a single database transaction.
Writes are throttled by default to protect the database from eager
tasks. Pass ``immediate=True`` to force a synchronous write, bypassing
throttling, when a downstream consumer must observe this update as soon
as the task completes (e.g. a dependent task reading a published value).
Progress can be specified in three ways:
- float (0.0-1.0): Percentage only, e.g., 0.5 means 50%
- int: Count only (total unknown), e.g., 42 means "42 items processed"
@@ -232,7 +161,6 @@ class TaskContext(ABC):
:param progress: Progress value, or None to leave unchanged
:param payload: Payload data to merge (dict), or None to leave unchanged
:param immediate: When True, write synchronously and bypass throttling
Examples:
# Percentage only - displays as "In progress: 50 %"
@@ -255,17 +183,6 @@ class TaskContext(ABC):
"""
...
@abstractmethod
def get_dependency_payloads(self) -> list[dict[str, Any]]:
"""
Return payloads published by prerequisite tasks.
The payloads are returned in dependency edge order. They let dependent
task code consume small pieces of output metadata from tasks that have
already satisfied the DAG all-success gate.
"""
...
@abstractmethod
def on_cleanup(self, handler: Callable[[], None]) -> Callable[[], None]:
"""
+2261 -2108
View File
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -37,18 +37,18 @@
"jwt-decode": "^4.0.0"
},
"devDependencies": {
"@babel/cli": "^8.0.1",
"@babel/core": "^8.0.1",
"@babel/preset-env": "^8.0.1",
"@babel/preset-typescript": "^8.0.1",
"@types/node": "^26.4.0",
"babel-loader": "^10.1.1",
"jsdom": "^30.0.1",
"@babel/cli": "^7.29.7",
"@babel/core": "^7.29.7",
"@babel/preset-env": "^7.29.7",
"@babel/preset-typescript": "7.29.7",
"@types/node": "^25.4.0",
"babel-loader": "^9.1.3",
"jsdom": "^26.1.0",
"tscw-config": "^1.1.2",
"typescript": "^7.0.2",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
"webpack": "^5.110.2",
"webpack-cli": "^7.2.3"
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4"
},
"repository": {
"type": "git",
-2
View File
@@ -1,7 +1,5 @@
{
"compilerOptions": {
"rootDir": "./src",
// syntax rules
"strict": true,
+48 -61
View File
@@ -3163,15 +3163,11 @@
]
},
"node_modules/baseline-browser-mapping": {
"version": "2.11.20",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz",
"integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==",
"license": "Apache-2.0",
"version": "2.9.19",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
"integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/bcrypt-pbkdf": {
@@ -3213,9 +3209,9 @@
}
},
"node_modules/browserslist": {
"version": "4.28.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
"integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"funding": [
{
"type": "opencollective",
@@ -3230,13 +3226,12 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.11.12",
"caniuse-lite": "^1.0.30001809",
"electron-to-chromium": "^1.5.402",
"node-releases": "^2.0.53",
"update-browserslist-db": "^1.3.0"
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -3330,9 +3325,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001810",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
"version": "1.0.30001769",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"funding": [
{
"type": "opencollective",
@@ -3346,8 +3341,7 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
]
},
"node_modules/caseless": {
"version": "0.12.0",
@@ -4003,10 +3997,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.418",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.418.tgz",
"integrity": "sha512-UzS26r3AEbG5wSoGVpJKqwHIU9zwQN7LHdVIThDrJpS0I5KdlXFMEb8543fhc9dVnIIAST6ar8rhwa00AL5MlA==",
"license": "ISC"
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
"integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -4115,7 +4108,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
@@ -6638,13 +6630,9 @@
}
},
"node_modules/node-releases": {
"version": "2.0.54",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
"integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
"license": "MIT",
"engines": {
"node": ">=18"
}
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="
},
"node_modules/npm-run-path": {
"version": "4.0.1",
@@ -8356,9 +8344,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
"integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"funding": [
{
"type": "opencollective",
@@ -8373,7 +8361,6 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
@@ -11131,9 +11118,9 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"baseline-browser-mapping": {
"version": "2.11.20",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz",
"integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw=="
"version": "2.9.19",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
"integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
@@ -11169,15 +11156,15 @@
}
},
"browserslist": {
"version": "4.28.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
"integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"requires": {
"baseline-browser-mapping": "^2.11.12",
"caniuse-lite": "^1.0.30001809",
"electron-to-chromium": "^1.5.402",
"node-releases": "^2.0.53",
"update-browserslist-db": "^1.3.0"
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
}
},
"buffer-crc32": {
@@ -11237,9 +11224,9 @@
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caniuse-lite": {
"version": "1.0.30001810",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg=="
"version": "1.0.30001769",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="
},
"caseless": {
"version": "0.12.0",
@@ -11738,9 +11725,9 @@
}
},
"electron-to-chromium": {
"version": "1.5.418",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.418.tgz",
"integrity": "sha512-UzS26r3AEbG5wSoGVpJKqwHIU9zwQN7LHdVIThDrJpS0I5KdlXFMEb8543fhc9dVnIIAST6ar8rhwa00AL5MlA=="
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
"integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="
},
"emoji-regex": {
"version": "8.0.0",
@@ -13483,9 +13470,9 @@
}
},
"node-releases": {
"version": "2.0.54",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
"integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ=="
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="
},
"npm-run-path": {
"version": "4.0.1",
@@ -14712,9 +14699,9 @@
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="
},
"update-browserslist-db": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
"integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"requires": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
@@ -1,5 +1,5 @@
{
"name": "@superset-ui/eslint-plugin-i18n-strings",
"name": "eslint-plugin-i18n-strings",
"version": "1.0.0",
"description": "Warns about translation variables",
"keywords": [],
@@ -1,5 +1,5 @@
{
"name": "@superset-ui/eslint-plugin-icons",
"name": "eslint-plugin-icons",
"version": "1.0.0",
"description": "Warns about direct usage of Ant Design icons",
"keywords": [],
@@ -1,5 +1,5 @@
{
"name": "@superset-ui/eslint-plugin-theme-colors",
"name": "eslint-plugin-theme-colors",
"version": "1.0.0",
"description": "Warns about rgb(a)/hex/literal colors",
"keywords": [],
+3 -3
View File
@@ -37,9 +37,9 @@
require('tsx/cjs');
const tsParser = require('@typescript-eslint/parser');
const themeColorsPlugin = require('@superset-ui/eslint-plugin-theme-colors');
const iconsPlugin = require('@superset-ui/eslint-plugin-icons');
const i18nStringsPlugin = require('@superset-ui/eslint-plugin-i18n-strings');
const themeColorsPlugin = require('eslint-plugin-theme-colors');
const iconsPlugin = require('eslint-plugin-icons');
const i18nStringsPlugin = require('eslint-plugin-i18n-strings');
module.exports = [
// Files this config applies to. Flat config has no `--ext`; globs live here.
+10 -19
View File
@@ -18,19 +18,6 @@
*/
// timezone for unit tests
process.env.TZ = 'America/New_York';
const reporters = ['default'];
// HTML reporter is not used on CI so skipping its generation for saving time
if (!process.env.CI) {
reporters.push([
'./node_modules/jest-html-reporter',
{
pageTitle: 'Test Report',
},
]);
}
module.exports = {
// [/\\] matches both path separators so the suite also collects on
// native Windows, where jest hands the regex backslash-separated paths.
@@ -90,11 +77,7 @@ module.exports = {
// @ant-design/colors and @ant-design/fast-color are allowed through because
// @ant-design/icons >= 6.3 deep-imports the ESM build of @ant-design/colors
// from its CJS output, so babel-jest must transform those files.
//
// react-markdown and the remark/rehype/vfile packages it pulls in are
// ESM-only, so they are allowed through for the suites that opt out of the
// react-markdown stub in spec/helpers/shim.tsx to render real Markdown.
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|react-markdown|vfile|web-namespaces|html-void-elements|html-url-attributes|estree-util-is-identifier-name|trim-lines|is-plain-obj|trough|unist-.*|hast-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge|content-disposition)',
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge|content-disposition)',
],
preset: 'ts-jest',
transform: {
@@ -105,6 +88,14 @@ module.exports = {
__DEV__: true,
caches: true,
},
reporters: reporters,
reporters: [
'default',
[
'./node_modules/jest-html-reporter',
{
pageTitle: 'Test Report',
},
],
],
testTimeout: 20000,
};
+1795 -5873
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More