mirror of
https://github.com/apache/superset.git
synced 2026-05-02 14:34:22 +00:00
Compare commits
39 Commits
npm-upgrad
...
docs/testi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d2c332165 | ||
|
|
572f3392d7 | ||
|
|
90f281f585 | ||
|
|
d62249d13f | ||
|
|
ff102aadb3 | ||
|
|
82e2bc6181 | ||
|
|
784ff82847 | ||
|
|
027b25e6b8 | ||
|
|
b652fab042 | ||
|
|
77a5969dc1 | ||
|
|
fb9032c05c | ||
|
|
7a9dbfe879 | ||
|
|
0de78d8203 | ||
|
|
abc2d46fed | ||
|
|
927cc1cda1 | ||
|
|
7f3840557a | ||
|
|
0defcb604b | ||
|
|
94686ddfbe | ||
|
|
ec322dfd8d | ||
|
|
cb88d886c7 | ||
|
|
608e3baf43 | ||
|
|
b6f6b75348 | ||
|
|
a5ad1d186c | ||
|
|
db88d80b3f | ||
|
|
4b71adaa9c | ||
|
|
5fbda3af40 | ||
|
|
bc0c40c80e | ||
|
|
f030d658c5 | ||
|
|
e85337c543 | ||
|
|
fe7f8062f3 | ||
|
|
dce74014da | ||
|
|
619b341cad | ||
|
|
9b6876be62 | ||
|
|
c601341520 | ||
|
|
78faaee685 | ||
|
|
4027bad1d6 | ||
|
|
ce55cc7dd7 | ||
|
|
48e1b1ff2c | ||
|
|
5ec8f9d886 |
69
.github/actions/setup-frontend/action.yml
vendored
69
.github/actions/setup-frontend/action.yml
vendored
@@ -1,69 +0,0 @@
|
||||
name: 'Setup Frontend Environment'
|
||||
description: 'Set up Node.js v20, npm v11, and install frontend dependencies. Uses Node v20 due to Docker memory constraints in GitHub Actions with v22.'
|
||||
inputs:
|
||||
node-version:
|
||||
description: 'Node.js version to set up. Defaults to reading from .nvmrc file.'
|
||||
required: false
|
||||
default: ''
|
||||
npm-version:
|
||||
description: 'npm version to install. Defaults to 10.8.1'
|
||||
required: false
|
||||
default: '10.8.1'
|
||||
install-dependencies:
|
||||
description: 'Whether to install frontend dependencies with npm ci'
|
||||
required: false
|
||||
default: 'true'
|
||||
build-assets:
|
||||
description: 'Build static assets after installing dependencies'
|
||||
required: false
|
||||
default: 'false'
|
||||
build-instrumented:
|
||||
description: 'Build instrumented assets for test coverage'
|
||||
required: false
|
||||
default: 'false'
|
||||
install-cypress:
|
||||
description: 'Install Cypress dependencies in cypress-base directory'
|
||||
required: false
|
||||
default: 'false'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup Node.js with npm caching
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ${{ inputs.node-version != '' && inputs.node-version || './superset-frontend/.nvmrc' }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: './superset-frontend/package-lock.json'
|
||||
|
||||
- name: Upgrade npm to v11
|
||||
shell: bash
|
||||
run: npm install -g npm@${{ inputs.npm-version }}
|
||||
|
||||
- name: Install Frontend Dependencies
|
||||
if: inputs.install-dependencies == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd superset-frontend
|
||||
npm ci
|
||||
|
||||
- name: Build Static Assets
|
||||
if: inputs.build-assets == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd superset-frontend
|
||||
npm run build
|
||||
|
||||
- name: Build Instrumented Assets
|
||||
if: inputs.build-instrumented == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd superset-frontend
|
||||
npm run build-instrumented
|
||||
|
||||
- name: Install Cypress Dependencies
|
||||
if: inputs.install-cypress == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd superset-frontend/cypress-base
|
||||
npm ci
|
||||
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -1 +1 @@
|
||||
../LLMS.md
|
||||
../AGENTS.md
|
||||
54
.github/workflows/bashlib.sh
vendored
54
.github/workflows/bashlib.sh
vendored
@@ -31,6 +31,48 @@ say() {
|
||||
fi
|
||||
}
|
||||
|
||||
pip-upgrade() {
|
||||
say "::group::Upgrade pip"
|
||||
pip install --upgrade pip
|
||||
say "::endgroup::"
|
||||
}
|
||||
|
||||
# prepare (lint and build) frontend code
|
||||
npm-install() {
|
||||
cd "$GITHUB_WORKSPACE/superset-frontend"
|
||||
|
||||
# cache-restore npm
|
||||
say "::group::Install npm packages"
|
||||
echo "npm: $(npm --version)"
|
||||
echo "node: $(node --version)"
|
||||
npm ci
|
||||
say "::endgroup::"
|
||||
|
||||
# cache-save npm
|
||||
}
|
||||
|
||||
build-assets() {
|
||||
cd "$GITHUB_WORKSPACE/superset-frontend"
|
||||
|
||||
say "::group::Build static assets"
|
||||
npm run build
|
||||
say "::endgroup::"
|
||||
}
|
||||
|
||||
build-instrumented-assets() {
|
||||
cd "$GITHUB_WORKSPACE/superset-frontend"
|
||||
|
||||
say "::group::Build static assets with JS instrumented for test coverage"
|
||||
cache-restore instrumented-assets
|
||||
if [[ -f "$ASSETS_MANIFEST" ]]; then
|
||||
echo 'Skip frontend build because instrumented static assets already exist.'
|
||||
else
|
||||
npm run build-instrumented
|
||||
cache-save instrumented-assets
|
||||
fi
|
||||
say "::endgroup::"
|
||||
}
|
||||
|
||||
setup-postgres() {
|
||||
say "::group::Install dependency for unit tests"
|
||||
sudo apt-get update && sudo apt-get install --yes libecpg-dev
|
||||
@@ -89,6 +131,18 @@ celery-worker() {
|
||||
say "::endgroup::"
|
||||
}
|
||||
|
||||
cypress-install() {
|
||||
cd "$GITHUB_WORKSPACE/superset-frontend/cypress-base"
|
||||
|
||||
cache-restore cypress
|
||||
|
||||
say "::group::Install Cypress"
|
||||
npm ci
|
||||
say "::endgroup::"
|
||||
|
||||
cache-save cypress
|
||||
}
|
||||
|
||||
cypress-run-all() {
|
||||
local USE_DASHBOARD=$1
|
||||
local APP_ROOT=$2
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Frontend Environment
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
install-dependencies: 'false'
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install -g @action-validator/core @action-validator/cli --save-dev
|
||||
|
||||
11
.github/workflows/pre-commit.yml
vendored
11
.github/workflows/pre-commit.yml
vendored
@@ -38,8 +38,15 @@ jobs:
|
||||
echo "HOMEBREW_CELLAR=$HOMEBREW_CELLAR" >>"${GITHUB_ENV}"
|
||||
echo "HOMEBREW_REPOSITORY=$HOMEBREW_REPOSITORY" >>"${GITHUB_ENV}"
|
||||
brew install norwoodj/tap/helm-docs
|
||||
- name: Setup Frontend Environment
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Frontend Dependencies
|
||||
run: |
|
||||
cd superset-frontend
|
||||
npm ci
|
||||
|
||||
- name: Install Docs Dependencies
|
||||
run: |
|
||||
|
||||
35
.github/workflows/release.yml
vendored
35
.github/workflows/release.yml
vendored
@@ -40,9 +40,40 @@ jobs:
|
||||
git fetch --prune --unshallow
|
||||
git tag -d `git tag | grep -E '^trigger-'`
|
||||
|
||||
- name: Setup Frontend Environment
|
||||
- name: Install Node.js
|
||||
if: env.HAS_TAGS
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
|
||||
- name: Cache npm
|
||||
if: env.HAS_TAGS
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-node-
|
||||
${{ runner.OS }}-
|
||||
|
||||
- name: Get npm cache directory path
|
||||
if: env.HAS_TAGS
|
||||
id: npm-cache-dir-path
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
- name: Cache npm
|
||||
if: env.HAS_TAGS
|
||||
uses: actions/cache@v4
|
||||
id: npm-cache # use this to check for `cache-hit` (`steps.npm-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: env.HAS_TAGS
|
||||
working-directory: ./superset-frontend
|
||||
run: npm ci
|
||||
- name: Run unit tests
|
||||
if: env.HAS_TAGS
|
||||
working-directory: ./superset-frontend
|
||||
|
||||
59
.github/workflows/showtime-trigger.yml
vendored
59
.github/workflows/showtime-trigger.yml
vendored
@@ -61,26 +61,11 @@ jobs:
|
||||
console.log(`📊 Permission level for ${actor}: ${permission.permission}`);
|
||||
const authorized = ['write', 'admin'].includes(permission.permission);
|
||||
|
||||
// Handle synchronize events
|
||||
if (context.eventName === 'pull_request_target' && context.payload.action === 'synchronize') {
|
||||
if (!authorized) {
|
||||
console.log(`🚨 Unauthorized user ${actor} pushed code - setting blocked label and bailing`);
|
||||
// If this is a synchronize event from unauthorized user, check if Showtime is active and set blocked label
|
||||
if (!authorized && context.eventName === 'pull_request_target' && context.payload.action === 'synchronize') {
|
||||
console.log(`🔒 Synchronize event detected - checking if Showtime is active`);
|
||||
|
||||
// Set blocked label for security
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
labels: ['🎪 🔒 showtime-blocked']
|
||||
});
|
||||
|
||||
core.setOutput('authorized', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`✅ Authorized maintainer ${actor} - checking if Showtime is active`);
|
||||
|
||||
// Check if PR has any circus tent labels (Showtime is active)
|
||||
// Check if PR has any circus tent labels (Showtime is in use)
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -90,24 +75,30 @@ jobs:
|
||||
const hasCircusLabels = issue.labels.some(label => label.name.startsWith('🎪 '));
|
||||
|
||||
if (hasCircusLabels) {
|
||||
console.log(`🎪 Circus labels found - Showtime is active, proceeding with workflow`);
|
||||
core.setOutput('authorized', 'true');
|
||||
} else {
|
||||
console.log(`ℹ️ No circus labels found - Showtime not active, skipping workflow`);
|
||||
core.setOutput('authorized', 'false');
|
||||
}
|
||||
} else {
|
||||
// Non-synchronize events - check authorization normally
|
||||
if (!authorized) {
|
||||
console.log(`🚨 Unauthorized user ${actor} - skipping all operations`);
|
||||
core.setOutput('authorized', 'false');
|
||||
return;
|
||||
}
|
||||
console.log(`🎪 Circus labels found - setting blocked label to prevent auto-deployment`);
|
||||
|
||||
console.log(`✅ Authorized maintainer: ${actor}`);
|
||||
core.setOutput('authorized', 'true');
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
labels: ['🎪 🔒 showtime-blocked']
|
||||
});
|
||||
|
||||
console.log(`✅ Blocked label set - Showtime will detect and skip operations`);
|
||||
} else {
|
||||
console.log(`ℹ️ No circus labels found - Showtime not in use, skipping block`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!authorized) {
|
||||
console.log(`🚨 Unauthorized user ${actor} - skipping all operations`);
|
||||
core.setOutput('authorized', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`✅ Authorized maintainer: ${actor}`);
|
||||
core.setOutput('authorized', 'true');
|
||||
|
||||
- name: Install Superset Showtime
|
||||
if: steps.auth.outputs.authorized == 'true'
|
||||
run: |
|
||||
|
||||
15
.github/workflows/superset-applitool-cypress.yml
vendored
15
.github/workflows/superset-applitool-cypress.yml
vendored
@@ -66,16 +66,23 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
- name: Setup Frontend Environment with builds and Cypress
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
- name: Install npm dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
build-instrumented: 'true'
|
||||
install-cypress: 'true'
|
||||
run: npm-install
|
||||
- name: Build javascript packages
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: build-instrumented-assets
|
||||
- name: Setup Postgres
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: setup-postgres
|
||||
- name: Install cypress
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: cypress-install
|
||||
- name: Run Cypress
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
env:
|
||||
|
||||
@@ -43,8 +43,10 @@ jobs:
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: eyes-storybook-dependencies
|
||||
- name: Setup Frontend Environment
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
- name: Install NPM dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: npm-install
|
||||
- name: Run Applitools Eyes-Storybook
|
||||
working-directory: ./superset-frontend
|
||||
run: npx eyes-storybook -u https://superset-storybook.netlify.app/
|
||||
|
||||
17
.github/workflows/superset-e2e.yml
vendored
17
.github/workflows/superset-e2e.yml
vendored
@@ -112,12 +112,21 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
- name: Setup Frontend Environment with builds
|
||||
- name: Install npm dependencies
|
||||
if: steps.check.outputs.python || steps.check.outputs.frontend
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
build-instrumented: 'true'
|
||||
install-cypress: 'true'
|
||||
run: npm-install
|
||||
- name: Build javascript packages
|
||||
if: steps.check.outputs.python || steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: build-instrumented-assets
|
||||
- name: Install cypress
|
||||
if: steps.check.outputs.python || steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: cypress-install
|
||||
- name: Run Cypress
|
||||
if: steps.check.outputs.python || steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
|
||||
2
.github/workflows/superset-frontend.yml
vendored
2
.github/workflows/superset-frontend.yml
vendored
@@ -138,7 +138,7 @@ jobs:
|
||||
- name: eslint
|
||||
run: |
|
||||
docker run --rm $TAG bash -c \
|
||||
"npm ci && npm rebuild && npm run eslint -- . --quiet"
|
||||
"npm i && npm run eslint -- . --quiet"
|
||||
|
||||
- name: tsc
|
||||
run: |
|
||||
|
||||
11
.github/workflows/superset-translations.yml
vendored
11
.github/workflows/superset-translations.yml
vendored
@@ -29,9 +29,16 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Frontend Environment
|
||||
- name: Setup Node.js
|
||||
if: steps.check.outputs.frontend
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
- name: Install dependencies
|
||||
if: steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: npm-install
|
||||
- name: lint
|
||||
if: steps.check.outputs.frontend
|
||||
working-directory: ./superset-frontend
|
||||
|
||||
10
.github/workflows/tag-release.yml
vendored
10
.github/workflows/tag-release.yml
vendored
@@ -59,6 +59,11 @@ jobs:
|
||||
install-docker-compose: "false"
|
||||
build: "true"
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup supersetbot
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
|
||||
@@ -106,6 +111,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup supersetbot
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
|
||||
|
||||
10
.github/workflows/tech-debt.yml
vendored
10
.github/workflows/tech-debt.yml
vendored
@@ -29,8 +29,14 @@ jobs:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Frontend Environment
|
||||
uses: ./.github/actions/setup-frontend/
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
working-directory: ./superset-frontend
|
||||
|
||||
- name: Run Script
|
||||
env:
|
||||
|
||||
@@ -82,8 +82,8 @@ intro_header.txt
|
||||
|
||||
# for LLMs
|
||||
llm-context.md
|
||||
LLMS.md
|
||||
AGENTS.md
|
||||
LLMS.md
|
||||
CLAUDE.md
|
||||
CURSOR.md
|
||||
GEMINI.md
|
||||
|
||||
235
AGENTS.md
Normal file
235
AGENTS.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# LLM Context Guide for Apache Superset
|
||||
|
||||
Apache Superset is a data visualization platform with Flask/Python backend and React/TypeScript frontend.
|
||||
|
||||
## ⚠️ CRITICAL: Ongoing Refactors (What NOT to Do)
|
||||
|
||||
**These migrations are actively happening - avoid deprecated patterns:**
|
||||
|
||||
### Frontend Modernization
|
||||
- **NO `any` types** - Use proper TypeScript types
|
||||
- **NO JavaScript files** - Convert to TypeScript (.ts/.tsx)
|
||||
- **Use @superset-ui/core** - Don't import Ant Design directly, prefer Ant Design component wrappers from @superset-ui/core/components
|
||||
- **Use antd theming tokens** - Prefer antd tokens over legacy theming tokens
|
||||
- **Avoid custom css and styles** - Follow antd best practices and avoid styling and custom CSS whenever possible
|
||||
|
||||
### Testing Strategy Migration
|
||||
- **Prefer unit tests** over integration tests
|
||||
- **Prefer integration tests** over end-to-end tests
|
||||
- **Use Playwright for E2E tests** - Migrating from Cypress
|
||||
- **Cypress is deprecated** - Will be removed once migration is completed
|
||||
- **Use Jest + React Testing Library** for component testing
|
||||
- **Use `test()` instead of `describe()`** - Follow [avoid nesting when testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) principles
|
||||
|
||||
### Backend Type Safety
|
||||
- **Add type hints** - All new Python code needs proper typing
|
||||
- **MyPy compliance** - Run `pre-commit run mypy` to validate
|
||||
- **SQLAlchemy typing** - Use proper model annotations
|
||||
|
||||
### UUID Migration
|
||||
- **Prefer UUIDs over auto-incrementing IDs** - New models should use UUID primary keys
|
||||
- **External API exposure** - Use UUIDs in public APIs instead of internal integer IDs
|
||||
- **Existing models** - Add UUID fields alongside integer IDs for gradual migration
|
||||
|
||||
## Key Directories
|
||||
|
||||
```
|
||||
superset/
|
||||
├── superset/ # Python backend (Flask, SQLAlchemy)
|
||||
│ ├── views/api/ # REST API endpoints
|
||||
│ ├── models/ # Database models
|
||||
│ └── connectors/ # Database connections
|
||||
├── superset-frontend/src/ # React TypeScript frontend
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── explore/ # Chart builder
|
||||
│ ├── dashboard/ # Dashboard interface
|
||||
│ └── SqlLab/ # SQL editor
|
||||
├── superset-frontend/packages/
|
||||
│ └── superset-ui-core/ # UI component library (USE THIS)
|
||||
├── tests/ # Python/integration tests
|
||||
├── docs/ # Documentation (UPDATE FOR CHANGES)
|
||||
└── UPDATING.md # Breaking changes log
|
||||
```
|
||||
|
||||
## Code Standards
|
||||
|
||||
### TypeScript Frontend
|
||||
- **Avoid `any` types** - Use proper TypeScript, reuse existing types
|
||||
- **Functional components** with hooks
|
||||
- **@superset-ui/core** for UI components (not direct antd)
|
||||
- **Jest** for testing (NO Enzyme)
|
||||
- **Redux** for global state where it exists, hooks for local
|
||||
|
||||
### Python Backend
|
||||
- **Type hints required** for all new code
|
||||
- **MyPy compliant** - run `pre-commit run mypy`
|
||||
- **SQLAlchemy models** with proper typing
|
||||
- **pytest** for testing
|
||||
|
||||
### Apache License Headers
|
||||
- **New files require ASF license headers** - When creating new code files, include the standard Apache Software Foundation license header
|
||||
- **LLM instruction files are excluded** - Files like AGENTS.md, CLAUDE.md, etc. are in `.rat-excludes` to avoid header token overhead
|
||||
|
||||
### Code Comments
|
||||
- **Avoid time-specific language** - Don't use words like "now", "currently", "today" in code comments as they become outdated
|
||||
- **Write timeless comments** - Comments should remain accurate regardless of when they're read
|
||||
|
||||
## Documentation Requirements
|
||||
|
||||
- **docs/**: Update for any user-facing changes
|
||||
- **UPDATING.md**: Add breaking changes here
|
||||
- **Docstrings**: Required for new functions/classes
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Security & Features
|
||||
- **RBAC**: Role-based access via Flask-AppBuilder
|
||||
- **Feature flags**: Control feature rollouts
|
||||
- **Row-level security**: SQL-based data access control
|
||||
|
||||
## Test Utilities
|
||||
|
||||
### Python Test Helpers
|
||||
- **`SupersetTestCase`** - Base class in `tests/integration_tests/base_tests.py`
|
||||
- **`@with_config`** - Config mocking decorator
|
||||
- **`@with_feature_flags`** - Feature flag testing
|
||||
- **`login_as()`, `login_as_admin()`** - Authentication helpers
|
||||
- **`create_dashboard()`, `create_slice()`** - Data setup utilities
|
||||
|
||||
### TypeScript Test Helpers
|
||||
- **`superset-frontend/spec/helpers/testing-library.tsx`** - Custom render() with providers
|
||||
- **`createWrapper()`** - Redux/Router/Theme wrapper
|
||||
- **`selectOption()`** - Select component helper
|
||||
- **React Testing Library** - NO Enzyme (removed)
|
||||
|
||||
### Test Structure Guidelines
|
||||
- **Use `test()` instead of `describe()` and `it()`** - Follow the [avoid nesting when testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) principle
|
||||
- **Why**: Reduces unnecessary nesting, improves test isolation, and makes tests more readable
|
||||
- **Pattern**: Write flat test files with descriptive test names that fully describe what's being tested
|
||||
- **Example**: Instead of nested `describe('Component', () => { it('should render', ...) })`, use `test('Component renders correctly', ...)`
|
||||
- **Benefits**:
|
||||
- Each test stands alone with a clear, searchable name
|
||||
- Easier to run individual tests
|
||||
- Forces you to write more descriptive test names
|
||||
- Reduces cognitive overhead from nested context switching
|
||||
|
||||
### Test Database Patterns
|
||||
- **Mock patterns**: Use `MagicMock()` for config objects, avoid `AsyncMock` for synchronous code
|
||||
- **API tests**: Update expected columns when adding new model fields
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Frontend
|
||||
npm run test # All tests
|
||||
npm run test -- filename.test.tsx # Single file
|
||||
|
||||
# E2E Tests (Playwright - NEW)
|
||||
npm run playwright:test # All Playwright tests
|
||||
npm run playwright:ui # Interactive UI mode
|
||||
npm run playwright:headed # See browser during tests
|
||||
npx playwright test tests/auth/login.spec.ts # Single file
|
||||
npm run playwright:debug tests/auth/login.spec.ts # Debug specific file
|
||||
|
||||
# E2E Tests (Cypress - DEPRECATED)
|
||||
cd superset-frontend/cypress-base
|
||||
npm run cypress-run-chrome # All Cypress tests (headless)
|
||||
npm run cypress-debug # Interactive Cypress UI
|
||||
|
||||
# Backend
|
||||
pytest # All tests
|
||||
pytest tests/unit_tests/specific_test.py # Single file
|
||||
pytest tests/unit_tests/ # Directory
|
||||
|
||||
# If pytest fails with database/setup issues, ask the user to run test environment setup
|
||||
```
|
||||
|
||||
## Environment Validation
|
||||
|
||||
**Quick Setup Check (run this first):**
|
||||
|
||||
```bash
|
||||
# Verify Superset is running
|
||||
curl -f http://localhost:8088/health || echo "❌ Setup required - see https://superset.apache.org/docs/contributing/development#working-with-llms"
|
||||
```
|
||||
|
||||
**If health checks fail:**
|
||||
"It appears you aren't set up properly. Please refer to the [Working with LLMs](https://superset.apache.org/docs/contributing/development#working-with-llms) section in the development docs for setup instructions."
|
||||
|
||||
**Key Project Files:**
|
||||
- `superset-frontend/package.json` - Frontend build scripts (`npm run dev` on port 9000, `npm run test`, `npm run lint`)
|
||||
- `pyproject.toml` - Python tooling (ruff, mypy configs)
|
||||
- `requirements/` folder - Python dependencies (base.txt, development.txt)
|
||||
|
||||
## SQLAlchemy Query Best Practices
|
||||
- **Use negation operator**: `~Model.field` instead of `== False` to avoid ruff E712 errors
|
||||
- **Example**: `~Model.is_active` instead of `Model.is_active == False`
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
**When creating pull requests:**
|
||||
|
||||
1. **Read the current PR template**: Always check `.github/PULL_REQUEST_TEMPLATE.md` for the latest format
|
||||
2. **Use the template sections**: Include all sections from the template (SUMMARY, BEFORE/AFTER, TESTING INSTRUCTIONS, ADDITIONAL INFORMATION)
|
||||
3. **Follow PR title conventions**: Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- Format: `type(scope): description`
|
||||
- Example: `fix(dashboard): load charts correctly`
|
||||
- Types: `fix`, `feat`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
|
||||
**Important**: Always reference the actual template file at `.github/PULL_REQUEST_TEMPLATE.md` instead of using cached content, as the template may be updated over time.
|
||||
|
||||
## Pre-commit Validation
|
||||
|
||||
**Use pre-commit hooks for quality validation:**
|
||||
|
||||
```bash
|
||||
# Install hooks
|
||||
pre-commit install
|
||||
|
||||
# IMPORTANT: Stage your changes first!
|
||||
git add . # Pre-commit only checks staged files
|
||||
|
||||
# Quick validation (faster than --all-files)
|
||||
pre-commit run # Staged files only
|
||||
pre-commit run mypy # Python type checking
|
||||
pre-commit run prettier # Code formatting
|
||||
pre-commit run eslint # Frontend linting
|
||||
```
|
||||
|
||||
**Important pre-commit usage notes:**
|
||||
- **Stage files first**: Run `git add .` before `pre-commit run` to check only changed files (much faster)
|
||||
- **Virtual environment**: Activate your Python virtual environment before running pre-commit
|
||||
```bash
|
||||
# Common virtual environment locations (yours may differ):
|
||||
source .venv/bin/activate # if using .venv
|
||||
source venv/bin/activate # if using venv
|
||||
source ~/venvs/superset/bin/activate # if using a central location
|
||||
```
|
||||
If you get a "command not found" error, ask the user which virtual environment to activate
|
||||
- **Auto-fixes**: Some hooks auto-fix issues (e.g., trailing whitespace). Re-run after fixes are applied
|
||||
|
||||
## Common File Patterns
|
||||
|
||||
### API Structure
|
||||
- **`/api.py`** - REST endpoints with decorators and OpenAPI docstrings
|
||||
- **`/schemas.py`** - Marshmallow validation schemas for OpenAPI spec
|
||||
- **`/commands/`** - Business logic classes with @transaction() decorators
|
||||
- **`/models/`** - SQLAlchemy database models
|
||||
- **OpenAPI docs**: Auto-generated at `/swagger/v1` from docstrings and schemas
|
||||
|
||||
### Migration Files
|
||||
- **Location**: `superset/migrations/versions/`
|
||||
- **Naming**: `YYYY-MM-DD_HH-MM_hash_description.py`
|
||||
- **Utilities**: Use helpers from `superset.migrations.shared.utils` for database compatibility
|
||||
- **Pattern**: Import utilities instead of raw SQLAlchemy operations
|
||||
|
||||
## Platform-Specific Instructions
|
||||
|
||||
- **[CLAUDE.md](CLAUDE.md)** - For Claude/Anthropic tools
|
||||
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - For GitHub Copilot
|
||||
- **[GEMINI.md](GEMINI.md)** - For Google Gemini tools
|
||||
- **[GPT.md](GPT.md)** - For OpenAI/ChatGPT tools
|
||||
- **[.cursor/rules/dev-standard.mdc](.cursor/rules/dev-standard.mdc)** - For Cursor editor
|
||||
|
||||
---
|
||||
|
||||
**LLM Note**: This codebase is actively modernizing toward full TypeScript and type safety. Always run `pre-commit run` to validate changes. Follow the ongoing refactors section to avoid deprecated patterns.
|
||||
220
LLMS.md
220
LLMS.md
@@ -1,220 +0,0 @@
|
||||
# LLM Context Guide for Apache Superset
|
||||
|
||||
Apache Superset is a data visualization platform with Flask/Python backend and React/TypeScript frontend.
|
||||
|
||||
## ⚠️ CRITICAL: Ongoing Refactors (What NOT to Do)
|
||||
|
||||
**These migrations are actively happening - avoid deprecated patterns:**
|
||||
|
||||
### Frontend Modernization
|
||||
- **NO `any` types** - Use proper TypeScript types
|
||||
- **NO JavaScript files** - Convert to TypeScript (.ts/.tsx)
|
||||
- **Use @superset-ui/core** - Don't import Ant Design directly, prefer Ant Design component wrappers from @superset-ui/core/components
|
||||
- **Use antd theming tokens** - Prefer antd tokens over legacy theming tokens
|
||||
- **Avoid custom css and styles** - Follow antd best practices and avoid styling and custom CSS whenever possible
|
||||
|
||||
### Testing Strategy Migration
|
||||
- **Prefer unit tests** over integration tests
|
||||
- **Prefer integration tests** over end-to-end tests
|
||||
- **Use Playwright for E2E tests** - Migrating from Cypress
|
||||
- **Cypress is deprecated** - Will be removed once migration is completed
|
||||
- **Use Jest + React Testing Library** for component testing
|
||||
- **Use `test()` instead of `describe()`** - Follow [avoid nesting when testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) principles
|
||||
|
||||
### Backend Type Safety
|
||||
- **Add type hints** - All new Python code needs proper typing
|
||||
- **MyPy compliance** - Run `pre-commit run mypy` to validate
|
||||
- **SQLAlchemy typing** - Use proper model annotations
|
||||
|
||||
### UUID Migration
|
||||
- **Prefer UUIDs over auto-incrementing IDs** - New models should use UUID primary keys
|
||||
- **External API exposure** - Use UUIDs in public APIs instead of internal integer IDs
|
||||
- **Existing models** - Add UUID fields alongside integer IDs for gradual migration
|
||||
|
||||
## Key Directories
|
||||
|
||||
```
|
||||
superset/
|
||||
├── superset/ # Python backend (Flask, SQLAlchemy)
|
||||
│ ├── views/api/ # REST API endpoints
|
||||
│ ├── models/ # Database models
|
||||
│ └── connectors/ # Database connections
|
||||
├── superset-frontend/src/ # React TypeScript frontend
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── explore/ # Chart builder
|
||||
│ ├── dashboard/ # Dashboard interface
|
||||
│ └── SqlLab/ # SQL editor
|
||||
├── superset-frontend/packages/
|
||||
│ └── superset-ui-core/ # UI component library (USE THIS)
|
||||
├── tests/ # Python/integration tests
|
||||
├── docs/ # Documentation (UPDATE FOR CHANGES)
|
||||
└── UPDATING.md # Breaking changes log
|
||||
```
|
||||
|
||||
## Code Standards
|
||||
|
||||
### TypeScript Frontend
|
||||
- **Avoid `any` types** - Use proper TypeScript, reuse existing types
|
||||
- **Functional components** with hooks
|
||||
- **@superset-ui/core** for UI components (not direct antd)
|
||||
- **Jest** for testing (NO Enzyme)
|
||||
- **Redux** for global state where it exists, hooks for local
|
||||
|
||||
### Python Backend
|
||||
- **Type hints required** for all new code
|
||||
- **MyPy compliant** - run `pre-commit run mypy`
|
||||
- **SQLAlchemy models** with proper typing
|
||||
- **pytest** for testing
|
||||
|
||||
### Apache License Headers
|
||||
- **New files require ASF license headers** - When creating new code files, include the standard Apache Software Foundation license header
|
||||
- **LLM instruction files are excluded** - Files like LLMS.md, CLAUDE.md, etc. are in `.rat-excludes` to avoid header token overhead
|
||||
|
||||
## Documentation Requirements
|
||||
|
||||
- **docs/**: Update for any user-facing changes
|
||||
- **UPDATING.md**: Add breaking changes here
|
||||
- **Docstrings**: Required for new functions/classes
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Security & Features
|
||||
- **RBAC**: Role-based access via Flask-AppBuilder
|
||||
- **Feature flags**: Control feature rollouts
|
||||
- **Row-level security**: SQL-based data access control
|
||||
|
||||
## Test Utilities
|
||||
|
||||
### Python Test Helpers
|
||||
- **`SupersetTestCase`** - Base class in `tests/integration_tests/base_tests.py`
|
||||
- **`@with_config`** - Config mocking decorator
|
||||
- **`@with_feature_flags`** - Feature flag testing
|
||||
- **`login_as()`, `login_as_admin()`** - Authentication helpers
|
||||
- **`create_dashboard()`, `create_slice()`** - Data setup utilities
|
||||
|
||||
### TypeScript Test Helpers
|
||||
- **`superset-frontend/spec/helpers/testing-library.tsx`** - Custom render() with providers
|
||||
- **`createWrapper()`** - Redux/Router/Theme wrapper
|
||||
- **`selectOption()`** - Select component helper
|
||||
- **React Testing Library** - NO Enzyme (removed)
|
||||
|
||||
### Test Database Patterns
|
||||
- **Mock patterns**: Use `MagicMock()` for config objects, avoid `AsyncMock` for synchronous code
|
||||
- **API tests**: Update expected columns when adding new model fields
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Frontend
|
||||
npm run test # All tests
|
||||
npm run test -- filename.test.tsx # Single file
|
||||
|
||||
# E2E Tests (Playwright - NEW)
|
||||
npm run playwright:test # All Playwright tests
|
||||
npm run playwright:ui # Interactive UI mode
|
||||
npm run playwright:headed # See browser during tests
|
||||
npx playwright test tests/auth/login.spec.ts # Single file
|
||||
npm run playwright:debug tests/auth/login.spec.ts # Debug specific file
|
||||
|
||||
# E2E Tests (Cypress - DEPRECATED)
|
||||
cd superset-frontend/cypress-base
|
||||
npm run cypress-run-chrome # All Cypress tests (headless)
|
||||
npm run cypress-debug # Interactive Cypress UI
|
||||
|
||||
# Backend
|
||||
pytest # All tests
|
||||
pytest tests/unit_tests/specific_test.py # Single file
|
||||
pytest tests/unit_tests/ # Directory
|
||||
|
||||
# If pytest fails with database/setup issues, ask the user to run test environment setup
|
||||
```
|
||||
|
||||
## Environment Validation
|
||||
|
||||
**Quick Setup Check (run this first):**
|
||||
|
||||
```bash
|
||||
# Verify Superset is running
|
||||
curl -f http://localhost:8088/health || echo "❌ Setup required - see https://superset.apache.org/docs/contributing/development#working-with-llms"
|
||||
```
|
||||
|
||||
**If health checks fail:**
|
||||
"It appears you aren't set up properly. Please refer to the [Working with LLMs](https://superset.apache.org/docs/contributing/development#working-with-llms) section in the development docs for setup instructions."
|
||||
|
||||
**Key Project Files:**
|
||||
- `superset-frontend/package.json` - Frontend build scripts (`npm run dev` on port 9000, `npm run test`, `npm run lint`)
|
||||
- `pyproject.toml` - Python tooling (ruff, mypy configs)
|
||||
- `requirements/` folder - Python dependencies (base.txt, development.txt)
|
||||
|
||||
## SQLAlchemy Query Best Practices
|
||||
- **Use negation operator**: `~Model.field` instead of `== False` to avoid ruff E712 errors
|
||||
- **Example**: `~Model.is_active` instead of `Model.is_active == False`
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
**When creating pull requests:**
|
||||
|
||||
1. **Read the current PR template**: Always check `.github/PULL_REQUEST_TEMPLATE.md` for the latest format
|
||||
2. **Use the template sections**: Include all sections from the template (SUMMARY, BEFORE/AFTER, TESTING INSTRUCTIONS, ADDITIONAL INFORMATION)
|
||||
3. **Follow PR title conventions**: Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- Format: `type(scope): description`
|
||||
- Example: `fix(dashboard): load charts correctly`
|
||||
- Types: `fix`, `feat`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
|
||||
**Important**: Always reference the actual template file at `.github/PULL_REQUEST_TEMPLATE.md` instead of using cached content, as the template may be updated over time.
|
||||
|
||||
## Pre-commit Validation
|
||||
|
||||
**Use pre-commit hooks for quality validation:**
|
||||
|
||||
```bash
|
||||
# Install hooks
|
||||
pre-commit install
|
||||
|
||||
# IMPORTANT: Stage your changes first!
|
||||
git add . # Pre-commit only checks staged files
|
||||
|
||||
# Quick validation (faster than --all-files)
|
||||
pre-commit run # Staged files only
|
||||
pre-commit run mypy # Python type checking
|
||||
pre-commit run prettier # Code formatting
|
||||
pre-commit run eslint # Frontend linting
|
||||
```
|
||||
|
||||
**Important pre-commit usage notes:**
|
||||
- **Stage files first**: Run `git add .` before `pre-commit run` to check only changed files (much faster)
|
||||
- **Virtual environment**: Activate your Python virtual environment before running pre-commit
|
||||
```bash
|
||||
# Common virtual environment locations (yours may differ):
|
||||
source .venv/bin/activate # if using .venv
|
||||
source venv/bin/activate # if using venv
|
||||
source ~/venvs/superset/bin/activate # if using a central location
|
||||
```
|
||||
If you get a "command not found" error, ask the user which virtual environment to activate
|
||||
- **Auto-fixes**: Some hooks auto-fix issues (e.g., trailing whitespace). Re-run after fixes are applied
|
||||
|
||||
## Common File Patterns
|
||||
|
||||
### API Structure
|
||||
- **`/api.py`** - REST endpoints with decorators and OpenAPI docstrings
|
||||
- **`/schemas.py`** - Marshmallow validation schemas for OpenAPI spec
|
||||
- **`/commands/`** - Business logic classes with @transaction() decorators
|
||||
- **`/models/`** - SQLAlchemy database models
|
||||
- **OpenAPI docs**: Auto-generated at `/swagger/v1` from docstrings and schemas
|
||||
|
||||
### Migration Files
|
||||
- **Location**: `superset/migrations/versions/`
|
||||
- **Naming**: `YYYY-MM-DD_HH-MM_hash_description.py`
|
||||
- **Utilities**: Use helpers from `superset.migrations.shared.utils` for database compatibility
|
||||
- **Pattern**: Import utilities instead of raw SQLAlchemy operations
|
||||
|
||||
## Platform-Specific Instructions
|
||||
|
||||
- **[CLAUDE.md](CLAUDE.md)** - For Claude/Anthropic tools
|
||||
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - For GitHub Copilot
|
||||
- **[GEMINI.md](GEMINI.md)** - For Google Gemini tools
|
||||
- **[GPT.md](GPT.md)** - For OpenAI/ChatGPT tools
|
||||
- **[.cursor/rules/dev-standard.mdc](.cursor/rules/dev-standard.mdc)** - For Cursor editor
|
||||
|
||||
---
|
||||
|
||||
**LLM Note**: This codebase is actively modernizing toward full TypeScript and type safety. Always run `pre-commit run` to validate changes. Follow the ongoing refactors section to avoid deprecated patterns.
|
||||
@@ -28,6 +28,7 @@ x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset
|
||||
x-superset-volumes:
|
||||
&superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
||||
- ./docker:/app/docker
|
||||
- ./superset-core:/app/superset-core
|
||||
- superset_home:/app/superset_home
|
||||
|
||||
services:
|
||||
|
||||
@@ -29,9 +29,11 @@ x-superset-volumes: &superset-volumes
|
||||
# /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
||||
- ./docker:/app/docker
|
||||
- ./superset:/app/superset
|
||||
- ./superset-core:/app/superset-core
|
||||
- ./superset-frontend:/app/superset-frontend
|
||||
- superset_home:/app/superset_home
|
||||
- ./tests:/app/tests
|
||||
- superset_data:/app/data
|
||||
x-common-build: &common-build
|
||||
context: .
|
||||
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
|
||||
@@ -274,3 +276,5 @@ volumes:
|
||||
external: false
|
||||
redis:
|
||||
external: false
|
||||
superset_data:
|
||||
external: false
|
||||
|
||||
@@ -21,8 +21,15 @@ set -eo pipefail
|
||||
# Make python interactive
|
||||
if [ "$DEV_MODE" == "true" ]; then
|
||||
if [ "$(whoami)" = "root" ] && command -v uv > /dev/null 2>&1; then
|
||||
echo "Reinstalling the app in editable mode"
|
||||
uv pip install -e .
|
||||
# Always ensure superset-core is available
|
||||
echo "Installing superset-core in editable mode"
|
||||
uv pip install --no-deps -e /app/superset-core
|
||||
|
||||
# Only reinstall the main app for non-worker processes
|
||||
if [ "$1" != "worker" ] && [ "$1" != "beat" ]; then
|
||||
echo "Reinstalling the app in editable mode"
|
||||
uv pip install -e .
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
REQUIREMENTS_LOCAL="/app/docker/requirements-local.txt"
|
||||
@@ -34,7 +41,8 @@ if [ "$CYPRESS_CONFIG" == "true" ]; then
|
||||
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset_cypress
|
||||
PORT=8081
|
||||
fi
|
||||
if [[ "$DATABASE_DIALECT" == postgres* ]] && [ "$(whoami)" = "root" ]; then
|
||||
# Skip postgres requirements installation for workers to avoid conflicts
|
||||
if [[ "$DATABASE_DIALECT" == postgres* ]] && [ "$(whoami)" = "root" ] && [ "$1" != "worker" ] && [ "$1" != "beat" ]; then
|
||||
# older images may not have the postgres dev requirements installed
|
||||
echo "Installing postgres requirements"
|
||||
if command -v uv > /dev/null 2>&1; then
|
||||
|
||||
@@ -36,11 +36,11 @@ Screenshots will be taken but no messages actually sent as long as `ALERT_REPORT
|
||||
#### In your `Dockerfile`
|
||||
|
||||
You'll need to extend the Superset image to include a headless browser. Your options include:
|
||||
- Use Playwright with Chrome: this is the recommended approach as of version >=4.1.x. A working example of a Dockerfile that installs these tools is provided under “Building your own production Docker image” on the [Docker Builds](/docs/installation/docker-builds#building-your-own-production-docker-image) page. Read the code comments there as you'll also need to change a feature flag in your config.
|
||||
- Use Playwright with Chrome: this is the recommended approach as of version 4.1.x or greater. A working example of a Dockerfile that installs these tools is provided under "Building your own production Docker image" on the [Docker Builds](/docs/installation/docker-builds#building-your-own-production-docker-image) page. Read the code comments there as you'll also need to change a feature flag in your config.
|
||||
- Use Firefox: you'll need to install geckodriver and Firefox.
|
||||
- Use Chrome without Playwright: you'll need to install Chrome and set the value of `WEBDRIVER_TYPE` to `"chrome"` in your `superset_config.py`.
|
||||
|
||||
In Superset versions <=4.0x, users installed Firefox or Chrome and that was documented here.
|
||||
In Superset versions prior to 4.1, users installed Firefox or Chrome and that was documented here.
|
||||
|
||||
Only the worker container needs the browser.
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ are compatible with Superset.
|
||||
| [IBM Netezza Performance Server](/docs/configuration/databases#ibm-netezza-performance-server) | `pip install nzalchemy` | `netezza+nzpy://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [MySQL](/docs/configuration/databases#mysql) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [OceanBase](/docs/configuration/databases#oceanbase) | `pip install oceanbase_py` | `oceanbase://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Oracle](/docs/configuration/databases#oracle) | `pip install cx_Oracle` | `oracle://<username>:<password>@<hostname>:<port>` |
|
||||
| [Oracle](/docs/configuration/databases#oracle) | `pip install oracledb` | `oracle://<username>:<password>@<hostname>:<port>` |
|
||||
| [Parseable](/docs/configuration/databases#parseable) | `pip install sqlalchemy-parseable` | `parseable://<UserName>:<DBPassword>@<Database Host>/<Stream Name>` |
|
||||
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://{username}:{password}@{hostname}:{port}/{database}` |
|
||||
|
||||
@@ -7,7 +7,7 @@ version: 1
|
||||
# Theming Superset
|
||||
|
||||
:::note
|
||||
apache-superset>=6.0
|
||||
`apache-superset>=6.0`
|
||||
:::
|
||||
|
||||
Superset now rides on **Ant Design v5's token-based theming**.
|
||||
|
||||
@@ -130,7 +130,7 @@ Committers may also update title to reflect the issue/PR content if the author-p
|
||||
|
||||
If the PR passes CI tests and does not have any `need:` labels, it is ready for review, add label `review` and/or `design-review`.
|
||||
|
||||
If an issue/PR has been inactive for >=30 days, it will be closed. If it does not have any status label, add `inactive`.
|
||||
If an issue/PR has been inactive for at least 30 days, it will be closed. If it does not have any status label, add `inactive`.
|
||||
|
||||
When creating a PR, if you're aiming to have it included in a specific release, please tag it with the version label. For example, to have a PR considered for inclusion in Superset 1.1 use the label `v1.1`.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.15.0 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
version: 0.15.1 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 13.4.4
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -203,6 +203,7 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetNode.connections.db_name | string | `"superset"` | |
|
||||
| supersetNode.connections.db_pass | string | `"superset"` | |
|
||||
| supersetNode.connections.db_port | string | `"5432"` | |
|
||||
| supersetNode.connections.db_type | string | `"postgresql"` | Database type for Superset metadata (Supported types: "postgresql", "mysql") |
|
||||
| supersetNode.connections.db_user | string | `"superset"` | |
|
||||
| supersetNode.connections.redis_cache_db | string | `"1"` | |
|
||||
| supersetNode.connections.redis_celery_db | string | `"0"` | |
|
||||
|
||||
@@ -96,7 +96,18 @@ CACHE_CONFIG = {
|
||||
}
|
||||
DATA_CACHE_CONFIG = CACHE_CONFIG
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{env('DB_USER')}:{env('DB_PASS')}@{env('DB_HOST')}:{env('DB_PORT')}/{env('DB_NAME')}"
|
||||
|
||||
if os.getenv("SQLALCHEMY_DATABASE_URI"):
|
||||
SQLALCHEMY_DATABASE_URI = os.getenv("SQLALCHEMY_DATABASE_URI")
|
||||
else:
|
||||
{{- if eq .Values.supersetNode.connections.db_type "postgresql" }}
|
||||
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{os.getenv('DB_USER')}:{os.getenv('DB_PASS')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"
|
||||
{{- else if eq .Values.supersetNode.connections.db_type "mysql" }}
|
||||
SQLALCHEMY_DATABASE_URI = f"mysql+mysqldb://{os.getenv('DB_USER')}:{os.getenv('DB_PASS')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"
|
||||
{{- else }}
|
||||
{{ fail (printf "Unsupported database type: %s. Please use 'postgresql' or 'mysql'." .Values.supersetNode.connections.db_type) }}
|
||||
{{- end }}
|
||||
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
|
||||
class CeleryConfig:
|
||||
|
||||
@@ -289,6 +289,8 @@ supersetNode:
|
||||
enabled: false
|
||||
ssl_cert_reqs: CERT_NONE
|
||||
# You need to change below configuration incase bringing own PostgresSQL instance and also set postgresql.enabled:false
|
||||
# -- Database type for Superset metadata (Supported types: "postgresql", "mysql")
|
||||
db_type: "postgresql"
|
||||
db_host: "{{ .Release.Name }}-postgresql"
|
||||
db_port: "5432"
|
||||
db_user: superset
|
||||
|
||||
@@ -100,7 +100,7 @@ dependencies = [
|
||||
"slack_sdk>=3.19.0, <4",
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-utils>=0.38.3, <0.39",
|
||||
"sqlglot>=27.3.0, <28",
|
||||
"sqlglot>=27.15.2, <28",
|
||||
# newer pandas needs 0.9+
|
||||
"tabulate>=0.9.0, <1.0",
|
||||
"typing-extensions>=4, <5",
|
||||
|
||||
@@ -395,7 +395,7 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
sqlglot==27.3.0
|
||||
sqlglot==27.15.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
sshtunnel==0.4.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
|
||||
@@ -848,7 +848,7 @@ sqlalchemy-utils==0.38.3
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
sqlglot==27.3.0
|
||||
sqlglot==27.15.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
|
||||
@@ -83,6 +83,7 @@ module.exports = {
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:react-prefer-function-component/recommended',
|
||||
'plugin:storybook/recommended',
|
||||
'plugin:react-you-might-not-need-an-effect/legacy-recommended',
|
||||
],
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
@@ -121,7 +122,7 @@ module.exports = {
|
||||
'lodash',
|
||||
'theme-colors',
|
||||
'icons',
|
||||
'superset-i18n',
|
||||
'i18n-strings',
|
||||
'react-prefer-function-component',
|
||||
'prettier',
|
||||
],
|
||||
@@ -177,7 +178,6 @@ module.exports = {
|
||||
'.json': 'always',
|
||||
},
|
||||
],
|
||||
'import/no-named-as-default': 0,
|
||||
'import/no-named-as-default-member': 0,
|
||||
'import/prefer-default-export': 0,
|
||||
indent: 0,
|
||||
@@ -394,7 +394,7 @@ module.exports = {
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
'icons/no-fa-icons-usage': 0,
|
||||
'superset-i18n/no-template-vars': 0,
|
||||
'i18n-strings/no-template-vars': 0,
|
||||
'no-restricted-imports': 0,
|
||||
'react/no-void-elements': 0,
|
||||
},
|
||||
@@ -411,15 +411,8 @@ module.exports = {
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 'error',
|
||||
'icons/no-fa-icons-usage': 'error',
|
||||
'superset-i18n/no-template-vars': ['error', true],
|
||||
'superset-i18n/sentence-case-buttons': 'error',
|
||||
camelcase: [
|
||||
'error',
|
||||
{
|
||||
allow: ['^UNSAFE_'],
|
||||
properties: 'never',
|
||||
},
|
||||
],
|
||||
'i18n-strings/no-template-vars': ['error', true],
|
||||
'i18n-strings/sentence-case-buttons': 'error',
|
||||
'class-methods-use-this': 0,
|
||||
curly: 2,
|
||||
'func-names': 0,
|
||||
|
||||
1
superset-frontend/.gitignore
vendored
1
superset-frontend/.gitignore
vendored
@@ -3,3 +3,4 @@ cypress/screenshots
|
||||
cypress/videos
|
||||
src/temp
|
||||
.temp_cache/
|
||||
.tsbuildinfo
|
||||
|
||||
@@ -46,16 +46,6 @@ module.exports = {
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
...customConfig.resolve,
|
||||
alias: {
|
||||
...config.resolve?.alias,
|
||||
...customConfig.resolve?.alias,
|
||||
'react-dom/test-utils': require.resolve('react-dom/test-utils.js'),
|
||||
},
|
||||
extensionAlias: {
|
||||
'.js': ['.js', '.ts', '.tsx'],
|
||||
'.mjs': ['.mjs', '.mts'],
|
||||
},
|
||||
fullySpecified: false,
|
||||
},
|
||||
plugins: [...config.plugins, ...customConfig.plugins],
|
||||
}),
|
||||
|
||||
@@ -44,6 +44,7 @@ module.exports = {
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'lodash',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
['@babel/plugin-transform-class-properties', { loose: true }],
|
||||
@@ -96,7 +97,7 @@ module.exports = {
|
||||
instrumented: {
|
||||
plugins: [
|
||||
[
|
||||
'babel-plugin-istanbul',
|
||||
'istanbul',
|
||||
{
|
||||
exclude: ['plugins/**/*', 'packages/**/*'],
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "eslint-plugin-superset-i18n",
|
||||
"name": "eslint-plugin-i18n-strings",
|
||||
"version": "1.0.0",
|
||||
"description": "Warns about translation variables",
|
||||
"main": "index.js",
|
||||
|
||||
@@ -35,16 +35,13 @@ module.exports = {
|
||||
'^@apache-superset/core$': '<rootDir>/packages/superset-core/src',
|
||||
'^@apache-superset/core/(.*)$': '<rootDir>/packages/superset-core/src/$1',
|
||||
},
|
||||
testEnvironment: 'jest-fixed-jsdom',
|
||||
testEnvironment: '<rootDir>/spec/helpers/jsDomWithFetchAPI.ts',
|
||||
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
|
||||
setupFilesAfterEnv: ['<rootDir>/spec/helpers/setup.ts'],
|
||||
snapshotSerializers: ['@emotion/jest/serializer'],
|
||||
testEnvironmentOptions: {
|
||||
globalsCleanup: true,
|
||||
url: 'http://localhost',
|
||||
// Jest 30 compatibility: Ensure proper cleanup
|
||||
resources: 'usable',
|
||||
runScripts: 'dangerously',
|
||||
},
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.{js,jsx,ts,tsx}',
|
||||
@@ -83,15 +80,4 @@ module.exports = {
|
||||
],
|
||||
],
|
||||
testTimeout: 20000,
|
||||
// Jest 30 compatibility: Handle timers and async operations properly
|
||||
fakeTimers: {
|
||||
enableGlobally: false,
|
||||
legacyFakeTimers: false,
|
||||
},
|
||||
// Better cleanup for worker processes
|
||||
detectOpenHandles: false,
|
||||
forceExit: true,
|
||||
// Improved memory management
|
||||
maxWorkers: '80%',
|
||||
workerIdleMemoryLimit: '512MB',
|
||||
};
|
||||
|
||||
7076
superset-frontend/package-lock.json
generated
7076
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@
|
||||
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.scss,.sass}|package.json)'",
|
||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --color --mode production",
|
||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
||||
"build-instrumented": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
||||
"build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
||||
"build-storybook": "storybook build",
|
||||
"build-translation": "scripts/po2json.sh",
|
||||
"bundle-stats": "cross-env BUNDLE_ANALYZER=true npm run build && npx open-cli ../superset/static/stats/statistics.html",
|
||||
@@ -133,15 +133,13 @@
|
||||
"chrono-node": "^2.7.8",
|
||||
"classnames": "^2.2.5",
|
||||
"content-disposition": "^0.5.4",
|
||||
"currencyformatter.js": "^2.2.0",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-scale": "^2.1.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"dom-to-image-more": "^3.6.0",
|
||||
"dom-to-pdf": "^0.3.2",
|
||||
"echarts": "^5.6.0",
|
||||
"emotion-rgba": "0.0.12",
|
||||
"eslint-plugin-superset-i18n": "file:eslint-rules/eslint-plugin-i18n-strings",
|
||||
"eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
@@ -152,7 +150,6 @@
|
||||
"geostyler-qgis-parser": "2.0.1",
|
||||
"geostyler-style": "7.5.0",
|
||||
"geostyler-wfs-parser": "^2.0.3",
|
||||
"global-box": "^2.0.2",
|
||||
"googleapis": "^154.1.0",
|
||||
"immer": "^10.1.1",
|
||||
"interweave": "^13.1.0",
|
||||
@@ -173,7 +170,6 @@
|
||||
"ol": "^7.5.2",
|
||||
"polished": "^4.3.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^7.1.3",
|
||||
"re-resizable": "^6.10.1",
|
||||
"react": "^17.0.2",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
@@ -194,10 +190,9 @@
|
||||
"react-search-input": "^0.11.3",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-split": "^2.0.9",
|
||||
"react-syntax-highlighter": "^15.6.6",
|
||||
"react-table": "^7.8.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-virtualized-auto-sizer": "^1.0.25",
|
||||
"react-virtualized-auto-sizer": "^1.0.26",
|
||||
"react-window": "^1.8.10",
|
||||
"redux": "^4.2.1",
|
||||
"redux-localstorage": "^0.4.1",
|
||||
@@ -211,7 +206,7 @@
|
||||
"urijs": "^1.19.8",
|
||||
"use-event-callback": "^0.1.0",
|
||||
"use-immer": "^0.9.0",
|
||||
"use-query-params": "^1.2.3",
|
||||
"use-query-params": "^1.1.9",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
@@ -240,15 +235,15 @@
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@mihkeleidast/storybook-addon-source": "^1.0.1",
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@storybook/addon-actions": "8.6.14",
|
||||
"@storybook/addon-controls": "8.6.14",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/addon-links": "8.6.14",
|
||||
"@storybook/addon-mdx-gfm": "8.6.14",
|
||||
"@storybook/components": "8.6.14",
|
||||
"@storybook/preview-api": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/react-webpack5": "8.6.14",
|
||||
"@storybook/addon-actions": "8.1.11",
|
||||
"@storybook/addon-controls": "8.1.11",
|
||||
"@storybook/addon-essentials": "8.1.11",
|
||||
"@storybook/addon-links": "8.1.11",
|
||||
"@storybook/addon-mdx-gfm": "8.1.11",
|
||||
"@storybook/components": "8.1.11",
|
||||
"@storybook/preview-api": "8.1.11",
|
||||
"@storybook/react": "8.1.11",
|
||||
"@storybook/react-webpack5": "8.1.11",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
@@ -272,7 +267,6 @@
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.8",
|
||||
"@types/react-ultimate-pagination": "^1.2.4",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/redux-localstorage": "^1.0.8",
|
||||
"@types/redux-mock-store": "^1.0.6",
|
||||
@@ -286,6 +280,7 @@
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"babel-plugin-typescript-to-proptypes": "^2.0.0",
|
||||
"cheerio": "1.1.0",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
@@ -310,6 +305,7 @@
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-prefer-function-component": "^3.3.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.5.1",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"eslint-plugin-testing-library": "^6.4.0",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
@@ -319,8 +315,7 @@
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"imports-loader": "^5.0.0",
|
||||
"jest": "^30.0.2",
|
||||
"jest-environment-jsdom": "^30.0.3",
|
||||
"jest-fixed-jsdom": "^0.0.10",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-html-reporter": "^4.3.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"jsdom": "^26.0.0",
|
||||
@@ -337,7 +332,7 @@
|
||||
"source-map": "^0.7.4",
|
||||
"source-map-support": "^0.5.21",
|
||||
"speed-measure-webpack-plugin": "^1.5.0",
|
||||
"storybook": "8.6.14",
|
||||
"storybook": "8.1.11",
|
||||
"style-loader": "^4.0.0",
|
||||
"thread-loader": "^4.0.4",
|
||||
"ts-jest": "^29.4.0",
|
||||
@@ -366,45 +361,12 @@
|
||||
"npm": "^10.8.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@superset-ui/legacy-plugin-chart-horizon": {
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"@superset-ui/legacy-preset-chart-deckgl": {
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"@superset-ui/plugin-chart-word-cloud": {
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"core-js": "^3.38.1",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-scale-chromatic": "^3.1.0",
|
||||
"encodable": "^0.5.4",
|
||||
"glob": "^11.0.0",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"test-exclude": "^7.0.1",
|
||||
"jspdf": "^3.0.1",
|
||||
"nwsapi": "^2.2.13",
|
||||
"prismjs": "^1.30.0",
|
||||
"puppeteer": "^22.4.1",
|
||||
"rimraf": "^6.0.0",
|
||||
"tr46": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"underscore": "^1.13.7",
|
||||
"handlebars": "^4.7.8",
|
||||
"@storybook/core": "8.6.14",
|
||||
"storybook": "8.6.14",
|
||||
"whatwg-url": {
|
||||
"punycode": "^2.3.1"
|
||||
}
|
||||
"jspdf": "^3.0.1",
|
||||
"nwsapi": "^2.2.13"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"scarfSettings": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@apache-superset/core",
|
||||
"version": "0.0.1-rc4",
|
||||
"version": "0.0.1-rc5",
|
||||
"description": "This package contains UI elements, APIs, and utility functions used by Superset.",
|
||||
"sideEffects": false,
|
||||
"main": "lib/index.js",
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*", "types/**/*"],
|
||||
"exclude": ["src/**/*.test.*", "src/**/*.stories.*"]
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactNode } from 'react';
|
||||
import { css, GenericDataType, styled, t } from '@superset-ui/core';
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import {
|
||||
ClockCircleOutlined,
|
||||
QuestionOutlined,
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
DTTM_ALIAS,
|
||||
GenericDataType,
|
||||
QueryColumn,
|
||||
QueryMode,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { DTTM_ALIAS, QueryColumn, QueryMode, t } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
|
||||
|
||||
export const DEFAULT_MAX_ROW = 100000;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { DatasourceType, GenericDataType } from '@superset-ui/core';
|
||||
import { DatasourceType } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { Dataset } from './types';
|
||||
|
||||
export const TestDataset: Dataset = {
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
import {
|
||||
ContributionType,
|
||||
ensureIsArray,
|
||||
GenericDataType,
|
||||
getColumnLabel,
|
||||
getMetricLabel,
|
||||
QueryFormColumn,
|
||||
QueryFormMetric,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import {
|
||||
ControlPanelState,
|
||||
ControlState,
|
||||
|
||||
@@ -17,12 +17,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
GenericDataType,
|
||||
QueryColumn,
|
||||
t,
|
||||
validateNonEmpty,
|
||||
} from '@superset-ui/core';
|
||||
import { QueryColumn, t, validateNonEmpty } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import {
|
||||
ExtraControlProps,
|
||||
SharedControlConfig,
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ensureIsArray, GenericDataType, ValueOf } from '@superset-ui/core';
|
||||
import { ensureIsArray, ValueOf } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { ControlPanelState, isDataset, isQueryResponse } from '../types';
|
||||
|
||||
export function checkColumnType(
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { GenericDataType, QueryColumn, QueryResponse } from '@superset-ui/core';
|
||||
import { QueryColumn, QueryResponse } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { ColumnMeta, Dataset, isDataset, isQueryResponse } from '../types';
|
||||
|
||||
export function columnsByType(
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
GenericDataType,
|
||||
getColumnLabel,
|
||||
isPhysicalColumn,
|
||||
QueryFormColumn,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { checkColumnType, ControlStateMapping } from '..';
|
||||
|
||||
export function isSortable(controls: ControlStateMapping): boolean {
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { ColumnOption, ColumnOptionProps } from '../../src';
|
||||
|
||||
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
import { isValidElement } from 'react';
|
||||
import { render, screen } from '@superset-ui/core/spec';
|
||||
import '@testing-library/jest-dom';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { ColumnTypeLabel, ColumnTypeLabelProps } from '../../src';
|
||||
|
||||
describe('ColumnOption', () => {
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { GenericDataType, testQueryResponse } from '@superset-ui/core';
|
||||
import { testQueryResponse } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { checkColumnType, TestDataset } from '../../src';
|
||||
|
||||
test('checkColumnType columns from a Dataset', () => {
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
DatasourceType,
|
||||
GenericDataType,
|
||||
testQueryResponse,
|
||||
} from '@superset-ui/core';
|
||||
import { DatasourceType, testQueryResponse } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { columnChoices } from '../../src';
|
||||
|
||||
describe('columnChoices()', () => {
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
GenericDataType,
|
||||
testQueryResponse,
|
||||
testQueryResults,
|
||||
} from '@superset-ui/core';
|
||||
import { testQueryResponse, testQueryResults } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import {
|
||||
Dataset,
|
||||
getTemporalColumns,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { ControlStateMapping } from '@superset-ui/chart-controls';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { isSortable } from '../../src/utils/isSortable';
|
||||
|
||||
const controls: ControlStateMapping = {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*", "types/**/*"],
|
||||
"exclude": ["src/**/*.test.*", "src/**/*.stories.*"],
|
||||
|
||||
@@ -16,5 +16,4 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
type FlashMessageType = 'info' | 'alert' | 'danger' | 'warning' | 'success';
|
||||
export type FlashMessage = [FlashMessageType, string];
|
||||
module.exports = {};
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
module.exports = 'test-file-stub';
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 { SVGProps, forwardRef } from 'react';
|
||||
|
||||
const SvgrMock = forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
||||
(props, ref) => <svg ref={ref} {...props} />,
|
||||
);
|
||||
|
||||
SvgrMock.displayName = 'SvgrMock';
|
||||
|
||||
export const ReactComponent = SvgrMock;
|
||||
export default SvgrMock;
|
||||
@@ -35,78 +35,57 @@ const selector = '[id="ace-editor"]';
|
||||
test('renders SQLEditor', async () => {
|
||||
const { container } = render(<SQLEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders FullSQLEditor', async () => {
|
||||
const { container } = render(<FullSQLEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders MarkdownEditor', async () => {
|
||||
const { container } = render(<MarkdownEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders TextAreaEditor', async () => {
|
||||
const { container } = render(<TextAreaEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders CssEditor', async () => {
|
||||
const { container } = render(<CssEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders JsonEditor', async () => {
|
||||
const { container } = render(<JsonEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders ConfigEditor', async () => {
|
||||
const { container } = render(<ConfigEditor />);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders a custom placeholder', () => {
|
||||
|
||||
@@ -25,32 +25,33 @@ const AsyncComponent = ({ bold }: { bold: boolean }) => (
|
||||
<span style={{ fontWeight: bold ? 700 : 400 }}>AsyncComponent</span>
|
||||
);
|
||||
|
||||
const createComponentPromise = () =>
|
||||
new Promise(resolve => setTimeout(() => resolve(AsyncComponent), 100));
|
||||
const ComponentPromise = new Promise(resolve =>
|
||||
setTimeout(() => resolve(AsyncComponent), 500),
|
||||
);
|
||||
|
||||
test('renders without placeholder', async () => {
|
||||
const Component = AsyncEsmComponent(createComponentPromise());
|
||||
const Component = AsyncEsmComponent(ComponentPromise);
|
||||
render(<Component showLoadingForImport={false} />);
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
||||
expect(await screen.findByText('AsyncComponent')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with default placeholder', async () => {
|
||||
const Component = AsyncEsmComponent(createComponentPromise());
|
||||
const Component = AsyncEsmComponent(ComponentPromise);
|
||||
render(<Component height={30} showLoadingForImport />);
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
expect(await screen.findByText('AsyncComponent')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with custom placeholder', async () => {
|
||||
const Component = AsyncEsmComponent(createComponentPromise(), Placeholder);
|
||||
const Component = AsyncEsmComponent(ComponentPromise, Placeholder);
|
||||
render(<Component showLoadingForImport />);
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(await screen.findByText('AsyncComponent')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with custom props', async () => {
|
||||
const Component = AsyncEsmComponent(createComponentPromise(), Placeholder);
|
||||
const Component = AsyncEsmComponent(ComponentPromise, Placeholder);
|
||||
render(<Component showLoadingForImport bold />);
|
||||
const asyncComponent = await screen.findByText('AsyncComponent');
|
||||
expect(asyncComponent).toBeInTheDocument();
|
||||
|
||||
@@ -154,7 +154,7 @@ test('accepts custom style props', () => {
|
||||
render(<DropdownContainer items={generateItems(2)} style={customStyle} />);
|
||||
|
||||
const container = screen.getByTestId('container');
|
||||
expect(container).toHaveStyle('background-color: rgb(255, 0, 0)');
|
||||
expect(container).toHaveStyle('background-color: red');
|
||||
expect(container).toHaveStyle('padding: 10px');
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import { render, screen, waitFor } from '@superset-ui/core/spec';
|
||||
import { render, screen } from '@superset-ui/core/spec';
|
||||
import { ImageLoader, type BackgroundPosition } from './ImageLoader';
|
||||
|
||||
global.URL.createObjectURL = jest.fn(() => '/local_url');
|
||||
@@ -48,9 +48,7 @@ describe('ImageLoader', () => {
|
||||
|
||||
it('is a valid element', async () => {
|
||||
setup();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('image-loader')).toBeVisible();
|
||||
});
|
||||
expect(await screen.findByTestId('image-loader')).toBeVisible();
|
||||
});
|
||||
|
||||
it('fetches loads the image in the background', async () => {
|
||||
|
||||
@@ -127,13 +127,9 @@ const Select = forwardRef(
|
||||
const shouldShowSearch = allowNewOptions ? true : showSearch;
|
||||
const [selectValue, setSelectValue] = useState(value);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(loading);
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [visibleOptions, setVisibleOptions] = useState<SelectOptionsType>([]);
|
||||
const [maxTagCount, setMaxTagCount] = useState(
|
||||
propsMaxTagCount ?? MAX_TAG_COUNT,
|
||||
);
|
||||
const [onChangeCount, setOnChangeCount] = useState(0);
|
||||
const previousChangeCount = usePrevious(onChangeCount, 0);
|
||||
const fireOnChange = useCallback(
|
||||
@@ -141,11 +137,11 @@ const Select = forwardRef(
|
||||
[onChangeCount],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (oneLine) {
|
||||
setMaxTagCount(isDropdownVisible ? 0 : 1);
|
||||
}
|
||||
}, [isDropdownVisible, oneLine]);
|
||||
const maxTagCount = oneLine
|
||||
? isDropdownVisible
|
||||
? 0
|
||||
: 1
|
||||
: (propsMaxTagCount ?? MAX_TAG_COUNT);
|
||||
|
||||
const mappedMode = isSingleMode ? undefined : 'multiple';
|
||||
|
||||
@@ -510,6 +506,8 @@ const Select = forwardRef(
|
||||
],
|
||||
);
|
||||
|
||||
const isLoading = loading ?? false;
|
||||
|
||||
const popupRender = (
|
||||
originNode: ReactElement & { ref?: RefObject<HTMLElement> },
|
||||
) =>
|
||||
@@ -536,12 +534,6 @@ const Select = forwardRef(
|
||||
setVisibleOptions(initialOptions);
|
||||
}, [initialOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading !== undefined && loading !== isLoading) {
|
||||
setIsLoading(loading);
|
||||
}
|
||||
}, [isLoading, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectValue(value);
|
||||
}, [value]);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { GenericDataType } from './QueryResponse';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { QueryFormColumn } from './QueryFormData';
|
||||
|
||||
export interface AdhocColumn {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { DatasourceType } from './Datasource';
|
||||
import { BinaryOperator, SetOperator, UnaryOperator } from './Operator';
|
||||
import { AppliedTimeExtras, TimeRange } from './Time';
|
||||
@@ -31,7 +32,7 @@ import { Maybe } from '../../types';
|
||||
import { PostProcessingRule } from './PostProcessing';
|
||||
import { JsonObject } from '../../connection';
|
||||
import { TimeGranularity } from '../../time-format';
|
||||
import { GenericDataType, DataRecordValue } from './QueryResponse';
|
||||
import { DataRecordValue } from './QueryResponse';
|
||||
|
||||
export type BaseQueryObjectFilterClause = {
|
||||
col: QueryFormColumn;
|
||||
|
||||
@@ -17,19 +17,10 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
import { TimeseriesDataRecord } from '../../chart';
|
||||
import { AnnotationData } from './AnnotationLayer';
|
||||
|
||||
/**
|
||||
* Generic data types, see enum of the same name in superset/utils/core.py.
|
||||
*/
|
||||
export enum GenericDataType {
|
||||
Numeric = 0,
|
||||
String = 1,
|
||||
Temporal = 2,
|
||||
Boolean = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive types for data field values.
|
||||
*/
|
||||
|
||||
@@ -513,7 +513,7 @@ describe('SupersetClientClass', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('when unauthorized', () => {
|
||||
describe('when unauthorized', () => {
|
||||
let originalLocation: any;
|
||||
let authSpy: jest.SpyInstance;
|
||||
const mockRequestUrl = 'https://host/get/url';
|
||||
|
||||
@@ -354,33 +354,19 @@ describe('callApi()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: These caching tests require complex jsdom 26 window.location.protocol mocking
|
||||
// They were skipped during Jest 30/jsdom 26 upgrade due to property redefinition issues
|
||||
// Consider implementing with different mocking strategy in future PR
|
||||
describe.skip('caching', () => {
|
||||
const originalProtocol = window.location.protocol;
|
||||
describe('caching', () => {
|
||||
const origLocation = window.location;
|
||||
|
||||
beforeEach(() => {
|
||||
// jsdom 26+ compatibility: Store and reset protocol per test
|
||||
Object.defineProperty(window.location, 'protocol', {
|
||||
value: 'https:',
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'location', { value: {} });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Reset protocol after each test
|
||||
if (window.location.protocol !== originalProtocol) {
|
||||
Object.defineProperty(window.location, 'protocol', {
|
||||
value: originalProtocol,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
afterAll(() => {
|
||||
Object.defineProperty(window, 'location', { value: origLocation });
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
window.location.protocol = 'https:';
|
||||
await caches.delete(constants.CACHE_KEY);
|
||||
});
|
||||
|
||||
@@ -396,13 +382,7 @@ describe('callApi()', () => {
|
||||
|
||||
it('will not use cache when running off an insecure connection', async () => {
|
||||
expect.assertions(2);
|
||||
|
||||
// Set insecure protocol for this specific test
|
||||
Object.defineProperty(window.location, 'protocol', {
|
||||
value: 'http:',
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
window.location.protocol = 'http:';
|
||||
|
||||
await callApi({ url: mockCacheUrl, method: 'GET' });
|
||||
const calls = fetchMock.calls(mockCacheUrl);
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { AdhocMetric, GenericDataType } from '@superset-ui/core';
|
||||
import { AdhocMetric } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/api/core';
|
||||
|
||||
export const NUM_METRIC: AdhocMetric = {
|
||||
expressionType: 'SIMPLE',
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*", "types/**/*"],
|
||||
"exclude": ["src/**/*.test.*", "src/**/*.stories.*"],
|
||||
|
||||
@@ -20,11 +20,6 @@ module.exports = {
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
...customConfig.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
...customConfig.resolve.alias,
|
||||
'react-dom/test-utils': 'react-dom/test-utils.js',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
@@ -36,36 +36,31 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mihkeleidast/storybook-addon-source": "^1.0.1",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@storybook/addon-actions": "8.6.14",
|
||||
"@storybook/addon-controls": "8.6.14",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/addon-links": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/types": "8.6.14",
|
||||
"@storybook/addon-actions": "9.0.8",
|
||||
"@storybook/addon-controls": "8.1.11",
|
||||
"@storybook/addon-links": "8.1.11",
|
||||
"@storybook/react": "8.1.11",
|
||||
"@storybook/types": "8.4.7",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
"core-js": "3.40.0",
|
||||
"d3-scale": "4.0.2",
|
||||
"gh-pages": "^6.3.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"jquery": "^3.7.1",
|
||||
"memoize-one": "^5.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-resizable": "^3.0.5",
|
||||
"react-syntax-highlighter": "^15.6.6",
|
||||
"storybook": "8.6.14"
|
||||
"react-resizable": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.28.3",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@storybook/react-webpack5": "8.6.14",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@storybook/react-webpack5": "8.2.9",
|
||||
"babel-loader": "^10.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "^9.1.0",
|
||||
"ts-loader": "^9.5.2",
|
||||
"typescript": "5.4.5"
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@encodable/color": "=1.1.1",
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*", "types/**/*"],
|
||||
"exclude": ["src/**/*.test.*", "src/**/*.stories.*"]
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"d3-array": "^2.0.3",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-scale": "^3.0.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
"dependencies": {
|
||||
"d3": "^3.5.17",
|
||||
"d3-array": "^2.4.0",
|
||||
"d3-color": "^3.1.0",
|
||||
"datamaps": "^0.5.9",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"react": "^17.0.2",
|
||||
"tinycolor2": "*"
|
||||
"react": "^17.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { rgb } from 'd3-color';
|
||||
import { getValueFormatter } from '@superset-ui/core';
|
||||
|
||||
export default function transformProps(chartProps) {
|
||||
@@ -66,7 +66,7 @@ export default function transformProps(chartProps) {
|
||||
maxBubbleSize: parseInt(maxBubbleSize, 10),
|
||||
showBubbles,
|
||||
linearColorScheme,
|
||||
color: tinycolor({ r, g, b }).toHexString(),
|
||||
color: rgb(r, g, b).hex(),
|
||||
colorBy,
|
||||
colorScheme,
|
||||
sliceId,
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// Path Resolution: Override baseUrl to maintain correct path mappings from parent config
|
||||
// (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
|
||||
"baseUrl": "../..",
|
||||
"outDir": "lib"
|
||||
|
||||
// Directory Overrides: Parent config paths are relative to frontend root,
|
||||
// but packages need paths relative to their own directory
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"declarationDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
|
||||
"exclude": [
|
||||
|
||||
@@ -26,26 +26,23 @@
|
||||
"dependencies": {
|
||||
"@deck.gl/aggregation-layers": "^9.1.14",
|
||||
"@deck.gl/core": "^9.1.14",
|
||||
"@deck.gl/extensions": "^9.1.14",
|
||||
"@deck.gl/geo-layers": "^9.1.13",
|
||||
"@deck.gl/layers": "^9.1.13",
|
||||
"@deck.gl/mesh-layers": "^9.1.14",
|
||||
"@deck.gl/react": "^9.1.14",
|
||||
"@deck.gl/widgets": "^9.1.14",
|
||||
"@luma.gl/constants": "^9.1.9",
|
||||
"@luma.gl/core": "^9.1.9",
|
||||
"@luma.gl/engine": "^9.1.9",
|
||||
"@luma.gl/shadertools": "^9.1.9",
|
||||
"@luma.gl/webgl": "^9.1.9",
|
||||
"@mapbox/geojson-extent": "^1.0.1",
|
||||
"@mapbox/tiny-sdf": "^2.0.6",
|
||||
"@mapbox/geojson-extent": "^1.0.1",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/d3-array": "^2.0.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"bootstrap-slider": "^11.0.2",
|
||||
"d3-array": "^1.2.4",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-color": "^1.4.1",
|
||||
"d3-scale": "^3.0.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"handlebars": "^4.7.8",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -68,8 +65,7 @@
|
||||
"mapbox-gl": "*",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-map-gl": "^6.1.19",
|
||||
"tinycolor2": "*"
|
||||
"react-map-gl": "^6.1.19"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -169,12 +169,12 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
|
||||
}));
|
||||
}
|
||||
case COLOR_SCHEME_TYPES.color_breakpoints: {
|
||||
const defaultBreakpointColor = fd.deafult_breakpoint_color
|
||||
const defaultBreakpointColor = fd.default_breakpoint_color
|
||||
? [
|
||||
fd.deafult_breakpoint_color.r,
|
||||
fd.deafult_breakpoint_color.g,
|
||||
fd.deafult_breakpoint_color.b,
|
||||
fd.deafult_breakpoint_color.a * 255,
|
||||
fd.default_breakpoint_color.r,
|
||||
fd.default_breakpoint_color.g,
|
||||
fd.default_breakpoint_color.b,
|
||||
fd.default_breakpoint_color.a * 255,
|
||||
]
|
||||
: [
|
||||
DEFAULT_DECKGL_COLOR.r,
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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 {
|
||||
buildQueryContext,
|
||||
ensureIsArray,
|
||||
SqlaFormData,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
getSpatialColumns,
|
||||
addSpatialNullFilters,
|
||||
SpatialFormData,
|
||||
} from '../spatialUtils';
|
||||
import { addTooltipColumnsToQuery } from '../buildQueryUtils';
|
||||
|
||||
export interface DeckArcFormData extends SqlaFormData {
|
||||
start_spatial: SpatialFormData['spatial'];
|
||||
end_spatial: SpatialFormData['spatial'];
|
||||
dimension?: string;
|
||||
js_columns?: string[];
|
||||
tooltip_contents?: unknown[];
|
||||
tooltip_template?: string;
|
||||
}
|
||||
|
||||
export default function buildQuery(formData: DeckArcFormData) {
|
||||
const {
|
||||
start_spatial,
|
||||
end_spatial,
|
||||
dimension,
|
||||
js_columns,
|
||||
tooltip_contents,
|
||||
} = formData;
|
||||
|
||||
if (!start_spatial || !end_spatial) {
|
||||
throw new Error(
|
||||
'Start and end spatial configurations are required for Arc charts',
|
||||
);
|
||||
}
|
||||
|
||||
return buildQueryContext(formData, baseQueryObject => {
|
||||
const startSpatialColumns = getSpatialColumns(start_spatial);
|
||||
const endSpatialColumns = getSpatialColumns(end_spatial);
|
||||
|
||||
let columns = [
|
||||
...(baseQueryObject.columns || []),
|
||||
...startSpatialColumns,
|
||||
...endSpatialColumns,
|
||||
];
|
||||
|
||||
if (dimension) {
|
||||
columns = [...columns, dimension];
|
||||
}
|
||||
|
||||
const jsColumns = ensureIsArray(js_columns || []);
|
||||
jsColumns.forEach(col => {
|
||||
if (!columns.includes(col)) {
|
||||
columns.push(col);
|
||||
}
|
||||
});
|
||||
|
||||
columns = addTooltipColumnsToQuery(columns, tooltip_contents);
|
||||
|
||||
let filters = addSpatialNullFilters(
|
||||
start_spatial,
|
||||
ensureIsArray(baseQueryObject.filters || []),
|
||||
);
|
||||
filters = addSpatialNullFilters(end_spatial, filters);
|
||||
|
||||
const isTimeseries = !!formData.time_grain_sqla;
|
||||
|
||||
return [
|
||||
{
|
||||
...baseQueryObject,
|
||||
columns,
|
||||
filters,
|
||||
is_timeseries: isTimeseries,
|
||||
row_limit: baseQueryObject.row_limit,
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -21,7 +21,8 @@ import thumbnail from './images/thumbnail.png';
|
||||
import thumbnailDark from './images/thumbnail-dark.png';
|
||||
import example from './images/example.png';
|
||||
import exampleDark from './images/example-dark.png';
|
||||
import transformProps from '../../transformProps';
|
||||
import transformProps from './transformProps';
|
||||
import buildQuery from './buildQuery';
|
||||
import controlPanel from './controlPanel';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
@@ -39,13 +40,13 @@ const metadata = new ChartMetadata({
|
||||
thumbnail,
|
||||
thumbnailDark,
|
||||
exampleGallery: [{ url: example, urlDark: exampleDark }],
|
||||
useLegacyApi: true,
|
||||
tags: [t('deckGL'), t('Geo'), t('3D'), t('Relational'), t('Web')],
|
||||
});
|
||||
|
||||
export default class ArcChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
buildQuery,
|
||||
loadChart: () => import('./Arc'),
|
||||
controlPanel,
|
||||
metadata,
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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 { ChartProps } from '@superset-ui/core';
|
||||
import {
|
||||
processSpatialData,
|
||||
addJsColumnsToExtraProps,
|
||||
DataRecord,
|
||||
} from '../spatialUtils';
|
||||
import {
|
||||
createBaseTransformResult,
|
||||
getRecordsFromQuery,
|
||||
addPropertiesToFeature,
|
||||
} from '../transformUtils';
|
||||
import { DeckArcFormData } from './buildQuery';
|
||||
|
||||
interface ArcPoint {
|
||||
sourcePosition: [number, number];
|
||||
targetPosition: [number, number];
|
||||
cat_color?: string;
|
||||
__timestamp?: number;
|
||||
extraProps?: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
function processArcData(
|
||||
records: DataRecord[],
|
||||
startSpatial: DeckArcFormData['start_spatial'],
|
||||
endSpatial: DeckArcFormData['end_spatial'],
|
||||
dimension?: string,
|
||||
jsColumns?: string[],
|
||||
): ArcPoint[] {
|
||||
if (!startSpatial || !endSpatial || !records.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const startFeatures = processSpatialData(records, startSpatial);
|
||||
const endFeatures = processSpatialData(records, endSpatial);
|
||||
const excludeKeys = new Set(
|
||||
['__timestamp', dimension, ...(jsColumns || [])].filter(
|
||||
(key): key is string => key != null,
|
||||
),
|
||||
);
|
||||
|
||||
return records
|
||||
.map((record, index) => {
|
||||
const startFeature = startFeatures[index];
|
||||
const endFeature = endFeatures[index];
|
||||
|
||||
if (!startFeature || !endFeature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let arcPoint: ArcPoint = {
|
||||
sourcePosition: startFeature.position,
|
||||
targetPosition: endFeature.position,
|
||||
extraProps: {},
|
||||
};
|
||||
|
||||
arcPoint = addJsColumnsToExtraProps(arcPoint, record, jsColumns);
|
||||
|
||||
if (dimension && record[dimension] != null) {
|
||||
arcPoint.cat_color = String(record[dimension]);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (record.__timestamp != null) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
arcPoint.__timestamp = Number(record.__timestamp);
|
||||
}
|
||||
|
||||
arcPoint = addPropertiesToFeature(arcPoint, record, excludeKeys);
|
||||
return arcPoint;
|
||||
})
|
||||
.filter((point): point is ArcPoint => point !== null);
|
||||
}
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
const { rawFormData: formData } = chartProps;
|
||||
const { start_spatial, end_spatial, dimension, js_columns } =
|
||||
formData as DeckArcFormData;
|
||||
|
||||
const records = getRecordsFromQuery(chartProps.queriesData);
|
||||
const features = processArcData(
|
||||
records,
|
||||
start_spatial,
|
||||
end_spatial,
|
||||
dimension,
|
||||
js_columns,
|
||||
);
|
||||
|
||||
return createBaseTransformResult(chartProps, features);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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 { SpatialFormData, buildSpatialQuery } from '../spatialUtils';
|
||||
|
||||
export interface DeckContourFormData extends SpatialFormData {
|
||||
cellSize?: string;
|
||||
aggregation?: string;
|
||||
contours?: Array<{
|
||||
color: { r: number; g: number; b: number };
|
||||
lowerThreshold: number;
|
||||
upperThreshold?: number;
|
||||
strokeWidth?: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default function buildQuery(formData: DeckContourFormData) {
|
||||
return buildSpatialQuery(formData);
|
||||
}
|
||||
@@ -17,12 +17,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { t, ChartMetadata, ChartPlugin, Behavior } from '@superset-ui/core';
|
||||
import transformProps from '../../transformProps';
|
||||
import controlPanel from './controlPanel';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import thumbnailDark from './images/thumbnail-dark.png';
|
||||
import example from './images/example.png';
|
||||
import exampleDark from './images/example-dark.png';
|
||||
import buildQuery from './buildQuery';
|
||||
import transformProps from './transformProps';
|
||||
import controlPanel from './controlPanel';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
category: t('Map'),
|
||||
@@ -34,7 +35,6 @@ const metadata = new ChartMetadata({
|
||||
name: t('deck.gl Contour'),
|
||||
thumbnail,
|
||||
thumbnailDark,
|
||||
useLegacyApi: true,
|
||||
tags: [t('deckGL'), t('Spatial'), t('Comparison')],
|
||||
behaviors: [Behavior.InteractiveChart],
|
||||
});
|
||||
@@ -42,6 +42,7 @@ const metadata = new ChartMetadata({
|
||||
export default class ContourChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
buildQuery,
|
||||
loadChart: () => import('./Contour'),
|
||||
controlPanel,
|
||||
metadata,
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { transformSpatialProps } from '../spatialUtils';
|
||||
|
||||
export { default as DrillDetailMenuItems } from './DrillDetailMenuItems';
|
||||
export { useDrillDetailMenuItems } from './useDrillDetailMenuItems';
|
||||
export default transformSpatialProps;
|
||||
@@ -76,7 +76,7 @@ export const getLayer: GetLayerType<GridLayer> = function ({
|
||||
|
||||
const colorSchemeType = fd.color_scheme_type;
|
||||
const colorRange = getColorRange({
|
||||
defaultBreakpointsColor: fd.deafult_breakpoint_color,
|
||||
defaultBreakpointsColor: fd.default_breakpoint_color,
|
||||
colorSchemeType,
|
||||
colorScale,
|
||||
colorBreakpoints,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user