mirror of
https://github.com/apache/superset.git
synced 2026-06-24 08:59:20 +00:00
Compare commits
1 Commits
chore/ci/s
...
feat/glyph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb36d7b043 |
2
.github/actions/setup-backend/action.yml
vendored
2
.github/actions/setup-backend/action.yml
vendored
@@ -42,7 +42,7 @@ runs:
|
||||
fi
|
||||
echo "python-version=$RESOLVED_VERSION" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Python ${{ steps.set-python-version.outputs.python-version }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
||||
with:
|
||||
python-version: ${{ steps.set-python-version.outputs.python-version }}
|
||||
cache: ${{ inputs.cache }}
|
||||
|
||||
4
.github/workflows/bashlib.sh
vendored
4
.github/workflows/bashlib.sh
vendored
@@ -114,7 +114,7 @@ testdata() {
|
||||
say "::group::Load test data"
|
||||
# must specify PYTHONPATH to make `tests.superset_test_config` importable
|
||||
export PYTHONPATH="$GITHUB_WORKSPACE"
|
||||
uv pip install --system -e .
|
||||
pip install -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
superset load_examples --load-test-data
|
||||
@@ -127,7 +127,7 @@ playwright_testdata() {
|
||||
say "::group::Load all examples for Playwright tests"
|
||||
# must specify PYTHONPATH to make `tests.superset_test_config` importable
|
||||
export PYTHONPATH="$GITHUB_WORKSPACE"
|
||||
uv pip install --system -e .
|
||||
pip install -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
superset load_examples
|
||||
|
||||
4
.github/workflows/bump-python-package.yml
vendored
4
.github/workflows/bump-python-package.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
checks: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: master
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
|
||||
- name: Set up Python ${{ inputs.python-version }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
|
||||
2
.github/workflows/check-python-deps.yml
vendored
2
.github/workflows/check-python-deps.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check and notify
|
||||
|
||||
5
.github/workflows/codeql-analysis.yml
vendored
5
.github/workflows/codeql-analysis.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -47,7 +47,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
@@ -58,7 +57,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
6
.github/workflows/dependency-review.yml
vendored
6
.github/workflows/dependency-review.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Dependency Review"
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
# the latest version. It's MIT: https://github.com/nbubna/store/blob/master/LICENSE-MIT
|
||||
# pkg:npm/node-forge@1.3.1
|
||||
# selecting BSD-3-Clause licensing terms for node-forge to ensure compatibility with Apache
|
||||
allow-dependencies-licenses: pkg:npm/rgbcolor, pkg:npm/jszip@3.10.1
|
||||
allow-dependencies-licenses: pkg:npm/store2@2.14.2, pkg:npm/node-forge@1.3.1, pkg:npm/rgbcolor, pkg:npm/jszip@3.10.1
|
||||
|
||||
python-dependency-liccheck:
|
||||
# NOTE: Configuration for liccheck lives in our pyproject.yml.
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
67
.github/workflows/docker.yml
vendored
67
.github/workflows/docker.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
docker: ${{ steps.check.outputs.docker }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -71,28 +71,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Free up disk space
|
||||
shell: bash
|
||||
run: |
|
||||
# Reclaim large preinstalled toolchains we don't use. The image
|
||||
# build, and especially the docker-compose sanity check (which
|
||||
# rebuilds from scratch whenever the registry cache image
|
||||
# apache/superset-cache is unavailable), can otherwise exhaust the
|
||||
# runner's root disk and fail with "no space left on device".
|
||||
echo "Disk before cleanup:"; df -h /
|
||||
sudo rm -rf \
|
||||
/usr/share/dotnet \
|
||||
/usr/local/lib/android \
|
||||
/opt/ghc \
|
||||
/usr/local/.ghcup \
|
||||
/opt/hostedtoolcache/CodeQL \
|
||||
/usr/local/share/boost || true
|
||||
echo "Disk after cleanup:"; df -h /
|
||||
|
||||
- name: Setup Docker Environment
|
||||
uses: ./.github/actions/setup-docker
|
||||
with:
|
||||
@@ -119,27 +101,13 @@ jobs:
|
||||
PUSH_OR_LOAD="--load"
|
||||
fi
|
||||
|
||||
# Retry to absorb transient Docker Hub registry errors (base-image
|
||||
# pull timeouts, 504/401 on push, ECONNRESET) that otherwise fail
|
||||
# the whole job. buildx reuses the buildkit layer cache from the
|
||||
# failed attempt, so a retry mostly re-does just the failed push.
|
||||
for attempt in 1 2 3; do
|
||||
if supersetbot docker \
|
||||
$PUSH_OR_LOAD \
|
||||
--preset "$BUILD_PRESET" \
|
||||
--context "$EVENT" \
|
||||
--context-ref "$RELEASE" $FORCE_LATEST \
|
||||
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
|
||||
$PLATFORM_ARG; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "::error::supersetbot docker build failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "::warning::Build attempt ${attempt} failed; retrying in 30s..."
|
||||
sleep 30
|
||||
done
|
||||
supersetbot docker \
|
||||
$PUSH_OR_LOAD \
|
||||
--preset "$BUILD_PRESET" \
|
||||
--context "$EVENT" \
|
||||
--context-ref "$RELEASE" $FORCE_LATEST \
|
||||
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
|
||||
$PLATFORM_ARG
|
||||
|
||||
# in the context of push (using multi-platform build), we need to pull the image locally
|
||||
- name: Docker pull
|
||||
@@ -177,24 +145,9 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Free up disk space
|
||||
shell: bash
|
||||
run: |
|
||||
# The sanity check rebuilds the image from scratch whenever the
|
||||
# registry cache image apache/superset-cache is unavailable, which
|
||||
# can exhaust the runner's root disk ("no space left on device").
|
||||
echo "Disk before cleanup:"; df -h /
|
||||
sudo rm -rf \
|
||||
/usr/share/dotnet \
|
||||
/usr/local/lib/android \
|
||||
/opt/ghc \
|
||||
/usr/local/.ghcup \
|
||||
/opt/hostedtoolcache/CodeQL \
|
||||
/usr/local/share/boost || true
|
||||
echo "Disk after cleanup:"; df -h /
|
||||
- name: Setup Docker Environment
|
||||
uses: ./.github/actions/setup-docker
|
||||
with:
|
||||
|
||||
36
.github/workflows/embedded-sdk-release.yml
vendored
36
.github/workflows/embedded-sdk-release.yml
vendored
@@ -10,29 +10,37 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Publishing uses npm trusted publishing (OIDC), so there is no NPM_TOKEN to
|
||||
# gate on. Restrict to the canonical repo: forks cannot mint a valid OIDC
|
||||
# token for this package and must not publish.
|
||||
if: github.repository == 'apache/superset'
|
||||
config:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
- name: "Check for secrets"
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${NPM_TOKEN}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
env:
|
||||
NPM_TOKEN: ${{ (secrets.NPM_TOKEN != '') || '' }}
|
||||
build:
|
||||
needs: config
|
||||
if: needs.config.outputs.has-secrets
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # required for npm trusted publishing (OIDC)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: superset-embedded-sdk
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Note: registry-url is intentionally omitted. When set, actions/setup-node
|
||||
# writes an .npmrc with `_authToken=${NODE_AUTH_TOKEN}` and a placeholder
|
||||
# token, which makes npm attempt token auth and skip the OIDC
|
||||
# trusted-publishing exchange. With no .npmrc auth line, npm authenticates
|
||||
# via OIDC against the default registry (registry.npmjs.org).
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: "./superset-embedded-sdk/.nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: npm ci
|
||||
- run: npm run ci:release
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
2
.github/workflows/embedded-sdk-test.yml
vendored
2
.github/workflows/embedded-sdk-test.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
run:
|
||||
working-directory: superset-embedded-sdk
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
|
||||
4
.github/workflows/generate-FOSSA-report.yml
vendored
4
.github/workflows/generate-FOSSA-report.yml
vendored
@@ -32,12 +32,12 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
2
.github/workflows/issue_creation.yml
vendored
2
.github/workflows/issue_creation.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
2
.github/workflows/latest-release-tag.yml
vendored
2
.github/workflows/latest-release-tag.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
4
.github/workflows/license-check.yml
vendored
4
.github/workflows/license-check.yml
vendored
@@ -18,12 +18,12 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
||||
2
.github/workflows/pr-lint.yml
vendored
2
.github/workflows/pr-lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
# pulls all commits (needed for lerna / semantic release to correctly version)
|
||||
|
||||
2
.github/workflows/showtime-trigger.yml
vendored
2
.github/workflows/showtime-trigger.yml
vendored
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Checkout PR code (only if build needed)
|
||||
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ steps.check.outputs.target_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
4
.github/workflows/superset-app-cli.yml
vendored
4
.github/workflows/superset-app-cli.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
- name: superset init
|
||||
if: steps.check.outputs.python
|
||||
run: |
|
||||
uv pip install --system -e .
|
||||
pip install -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
- name: superset load_examples
|
||||
|
||||
4
.github/workflows/superset-docs-deploy.yml
vendored
4
.github/workflows/superset-docs-deploy.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.event.workflow_run.head_sha || github.sha }}"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
||||
persist-credentials: false
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: "21"
|
||||
|
||||
6
.github/workflows/superset-docs-verify.yml
vendored
6
.github/workflows/superset-docs-verify.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
name: Link Checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Do not bump this linkinator-action version without opening
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- name: "Checkout PR head: ${{ github.event.workflow_run.head_sha }}"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
16
.github/workflows/superset-e2e.yml
vendored
16
.github/workflows/superset-e2e.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
cypress-matrix:
|
||||
needs: changes
|
||||
if: (needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true') && github.event.pull_request.draft == false
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
|
||||
# Somehow one test flakes on 24.04 for unknown reasons, this is the only GHA left on 22.04
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
@@ -97,21 +97,21 @@ jobs:
|
||||
# Conditional checkout based on context
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
@@ -207,21 +207,21 @@ jobs:
|
||||
# Conditional checkout based on context (same as Cypress workflow)
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: superset-extensions-cli
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
4
.github/workflows/superset-frontend.yml
vendored
4
.github/workflows/superset-frontend.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
should-run: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
2
.github/workflows/superset-helm-lint.yml
vendored
2
.github/workflows/superset-helm-lint.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
2
.github/workflows/superset-helm-release.yml
vendored
2
.github/workflows/superset-helm-release.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref_name }}
|
||||
persist-credentials: true
|
||||
|
||||
8
.github/workflows/superset-playwright.yml
vendored
8
.github/workflows/superset-playwright.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -83,21 +83,21 @@ jobs:
|
||||
# Conditional checkout based on context (same as Cypress workflow)
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
# Python integration tests
|
||||
name: Python-Integration
|
||||
|
||||
# Least-privilege default for GITHUB_TOKEN. Jobs that need more (e.g. OIDC for
|
||||
# codecov uploads) opt in via their own job-level `permissions:` block.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -29,7 +24,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -72,7 +67,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -157,7 +152,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -207,7 +202,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
run: celery-worker
|
||||
- name: Python unit tests (PostgreSQL)
|
||||
run: |
|
||||
uv pip install --system -e .[hive]
|
||||
pip install -e .[hive]
|
||||
./scripts/python_tests.sh -m 'chart_data_flow or sql_json_flow'
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
# Python unit tests
|
||||
name: Python-Unit
|
||||
|
||||
# Least-privilege default for GITHUB_TOKEN. Jobs that need more (e.g. OIDC for
|
||||
# codecov uploads) opt in via their own job-level `permissions:` block.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -30,7 +25,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -55,7 +50,7 @@ jobs:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
4
.github/workflows/superset-translations.yml
vendored
4
.github/workflows/superset-translations.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
2
.github/workflows/superset-websocket.yml
vendored
2
.github/workflows/superset-websocket.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install dependencies
|
||||
|
||||
2
.github/workflows/supersetbot.yml
vendored
2
.github/workflows/supersetbot.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
});
|
||||
|
||||
- name: "Checkout ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
name: Sync requirements for Python dependency PRs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
sync-python-dep-requirements:
|
||||
# This action is limited for (1) PRs authored by Dependabot and (2) upstream repo due to write back to remote
|
||||
if: github.repository == 'apache/superset' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
shell: bash
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref }}
|
||||
run: |
|
||||
# Get current branch name, extract the package ecosystem and return as GHA step output
|
||||
packageEcosystem=$(echo "$BRANCH_NAME" | cut -d'/' -f2)
|
||||
echo "package-ecosystem=$packageEcosystem" >> $GITHUB_OUTPUT
|
||||
|
||||
# zizmor: ignore[artipacked] - required persisted credentials to push synced requirement changes back to remote
|
||||
- name: Checkout source code
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
persist-credentials: true
|
||||
|
||||
# Authenticate the Docker daemon so the python:slim pull in
|
||||
# uv-pip-compile.sh uses our (much higher) authenticated rate limit
|
||||
# instead of the shared-runner anonymous one.
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
continue-on-error: true
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Sync requirements in containerized environment
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
run: ./scripts/uv-pip-compile.sh
|
||||
|
||||
- name: Push changes to remote PRs
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git add requirements
|
||||
git diff --cached --quiet && exit 0
|
||||
git commit --signoff --message "build(deps): sync pinned requirements for Dependabot pip PRs"
|
||||
git push origin "HEAD:refs/heads/${GITHUB_EVENT_PULL_REQUEST_HEAD_REF}"
|
||||
env:
|
||||
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
4
.github/workflows/tag-release.yml
vendored
4
.github/workflows/tag-release.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
2
.github/workflows/tech-debt.yml
vendored
2
.github/workflows/tech-debt.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
name: Generate Reports
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Install pre-push hooks too, not just pre-commit:
|
||||
# pre-commit install --hook-type pre-push
|
||||
# (or this default applies automatically on `pre-commit install`)
|
||||
default_install_hook_types: [pre-commit, pre-push]
|
||||
repos:
|
||||
- repo: https://github.com/MarcoGorelli/auto-walrus
|
||||
rev: 0.3.4
|
||||
@@ -94,6 +98,19 @@ repos:
|
||||
files: ^superset-frontend\/.*\.(js|jsx|ts|tsx)$
|
||||
exclude: ^superset-frontend/cypress-base\/
|
||||
require_serial: true
|
||||
- id: full-type-check-frontend
|
||||
name: Full Type-Check (Frontend, pre-push)
|
||||
# The pre-commit (per-file) variant catches most issues but `tsc <file>`
|
||||
# bypasses some project-wide invariants. This runs the same check CI
|
||||
# runs (`npm run type` -> `tsc --noEmit -p tsconfig.json`) so cross-file
|
||||
# type errors are surfaced before they reach origin.
|
||||
entry: bash -c 'cd superset-frontend && npm run type'
|
||||
language: system
|
||||
files: ^superset-frontend\/.*\.(js|jsx|ts|tsx)$
|
||||
exclude: ^superset-frontend/cypress-base\/
|
||||
pass_filenames: false
|
||||
stages: [pre-push]
|
||||
require_serial: true
|
||||
# blacklist unsafe functions like make_url (see #19526)
|
||||
- repo: https://github.com/skorokithakis/blacklist-pre-commit-hook
|
||||
rev: e2f070289d8eddcaec0b580d3bde29437e7c8221
|
||||
|
||||
@@ -50,4 +50,3 @@ under the License.
|
||||
- [4.1.4](./CHANGELOG/4.1.4.md)
|
||||
- [5.0.0](./CHANGELOG/5.0.0.md)
|
||||
- [6.0.0](./CHANGELOG/6.0.0.md)
|
||||
- [6.1.0](./CHANGELOG/6.1.0.md)
|
||||
|
||||
1563
CHANGELOG/6.1.0.md
1563
CHANGELOG/6.1.0.md
File diff suppressed because it is too large
Load Diff
17
Makefile
17
Makefile
@@ -23,14 +23,11 @@ PYTHON=`command -v python3.11 || command -v python3.10`
|
||||
install: superset pre-commit
|
||||
|
||||
superset:
|
||||
# Bootstrap uv (the project's installer) into the active environment
|
||||
pip install uv
|
||||
|
||||
# Install external dependencies
|
||||
uv pip install -r requirements/development.txt
|
||||
pip install -r requirements/development.txt
|
||||
|
||||
# Install Superset in editable (development) mode
|
||||
uv pip install -e .
|
||||
pip install -e .
|
||||
|
||||
# Create an admin user in your metadata database
|
||||
superset fab create-admin \
|
||||
@@ -55,14 +52,11 @@ superset:
|
||||
update: update-py update-js
|
||||
|
||||
update-py:
|
||||
# Bootstrap uv (the project's installer) into the active environment
|
||||
pip install uv
|
||||
|
||||
# Install external dependencies
|
||||
uv pip install -r requirements/development.txt
|
||||
pip install -r requirements/development.txt
|
||||
|
||||
# Install Superset in editable (development) mode
|
||||
uv pip install -e .
|
||||
pip install -e .
|
||||
|
||||
# Initialize the database
|
||||
superset db upgrade
|
||||
@@ -85,8 +79,7 @@ activate:
|
||||
|
||||
pre-commit:
|
||||
# setup pre commit dependencies
|
||||
pip install uv
|
||||
uv pip install -r requirements/development.txt
|
||||
pip3 install -r requirements/development.txt
|
||||
pre-commit install
|
||||
|
||||
format: py-format js-format
|
||||
|
||||
@@ -83,9 +83,6 @@ categories:
|
||||
- name: Clark.de
|
||||
url: https://clark.de/
|
||||
|
||||
- name: Cover Genius
|
||||
url: https://covergenius.com/
|
||||
|
||||
- name: EnquiryLabs
|
||||
url: https://www.enquirylabs.co.uk
|
||||
|
||||
@@ -95,10 +92,6 @@ categories:
|
||||
- name: KarrotPay
|
||||
url: https://www.daangnpay.com/
|
||||
|
||||
- name: NICE Actimize
|
||||
url: https://www.niceactimize.com/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Remita
|
||||
url: https://remita.net
|
||||
contributors: ["@mujibishola"]
|
||||
@@ -119,6 +112,9 @@ categories:
|
||||
url: https://xendit.co/
|
||||
contributors: ["@LieAlbertTriAdrian"]
|
||||
|
||||
- name: Cover Genius
|
||||
url: https://covergenius.com/
|
||||
|
||||
Gaming:
|
||||
- name: Popoko VM Games Studio
|
||||
url: https://popoko.live
|
||||
@@ -300,6 +296,7 @@ categories:
|
||||
logo: hifadih.png
|
||||
contributors: ["@saintLaurent00"]
|
||||
|
||||
# Logo approved by @anmol-hpe on behalf of HPE
|
||||
- name: HPE
|
||||
url: https://www.hpe.com/in/en/home.html
|
||||
logo: hpe.png
|
||||
@@ -399,10 +396,6 @@ categories:
|
||||
url: https://www.techaudit.info
|
||||
contributors: ["@ETselikov"]
|
||||
|
||||
- name: Tech Solution
|
||||
url: https://www.tech-solution.com.ar/
|
||||
contributors: ["@danteGiuliano", "@LeandroVallejos", "@McJaben", "@xJeree", "@zeo-return-null"]
|
||||
|
||||
- name: Tenable
|
||||
url: https://www.tenable.com
|
||||
contributors: ["@dflionis"]
|
||||
@@ -432,10 +425,6 @@ categories:
|
||||
logo: userguiding.svg
|
||||
contributors: ["@tzercin"]
|
||||
|
||||
- name: Value Ad
|
||||
url: https://bestpair.info/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Virtuoso QA
|
||||
url: https://www.virtuosoqa.com
|
||||
|
||||
@@ -520,6 +509,10 @@ categories:
|
||||
url: https://www.sunbird.org/
|
||||
contributors: ["@eksteporg"]
|
||||
|
||||
- name: The GRAPH Network
|
||||
url: https://thegraphnetwork.org/
|
||||
contributors: ["@fccoelho"]
|
||||
|
||||
- name: Udemy
|
||||
url: https://www.udemy.com/
|
||||
contributors: ["@sungjuly"]
|
||||
@@ -528,24 +521,7 @@ categories:
|
||||
url: https://www.vipkid.com.cn/
|
||||
contributors: ["@illpanda"]
|
||||
|
||||
Social Organization:
|
||||
- name: Living Goods
|
||||
url: https://www.livinggoods.org
|
||||
contributors: ["@chelule"]
|
||||
|
||||
- name: One Acre Fund
|
||||
url: https://oneacrefund.org/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Quest Alliance
|
||||
url: https://www.questalliance.net/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: The GRAPH Network
|
||||
url: https://thegraphnetwork.org/
|
||||
contributors: ["@fccoelho"]
|
||||
|
||||
- name: Wikimedia Foundation
|
||||
- name: WikiMedia Foundation
|
||||
url: https://wikimediafoundation.org
|
||||
contributors: ["@vg"]
|
||||
|
||||
@@ -558,10 +534,6 @@ categories:
|
||||
url: https://www.douroeci.com/
|
||||
contributors: ["@nunohelibeires"]
|
||||
|
||||
- name: Rogow
|
||||
url: https://rogow.com.br/
|
||||
contributors: ["@nilmonto"]
|
||||
|
||||
- name: Safaricom
|
||||
url: https://www.safaricom.co.ke/
|
||||
contributors: ["@mmutiso"]
|
||||
@@ -574,10 +546,11 @@ categories:
|
||||
url: https://wattbewerb.de/
|
||||
contributors: ["@wattbewerb"]
|
||||
|
||||
Healthcare:
|
||||
- name: 2070Health
|
||||
url: https://2070health.com/
|
||||
- name: Rogow
|
||||
url: https://rogow.com.br/
|
||||
contributors: ["@nilmonto"]
|
||||
|
||||
Healthcare:
|
||||
- name: Amino
|
||||
url: https://amino.com
|
||||
contributors: ["@shkr"]
|
||||
@@ -590,6 +563,10 @@ categories:
|
||||
url: https://www.getcare.io/
|
||||
contributors: ["@alandao2021"]
|
||||
|
||||
- name: Living Goods
|
||||
url: https://www.livinggoods.org
|
||||
contributors: ["@chelule"]
|
||||
|
||||
- name: Maieutical Labs
|
||||
url: https://maieuticallabs.it
|
||||
contributors: ["@xrmx"]
|
||||
@@ -608,10 +585,10 @@ categories:
|
||||
- name: WeSure
|
||||
url: https://www.wesure.cn/
|
||||
|
||||
HR / Staffing:
|
||||
- name: bluquist
|
||||
url: https://bluquist.com/
|
||||
- name: 2070Health
|
||||
url: https://2070health.com/
|
||||
|
||||
HR / Staffing:
|
||||
- name: Swile
|
||||
url: https://www.swile.co/
|
||||
contributors: ["@PaoloTerzi"]
|
||||
@@ -619,18 +596,21 @@ categories:
|
||||
- name: Symmetrics
|
||||
url: https://www.symmetrics.fyi
|
||||
|
||||
- name: bluquist
|
||||
url: https://bluquist.com/
|
||||
|
||||
Government:
|
||||
- name: City of Ann Arbor, MI
|
||||
url: https://www.a2gov.org/
|
||||
contributors: ["@sfirke"]
|
||||
|
||||
- name: NRLM - Sarathi, India
|
||||
url: https://pib.gov.in/PressReleasePage.aspx?PRID=1999586
|
||||
|
||||
- name: RIS3 Strategy of CZ, MIT CR
|
||||
url: https://www.ris3.cz/
|
||||
contributors: ["@RIS3CZ"]
|
||||
|
||||
- name: NRLM - Sarathi, India
|
||||
url: https://pib.gov.in/PressReleasePage.aspx?PRID=1999586
|
||||
|
||||
Mobile Software:
|
||||
- name: VLMedia
|
||||
url: https://www.vlmedia.com.tr
|
||||
|
||||
@@ -24,10 +24,6 @@ assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
### Pivot table First/Last aggregations follow data order
|
||||
|
||||
The pivot table chart's `First` and `Last` aggregations now return the first and last value in data (query result) order, instead of effectively returning the minimum and maximum. Existing pivot tables that use these aggregations for totals/subtotals may show different values after upgrading. For deterministic results, ensure the underlying query has a stable sort order.
|
||||
|
||||
### `thumbnail_url` removed from dashboard list API response
|
||||
|
||||
The `thumbnail_url` field has been removed from `GET /api/v1/dashboard/` list responses. External consumers relying on this field must now construct the thumbnail URL client-side using `id` and `changed_on_utc`:
|
||||
@@ -227,9 +223,6 @@ Added a new combined datasource list endpoint at `GET /api/v1/datasource/` to se
|
||||
- The endpoint is available to users with at least one of `can_read` on `Dataset` or `SemanticView`.
|
||||
- Semantic views are included only when the `SEMANTIC_LAYERS` feature flag is enabled.
|
||||
- The endpoint enforces strict `order_column` validation and returns `400` for invalid sort columns.
|
||||
|
||||
## 6.1.0
|
||||
|
||||
### ClickHouse minimum driver version bump
|
||||
|
||||
The minimum required version of `clickhouse-connect` has been raised to `>=0.13.0`. If you are using the ClickHouse connector, please upgrade your `clickhouse-connect` package. The `_mutate_label` workaround that appended hash suffixes to column aliases has also been removed, as it is no longer needed with modern versions of the driver.
|
||||
|
||||
@@ -111,6 +111,8 @@ services:
|
||||
superset-init-light:
|
||||
condition: service_completed_successfully
|
||||
volumes: *superset-volumes
|
||||
ports:
|
||||
- "${SUPERSET_PORT:-8088}:8088"
|
||||
environment:
|
||||
DATABASE_HOST: db-light
|
||||
DATABASE_DB: superset_light
|
||||
@@ -162,7 +164,7 @@ services:
|
||||
environment:
|
||||
# set this to false if you have perf issues running the npm i; npm run dev in-docker
|
||||
# if you do so, you have to run this manually on the host, which should perform better!
|
||||
BUILD_SUPERSET_FRONTEND_IN_DOCKER: true
|
||||
BUILD_SUPERSET_FRONTEND_IN_DOCKER: false
|
||||
NPM_RUN_PRUNE: false
|
||||
SCARF_ANALYTICS: "${SCARF_ANALYTICS:-}"
|
||||
DISABLE_TS_CHECKER: "${DISABLE_TS_CHECKER:-true}"
|
||||
|
||||
@@ -34,6 +34,14 @@ x-superset-volumes: &superset-volumes
|
||||
- superset_home:/app/superset_home
|
||||
- ./tests:/app/tests
|
||||
- superset_data:/app/data
|
||||
# Python package metadata for the editable `uv pip install -e .` that
|
||||
# docker-bootstrap.sh runs at container start. Without these bind mounts
|
||||
# the editable install reads stale metadata baked into the image at
|
||||
# build time and may conflict with apache-superset-core's current pins.
|
||||
- ./pyproject.toml:/app/pyproject.toml
|
||||
- ./setup.py:/app/setup.py
|
||||
- ./MANIFEST.in:/app/MANIFEST.in
|
||||
- ./README.md:/app/README.md
|
||||
x-common-build: &common-build
|
||||
context: .
|
||||
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
|
||||
@@ -72,23 +80,20 @@ services:
|
||||
- -c
|
||||
- |
|
||||
url="http://host.docker.internal:9000/static/assets/manifest.json"
|
||||
max_attempts=300 # ~10 minutes at 2s intervals; first build can be slow
|
||||
echo "Waiting for webpack dev server at $$url..."
|
||||
max_attempts=150 # ~5 minutes at 2s intervals
|
||||
echo "Waiting for webpack dev server at $url..."
|
||||
attempt=0
|
||||
until curl -sf --max-time 5 -H "Host: localhost" -o /dev/null "$$url"; do
|
||||
attempt=$$((attempt + 1))
|
||||
if [ "$$attempt" -ge "$$max_attempts" ]; then
|
||||
echo "ERROR: webpack dev server did not serve $$url after $$max_attempts attempts." >&2
|
||||
until curl -sf --max-time 5 -o /dev/null "$url"; do
|
||||
attempt=$((attempt + 1))
|
||||
if [ "$attempt" -ge "$max_attempts" ]; then
|
||||
echo "ERROR: webpack dev server did not serve $url after $max_attempts attempts (~5 minutes)." >&2
|
||||
echo "Is the dev server running? With BUILD_SUPERSET_FRONTEND_IN_DOCKER=false you must start it on the host (e.g. 'npm run dev' in superset-frontend)." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $$((attempt % 15)) -eq 0 ]; then
|
||||
echo "Still waiting for webpack dev server... ($$attempt/$$max_attempts)"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "Webpack dev server is ready; starting nginx."
|
||||
exec /docker-entrypoint.sh nginx -g 'daemon off;'
|
||||
exec nginx -g 'daemon off;'
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
|
||||
@@ -71,29 +71,27 @@ case "${1}" in
|
||||
worker)
|
||||
echo "Starting Celery worker..."
|
||||
# setting up only 2 workers by default to contain memory usage in dev environments
|
||||
celery --app=superset.tasks.celery_app:app worker -O fair -l INFO --concurrency=${CELERYD_CONCURRENCY:-2} ${WORKER_LOG_FILE:+--logfile=$WORKER_LOG_FILE}
|
||||
celery --app=superset.tasks.celery_app:app worker -O fair -l INFO --concurrency=${CELERYD_CONCURRENCY:-2}
|
||||
;;
|
||||
beat)
|
||||
echo "Starting Celery beat..."
|
||||
rm -f /tmp/celerybeat.pid
|
||||
celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule ${BEAT_LOG_FILE:+--logfile=$BEAT_LOG_FILE}
|
||||
celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule
|
||||
;;
|
||||
app)
|
||||
echo "Starting web app (using development server)..."
|
||||
|
||||
# Default to Flask debug mode in this dev compose entrypoint so the Talisman
|
||||
# dev CSP (which permits 'unsafe-eval' required by React Refresh / HMR) is
|
||||
# served. Operators can still set FLASK_DEBUG=false in docker/.env-local
|
||||
# to exercise the production-like CSP and error handling.
|
||||
: "${FLASK_DEBUG:=1}"
|
||||
export FLASK_DEBUG
|
||||
|
||||
# Werkzeug's interactive debugger (/console) is a separate, security-sensitive
|
||||
# feature and must be opted into explicitly via SUPERSET_DEBUG_ENABLED=true.
|
||||
# Environment-based debugger control for security
|
||||
# Only enable Werkzeug interactive debugger when explicitly requested
|
||||
# Modern Werkzeug (3.0+) includes PIN protection, but defense-in-depth approach
|
||||
# Override FLASK_DEBUG so the effective state matches SUPERSET_DEBUG_ENABLED even
|
||||
# when FLASK_DEBUG=true is inherited from docker/.env or .flaskenv
|
||||
if [[ "${SUPERSET_DEBUG_ENABLED:-}" == "true" ]]; then
|
||||
export FLASK_DEBUG=1
|
||||
DEBUGGER_FLAG="--debugger"
|
||||
echo " ⚠️ Werkzeug debugger enabled (requires PIN for /console access)"
|
||||
else
|
||||
export FLASK_DEBUG=0
|
||||
DEBUGGER_FLAG="--no-debugger"
|
||||
echo " 🔒 Werkzeug debugger disabled (set SUPERSET_DEBUG_ENABLED=true to enable)"
|
||||
fi
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
HYPHEN_SYMBOL='-'
|
||||
|
||||
exec gunicorn \
|
||||
gunicorn \
|
||||
--bind "${SUPERSET_BIND_ADDRESS:-0.0.0.0}:${SUPERSET_PORT:-8088}" \
|
||||
--access-logfile "${ACCESS_LOG_FILE:-$HYPHEN_SYMBOL}" \
|
||||
--error-logfile "${ERROR_LOG_FILE:-$HYPHEN_SYMBOL}" \
|
||||
|
||||
@@ -455,51 +455,6 @@ def FLASK_APP_MUTATOR(app: Flask) -> None:
|
||||
app.before_request_funcs.setdefault(None, []).append(make_session_permanent)
|
||||
```
|
||||
|
||||
## Customizing the landing page (index view)
|
||||
|
||||
The page served at `/` is rendered by an index view. By default Superset registers
|
||||
`SupersetIndexView`, which redirects to `/superset/welcome/` and also adds the
|
||||
`/lang/<locale>` locale handler. You can replace it with your own view, for example
|
||||
to send users straight to a specific dashboard or to a chart list.
|
||||
|
||||
Set `FAB_INDEX_VIEW` to the **importable dotted path** of your view class. Flask-AppBuilder
|
||||
resolves this during app initialization and uses it in place of the default:
|
||||
|
||||
```python
|
||||
# my_overrides.py — must be importable on the PYTHONPATH
|
||||
from flask import redirect
|
||||
from superset.initialization import SupersetIndexView
|
||||
from superset.superset_typing import FlaskResponse
|
||||
from flask_appbuilder import expose
|
||||
|
||||
|
||||
class MyIndexView(SupersetIndexView):
|
||||
@expose("/")
|
||||
def index(self) -> FlaskResponse:
|
||||
return redirect("/chart/list/")
|
||||
```
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
FAB_INDEX_VIEW = "my_overrides.MyIndexView"
|
||||
```
|
||||
|
||||
A few things that commonly trip people up:
|
||||
|
||||
- **Subclass `SupersetIndexView`, not Flask-AppBuilder's bare `IndexView`.** Subclassing
|
||||
keeps Superset's `/lang/<locale>` locale handling; replacing it with a bare `IndexView`
|
||||
silently drops that behavior.
|
||||
- **The class must be importable as a real module.** `FAB_INDEX_VIEW` is resolved by
|
||||
importing the dotted path, which is independent of how `superset_config.py` itself is
|
||||
loaded. Superset only copies **uppercase** names out of `superset_config.py` into its
|
||||
runtime config, so a `FAB_INDEX_VIEW = "superset_config.MyIndexView"` reference only works
|
||||
if `superset_config` is itself importable by that name on the `PYTHONPATH`. If you load
|
||||
config via `SUPERSET_CONFIG_PATH` (an arbitrary file path), put the view in a separate
|
||||
importable module instead and reference that module.
|
||||
- **Don't set `appbuilder.indexview` from `FLASK_APP_MUTATOR`.** The mutator runs after
|
||||
routes are already registered, so the assignment has no effect on the `/` route. Use
|
||||
`FAB_INDEX_VIEW` instead.
|
||||
|
||||
## Feature Flags
|
||||
|
||||
To support a diverse set of users, Superset has some features that are not enabled by default. For
|
||||
|
||||
@@ -22,24 +22,31 @@ level dependencies.
|
||||
|
||||
**Debian and Ubuntu**
|
||||
|
||||
The following command will ensure that the required dependencies are installed (tested on Ubuntu 20.04, 22.04, and 24.04):
|
||||
|
||||
Ubuntu **24.04** uses python 3.12 per default, which currently is not supported by Superset. You need to add a second python installation of 3.11 and install the required additional dependencies.
|
||||
```bash
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip python3-venv libsasl2-dev libldap2-dev libpq-dev default-libmysqlclient-dev pkg-config
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
sudo apt update
|
||||
sudo apt install python3.11 python3.11-dev python3.11-venv build-essential libssl-dev libffi-dev libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
```
|
||||
|
||||
Refer to the
|
||||
[pyproject.toml](https://github.com/apache/superset/blob/master/pyproject.toml) file for the list of
|
||||
Python versions officially supported by Superset, and install a matching `python3` interpreter for
|
||||
your distribution. The `libpq-dev` package is only needed if you intend to connect to (or use) a
|
||||
PostgreSQL database; you can omit it otherwise.
|
||||
In Ubuntu **20.04 and 22.04** the following command will ensure that the required dependencies are installed:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
```
|
||||
|
||||
In Ubuntu **before 20.04** the following command will ensure that the required dependencies are installed:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python-dev python-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
```
|
||||
|
||||
**Fedora and RHEL-derivative Linux distributions**
|
||||
|
||||
Install the following packages using the `yum` package manager:
|
||||
|
||||
```bash
|
||||
sudo yum install gcc gcc-c++ libffi-devel python3-devel python3-pip python3-wheel openssl-devel cyrus-sasl-devel openldap-devel
|
||||
sudo yum install gcc gcc-c++ libffi-devel python-devel python-pip python-wheel openssl-devel cyrus-sasl-devel openldap-devel
|
||||
```
|
||||
|
||||
In more recent versions of CentOS and Fedora, you may need to install a slightly different set of packages using `dnf`:
|
||||
|
||||
@@ -28,19 +28,14 @@
|
||||
# Skip builds when no docs changes (exit 0 = skip, non-zero = build).
|
||||
# Checks for changes in docs/ and README.md (which gets pulled into docs).
|
||||
#
|
||||
# $CACHED_COMMIT_REF is the last *deployed* commit; it is set on incremental
|
||||
# builds (notably the master production deploy) and empty on a context's
|
||||
# first build (every deploy preview). The production path diffs against it
|
||||
# and skips correctly.
|
||||
#
|
||||
# Deploy previews need different handling: Netlify checks out a *merge*
|
||||
# commit, so $COMMIT_REF (the PR head SHA) is frequently not resolvable in
|
||||
# the clone, and on a shallow clone `git merge-base` can fail too -- so the
|
||||
# previous logic fell through to a build on every PR, even non-docs ones.
|
||||
# Instead, always diff the checked-out HEAD against its merge-base with
|
||||
# master, deepening the shallow clone until that merge-base resolves. If it
|
||||
# genuinely can't be determined, exit non-zero to build (fail safe).
|
||||
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'
|
||||
# $CACHED_COMMIT_REF is the last *deployed* commit. On a PR's first build it
|
||||
# is empty, so the original `git diff` errored and Netlify fell back to
|
||||
# building -- which is why every PR built a docs preview once even with no
|
||||
# docs changes. When it is empty we instead diff the whole branch against its
|
||||
# merge-base with master, so non-docs PRs are skipped from the very first
|
||||
# build. Subsequent builds (and the master production build) keep the cheaper
|
||||
# incremental $CACHED_COMMIT_REF diff. Any failure exits non-zero -> build.
|
||||
ignore = 'if [ -n "$CACHED_COMMIT_REF" ]; then git diff --quiet "$CACHED_COMMIT_REF" "$COMMIT_REF" -- . ../README.md; else git fetch origin master --depth=100 >/dev/null 2>&1; git diff --quiet "$(git merge-base origin/master "$COMMIT_REF" 2>/dev/null || echo origin/master)" "$COMMIT_REF" -- . ../README.md; fi'
|
||||
|
||||
[build.environment]
|
||||
# Node version matching docs/.nvmrc
|
||||
|
||||
@@ -71,9 +71,9 @@
|
||||
"@storybook/theming": "^8.6.15",
|
||||
"@superset-ui/core": "^0.20.4",
|
||||
"@swc/core": "^1.15.41",
|
||||
"antd": "^6.4.4",
|
||||
"baseline-browser-mapping": "^2.10.37",
|
||||
"caniuse-lite": "^1.0.30001799",
|
||||
"antd": "^6.4.3",
|
||||
"baseline-browser-mapping": "^2.10.35",
|
||||
"caniuse-lite": "^1.0.30001797",
|
||||
"docusaurus-plugin-openapi-docs": "^5.0.2",
|
||||
"docusaurus-theme-openapi-docs": "^5.0.2",
|
||||
"js-yaml": "^4.2.0",
|
||||
@@ -107,9 +107,9 @@
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^17.6.0",
|
||||
"prettier": "^3.8.4",
|
||||
"prettier": "^3.8.3",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.61.1",
|
||||
"typescript-eslint": "^8.61.0",
|
||||
"webpack": "^5.107.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
@@ -1808,10 +1808,6 @@ If you enable DML in the meta database users will be able to run DML queries on
|
||||
|
||||
Second, you might want to change the value of `SUPERSET_META_DB_LIMIT`. The default value is 1000, and defines how many are read from each database before any aggregations and joins are executed. You can also set this value `None` if you only have small tables.
|
||||
|
||||
:::warning
|
||||
`SUPERSET_META_DB_LIMIT` is applied to **each** underlying table *before* the in-memory join runs, not to the final result. If any table involved in a join has more rows than the limit, the meta database will read only the first `SUPERSET_META_DB_LIMIT` rows of that table, which means matching rows can be silently dropped and the join can return **incomplete or even empty** results with no error. If you join tables larger than the limit, raise `SUPERSET_META_DB_LIMIT` to comfortably exceed your largest joined table, or set it to `None` when working only with small tables, to get correct results.
|
||||
:::
|
||||
|
||||
Additionally, you might want to restrict the databases to with the meta database has access to. This can be done in the database configuration, under "Advanced" -> "Other" -> "ENGINE PARAMETERS" and adding:
|
||||
|
||||
```json
|
||||
|
||||
415
docs/yarn.lock
415
docs/yarn.lock
@@ -212,7 +212,7 @@
|
||||
resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz"
|
||||
integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==
|
||||
|
||||
"@ant-design/icons@^6.2.5":
|
||||
"@ant-design/icons@^6.2.3", "@ant-design/icons@^6.2.5":
|
||||
version "6.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.5.tgz#31c142aa6ce5eaf99598aaead222f4c459693512"
|
||||
integrity sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==
|
||||
@@ -3162,21 +3162,21 @@
|
||||
resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz"
|
||||
integrity sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==
|
||||
|
||||
"@rc-component/async-validator@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-6.0.0.tgz#1c20b8864f69bac63b7876b1321697c2ea71c68d"
|
||||
integrity sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==
|
||||
"@rc-component/async-validator@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.0.tgz"
|
||||
integrity sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.24.4"
|
||||
|
||||
"@rc-component/cascader@~1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.16.1.tgz#94193ee55009219999a46e005a0f8589c8c021a2"
|
||||
integrity sha512-wxLopwM+EBed0zNNGdnGE4coYoqcO+XD42fHgn+pDvO+XzhNFbdgSlSNXdKocIYqccvqgWvoxDPNb0OVRdi59A==
|
||||
"@rc-component/cascader@~1.15.0":
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.15.0.tgz#554cba8e01e94a1288547cec96422b2cfc73ff40"
|
||||
integrity sha512-ZzpMtwFCRo3fbXHuDnncARJMZQjdqA2w7aDuPofNQt+aDx39st1hgfIpEwTBLhe2Hqsvs/zOr8RTtgxTkCPySw==
|
||||
dependencies:
|
||||
"@rc-component/select" "~1.7.1"
|
||||
"@rc-component/tree" "~1.3.2"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/select" "~1.6.0"
|
||||
"@rc-component/tree" "~1.3.0"
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/checkbox@~2.0.0":
|
||||
@@ -3242,13 +3242,13 @@
|
||||
"@rc-component/util" "^1.2.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/form@~1.8.3":
|
||||
version "1.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.5.tgz#20571cfd401dc38c74c38cdf4722ddc6c23a9806"
|
||||
integrity sha512-d24EYtvUOBhxEtSd/EqIu9DaMuqrWF2IRIvAFCTM6NQ/GJIYNr8DvEpUSUlv2uPxEJ0ZPwYQ+wwlGIAaiHvdrw==
|
||||
"@rc-component/form@~1.8.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.1.tgz#d811fb52df41bf72297938ebfe5cf4a4774588d4"
|
||||
integrity sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ==
|
||||
dependencies:
|
||||
"@rc-component/async-validator" "^6.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/async-validator" "^5.1.0"
|
||||
"@rc-component/util" "^1.6.2"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/image@~1.9.0":
|
||||
@@ -3270,13 +3270,13 @@
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/input@~1.3.0", "@rc-component/input@~1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.3.1.tgz#230b8b59cdde8521d50f0eede63ddacb61cc0cd3"
|
||||
integrity sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==
|
||||
"@rc-component/input@~1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.3.0.tgz#a8c113000bbc39089cf75337bec68120115b9e05"
|
||||
integrity sha512-IUUNOdAuWuEvDEFFgfmwQl818tiDbvXwLgon4HL1q2hJeYkqrRrYwYhJN0zfPHGTDxs3gvyVC/C02D4hWFoIcA==
|
||||
dependencies:
|
||||
"@rc-component/resize-observer" "^1.1.1"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/mentions@~1.9.0":
|
||||
@@ -3290,15 +3290,15 @@
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/menu@~1.3.0", "@rc-component/menu@~1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.3.1.tgz#16cae71a01080914e8bac08359fccdda7bfce540"
|
||||
integrity sha512-pSZl9nBPgKgxN0aaW7NilIBEwWsc+43S+ulGdWAg9afak96dNOGWsGx0DLLBB1VQsAJvo6bQMTDzXoPlEHsBEw==
|
||||
"@rc-component/menu@~1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.3.0.tgz#fc70d81ca76ae6013b0d7955f20a2393adef04b3"
|
||||
integrity sha512-u3NfiwpiEgT177qa5Yxm5QsI8i/93EBGpWj8HYZQDnh2pCZ2xtQCe/+w3pSR2NlwKOZDTCKzEhEyD09mGphssA==
|
||||
dependencies:
|
||||
"@rc-component/motion" "^1.1.4"
|
||||
"@rc-component/overflow" "^1.0.0"
|
||||
"@rc-component/trigger" "^3.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/mini-decimal@^1.0.1":
|
||||
@@ -3308,12 +3308,12 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.0"
|
||||
|
||||
"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.3":
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.3.tgz#6a7bbe0a9f070bd11642168f741d40e78bb28565"
|
||||
integrity sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==
|
||||
"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.2.tgz#bd96e0fd16ee9d98c1d9be14198f003e367d8feb"
|
||||
integrity sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ==
|
||||
dependencies:
|
||||
"@rc-component/util" "^1.11.0"
|
||||
"@rc-component/util" "^1.2.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/mutate-observer@^2.0.1":
|
||||
@@ -3342,12 +3342,12 @@
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/pagination@~1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/pagination/-/pagination-1.3.0.tgz#ee66301e37a03974826fb3028a91a1aeacdcd0ac"
|
||||
integrity sha512-12ahTY+HPITg1L2bjWKXUqBJe/oOnpA2QsChdCjthqLVf/e19StiCsv8OLKpWoHbc+8PFEkNjRqRqrLoRBHjFw==
|
||||
"@rc-component/pagination@~1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.2.0.tgz"
|
||||
integrity sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==
|
||||
dependencies:
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/picker@~1.10.0":
|
||||
@@ -3377,10 +3377,10 @@
|
||||
"@rc-component/util" "^1.2.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/qrcode@~2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-2.0.0.tgz#ef4134c213002e7a43edbe609b24248914de4974"
|
||||
integrity sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==
|
||||
"@rc-component/qrcode@~1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.1.tgz"
|
||||
integrity sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.24.7"
|
||||
|
||||
@@ -3409,15 +3409,15 @@
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/select@~1.7.0", "@rc-component/select@~1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.7.1.tgz#cdda0ac185f00ebed1c85e7809ae1f7855a9f7ab"
|
||||
integrity sha512-GZ1cMJk2xQh0VHyOQjjG8drYL4iu24NcbkXioUcReQOCUr+ub/3fmRonZe6cRPEZhWMbJdeHsqnEltogDaZ5Tg==
|
||||
"@rc-component/select@~1.6.0", "@rc-component/select@~1.6.15":
|
||||
version "1.6.15"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.6.15.tgz#de2a3c8b020834cabd600b52de573a328c061eef"
|
||||
integrity sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g==
|
||||
dependencies:
|
||||
"@rc-component/overflow" "^1.0.0"
|
||||
"@rc-component/trigger" "^3.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/virtual-list" "^1.2.0"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
"@rc-component/virtual-list" "^1.0.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/slider@~1.0.1":
|
||||
@@ -3444,27 +3444,27 @@
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/table@~1.10.2":
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.10.2.tgz#7b052fd5eb2ccf6996a93eebeb7af7036a262c8b"
|
||||
integrity sha512-b3PjqB9Gp25p5t/zq+9QrbXbodkptT8/zvLmwgd2FNPUUtaYyDnQqfxeD5a7ao8E8lpinLHsi2u2vdfPhyNvAw==
|
||||
"@rc-component/table@~1.10.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.10.0.tgz#7a98d68176f23f50a762df464f4c9142e7db3942"
|
||||
integrity sha512-SjtpcCf+rL7dDc62GKT3rXTdERjVuJvRiqjpU7g0Jc/ewCifXynHc7Nm3Em1XsD+WhGrgQtxNDScI/0+Lpfr0w==
|
||||
dependencies:
|
||||
"@rc-component/context" "^2.0.1"
|
||||
"@rc-component/resize-observer" "^1.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.1.0"
|
||||
"@rc-component/virtual-list" "^1.0.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/tabs@~1.9.1":
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.9.1.tgz#52b9cb0392c718fba43e7d558f46f6c910d19acb"
|
||||
integrity sha512-6mY08Fce6aNOHuGsxbzT+f2ekgL9mg1cGGHkittMlVGymjGg+kGupu5v90sRxcUd/paRU9jclLLXtF/PkK1FUA==
|
||||
"@rc-component/tabs@~1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.9.0.tgz#8f3e3755450e5a90d240d1ed3dc140d520b1fbef"
|
||||
integrity sha512-tn1slmbbaTyt8mgwyWJcT8jo/qNiYUs6u1H7OgGQt9faYO06BJIkU5cTmMqORzIrNmSEeeUY6pD5i+JlqSHYhg==
|
||||
dependencies:
|
||||
"@rc-component/dropdown" "~1.0.0"
|
||||
"@rc-component/menu" "~1.3.0"
|
||||
"@rc-component/motion" "^1.1.3"
|
||||
"@rc-component/resize-observer" "^1.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/tooltip@~1.4.0":
|
||||
@@ -3486,30 +3486,30 @@
|
||||
"@rc-component/util" "^1.7.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/tree-select@~1.10.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.10.0.tgz#72e337fd58591f677404189cb3e9d3f718cd3332"
|
||||
integrity sha512-E1U4pn2LAbXEhLJdzIzid7WYbIuFbkTIctuFoeC6weppf8UbPR3+YYB6/ay0c0ksand4gXMRQpa1Z60Auo7VJA==
|
||||
"@rc-component/tree-select@~1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.9.0.tgz#13ea516478b6cb558e04181abb0a01ae6fbdd31f"
|
||||
integrity sha512-GXcFe15a+trUl1/J3OHWQhsVWFpwFpGFK2cqYWZ1sK22Zs3KZTvMwDpzr75PIo1s6QVioVxpE/pRwRopkeDQ6w==
|
||||
dependencies:
|
||||
"@rc-component/select" "~1.7.0"
|
||||
"@rc-component/select" "~1.6.0"
|
||||
"@rc-component/tree" "~1.3.0"
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/tree@~1.3.0", "@rc-component/tree@~1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.3.2.tgz#4b0c13564314eff61ca948c18ef923b87c9d7e44"
|
||||
integrity sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA==
|
||||
"@rc-component/tree@~1.3.0", "@rc-component/tree@~1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.3.1.tgz#6983ca6bd9d5f6d04dd7258d00cb0fe71cdfe661"
|
||||
integrity sha512-zlL0PW0bTFlveTtLcA01VD/yMWKK73EywItFMgIZUY5sb6tMOAw7zV6qGzqldufqrV93ZWQB4H3NBNoTMCueJA==
|
||||
dependencies:
|
||||
"@rc-component/motion" "^1.0.0"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/virtual-list" "^1.2.0"
|
||||
"@rc-component/util" "^1.8.1"
|
||||
"@rc-component/virtual-list" "^1.0.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.1":
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-3.9.1.tgz#3f730315c558bc392921a563ac9109a1d094ef23"
|
||||
integrity sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q==
|
||||
"@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.0":
|
||||
version "3.9.0"
|
||||
resolved "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.9.0.tgz"
|
||||
integrity sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==
|
||||
dependencies:
|
||||
"@rc-component/motion" "^1.1.4"
|
||||
"@rc-component/portal" "^2.2.0"
|
||||
@@ -3517,18 +3517,18 @@
|
||||
"@rc-component/util" "^1.2.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/upload@~1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/upload/-/upload-1.1.1.tgz#90d16edcdaeb104ffa9111fd0b428061de7c21ac"
|
||||
integrity sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==
|
||||
"@rc-component/upload@~1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.0.tgz"
|
||||
integrity sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==
|
||||
dependencies:
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/util@^1.10.1", "@rc-component/util@^1.11.0", "@rc-component/util@^1.11.1", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.7.0", "@rc-component/util@^1.9.0":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.11.1.tgz#07d698908339c55648e4f974afa739345e65b483"
|
||||
integrity sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==
|
||||
"@rc-component/util@^1.1.0", "@rc-component/util@^1.10.1", "@rc-component/util@^1.11.0", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.6.2", "@rc-component/util@^1.7.0", "@rc-component/util@^1.8.1", "@rc-component/util@^1.9.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.11.0.tgz#965c8b44a3f57fc96dc14e5072afbe32e422fd4d"
|
||||
integrity sha512-jHG3/BYgUWiP5c7RZHiaUNToyw1L3nlPSKG2RPu+YoiD9b3ajiJwBWhsjO+ZELmCsKFAjNR5DelbKdlF0e2BDA==
|
||||
dependencies:
|
||||
is-mobile "^5.0.0"
|
||||
react-is "^18.2.0"
|
||||
@@ -3543,16 +3543,6 @@
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/virtual-list@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/virtual-list/-/virtual-list-1.2.0.tgz#3c9ee8cd7d10da334a2a06ad86ca234e2792a381"
|
||||
integrity sha512-iavRm1Jo4GDbASQwdGa7jFyk93RvSOo9xHyBT4QL1pgFJj/Fdf1G+3RErH7/7BmAMvx2AkF62mjGYxDbXsK9TQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.20.0"
|
||||
"@rc-component/resize-observer" "^1.0.1"
|
||||
"@rc-component/util" "^1.4.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@redocly/ajv@^8.18.0":
|
||||
version "8.18.3"
|
||||
resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.18.3.tgz#a925753d9a33375219f1b2ba91aef320f9929577"
|
||||
@@ -4932,110 +4922,110 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@8.61.1", "@typescript-eslint/eslint-plugin@^8.59.3":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz#6e4b7fee21f1983308e9e9b634ecbaf702c86006"
|
||||
integrity sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==
|
||||
"@typescript-eslint/eslint-plugin@8.61.0", "@typescript-eslint/eslint-plugin@^8.59.3":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz#db20271974b94a3a54d3b9544e5f5b3481448400"
|
||||
integrity sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.12.2"
|
||||
"@typescript-eslint/scope-manager" "8.61.1"
|
||||
"@typescript-eslint/type-utils" "8.61.1"
|
||||
"@typescript-eslint/utils" "8.61.1"
|
||||
"@typescript-eslint/visitor-keys" "8.61.1"
|
||||
"@typescript-eslint/scope-manager" "8.61.0"
|
||||
"@typescript-eslint/type-utils" "8.61.0"
|
||||
"@typescript-eslint/utils" "8.61.0"
|
||||
"@typescript-eslint/visitor-keys" "8.61.0"
|
||||
ignore "^7.0.5"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/parser@8.61.1", "@typescript-eslint/parser@^8.61.0":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.61.1.tgz#881fba60b50636249cdeea2e547bf75715254c72"
|
||||
integrity sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==
|
||||
"@typescript-eslint/parser@8.61.0", "@typescript-eslint/parser@^8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.61.0.tgz#1afe73c9ccce16b7a26d6b95f9400b0ccc34af87"
|
||||
integrity sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/typescript-estree" "8.61.1"
|
||||
"@typescript-eslint/visitor-keys" "8.61.1"
|
||||
"@typescript-eslint/scope-manager" "8.61.0"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
"@typescript-eslint/typescript-estree" "8.61.0"
|
||||
"@typescript-eslint/visitor-keys" "8.61.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/project-service@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.61.1.tgz#fcd9739964a40867eed55f1ac318d3909f24b4af"
|
||||
integrity sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==
|
||||
"@typescript-eslint/project-service@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.61.0.tgz#417a2feac32e8ebd336d63f068c3b42b736ea1ac"
|
||||
integrity sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils" "^8.61.1"
|
||||
"@typescript-eslint/types" "^8.61.1"
|
||||
"@typescript-eslint/tsconfig-utils" "^8.61.0"
|
||||
"@typescript-eslint/types" "^8.61.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz#2479921a40fdb0afa18f5838fae6167264b417b2"
|
||||
integrity sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==
|
||||
"@typescript-eslint/scope-manager@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz#93c2520d05653fe65eb9ee98efc74fd0134a7852"
|
||||
integrity sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/visitor-keys" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
"@typescript-eslint/visitor-keys" "8.61.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@8.61.1":
|
||||
"@typescript-eslint/tsconfig-utils@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz#05d6e3ff20001674ebcd22d03dac29ee448043ba"
|
||||
integrity sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@^8.61.0":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz#ca88080e0cf191d49516d7f300b67aa090d2254f"
|
||||
integrity sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@^8.61.1":
|
||||
version "8.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz#9440a673581c6d9de308c4d5803dd52ed5d71729"
|
||||
integrity sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==
|
||||
|
||||
"@typescript-eslint/type-utils@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz#8fa18f453ee140893b47d339d1a6b64cac9b08a1"
|
||||
integrity sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==
|
||||
"@typescript-eslint/type-utils@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz#50219b57e6b89cecfb1a15f093b15ec9ee019974"
|
||||
integrity sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/typescript-estree" "8.61.1"
|
||||
"@typescript-eslint/utils" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
"@typescript-eslint/typescript-estree" "8.61.0"
|
||||
"@typescript-eslint/utils" "8.61.0"
|
||||
debug "^4.4.3"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/types@8.61.1":
|
||||
"@typescript-eslint/types@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.61.0.tgz#0ddb46e012a4288292950bdd253db42f278ce64d"
|
||||
integrity sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==
|
||||
|
||||
"@typescript-eslint/types@^8.61.0":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.61.1.tgz#0c51f518e4e6848371a1c988e859d59eb7522d5a"
|
||||
integrity sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==
|
||||
|
||||
"@typescript-eslint/types@^8.61.1":
|
||||
version "8.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.62.0.tgz#601427c10203d9f0f34f0b3e474df735eb12b593"
|
||||
integrity sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz#febbe70365ac0bf7611262b61b338fc8797965c7"
|
||||
integrity sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==
|
||||
"@typescript-eslint/typescript-estree@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz#98ca47260bbf627fc28f018b3a0abf00e3090690"
|
||||
integrity sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service" "8.61.1"
|
||||
"@typescript-eslint/tsconfig-utils" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/visitor-keys" "8.61.1"
|
||||
"@typescript-eslint/project-service" "8.61.0"
|
||||
"@typescript-eslint/tsconfig-utils" "8.61.0"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
"@typescript-eslint/visitor-keys" "8.61.0"
|
||||
debug "^4.4.3"
|
||||
minimatch "^10.2.2"
|
||||
semver "^7.7.3"
|
||||
tinyglobby "^0.2.15"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/utils@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.61.1.tgz#ffd1054de7dd33b7873cd6c6713ec6b0366316d3"
|
||||
integrity sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==
|
||||
"@typescript-eslint/utils@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.61.0.tgz#ed3546a052787e84ea6c5064d0919fc5eea8522f"
|
||||
integrity sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.9.1"
|
||||
"@typescript-eslint/scope-manager" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/typescript-estree" "8.61.1"
|
||||
"@typescript-eslint/scope-manager" "8.61.0"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
"@typescript-eslint/typescript-estree" "8.61.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.61.1":
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz#546cf102b4efdb72a9a08e63a1b0d7d745eb66eb"
|
||||
integrity sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==
|
||||
"@typescript-eslint/visitor-keys@8.61.0":
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz#39b4e1ab8936d23bea973d39fd092f9aa21f275e"
|
||||
integrity sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.61.1"
|
||||
"@typescript-eslint/types" "8.61.0"
|
||||
eslint-visitor-keys "^5.0.0"
|
||||
|
||||
"@ungap/structured-clone@^1.0.0":
|
||||
@@ -5392,54 +5382,54 @@ ansis@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/ansis/-/ansis-3.17.0.tgz#fa8d9c2a93fe7d1177e0c17f9eeb562a58a832d7"
|
||||
integrity sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==
|
||||
|
||||
antd@^6.4.4:
|
||||
version "6.4.4"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.4.4.tgz#a422610959b37ac4d4b766dbaac67ea2d8fd0785"
|
||||
integrity sha512-lgPz4KhfhiYddV/qPYo0ieqWimCVgV2OQF72mbeGNixE753JWNnmEc7UNGy08wBS/zZ7hxrmX0pc5aX7EUaIIg==
|
||||
antd@^6.4.3:
|
||||
version "6.4.3"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.4.3.tgz#80a7aab9c13c35daa0e0e7eea80585ba57cb7203"
|
||||
integrity sha512-6H2avkxCGfxcF67r3J2mwm9Ck50el1pks/73vfM1wDsPL/tPtj5vHuauMgJFnrqmq7CH3g8aoZ0VBQbt+jpAsw==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^8.0.1"
|
||||
"@ant-design/cssinjs" "^2.1.2"
|
||||
"@ant-design/cssinjs-utils" "^2.1.2"
|
||||
"@ant-design/fast-color" "^3.0.1"
|
||||
"@ant-design/icons" "^6.2.5"
|
||||
"@ant-design/icons" "^6.2.3"
|
||||
"@ant-design/react-slick" "~2.0.0"
|
||||
"@babel/runtime" "^7.29.2"
|
||||
"@rc-component/cascader" "~1.16.1"
|
||||
"@rc-component/cascader" "~1.15.0"
|
||||
"@rc-component/checkbox" "~2.0.0"
|
||||
"@rc-component/collapse" "~1.2.0"
|
||||
"@rc-component/color-picker" "~3.1.1"
|
||||
"@rc-component/dialog" "~1.9.0"
|
||||
"@rc-component/drawer" "~1.4.2"
|
||||
"@rc-component/dropdown" "~1.0.2"
|
||||
"@rc-component/form" "~1.8.3"
|
||||
"@rc-component/form" "~1.8.1"
|
||||
"@rc-component/image" "~1.9.0"
|
||||
"@rc-component/input" "~1.3.1"
|
||||
"@rc-component/input" "~1.3.0"
|
||||
"@rc-component/input-number" "~1.6.2"
|
||||
"@rc-component/mentions" "~1.9.0"
|
||||
"@rc-component/menu" "~1.3.1"
|
||||
"@rc-component/motion" "^1.3.3"
|
||||
"@rc-component/menu" "~1.3.0"
|
||||
"@rc-component/motion" "^1.3.2"
|
||||
"@rc-component/mutate-observer" "^2.0.1"
|
||||
"@rc-component/notification" "~2.0.7"
|
||||
"@rc-component/pagination" "~1.3.0"
|
||||
"@rc-component/pagination" "~1.2.0"
|
||||
"@rc-component/picker" "~1.10.0"
|
||||
"@rc-component/progress" "~1.0.2"
|
||||
"@rc-component/qrcode" "~2.0.0"
|
||||
"@rc-component/qrcode" "~1.1.1"
|
||||
"@rc-component/rate" "~1.0.1"
|
||||
"@rc-component/resize-observer" "^1.1.2"
|
||||
"@rc-component/segmented" "~1.3.0"
|
||||
"@rc-component/select" "~1.7.1"
|
||||
"@rc-component/select" "~1.6.15"
|
||||
"@rc-component/slider" "~1.0.1"
|
||||
"@rc-component/steps" "~1.2.2"
|
||||
"@rc-component/switch" "~1.0.3"
|
||||
"@rc-component/table" "~1.10.2"
|
||||
"@rc-component/tabs" "~1.9.1"
|
||||
"@rc-component/table" "~1.10.0"
|
||||
"@rc-component/tabs" "~1.9.0"
|
||||
"@rc-component/tooltip" "~1.4.0"
|
||||
"@rc-component/tour" "~2.4.0"
|
||||
"@rc-component/tree" "~1.3.2"
|
||||
"@rc-component/tree-select" "~1.10.0"
|
||||
"@rc-component/trigger" "^3.9.1"
|
||||
"@rc-component/upload" "~1.1.1"
|
||||
"@rc-component/util" "^1.11.1"
|
||||
"@rc-component/tree" "~1.3.1"
|
||||
"@rc-component/tree-select" "~1.9.0"
|
||||
"@rc-component/trigger" "^3.9.0"
|
||||
"@rc-component/upload" "~1.1.0"
|
||||
"@rc-component/util" "^1.11.0"
|
||||
clsx "^2.1.1"
|
||||
dayjs "^1.11.11"
|
||||
scroll-into-view-if-needed "^3.1.0"
|
||||
@@ -5698,10 +5688,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
baseline-browser-mapping@^2.10.37, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.37"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz#3e636475b6b293244e2b23e2c71a2ab9d9e6ba7d"
|
||||
integrity sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==
|
||||
baseline-browser-mapping@^2.10.35, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.35"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz#f0f2232e0de2d2f82cc491bcf830b05ed05937c6"
|
||||
integrity sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==
|
||||
|
||||
batch@0.6.1:
|
||||
version "0.6.1"
|
||||
@@ -5944,10 +5934,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001799:
|
||||
version "1.0.30001799"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55"
|
||||
integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001797:
|
||||
version "1.0.30001797"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz#1332709e1439f01ff92085dd17001e0a45897ec0"
|
||||
integrity sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==
|
||||
|
||||
ccount@^2.0.0:
|
||||
version "2.0.1"
|
||||
@@ -7262,10 +7252,17 @@ domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
|
||||
dompurify@^3.3.1, dompurify@^3.4.0:
|
||||
version "3.4.11"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.11.tgz#29c8ba496475f279ef4015784068452fb14a0680"
|
||||
integrity sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==
|
||||
dompurify@^3.3.1:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.2.tgz#f0ff81be682c485505097ba8195a058d8f575218"
|
||||
integrity sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==
|
||||
optionalDependencies:
|
||||
"@types/trusted-types" "^2.0.7"
|
||||
|
||||
dompurify@^3.4.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.1.tgz#521d04483ac12631b2aedf434a5f5390933b8789"
|
||||
integrity sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==
|
||||
optionalDependencies:
|
||||
"@types/trusted-types" "^2.0.7"
|
||||
|
||||
@@ -8768,9 +8765,9 @@ http-parser-js@>=0.5.1:
|
||||
integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==
|
||||
|
||||
http-proxy-middleware@^2.0.9:
|
||||
version "2.0.10"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz#b2df7b705203d7a8c269ac8450cf96b00c532f94"
|
||||
integrity sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==
|
||||
version "2.0.9"
|
||||
resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz"
|
||||
integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==
|
||||
dependencies:
|
||||
"@types/http-proxy" "^1.17.8"
|
||||
http-proxy "^1.18.1"
|
||||
@@ -12273,10 +12270,10 @@ prettier-linter-helpers@^1.0.1:
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^3.8.4:
|
||||
version "3.8.4"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.4.tgz#f334f013ac04a96676f24dabc23c1c4ae1bae411"
|
||||
integrity sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==
|
||||
prettier@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0"
|
||||
integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==
|
||||
|
||||
pretty-error@^4.0.0:
|
||||
version "4.0.0"
|
||||
@@ -14502,15 +14499,15 @@ types-ramda@^0.30.1:
|
||||
dependencies:
|
||||
ts-toolbelt "^9.6.0"
|
||||
|
||||
typescript-eslint@^8.61.1:
|
||||
version "8.61.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.61.1.tgz#7c224a9a643b7f42d295c67a75c1e30fee8c3eaa"
|
||||
integrity sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==
|
||||
typescript-eslint@^8.61.0:
|
||||
version "8.61.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.61.0.tgz#6927fb94f5f29623e370d33fd9fa61f15d6d996b"
|
||||
integrity sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.61.1"
|
||||
"@typescript-eslint/parser" "8.61.1"
|
||||
"@typescript-eslint/typescript-estree" "8.61.1"
|
||||
"@typescript-eslint/utils" "8.61.1"
|
||||
"@typescript-eslint/eslint-plugin" "8.61.0"
|
||||
"@typescript-eslint/parser" "8.61.0"
|
||||
"@typescript-eslint/typescript-estree" "8.61.0"
|
||||
"@typescript-eslint/utils" "8.61.0"
|
||||
|
||||
typescript@~6.0.3:
|
||||
version "6.0.3"
|
||||
@@ -15009,9 +15006,9 @@ webpack-dev-middleware@^7.4.2:
|
||||
schema-utils "^4.0.0"
|
||||
|
||||
webpack-dev-server@^5.2.2:
|
||||
version "5.2.5"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.5.tgz#648fceaac6a5736b0935e5c1e55d6aa1d0626119"
|
||||
integrity sha512-4wZtCquSuv9CKX8oybo+mqxtxZqWz47uM1Ch94lxowBztOhWCbhqvRbfC/mODOwxgV2brY+JGZpHq58/SuVFYg==
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.4.tgz#6e6306ce59848ed322c235e48b326632b1eed6d6"
|
||||
integrity sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==
|
||||
dependencies:
|
||||
"@types/bonjour" "^3.5.13"
|
||||
"@types/connect-history-api-fallback" "^1.5.4"
|
||||
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.17.2 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
version: 0.16.1 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 16.7.27
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
|
||||
|
||||
# superset
|
||||
|
||||

|
||||

|
||||
|
||||
Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
|
||||
@@ -111,6 +111,9 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| init.resources | object | `{}` | |
|
||||
| init.tolerations | list | `[]` | |
|
||||
| init.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to init job |
|
||||
| initImage.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| initImage.repository | string | `"apache/superset"` | |
|
||||
| initImage.tag | string | `"dockerize"` | |
|
||||
| nameOverride | string | `nil` | Provide a name to override the name of the chart |
|
||||
| nodeSelector | object | `{}` | |
|
||||
| postgresql | object | see `values.yaml` | Configuration values for the postgresql dependency. ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql |
|
||||
@@ -216,7 +219,6 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetNode.extraContainers | list | `[]` | Launch additional containers into supersetNode pod |
|
||||
| supersetNode.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetNode.initContainers | list | a container waiting for postgres | Init containers |
|
||||
| supersetNode.lifecycle | object | `{}` | Container lifecycle hooks, e.g. a preStop sleep so the Service/Ingress stops routing to the pod before gunicorn receives SIGTERM |
|
||||
| supersetNode.livenessProbe.failureThreshold | int | `3` | |
|
||||
| supersetNode.livenessProbe.httpGet.path | string | `"/health"` | |
|
||||
| supersetNode.livenessProbe.httpGet.port | string | `"http"` | |
|
||||
@@ -249,7 +251,6 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetNode.startupProbe.successThreshold | int | `1` | |
|
||||
| supersetNode.startupProbe.timeoutSeconds | int | `1` | |
|
||||
| supersetNode.strategy | object | `{}` | |
|
||||
| supersetNode.terminationGracePeriodSeconds | string | `nil` | Pod termination grace period (seconds). Set greater than GUNICORN_TIMEOUT so in-flight requests can drain before SIGKILL |
|
||||
| supersetNode.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to supersetNode deployments |
|
||||
| supersetWebsockets.affinity | object | `{}` | Affinity to be added to supersetWebsockets deployment |
|
||||
| supersetWebsockets.command | list | `[]` | |
|
||||
@@ -313,7 +314,6 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetWorker.extraContainers | list | `[]` | Launch additional containers into supersetWorker pod |
|
||||
| supersetWorker.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetWorker.initContainers | list | a container waiting for postgres and redis | Init container |
|
||||
| supersetWorker.lifecycle | object | `{}` | Container lifecycle hooks for the worker pod |
|
||||
| supersetWorker.livenessProbe.exec.command | list | a `celery inspect ping` command | Liveness probe command |
|
||||
| supersetWorker.livenessProbe.failureThreshold | int | `3` | |
|
||||
| supersetWorker.livenessProbe.initialDelaySeconds | int | `120` | |
|
||||
@@ -334,7 +334,6 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetWorker.resources | object | `{}` | Resource settings for the supersetWorker pods - these settings overwrite might existing values from the global resources object defined above. |
|
||||
| supersetWorker.startupProbe | object | `{}` | No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic) |
|
||||
| supersetWorker.strategy | object | `{}` | |
|
||||
| supersetWorker.terminationGracePeriodSeconds | string | `nil` | Pod termination grace period (seconds) for the worker pod so in-flight tasks can drain before SIGKILL |
|
||||
| supersetWorker.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to supersetWorker deployments |
|
||||
| tolerations | list | `[]` | |
|
||||
| topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to all deployments |
|
||||
|
||||
@@ -126,7 +126,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryBeat.extraContainers }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryBeat.extraContainers) . | nindent 8 }}
|
||||
{{- toYaml .Values.supersetCeleryBeat.extraContainers | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -121,7 +121,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryFlower.extraContainers }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryFlower.extraContainers) . | nindent 8 }}
|
||||
{{- toYaml .Values.supersetCeleryFlower.extraContainers | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -134,9 +134,6 @@ spec:
|
||||
{{- if .Values.supersetWorker.livenessProbe }}
|
||||
livenessProbe: {{- .Values.supersetWorker.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.lifecycle }}
|
||||
lifecycle: {{- .Values.supersetWorker.lifecycle | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- if .Values.supersetWorker.resources }}
|
||||
{{- toYaml .Values.supersetWorker.resources | nindent 12 }}
|
||||
@@ -144,7 +141,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.extraContainers }}
|
||||
{{- tpl (toYaml .Values.supersetWorker.extraContainers) . | nindent 8 }}
|
||||
{{- toYaml .Values.supersetWorker.extraContainers | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
@@ -173,9 +170,6 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ .Values.supersetWorker.terminationGracePeriodSeconds }}
|
||||
{{- end }}
|
||||
{{- if .Values.imagePullSecrets }}
|
||||
imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -120,7 +120,7 @@ spec:
|
||||
livenessProbe: {{- .Values.supersetWebsockets.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWebsockets.extraContainers }}
|
||||
{{- tpl (toYaml .Values.supersetWebsockets.extraContainers) . | nindent 8 }}
|
||||
{{- toYaml .Values.supersetWebsockets.extraContainers | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -144,9 +144,6 @@ spec:
|
||||
{{- if .Values.supersetNode.livenessProbe }}
|
||||
livenessProbe: {{- .Values.supersetNode.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.lifecycle }}
|
||||
lifecycle: {{- .Values.supersetNode.lifecycle | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- if .Values.supersetNode.resources }}
|
||||
{{- toYaml .Values.supersetNode.resources | nindent 12 }}
|
||||
@@ -154,7 +151,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.extraContainers }}
|
||||
{{- tpl (toYaml .Values.supersetNode.extraContainers) . | nindent 8 }}
|
||||
{{- toYaml .Values.supersetNode.extraContainers | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
@@ -183,9 +180,6 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ .Values.supersetNode.terminationGracePeriodSeconds }}
|
||||
{{- end }}
|
||||
{{- if .Values.imagePullSecrets }}
|
||||
imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -104,7 +104,7 @@ spec:
|
||||
command: {{ tpl (toJson .Values.init.command) . }}
|
||||
resources: {{- toYaml .Values.init.resources | nindent 10 }}
|
||||
{{- if .Values.init.extraContainers }}
|
||||
{{- tpl (toYaml .Values.init.extraContainers) . | nindent 6 }}
|
||||
{{- toYaml .Values.init.extraContainers | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -194,6 +194,11 @@ image:
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
initImage:
|
||||
repository: apache/superset
|
||||
tag: dockerize
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8088
|
||||
@@ -269,7 +274,7 @@ supersetNode:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec /usr/bin/run-server.sh"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh"
|
||||
connections:
|
||||
# -- Change in case of bringing your own redis and then also set redis.enabled:false
|
||||
redis_host: "{{ .Release.Name }}-redis-headless"
|
||||
@@ -298,29 +303,15 @@ supersetNode:
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
# opening a /dev/tcp fd performs a TCP connect without sending any
|
||||
# payload (avoids postgres "incomplete startup packet" log noise);
|
||||
# no external `dockerize`, `nc`, or busybox needed. SECONDS-based
|
||||
# deadline mirrors the prior `dockerize -timeout 120s` behaviour.
|
||||
SECONDS=0
|
||||
until (exec 3<>/dev/tcp/"$DB_HOST"/"$DB_PORT") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for postgres at $DB_HOST:$DB_PORT after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for postgres at $DB_HOST:$DB_PORT (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "postgres at $DB_HOST:$DB_PORT is up"
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -369,12 +360,6 @@ supersetNode:
|
||||
failureThreshold: 3
|
||||
periodSeconds: 15
|
||||
successThreshold: 1
|
||||
# -- Container lifecycle hooks, e.g. a preStop sleep so the Service/Ingress
|
||||
# stops routing to the pod before gunicorn receives SIGTERM
|
||||
lifecycle: {}
|
||||
# -- Pod termination grace period (seconds). Set greater than GUNICORN_TIMEOUT so
|
||||
# in-flight requests can drain before SIGKILL
|
||||
terminationGracePeriodSeconds: ~
|
||||
# -- Resource settings for the supersetNode pods - these settings overwrite might existing values from the global resources object defined above.
|
||||
resources: {}
|
||||
# limits:
|
||||
@@ -415,38 +400,22 @@ supersetWorker:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec celery --app=superset.tasks.celery_app:app worker"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker"
|
||||
# -- If true, forces deployment to reload on each upgrade
|
||||
forceReload: false
|
||||
# -- Init container
|
||||
# @default -- a container waiting for postgres and redis
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -495,10 +464,6 @@ supersetWorker:
|
||||
failureThreshold: 3
|
||||
periodSeconds: 60
|
||||
successThreshold: 1
|
||||
# -- Container lifecycle hooks for the worker pod
|
||||
lifecycle: {}
|
||||
# -- Pod termination grace period (seconds) for the worker pod so in-flight tasks can drain before SIGKILL
|
||||
terminationGracePeriodSeconds: ~
|
||||
# -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic)
|
||||
startupProbe: {}
|
||||
# -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic)
|
||||
@@ -523,38 +488,22 @@ supersetCeleryBeat:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule"
|
||||
# -- If true, forces deployment to reload on each upgrade
|
||||
forceReload: false
|
||||
# -- List of init containers
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -645,31 +594,15 @@ supersetCeleryFlower:
|
||||
# @default -- a container waiting for postgres and redis
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -831,26 +764,15 @@ init:
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
until (exec 3<>/dev/tcp/"$DB_HOST"/"$DB_PORT") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for postgres at $DB_HOST:$DB_PORT after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for postgres at $DB_HOST:$DB_PORT (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "postgres at $DB_HOST:$DB_PORT is up"
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
|
||||
@@ -38,20 +38,14 @@ dependencies = [
|
||||
# no bounds for apache-superset-core until we have a stable version
|
||||
"apache-superset-core",
|
||||
"backoff>=1.8.0",
|
||||
# cachetools is used directly by ``superset.db_engine_specs.aws_iam`` (TTLCache).
|
||||
# It used to be installed transitively via ``google-auth`` (<2.53), but
|
||||
# ``google-auth`` 2.53+ dropped it, so Superset must declare it
|
||||
# explicitly to keep fresh ``pip install apache-superset`` working
|
||||
# without the ``base.txt`` lock file (#40962).
|
||||
"cachetools>=6.2.1, <7",
|
||||
"celery>=5.3.6, <6.0.0",
|
||||
"click>=8.4.0",
|
||||
"click-option-group",
|
||||
"colorama",
|
||||
"flask-cors>=6.0.5, <7.0",
|
||||
"flask-cors>=6.0.0, <7.0",
|
||||
"croniter>=6.2.2",
|
||||
"cron-descriptor",
|
||||
"cryptography>=48.0.0, <49.0.0",
|
||||
"cryptography>=42.0.4, <47.0.0",
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <4.0.0",
|
||||
"flask-appbuilder>=5.2.1, <6.0.0",
|
||||
@@ -63,7 +57,7 @@ dependencies = [
|
||||
"flask-session>=0.4.0, <1.0",
|
||||
"flask-wtf>=1.3.0, <2.0",
|
||||
"geopy",
|
||||
"greenlet<=3.5.1, >=3.5.1",
|
||||
"greenlet>=3.0.3, <=3.5.0",
|
||||
"gunicorn>=25.3.0, <26; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
# holidays>=0.45 required for security fix
|
||||
@@ -73,12 +67,10 @@ dependencies = [
|
||||
"jsonpath-ng>=1.8.0, <2",
|
||||
"Mako>=1.2.2",
|
||||
"markdown>=3.10.2",
|
||||
# marshmallow 4 compatibility: see superset/marshmallow_compatibility.py for a
|
||||
# Flask-AppBuilder workaround. Tracking issue:
|
||||
# https://github.com/apache/superset/issues/33162
|
||||
"marshmallow>=3.0, <5",
|
||||
# marshmallow>=4 has issues: https://github.com/apache/superset/issues/33162
|
||||
"marshmallow>=3.0, <4",
|
||||
"marshmallow-union>=0.1",
|
||||
"msgpack>=1.2.0, <1.3",
|
||||
"msgpack>=1.0.0, <1.2",
|
||||
"nh3>=0.3.5, <0.4",
|
||||
"numpy>1.23.5, <2.3",
|
||||
"packaging",
|
||||
@@ -98,7 +90,7 @@ dependencies = [
|
||||
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
|
||||
"pygeohash",
|
||||
"pyarrow>=24.0.0, <25", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyyaml>=6.0.3, <7.0.0",
|
||||
"pyyaml>=6.0.0, <7.0.0",
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <6.0",
|
||||
"rison>=2.0.0, <3.0",
|
||||
@@ -149,7 +141,7 @@ drill = ["sqlalchemy-drill>=1.1.10, <2"]
|
||||
druid = ["pydruid>=0.6.5,<0.7"]
|
||||
duckdb = ["duckdb>=1.5.2,<2", "duckdb-engine>=0.17.0"]
|
||||
dynamodb = ["pydynamodb>=0.4.2"]
|
||||
solr = ["sqlalchemy-solr >= 0.2.4.3"]
|
||||
solr = ["sqlalchemy-solr >= 0.2.0"]
|
||||
elasticsearch = ["elasticsearch-dbapi>=0.2.13, <0.3.0"]
|
||||
exasol = ["sqlalchemy-exasol>=2.4.0, <8.0"]
|
||||
excel = ["xlrd>=2.0.2, <2.1"]
|
||||
@@ -191,7 +183,7 @@ pinot = ["pinotdb>=5.0.0, <10.0.0"]
|
||||
playwright = ["playwright>=1.60.0, <2"]
|
||||
postgres = ["psycopg2-binary==2.9.12"]
|
||||
presto = ["pyhive[presto]>=0.6.5"]
|
||||
trino = ["trino>=0.337.0"]
|
||||
trino = ["trino>=0.328.0"]
|
||||
prophet = ["prophet>=1.1.6, <2"]
|
||||
redshift = ["sqlalchemy-redshift>=0.8.1, <0.9"]
|
||||
risingwave = ["sqlalchemy-risingwave"]
|
||||
@@ -216,7 +208,7 @@ netezza = ["nzalchemy>=11.0.2"]
|
||||
starrocks = ["starrocks>=1.3.3, <2"]
|
||||
doris = ["pydoris>=1.0.0, <2.0.0"]
|
||||
oceanbase = ["oceanbase_py>=0.0.1.2"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.22", "ydb-sqlglot-plugin>=0.2.5"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.2", "ydb-sqlglot-plugin>=0.2.5"]
|
||||
development = [
|
||||
# no bounds for apache-superset-extensions-cli until a stable version
|
||||
"apache-superset-extensions-cli",
|
||||
@@ -224,7 +216,7 @@ development = [
|
||||
"docker",
|
||||
"flask-testing",
|
||||
"freezegun",
|
||||
"grpcio>=1.81.1",
|
||||
"grpcio>=1.55.3",
|
||||
"openapi-spec-validator",
|
||||
"parameterized",
|
||||
"pip",
|
||||
@@ -375,6 +367,7 @@ select = [
|
||||
|
||||
ignore = [
|
||||
"S101",
|
||||
"PT004", # Fixtures that don't return values - underscore prefix conflicts with pytest usage
|
||||
"PT006",
|
||||
"T201",
|
||||
"N999",
|
||||
|
||||
27
pytest.ini
27
pytest.ini
@@ -18,30 +18,5 @@
|
||||
testpaths =
|
||||
tests
|
||||
python_files = *_test.py test_*.py *_tests.py *viz/utils.py
|
||||
# `-p no:warnings` temporarily disabled in favor of more finely tuned `filterwarnings`.
|
||||
#addopts = -p no:warnings
|
||||
addopts = -p no:warnings
|
||||
asyncio_mode = auto
|
||||
|
||||
# `ignore` is effectively equivalent to `-p no:warnings`.
|
||||
# Always print RemovedIn20Warning when SQLALCHEMY_WARN_20=1.
|
||||
# Additionally, raise errors for refactored RemovedIn20Warning cases to prevent regression.
|
||||
filterwarnings =
|
||||
ignore
|
||||
always::sqlalchemy.exc.RemovedIn20Warning
|
||||
error:Passing a string to Connection.execute\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"Query" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SavedQuery" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SqlaTable" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SqlMetric" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TableColumn" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TaggedObject" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``as_declarative\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The autoload parameter is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The connection.execute\(\) method:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The `database` package is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``declarative_base\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The Engine.execute\(\) method is considered legacy:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The legacy calling style of select\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The "whens" argument to case:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"User" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
|
||||
@@ -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>=48.0.0,<49.0.0
|
||||
cryptography>=46.0.7,<47.0.0
|
||||
# Security: Snyk - XSS vulnerability in Mako templates
|
||||
mako>=1.3.11,<2.0.0
|
||||
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
|
||||
@@ -44,10 +44,11 @@ async_timeout>=4.0.0,<5.0.0
|
||||
# a bit of attention to bump.
|
||||
apispec>=6.0.0,<6.7.0
|
||||
|
||||
# 1.4.1 introduced a memory regression that exhausts memory in the test suite
|
||||
# (https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665). 1.4.2
|
||||
# claimed a fix but did not address the root cause; only 1.5.0 actually fixes it.
|
||||
marshmallow-sqlalchemy>=1.5.0
|
||||
# 1.4.1 appears to use much more memory, where the python test suite runs out of memory
|
||||
# causing CI to fail. 1.4.0 is the last version that works.
|
||||
# https://marshmallow-sqlalchemy.readthedocs.io/en/latest/changelog.html#id3
|
||||
# Opened this issue https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665
|
||||
marshmallow-sqlalchemy>=1.3.0,<1.4.1
|
||||
|
||||
# needed for python 3.12 support
|
||||
openapi-schema-validator>=0.6.3
|
||||
@@ -57,9 +58,3 @@ openapi-schema-validator>=0.6.3
|
||||
# Known affected packages: Preset's 'clients' package
|
||||
# See docs/docs/contributing/pkg-resources-migration.md for details
|
||||
setuptools<81
|
||||
|
||||
# google-auth 2.53+ dropped its transitive dependency on cachetools, which is
|
||||
# imported directly by superset.db_engine_specs.aws_iam. We declare cachetools
|
||||
# explicitly in pyproject.toml and pin google-auth to the post-drop range so
|
||||
# the install path is internally consistent (#40962).
|
||||
google-auth>=2.53.0,<3.0.0
|
||||
|
||||
@@ -45,7 +45,7 @@ cachelib==0.13.0
|
||||
# flask-caching
|
||||
# flask-session
|
||||
cachetools==6.2.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
# via google-auth
|
||||
cattrs==25.1.1
|
||||
# via requests-cache
|
||||
celery==5.5.2
|
||||
@@ -86,11 +86,10 @@ cron-descriptor==1.4.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
croniter==6.2.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
cryptography==48.0.1
|
||||
cryptography==46.0.7
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# apache-superset (pyproject.toml)
|
||||
# google-auth
|
||||
# paramiko
|
||||
# pyopenssl
|
||||
defusedxml==0.7.1
|
||||
@@ -132,7 +131,7 @@ flask-caching==2.3.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-compress==1.17
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-cors==6.0.5
|
||||
flask-cors==6.0.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-jwt-extended==4.7.1
|
||||
# via flask-appbuilder
|
||||
@@ -160,11 +159,9 @@ geographiclib==2.0
|
||||
# via geopy
|
||||
geopy==2.4.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
google-auth==2.53.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# shillelagh
|
||||
greenlet==3.5.1
|
||||
google-auth==2.43.0
|
||||
# via shillelagh
|
||||
greenlet==3.5.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# shillelagh
|
||||
@@ -226,13 +223,13 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==4.3.0
|
||||
marshmallow==3.26.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
# marshmallow-union
|
||||
marshmallow-sqlalchemy==1.5.0
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask-appbuilder
|
||||
@@ -240,7 +237,7 @@ marshmallow-union==0.1.15
|
||||
# via apache-superset (pyproject.toml)
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
msgpack==1.2.1
|
||||
msgpack==1.0.8
|
||||
# via apache-superset (pyproject.toml)
|
||||
msgspec==0.19.0
|
||||
# via flask-session
|
||||
@@ -273,6 +270,7 @@ packaging==25.0
|
||||
# deprecation
|
||||
# gunicorn
|
||||
# limits
|
||||
# marshmallow
|
||||
# shillelagh
|
||||
pandas==2.1.4
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -300,7 +298,9 @@ pyarrow==24.0.0
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
pyasn1==0.6.3
|
||||
# via pyasn1-modules
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# rsa
|
||||
pyasn1-modules==0.4.2
|
||||
# via google-auth
|
||||
pycparser==2.22
|
||||
@@ -323,7 +323,7 @@ pyjwt==2.12.0
|
||||
# redis
|
||||
pynacl==1.6.2
|
||||
# via paramiko
|
||||
pyopenssl==26.2.0
|
||||
pyopenssl==26.0.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# shillelagh
|
||||
@@ -344,11 +344,12 @@ python-dotenv==1.2.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
pytz==2025.2
|
||||
# via
|
||||
# croniter
|
||||
# flask-babel
|
||||
# pandas
|
||||
pyxlsb==1.0.10
|
||||
# via pandas
|
||||
pyyaml==6.0.3
|
||||
pyyaml==6.0.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apispec
|
||||
@@ -375,6 +376,8 @@ rpds-py==0.25.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9.1
|
||||
# via google-auth
|
||||
selenium==4.44.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
setuptools==80.9.0
|
||||
|
||||
@@ -100,7 +100,7 @@ cachelib==0.13.0
|
||||
cachetools==6.2.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# google-auth
|
||||
# py-key-value-aio
|
||||
caio==0.9.25
|
||||
# via aiofile
|
||||
@@ -178,12 +178,11 @@ croniter==6.2.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
cryptography==48.0.1
|
||||
cryptography==46.0.7
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# authlib
|
||||
# google-auth
|
||||
# paramiko
|
||||
# pyjwt
|
||||
# pyopenssl
|
||||
@@ -277,7 +276,7 @@ flask-compress==1.17
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
flask-cors==6.0.5
|
||||
flask-cors==6.0.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -341,7 +340,7 @@ google-api-core==2.23.0
|
||||
# google-cloud-core
|
||||
# pandas-gbq
|
||||
# sqlalchemy-bigquery
|
||||
google-auth==2.53.0
|
||||
google-auth==2.43.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# google-api-core
|
||||
@@ -374,7 +373,7 @@ googleapis-common-protos==1.66.0
|
||||
# via
|
||||
# google-api-core
|
||||
# grpcio-status
|
||||
greenlet==3.5.1
|
||||
greenlet==3.5.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -383,7 +382,7 @@ greenlet==3.5.1
|
||||
# sqlalchemy
|
||||
griffelib==2.0.2
|
||||
# via fastmcp
|
||||
grpcio==1.81.1
|
||||
grpcio==1.71.0
|
||||
# via
|
||||
# apache-superset
|
||||
# google-api-core
|
||||
@@ -508,8 +507,6 @@ limits==5.1.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-limiter
|
||||
lz4==4.4.5
|
||||
# via trino
|
||||
mako==1.3.12
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -530,14 +527,14 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==4.3.0
|
||||
marshmallow==3.26.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
# marshmallow-union
|
||||
marshmallow-sqlalchemy==1.5.0
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-appbuilder
|
||||
@@ -559,7 +556,7 @@ more-itertools==10.8.0
|
||||
# via
|
||||
# jaraco-classes
|
||||
# jaraco-functools
|
||||
msgpack==1.2.1
|
||||
msgpack==1.0.8
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -611,8 +608,6 @@ ordered-set==4.1.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-limiter
|
||||
orjson==3.11.9
|
||||
# via trino
|
||||
outcome==1.3.0.post0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -631,6 +626,7 @@ packaging==25.0
|
||||
# google-cloud-bigquery
|
||||
# gunicorn
|
||||
# limits
|
||||
# marshmallow
|
||||
# matplotlib
|
||||
# pytest
|
||||
# shillelagh
|
||||
@@ -727,6 +723,7 @@ pyasn1==0.6.3
|
||||
# -c requirements/base-constraint.txt
|
||||
# pyasn1-modules
|
||||
# python-ldap
|
||||
# rsa
|
||||
pyasn1-modules==0.4.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -783,7 +780,7 @@ pynacl==1.6.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# paramiko
|
||||
pyopenssl==26.2.0
|
||||
pyopenssl==26.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# shillelagh
|
||||
@@ -844,6 +841,7 @@ python-multipart==0.0.29
|
||||
pytz==2025.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# croniter
|
||||
# flask-babel
|
||||
# pandas
|
||||
# trino
|
||||
@@ -851,7 +849,7 @@ pyxlsb==1.0.10
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# pandas
|
||||
pyyaml==6.0.3
|
||||
pyyaml==6.0.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -913,6 +911,10 @@ rpds-py==0.25.0
|
||||
# -c requirements/base-constraint.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# google-auth
|
||||
ruff==0.9.7
|
||||
# via apache-superset
|
||||
s3transfer==0.16.0
|
||||
@@ -1015,7 +1017,7 @@ tqdm==4.67.1
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.337.0
|
||||
trino==0.330.0
|
||||
# via apache-superset
|
||||
trio==0.33.0
|
||||
# via
|
||||
@@ -1035,7 +1037,6 @@ typing-extensions==4.15.0
|
||||
# apache-superset-core
|
||||
# cattrs
|
||||
# exceptiongroup
|
||||
# grpcio
|
||||
# limits
|
||||
# mcp
|
||||
# opentelemetry-api
|
||||
@@ -1150,4 +1151,3 @@ zstandard==0.23.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-compress
|
||||
# trino
|
||||
|
||||
@@ -30,7 +30,7 @@ from flask import current_app
|
||||
from flask_appbuilder import Model
|
||||
from flask_migrate import downgrade, upgrade
|
||||
from progress.bar import ChargingBar
|
||||
from sqlalchemy import create_engine, inspect, text
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
from superset import db
|
||||
@@ -154,7 +154,7 @@ def main( # noqa: C901
|
||||
|
||||
print(f"Migration goes from {down_revision} to {revision}")
|
||||
current_revision = db.engine.execute(
|
||||
text("SELECT version_num FROM alembic_version")
|
||||
"SELECT version_num FROM alembic_version"
|
||||
).scalar()
|
||||
print(f"Current version of the DB is {current_revision}")
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ LANGUAGE_NAMES: dict[str, str] = {
|
||||
"ru": "Russian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sr": "Serbian",
|
||||
"tr": "Turkish",
|
||||
"uk": "Ukrainian",
|
||||
"zh": "Chinese (Simplified)",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@superset-ui/embedded-sdk",
|
||||
"version": "0.4.0",
|
||||
"version": "0.3.0",
|
||||
"description": "SDK for embedding resources from Superset into your own application",
|
||||
"access": "public",
|
||||
"keywords": [
|
||||
|
||||
@@ -47,11 +47,7 @@ function logError(...args) {
|
||||
execSync('npm publish --access public', { stdio: 'pipe' });
|
||||
log(`published ${version} to npm`);
|
||||
} catch (err) {
|
||||
// npm writes failure details to stderr (auth/permission/registry
|
||||
// errors in particular), so surface both streams to avoid masking
|
||||
// the real cause in CI logs.
|
||||
if (err.stdout) console.error(String(err.stdout));
|
||||
if (err.stderr) console.error(String(err.stderr));
|
||||
console.error(String(err.stdout));
|
||||
logError('Encountered an error, details should be above');
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ module.exports = {
|
||||
'\\.svg$': '<rootDir>/spec/__mocks__/svgrMock.tsx',
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
'^spec/(.*)$': '<rootDir>/spec/$1',
|
||||
// mapping glyph-core to local package source
|
||||
'^@superset-ui/glyph-core$':
|
||||
'<rootDir>/packages/superset-ui-glyph-core/src',
|
||||
'^@superset-ui/glyph-core/(.*)$':
|
||||
'<rootDir>/packages/superset-ui-glyph-core/src/$1',
|
||||
// mapping plugins of superset-ui to source code
|
||||
'^@superset-ui/([^/]+)/(.*)$':
|
||||
'<rootDir>/node_modules/@superset-ui/$1/src/$2',
|
||||
@@ -69,7 +74,7 @@ module.exports = {
|
||||
],
|
||||
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format)|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/*.|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-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|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)',
|
||||
'node_modules/(?!@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format)|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/*.|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-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|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/*.)',
|
||||
],
|
||||
preset: 'ts-jest',
|
||||
transform: {
|
||||
|
||||
2122
superset-frontend/package-lock.json
generated
2122
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -119,7 +119,7 @@
|
||||
"@great-expectations/jsonforms-antd-renderers": "^2.2.10",
|
||||
"@jsonforms/core": "^3.7.0",
|
||||
"@jsonforms/react": "^3.7.0",
|
||||
"@jsonforms/vanilla-renderers": "^3.8.0",
|
||||
"@jsonforms/vanilla-renderers": "^3.7.0",
|
||||
"@luma.gl/constants": "~9.2.5",
|
||||
"@luma.gl/core": "~9.2.5",
|
||||
"@luma.gl/engine": "~9.2.5",
|
||||
@@ -134,6 +134,7 @@
|
||||
"@scarf/scarf": "^1.4.0",
|
||||
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
|
||||
"@superset-ui/core": "file:./packages/superset-ui-core",
|
||||
"@superset-ui/glyph-core": "file:./packages/superset-ui-glyph-core",
|
||||
"@superset-ui/legacy-plugin-chart-calendar": "file:./plugins/legacy-plugin-chart-calendar",
|
||||
"@superset-ui/legacy-plugin-chart-chord": "file:./plugins/legacy-plugin-chart-chord",
|
||||
"@superset-ui/legacy-plugin-chart-country-map": "file:./plugins/legacy-plugin-chart-country-map",
|
||||
@@ -158,21 +159,22 @@
|
||||
"@types/d3-selection": "^3.0.11",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/react-google-recaptcha": "^2.1.9",
|
||||
"@visx/axis": "^4.0.0",
|
||||
"@visx/grid": "^4.0.0",
|
||||
"@visx/responsive": "^4.0.0",
|
||||
"@visx/scale": "^4.0.0",
|
||||
"@visx/tooltip": "^4.0.0",
|
||||
"@visx/xychart": "^4.0.0",
|
||||
"@visx/axis": "^3.8.0",
|
||||
"@visx/grid": "^3.5.0",
|
||||
"@visx/responsive": "^3.0.0",
|
||||
"@visx/scale": "^3.5.0",
|
||||
"@visx/tooltip": "^3.0.0",
|
||||
"@visx/xychart": "^3.5.1",
|
||||
"ag-grid-community": "35.3.1",
|
||||
"ag-grid-react": "35.3.1",
|
||||
"antd": "^5.26.0",
|
||||
"chrono-node": "^2.9.1",
|
||||
"classnames": "^2.2.5",
|
||||
"content-disposition": "^2.0.1",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-scale": "^4.0.2",
|
||||
"dayjs": "^1.11.21",
|
||||
"dom-to-image-more": "^3.10.0",
|
||||
"dom-to-image-more": "^3.7.2",
|
||||
"dom-to-pdf": "^0.3.2",
|
||||
"echarts": "^5.6.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
@@ -190,12 +192,12 @@
|
||||
"jquery": "^4.0.0",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
"json-bigint": "^1.0.0",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"lodash": "^4.18.1",
|
||||
"mapbox-gl": "^3.24.1",
|
||||
"markdown-to-jsx": "^9.8.2",
|
||||
"mapbox-gl": "^3.24.0",
|
||||
"markdown-to-jsx": "^9.8.1",
|
||||
"match-sorter": "^8.3.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"memoize-one": "^5.2.1",
|
||||
"mousetrap": "^1.6.5",
|
||||
"mustache": "^4.2.0",
|
||||
"nanoid": "^5.1.11",
|
||||
@@ -203,7 +205,7 @@
|
||||
"query-string": "9.4.0",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^18.3.0",
|
||||
"react-arborist": "^3.10.5",
|
||||
"react-arborist": "^3.10.1",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
@@ -230,9 +232,9 @@
|
||||
"redux-undo": "^1.0.0-beta9-9-7",
|
||||
"rison": "^0.1.1",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"simple-zstd": "^2.1.0",
|
||||
"simple-zstd": "^1.4.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"urijs": "^1.19.8",
|
||||
"use-event-callback": "^0.1.0",
|
||||
"use-immer": "^0.11.0",
|
||||
@@ -260,17 +262,17 @@
|
||||
"@babel/types": "^7.29.7",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/jest": "^11.14.2",
|
||||
"@formatjs/intl-durationformat": "^0.10.15",
|
||||
"@formatjs/intl-durationformat": "^0.10.14",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@playwright/test": "^1.61.0",
|
||||
"@playwright/test": "^1.60.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||
"@storybook/addon-docs": "10.4.5",
|
||||
"@storybook/addon-links": "10.4.4",
|
||||
"@storybook/react-webpack5": "10.4.4",
|
||||
"@storybook/addon-docs": "10.4.2",
|
||||
"@storybook/addon-links": "10.4.2",
|
||||
"@storybook/react-webpack5": "10.4.2",
|
||||
"@storybook/test-runner": "0.24.4",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.15.41",
|
||||
"@swc/plugin-emotion": "^14.13.0",
|
||||
"@swc/plugin-emotion": "^14.12.0",
|
||||
"@swc/plugin-transform-imports": "^12.5.0",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
@@ -283,7 +285,7 @@
|
||||
"@types/js-levenshtein": "^1.1.3",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^25.9.3",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/react": "^18.3.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
@@ -296,21 +298,21 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.61.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"babel-jest": "^30.4.1",
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"baseline-browser-mapping": "^2.10.37",
|
||||
"baseline-browser-mapping": "^2.10.34",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^10.0.3",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
||||
"eslint": "^10.5.0",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
@@ -323,8 +325,8 @@
|
||||
"eslint-plugin-no-only-tests": "^3.4.0",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.1",
|
||||
"eslint-plugin-storybook": "10.4.5",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.0",
|
||||
"eslint-plugin-storybook": "10.4.2",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
"fetch-mock": "^12.6.0",
|
||||
@@ -343,19 +345,19 @@
|
||||
"lightningcss": "^1.32.0",
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
"open-cli": "^9.0.0",
|
||||
"oxlint": "^1.70.0",
|
||||
"oxlint": "^1.69.0",
|
||||
"po2json": "^0.4.5",
|
||||
"prettier": "3.8.4",
|
||||
"prettier-plugin-packagejson": "^3.0.2",
|
||||
"process": "^0.11.10",
|
||||
"react-dnd-test-backend": "^16.0.1",
|
||||
"react-dnd-test-backend": "^11.1.3",
|
||||
"react-refresh": "^0.18.0",
|
||||
"react-resizable": "^4.0.1",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
"source-map": "^0.7.6",
|
||||
"source-map-support": "^0.5.21",
|
||||
"speed-measure-webpack-plugin": "^1.6.0",
|
||||
"storybook": "10.4.6",
|
||||
"storybook": "10.4.3",
|
||||
"style-loader": "^4.0.0",
|
||||
"swc-loader": "^0.2.7",
|
||||
"terser-webpack-plugin": "^5.6.1",
|
||||
@@ -369,7 +371,7 @@
|
||||
"webpack": "^5.107.2",
|
||||
"webpack-bundle-analyzer": "^5.3.0",
|
||||
"webpack-cli": "^7.0.3",
|
||||
"webpack-dev-server": "^5.2.5",
|
||||
"webpack-dev-server": "^5.2.4",
|
||||
"webpack-manifest-plugin": "^6.0.1",
|
||||
"webpack-sources": "^3.5.0",
|
||||
"webpack-visualizer-plugin2": "^2.0.0"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"cross-env": "^10.1.0",
|
||||
"fs-extra": "^11.3.5",
|
||||
"jest": "^30.4.2",
|
||||
"yeoman-test": "^11.6.0"
|
||||
"yeoman-test": "^11.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">= 4.0.0",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@testing-library/user-event": "*",
|
||||
"ace-builds": "^1.4.14",
|
||||
"brace": "^0.11.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"memoize-one": "^5.1.1",
|
||||
"react": "^18.3.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^18.3.0"
|
||||
|
||||
@@ -415,6 +415,8 @@ export interface ControlPanelSectionConfig {
|
||||
props: ControlPanelsContainerProps,
|
||||
controlData: AnyDict,
|
||||
) => boolean;
|
||||
/** @internal Marks the auto-generated glyph "Chart Options" section */
|
||||
_glyphChartOptions?: boolean;
|
||||
}
|
||||
|
||||
export interface StandardizedControls {
|
||||
@@ -447,6 +449,8 @@ export interface ControlPanelConfig {
|
||||
sectionOverrides?: SectionOverrides;
|
||||
onInit?: (state: ControlStateMapping) => void;
|
||||
formDataOverrides?: (formData: QueryFormData) => QueryFormData;
|
||||
/** @internal Raw glyph argument definitions from defineChart() – used for native control panel rendering */
|
||||
_glyphArgs?: unknown;
|
||||
}
|
||||
|
||||
export type ControlOverrides = {
|
||||
@@ -677,9 +681,7 @@ export interface ServerPaginationData {
|
||||
|
||||
export type TableColumnConfig = {
|
||||
d3NumberFormat?: string;
|
||||
// Allow null to match JSON round-trips, where an unset value deserializes
|
||||
// from the metadata DB as `null` rather than `undefined`.
|
||||
d3SmallNumberFormat?: string | null;
|
||||
d3SmallNumberFormat?: string;
|
||||
d3TimeFormat?: string;
|
||||
columnWidth?: number;
|
||||
horizontalAlign?: 'left' | 'right' | 'center';
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@babel/runtime": "^7.29.7",
|
||||
"@braintree/sanitize-url": "^7.1.2",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@visx/responsive": "^4.0.0",
|
||||
"@visx/responsive": "^3.12.0",
|
||||
"ace-builds": "^1.44.0",
|
||||
"ag-grid-community": "35.3.1",
|
||||
"ag-grid-react": "35.3.1",
|
||||
@@ -43,7 +43,7 @@
|
||||
"d3-time": "^3.1.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"dayjs": "^1.11.21",
|
||||
"dompurify": "^3.4.11",
|
||||
"dompurify": "^3.4.9",
|
||||
"fetch-retry": "^6.0.0",
|
||||
"handlebars": "^4.7.9",
|
||||
"jed": "^1.1.1",
|
||||
@@ -77,7 +77,7 @@
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.3",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/react-table": "^7.7.20",
|
||||
|
||||
@@ -33,6 +33,14 @@ export enum Behavior {
|
||||
*/
|
||||
DrillToDetail = 'DRILL_TO_DETAIL',
|
||||
DrillBy = 'DRILL_BY',
|
||||
|
||||
/**
|
||||
* Include `ALLOWS_EMPTY_RESULTS` behavior if the chart handles empty/no data
|
||||
* gracefully (e.g., showing a drop zone for drag-and-drop configuration).
|
||||
* Charts with this behavior will receive empty data instead of seeing
|
||||
* the "No results" message.
|
||||
*/
|
||||
AllowsEmptyResults = 'ALLOWS_EMPTY_RESULTS',
|
||||
}
|
||||
|
||||
export interface ContextMenuFilters {
|
||||
|
||||
@@ -86,7 +86,6 @@ import {
|
||||
FundProjectionScreenOutlined,
|
||||
FunctionOutlined,
|
||||
HighlightOutlined,
|
||||
HomeOutlined,
|
||||
InfoCircleOutlined,
|
||||
InfoCircleFilled,
|
||||
InsertRowAboveOutlined,
|
||||
@@ -244,7 +243,6 @@ const AntdIcons = {
|
||||
GoogleOutlined,
|
||||
GroupOutlined,
|
||||
HighlightOutlined,
|
||||
HomeOutlined,
|
||||
InfoCircleOutlined,
|
||||
InfoCircleFilled,
|
||||
InsertRowAboveOutlined,
|
||||
|
||||
@@ -74,10 +74,7 @@ export function transformLinkUri(uri: string): string {
|
||||
// "java\tscript:" or "java\x01script:") are ignored by browsers, so strip
|
||||
// them before comparing against the blocklist.
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const scheme = url
|
||||
.slice(0, colon)
|
||||
.replace(/[\u0000-\u0020]/g, '')
|
||||
.toLowerCase();
|
||||
const scheme = url.slice(0, colon).replace(/[\u0000-\u0020]/g, '').toLowerCase();
|
||||
return DANGEROUS_LINK_PROTOCOLS.includes(scheme) ? '' : url;
|
||||
}
|
||||
|
||||
|
||||
@@ -519,8 +519,7 @@ const Select = forwardRef(
|
||||
handleSelectAll();
|
||||
}}
|
||||
>
|
||||
{t('Select all')}{' '}
|
||||
{`(${formatNumber('SMART_NUMBER', bulkSelectCounts.selectable)})`}
|
||||
{t('Select all')} {`(${formatNumber('SMART_NUMBER', bulkSelectCounts.selectable)})`}
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
@@ -537,8 +536,7 @@ const Select = forwardRef(
|
||||
handleDeselectAll();
|
||||
}}
|
||||
>
|
||||
{t('Clear')}{' '}
|
||||
{`(${formatNumber('SMART_NUMBER', bulkSelectCounts.deselectable)})`}
|
||||
{t('Clear')} {`(${formatNumber('SMART_NUMBER', bulkSelectCounts.deselectable)})`}
|
||||
</Button>
|
||||
</StyledBulkActionsContainer>
|
||||
),
|
||||
|
||||
@@ -52,13 +52,6 @@ const SupersetClient: SupersetClientInterface = {
|
||||
request: request => getInstance().request(request),
|
||||
getCSRFToken: () => getInstance().getCSRFToken(),
|
||||
getUrl: (...args) => getInstance().getUrl(...args),
|
||||
get guestTokenHeaderName() {
|
||||
try {
|
||||
return getInstance().guestTokenHeaderName;
|
||||
} catch {
|
||||
return 'X-GuestToken';
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default SupersetClient;
|
||||
|
||||
@@ -163,7 +163,6 @@ export interface SupersetClientInterface extends Pick<
|
||||
configure: (config?: ClientConfig) => SupersetClientInterface;
|
||||
reset: () => void;
|
||||
getCSRFToken: () => CsrfPromise;
|
||||
guestTokenHeaderName?: string;
|
||||
}
|
||||
|
||||
export type SupersetClientResponse = Response | JsonResponse | TextResponse;
|
||||
|
||||
@@ -18,11 +18,7 @@
|
||||
*/
|
||||
|
||||
import { ExtensibleFunction } from '../models';
|
||||
// Import from the concrete modules rather than the `number-format` barrel to
|
||||
// avoid a circular dependency (the barrel pulls in getSmallNumberFormatter,
|
||||
// which imports CurrencyFormatter).
|
||||
import { getNumberFormatter } from '../number-format/NumberFormatterRegistrySingleton';
|
||||
import NumberFormats from '../number-format/NumberFormats';
|
||||
import { getNumberFormatter, NumberFormats } from '../number-format';
|
||||
import { Currency } from '../query';
|
||||
import { RowData, RowDataValue } from './types';
|
||||
import { AUTO_CURRENCY_SYMBOL, ISO_4217_REGEX } from './CurrencyFormats';
|
||||
|
||||
@@ -1,54 +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.
|
||||
*/
|
||||
|
||||
import { CurrencyFormatter } from '../currency-format';
|
||||
import { Currency } from '../query';
|
||||
import NumberFormatter from './NumberFormatter';
|
||||
import { getNumberFormatter } from './NumberFormatterRegistrySingleton';
|
||||
|
||||
/**
|
||||
* Returns the appropriate formatter for small numbers (|value| < 1).
|
||||
*
|
||||
* When `d3SmallNumberFormat` is nullish or blank the caller's default
|
||||
* formatter is returned unchanged, which preserves percentage formats
|
||||
* that would otherwise be lost.
|
||||
*
|
||||
* Handles the cases where `d3SmallNumberFormat` is `null` (from JSON
|
||||
* serialization of `undefined`) or `""` (from a cleared Select control).
|
||||
*
|
||||
* The generic parameter `F` allows callers to pass any formatter type
|
||||
* (e.g. TimeFormatter, CustomFormatter) without a circular dependency
|
||||
* on @superset-ui/chart-controls.
|
||||
*/
|
||||
export default function getSmallNumberFormatter<F>(
|
||||
defaultFormatter: F,
|
||||
d3SmallNumberFormat: string | null | undefined,
|
||||
currencyFormat?: Currency,
|
||||
): F | NumberFormatter | CurrencyFormatter {
|
||||
if (d3SmallNumberFormat == null || d3SmallNumberFormat.trim() === '') {
|
||||
return defaultFormatter;
|
||||
}
|
||||
if (currencyFormat) {
|
||||
return new CurrencyFormatter({
|
||||
d3Format: d3SmallNumberFormat,
|
||||
currency: currencyFormat,
|
||||
});
|
||||
}
|
||||
return getNumberFormatter(d3SmallNumberFormat);
|
||||
}
|
||||
@@ -34,4 +34,3 @@ export { default as createDurationFormatter } from './factories/createDurationFo
|
||||
export { default as createMemoryFormatter } from './factories/createMemoryFormatter';
|
||||
export { default as createSiAtMostNDigitFormatter } from './factories/createSiAtMostNDigitFormatter';
|
||||
export { default as createSmartNumberFormatter } from './factories/createSmartNumberFormatter';
|
||||
export { default as getSmallNumberFormatter } from './getSmallNumberFormatter';
|
||||
|
||||
@@ -172,15 +172,4 @@ describe('SupersetClient', () => {
|
||||
const token = await SupersetClient.getCSRFToken();
|
||||
expect(token).toBe('my_token');
|
||||
});
|
||||
|
||||
test('guestTokenHeaderName returns the configured header name when instance exists', () => {
|
||||
SupersetClient.configure({ guestTokenHeaderName: 'X-Custom-Guest' });
|
||||
expect(SupersetClient.guestTokenHeaderName).toBe('X-Custom-Guest');
|
||||
});
|
||||
|
||||
test('guestTokenHeaderName returns default X-GuestToken when instance is not configured', () => {
|
||||
// Ensure instance is reset (afterEach calls SupersetClient.reset())
|
||||
// Access the property without calling configure() first
|
||||
expect(SupersetClient.guestTokenHeaderName).toBe('X-GuestToken');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,82 +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.
|
||||
*/
|
||||
|
||||
import {
|
||||
CurrencyFormatter,
|
||||
getNumberFormatter,
|
||||
getSmallNumberFormatter,
|
||||
NumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
const defaultFormatter = getNumberFormatter('.8%');
|
||||
|
||||
describe('getSmallNumberFormatter', () => {
|
||||
test('returns defaultFormatter when d3SmallNumberFormat is undefined', () => {
|
||||
expect(getSmallNumberFormatter(defaultFormatter, undefined)).toBe(
|
||||
defaultFormatter,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns defaultFormatter when d3SmallNumberFormat is null (JSON round-trip)', () => {
|
||||
expect(getSmallNumberFormatter(defaultFormatter, null)).toBe(
|
||||
defaultFormatter,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns defaultFormatter when d3SmallNumberFormat is empty string (cleared Select)', () => {
|
||||
expect(getSmallNumberFormatter(defaultFormatter, '')).toBe(
|
||||
defaultFormatter,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns defaultFormatter when d3SmallNumberFormat is whitespace', () => {
|
||||
expect(getSmallNumberFormatter(defaultFormatter, ' ')).toBe(
|
||||
defaultFormatter,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns a NumberFormatter when d3SmallNumberFormat is a valid format', () => {
|
||||
const result = getSmallNumberFormatter(defaultFormatter, ',.4f');
|
||||
expect(result).toBeInstanceOf(NumberFormatter);
|
||||
expect(result).not.toBe(defaultFormatter);
|
||||
expect(result!(0.12345)).toBe('0.1235');
|
||||
});
|
||||
|
||||
test('returns a CurrencyFormatter when currencyFormat is provided', () => {
|
||||
const result = getSmallNumberFormatter(defaultFormatter, ',.4f', {
|
||||
symbol: 'USD',
|
||||
symbolPosition: 'prefix',
|
||||
});
|
||||
expect(result).toBeInstanceOf(CurrencyFormatter);
|
||||
expect(result!(0.12345)).toContain('0.1235');
|
||||
expect(result!(0.12345)).toContain('$');
|
||||
});
|
||||
|
||||
test('preserves percentage formatter output for small numbers when d3SmallNumberFormat is null', () => {
|
||||
const pctFormatter = getNumberFormatter('.8%');
|
||||
const result = getSmallNumberFormatter(pctFormatter, null);
|
||||
expect(result!(-0.00001229)).toBe('-0.00122900%');
|
||||
});
|
||||
|
||||
test('returns undefined when defaultFormatter is undefined and d3SmallNumberFormat is nullish', () => {
|
||||
expect(getSmallNumberFormatter(undefined, null)).toBeUndefined();
|
||||
expect(getSmallNumberFormatter(undefined, undefined)).toBeUndefined();
|
||||
expect(getSmallNumberFormatter(undefined, '')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,335 @@
|
||||
# Glyph Pattern Migration Guide
|
||||
|
||||
This guide documents how to migrate traditional Superset chart plugins to the single-file Glyph pattern.
|
||||
|
||||
## Overview
|
||||
|
||||
The Glyph pattern simplifies chart plugin development by:
|
||||
- **Arguments define BOTH controls AND render props** - No separate files needed
|
||||
- **No `controlPanel.ts`** - Generated from argument definitions
|
||||
- **No `transformProps.ts`** - Arguments are passed directly to render
|
||||
- **No `buildQuery.ts`** - Inferred from Metric/Dimension/Temporal arguments
|
||||
- **Single file** - Everything in one place (~200 lines vs 500+ across multiple files)
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 1. Analyze the Existing Chart
|
||||
|
||||
Identify from the original chart:
|
||||
- **Metrics/Dimensions**: What data does it query?
|
||||
- **Controls**: What options does the user configure?
|
||||
- **Styling**: What visual customizations exist?
|
||||
- **Rendering**: How is the data displayed?
|
||||
|
||||
### 2. Create the Glyph Chart File
|
||||
|
||||
Create a new file: `src/BigNumber/BigNumberGlyph/index.tsx`
|
||||
|
||||
```typescript
|
||||
import { t } from '@apache-superset/core';
|
||||
import { styled } from '@apache-superset/core/ui';
|
||||
import { Behavior, getNumberFormatter, CurrencyFormatter } from '@superset-ui/core';
|
||||
|
||||
import {
|
||||
defineChart,
|
||||
Metric,
|
||||
Select,
|
||||
Text,
|
||||
Checkbox,
|
||||
NumberFormat,
|
||||
Currency,
|
||||
TimeFormat,
|
||||
ConditionalFormatting,
|
||||
} from '@superset-ui/glyph-core';
|
||||
```
|
||||
|
||||
### 3. Define Arguments (Controls + Props)
|
||||
|
||||
**CRITICAL: Use camelCase for argument names!**
|
||||
|
||||
Superset converts control names to camelCase in `formData`. If you use snake_case (`show_metric_name`), it won't match the camelCase key in formData (`showMetricName`).
|
||||
|
||||
```typescript
|
||||
arguments: {
|
||||
// Data arguments
|
||||
metric: Metric.with({ label: t('Metric') }),
|
||||
|
||||
// Visual arguments - USE CAMELCASE!
|
||||
headerFontSize: Select.with({
|
||||
label: t('Font Size'),
|
||||
options: [
|
||||
{ label: t('Small'), value: 0.2 },
|
||||
{ label: t('Large'), value: 0.4 },
|
||||
],
|
||||
default: 0.4,
|
||||
}),
|
||||
|
||||
showMetricName: Checkbox.with({
|
||||
label: t('Show Metric Name'),
|
||||
default: false,
|
||||
}),
|
||||
|
||||
// Declarative visibility (preferred)
|
||||
metricNameFontSize: {
|
||||
arg: Select.with({ ... }),
|
||||
visibleWhen: { showMetricName: true },
|
||||
},
|
||||
|
||||
// Declarative disabled state
|
||||
subtitleFontSize: {
|
||||
arg: Select.with({ ... }),
|
||||
disabledWhen: { subtitle: '' },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Available Argument Types
|
||||
|
||||
| Type | Control Generated | Value Type | Properties |
|
||||
|------|------------------|------------|------------|
|
||||
| `Metric` | MetricControl | `{ value, name, formattedValue }` | `label` |
|
||||
| `Dimension` | GroupByControl | `string[]` | `label` |
|
||||
| `Temporal` | TemporalControl | `string` | `label` |
|
||||
| `Select` | SelectControl | `string \| number` | `label`, `description`, `options`, `default` |
|
||||
| `Text` | TextControl | `string` | `label`, `description`, `default`, `placeholder` |
|
||||
| `Checkbox` | CheckboxControl | `boolean` | `label`, `description`, `default` |
|
||||
| `Int` | SliderControl | `number` | `label`, `description`, `default`, `min`, `max`, `step` |
|
||||
| `Color` | ColorPickerControl | `string` (hex) | `label`, `description`, `default` |
|
||||
| `NumberFormat` | SelectControl (freeform) | `string` | `label`, `description`, `default` |
|
||||
| `Currency` | CurrencyControl | `{ symbol?, symbolPosition? }` | `label`, `description`, `default` |
|
||||
| `TimeFormat` | SelectControl (freeform) | `string` | `label`, `description`, `default` |
|
||||
| `ConditionalFormatting` | ConditionalFormattingControl | `Rule[]` | `label`, `description` |
|
||||
|
||||
### 5. Declarative Visibility & Disabled States
|
||||
|
||||
Instead of Redux `mapStateToProps`, use declarative conditions:
|
||||
|
||||
```typescript
|
||||
// Simple equality check - visible when showMetricName is true
|
||||
metricNameFontSize: {
|
||||
arg: Select.with({ ... }),
|
||||
visibleWhen: { showMetricName: true },
|
||||
},
|
||||
|
||||
// Function check - visible when subtitle is not empty
|
||||
subtitleFontSize: {
|
||||
arg: Select.with({ ... }),
|
||||
visibleWhen: { subtitle: (val) => !!val },
|
||||
},
|
||||
|
||||
// Multiple conditions (AND) - visible when both conditions are met
|
||||
advancedOption: {
|
||||
arg: Checkbox.with({ ... }),
|
||||
visibleWhen: {
|
||||
showMetricName: true,
|
||||
subtitle: (val) => !!val,
|
||||
},
|
||||
},
|
||||
|
||||
// Disabled state (control visible but not editable)
|
||||
formatOption: {
|
||||
arg: Select.with({ ... }),
|
||||
disabledWhen: { forceTimestampFormatting: true },
|
||||
},
|
||||
```
|
||||
|
||||
### 6. Number, Currency, and Time Formatting
|
||||
|
||||
Use the built-in format argument types:
|
||||
|
||||
```typescript
|
||||
arguments: {
|
||||
numberFormat: NumberFormat.with({
|
||||
label: t('Number Format'),
|
||||
description: t('D3 format string'),
|
||||
default: 'SMART_NUMBER',
|
||||
}),
|
||||
|
||||
currencyFormat: Currency.with({
|
||||
label: t('Currency Format'),
|
||||
}),
|
||||
|
||||
timeFormat: TimeFormat.with({
|
||||
label: t('Date Format'),
|
||||
default: 'smart_date',
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
Then use them directly in the render function:
|
||||
|
||||
```typescript
|
||||
render: ({ numberFormat, currencyFormat, timeFormat, metric }) => {
|
||||
const formatter = currencyFormat?.symbol
|
||||
? new CurrencyFormatter({
|
||||
currency: { symbol: currencyFormat.symbol, symbolPosition: currencyFormat.symbolPosition ?? 'prefix' },
|
||||
d3Format: numberFormat,
|
||||
})
|
||||
: getNumberFormatter(numberFormat);
|
||||
|
||||
return <div>{formatter(metric.value)}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Conditional Formatting (Colors)
|
||||
|
||||
Use `ConditionalFormatting` for color-based rules:
|
||||
|
||||
```typescript
|
||||
import { getColorFormatters } from '@superset-ui/chart-controls';
|
||||
|
||||
arguments: {
|
||||
conditionalFormatting: ConditionalFormatting.with({
|
||||
label: t('Conditional Formatting'),
|
||||
description: t('Apply conditional color formatting to metric'),
|
||||
}),
|
||||
},
|
||||
|
||||
render: ({ conditionalFormatting, metric, data, theme }) => {
|
||||
let numberColor: string | undefined;
|
||||
|
||||
if (conditionalFormatting?.length > 0 && metric.value != null) {
|
||||
const colorFormatters = getColorFormatters(conditionalFormatting, data, theme, false);
|
||||
if (colorFormatters) {
|
||||
for (const formatter of colorFormatters) {
|
||||
const color = formatter.getColorFromValue(metric.value as number);
|
||||
if (color) {
|
||||
numberColor = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <BigNumberText color={numberColor}>{metric.formattedValue}</BigNumberText>;
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Styled Components
|
||||
|
||||
Use Superset's theme properties with template literal syntax:
|
||||
|
||||
```typescript
|
||||
const Container = styled.div<{ height: number }>`
|
||||
${({ theme, height }) => `
|
||||
height: ${height}px;
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
font-family: ${theme.fontFamily};
|
||||
color: ${theme.colorText};
|
||||
`}
|
||||
`;
|
||||
```
|
||||
|
||||
**Common theme properties:**
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `theme.sizeUnit` | Base spacing unit (typically 4px) |
|
||||
| `theme.fontFamily` | Default font family |
|
||||
| `theme.fontWeightNormal` | Normal font weight |
|
||||
| `theme.fontWeightLight` | Light font weight |
|
||||
| `theme.fontSizeSM` | Small font size |
|
||||
| `theme.colorText` | Primary text color |
|
||||
| `theme.colorTextTertiary` | Muted/secondary text color |
|
||||
| `theme.borderRadius` | Standard border radius |
|
||||
|
||||
### 9. Render Function
|
||||
|
||||
The render function receives all arguments directly - no formData lookup needed:
|
||||
|
||||
```typescript
|
||||
render: ({
|
||||
metric,
|
||||
headerFontSize,
|
||||
showMetricName,
|
||||
numberFormat,
|
||||
currencyFormat,
|
||||
conditionalFormatting,
|
||||
height,
|
||||
data,
|
||||
theme,
|
||||
}) => {
|
||||
// All arguments are directly available!
|
||||
const formatter = currencyFormat?.symbol
|
||||
? new CurrencyFormatter({ currency: currencyFormat, d3Format: numberFormat })
|
||||
: getNumberFormatter(numberFormat);
|
||||
|
||||
const formattedValue = metric.value != null
|
||||
? formatter(metric.value as number)
|
||||
: t('No data');
|
||||
|
||||
return (
|
||||
<Container height={height}>
|
||||
{showMetricName && <MetricName>{metric.name}</MetricName>}
|
||||
<BigNumberText>{formattedValue}</BigNumberText>
|
||||
</Container>
|
||||
);
|
||||
},
|
||||
```
|
||||
|
||||
### 10. Register the Plugin
|
||||
|
||||
In `BigNumber/index.ts`:
|
||||
```typescript
|
||||
export { default as BigNumberGlyphChartPlugin } from './BigNumberGlyph';
|
||||
```
|
||||
|
||||
In `plugin-chart-echarts/src/index.ts`:
|
||||
```typescript
|
||||
export { BigNumberGlyphChartPlugin } from './BigNumber';
|
||||
```
|
||||
|
||||
In `MainPreset.js`:
|
||||
```typescript
|
||||
import { BigNumberGlyphChartPlugin } from '@superset-ui/plugin-chart-echarts';
|
||||
|
||||
new BigNumberGlyphChartPlugin().configure({ key: 'big_number_glyph' }),
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### 1. Snake Case vs Camel Case
|
||||
- **WRONG**: `show_metric_name` - won't match formData
|
||||
- **RIGHT**: `showMetricName` - matches Superset's camelCase conversion
|
||||
|
||||
### 2. Theme Undefined
|
||||
- **WRONG**: `theme.gridUnit` - crashes if theme is undefined
|
||||
- **RIGHT**: `theme?.gridUnit ?? 4` - safe with fallback
|
||||
|
||||
### 3. Metric Value Extraction
|
||||
The Glyph core automatically extracts metric values from query results. The `metric` argument provides:
|
||||
- `metric.value` - The raw numeric value
|
||||
- `metric.name` - The metric label/name
|
||||
- `metric.formattedValue` - Basic string representation
|
||||
|
||||
### 4. Visibility vs Legacy Functions
|
||||
- **Prefer**: `visibleWhen: { showMetricName: true }` - declarative, clean
|
||||
- **Legacy**: `visibility: ({ controls }) => controls?.showMetricName?.value === true` - still works
|
||||
|
||||
## File Structure Comparison
|
||||
|
||||
### Traditional (5+ files, ~500 lines)
|
||||
```
|
||||
BigNumberTotal/
|
||||
├── index.ts # Plugin registration
|
||||
├── controlPanel.ts # Control definitions (~100 lines)
|
||||
├── transformProps.ts # Data transformation (~150 lines)
|
||||
├── buildQuery.ts # Query building (~50 lines)
|
||||
├── BigNumberViz.tsx # React component (~150 lines)
|
||||
└── types.ts # TypeScript types (~50 lines)
|
||||
```
|
||||
|
||||
### Glyph Pattern (1 file, ~250 lines)
|
||||
```
|
||||
BigNumberGlyph/
|
||||
└── index.tsx # Everything in one file!
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
See `BigNumber/BigNumberGlyph/index.tsx` for a complete working example with:
|
||||
- Metric display
|
||||
- Number/currency/time formatting
|
||||
- Conditional color formatting
|
||||
- Declarative visibility
|
||||
- Subtitle support
|
||||
- Font size controls
|
||||
@@ -0,0 +1,267 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
# [SIP] Proposal for single-file chart plugins via `defineChart()` (the "Glyph" pattern)
|
||||
|
||||
> Draft — to be filed per the [SIP process](https://github.com/apache/superset/issues/5602).
|
||||
> The SIP will be numbered by a committer upon acceptance.
|
||||
|
||||
### Motivation
|
||||
|
||||
Building a Superset visualization plugin today requires authoring and keeping in
|
||||
sync five or more files per chart — `index.ts`, `controlPanel.ts`,
|
||||
`transformProps.ts`, `buildQuery.ts`, `types.ts`, plus the component — wired
|
||||
together through several layers of incidental complexity:
|
||||
|
||||
- **String-based control references.** Controls are referenced by name strings
|
||||
(`'metric'`, `'groupby'`, `'y_axis_format'`) that are resolved at runtime
|
||||
through registries and `expandControlConfig`. Typos fail silently; renames
|
||||
require grep-driven archaeology.
|
||||
- **Array-of-arrays layouts.** Control panel layout is expressed as
|
||||
`controlSetRows: [[control], [control, control], [<JSX/>]]` — positional
|
||||
nesting that conflates layout with configuration and is hostile to both
|
||||
reading and tooling.
|
||||
- **Triplicated knowledge.** The same fact (e.g. "this chart has a `showLegend`
|
||||
boolean defaulting to true") is restated in the control panel (control
|
||||
config), in transformProps (formData extraction + defaulting), and in the
|
||||
component's prop types. The three routinely drift.
|
||||
- **No type safety across the seam.** `transformProps` output and component
|
||||
props are connected only by convention; mismatches surface as silently
|
||||
`undefined` props at runtime, not compile errors.
|
||||
|
||||
The result is a high floor for contributing a chart, and a maintenance burden
|
||||
proportional to (charts × files × duplicated facts).
|
||||
|
||||
### Proposed Change
|
||||
|
||||
A new workspace package, **`@superset-ui/glyph-core`**, provides a
|
||||
**`defineChart()`** function that defines a complete chart plugin in a single
|
||||
file. The core idea: **argument definitions are the single source of truth**
|
||||
for the control panel, the query, the transform, and the render props.
|
||||
|
||||
```tsx
|
||||
export default defineChart({
|
||||
metadata: { name: t('My Chart'), thumbnail, behaviors: [Behavior.InteractiveChart] },
|
||||
arguments: {
|
||||
metric: Metric.with({ label: t('Metric') }),
|
||||
groupby: Dimension.with({ label: t('Dimension') }),
|
||||
showLegend: ShowLegend, // reusable preset
|
||||
legendType: { arg: LegendType, visibleWhen: { showLegend: true } },
|
||||
},
|
||||
transform: (chartProps, argValues) => ({ echartOptions: build(chartProps) }),
|
||||
render: props => <Echart {...props} />,
|
||||
});
|
||||
```
|
||||
|
||||
Key elements:
|
||||
|
||||
- **Argument classes** (`Metric`, `Dimension`, `Temporal`, `Select`, `Checkbox`,
|
||||
`Int`, `Slider`, `Color`, `ColorPicker`, `RadioButton`, `Bounds`, `Text`,
|
||||
`NumberFormat`, `Currency`, `TimeFormat`, `ConditionalFormatting`) declare
|
||||
what a chart needs; glyph-core generates the matching control configs,
|
||||
formData extraction with defaults, and typed render props.
|
||||
- **Declarative conditions** — `visibleWhen` / `disabledWhen` replace
|
||||
imperative `visibility:` functions reaching into Redux controls state, with a
|
||||
single shared evaluator (`evaluateGlyphCondition`) usable from both the
|
||||
legacy pipeline and native rendering.
|
||||
- **Generated `buildQuery`** for the common case (built on the core
|
||||
`getXAxisColumn`/`normalizeTimeColumn` helpers so time grain, adhoc x-axis
|
||||
columns, and the `DTTM_ALIAS` fallback behave identically to hand-written
|
||||
queries); charts with post-processing pass their own `buildQuery`.
|
||||
- **Generated `transformProps`** maps formData → typed render props (metric
|
||||
label resolution via core `getMetricLabel`); charts with heavy transforms
|
||||
supply a `transform` whose return value is merged into render props.
|
||||
- **Escape hatches for incremental migration** — `additionalControls`,
|
||||
`prependSections` / `middleSections` / `additionalSections`,
|
||||
`controlOverrides`, `formDataOverrides`, `onInit`, and
|
||||
`chartOptionsTabOverride` let complex charts keep hand-crafted sections while
|
||||
adopting the pattern for everything else.
|
||||
- **Reusable presets** (`presets.ts`) — `ShowLegend`, `LegendType`,
|
||||
`LegendOrientation`, `HeaderFontSize`, `Subtitle`, etc. — shared,
|
||||
pre-translated argument configurations that keep label strings and defaults
|
||||
consistent across chart families.
|
||||
- **Native Customize-tab rendering** — `GlyphOptionsPanel` renders glyph
|
||||
arguments directly from formData (value + visibility), bypassing the string
|
||||
expansion pipeline. The generated "Chart Options" section is identified by a
|
||||
structural `_glyphChartOptions` marker (never by label matching). Controls
|
||||
whose config requires `mapStateToProps` (e.g. ConditionalFormatting) and JSX
|
||||
rows (sub-section headers) route through the existing legacy renderer for
|
||||
full compatibility.
|
||||
|
||||
### Current Status (what has been done)
|
||||
|
||||
All work lives on `feat/glyph-single-file`. **Every chart family in the main
|
||||
preset has been consolidated** to `defineChart()`:
|
||||
|
||||
- **plugin-chart-echarts**: Pie, Funnel, Gauge, Sankey, Waterfall, Histogram,
|
||||
Tree, Bubble, BoxPlot, Sunburst, Radar, Treemap, Graph, Heatmap, Gantt,
|
||||
BigNumber, BigNumberWithTrendline, BigNumberTotal, BigNumberPeriodOverPeriod,
|
||||
Timeseries (Generic, Area, Bar, Line, Scatter, SmoothLine, Step),
|
||||
MixedTimeseries
|
||||
- **preset-chart-deckgl**: all 11 layers (Arc, Contour, Geojson, Grid, Heatmap,
|
||||
Hex, Path, Polygon, Scatter, Screengrid, Multi)
|
||||
- **legacy plugins**: calendar, horizon, chord, country-map, world-map,
|
||||
paired-t-test, parallel-coordinates, partition, rose
|
||||
- **legacy-preset-chart-nvd3**: Bubble, Bullet, Compare, TimePivot
|
||||
- **others**: table, pivot-table, ag-grid-table (light-touch), word-cloud,
|
||||
point-cluster-map, handlebars, cartodiagram
|
||||
|
||||
Net effect: **−13,000 lines** (+60k/−73k across 482 files).
|
||||
|
||||
**Test coverage added**: a 168-test suite spanning glyph-core unit tests
|
||||
(arguments, defineChart, presets, crossFilter — including regression tests for
|
||||
every audit finding below), `GlyphOptionsPanel` component tests, plugin-level
|
||||
smoke tests for migrated charts, and a Playwright E2E test for the Customize
|
||||
tab.
|
||||
|
||||
**A full-branch audit (2026-06-11)** ran a seven-angle review over the diff;
|
||||
17 findings were confirmed and 14 fixed in-tree, notably:
|
||||
|
||||
- `behaviors` no longer defaults to `[Behavior.InteractiveChart]` — charts must
|
||||
opt in, matching `ChartMetadata`'s own default (21 charts had acquired dead
|
||||
cross-filter UI).
|
||||
- The real `BigNumberTotalChartPlugin` is registered under
|
||||
`big_number_total` again (a demo plugin had displaced it, breaking saved
|
||||
charts' formats and subheaders).
|
||||
- `@superset-ui/glyph-core` added to root `package.json` so webpack's
|
||||
workspace-alias loop resolves live `src/` (fresh builds previously failed).
|
||||
- Explore's initial-query effect is one-shot again — filling in the last
|
||||
invalid control no longer auto-fires a query for any chart type.
|
||||
- `ChartDefinition.metadata` can now express `queryObjectCount`,
|
||||
`dynamicQueryObjectCount`, `parseMethod`, `suppressContextMenu`, and
|
||||
`enableNoResults` (Mixed Chart's second Results tab returned).
|
||||
- `disabledWhen` reads controls from the correct `mapStateToProps` argument
|
||||
(the API was previously non-functional).
|
||||
- Generated queries carry `timeGrain` on the x-axis column; generated metric
|
||||
extraction uses `getMetricLabel` and no longer guesses among multiple numeric
|
||||
columns.
|
||||
- `NumberFormat`/`TimeFormat` options derive from chart-controls'
|
||||
`D3_FORMAT_OPTIONS` (translated, with previews) instead of stale copies.
|
||||
- Dead parallel implementation (`generators.ts`, 419 lines + tests + orphaned
|
||||
types) deleted; condition evaluation deduplicated to a single function.
|
||||
- `scripts/check-custom-rules.js` suppression matching is line-based (real
|
||||
ESLint semantics) — one disable comment can no longer silence an entire
|
||||
subtree; the leaks it had hidden were fixed with properly scoped disables.
|
||||
|
||||
Branch health: full-monorepo `tsc --noEmit` clean, all 24 packages build, all
|
||||
tests and the complete pre-commit suite (mypy, ruff, pylint, oxlint, prettier,
|
||||
frontend type-check, custom rules) pass.
|
||||
|
||||
### New or Changed Public Interfaces
|
||||
|
||||
- **New package** `@superset-ui/glyph-core` (workspace package; public API:
|
||||
`defineChart`, argument classes, presets, `evaluateGlyphCondition`,
|
||||
`getGlyphControlConfig`, `resolveArgClass`, cross-filter utilities).
|
||||
- **`@superset-ui/chart-controls`**: `ControlPanelConfig._glyphArgs` and
|
||||
`ControlPanelSectionConfig._glyphChartOptions` — `@internal` structural
|
||||
markers consumed by explore.
|
||||
- **`@superset-ui/core`**: `Behavior.AllowsEmptyResults` added to the
|
||||
`Behavior` enum (no producer yet — see Remaining Work).
|
||||
- **Explore**: new `GlyphOptionsPanel` component; `ControlPanelsContainer`,
|
||||
`ExploreChartPanel`, `ExploreViewContainer`, `ChartRenderer`, and
|
||||
`getSectionsToRender` learned the glyph path alongside the legacy one.
|
||||
- **REST API**: `ChartDataExtrasSchema` accepts an optional
|
||||
`allow_empty_query` boolean (`load_default=False`); `get_sqla_query` honors
|
||||
it for validation while refusing to compile a zero-column SELECT.
|
||||
- No new CLI surface, no deployment changes.
|
||||
|
||||
### New dependencies
|
||||
|
||||
None. `@superset-ui/glyph-core` is a new in-repo workspace package depending
|
||||
only on existing workspace packages (`@superset-ui/core`,
|
||||
`@superset-ui/chart-controls`). No new npm or PyPI dependencies.
|
||||
|
||||
### Migration Plan and Compatibility
|
||||
|
||||
- **No database migrations.** Saved charts keep their `viz_type` keys and
|
||||
form data; `defineChart` plugins register under the same keys.
|
||||
- **Form-data compatibility** is preserved through generated controls using the
|
||||
same shared control names (`metric`, `groupby`, `adhoc_filters`, `x_axis`,
|
||||
`time_grain_sqla`) and per-chart transforms reading legacy keys. One known
|
||||
gap remains (camelCase arg keys vs. saved snake_case keys — see Remaining
|
||||
Work item 2).
|
||||
- **Incremental by design**: the legacy `controlPanel` pipeline still works
|
||||
untouched; glyph charts coexist with non-migrated charts. Removal of legacy
|
||||
controlPanel support is an explicit later phase, gated on the roadmap below.
|
||||
|
||||
### Remaining Work / Roadmap
|
||||
|
||||
Ordered by priority; items 1–3 should land before this branch merges or
|
||||
immediately after.
|
||||
|
||||
1. **Restore code-splitting (perf regression).**
|
||||
`loadChart: () => Promise.resolve(Component)` makes every migrated
|
||||
renderer — including maplibre-gl (~800 KB) and deck.gl — eager in the
|
||||
initial bundle, where master lazy-loaded them via
|
||||
`loadChart: () => import('./Chart')`. Add lazy-render support to
|
||||
`defineChart` (e.g. `render: () => import('./Render')` or a
|
||||
`loadRender` field) and move heavy renderers back behind dynamic imports.
|
||||
2. **Per-arg `formDataKey` aliases (saved-chart compatibility).**
|
||||
Some migrated charts renamed keys (e.g. BigNumberTotal's `yAxisFormat` /
|
||||
`subtitle` vs. saved `y_axis_format` / `subheader`). Rendering works via
|
||||
camelCase conversion and transform fallbacks, but explore controls show
|
||||
defaults for saved charts and write parallel keys. A
|
||||
`formDataKey: 'y_axis_format'` alias on argument definitions fixes this
|
||||
generally — and is the foundation for settings transfer between viz types
|
||||
(arguments sharing a semantic key carry over on viz-type switch).
|
||||
3. **Typed escape hatch for legacy transforms.**
|
||||
~14 plugins (table, pivot-table, ag-grid, all deckgl layers,
|
||||
point-cluster-map) use `transform: p => transformProps(p as any) as any`
|
||||
plus `render: props => <Comp {...(props as any)} />`. This double-cast class
|
||||
is what previously hid a silent render-props bug. Provide a
|
||||
`defineChart<Props>` overload (typed transform fully replaces render props)
|
||||
or a `wrapLegacyTransform(transformProps, Component)` helper so the next
|
||||
shape mismatch is a compile error.
|
||||
4. **Collapse the dual control-panel pipeline.**
|
||||
`defineChart` currently emits a full legacy `ControlPanelConfig` *and*
|
||||
native `_glyphArgs`; visibility/disabled/validation exist in both paths
|
||||
with different data sources (Redux controls vs. formData). Make the native
|
||||
definition canonical and derive the legacy config as a thin adapter — this
|
||||
is the enabler for the planned consistent section/area rendering schema.
|
||||
5. **Extract shared timeseries transform logic.**
|
||||
The six Timeseries-family charts each inline ~1,200 lines of largely
|
||||
duplicated transform code. Hoist a shared `timeseriesTransform` into
|
||||
glyph-core (or a glyph-echarts helper) before the copies drift.
|
||||
6. **Resolve the `allow_empty_query` plumbing.**
|
||||
Backend accepts the flag and `Behavior.AllowsEmptyResults` exists, but
|
||||
nothing produces either. Either wire the drag-and-drop empty-chart flow
|
||||
end-to-end (derive the extras flag from chart metadata in one place) or
|
||||
drop the speculative plumbing until the feature lands.
|
||||
7. **Finish the migration and remove legacy controlPanel support** once 1–4
|
||||
are in place, per the original plan: every plugin on `defineChart`, the
|
||||
string-expansion pipeline (`expandControlConfig`, array-of-arrays sections)
|
||||
deleted, and chart wrappers reduced to enable animation/realtime rendering.
|
||||
|
||||
### Rejected Alternatives
|
||||
|
||||
- **`createGlyphPlugin()` builder API** (a Map-based generator predating
|
||||
`defineChart`): shipped briefly as `generators.ts`, never adopted by any
|
||||
chart, drifted from the real implementation, and was deleted during the
|
||||
audit in favor of the single `defineChart()` entry point.
|
||||
- **JSON/YAML chart manifests**: a pure-data format can describe controls but
|
||||
not transforms or renderers, forcing a second mechanism anyway; a typed
|
||||
TypeScript API keeps definition, transform, and render in one
|
||||
compiler-checked file.
|
||||
- **Fixing the legacy controlPanel format in place** (e.g. typed control
|
||||
references over the existing array-of-arrays): preserves the triplication of
|
||||
knowledge across controlPanel/transformProps/component and the runtime
|
||||
string-resolution layer that motivates this SIP.
|
||||
- **Big-bang removal of the legacy pipeline**: rejected in favor of
|
||||
coexistence + escape hatches; third-party plugins and complex charts need a
|
||||
migration window.
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@superset-ui/glyph-core",
|
||||
"version": "0.20.3",
|
||||
"description": "Glyph Core - A declarative visualization plugin framework for Apache Superset",
|
||||
"sideEffects": false,
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"files": [
|
||||
"esm",
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/apache/superset.git",
|
||||
"directory": "superset-frontend/packages/superset-ui-glyph-core"
|
||||
},
|
||||
"keywords": [
|
||||
"superset",
|
||||
"glyph",
|
||||
"visualization",
|
||||
"chart"
|
||||
],
|
||||
"author": "Superset",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/apache/superset/issues"
|
||||
},
|
||||
"homepage": "https://github.com/apache/superset#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@apache-superset/core": "*",
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,621 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import {
|
||||
D3_FORMAT_OPTIONS,
|
||||
D3_TIME_FORMAT_OPTIONS,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ColumnType,
|
||||
SelectOptions,
|
||||
SelectOption,
|
||||
TextOptions,
|
||||
CheckboxOptions,
|
||||
IntOptions,
|
||||
ColorOptions,
|
||||
MetricOptions,
|
||||
DimensionOptions,
|
||||
NumberFormatOptions,
|
||||
CurrencyOptions,
|
||||
CurrencyValue,
|
||||
TimeFormatOptions,
|
||||
ConditionalFormattingOptions,
|
||||
ConditionalFormattingRule,
|
||||
SliderOptions,
|
||||
BoundsOptions,
|
||||
BoundsValue,
|
||||
ColorPickerOptions,
|
||||
RadioButtonOptions,
|
||||
RadioOption,
|
||||
RgbaColor,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Base Argument class - all argument types extend from this.
|
||||
*
|
||||
* Arguments define:
|
||||
* 1. What the chart needs (semantically)
|
||||
* 2. How to render controls in the control panel
|
||||
* 3. Default values and validation
|
||||
*/
|
||||
export class Argument {
|
||||
static label: string | null = null;
|
||||
static description: string | null = null;
|
||||
static columnType: ColumnType = ColumnType.Argument;
|
||||
static controlType: string = 'TextControl';
|
||||
|
||||
value: unknown;
|
||||
|
||||
constructor(value: unknown) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Metric - represents a numeric aggregation (SUM, COUNT, AVG, etc.)
|
||||
*
|
||||
* Maps to Superset's MetricsControl in the query section.
|
||||
*/
|
||||
export class Metric extends Argument {
|
||||
static override label: string | null = 'Metric';
|
||||
static override description: string | null =
|
||||
'A numeric aggregation (SUM, COUNT, AVG, etc.)';
|
||||
static override columnType = ColumnType.Metric;
|
||||
static override controlType = 'MetricsControl';
|
||||
static multi = false;
|
||||
|
||||
static with(options: MetricOptions): typeof Metric {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override multi = options.multi ?? Base.multi;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dimension - represents a categorical column for grouping data
|
||||
*
|
||||
* Maps to Superset's GroupByControl in the query section.
|
||||
*/
|
||||
export class Dimension extends Argument {
|
||||
static override label: string | null = 'Dimension';
|
||||
static override description: string | null =
|
||||
'A categorical column for grouping data';
|
||||
static override columnType = ColumnType.Dimension;
|
||||
static override controlType = 'GroupByControl';
|
||||
static multi = true;
|
||||
|
||||
static with(options: DimensionOptions): typeof Dimension {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override multi = options.multi ?? Base.multi;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporal - represents a time column
|
||||
*
|
||||
* Maps to Superset's temporal controls (x_axis, time_grain_sqla).
|
||||
*/
|
||||
export class Temporal extends Argument {
|
||||
static override label: string | null = 'Time Column';
|
||||
static override description: string | null =
|
||||
'A temporal column for time series data';
|
||||
static override columnType = ColumnType.Temporal;
|
||||
static override controlType = 'TemporalControl';
|
||||
|
||||
static with(options: {
|
||||
label?: string;
|
||||
description?: string;
|
||||
}): typeof Temporal {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select - dropdown selection from predefined options
|
||||
*
|
||||
* Maps to Superset's SelectControl.
|
||||
*/
|
||||
export class Select extends Argument {
|
||||
static override label: string | null = 'Select';
|
||||
static override description: string | null = 'Choose from options';
|
||||
static override controlType = 'SelectControl';
|
||||
static default: string | number = '';
|
||||
static options: SelectOption[] = [];
|
||||
static clearable = false;
|
||||
|
||||
static with(options: SelectOptions): typeof Select {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
static override options = options.options ?? Base.options;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text - free-form text input
|
||||
*
|
||||
* Maps to Superset's TextControl.
|
||||
*/
|
||||
export class Text extends Argument {
|
||||
static override label: string | null = 'Text';
|
||||
static override description: string | null = 'Text input';
|
||||
static override controlType = 'TextControl';
|
||||
static default: string = '';
|
||||
static placeholder: string = '';
|
||||
|
||||
static with(options: TextOptions): typeof Text {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
static override placeholder = options.placeholder ?? Base.placeholder;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkbox - boolean toggle
|
||||
*
|
||||
* Maps to Superset's CheckboxControl.
|
||||
*/
|
||||
export class Checkbox extends Argument {
|
||||
static override label: string | null = 'Checkbox';
|
||||
static override description: string | null = 'Toggle option';
|
||||
static override controlType = 'CheckboxControl';
|
||||
static default: boolean = false;
|
||||
|
||||
static with(options: CheckboxOptions): typeof Checkbox {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Int - numeric input with slider
|
||||
*
|
||||
* Maps to Superset's SliderControl.
|
||||
*/
|
||||
export class Int extends Argument {
|
||||
static override label: string | null = 'Integer';
|
||||
static override description: string | null = 'A numeric value';
|
||||
static override controlType = 'SliderControl';
|
||||
static default: number = 0;
|
||||
static min: number = 0;
|
||||
static max: number = 100;
|
||||
static step: number = 1;
|
||||
|
||||
static with(options: IntOptions): typeof Int {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
static override min = options.min ?? Base.min;
|
||||
static override max = options.max ?? Base.max;
|
||||
static override step = options.step ?? Base.step;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Color - color picker
|
||||
*
|
||||
* Maps to Superset's ColorPickerControl.
|
||||
*/
|
||||
export class Color extends Argument {
|
||||
static override label: string | null = 'Color';
|
||||
static override description: string | null = 'A color value';
|
||||
static override controlType = 'ColorPickerControl';
|
||||
// eslint-disable-next-line theme-colors/no-literal-colors
|
||||
static default: string = '#000000';
|
||||
|
||||
static with(options: ColorOptions): typeof Color {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NumberFormat - D3 number format string selection
|
||||
*
|
||||
* Maps to Superset's SelectControl with D3 format options.
|
||||
* Allows freeform input for custom formats.
|
||||
*/
|
||||
export class NumberFormat extends Argument {
|
||||
static override label: string | null = 'Number Format';
|
||||
static override description: string | null =
|
||||
'D3 format string for number display (e.g., ".2f", ".1%", ",.0f")';
|
||||
static override controlType = 'NumberFormatControl';
|
||||
static default: string = 'SMART_NUMBER';
|
||||
|
||||
// Standard D3 format options — derived from the canonical list in
|
||||
// @superset-ui/chart-controls so glyph charts offer the same formats
|
||||
// (with previews and translations) as legacy charts, and new formats
|
||||
// propagate automatically.
|
||||
static readonly FORMAT_OPTIONS: SelectOption[] = D3_FORMAT_OPTIONS.map(
|
||||
([value, label]) => ({ value, label }),
|
||||
);
|
||||
|
||||
static with(options: NumberFormatOptions): typeof NumberFormat {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Currency - currency format with symbol and position
|
||||
*
|
||||
* Maps to Superset's CurrencyControl.
|
||||
* Value is { symbol: 'USD', symbolPosition: 'prefix' | 'suffix' }
|
||||
*/
|
||||
export class Currency extends Argument {
|
||||
static override label: string | null = 'Currency Format';
|
||||
static override description: string | null =
|
||||
'Currency symbol and position for formatting';
|
||||
static override controlType = 'CurrencyControl';
|
||||
static default: CurrencyValue = {};
|
||||
|
||||
static with(options: CurrencyOptions): typeof Currency {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TimeFormat - D3 time format string selection
|
||||
*
|
||||
* Maps to Superset's SelectControl with D3 time format options.
|
||||
* Allows freeform input for custom formats.
|
||||
*/
|
||||
export class TimeFormat extends Argument {
|
||||
static override label: string | null = 'Time Format';
|
||||
static override description: string | null =
|
||||
'D3 time format string (e.g., "%Y-%m-%d", "%H:%M:%S")';
|
||||
static override controlType = 'TimeFormatControl';
|
||||
static default: string = 'smart_date';
|
||||
|
||||
// Standard D3 time format options — derived from the canonical list in
|
||||
// @superset-ui/chart-controls (see NumberFormat.FORMAT_OPTIONS).
|
||||
static readonly FORMAT_OPTIONS: SelectOption[] = D3_TIME_FORMAT_OPTIONS.map(
|
||||
([value, label]) => ({ value, label }),
|
||||
);
|
||||
|
||||
static with(options: TimeFormatOptions): typeof TimeFormat {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ConditionalFormatting - apply color rules based on metric values
|
||||
*
|
||||
* This is a special argument type that encapsulates the complex
|
||||
* mapStateToProps logic needed for conditional formatting controls.
|
||||
* The control automatically receives numeric column options from the chart response.
|
||||
*/
|
||||
export class ConditionalFormatting extends Argument {
|
||||
static override label: string | null = 'Conditional Formatting';
|
||||
static override description: string | null =
|
||||
'Apply conditional color formatting to metric values';
|
||||
static override controlType = 'ConditionalFormattingControl';
|
||||
static default: ConditionalFormattingRule[] = [];
|
||||
|
||||
static with(
|
||||
options: ConditionalFormattingOptions,
|
||||
): typeof ConditionalFormatting {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slider - continuous floating point values with min/max/step
|
||||
*
|
||||
* Similar to Int but for float values.
|
||||
* Maps to Superset's SliderControl.
|
||||
*/
|
||||
export class Slider extends Argument {
|
||||
static override label: string | null = 'Slider';
|
||||
static override description: string | null = 'A continuous numeric value';
|
||||
static override controlType = 'SliderControl';
|
||||
static default: number = 0;
|
||||
static min: number = 0;
|
||||
static max: number = 1;
|
||||
static step: number = 0.1;
|
||||
|
||||
static with(options: SliderOptions): typeof Slider {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
static override min = options.min ?? Base.min;
|
||||
static override max = options.max ?? Base.max;
|
||||
static override step = options.step ?? Base.step;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounds - min/max value pairs
|
||||
*
|
||||
* Used for axis bounds, value ranges, etc.
|
||||
* Maps to Superset's BoundsControl.
|
||||
*/
|
||||
export class Bounds extends Argument {
|
||||
static override label: string | null = 'Bounds';
|
||||
static override description: string | null = 'Min and max value bounds';
|
||||
static override controlType = 'BoundsControl';
|
||||
static default: BoundsValue = [null, null];
|
||||
|
||||
static with(options: BoundsOptions): typeof Bounds {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ColorPicker - RGBA color selection
|
||||
*
|
||||
* Different from Color (which uses hex strings).
|
||||
* Maps to Superset's ColorPickerControl with RGBA format.
|
||||
*/
|
||||
export class ColorPicker extends Argument {
|
||||
static override label: string | null = 'Color';
|
||||
static override description: string | null = 'Select a color';
|
||||
static override controlType = 'ColorPickerControl';
|
||||
static default: RgbaColor = { r: 0, g: 0, b: 0, a: 1 };
|
||||
|
||||
static with(options: ColorPickerOptions): typeof ColorPicker {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RadioButton - mutually exclusive options
|
||||
*
|
||||
* Use for small sets of exclusive choices (2-4 options).
|
||||
* Maps to Superset's RadioButtonControl.
|
||||
*/
|
||||
export class RadioButton extends Argument {
|
||||
static override label: string | null = 'Option';
|
||||
static override description: string | null = 'Select one option';
|
||||
static override controlType = 'RadioButtonControl';
|
||||
static default: string | boolean = '';
|
||||
static options: RadioOption[] = [];
|
||||
|
||||
static with(options: RadioButtonOptions): typeof RadioButton {
|
||||
const Base = this;
|
||||
return class extends Base {
|
||||
static override label = options.label ?? Base.label;
|
||||
static override description = options.description ?? Base.description;
|
||||
static override default = options.default ?? Base.default;
|
||||
static override options = options.options;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a ConditionalFormatting type
|
||||
*/
|
||||
export function isConditionalFormattingArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof ConditionalFormatting {
|
||||
return argClass.controlType === 'ConditionalFormattingControl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a TimeFormat type
|
||||
*/
|
||||
export function isTimeFormatArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof TimeFormat {
|
||||
return argClass.controlType === 'TimeFormatControl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a NumberFormat type
|
||||
*/
|
||||
export function isNumberFormatArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof NumberFormat {
|
||||
return argClass.controlType === 'NumberFormatControl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Currency type
|
||||
*/
|
||||
export function isCurrencyArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Currency {
|
||||
return argClass.controlType === 'CurrencyControl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Select type
|
||||
*/
|
||||
export function isSelectArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Select {
|
||||
return (
|
||||
'options' in argClass && Array.isArray((argClass as typeof Select).options)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Checkbox type
|
||||
*/
|
||||
export function isCheckboxArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Checkbox {
|
||||
return (
|
||||
'default' in argClass &&
|
||||
typeof (argClass as typeof Checkbox).default === 'boolean'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Text type
|
||||
*/
|
||||
export function isTextArg(argClass: typeof Argument): argClass is typeof Text {
|
||||
return (
|
||||
argClass.controlType === 'TextControl' ||
|
||||
(argClass.prototype instanceof Text &&
|
||||
!isSelectArg(argClass) &&
|
||||
!isCheckboxArg(argClass))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is an Int type
|
||||
*/
|
||||
export function isIntArg(argClass: typeof Argument): argClass is typeof Int {
|
||||
return 'min' in argClass && 'max' in argClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Color type
|
||||
*/
|
||||
export function isColorArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Color {
|
||||
return (
|
||||
argClass.controlType === 'ColorPickerControl' ||
|
||||
argClass.prototype instanceof Color
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Metric type
|
||||
*/
|
||||
export function isMetricArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Metric {
|
||||
return argClass.columnType === ColumnType.Metric;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Dimension type
|
||||
*/
|
||||
export function isDimensionArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Dimension {
|
||||
return argClass.columnType === ColumnType.Dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Temporal type
|
||||
*/
|
||||
export function isTemporalArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Temporal {
|
||||
return argClass.columnType === ColumnType.Temporal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Slider type
|
||||
*/
|
||||
export function isSliderArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Slider {
|
||||
return (
|
||||
argClass.controlType === 'SliderControl' &&
|
||||
'step' in argClass &&
|
||||
typeof (argClass as typeof Slider).step === 'number'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a Bounds type
|
||||
*/
|
||||
export function isBoundsArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof Bounds {
|
||||
return argClass.controlType === 'BoundsControl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a ColorPicker type
|
||||
*/
|
||||
export function isColorPickerArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof ColorPicker {
|
||||
return (
|
||||
argClass.controlType === 'ColorPickerControl' &&
|
||||
'default' in argClass &&
|
||||
typeof (argClass as typeof ColorPicker).default === 'object' &&
|
||||
'r' in ((argClass as typeof ColorPicker).default as object)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an argument class is a RadioButton type
|
||||
*/
|
||||
export function isRadioButtonArg(
|
||||
argClass: typeof Argument,
|
||||
): argClass is typeof RadioButton {
|
||||
return argClass.controlType === 'RadioButtonControl';
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cross-Filter Utilities for Glyph Charts
|
||||
*
|
||||
* This module provides helpers for implementing cross-filtering in Glyph charts.
|
||||
* Cross-filtering allows charts to filter other charts on the dashboard when
|
||||
* users click on data points.
|
||||
*
|
||||
* ## Quick Start
|
||||
*
|
||||
* 1. Add behaviors to metadata:
|
||||
* ```typescript
|
||||
* metadata: {
|
||||
* behaviors: [Behavior.InteractiveChart, Behavior.DrillToDetail, Behavior.DrillBy],
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 2. Extract cross-filter props in transform:
|
||||
* ```typescript
|
||||
* transform: (chartProps) => {
|
||||
* const crossFilterProps = extractCrossFilterProps(chartProps, groupby, labelMap);
|
||||
* return { transformedProps: { ...otherProps, ...crossFilterProps } };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 3. Use event handlers in render:
|
||||
* ```typescript
|
||||
* render: ({ transformedProps }) => {
|
||||
* const eventHandlers = allEventHandlers(transformedProps);
|
||||
* return <Echart eventHandlers={eventHandlers} ... />;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import type {
|
||||
ChartProps,
|
||||
FilterState,
|
||||
QueryFormColumn,
|
||||
SetDataMaskHook,
|
||||
ContextMenuFilters,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
/**
|
||||
* Props needed for cross-filtering in the render component.
|
||||
* These are typically returned from the transform function and passed to Echart.
|
||||
*/
|
||||
export interface CrossFilterRenderProps {
|
||||
/** Groupby columns used for filtering */
|
||||
groupby: QueryFormColumn[];
|
||||
/** Maps series names to their groupby column values */
|
||||
labelMap: Record<string, string[]>;
|
||||
/** Callback to emit cross-filter data mask */
|
||||
setDataMask: SetDataMaskHook;
|
||||
/** Maps series indices to selected value names */
|
||||
selectedValues: Record<number, string>;
|
||||
/** Whether cross-filters are enabled for this chart */
|
||||
emitCrossFilters?: boolean;
|
||||
/** Context menu handler for drill actions */
|
||||
onContextMenu?: (
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
filters?: ContextMenuFilters,
|
||||
) => void;
|
||||
/** Column type mapping for formatting */
|
||||
coltypeMapping?: Record<string, number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a selectedValues map from filterState.
|
||||
*
|
||||
* The selectedValues map is used by the Echart component to track which
|
||||
* data points are currently selected (for highlighting).
|
||||
*
|
||||
* @param filterState - Current filter state from chartProps
|
||||
* @param seriesNames - Array of series/data point names
|
||||
* @returns Map of index -> name for selected values
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const selectedValues = createSelectedValuesMap(
|
||||
* filterState,
|
||||
* transformedData.map(d => d.name),
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
export function createSelectedValuesMap(
|
||||
filterState: FilterState | undefined,
|
||||
seriesNames: string[],
|
||||
): Record<number, string> {
|
||||
return (filterState?.selectedValues || []).reduce(
|
||||
(acc: Record<number, string>, selectedValue: string) => {
|
||||
const index = seriesNames.findIndex(name => name === selectedValue);
|
||||
if (index >= 0) {
|
||||
return { ...acc, [index]: selectedValue };
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract cross-filter related props from ChartProps.
|
||||
*
|
||||
* This is a convenience function that extracts all the props needed for
|
||||
* cross-filtering from the standard ChartProps object.
|
||||
*
|
||||
* @param chartProps - The chart props from Superset
|
||||
* @param groupby - The groupby columns (dimensions) from form data
|
||||
* @param labelMap - A map from series names to their groupby values
|
||||
* @param seriesNames - Array of series/data point names for selectedValues mapping
|
||||
* @param coltypeMapping - Optional column type mapping
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In transform function:
|
||||
* const labelMap = data.reduce((acc, datum) => ({
|
||||
* ...acc,
|
||||
* [extractGroupbyLabel({ datum, groupby })]: groupby.map(col => datum[col]),
|
||||
* }), {});
|
||||
*
|
||||
* const crossFilterProps = extractCrossFilterProps(
|
||||
* chartProps,
|
||||
* groupby,
|
||||
* labelMap,
|
||||
* transformedData.map(d => d.name),
|
||||
* coltypeMapping,
|
||||
* );
|
||||
*
|
||||
* return {
|
||||
* transformedProps: {
|
||||
* echartOptions,
|
||||
* formData,
|
||||
* width,
|
||||
* height,
|
||||
* refs,
|
||||
* ...crossFilterProps,
|
||||
* },
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export function extractCrossFilterProps(
|
||||
chartProps: ChartProps,
|
||||
groupby: QueryFormColumn[],
|
||||
labelMap: Record<string, string[]>,
|
||||
seriesNames: string[],
|
||||
coltypeMapping?: Record<string, number>,
|
||||
): CrossFilterRenderProps {
|
||||
const { hooks, filterState, emitCrossFilters, formData } = chartProps;
|
||||
const { setDataMask = () => {}, onContextMenu } = hooks ?? {};
|
||||
|
||||
const selectedValues = createSelectedValuesMap(filterState, seriesNames);
|
||||
|
||||
return {
|
||||
groupby,
|
||||
labelMap,
|
||||
setDataMask,
|
||||
selectedValues,
|
||||
emitCrossFilters,
|
||||
onContextMenu,
|
||||
coltypeMapping,
|
||||
// Also include formData for context menu formatting
|
||||
formData,
|
||||
} as CrossFilterRenderProps & { formData: unknown };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a data point is currently filtered (should be dimmed).
|
||||
*
|
||||
* Use this in the transform function to apply opacity/styling to
|
||||
* data points that are not part of the current filter selection.
|
||||
*
|
||||
* @param filterState - Current filter state from chartProps
|
||||
* @param name - The name/label of the data point to check
|
||||
* @returns true if the data point should be dimmed, false otherwise
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const isFiltered = isDataPointFiltered(filterState, datum.name);
|
||||
* const opacity = isFiltered ? OpacityEnum.SemiTransparent : OpacityEnum.NonTransparent;
|
||||
* ```
|
||||
*/
|
||||
export function isDataPointFiltered(
|
||||
filterState: FilterState | undefined,
|
||||
name: string,
|
||||
): boolean {
|
||||
return Boolean(
|
||||
filterState?.selectedValues &&
|
||||
filterState.selectedValues.length > 0 &&
|
||||
!filterState.selectedValues.includes(name),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a labelMap from data records.
|
||||
*
|
||||
* The labelMap maps series names (like "USA" or "2024-01") to their
|
||||
* corresponding groupby column values. This is needed for the cross-filter
|
||||
* event handlers to construct proper filter clauses.
|
||||
*
|
||||
* @param data - Array of data records
|
||||
* @param groupbyLabels - Array of groupby column labels
|
||||
* @param extractLabel - Function to extract the series label from a datum
|
||||
* @returns Map of label -> groupby values
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const labelMap = createLabelMap(
|
||||
* data,
|
||||
* groupbyLabels,
|
||||
* datum => extractGroupbyLabel({ datum, groupby: groupbyLabels, coltypeMapping }),
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
export function createLabelMap<T extends Record<string, unknown>>(
|
||||
data: T[],
|
||||
groupbyLabels: string[],
|
||||
extractLabel: (datum: T) => string,
|
||||
): Record<string, string[]> {
|
||||
return data.reduce((acc: Record<string, string[]>, datum: T) => {
|
||||
const label = extractLabel(datum);
|
||||
return {
|
||||
...acc,
|
||||
[label]: groupbyLabels.map(col => datum[col] as string),
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Glyph Core - A declarative visualization plugin framework
|
||||
*
|
||||
* This module enables single-file visualization plugins where:
|
||||
* 1. Arguments define both the chart's inputs AND the control panel
|
||||
* 2. transformProps is auto-generated from argument definitions
|
||||
* 3. The chart component is a simple function receiving typed arguments
|
||||
*
|
||||
* Features:
|
||||
* - Single-file chart definitions with defineChart()
|
||||
* - Declarative argument types (Metric, Dimension, Select, Checkbox, etc.)
|
||||
* - Conditional visibility with visibleWhen/disabledWhen
|
||||
* - Cross-filtering support with extractCrossFilterProps() and allEventHandlers()
|
||||
* - Reusable presets (ShowLegend, HeaderFontSize, etc.)
|
||||
*
|
||||
* Example usage:
|
||||
* ```typescript
|
||||
* export default defineChart({
|
||||
* metadata: {
|
||||
* name: 'My Chart',
|
||||
* thumbnail,
|
||||
* behaviors: [Behavior.InteractiveChart, Behavior.DrillToDetail, Behavior.DrillBy],
|
||||
* },
|
||||
* arguments: {
|
||||
* metric: Metric.with({ label: 'Metric' }),
|
||||
* groupby: Dimension.with({ label: 'Breakdowns' }),
|
||||
* fontSize: Select.with({
|
||||
* label: 'Font Size',
|
||||
* options: [{ label: 'Small', value: 0.2 }, { label: 'Large', value: 0.4 }],
|
||||
* default: 0.3,
|
||||
* }),
|
||||
* },
|
||||
* transform: (chartProps, argValues) => {
|
||||
* // Extract cross-filter props for interactive filtering
|
||||
* const crossFilterProps = extractCrossFilterProps(chartProps, groupby, labelMap, seriesNames);
|
||||
* return { transformedProps: { echartOptions, ...crossFilterProps } };
|
||||
* },
|
||||
* render: ({ transformedProps }) => {
|
||||
* const eventHandlers = allEventHandlers(transformedProps);
|
||||
* return <Echart eventHandlers={eventHandlers} ... />;
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
// Re-export everything
|
||||
export * from './types';
|
||||
export * from './arguments';
|
||||
export * from './defineChart';
|
||||
export * from './presets';
|
||||
export * from './crossFilter';
|
||||
408
superset-frontend/packages/superset-ui-glyph-core/src/presets.ts
Normal file
408
superset-frontend/packages/superset-ui-glyph-core/src/presets.ts
Normal file
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Glyph Presets - Reusable argument configurations
|
||||
*
|
||||
* This module contains pre-configured arguments that are commonly
|
||||
* used across multiple visualization types. Charts can import these
|
||||
* directly or use .with() to customize them further.
|
||||
*
|
||||
* Example usage:
|
||||
* ```typescript
|
||||
* import { HeaderFontSize, Subtitle } from '../../glyph-core/presets';
|
||||
*
|
||||
* arguments: {
|
||||
* headerFontSize: HeaderFontSize,
|
||||
* subtitle: Subtitle,
|
||||
* // Override defaults when needed:
|
||||
* customSize: HeaderFontSize.with({ default: 0.5 }),
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { Select, Text, Checkbox } from './arguments';
|
||||
import { SelectOption } from './types';
|
||||
|
||||
// ============================================================================
|
||||
// Font Size Options
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Large font size options - for primary/header text elements
|
||||
* Values are multipliers of container height (0.2 = 20% of height)
|
||||
*/
|
||||
export const FONT_SIZE_OPTIONS_LARGE: SelectOption[] = [
|
||||
{ label: t('Tiny'), value: 0.2 },
|
||||
{ label: t('Small'), value: 0.3 },
|
||||
{ label: t('Normal'), value: 0.4 },
|
||||
{ label: t('Large'), value: 0.5 },
|
||||
{ label: t('Huge'), value: 0.6 },
|
||||
];
|
||||
|
||||
/**
|
||||
* Small font size options - for secondary text elements (subtitles, labels)
|
||||
* Values are multipliers of container height
|
||||
*/
|
||||
export const FONT_SIZE_OPTIONS_SMALL: SelectOption[] = [
|
||||
{ label: t('Tiny'), value: 0.125 },
|
||||
{ label: t('Small'), value: 0.15 },
|
||||
{ label: t('Normal'), value: 0.2 },
|
||||
{ label: t('Large'), value: 0.3 },
|
||||
{ label: t('Huge'), value: 0.4 },
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// Pre-configured Arguments
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Header/primary font size selector
|
||||
* Used for main display elements like big numbers, titles
|
||||
*/
|
||||
export const HeaderFontSize = Select.with({
|
||||
label: t('Font Size'),
|
||||
description: t('Font size for the primary display element'),
|
||||
options: FONT_SIZE_OPTIONS_LARGE,
|
||||
default: 0.4,
|
||||
});
|
||||
|
||||
/**
|
||||
* Subheader/secondary font size selector
|
||||
* Used for subtitles, labels, secondary text
|
||||
*/
|
||||
export const SubheaderFontSize = Select.with({
|
||||
label: t('Subheader Font Size'),
|
||||
description: t('Font size for secondary text elements'),
|
||||
options: FONT_SIZE_OPTIONS_SMALL,
|
||||
default: 0.15,
|
||||
});
|
||||
|
||||
/**
|
||||
* Subtitle text input
|
||||
* Generic subtitle/description field used by many chart types
|
||||
*/
|
||||
export const Subtitle = Text.with({
|
||||
label: t('Subtitle'),
|
||||
description: t('Description text displayed below the main content'),
|
||||
default: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* Show legend toggle
|
||||
* Common toggle for charts with legends
|
||||
*/
|
||||
export const ShowLegend = Checkbox.with({
|
||||
// Strings match plugin-chart-echarts' legend controls so existing i18n
|
||||
// catalogs apply and legend UX stays consistent across chart families.
|
||||
label: t('Show legend'),
|
||||
description: t('Whether to display a legend for the chart'),
|
||||
default: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* Force timestamp formatting toggle
|
||||
* Used when a value might be a timestamp but isn't auto-detected
|
||||
*/
|
||||
export const ForceTimestampFormatting = Checkbox.with({
|
||||
label: t('Force Date Format'),
|
||||
description: t(
|
||||
'Use date formatting even when the value is not detected as a timestamp',
|
||||
),
|
||||
default: false,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Legend Options
|
||||
// ============================================================================
|
||||
|
||||
export const LEGEND_TYPE_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Scroll'), value: 'scroll' },
|
||||
{ label: t('List'), value: 'plain' },
|
||||
];
|
||||
|
||||
export const LEGEND_ORIENTATION_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Top'), value: 'top' },
|
||||
{ label: t('Bottom'), value: 'bottom' },
|
||||
{ label: t('Left'), value: 'left' },
|
||||
{ label: t('Right'), value: 'right' },
|
||||
];
|
||||
|
||||
export const LEGEND_SORT_OPTIONS: SelectOption[] = [
|
||||
{ label: t('No sort'), value: '' },
|
||||
{ label: t('Ascending'), value: 'asc' },
|
||||
{ label: t('Descending'), value: 'desc' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Legend type selector
|
||||
* Choose between scrollable or plain list legend
|
||||
*/
|
||||
export const LegendType = Select.with({
|
||||
label: t('Type'),
|
||||
description: t('Legend type'),
|
||||
options: LEGEND_TYPE_OPTIONS,
|
||||
default: 'scroll',
|
||||
});
|
||||
|
||||
/**
|
||||
* Legend orientation selector
|
||||
* Position the legend relative to the chart
|
||||
*/
|
||||
export const LegendOrientation = Select.with({
|
||||
label: t('Orientation'),
|
||||
description: t('Legend Orientation'),
|
||||
options: LEGEND_ORIENTATION_OPTIONS,
|
||||
default: 'top',
|
||||
});
|
||||
|
||||
/**
|
||||
* Legend sort selector
|
||||
* Sort legend items alphabetically
|
||||
*/
|
||||
export const LegendSort = Select.with({
|
||||
label: t('Legend Sort'),
|
||||
description: t('Sort order for legend items'),
|
||||
options: LEGEND_SORT_OPTIONS,
|
||||
default: '',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Label Presets
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Show labels toggle
|
||||
* Common toggle for chart labels
|
||||
*/
|
||||
export const ShowLabels = Checkbox.with({
|
||||
label: t('Show Labels'),
|
||||
description: t('Whether to display labels on the chart'),
|
||||
default: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* Show value toggle
|
||||
* Common toggle for showing values on chart elements
|
||||
*/
|
||||
export const ShowValue = Checkbox.with({
|
||||
label: t('Show Value'),
|
||||
description: t('Whether to display values on the chart'),
|
||||
default: false,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Metric Name Presets
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Show metric name toggle
|
||||
* Used in BigNumber charts to optionally show the metric name
|
||||
*/
|
||||
export const ShowMetricName = Checkbox.with({
|
||||
label: t('Show Metric Name'),
|
||||
description: t('Whether to display the metric name as a title'),
|
||||
default: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* Metric name font size selector
|
||||
* Typically used with visibility tied to ShowMetricName
|
||||
*/
|
||||
export const MetricNameFontSize = Select.with({
|
||||
label: t('Metric Name Font Size'),
|
||||
description: t('Font size for the metric name'),
|
||||
options: FONT_SIZE_OPTIONS_SMALL,
|
||||
default: 0.15,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Label Type Options (shared by Pie, Funnel, etc.)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Standard label content type options
|
||||
* Used by Pie, Funnel, and other category-based charts
|
||||
*/
|
||||
export const LABEL_TYPE_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Category Name'), value: 'key' },
|
||||
{ label: t('Value'), value: 'value' },
|
||||
{ label: t('Percentage'), value: 'percent' },
|
||||
{ label: t('Category and Value'), value: 'key_value' },
|
||||
{ label: t('Category and Percentage'), value: 'key_percent' },
|
||||
{ label: t('Category, Value and Percentage'), value: 'key_value_percent' },
|
||||
{ label: t('Value and Percentage'), value: 'value_percent' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Label type selector for category-based charts
|
||||
*/
|
||||
export const LabelType = Select.with({
|
||||
label: t('Label Type'),
|
||||
description: t('What should be shown on the label?'),
|
||||
options: LABEL_TYPE_OPTIONS,
|
||||
default: 'key',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Sort Options
|
||||
// ============================================================================
|
||||
|
||||
export const SORT_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Descending'), value: 'descending' },
|
||||
{ label: t('Ascending'), value: 'ascending' },
|
||||
{ label: t('None'), value: 'none' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Sort by metric toggle
|
||||
* Common for charts that need to sort data by metric value
|
||||
*/
|
||||
export const SortByMetric = Checkbox.with({
|
||||
label: t('Sort by Metric'),
|
||||
description: t('Sort results by the selected metric'),
|
||||
default: true,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Label Position Options
|
||||
// ============================================================================
|
||||
|
||||
export const LABEL_POSITION_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Top'), value: 'top' },
|
||||
{ label: t('Left'), value: 'left' },
|
||||
{ label: t('Right'), value: 'right' },
|
||||
{ label: t('Bottom'), value: 'bottom' },
|
||||
{ label: t('Inside'), value: 'inside' },
|
||||
{ label: t('Inside Left'), value: 'insideLeft' },
|
||||
{ label: t('Inside Right'), value: 'insideRight' },
|
||||
{ label: t('Inside Top'), value: 'insideTop' },
|
||||
{ label: t('Inside Bottom'), value: 'insideBottom' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Label position selector
|
||||
* Position labels relative to chart elements
|
||||
*/
|
||||
export const LabelPosition = Select.with({
|
||||
label: t('Label Position'),
|
||||
description: t('Position of labels on the chart'),
|
||||
options: LABEL_POSITION_OPTIONS,
|
||||
default: 'top',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Simple Label Type (key/value variants only)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Simple label type options - for charts with fewer label display options
|
||||
* Used by Radar, Sunburst, etc.
|
||||
*/
|
||||
export const SIMPLE_LABEL_TYPE_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Category Name'), value: 'key' },
|
||||
{ label: t('Value'), value: 'value' },
|
||||
{ label: t('Category and Value'), value: 'key_value' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Simple label type selector
|
||||
* For charts that only need key/value/key_value options
|
||||
*/
|
||||
export const SimpleLabelType = Select.with({
|
||||
label: t('Label Type'),
|
||||
description: t('What should be shown on the label?'),
|
||||
options: SIMPLE_LABEL_TYPE_OPTIONS,
|
||||
default: 'key',
|
||||
});
|
||||
|
||||
/**
|
||||
* Value-only label type options - for charts like Radar
|
||||
*/
|
||||
export const VALUE_LABEL_TYPE_OPTIONS: SelectOption[] = [
|
||||
{ label: t('Value'), value: 'value' },
|
||||
{ label: t('Category and Value'), value: 'key_value' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Value label type selector
|
||||
* For charts that show value or category+value
|
||||
*/
|
||||
export const ValueLabelType = Select.with({
|
||||
label: t('Label Type'),
|
||||
description: t('What should be shown on the label?'),
|
||||
options: VALUE_LABEL_TYPE_OPTIONS,
|
||||
default: 'value',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Totals and Aggregates
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Show total toggle
|
||||
* For charts that can display aggregate totals
|
||||
*/
|
||||
export const ShowTotal = Checkbox.with({
|
||||
label: t('Show Total'),
|
||||
description: t('Whether to display the aggregate total'),
|
||||
default: false,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Threshold Controls
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Label percentage threshold
|
||||
* Minimum percentage for showing labels (avoids clutter on small slices)
|
||||
*/
|
||||
export const LabelThreshold = Text.with({
|
||||
label: t('Percentage Threshold'),
|
||||
description: t('Minimum threshold in percentage points for showing labels'),
|
||||
default: '5',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Shape Options
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Circle shape toggle (used by Radar)
|
||||
*/
|
||||
export const CircleShape = Checkbox.with({
|
||||
label: t('Circle Shape'),
|
||||
description: t('Use circular shape instead of polygon'),
|
||||
default: false,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Data Zoom
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Enable data zoom toggle
|
||||
* For charts with zoomable data areas
|
||||
*/
|
||||
export const DataZoom = Checkbox.with({
|
||||
label: t('Data Zoom'),
|
||||
description: t('Enable data zooming controls'),
|
||||
default: false,
|
||||
});
|
||||
257
superset-frontend/packages/superset-ui-glyph-core/src/types.ts
Normal file
257
superset-frontend/packages/superset-ui-glyph-core/src/types.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Option for Select controls
|
||||
*/
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Select argument type
|
||||
*/
|
||||
export interface SelectOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string | number;
|
||||
options?: SelectOption[];
|
||||
clearable?: boolean;
|
||||
renderTrigger?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Text argument type
|
||||
*/
|
||||
export interface TextOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Checkbox argument type
|
||||
*/
|
||||
export interface CheckboxOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Int argument type (slider)
|
||||
*/
|
||||
export interface IntOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Color argument type
|
||||
*/
|
||||
export interface ColorOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Metric argument type
|
||||
*/
|
||||
export interface MetricOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Dimension argument type
|
||||
*/
|
||||
export interface DimensionOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for NumberFormat argument type
|
||||
*/
|
||||
export interface NumberFormatOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currency value structure
|
||||
*/
|
||||
export interface CurrencyValue {
|
||||
symbol?: string;
|
||||
symbolPosition?: 'prefix' | 'suffix';
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Currency argument type
|
||||
*/
|
||||
export interface CurrencyOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: CurrencyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for TimeFormat argument type
|
||||
*/
|
||||
export interface TimeFormatOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for ConditionalFormatting argument type
|
||||
*/
|
||||
export interface ConditionalFormattingOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Slider argument type (continuous float values)
|
||||
*/
|
||||
export interface SliderOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for Bounds argument type (min/max pairs)
|
||||
*/
|
||||
export interface BoundsOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: [number | null, number | null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounds value type - tuple of [min, max] where either can be null
|
||||
*/
|
||||
export type BoundsValue = [number | null, number | null];
|
||||
|
||||
/**
|
||||
* Configuration options for ColorPicker argument type (RGBA colors)
|
||||
*/
|
||||
export interface ColorPickerOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: RgbaColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for RadioButton argument type
|
||||
*/
|
||||
export interface RadioButtonOptions {
|
||||
label?: string;
|
||||
description?: string;
|
||||
default?: string | boolean;
|
||||
options: RadioOption[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Option for RadioButton controls
|
||||
*/
|
||||
export interface RadioOption {
|
||||
label: string;
|
||||
value: string | boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditional formatting rule value
|
||||
*/
|
||||
export interface ConditionalFormattingRule {
|
||||
column?: string;
|
||||
operator?: '<' | '<=' | '>' | '>=' | '==' | '!=' | 'between';
|
||||
targetValue?: number;
|
||||
targetValueLeft?: number;
|
||||
targetValueRight?: number;
|
||||
colorScheme?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Column type enum for data arguments
|
||||
*/
|
||||
export enum ColumnType {
|
||||
Metric = 'metric',
|
||||
Dimension = 'dimension',
|
||||
Temporal = 'temporal',
|
||||
Argument = 'argument',
|
||||
}
|
||||
|
||||
/**
|
||||
* RGBA color format used by Superset's ColorPickerControl
|
||||
*/
|
||||
export interface RgbaColor {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visibility function for conditional control display (legacy)
|
||||
*/
|
||||
export type VisibilityFn = (state: {
|
||||
controls: Record<string, { value: unknown }>;
|
||||
}) => boolean;
|
||||
|
||||
/**
|
||||
* Declarative condition for argument visibility/disabled state.
|
||||
*
|
||||
* Keys are argument names, values define the condition:
|
||||
* - Literal value: equality check (e.g., { showMetricName: true })
|
||||
* - Function: custom check (e.g., { subtitle: (val) => !!val })
|
||||
*
|
||||
* Multiple keys are AND'd together.
|
||||
*
|
||||
* @example
|
||||
* // Visible when showMetricName is true
|
||||
* visibleWhen: { showMetricName: true }
|
||||
*
|
||||
* @example
|
||||
* // Visible when subtitle is not empty
|
||||
* visibleWhen: { subtitle: (val) => !!val }
|
||||
*
|
||||
* @example
|
||||
* // Visible when showMetricName is true AND subtitle is not empty
|
||||
* visibleWhen: { showMetricName: true, subtitle: (val) => !!val }
|
||||
*/
|
||||
export type ArgumentCondition = Record<
|
||||
string,
|
||||
unknown | ((value: unknown) => boolean)
|
||||
>;
|
||||
@@ -0,0 +1,475 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import {
|
||||
Argument,
|
||||
Bounds,
|
||||
Checkbox,
|
||||
Color,
|
||||
ColorPicker,
|
||||
ConditionalFormatting,
|
||||
Currency,
|
||||
Dimension,
|
||||
Int,
|
||||
isBoundsArg,
|
||||
isCheckboxArg,
|
||||
isColorArg,
|
||||
isColorPickerArg,
|
||||
isConditionalFormattingArg,
|
||||
isCurrencyArg,
|
||||
isDimensionArg,
|
||||
isIntArg,
|
||||
isMetricArg,
|
||||
isNumberFormatArg,
|
||||
isRadioButtonArg,
|
||||
isSelectArg,
|
||||
isSliderArg,
|
||||
isTemporalArg,
|
||||
isTextArg,
|
||||
isTimeFormatArg,
|
||||
Metric,
|
||||
NumberFormat,
|
||||
RadioButton,
|
||||
Select,
|
||||
Slider,
|
||||
Temporal,
|
||||
Text,
|
||||
TimeFormat,
|
||||
} from '@superset-ui/glyph-core';
|
||||
import { ColumnType } from '@superset-ui/glyph-core/types';
|
||||
|
||||
describe('Argument base class', () => {
|
||||
test('stores its constructor value', () => {
|
||||
const a = new Argument(42);
|
||||
expect(a.value).toBe(42);
|
||||
});
|
||||
|
||||
test('has expected static defaults', () => {
|
||||
expect(Argument.label).toBeNull();
|
||||
expect(Argument.description).toBeNull();
|
||||
expect(Argument.columnType).toBe(ColumnType.Argument);
|
||||
expect(Argument.controlType).toBe('TextControl');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Metric', () => {
|
||||
test('has expected static metadata', () => {
|
||||
expect(Metric.label).toBe('Metric');
|
||||
expect(Metric.columnType).toBe(ColumnType.Metric);
|
||||
expect(Metric.controlType).toBe('MetricsControl');
|
||||
expect(Metric.multi).toBe(false);
|
||||
});
|
||||
|
||||
test('.with() overrides label, description, multi', () => {
|
||||
const M = Metric.with({
|
||||
label: 'Sales',
|
||||
description: 'Total sales',
|
||||
multi: true,
|
||||
});
|
||||
expect(M.label).toBe('Sales');
|
||||
expect(M.description).toBe('Total sales');
|
||||
expect(M.multi).toBe(true);
|
||||
// unaltered ancestor metadata still present
|
||||
expect(M.columnType).toBe(ColumnType.Metric);
|
||||
expect(M.controlType).toBe('MetricsControl');
|
||||
});
|
||||
|
||||
test('.with() falls back to parent defaults when option omitted', () => {
|
||||
const M = Metric.with({ label: 'X' });
|
||||
expect(M.label).toBe('X');
|
||||
expect(M.multi).toBe(Metric.multi);
|
||||
expect(M.description).toBe(Metric.description);
|
||||
});
|
||||
|
||||
test('isMetricArg type guard', () => {
|
||||
expect(isMetricArg(Metric)).toBe(true);
|
||||
expect(isMetricArg(Metric.with({ label: 'X' }))).toBe(true);
|
||||
expect(isMetricArg(Dimension)).toBe(false);
|
||||
expect(isMetricArg(Select)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dimension', () => {
|
||||
test('has expected static metadata', () => {
|
||||
expect(Dimension.label).toBe('Dimension');
|
||||
expect(Dimension.columnType).toBe(ColumnType.Dimension);
|
||||
expect(Dimension.controlType).toBe('GroupByControl');
|
||||
expect(Dimension.multi).toBe(true);
|
||||
});
|
||||
|
||||
test('.with() overrides label, description, multi', () => {
|
||||
const D = Dimension.with({
|
||||
label: 'Region',
|
||||
multi: false,
|
||||
});
|
||||
expect(D.label).toBe('Region');
|
||||
expect(D.multi).toBe(false);
|
||||
});
|
||||
|
||||
test('isDimensionArg type guard', () => {
|
||||
expect(isDimensionArg(Dimension)).toBe(true);
|
||||
expect(isDimensionArg(Dimension.with({ label: 'X' }))).toBe(true);
|
||||
expect(isDimensionArg(Metric)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Temporal', () => {
|
||||
test('has expected static metadata', () => {
|
||||
expect(Temporal.label).toBe('Time Column');
|
||||
expect(Temporal.columnType).toBe(ColumnType.Temporal);
|
||||
expect(Temporal.controlType).toBe('TemporalControl');
|
||||
});
|
||||
|
||||
test('.with() overrides label and description', () => {
|
||||
const T = Temporal.with({ label: 'Order Date' });
|
||||
expect(T.label).toBe('Order Date');
|
||||
});
|
||||
|
||||
test('isTemporalArg type guard', () => {
|
||||
expect(isTemporalArg(Temporal)).toBe(true);
|
||||
expect(isTemporalArg(Metric)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Select', () => {
|
||||
const OPTIONS = [
|
||||
{ label: 'A', value: 'a' },
|
||||
{ label: 'B', value: 'b' },
|
||||
];
|
||||
|
||||
test('has expected static defaults', () => {
|
||||
expect(Select.label).toBe('Select');
|
||||
expect(Select.controlType).toBe('SelectControl');
|
||||
expect(Select.options).toEqual([]);
|
||||
expect(Select.default).toBe('');
|
||||
});
|
||||
|
||||
test('.with() applies label, default, options', () => {
|
||||
const S = Select.with({
|
||||
label: 'Choice',
|
||||
default: 'a',
|
||||
options: OPTIONS,
|
||||
});
|
||||
expect(S.label).toBe('Choice');
|
||||
expect(S.default).toBe('a');
|
||||
expect(S.options).toEqual(OPTIONS);
|
||||
});
|
||||
|
||||
test('isSelectArg type guard', () => {
|
||||
expect(isSelectArg(Select.with({ options: OPTIONS }))).toBe(true);
|
||||
expect(isSelectArg(Checkbox)).toBe(false);
|
||||
expect(isSelectArg(Metric)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Text', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Text.controlType).toBe('TextControl');
|
||||
expect(Text.default).toBe('');
|
||||
expect(Text.placeholder).toBe('');
|
||||
});
|
||||
|
||||
test('.with() applies label, default, placeholder', () => {
|
||||
const T = Text.with({
|
||||
label: 'Title',
|
||||
default: 'Untitled',
|
||||
placeholder: 'Enter title',
|
||||
});
|
||||
expect(T.label).toBe('Title');
|
||||
expect(T.default).toBe('Untitled');
|
||||
expect(T.placeholder).toBe('Enter title');
|
||||
});
|
||||
|
||||
test('isTextArg type guard accepts Text but not Select/Checkbox', () => {
|
||||
expect(isTextArg(Text)).toBe(true);
|
||||
expect(isTextArg(Text.with({ label: 'X' }))).toBe(true);
|
||||
expect(isTextArg(Checkbox)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Checkbox', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Checkbox.controlType).toBe('CheckboxControl');
|
||||
expect(Checkbox.default).toBe(false);
|
||||
});
|
||||
|
||||
test('.with() applies label, description, default', () => {
|
||||
const C = Checkbox.with({
|
||||
label: 'Show legend',
|
||||
default: true,
|
||||
});
|
||||
expect(C.label).toBe('Show legend');
|
||||
expect(C.default).toBe(true);
|
||||
});
|
||||
|
||||
test('isCheckboxArg type guard', () => {
|
||||
expect(isCheckboxArg(Checkbox)).toBe(true);
|
||||
expect(isCheckboxArg(Checkbox.with({ default: true }))).toBe(true);
|
||||
expect(isCheckboxArg(Text)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Int', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Int.controlType).toBe('SliderControl');
|
||||
expect(Int.default).toBe(0);
|
||||
expect(Int.min).toBe(0);
|
||||
expect(Int.max).toBe(100);
|
||||
expect(Int.step).toBe(1);
|
||||
});
|
||||
|
||||
test('.with() applies label, default, min, max, step', () => {
|
||||
const I = Int.with({
|
||||
label: 'Limit',
|
||||
default: 50,
|
||||
min: 10,
|
||||
max: 1000,
|
||||
step: 5,
|
||||
});
|
||||
expect(I.label).toBe('Limit');
|
||||
expect(I.default).toBe(50);
|
||||
expect(I.min).toBe(10);
|
||||
expect(I.max).toBe(1000);
|
||||
expect(I.step).toBe(5);
|
||||
});
|
||||
|
||||
test('isIntArg type guard', () => {
|
||||
expect(isIntArg(Int)).toBe(true);
|
||||
expect(isIntArg(Slider)).toBe(true); // Slider also has min/max
|
||||
expect(isIntArg(Checkbox)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Color', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Color.controlType).toBe('ColorPickerControl');
|
||||
expect(Color.default).toBe('#000000');
|
||||
});
|
||||
|
||||
test('.with() applies label, default', () => {
|
||||
const C = Color.with({ label: 'Fill', default: '#ff0000' });
|
||||
expect(C.label).toBe('Fill');
|
||||
expect(C.default).toBe('#ff0000');
|
||||
});
|
||||
|
||||
test('isColorArg type guard', () => {
|
||||
expect(isColorArg(Color)).toBe(true);
|
||||
expect(isColorArg(Color.with({ default: '#ff0000' }))).toBe(true);
|
||||
expect(isColorArg(Metric)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NumberFormat', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(NumberFormat.controlType).toBe('NumberFormatControl');
|
||||
expect(NumberFormat.default).toBe('SMART_NUMBER');
|
||||
expect(NumberFormat.FORMAT_OPTIONS.length).toBeGreaterThan(10);
|
||||
expect(
|
||||
NumberFormat.FORMAT_OPTIONS.some(o => o.value === 'SMART_NUMBER'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('.with() applies label, default', () => {
|
||||
const N = NumberFormat.with({ label: 'Amount', default: '.2f' });
|
||||
expect(N.label).toBe('Amount');
|
||||
expect(N.default).toBe('.2f');
|
||||
});
|
||||
|
||||
test('isNumberFormatArg type guard', () => {
|
||||
expect(isNumberFormatArg(NumberFormat)).toBe(true);
|
||||
expect(isNumberFormatArg(TimeFormat)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Currency', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Currency.controlType).toBe('CurrencyControl');
|
||||
expect(Currency.default).toEqual({});
|
||||
});
|
||||
|
||||
test('.with() applies label, default', () => {
|
||||
const C = Currency.with({
|
||||
label: 'Money',
|
||||
default: { symbol: 'USD', symbolPosition: 'prefix' },
|
||||
});
|
||||
expect(C.label).toBe('Money');
|
||||
expect(C.default).toEqual({ symbol: 'USD', symbolPosition: 'prefix' });
|
||||
});
|
||||
|
||||
test('isCurrencyArg type guard', () => {
|
||||
expect(isCurrencyArg(Currency)).toBe(true);
|
||||
expect(isCurrencyArg(NumberFormat)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TimeFormat', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(TimeFormat.controlType).toBe('TimeFormatControl');
|
||||
expect(TimeFormat.default).toBe('smart_date');
|
||||
expect(
|
||||
TimeFormat.FORMAT_OPTIONS.some(o => o.value === 'smart_date'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('.with() applies label, default', () => {
|
||||
const T = TimeFormat.with({ label: 'When', default: '%Y-%m-%d' });
|
||||
expect(T.label).toBe('When');
|
||||
expect(T.default).toBe('%Y-%m-%d');
|
||||
});
|
||||
|
||||
test('isTimeFormatArg type guard', () => {
|
||||
expect(isTimeFormatArg(TimeFormat)).toBe(true);
|
||||
expect(isTimeFormatArg(NumberFormat)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ConditionalFormatting', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(ConditionalFormatting.controlType).toBe(
|
||||
'ConditionalFormattingControl',
|
||||
);
|
||||
expect(ConditionalFormatting.default).toEqual([]);
|
||||
});
|
||||
|
||||
test('.with() applies label and description (not default)', () => {
|
||||
const CF = ConditionalFormatting.with({ label: 'Format' });
|
||||
expect(CF.label).toBe('Format');
|
||||
});
|
||||
|
||||
test('isConditionalFormattingArg type guard', () => {
|
||||
expect(isConditionalFormattingArg(ConditionalFormatting)).toBe(true);
|
||||
expect(isConditionalFormattingArg(Select)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Slider', () => {
|
||||
test('has expected float-friendly defaults', () => {
|
||||
expect(Slider.controlType).toBe('SliderControl');
|
||||
expect(Slider.default).toBe(0);
|
||||
expect(Slider.min).toBe(0);
|
||||
expect(Slider.max).toBe(1);
|
||||
expect(Slider.step).toBe(0.1);
|
||||
});
|
||||
|
||||
test('.with() applies all numeric fields', () => {
|
||||
const S = Slider.with({
|
||||
label: 'Opacity',
|
||||
default: 0.8,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
});
|
||||
expect(S.label).toBe('Opacity');
|
||||
expect(S.default).toBe(0.8);
|
||||
expect(S.step).toBe(0.05);
|
||||
});
|
||||
|
||||
test('isSliderArg type guard requires float step', () => {
|
||||
expect(isSliderArg(Slider)).toBe(true);
|
||||
// Int is also SliderControl + has step but step is integer-valued — still
|
||||
// numeric so the guard recognizes it (current behavior); document it.
|
||||
expect(isSliderArg(Int)).toBe(true);
|
||||
expect(isSliderArg(Checkbox)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bounds', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(Bounds.controlType).toBe('BoundsControl');
|
||||
expect(Bounds.default).toEqual([null, null]);
|
||||
});
|
||||
|
||||
test('.with() applies default', () => {
|
||||
const B = Bounds.with({ label: 'Range', default: [0, 100] });
|
||||
expect(B.label).toBe('Range');
|
||||
expect(B.default).toEqual([0, 100]);
|
||||
});
|
||||
|
||||
test('isBoundsArg type guard', () => {
|
||||
expect(isBoundsArg(Bounds)).toBe(true);
|
||||
expect(isBoundsArg(Int)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ColorPicker', () => {
|
||||
test('has expected static defaults', () => {
|
||||
expect(ColorPicker.controlType).toBe('ColorPickerControl');
|
||||
expect(ColorPicker.default).toEqual({ r: 0, g: 0, b: 0, a: 1 });
|
||||
});
|
||||
|
||||
test('.with() applies default', () => {
|
||||
const CP = ColorPicker.with({
|
||||
label: 'Pick',
|
||||
default: { r: 255, g: 0, b: 0, a: 0.5 },
|
||||
});
|
||||
expect(CP.label).toBe('Pick');
|
||||
expect(CP.default).toEqual({ r: 255, g: 0, b: 0, a: 0.5 });
|
||||
});
|
||||
|
||||
test('isColorPickerArg distinguishes from Color (string)', () => {
|
||||
expect(isColorPickerArg(ColorPicker)).toBe(true);
|
||||
expect(isColorPickerArg(Color)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RadioButton', () => {
|
||||
const RADIO_OPTIONS = [
|
||||
{ label: 'Yes', value: true },
|
||||
{ label: 'No', value: false },
|
||||
];
|
||||
|
||||
test('has expected static defaults', () => {
|
||||
expect(RadioButton.controlType).toBe('RadioButtonControl');
|
||||
expect(RadioButton.default).toBe('');
|
||||
expect(RadioButton.options).toEqual([]);
|
||||
});
|
||||
|
||||
test('.with() applies all fields', () => {
|
||||
const RB = RadioButton.with({
|
||||
label: 'Toggle',
|
||||
default: true,
|
||||
options: RADIO_OPTIONS,
|
||||
});
|
||||
expect(RB.label).toBe('Toggle');
|
||||
expect(RB.default).toBe(true);
|
||||
expect(RB.options).toEqual(RADIO_OPTIONS);
|
||||
});
|
||||
|
||||
test('isRadioButtonArg type guard', () => {
|
||||
expect(isRadioButtonArg(RadioButton)).toBe(true);
|
||||
expect(isRadioButtonArg(Select)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Argument inheritance via .with()', () => {
|
||||
test('chained .with() calls compose overrides', () => {
|
||||
const Base = Select.with({
|
||||
label: 'Pick one',
|
||||
options: [{ label: 'A', value: 'a' }],
|
||||
});
|
||||
const Tighter = Base.with({ label: 'Pick exactly one' });
|
||||
expect(Tighter.label).toBe('Pick exactly one');
|
||||
expect(Tighter.options).toEqual([{ label: 'A', value: 'a' }]);
|
||||
});
|
||||
|
||||
test('original class is unmodified after .with()', () => {
|
||||
const before = Metric.multi;
|
||||
Metric.with({ multi: !before });
|
||||
expect(Metric.multi).toBe(before);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import type { ChartProps, FilterState } from '@superset-ui/core';
|
||||
import {
|
||||
createLabelMap,
|
||||
createSelectedValuesMap,
|
||||
extractCrossFilterProps,
|
||||
isDataPointFiltered,
|
||||
} from '@superset-ui/glyph-core';
|
||||
|
||||
describe('createSelectedValuesMap', () => {
|
||||
test('returns empty object when filterState is undefined', () => {
|
||||
expect(createSelectedValuesMap(undefined, ['a', 'b'])).toEqual({});
|
||||
});
|
||||
|
||||
test('returns empty object when selectedValues is undefined', () => {
|
||||
expect(
|
||||
createSelectedValuesMap({} as FilterState, ['a', 'b']),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('returns empty object when selectedValues is empty', () => {
|
||||
expect(
|
||||
createSelectedValuesMap(
|
||||
{ selectedValues: [] } as unknown as FilterState,
|
||||
['a', 'b'],
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('maps selected value to its index in seriesNames', () => {
|
||||
const result = createSelectedValuesMap(
|
||||
{ selectedValues: ['b'] } as unknown as FilterState,
|
||||
['a', 'b', 'c'],
|
||||
);
|
||||
expect(result).toEqual({ 1: 'b' });
|
||||
});
|
||||
|
||||
test('maps multiple selected values to their indices', () => {
|
||||
const result = createSelectedValuesMap(
|
||||
{ selectedValues: ['a', 'c'] } as unknown as FilterState,
|
||||
['a', 'b', 'c'],
|
||||
);
|
||||
expect(result).toEqual({ 0: 'a', 2: 'c' });
|
||||
});
|
||||
|
||||
test('ignores selected values not in seriesNames', () => {
|
||||
const result = createSelectedValuesMap(
|
||||
{ selectedValues: ['x', 'a'] } as unknown as FilterState,
|
||||
['a', 'b', 'c'],
|
||||
);
|
||||
expect(result).toEqual({ 0: 'a' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDataPointFiltered', () => {
|
||||
test('returns false when no filterState', () => {
|
||||
expect(isDataPointFiltered(undefined, 'a')).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false when selectedValues is empty', () => {
|
||||
expect(
|
||||
isDataPointFiltered(
|
||||
{ selectedValues: [] } as unknown as FilterState,
|
||||
'a',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false when name is in selectedValues', () => {
|
||||
expect(
|
||||
isDataPointFiltered(
|
||||
{ selectedValues: ['a', 'b'] } as unknown as FilterState,
|
||||
'a',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true when name is NOT in non-empty selectedValues', () => {
|
||||
expect(
|
||||
isDataPointFiltered(
|
||||
{ selectedValues: ['a', 'b'] } as unknown as FilterState,
|
||||
'c',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createLabelMap', () => {
|
||||
test('returns empty object for empty data', () => {
|
||||
expect(createLabelMap([], ['col1'], () => 'label')).toEqual({});
|
||||
});
|
||||
|
||||
test('maps each record to its label and groupby column values', () => {
|
||||
const data = [
|
||||
{ country: 'USA', region: 'North' },
|
||||
{ country: 'Brazil', region: 'South' },
|
||||
];
|
||||
const result = createLabelMap(
|
||||
data,
|
||||
['country', 'region'],
|
||||
d => d.country as string,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
USA: ['USA', 'North'],
|
||||
Brazil: ['Brazil', 'South'],
|
||||
});
|
||||
});
|
||||
|
||||
test('last record wins when extractLabel collides', () => {
|
||||
const data = [
|
||||
{ name: 'X', value: 1 },
|
||||
{ name: 'X', value: 2 },
|
||||
];
|
||||
const result = createLabelMap(data, ['value'], d => d.name as string);
|
||||
// collision: later entry overwrites
|
||||
expect(result).toEqual({ X: [2] });
|
||||
});
|
||||
|
||||
test('groupbyLabels controls the columns extracted, not the label', () => {
|
||||
const data = [{ a: 1, b: 2, c: 3 }];
|
||||
const result = createLabelMap(data, ['c'], () => 'only-key');
|
||||
expect(result).toEqual({ 'only-key': [3] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractCrossFilterProps', () => {
|
||||
const baseChartProps = {
|
||||
hooks: { setDataMask: jest.fn(), onContextMenu: jest.fn() },
|
||||
filterState: {
|
||||
selectedValues: ['USA'],
|
||||
} as unknown as FilterState,
|
||||
emitCrossFilters: true,
|
||||
formData: { viz_type: 'test' },
|
||||
} as unknown as ChartProps;
|
||||
|
||||
test('returns all expected fields', () => {
|
||||
const result = extractCrossFilterProps(
|
||||
baseChartProps,
|
||||
['country'],
|
||||
{ USA: ['USA'] },
|
||||
['USA', 'Brazil'],
|
||||
);
|
||||
expect(result.groupby).toEqual(['country']);
|
||||
expect(result.labelMap).toEqual({ USA: ['USA'] });
|
||||
expect(result.selectedValues).toEqual({ 0: 'USA' });
|
||||
expect(result.emitCrossFilters).toBe(true);
|
||||
expect(result.setDataMask).toBe(baseChartProps.hooks!.setDataMask);
|
||||
expect(result.onContextMenu).toBe(baseChartProps.hooks!.onContextMenu);
|
||||
});
|
||||
|
||||
test('coltypeMapping pass-through when provided', () => {
|
||||
const result = extractCrossFilterProps(
|
||||
baseChartProps,
|
||||
['country'],
|
||||
{},
|
||||
[],
|
||||
{ country: 1 },
|
||||
);
|
||||
expect(result.coltypeMapping).toEqual({ country: 1 });
|
||||
});
|
||||
|
||||
test('defaults setDataMask to a no-op when hooks omits it', () => {
|
||||
const chartProps = {
|
||||
...baseChartProps,
|
||||
hooks: {},
|
||||
} as unknown as ChartProps;
|
||||
const result = extractCrossFilterProps(chartProps, [], {}, []);
|
||||
expect(typeof result.setDataMask).toBe('function');
|
||||
// No throw when invoked
|
||||
expect(() =>
|
||||
result.setDataMask({ filterState: {} } as unknown as Parameters<
|
||||
typeof result.setDataMask
|
||||
>[0]),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
test('formData is included in the returned shape (for context menu formatting)', () => {
|
||||
const result = extractCrossFilterProps(
|
||||
baseChartProps,
|
||||
['country'],
|
||||
{},
|
||||
[],
|
||||
) as ReturnType<typeof extractCrossFilterProps> & { formData: unknown };
|
||||
expect(result.formData).toEqual({ viz_type: 'test' });
|
||||
});
|
||||
|
||||
test('selectedValues is empty when filterState has none', () => {
|
||||
const chartProps = {
|
||||
...baseChartProps,
|
||||
filterState: {} as FilterState,
|
||||
} as unknown as ChartProps;
|
||||
const result = extractCrossFilterProps(chartProps, [], {}, ['x']);
|
||||
expect(result.selectedValues).toEqual({});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user