mirror of
https://github.com/apache/superset.git
synced 2026-08-12 11:11:01 +00:00
Compare commits
358
Commits
@@ -0,0 +1,38 @@
|
||||
# Security Policy
|
||||
|
||||
This is a project of the [Apache Software Foundation](https://apache.org) and follows the
|
||||
ASF [vulnerability handling process](https://apache.org/security/#vulnerability-handling).
|
||||
|
||||
## Reporting Vulnerabilities
|
||||
|
||||
**⚠️ Please do not file GitHub issues for security vulnerabilities as they are public! ⚠️**
|
||||
|
||||
|
||||
Apache Software Foundation takes a rigorous standpoint in annihilating the security issues
|
||||
in its software projects. Apache Superset is highly sensitive and forthcoming to issues
|
||||
pertaining to its features and functionality.
|
||||
If you have any concern or believe you have found a vulnerability in Apache Superset,
|
||||
please get in touch with the Apache Security Team privately at
|
||||
e-mail address [security@apache.org](mailto:security@apache.org).
|
||||
|
||||
More details can be found on the ASF website at
|
||||
[ASF vulnerability reporting process](https://apache.org/security/#reporting-a-vulnerability)
|
||||
|
||||
We kindly ask you to include the following information in your report:
|
||||
- Apache Superset version that you are using
|
||||
- A sanitized copy of your `superset_config.py` file or any config overrides
|
||||
- Detailed steps to reproduce the vulnerability
|
||||
|
||||
Note that Apache Superset is not responsible for any third-party dependencies that may
|
||||
have security issues. Any vulnerabilities found in third-party dependencies should be
|
||||
reported to the maintainers of those projects. Results from security scans of Apache
|
||||
Superset dependencies found on its official Docker image can be remediated at release time
|
||||
by extending the image itself.
|
||||
|
||||
**Your responsible disclosure and collaboration are invaluable.**
|
||||
|
||||
## Extra Information
|
||||
|
||||
- [Apache Superset documentation](https://superset.apache.org/docs/security)
|
||||
- [Common Vulnerabilities and Exposures by release](https://superset.apache.org/docs/security/cves)
|
||||
- [How Security Vulnerabilities are Reported & Handled in Apache Superset (Blog)](https://preset.io/blog/how-security-vulnerabilities-are-reported-and-handled-in-apache-superset/)
|
||||
@@ -18,8 +18,6 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${{ (secrets.AWS_ACCESS_KEY_ID != '' &&
|
||||
secrets.AWS_ACCESS_KEY_ID != '' &&
|
||||
secrets.AWS_SECRET_ACCESS_KEY != '' &&
|
||||
secrets.AWS_SECRET_ACCESS_KEY != '') || '' }}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
echo "has secrets!"
|
||||
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
jobs:
|
||||
config:
|
||||
runs-on: "ubuntu-latest"
|
||||
if: github.event.pull_request.draft == false
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
jobs:
|
||||
config:
|
||||
runs-on: "ubuntu-latest"
|
||||
if: github.event.pull_request.draft == false
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
"value": "8080"
|
||||
},
|
||||
{
|
||||
"name": "SUPERSET_SECRET_KEY",
|
||||
"value": "super-secret-for-ephemerals"
|
||||
"name": "SUPERSET_SECRET_KEY",
|
||||
"value": "super-secret-for-ephemerals"
|
||||
},
|
||||
{
|
||||
"name": "TALISMAN_ENABLED",
|
||||
"value": "False"
|
||||
}
|
||||
],
|
||||
"mountPoints": [],
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
embedded-sdk-test:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test-load-examples:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -11,6 +11,7 @@ on:
|
||||
|
||||
jobs:
|
||||
cypress-matrix:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
frontend-build:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test-mysql:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -75,6 +76,7 @@ jobs:
|
||||
bash .github/workflows/codecov.sh -c -F python -F mysql
|
||||
|
||||
test-postgres:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -141,6 +143,7 @@ jobs:
|
||||
bash .github/workflows/codecov.sh -c -F python -F postgres
|
||||
|
||||
test-sqlite:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
python-lint:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -49,6 +50,7 @@ jobs:
|
||||
run: pylint -j 0 superset
|
||||
|
||||
pre-commit:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -89,6 +91,7 @@ jobs:
|
||||
run: pre-commit run --all-files
|
||||
|
||||
babel-extract:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test-postgres-presto:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -86,6 +87,7 @@ jobs:
|
||||
bash .github/workflows/codecov.sh -c -F python -F presto
|
||||
|
||||
test-postgres-hive:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
frontend-check:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
@@ -30,6 +31,7 @@ jobs:
|
||||
npm run check-translation
|
||||
|
||||
babel-extract:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
app-checks:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
|
||||
@@ -42,12 +42,13 @@ repos:
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--check-untyped-defs]
|
||||
additional_dependencies:
|
||||
[
|
||||
additional_dependencies: [
|
||||
types-simplejson,
|
||||
types-python-dateutil,
|
||||
types-requests,
|
||||
types-redis,
|
||||
# types-redis 4.6.0.5 is failing mypy
|
||||
# because of https://github.com/python/typeshed/pull/10531
|
||||
types-redis==4.6.0.4,
|
||||
types-pytz,
|
||||
types-croniter,
|
||||
types-PyYAML,
|
||||
|
||||
@@ -83,6 +83,7 @@ enable=
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=
|
||||
no-member, # re-enable once this no longer raises false positives. This will become redundant after the min required version is 3.11
|
||||
missing-docstring,
|
||||
duplicate-code,
|
||||
unspecified-encoding,
|
||||
|
||||
+1098
-19
File diff suppressed because it is too large
Load Diff
+55
-45
@@ -577,6 +577,19 @@ cd superset-frontend
|
||||
npm ci
|
||||
```
|
||||
|
||||
Note that Superset uses [Scarf](https://docs.scarf.sh) to capture telemetry/analytics about versions being installed, including the `scarf-js` npm package. As noted elsewhere in this documentation, Scarf gathers aggregated stats for the sake of security/release strategy, and does not capture/retain PII. [You can read here](https://docs.scarf.sh/package-analytics/) about the package, and various means to opt out of it, but one easy way to opt out is to add this setting in `superset-frontent/package.json`:
|
||||
|
||||
```json
|
||||
// your-package/package.json
|
||||
{
|
||||
// ...
|
||||
"scarfSettings": {
|
||||
"enabled": false
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Build assets
|
||||
|
||||
There are three types of assets you can build:
|
||||
@@ -586,10 +599,13 @@ There are three types of assets you can build:
|
||||
3. `npm run build-instrumented`: instrumented application code for collecting code coverage from Cypress tests
|
||||
|
||||
If this type of error comes while building assets(i.e using above commands):
|
||||
|
||||
```bash
|
||||
Error: You must provide the URL of lib/mappings.wasm by calling SourceMapConsumer.initialize
|
||||
```
|
||||
|
||||
Then put this:
|
||||
|
||||
```bash
|
||||
export NODE_OPTIONS=--no-experimental-fetch
|
||||
```
|
||||
@@ -913,28 +929,22 @@ For debugging locally using VSCode, you can configure a launch configuration fil
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Flask",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "flask",
|
||||
"env": {
|
||||
"FLASK_APP": "superset",
|
||||
"SUPERSET_ENV": "development"
|
||||
},
|
||||
"args": [
|
||||
"run",
|
||||
"-p 8088",
|
||||
"--with-threads",
|
||||
"--reload",
|
||||
"--debugger"
|
||||
],
|
||||
"jinja": true,
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Flask",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "flask",
|
||||
"env": {
|
||||
"FLASK_APP": "superset",
|
||||
"SUPERSET_ENV": "development"
|
||||
},
|
||||
"args": ["run", "-p 8088", "--with-threads", "--reload", "--debugger"],
|
||||
"jinja": true,
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1019,24 +1029,24 @@ You are now ready to attach a debugger to the process. Using VSCode you can conf
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Superset App in Docker Container",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 5678
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"name": "Attach to Superset App in Docker Container",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 5678
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "/app"
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "/app"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1337,7 +1347,7 @@ To do this, you'll need to:
|
||||
but perfect for testing (stores cache in `/tmp`)
|
||||
|
||||
```python
|
||||
from cachelib.file import FileSystemCache
|
||||
from flask_caching.backends.filesystemcache import FileSystemCache
|
||||
RESULTS_BACKEND = FileSystemCache('/tmp/sqllab')
|
||||
```
|
||||
|
||||
@@ -1403,11 +1413,11 @@ Note not all fields are correctly categorized. The fields vary based on visualiz
|
||||
|
||||
### Time
|
||||
|
||||
| Field | Type | Notes |
|
||||
| ------------------ | -------- | ------------------------------------- |
|
||||
| `granularity_sqla` | _string_ | The SQLA **Time Column** widget |
|
||||
| `time_grain_sqla` | _string_ | The SQLA **Time Grain** widget |
|
||||
| `time_range` | _string_ | The **Time range** widget |
|
||||
| Field | Type | Notes |
|
||||
| ------------------ | -------- | ------------------------------- |
|
||||
| `granularity_sqla` | _string_ | The SQLA **Time Column** widget |
|
||||
| `time_grain_sqla` | _string_ | The SQLA **Time Grain** widget |
|
||||
| `time_range` | _string_ | The **Time range** widget |
|
||||
|
||||
### GROUP BY
|
||||
|
||||
|
||||
+10
-2
@@ -25,8 +25,16 @@ ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64}
|
||||
FROM --platform=${BUILDPLATFORM} node:16-slim AS superset-node
|
||||
|
||||
ARG NPM_BUILD_CMD="build"
|
||||
ENV BUILD_CMD=${NPM_BUILD_CMD}
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
RUN apt-get update -q \
|
||||
&& apt-get install -yq --no-install-recommends \
|
||||
python3 \
|
||||
make \
|
||||
gcc \
|
||||
g++
|
||||
|
||||
ENV BUILD_CMD=${NPM_BUILD_CMD} \
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
# NPM ci first, as to NOT invalidate previous steps except for when package.json changes
|
||||
WORKDIR /app/superset-frontend
|
||||
|
||||
@@ -81,9 +81,9 @@
|
||||
|can available domains on Superset|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can request access on Superset|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can dashboard on Superset|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can post on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can expanded on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can delete on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
|
||||
|can post on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|O|O|
|
||||
|can expanded on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|O|O|
|
||||
|can delete on TableSchemaView|:heavy_check_mark:|:heavy_check_mark:|O|O|
|
||||
|can get on TabStateView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
|
||||
|can post on TabStateView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
|
||||
|can delete query on TabStateView|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
|
||||
|
||||
+26
-3
@@ -24,10 +24,25 @@ assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Potential Downtime
|
||||
|
||||
### Other
|
||||
|
||||
## 3.0.3
|
||||
|
||||
- [26034](https://github.com/apache/superset/issues/26034): Fixes a problem where numeric x-axes were being treated as categorical values. As a consequence of that, the way labels are displayed might change given that ECharts has a different treatment for numerical and categorical values. To revert to the old behavior, users need to manually convert numerical columns to text so that they are treated as categories. Check https://github.com/apache/superset/issues/26159 for more details.
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- [25053](https://github.com/apache/superset/pull/25053): Extends the `ab_user.email` column from 64 to 320 characters which has an associated unique key constraint. This will be problematic for MySQL metadata databases which use the InnoDB storage engine with the `innodb_large_prefix` parameter disabled as the key prefix limit is 767 bytes. Enabling said parameter and ensuring that the table uses either the `DYNAMIC` or `COMPRESSED` row format should remedy the problem. See [here](https://dev.mysql.com/doc/refman/5.7/en/innodb-limits.html) for more details.
|
||||
- [24911](https://github.com/apache/superset/pull/24911): Changes the column type from `TEXT` to `MediumText` in table `logs`, potentially requiring a table lock on MySQL dbs or taking some time to complete on large deployments.
|
||||
- [24939](https://github.com/apache/superset/pull/24939): Augments the foreign key constraints for the `embedded_dashboards` table to include an explicit CASCADE ON DELETE to ensure the relevant records are deleted when a dashboard is deleted. Scheduled downtime may be advised.
|
||||
- [24938](https://github.com/apache/superset/pull/24938): Augments the foreign key constraints for the `dashboard_slices` table to include an explicit CASCADE ON DELETE to ensure the relevant records are deleted when a dashboard or slice is deleted. Scheduled downtime may be advised.
|
||||
- [24628]https://github.com/apache/superset/pull/24628): Augments the foreign key constraints for the `dashboard_owner`, `report_schedule_owner`, and `slice_owner` tables to include an explicit CASCADE ON DELETE to ensure the relevant ownership records are deleted when a dataset is deleted. Scheduled downtime may be advised.
|
||||
- [24488](https://github.com/apache/superset/pull/24488): Augments the foreign key constraints for the `sql_metrics`, `sqlatable_user`, and `table_columns` tables to include an explicit CASCADE ON DELETE to ensure the relevant records are deleted when a dataset is deleted. Scheduled downtime may be advised.
|
||||
- [24488](https://github.com/apache/superset/pull/24488): Augments the foreign key constraints for the `sql_metrics`, `sqlatable_user`, and `table_columns` tables which reference the `tables` table to include an explicit CASCADE ON DELETE to ensure the relevant records are deleted when a dataset is deleted. Scheduled downtime may be advised.
|
||||
- [24335](https://github.com/apache/superset/pull/24335): Removed deprecated API `/superset/filter/<datasource_type>/<int:datasource_id>/<column>/`
|
||||
- [24185](https://github.com/apache/superset/pull/24185): `/api/v1/database/test_connection` and `api/v1/database/validate_parameters` permissions changed from `can_read` to `can_write`. Only Admin user's have access.
|
||||
- [24232](https://github.com/apache/superset/pull/24232): Enables ENABLE_TEMPLATE_REMOVE_FILTERS, DRILL_TO_DETAIL, DASHBOARD_CROSS_FILTERS by default, marks VERSIONED_EXPORT and ENABLE_TEMPLATE_REMOVE_FILTERS as deprecated.
|
||||
- [23652](https://github.com/apache/superset/pull/23652): Enables GENERIC_CHART_AXES feature flag by default.
|
||||
- [23226](https://github.com/apache/superset/pull/23226): Migrated endpoint `/estimate_query_cost/<int:database_id>` to `/api/v1/sqllab/estimate/`. Corresponding permissions are can estimate query cost on SQLLab. Make sure you add/replace the necessary permissions on any custom roles you may have.
|
||||
@@ -38,9 +53,11 @@ assists people when migrating to a new version.
|
||||
make it more clear which envrionment your are in.
|
||||
`SUPERSET_ENV=production` and `SUPERSET_ENV=development` are the two
|
||||
supported switches based on the default config.
|
||||
- [19242](https://github.com/apache/superset/pull/19242): Adhoc subqueries are now disabled by default for security reasons. To enable them, set the feature flag `ALLOW_ADHOC_SUBQUERY` to `True`.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [24686]https://github.com/apache/superset/pull/24686): All dataset's custom explore_url are handled as relative URLs on the frontend, behaviour controlled by PREVENT_UNSAFE_DEFAULT_URLS_ON_DATASET.
|
||||
- [24262](https://github.com/apache/superset/pull/24262): Enabled `TALISMAN_ENABLED` flag by default and provided stricter default Content Security Policy
|
||||
- [24415](https://github.com/apache/superset/pull/24415): Removed the obsolete Druid NoSQL REGEX operator.
|
||||
- [24423](https://github.com/apache/superset/pull/24423): Removed deprecated APIs `/superset/slice_json/...`, `/superset/annotation_json/...`
|
||||
@@ -71,10 +88,16 @@ assists people when migrating to a new version.
|
||||
- [23663](https://github.com/apache/superset/pull/23663): Removes deprecated feature flags `ALLOW_DASHBOARD_DOMAIN_SHARDING`, `DISPLAY_MARKDOWN_HTML`, and `FORCE_DATABASE_CONNECTIONS_SSL`.
|
||||
- [22325](https://github.com/apache/superset/pull/22325): "RLS_FORM_QUERY_REL_FIELDS" is replaced by "RLS_BASE_RELATED_FIELD_FILTERS" feature flag. Its value format stays same.
|
||||
|
||||
### Potential Downtime
|
||||
## 2.1.1
|
||||
|
||||
- [24185](https://github.com/apache/superset/pull/24185): `/api/v1/database/test_connection` and `api/v1/database/validate_parameters` permissions changed from `can_read` to `can_write`. Only Admin user's have access.
|
||||
- [24256](https://github.com/apache/superset/pull/24256): `Flask-Login` session validation is now set to `strong` by default. Previous setting was `basic`.
|
||||
|
||||
### Other
|
||||
|
||||
- [24982](https://github.com/apache/superset/pull/24982): By default, physical datasets on Oracle-like dialects like Snowflake will now use denormalized column names. However, existing datasets won't be affected. To change this behavior, the "Advanced" section on the dataset modal has a "Normalize column names" flag which can be changed to change this behavior.
|
||||
- [23888](https://github.com/apache/superset/pull/23888): Database Migration for json serialization instead of pickle should upgrade/downgrade correctly when bumping to/from this patch version
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- [22809](https://github.com/apache/superset/pull/22809): Migrated endpoint `/superset/sql_json` and `/superset/results/` to `/api/v1/sqllab/execute/` and `/api/v1/sqllab/results/` respectively. Corresponding permissions are `can sql_json on Superset` to `can execute on SQLLab`, `can results on Superset` to `can results on SQLLab`. Make sure you add/replace the necessary permissions on any custom roles you may have.
|
||||
|
||||
@@ -18,8 +18,8 @@ x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset
|
||||
x-superset-depends-on: &superset-depends-on
|
||||
- db
|
||||
- redis
|
||||
x-superset-volumes: &superset-volumes
|
||||
# /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
||||
x-superset-volumes:
|
||||
&superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
||||
- ./docker:/app/docker
|
||||
- superset_home:/app/superset_home
|
||||
|
||||
@@ -39,6 +39,7 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db_home:/var/lib/postgresql/data
|
||||
- ./docker/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
|
||||
|
||||
superset:
|
||||
env_file: docker/.env-non-dev
|
||||
@@ -73,7 +74,11 @@ services:
|
||||
user: "root"
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME"]
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME",
|
||||
]
|
||||
|
||||
superset-worker-beat:
|
||||
image: *superset-image
|
||||
|
||||
@@ -47,6 +47,7 @@ services:
|
||||
- "127.0.0.1:5432:5432"
|
||||
volumes:
|
||||
- db_home:/var/lib/postgresql/data
|
||||
- ./docker/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
|
||||
|
||||
superset:
|
||||
env_file: docker/.env
|
||||
|
||||
@@ -22,6 +22,12 @@ DATABASE_HOST=db
|
||||
DATABASE_PASSWORD=superset
|
||||
DATABASE_USER=superset
|
||||
|
||||
EXAMPLES_DB=examples
|
||||
EXAMPLES_HOST=db
|
||||
EXAMPLES_USER=examples
|
||||
EXAMPLES_PASSWORD=examples
|
||||
EXAMPLES_PORT=5432
|
||||
|
||||
# database engine specific environment variables
|
||||
# change the below if you prefer another database engine
|
||||
DATABASE_PORT=5432
|
||||
|
||||
+8
-2
@@ -21,11 +21,17 @@ DATABASE_DB=superset
|
||||
DATABASE_HOST=db
|
||||
DATABASE_PASSWORD=superset
|
||||
DATABASE_USER=superset
|
||||
DATABASE_PORT=5432
|
||||
DATABASE_DIALECT=postgresql
|
||||
|
||||
EXAMPLES_DB=examples
|
||||
EXAMPLES_HOST=db
|
||||
EXAMPLES_USER=examples
|
||||
EXAMPLES_PASSWORD=examples
|
||||
EXAMPLES_PORT=5432
|
||||
|
||||
# database engine specific environment variables
|
||||
# change the below if you prefer another database engine
|
||||
DATABASE_PORT=5432
|
||||
DATABASE_DIALECT=postgresql
|
||||
POSTGRES_DB=superset
|
||||
POSTGRES_USER=superset
|
||||
POSTGRES_PASSWORD=superset
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# ------------------------------------------------------------------------
|
||||
# Creates the examples database and repective user. This database location
|
||||
# and access credentials are defined on the environment variables
|
||||
# ------------------------------------------------------------------------
|
||||
set -e
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" <<-EOSQL
|
||||
CREATE USER ${EXAMPLES_USER} WITH PASSWORD '${EXAMPLES_PASSWORD}';
|
||||
CREATE DATABASE ${EXAMPLES_DB};
|
||||
GRANT ALL PRIVILEGES ON DATABASE ${EXAMPLES_DB} TO ${EXAMPLES_USER};
|
||||
EOSQL
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" -d "${EXAMPLES_DB}" <<-EOSQL
|
||||
GRANT ALL ON SCHEMA public TO ${EXAMPLES_USER};
|
||||
EOSQL
|
||||
@@ -22,49 +22,42 @@
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from cachelib.file import FileSystemCache
|
||||
from celery.schedules import crontab
|
||||
from flask_caching.backends.filesystemcache import FileSystemCache
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
DATABASE_DIALECT = os.getenv("DATABASE_DIALECT")
|
||||
DATABASE_USER = os.getenv("DATABASE_USER")
|
||||
DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD")
|
||||
DATABASE_HOST = os.getenv("DATABASE_HOST")
|
||||
DATABASE_PORT = os.getenv("DATABASE_PORT")
|
||||
DATABASE_DB = os.getenv("DATABASE_DB")
|
||||
|
||||
def get_env_variable(var_name: str, default: Optional[str] = None) -> str:
|
||||
"""Get the environment variable or raise exception."""
|
||||
try:
|
||||
return os.environ[var_name]
|
||||
except KeyError:
|
||||
if default is not None:
|
||||
return default
|
||||
else:
|
||||
error_msg = "The environment variable {} was missing, abort...".format(
|
||||
var_name
|
||||
)
|
||||
raise OSError(error_msg)
|
||||
|
||||
|
||||
DATABASE_DIALECT = get_env_variable("DATABASE_DIALECT")
|
||||
DATABASE_USER = get_env_variable("DATABASE_USER")
|
||||
DATABASE_PASSWORD = get_env_variable("DATABASE_PASSWORD")
|
||||
DATABASE_HOST = get_env_variable("DATABASE_HOST")
|
||||
DATABASE_PORT = get_env_variable("DATABASE_PORT")
|
||||
DATABASE_DB = get_env_variable("DATABASE_DB")
|
||||
EXAMPLES_USER = os.getenv("EXAMPLES_USER")
|
||||
EXAMPLES_PASSWORD = os.getenv("EXAMPLES_PASSWORD")
|
||||
EXAMPLES_HOST = os.getenv("EXAMPLES_HOST")
|
||||
EXAMPLES_PORT = os.getenv("EXAMPLES_PORT")
|
||||
EXAMPLES_DB = os.getenv("EXAMPLES_DB")
|
||||
|
||||
# The SQLAlchemy connection string.
|
||||
SQLALCHEMY_DATABASE_URI = "{}://{}:{}@{}:{}/{}".format(
|
||||
DATABASE_DIALECT,
|
||||
DATABASE_USER,
|
||||
DATABASE_PASSWORD,
|
||||
DATABASE_HOST,
|
||||
DATABASE_PORT,
|
||||
DATABASE_DB,
|
||||
SQLALCHEMY_DATABASE_URI = (
|
||||
f"{DATABASE_DIALECT}://"
|
||||
f"{DATABASE_USER}:{DATABASE_PASSWORD}@"
|
||||
f"{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_DB}"
|
||||
)
|
||||
|
||||
REDIS_HOST = get_env_variable("REDIS_HOST")
|
||||
REDIS_PORT = get_env_variable("REDIS_PORT")
|
||||
REDIS_CELERY_DB = get_env_variable("REDIS_CELERY_DB", "0")
|
||||
REDIS_RESULTS_DB = get_env_variable("REDIS_RESULTS_DB", "1")
|
||||
SQLALCHEMY_EXAMPLES_URI = (
|
||||
f"{DATABASE_DIALECT}://"
|
||||
f"{EXAMPLES_USER}:{EXAMPLES_PASSWORD}@"
|
||||
f"{EXAMPLES_HOST}:{EXAMPLES_PORT}/{EXAMPLES_DB}"
|
||||
)
|
||||
|
||||
REDIS_HOST = os.getenv("REDIS_HOST", "redis")
|
||||
REDIS_PORT = os.getenv("REDIS_PORT", "6379")
|
||||
REDIS_CELERY_DB = os.getenv("REDIS_CELERY_DB", "0")
|
||||
REDIS_RESULTS_DB = os.getenv("REDIS_RESULTS_DB", "1")
|
||||
|
||||
RESULTS_BACKEND = FileSystemCache("/app/superset_home/sqllab")
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ The Superset project is always happy to review proposals for new high quality vi
|
||||
plugins. However, for highly custom viz types it is recommended to maintain a fork
|
||||
of Superset, and add the custom built viz plugins by hand.
|
||||
|
||||
**Note:** Additional community-generated resources about creating and deploying custom visualization plugins can be found on the [Superset Wiki](https://github.com/apache/superset/wiki/Community-Resource-Library#creating-custom-data-visualizations)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
In order to create a new viz plugin, you need the following:
|
||||
|
||||
@@ -87,6 +87,3 @@ You should then be able to connect to your BigQuery datasets.
|
||||
|
||||
To be able to upload CSV or Excel files to BigQuery in Superset, you'll need to also add the
|
||||
[pandas_gbq](https://github.com/pydata/pandas-gbq) library.
|
||||
|
||||
Currently, Google BigQuery python sdk is not compatible with `gevent`, due to some dynamic monkeypatching on python core library by `gevent`.
|
||||
So, when you deploy Superset with `gunicorn` server, you have to use worker type except `gevent`.
|
||||
|
||||
@@ -10,13 +10,13 @@ version: 1
|
||||
To use ClickHouse with Superset, you will need to add the following Python library:
|
||||
|
||||
```
|
||||
clickhouse-connect>=0.4.1
|
||||
clickhouse-connect>=0.6.8
|
||||
```
|
||||
|
||||
If running Superset using Docker Compose, add the following to your `./docker/requirements-local.txt` file:
|
||||
|
||||
```
|
||||
clickhouse-connect>=0.4.1
|
||||
clickhouse-connect>=0.6.8
|
||||
```
|
||||
|
||||
The recommended connector library for ClickHouse is
|
||||
|
||||
@@ -45,9 +45,9 @@ A common pattern is to use environment variables to make secrets available.
|
||||
|
||||
```python
|
||||
def example_password_as_env_var(url):
|
||||
# assuming the uri looks like
|
||||
# mysql://localhost?superset_user:{SUPERSET_PASSWORD}
|
||||
return url.password.format(**os.environ)
|
||||
# assuming the uri looks like
|
||||
# mysql://localhost?superset_user:{SUPERSET_PASSWORD}
|
||||
return url.password.format(os.environ)
|
||||
|
||||
SQLALCHEMY_CUSTOM_PASSWORD_STORE = example_password_as_env_var
|
||||
```
|
||||
|
||||
@@ -22,47 +22,47 @@ as well as the packages needed to connect to the databases you want to access th
|
||||
|
||||
Some of the recommended packages are shown below. Please refer to [setup.py](https://github.com/apache/superset/blob/master/setup.py) for the versions that are compatible with Superset.
|
||||
|
||||
| Database | PyPI package | Connection String |
|
||||
| --------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| [Amazon Athena](/docs/databases/athena) | `pip install pyathena[pandas]` , `pip install PyAthenaJDBC` | `awsathena+rest://{aws_access_key_id}:{aws_secret_access_key}@athena.{region_name}.amazonaws.com/{ ` |
|
||||
| [Amazon DynamoDB](/docs/databases/dynamodb) | `pip install pydynamodb` | `dynamodb://{access_key_id}:{secret_access_key}@dynamodb.{region_name}.amazonaws.com?connector=superset` |
|
||||
| [Amazon Redshift](/docs/databases/redshift) | `pip install sqlalchemy-redshift` | ` redshift+psycopg2://<userName>:<DBPassword>@<AWS End Point>:5439/<Database Name>` |
|
||||
| [Apache Drill](/docs/databases/drill) | `pip install sqlalchemy-drill` | `drill+sadrill:// For JDBC drill+jdbc://` |
|
||||
| [Apache Druid](/docs/databases/druid) | `pip install pydruid` | `druid://<User>:<password>@<Host>:<Port-default-9088>/druid/v2/sql` |
|
||||
| [Apache Hive](/docs/databases/hive) | `pip install pyhive` | `hive://hive@{hostname}:{port}/{database}` |
|
||||
| [Apache Impala](/docs/databases/impala) | `pip install impyla` | `impala://{hostname}:{port}/{database}` |
|
||||
| [Apache Kylin](/docs/databases/kylin) | `pip install kylinpy` | `kylin://<username>:<password>@<hostname>:<port>/<project>?<param1>=<value1>&<param2>=<value2>` |
|
||||
| [Apache Pinot](/docs/databases/pinot) | `pip install pinotdb` | `pinot://BROKER:5436/query?server=http://CONTROLLER:5983/` |
|
||||
| [Apache Solr](/docs/databases/solr) | `pip install sqlalchemy-solr` | `solr://{username}:{password}@{hostname}:{port}/{server_path}/{collection}` |
|
||||
| [Apache Spark SQL](/docs/databases/spark-sql) | `pip install pyhive` | `hive://hive@{hostname}:{port}/{database}` |
|
||||
| [Ascend.io](/docs/databases/ascend) | `pip install impyla` | `ascend://{username}:{password}@{hostname}:{port}/{database}?auth_mechanism=PLAIN;use_ssl=true` |
|
||||
| [Azure MS SQL](/docs/databases/sql-server) | `pip install pymssql` | `mssql+pymssql://UserName@presetSQL:TestPassword@presetSQL.database.windows.net:1433/TestSchema` |
|
||||
| [Big Query](/docs/databases/bigquery) | `pip install sqlalchemy-bigquery` | `bigquery://{project_id}` |
|
||||
| [ClickHouse](/docs/databases/clickhouse) | `pip install clickhouse-connect` | `clickhousedb://{username}:{password}@{hostname}:{port}/{database}` |
|
||||
| [CockroachDB](/docs/databases/cockroachdb) | `pip install cockroachdb` | `cockroachdb://root@{hostname}:{port}/{database}?sslmode=disable` |
|
||||
| [Dremio](/docs/databases/dremio) | `pip install sqlalchemy_dremio` | `dremio://user:pwd@host:31010/` |
|
||||
| [Elasticsearch](/docs/databases/elasticsearch) | `pip install elasticsearch-dbapi` | `elasticsearch+http://{user}:{password}@{host}:9200/` |
|
||||
| [Exasol](/docs/databases/exasol) | `pip install sqlalchemy-exasol` | `exa+pyodbc://{username}:{password}@{hostname}:{port}/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC` |
|
||||
| [Google Sheets](/docs/databases/google-sheets) | `pip install shillelagh[gsheetsapi]` | `gsheets://` |
|
||||
| [Firebolt](/docs/databases/firebolt) | `pip install firebolt-sqlalchemy` | `firebolt://{username}:{password}@{database} or firebolt://{username}:{password}@{database}/{engine_name}` |
|
||||
| [Hologres](/docs/databases/hologres) | `pip install psycopg2` | `postgresql+psycopg2://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [IBM Db2](/docs/databases/ibm-db2) | `pip install ibm_db_sa` | `db2+ibm_db://` |
|
||||
| [IBM Netezza Performance Server](/docs/databases/netezza) | `pip install nzalchemy` | `netezza+nzpy://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [MySQL](/docs/databases/mysql) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Oracle](/docs/databases/oracle) | `pip install cx_Oracle` | `oracle://` |
|
||||
| [PostgreSQL](/docs/databases/postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Presto](/docs/databases/presto) | `pip install pyhive` | `presto://` |
|
||||
| [Rockset](/docs/databases/rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>`
|
||||
| [SAP Hana](/docs/databases/hana) | `pip install hdbcli sqlalchemy-hana or pip install apache-superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [StarRocks](/docs/databases/starrocks) | `pip install starrocks` | `starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>` |
|
||||
| [Snowflake](/docs/databases/snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` |
|
||||
| SQLite | No additional library needed | `sqlite://` |
|
||||
| [SQL Server](/docs/databases/sql-server) | `pip install pymssql` | `mssql+pymssql://` |
|
||||
| [Teradata](/docs/databases/teradata) | `pip install teradatasqlalchemy` | `teradatasql://{user}:{password}@{host}` |
|
||||
| [TimescaleDB](/docs/databases/timescaledb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>:<Port>/<Database Name>` |
|
||||
| [Trino](/docs/databases/trino) | `pip install trino` | `trino://{username}:{password}@{hostname}:{port}/{catalog}` |
|
||||
| [Vertica](/docs/databases/vertica) | `pip install sqlalchemy-vertica-python` | `vertica+vertica_python://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [YugabyteDB](/docs/databases/yugabytedb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| Database | PyPI package | Connection String |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| [Amazon Athena](/docs/databases/athena) | `pip install pyathena[pandas]` , `pip install PyAthenaJDBC` | `awsathena+rest://{aws_access_key_id}:{aws_secret_access_key}@athena.{region_name}.amazonaws.com/{ ` |
|
||||
| [Amazon DynamoDB](/docs/databases/dynamodb) | `pip install pydynamodb` | `dynamodb://{access_key_id}:{secret_access_key}@dynamodb.{region_name}.amazonaws.com?connector=superset` |
|
||||
| [Amazon Redshift](/docs/databases/redshift) | `pip install sqlalchemy-redshift` | ` redshift+psycopg2://<userName>:<DBPassword>@<AWS End Point>:5439/<Database Name>` |
|
||||
| [Apache Drill](/docs/databases/drill) | `pip install sqlalchemy-drill` | `drill+sadrill:// For JDBC drill+jdbc://` |
|
||||
| [Apache Druid](/docs/databases/druid) | `pip install pydruid` | `druid://<User>:<password>@<Host>:<Port-default-9088>/druid/v2/sql` |
|
||||
| [Apache Hive](/docs/databases/hive) | `pip install pyhive` | `hive://hive@{hostname}:{port}/{database}` |
|
||||
| [Apache Impala](/docs/databases/impala) | `pip install impyla` | `impala://{hostname}:{port}/{database}` |
|
||||
| [Apache Kylin](/docs/databases/kylin) | `pip install kylinpy` | `kylin://<username>:<password>@<hostname>:<port>/<project>?<param1>=<value1>&<param2>=<value2>` |
|
||||
| [Apache Pinot](/docs/databases/pinot) | `pip install pinotdb` | `pinot://BROKER:5436/query?server=http://CONTROLLER:5983/` |
|
||||
| [Apache Solr](/docs/databases/solr) | `pip install sqlalchemy-solr` | `solr://{username}:{password}@{hostname}:{port}/{server_path}/{collection}` |
|
||||
| [Apache Spark SQL](/docs/databases/spark-sql) | `pip install pyhive` | `hive://hive@{hostname}:{port}/{database}` |
|
||||
| [Ascend.io](/docs/databases/ascend) | `pip install impyla` | `ascend://{username}:{password}@{hostname}:{port}/{database}?auth_mechanism=PLAIN;use_ssl=true` |
|
||||
| [Azure MS SQL](/docs/databases/sql-server) | `pip install pymssql` | `mssql+pymssql://UserName@presetSQL:TestPassword@presetSQL.database.windows.net:1433/TestSchema` |
|
||||
| [Big Query](/docs/databases/bigquery) | `pip install sqlalchemy-bigquery` | `bigquery://{project_id}` |
|
||||
| [ClickHouse](/docs/databases/clickhouse) | `pip install clickhouse-connect` | `clickhousedb://{username}:{password}@{hostname}:{port}/{database}` |
|
||||
| [CockroachDB](/docs/databases/cockroachdb) | `pip install cockroachdb` | `cockroachdb://root@{hostname}:{port}/{database}?sslmode=disable` |
|
||||
| [Dremio](/docs/databases/dremio) | `pip install sqlalchemy_dremio` | `dremio://user:pwd@host:31010/` |
|
||||
| [Elasticsearch](/docs/databases/elasticsearch) | `pip install elasticsearch-dbapi` | `elasticsearch+http://{user}:{password}@{host}:9200/` |
|
||||
| [Exasol](/docs/databases/exasol) | `pip install sqlalchemy-exasol` | `exa+pyodbc://{username}:{password}@{hostname}:{port}/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC` |
|
||||
| [Google Sheets](/docs/databases/google-sheets) | `pip install shillelagh[gsheetsapi]` | `gsheets://` |
|
||||
| [Firebolt](/docs/databases/firebolt) | `pip install firebolt-sqlalchemy` | `firebolt://{username}:{password}@{database} or firebolt://{username}:{password}@{database}/{engine_name}` |
|
||||
| [Hologres](/docs/databases/hologres) | `pip install psycopg2` | `postgresql+psycopg2://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [IBM Db2](/docs/databases/ibm-db2) | `pip install ibm_db_sa` | `db2+ibm_db://` |
|
||||
| [IBM Netezza Performance Server](/docs/databases/netezza) | `pip install nzalchemy` | `netezza+nzpy://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [MySQL](/docs/databases/mysql) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Oracle](/docs/databases/oracle) | `pip install cx_Oracle` | `oracle://` |
|
||||
| [PostgreSQL](/docs/databases/postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Trino](/docs/databases/trino) | `pip install trino` | `trino://{username}:{password}@{hostname}:{port}/{catalog}` |
|
||||
| [Presto](/docs/databases/presto) | `pip install pyhive` | `presto://` |
|
||||
| [SAP Hana](/docs/databases/hana) | `pip install hdbcli sqlalchemy-hana or pip install apache-superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [StarRocks](/docs/databases/starrocks) | `pip install starrocks` | `starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>` |
|
||||
| [Snowflake](/docs/databases/snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` |
|
||||
| SQLite | No additional library needed | `sqlite://path/to/file.db?check_same_thread=false` |
|
||||
| [SQL Server](/docs/databases/sql-server) | `pip install pymssql` | `mssql+pymssql://` |
|
||||
| [Teradata](/docs/databases/teradata) | `pip install teradatasqlalchemy` | `teradatasql://{user}:{password}@{host}` |
|
||||
| [TimescaleDB](/docs/databases/timescaledb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>:<Port>/<Database Name>` |
|
||||
| [Vertica](/docs/databases/vertica) | `pip install sqlalchemy-vertica-python` | `vertica+vertica_python://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [YugabyteDB](/docs/databases/yugabytedb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
|
||||
---
|
||||
|
||||
Note that many other databases are supported, the main criteria being the existence of a functional
|
||||
|
||||
@@ -10,16 +10,7 @@ version: 1
|
||||
The connection string for Rockset is:
|
||||
|
||||
```
|
||||
rockset://{api key}:@{api server}
|
||||
```
|
||||
|
||||
Get your API key from the [Rockset console](https://console.rockset.com/apikeys).
|
||||
Find your API server from the [API reference](https://rockset.com/docs/rest-api/#introduction). Omit the `https://` portion of the URL.
|
||||
|
||||
To target to a specific virtual instance, use this URI format:
|
||||
|
||||
```
|
||||
rockset://{api key}:@{api server}/{VI ID}
|
||||
rockset://apikey:{your-apikey}@api.rs2.usw2.rockset.com/
|
||||
```
|
||||
|
||||
For more complete instructions, we recommend the [Rockset documentation](https://docs.rockset.com/apache-superset/).
|
||||
|
||||
@@ -168,7 +168,7 @@ Another workaround is to change where superset stores the sqlite database by add
|
||||
`superset_config.py`:
|
||||
|
||||
```
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////new/location/superset.db'
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////new/location/superset.db?check_same_thread=false'
|
||||
```
|
||||
|
||||
You can read more about customizing Superset using the configuration file
|
||||
|
||||
@@ -66,7 +66,7 @@ celery --app=superset.tasks.celery_app:app beat
|
||||
```
|
||||
|
||||
To setup a result backend, you need to pass an instance of a derivative of from
|
||||
cachelib.base.BaseCache to the RESULTS_BACKEND configuration key in your superset_config.py. You can
|
||||
from flask_caching.backends.base import BaseCache to the RESULTS_BACKEND configuration key in your superset_config.py. You can
|
||||
use Memcached, Redis, S3 (https://pypi.python.org/pypi/s3werkzeugcache), memory or the file system
|
||||
(in a single server-type setup or for testing), or to write your own caching interface. Your
|
||||
`superset_config.py` may look something like:
|
||||
@@ -79,7 +79,7 @@ S3_CACHE_KEY_PREFIX = 'sql_lab_result'
|
||||
RESULTS_BACKEND = S3Cache(S3_CACHE_BUCKET, S3_CACHE_KEY_PREFIX)
|
||||
|
||||
# On Redis
|
||||
from cachelib.redis import RedisCache
|
||||
from flask_caching.backends.rediscache import RedisCache
|
||||
RESULTS_BACKEND = RedisCache(
|
||||
host='localhost', port=6379, key_prefix='superset_results')
|
||||
```
|
||||
|
||||
@@ -13,6 +13,7 @@ To configure your application, you need to create a file `superset_config.py` an
|
||||
`PYTHONPATH`. If your application was installed using docker-compose an alternative configuration is required. See [https://github.com/apache/superset/tree/master/docker#readme](https://github.com/apache/superset/tree/master/docker#readme) for details.
|
||||
|
||||
The following is an example of just a few of the parameters you can set in your `superset_config.py` file:
|
||||
|
||||
```
|
||||
# Superset specific config
|
||||
ROW_LIMIT = 5000
|
||||
@@ -31,7 +32,9 @@ SECRET_KEY = 'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY'
|
||||
# superset metadata (slices, connections, tables, dashboards, ...).
|
||||
# Note that the connection information to connect to the datasources
|
||||
# you want to explore are managed directly in the web UI
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////path/to/superset.db'
|
||||
# The check_same_thread=false property ensures the sqlite client does not attempt
|
||||
# to enforce single-threaded access, which may be problematic in some edge cases
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////path/to/superset.db?check_same_thread=false'
|
||||
|
||||
# Flask-WTF flag for CSRF
|
||||
WTF_CSRF_ENABLED = True
|
||||
@@ -72,7 +75,7 @@ WTF_CSRF_EXEMPT_LIST = [‘’]
|
||||
|
||||
#### Adding an initial SECRET_KEY
|
||||
|
||||
Superset requires a user-specified SECRET_KEY to start up. This requirement was [added in version 2.1.0 to force secure configurations](https://preset.io/blog/superset-security-update-default-secret_key-vulnerability/). Add a strong SECRET_KEY to your `superset_config.py` file like:
|
||||
Superset requires a user-specified SECRET_KEY to start up. This requirement was [added in version 2.1.0 to force secure configurations](https://preset.io/blog/superset-security-update-default-secret_key-vulnerability/). Add a strong SECRET_KEY to your `superset_config.py` file like:
|
||||
|
||||
```python
|
||||
SECRET_KEY = 'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY'
|
||||
@@ -83,7 +86,7 @@ You can generate a strong secure key with `openssl rand -base64 42`.
|
||||
#### Rotating to a newer SECRET_KEY
|
||||
|
||||
If you wish to change your existing SECRET_KEY, add the existing SECRET_KEY to your `superset_config.py` file as
|
||||
`PREVIOUS_SECRET_KEY = `and provide your new key as `SECRET_KEY =`. You can find your current SECRET_KEY with these
|
||||
`PREVIOUS_SECRET_KEY = `and provide your new key as `SECRET_KEY =`. You can find your current SECRET_KEY with these
|
||||
commands - if running Superset with Docker, execute from within the Superset application container:
|
||||
|
||||
```python
|
||||
@@ -103,23 +106,21 @@ database engine on a separate host or container.
|
||||
|
||||
Superset supports the following database engines/versions:
|
||||
|
||||
| Database Engine | Supported Versions |
|
||||
| --------------------------------------------------------- | --------------------------------- |
|
||||
| [PostgreSQL](https://www.postgresql.org/) | 10.X, 11.X, 12.X, 13.X, 14.X |
|
||||
| [MySQL](https://www.mysql.com/) | 5.X |
|
||||
|
||||
| Database Engine | Supported Versions |
|
||||
| ----------------------------------------- | ---------------------------------- |
|
||||
| [PostgreSQL](https://www.postgresql.org/) | 10.X, 11.X, 12.X, 13.X, 14.X, 15.X |
|
||||
| [MySQL](https://www.mysql.com/) | 5.7, 8.X |
|
||||
|
||||
Use the following database drivers and connection strings:
|
||||
|
||||
| Database | PyPI package | Connection String |
|
||||
| ----------------------------------------- | --------------------------------- | ------------------------------------------------------------------------ |
|
||||
| [PostgreSQL](https://www.postgresql.org/) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [MySQL](https://www.mysql.com/) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| Database | PyPI package | Connection String |
|
||||
| ----------------------------------------- | ------------------------- | ---------------------------------------------------------------------- |
|
||||
| [PostgreSQL](https://www.postgresql.org/) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [MySQL](https://www.mysql.com/) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
|
||||
To configure Superset metastore set `SQLALCHEMY_DATABASE_URI` config key on `superset_config`
|
||||
to the appropriate connection string.
|
||||
|
||||
|
||||
### Running on a WSGI HTTP Server
|
||||
|
||||
While you can run Superset on NGINX or Apache, we recommend using Gunicorn in async mode. This
|
||||
@@ -146,9 +147,6 @@ for production use._
|
||||
If you're not using Gunicorn, you may want to disable the use of `flask-compress` by setting
|
||||
`COMPRESS_REGISTER = False` in your `superset_config.py`.
|
||||
|
||||
Currently, Google BigQuery python sdk is not compatible with `gevent`, due to some dynamic monkeypatching on python core library by `gevent`.
|
||||
So, when you use `BigQuery` datasource on Superset, you have to use `gunicorn` worker type except `gevent`.
|
||||
|
||||
### Configuration Behind a Load Balancer
|
||||
|
||||
If you are running superset behind a load balancer or reverse proxy (e.g. NGINX or ELB on AWS), you
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Security",
|
||||
"position": 10
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: CVEs by release
|
||||
hide_title: true
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
#### Version 2.1.0
|
||||
|
||||
| CVE | Title | Affected |
|
||||
| :------------- | :---------------------------------------------------------------------- | -----------------:|
|
||||
| CVE-2023-25504 | Possible SSRF on import datasets | <= 2.1.0 |
|
||||
| CVE-2023-27524 | Session validation vulnerability when using provided default SECRET_KEY | <= 2.1.0 |
|
||||
| CVE-2023-27525 | Incorrect default permissions for Gamma role | <= 2.1.0 |
|
||||
| CVE-2023-30776 | Database connection password leak | <= 2.1.0 |
|
||||
|
||||
|
||||
#### Version 2.0.1
|
||||
|
||||
| CVE | Title | Affected |
|
||||
| :------------- | :---------------------------------------------------------- | -----------------:|
|
||||
| CVE-2022-41703 | SQL injection vulnerability in adhoc clauses | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-43717 | Cross-Site Scripting on dashboards | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-43718 | Cross-Site Scripting vulnerability on upload forms | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-43719 | Cross Site Request Forgery (CSRF) on accept, request access | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-43720 | Improper rendering of user input | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-43721 | Open Redirect Vulnerability | < 2.0.1 or <1.5.2 |
|
||||
| CVE-2022-45438 | Dashboard metadata information leak | < 2.0.1 or <1.5.2 |
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Security
|
||||
title: Role based Access
|
||||
hide_title: true
|
||||
sidebar_position: 10
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
### Roles
|
||||
|
||||
Security in Superset is handled by Flask AppBuilder (FAB), an application development framework
|
||||
Authentication and authorization in Superset is handled by Flask AppBuilder (FAB), an application development framework
|
||||
built on top of Flask. FAB provides authentication, user management, permissions and roles.
|
||||
Please read its [Security documentation](https://flask-appbuilder.readthedocs.io/en/latest/security.html).
|
||||
|
||||
@@ -157,6 +157,34 @@ HTTPS if the cookie is marked “secure”. The application must be served over
|
||||
|
||||
`PERMANENT_SESSION_LIFETIME`: (default: "31 days") The lifetime of a permanent session as a `datetime.timedelta` object.
|
||||
|
||||
#### Switching to server side sessions
|
||||
|
||||
Server side sessions offer benefits over client side sessions on security and performance.
|
||||
By enabling server side sessions, the session data is stored server side and only a session ID
|
||||
is sent to the client. When a user logs in, a session is created server side and the session ID
|
||||
is sent to the client in a cookie. The client will send the session ID with each request and the
|
||||
server will use it to retrieve the session data.
|
||||
On logout, the session is destroyed server side and the session cookie is deleted on the client side.
|
||||
This reduces the risk for replay attacks and session hijacking.
|
||||
|
||||
Superset uses [Flask-Session](https://flask-session.readthedocs.io/en/latest/) to manage server side sessions.
|
||||
To enable this extension you have to set:
|
||||
|
||||
```python
|
||||
SESSION_SERVER_SIDE = True
|
||||
```
|
||||
|
||||
Flask-Session offers multiple backend session interfaces for Flask, here's an example for Redis:
|
||||
|
||||
```python
|
||||
from redis import Redis
|
||||
|
||||
SESSION_TYPE = "redis"
|
||||
SESSION_REDIS = Redis(host="redis", port=6379, db=0)
|
||||
# sign the session cookie sid
|
||||
SESSION_USE_SIGNER = True
|
||||
```
|
||||
|
||||
### Content Security Policy (CSP)
|
||||
|
||||
Superset uses the [Talisman](https://pypi.org/project/flask-talisman/) extension to enable implementation of a
|
||||
@@ -177,8 +205,8 @@ It's extremely important to correctly configure a Content Security Policy when d
|
||||
prevent many types of attacks. Superset provides two variables in `config.py` for deploying a CSP:
|
||||
|
||||
- `TALISMAN_ENABLED` defaults to `True`; set this to `False` in order to disable CSP
|
||||
- `TALISMAN_CONFIG` holds the actual the policy definition (*see example below*) as well as any
|
||||
other arguments to be passed to Talisman.
|
||||
- `TALISMAN_CONFIG` holds the actual the policy definition (_see example below_) as well as any
|
||||
other arguments to be passed to Talisman.
|
||||
|
||||
When running in production mode, Superset will check at startup for the presence
|
||||
of a CSP. If one is not found, it will issue a warning with the security risks. For environments
|
||||
@@ -187,15 +215,15 @@ this warning using the `CONTENT_SECURITY_POLICY_WARNING` key in `config.py`.
|
||||
|
||||
#### CSP Requirements
|
||||
|
||||
* Superset needs the `style-src unsafe-inline` CSP directive in order to operate.
|
||||
- Superset needs the `style-src unsafe-inline` CSP directive in order to operate.
|
||||
|
||||
```
|
||||
style-src 'self' 'unsafe-inline'
|
||||
```
|
||||
|
||||
* Only scripts marked with a [nonce](https://content-security-policy.com/nonce/) can be loaded and executed.
|
||||
Nonce is a random string automatically generated by Talisman on each page load.
|
||||
You can get current nonce value by calling jinja macro `csp_nonce()`.
|
||||
- Only scripts marked with a [nonce](https://content-security-policy.com/nonce/) can be loaded and executed.
|
||||
Nonce is a random string automatically generated by Talisman on each page load.
|
||||
You can get current nonce value by calling jinja macro `csp_nonce()`.
|
||||
|
||||
```
|
||||
<script nonce="{{ csp_nonce() }}">
|
||||
@@ -203,25 +231,24 @@ You can get current nonce value by calling jinja macro `csp_nonce()`.
|
||||
</script>
|
||||
```
|
||||
|
||||
- Some dashboards load images using data URIs and require `data:` in their `img-src`
|
||||
* Some dashboards load images using data URIs and require `data:` in their `img-src`
|
||||
|
||||
```
|
||||
img-src 'self' data:
|
||||
```
|
||||
|
||||
- MapBox charts use workers and need to connect to MapBox servers in addition to the Superset origin
|
||||
* MapBox charts use workers and need to connect to MapBox servers in addition to the Superset origin
|
||||
|
||||
```
|
||||
worker-src 'self' blob:
|
||||
connect-src 'self' https://api.mapbox.com https://events.mapbox.com
|
||||
```
|
||||
|
||||
* Other CSP directives default to `'self'` to limit content to the same origin as the Superset server.
|
||||
- Other CSP directives default to `'self'` to limit content to the same origin as the Superset server.
|
||||
|
||||
In order to adjust provided CSP configuration to your needs, follow the instructions and examples provided in
|
||||
[Content Security Policy Reference](https://content-security-policy.com/)
|
||||
|
||||
|
||||
#### Other Talisman security considerations
|
||||
|
||||
Setting `TALISMAN_ENABLED = True` will invoke Talisman's protection with its default arguments,
|
||||
@@ -231,7 +258,7 @@ These generally improve security, but administrators should be aware of their ex
|
||||
|
||||
In particular, the option of `force_https = True` (`False` by default) may break Superset's Alerts & Reports
|
||||
if workers are configured to access charts via a `WEBDRIVER_BASEURL` beginning
|
||||
with `http://`. As long as a Superset deployment enforces https upstream, e.g.,
|
||||
with `http://`. As long as a Superset deployment enforces https upstream, e.g.,
|
||||
through a loader balancer or application gateway, it should be acceptable to keep this
|
||||
option disabled. Otherwise, you may want to enable `force_https` like this:
|
||||
|
||||
@@ -68,7 +68,7 @@ const config = {
|
||||
from: '/usertutorial.html',
|
||||
},
|
||||
{
|
||||
to: '/docs/security',
|
||||
to: '/docs/security/',
|
||||
from: '/security.html',
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+4287
-739
File diff suppressed because it is too large
Load Diff
+18
-11
@@ -9719,23 +9719,30 @@ semver-diff@^3.1.1:
|
||||
|
||||
semver@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^5.4.1, semver@^5.6.0:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
||||
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
||||
version "5.7.1"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
version "6.3.0"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.3.7, semver@^7.3.8:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
apiVersion: v2
|
||||
appVersion: "2.1.0"
|
||||
appVersion: "3.0.3"
|
||||
description: Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
name: superset
|
||||
icon: https://artifacthub.io/image/68c1d717-0e97-491f-b046-754e46f46922@2x
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.10.5
|
||||
version: 0.11.2
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 12.1.6
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,12 @@ helm repo add superset http://apache.github.io/superset/
|
||||
helm install my-superset superset/superset
|
||||
```
|
||||
|
||||
Make sure you set your own `SECRET_KEY` to something unique and secret. This secret key is used by Flask for
|
||||
securely signing the session cookie and will be used to encrypt sensitive data on Superset's metadata database.
|
||||
It should be a long random bytes or str.
|
||||
|
||||
On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverrides.secrets`
|
||||
|
||||
## Requirements
|
||||
|
||||
| Repository | Name | Version |
|
||||
@@ -106,7 +112,7 @@ helm install my-superset superset/superset
|
||||
| initImage.tag | string | `"dockerize"` | |
|
||||
| nameOverride | string | `nil` | Provide a name to override the name of the chart |
|
||||
| nodeSelector | object | `{}` | |
|
||||
| postgresql | object | see `values.yaml` | Configuration values for the postgresql dependency. ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql |
|
||||
| postgresql | object | see `values.yaml` | Configuration values for the postgresql dependency. ref: https://github.com/kubernetes/charts/blob/master/stable/postgresql/README.md |
|
||||
| redis | object | see `values.yaml` | Configuration values for the Redis dependency. ref: https://github.com/bitnami/charts/blob/master/bitnami/redis More documentation can be found here: https://artifacthub.io/packages/helm/bitnami/redis |
|
||||
| resources | object | `{}` | |
|
||||
| runAsUser | int | `0` | User ID directive. This user must have enough permissions to run the bootstrap script Running containers as root is not recommended in production. Change this to another UID - e.g. 1000 to be more secure |
|
||||
|
||||
@@ -39,6 +39,12 @@ helm repo add superset http://apache.github.io/superset/
|
||||
helm install my-superset superset/superset
|
||||
```
|
||||
|
||||
Make sure you set your own `SECRET_KEY` to something unique and secret. This secret key is used by Flask for
|
||||
securely signing the session cookie and will be used to encrypt sensitive data on Superset's metadata database.
|
||||
It should be a long random bytes or str.
|
||||
|
||||
On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverrides.secrets`
|
||||
|
||||
{{ template "chart.requirementsSection" . }}
|
||||
|
||||
{{ template "chart.valuesSection" . }}
|
||||
|
||||
@@ -63,7 +63,7 @@ Create chart name and version as used by the chart label.
|
||||
|
||||
{{- define "superset-config" }}
|
||||
import os
|
||||
from cachelib.redis import RedisCache
|
||||
from flask_caching.backends.rediscache import RedisCache
|
||||
|
||||
def env(key, default=None):
|
||||
return os.getenv(key, default)
|
||||
@@ -82,7 +82,6 @@ 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')}"
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SECRET_KEY = env('SECRET_KEY', 'thisISaSECRET_1234')
|
||||
|
||||
class CeleryConfig(object):
|
||||
CELERY_IMPORTS = ('superset.sql_lab', )
|
||||
|
||||
@@ -42,6 +42,7 @@ spec:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/superset_config.py: {{ include "superset-config" . | sha256sum }}
|
||||
checksum/superset_bootstrap.sh: {{ tpl .Values.bootstrapScript . | sha256sum }}
|
||||
checksum/connections: {{ .Values.supersetNode.connections | toYaml | sha256sum }}
|
||||
checksum/extraConfigs: {{ .Values.extraConfigs | toYaml | sha256sum }}
|
||||
checksum/extraSecrets: {{ .Values.extraSecrets | toYaml | sha256sum }}
|
||||
|
||||
@@ -46,6 +46,7 @@ spec:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/superset_config.py: {{ include "superset-config" . | sha256sum }}
|
||||
checksum/superset_bootstrap.sh: {{ tpl .Values.bootstrapScript . | sha256sum }}
|
||||
checksum/connections: {{ .Values.supersetNode.connections | toYaml | sha256sum }}
|
||||
checksum/extraConfigs: {{ .Values.extraConfigs | toYaml | sha256sum }}
|
||||
checksum/extraSecrets: {{ .Values.extraSecrets | toYaml | sha256sum }}
|
||||
|
||||
@@ -63,7 +63,7 @@ spec:
|
||||
name: {{ tpl .Values.envFromSecret . }}
|
||||
{{- range .Values.envFromSecrets }}
|
||||
- secretRef:
|
||||
name: {{ tpl . $ }}
|
||||
name: {{ tpl . $ | quote }}
|
||||
{{- end }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
{{- if .Values.init.containerSecurityContext }}
|
||||
|
||||
@@ -93,6 +93,8 @@ extraSecretEnv: {}
|
||||
# # Google API Keys: https://console.cloud.google.com/apis/credentials
|
||||
# GOOGLE_KEY: ...
|
||||
# GOOGLE_SECRET: ...
|
||||
# # Generate your own secret key for encryption. Use openssl rand -base64 42 to generate a good key
|
||||
# SUPERSET_SECRET_KEY: 'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET'
|
||||
|
||||
# -- Extra files to mount on `/app/pythonpath`
|
||||
extraConfigs: {}
|
||||
@@ -695,7 +697,7 @@ init:
|
||||
topologySpreadConstraints: []
|
||||
|
||||
# -- Configuration values for the postgresql dependency.
|
||||
# ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql
|
||||
# ref: https://github.com/kubernetes/charts/blob/master/stable/postgresql/README.md
|
||||
# @default -- see `values.yaml`
|
||||
postgresql:
|
||||
##
|
||||
|
||||
+22
-8
@@ -27,8 +27,9 @@ billiard==3.6.4.0
|
||||
# via celery
|
||||
brotli==1.0.9
|
||||
# via flask-compress
|
||||
cachelib==0.4.1
|
||||
# via apache-superset
|
||||
cachelib==0.9.0
|
||||
# via
|
||||
# flask-caching
|
||||
celery==5.2.2
|
||||
# via apache-superset
|
||||
cffi==1.15.1
|
||||
@@ -84,13 +85,14 @@ flask==2.2.5
|
||||
# flask-limiter
|
||||
# flask-login
|
||||
# flask-migrate
|
||||
# flask-session
|
||||
# flask-sqlalchemy
|
||||
# flask-wtf
|
||||
flask-appbuilder==4.3.4
|
||||
flask-appbuilder==4.3.11
|
||||
# via apache-superset
|
||||
flask-babel==1.0.0
|
||||
# via flask-appbuilder
|
||||
flask-caching==1.10.1
|
||||
flask-caching==2.1.0
|
||||
# via apache-superset
|
||||
flask-compress==1.13
|
||||
# via apache-superset
|
||||
@@ -104,6 +106,8 @@ flask-login==0.6.0
|
||||
# flask-appbuilder
|
||||
flask-migrate==3.1.0
|
||||
# via apache-superset
|
||||
flask-session==0.5.0
|
||||
# via apache-superset
|
||||
flask-sqlalchemy==2.5.1
|
||||
# via
|
||||
# flask-appbuilder
|
||||
@@ -124,14 +128,16 @@ gunicorn==20.1.0
|
||||
# via apache-superset
|
||||
hashids==1.3.1
|
||||
# via apache-superset
|
||||
holidays==0.28
|
||||
holidays==0.25
|
||||
# via apache-superset
|
||||
humanize==3.11.0
|
||||
# via apache-superset
|
||||
idna==3.2
|
||||
# via email-validator
|
||||
importlib-metadata==6.6.0
|
||||
# via apache-superset
|
||||
# via
|
||||
# apache-superset
|
||||
# flask
|
||||
importlib-resources==5.12.0
|
||||
# via limits
|
||||
isodate==0.6.0
|
||||
@@ -148,6 +154,8 @@ jsonschema==4.17.3
|
||||
# via flask-appbuilder
|
||||
kombu==5.2.4
|
||||
# via celery
|
||||
korean-lunar-calendar==0.3.1
|
||||
# via holidays
|
||||
limits==3.4.0
|
||||
# via flask-limiter
|
||||
mako==1.2.4
|
||||
@@ -204,7 +212,7 @@ prison==0.2.1
|
||||
# via flask-appbuilder
|
||||
prompt-toolkit==3.0.38
|
||||
# via click-repl
|
||||
pyarrow==12.0.0
|
||||
pyarrow==14.0.1
|
||||
# via apache-superset
|
||||
pycparser==2.20
|
||||
# via cffi
|
||||
@@ -277,6 +285,8 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
sqlglot==20.8.0
|
||||
# via apache-superset
|
||||
sqlparse==0.4.4
|
||||
# via apache-superset
|
||||
sshtunnel==0.4.0
|
||||
@@ -287,6 +297,7 @@ typing-extensions==4.4.0
|
||||
# via
|
||||
# apache-superset
|
||||
# flask-limiter
|
||||
# kombu
|
||||
# limits
|
||||
urllib3==1.26.6
|
||||
# via selenium
|
||||
@@ -301,6 +312,7 @@ werkzeug==2.3.3
|
||||
# via
|
||||
# apache-superset
|
||||
# flask
|
||||
# flask-appbuilder
|
||||
# flask-jwt-extended
|
||||
# flask-login
|
||||
wrapt==1.12.1
|
||||
@@ -316,7 +328,9 @@ wtforms-json==0.3.5
|
||||
xlsxwriter==3.0.7
|
||||
# via apache-superset
|
||||
zipp==3.15.0
|
||||
# via importlib-metadata
|
||||
# via
|
||||
# importlib-metadata
|
||||
# importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
||||
|
||||
@@ -85,7 +85,9 @@ ptyprocess==0.7.0
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
pure-sasl==0.6.2
|
||||
# via thrift-sasl
|
||||
# via
|
||||
# pyhive
|
||||
# thrift-sasl
|
||||
pyasn1==0.5.0
|
||||
# via
|
||||
# pyasn1-modules
|
||||
@@ -94,7 +96,7 @@ pyasn1-modules==0.3.0
|
||||
# via python-ldap
|
||||
pydruid==0.6.5
|
||||
# via apache-superset
|
||||
pyhive[hive]==0.6.5
|
||||
pyhive[hive]==0.7.0
|
||||
# via apache-superset
|
||||
pyinstrument==4.4.0
|
||||
# via -r requirements/development.in
|
||||
|
||||
@@ -35,7 +35,7 @@ packaging==23.1
|
||||
# pyproject-api
|
||||
# tox
|
||||
pip-compile-multi==2.6.3
|
||||
# via -r requirements/integration.in
|
||||
# via -r integration.in
|
||||
pip-tools==6.13.0
|
||||
# via pip-compile-multi
|
||||
platformdirs==3.8.1
|
||||
@@ -45,7 +45,7 @@ platformdirs==3.8.1
|
||||
pluggy==1.2.0
|
||||
# via tox
|
||||
pre-commit==3.3.3
|
||||
# via -r requirements/integration.in
|
||||
# via -r integration.in
|
||||
pyproject-api==1.5.2
|
||||
# via tox
|
||||
pyproject-hooks==1.0.0
|
||||
@@ -55,6 +55,7 @@ pyyaml==6.0.1
|
||||
tomli==2.0.1
|
||||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
# pyproject-api
|
||||
# tox
|
||||
toposort==1.10
|
||||
|
||||
@@ -16,8 +16,6 @@ cmdstanpy==1.1.0
|
||||
# via prophet
|
||||
contourpy==1.0.7
|
||||
# via matplotlib
|
||||
convertdate==2.4.0
|
||||
# via prophet
|
||||
coverage[toml]==7.2.5
|
||||
# via pytest-cov
|
||||
cycler==0.11.0
|
||||
@@ -26,10 +24,6 @@ db-dtypes==1.1.1
|
||||
# via pandas-gbq
|
||||
docker==6.1.1
|
||||
# via -r requirements/testing.in
|
||||
ephem==4.1.4
|
||||
# via lunarcalendar
|
||||
exceptiongroup==1.1.1
|
||||
# via pytest
|
||||
flask-testing==0.8.1
|
||||
# via -r requirements/testing.in
|
||||
fonttools==4.39.4
|
||||
@@ -87,8 +81,6 @@ jsonschema-spec==0.1.4
|
||||
# via openapi-spec-validator
|
||||
kiwisolver==1.4.4
|
||||
# via matplotlib
|
||||
lunarcalendar==0.0.9
|
||||
# via prophet
|
||||
matplotlib==3.7.1
|
||||
# via prophet
|
||||
oauthlib==3.2.2
|
||||
@@ -103,7 +95,7 @@ parameterized==0.9.0
|
||||
# via -r requirements/testing.in
|
||||
pathable==0.4.3
|
||||
# via jsonschema-spec
|
||||
prophet==1.1.3
|
||||
prophet==1.1.5
|
||||
# via apache-superset
|
||||
proto-plus==1.22.2
|
||||
# via
|
||||
@@ -119,12 +111,12 @@ protobuf==4.23.0
|
||||
# proto-plus
|
||||
pydata-google-auth==1.7.0
|
||||
# via pandas-gbq
|
||||
pyee==9.0.4
|
||||
# via playwright
|
||||
pyfakefs==5.2.2
|
||||
# via -r requirements/testing.in
|
||||
pyhive[presto]==0.6.5
|
||||
pyhive[presto]==0.7.0
|
||||
# via apache-superset
|
||||
pymeeus==0.5.12
|
||||
# via convertdate
|
||||
pytest==7.3.1
|
||||
# via
|
||||
# -r requirements/testing.in
|
||||
|
||||
@@ -30,7 +30,7 @@ combine_as_imports = true
|
||||
include_trailing_comma = true
|
||||
line_length = 88
|
||||
known_first_party = superset
|
||||
known_third_party =alembic,apispec,backoff,cachelib,celery,click,colorama,cron_descriptor,croniter,cryptography,dateutil,deprecation,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_jwt_extended,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,holidays,humanize,isodate,jinja2,jwt,markdown,markupsafe,marshmallow,msgpack,nh3,numpy,pandas,parameterized,parsedatetime,pgsanity,pkg_resources,polyline,prison,progress,pyarrow,sqlalchemy_bigquery,pyhive,pyparsing,pytest,pytest_mock,pytz,redis,requests,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,typing_extensions,urllib3,werkzeug,wtforms,wtforms_json,yaml
|
||||
known_third_party =alembic,apispec,backoff,celery,click,colorama,cron_descriptor,croniter,cryptography,dateutil,deprecation,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_jwt_extended,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,holidays,humanize,isodate,jinja2,jwt,markdown,markupsafe,marshmallow,msgpack,nh3,numpy,pandas,parameterized,parsedatetime,pgsanity,pkg_resources,polyline,prison,progress,pyarrow,sqlalchemy_bigquery,pyhive,pyparsing,pytest,pytest_mock,pytz,redis,requests,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,typing_extensions,urllib3,werkzeug,wtforms,wtforms_json,yaml
|
||||
multi_line_output = 3
|
||||
order_by_type = false
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ setup(
|
||||
},
|
||||
install_requires=[
|
||||
"backoff>=1.8.0",
|
||||
"cachelib>=0.4.1,<0.5",
|
||||
"celery>=5.2.2, <6.0.0",
|
||||
"click>=8.0.3",
|
||||
"click-option-group",
|
||||
@@ -81,18 +80,19 @@ setup(
|
||||
"cryptography>=39.0.1, <40",
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <3.0.0",
|
||||
"flask-appbuilder>=4.3.4, <5.0.0",
|
||||
"flask-caching>=1.10.1, <2.0",
|
||||
"flask-appbuilder>=4.3.11, <5.0.0",
|
||||
"flask-caching>=2.1.0, <3",
|
||||
"flask-compress>=1.13, <2.0",
|
||||
"flask-talisman>=1.0.0, <2.0",
|
||||
"flask-login>=0.6.0, < 1.0",
|
||||
"flask-migrate>=3.1.0, <4.0",
|
||||
"flask-session>=0.4.0, <1.0",
|
||||
"flask-wtf>=1.1.0, <2.0",
|
||||
"func_timeout",
|
||||
"geopy",
|
||||
"gunicorn>=20.1.0; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
"holidays>=0.28, <1.0",
|
||||
"holidays>=0.25, <0.26",
|
||||
"humanize",
|
||||
"importlib_metadata",
|
||||
"isodate",
|
||||
@@ -110,7 +110,7 @@ setup(
|
||||
"python-dateutil",
|
||||
"python-dotenv",
|
||||
"python-geohash",
|
||||
"pyarrow>=12.0.0, <13",
|
||||
"pyarrow>=14.0.1, <15",
|
||||
"pyyaml>=6.0.0, <7.0.0",
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=4.5.4, <5.0",
|
||||
@@ -121,6 +121,7 @@ setup(
|
||||
"slack_sdk>=3.19.0, <4",
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-utils>=0.38.3, <0.39",
|
||||
"sqlglot>=20,<21",
|
||||
"sqlparse>=0.4.4, <0.5",
|
||||
"tabulate>=0.8.9, <0.9",
|
||||
"typing-extensions>=4, <5",
|
||||
@@ -146,9 +147,9 @@ setup(
|
||||
"databricks-sql-connector>=2.0.2, <3",
|
||||
"sqlalchemy-databricks>=0.2.0",
|
||||
],
|
||||
"db2": ["ibm-db-sa>=0.3.5, <0.4"],
|
||||
"db2": ["ibm-db-sa>0.3.8, <=0.4.0"],
|
||||
"dremio": ["sqlalchemy-dremio>=1.1.5, <1.3"],
|
||||
"drill": ["sqlalchemy-drill==0.1.dev"],
|
||||
"drill": ["sqlalchemy-drill>=1.1.4, <2"],
|
||||
"druid": ["pydruid>=0.6.5,<0.7"],
|
||||
"dynamodb": ["pydynamodb>=0.4.2"],
|
||||
"solr": ["sqlalchemy-solr >= 0.2.0"],
|
||||
@@ -176,16 +177,16 @@ setup(
|
||||
"postgres": ["psycopg2-binary==2.9.6"],
|
||||
"presto": ["pyhive[presto]>=0.6.5"],
|
||||
"trino": ["trino>=0.324.0"],
|
||||
"prophet": ["prophet>=1.1.0, <2.0.0"],
|
||||
"prophet": ["prophet>=1.1.5, <2"],
|
||||
"redshift": ["sqlalchemy-redshift>=0.8.1, < 0.9"],
|
||||
"rockset": ["rockset-sqlalchemy>=0.0.1, <1.0.0"],
|
||||
"rockset": ["rockset>=0.8.10, <0.9"],
|
||||
"shillelagh": [
|
||||
"shillelagh[datasetteapi,gsheetsapi,socrata,weatherapi]>=1.1.1, <2"
|
||||
],
|
||||
"snowflake": ["snowflake-sqlalchemy>=1.2.4, <2"],
|
||||
"spark": ["pyhive[hive]>=0.6.5", "tableschema", "thrift>=0.14.1, <1.0.0"],
|
||||
"teradata": ["teradatasql>=16.20.0.23"],
|
||||
"thumbnails": ["Pillow>=9.5.0, <10.0.0"],
|
||||
"thumbnails": ["Pillow>=10.0.1, <11"],
|
||||
"vertica": ["sqlalchemy-vertica-python>=0.5.9, < 0.6"],
|
||||
"netezza": ["nzalchemy>=11.0.2"],
|
||||
"starrocks": ["starrocks>=1.0.0"],
|
||||
|
||||
Generated
+22
-68
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@superset-ui/embedded-sdk",
|
||||
"version": "0.1.0-alpha.9",
|
||||
"version": "0.1.0-alpha.10",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@superset-ui/embedded-sdk",
|
||||
"version": "0.1.0-alpha.9",
|
||||
"version": "0.1.0-alpha.10",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@superset-ui/switchboard": "^0.18.26-0",
|
||||
@@ -7036,12 +7036,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@@ -7168,12 +7162,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -7678,15 +7666,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
"universalify": "^0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -7800,9 +7787,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
@@ -7817,16 +7804,6 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-to-istanbul": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz",
|
||||
@@ -8074,9 +8051,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -13476,12 +13453,6 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@@ -13589,12 +13560,6 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -13964,15 +13929,14 @@
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
"universalify": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
@@ -14049,9 +14013,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
@@ -14063,16 +14027,6 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"v8-to-istanbul": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz",
|
||||
@@ -14253,9 +14207,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@superset-ui/embedded-sdk",
|
||||
"version": "0.1.0-alpha.9",
|
||||
"version": "0.1.0-alpha.10",
|
||||
"description": "SDK for embedding resources from Superset into your own application",
|
||||
"access": "public",
|
||||
"keywords": [
|
||||
|
||||
@@ -89,6 +89,10 @@ export async function embedDashboard({
|
||||
|
||||
log('embedding');
|
||||
|
||||
if (supersetDomain.endsWith("/")) {
|
||||
supersetDomain = supersetDomain.slice(0, -1);
|
||||
}
|
||||
|
||||
function calculateConfig() {
|
||||
let configNumber = 0
|
||||
if(dashboardUiConfig) {
|
||||
|
||||
@@ -38,6 +38,23 @@ export default defineConfig({
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
// ECONNRESET on Chrome/Chromium 117.0.5851.0 when using Cypress <12.15.0
|
||||
// Check https://github.com/cypress-io/cypress/issues/27804 for context
|
||||
// TODO: This workaround should be removed when upgrading Cypress
|
||||
on('before:browser:launch', (browser, launchOptions) => {
|
||||
if (browser.name === 'chrome' && browser.isHeadless) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
launchOptions.args = launchOptions.args.map(arg => {
|
||||
if (arg === '--headless') {
|
||||
return '--headless=new';
|
||||
}
|
||||
|
||||
return arg;
|
||||
});
|
||||
}
|
||||
return launchOptions;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line global-require,import/extensions
|
||||
return require('./cypress/plugins/index.js')(on, config);
|
||||
},
|
||||
|
||||
@@ -515,7 +515,7 @@ describe('Dashboard edit', () => {
|
||||
// label Anthony
|
||||
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
|
||||
.eq(2)
|
||||
.should('have.css', 'fill', 'rgb(0, 122, 135)');
|
||||
.should('have.css', 'fill', 'rgb(244, 176, 42)');
|
||||
|
||||
// open main tab and nested tab
|
||||
openTab(0, 0);
|
||||
@@ -526,7 +526,7 @@ describe('Dashboard edit', () => {
|
||||
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
|
||||
)
|
||||
.first()
|
||||
.should('have.css', 'fill', 'rgb(0, 122, 135)');
|
||||
.should('have.css', 'fill', 'rgb(244, 176, 42)');
|
||||
});
|
||||
|
||||
it('should apply the color scheme across main tabs', () => {
|
||||
@@ -557,7 +557,7 @@ describe('Dashboard edit', () => {
|
||||
|
||||
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
|
||||
.first()
|
||||
.should('have.css', 'fill', 'rgb(204, 0, 134)');
|
||||
.should('have.css', 'fill', 'rgb(156, 52, 152)');
|
||||
|
||||
// change scheme now that charts are rendered across the main tabs
|
||||
editDashboard();
|
||||
|
||||
@@ -113,7 +113,7 @@ function prepareDashboardFilters(
|
||||
},
|
||||
type: 'NATIVE_FILTER',
|
||||
description: '',
|
||||
chartsInScope: [6],
|
||||
chartsInScope: [5],
|
||||
tabsInScope: [],
|
||||
});
|
||||
});
|
||||
@@ -150,7 +150,7 @@ function prepareDashboardFilters(
|
||||
meta: {
|
||||
width: 4,
|
||||
height: 50,
|
||||
chartId: 6,
|
||||
chartId: 5,
|
||||
sliceName: 'Most Populated Countries',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -25,7 +25,6 @@ import { TABBED_DASHBOARD } from 'cypress/utils/urls';
|
||||
import { expandFilterOnLeftPanel } from './utils';
|
||||
|
||||
const TREEMAP = { name: 'Treemap', viz: 'treemap_v2' };
|
||||
const FILTER_BOX = { name: 'Region Filter', viz: 'filter_box' };
|
||||
const LINE_CHART = { name: 'Growth Rate', viz: 'line' };
|
||||
const BOX_PLOT = { name: 'Box plot', viz: 'box_plot' };
|
||||
const BIG_NUMBER = { name: 'Number of Girls', viz: 'big_number_total' };
|
||||
@@ -41,7 +40,6 @@ function topLevelTabs() {
|
||||
function resetTabs() {
|
||||
topLevelTabs();
|
||||
cy.get('@top-level-tabs').first().click();
|
||||
waitForChartLoad(FILTER_BOX);
|
||||
waitForChartLoad(TREEMAP);
|
||||
waitForChartLoad(BIG_NUMBER);
|
||||
waitForChartLoad(TABLE);
|
||||
@@ -96,7 +94,6 @@ describe('Dashboard tabs', () => {
|
||||
|
||||
it.skip('should send new queries when tab becomes visible', () => {
|
||||
// landing in first tab
|
||||
waitForChartLoad(FILTER_BOX);
|
||||
waitForChartLoad(TREEMAP);
|
||||
|
||||
getChartAliasBySpec(TREEMAP).then(treemapAlias => {
|
||||
|
||||
@@ -23,7 +23,6 @@ import { ChartSpec, waitForChartLoad } from 'cypress/utils';
|
||||
export const WORLD_HEALTH_CHARTS = [
|
||||
{ name: '% Rural', viz: 'world_map' },
|
||||
{ name: 'Most Populated Countries', viz: 'table' },
|
||||
{ name: 'Region Filter', viz: 'filter_box' },
|
||||
{ name: "World's Population", viz: 'big_number' },
|
||||
{ name: 'Growth Rate', viz: 'line' },
|
||||
{ name: 'Rural Breakdown', viz: 'sunburst' },
|
||||
@@ -322,7 +321,7 @@ export function applyNativeFilterValueWithIndex(index: number, value: string) {
|
||||
cy.get(nativeFilters.filterFromDashboardView.filterValueInput)
|
||||
.eq(index)
|
||||
.should('exist', { timeout: 10000 })
|
||||
.type(`${value}{enter}`);
|
||||
.type(`${value}{enter}`, { force: true });
|
||||
// click the title to dismiss shown options
|
||||
cy.get(nativeFilters.filterFromDashboardView.filterName)
|
||||
.eq(index)
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('Advanced analytics', () => {
|
||||
|
||||
cy.get('[data-test=time_compare]')
|
||||
.find('input[type=search]')
|
||||
.clear()
|
||||
.type('1 year{enter}');
|
||||
|
||||
cy.get('button[data-test="run-query-button"]').click();
|
||||
|
||||
@@ -32,13 +32,13 @@ function openDashboardsAddedTo() {
|
||||
cy.getBySel('actions-trigger').click();
|
||||
cy.get('.ant-dropdown-menu-submenu-title')
|
||||
.contains('Dashboards added to')
|
||||
.trigger('mouseover');
|
||||
.trigger('mouseover', { force: true });
|
||||
}
|
||||
|
||||
function closeDashboardsAddedTo() {
|
||||
cy.get('.ant-dropdown-menu-submenu-title')
|
||||
.contains('Dashboards added to')
|
||||
.trigger('mouseout');
|
||||
.trigger('mouseout', { force: true });
|
||||
cy.getBySel('actions-trigger').click();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,6 @@ describe('Visualization > Distribution bar chart', () => {
|
||||
).should('exist');
|
||||
cy.get('.dist_bar .nv-legend .nv-legend-symbol')
|
||||
.first()
|
||||
.should('have.css', 'fill', 'rgb(255, 90, 95)');
|
||||
.should('have.css', 'fill', 'rgb(41, 105, 107)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('Visualization > Line', () => {
|
||||
).should('exist');
|
||||
cy.get('.line .nv-legend .nv-legend-symbol')
|
||||
.first()
|
||||
.should('have.css', 'fill', 'rgb(255, 90, 95)');
|
||||
.should('have.css', 'fill', 'rgb(41, 105, 107)');
|
||||
});
|
||||
|
||||
it('should work with adhoc metric', () => {
|
||||
|
||||
+723
-1779
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"@applitools/eyes-cypress": "^3.29.1",
|
||||
"@cypress/code-coverage": "^3.10.4",
|
||||
"@superset-ui/core": "^2.1.0",
|
||||
"@superset-ui/core": "^0.18.8",
|
||||
"brace": "^0.11.1",
|
||||
"cy-verify-downloads": "^0.1.6",
|
||||
"querystringify": "^2.2.0",
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// timezone for unit tests
|
||||
process.env.TZ = 'America/New_York';
|
||||
|
||||
module.exports = {
|
||||
testRegex:
|
||||
'\\/superset-frontend\\/(spec|src|plugins|packages|tools)\\/.*(_spec|\\.test)\\.[jt]sx?$',
|
||||
|
||||
Generated
+12194
-14988
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superset",
|
||||
"version": "0.0.0-dev",
|
||||
"version": "3.0.4",
|
||||
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
||||
"keywords": [
|
||||
"big",
|
||||
@@ -79,13 +79,14 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.0.1",
|
||||
"@babel/runtime-corejs3": "^7.22.6",
|
||||
"@babel/runtime-corejs3": "^7.12.5",
|
||||
"@emotion/babel-preset-css-prop": "^11.2.0",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@fontsource/inter": "^4.0.0",
|
||||
"@reduxjs/toolkit": "^1.9.3",
|
||||
"@scarf/scarf": "^1.1.1",
|
||||
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
|
||||
"@superset-ui/core": "file:./packages/superset-ui-core",
|
||||
"@superset-ui/legacy-plugin-chart-calendar": "file:./plugins/legacy-plugin-chart-calendar",
|
||||
@@ -215,18 +216,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-storybook": "^3.30.1",
|
||||
"@babel/cli": "^7.22.6",
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
"@babel/core": "^7.22.8",
|
||||
"@babel/eslint-parser": "^7.22.7",
|
||||
"@babel/node": "^7.22.6",
|
||||
"@babel/cli": "^7.18.10",
|
||||
"@babel/compat-data": "^7.18.8",
|
||||
"@babel/core": "^7.18.10",
|
||||
"@babel/eslint-parser": "^7.18.9",
|
||||
"@babel/node": "^7.18.10",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.18.9",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.22.7",
|
||||
"@babel/preset-env": "^7.22.7",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@babel/register": "^7.22.5",
|
||||
"@babel/plugin-transform-runtime": "^7.18.10",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/register": "^7.18.9",
|
||||
"@cypress/react": "^5.10.0",
|
||||
"@emotion/jest": "^11.3.0",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
@@ -241,7 +242,7 @@
|
||||
"@storybook/client-api": "^6.4.22",
|
||||
"@storybook/manager-webpack5": "^6.4.22",
|
||||
"@storybook/react": "^6.4.22",
|
||||
"@svgr/webpack": "^8.0.1",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/dom": "^7.29.4",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.2.0",
|
||||
@@ -274,18 +275,18 @@
|
||||
"@types/sinon": "^9.0.5",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/yargs": "12 - 15",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^2.1.3",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"chromatic": "^6.7.4",
|
||||
"copy-webpack-plugin": "^9.1.0",
|
||||
"cross-env": "^5.2.1",
|
||||
"css-loader": "^6.8.1",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^6.2.0",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.7",
|
||||
"eslint": "^7.32.0",
|
||||
@@ -295,14 +296,14 @@
|
||||
"eslint-plugin-cypress": "^2.11.2",
|
||||
"eslint-plugin-file-progress": "^1.2.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^24.7.0",
|
||||
"eslint-plugin-jest": "^24.1.3",
|
||||
"eslint-plugin-jest-dom": "^3.6.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-no-only-tests": "^2.4.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-testing-library": "^3.10.2",
|
||||
"eslint-plugin-testing-library": "^3.10.1",
|
||||
"eslint-plugin-theme-colors": "file:tools/eslint-plugin-theme-colors",
|
||||
"eslint-plugin-translation-vars": "file:tools/eslint-plugin-translation-vars",
|
||||
"exports-loader": "^0.7.0",
|
||||
@@ -310,18 +311,18 @@
|
||||
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||
"history": "^4.10.1",
|
||||
"ignore-styles": "^5.0.1",
|
||||
"imports-loader": "^3.1.1",
|
||||
"imports-loader": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-enzyme": "^7.1.2",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-websocket-mock": "^2.2.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"lerna": "^6.6.2",
|
||||
"lerna": "^6.1.0",
|
||||
"less": "^3.12.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"mock-socket": "^9.0.3",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier-plugin-packagejson": "^2.2.15",
|
||||
"process": "^0.11.10",
|
||||
@@ -329,22 +330,22 @@
|
||||
"react-test-renderer": "^16.9.0",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
"sinon": "^9.0.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"source-map-support": "^0.5.16",
|
||||
"speed-measure-webpack-plugin": "^1.5.0",
|
||||
"storybook-addon-jsx": "^7.3.14",
|
||||
"storybook-addon-paddings": "^4.3.0",
|
||||
"style-loader": "^3.3.3",
|
||||
"style-loader": "^3.2.1",
|
||||
"thread-loader": "^3.0.4",
|
||||
"transform-loader": "^0.2.4",
|
||||
"ts-loader": "^9.4.4",
|
||||
"ts-loader": "^9.2.5",
|
||||
"typescript": "^4.5.4",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-bundle-analyzer": "^4.9.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-manifest-plugin": "^4.1.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-bundle-analyzer": "^4.4.2",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.10.1",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpack-sources": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.9.1",
|
||||
@@ -357,5 +358,8 @@
|
||||
}
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"scarfSettings": {
|
||||
"allowTopLevel": true
|
||||
},
|
||||
"_id": "superset@0.0.0-dev"
|
||||
}
|
||||
|
||||
+2
@@ -44,6 +44,8 @@ const TypeIconWrapper = styled.div`
|
||||
&& svg {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Slider, InputNumber, Input } from 'antd';
|
||||
import Checkbox, { CheckboxProps } from 'antd/lib/checkbox';
|
||||
import Select, { SelectOption } from '../Select';
|
||||
import RadioButtonControl, {
|
||||
RadioButtonOption,
|
||||
} from '../../shared-controls/components/RadioButtonControl';
|
||||
|
||||
export const ControlFormItemComponents = {
|
||||
Slider,
|
||||
InputNumber,
|
||||
Input,
|
||||
Select,
|
||||
// Directly export Checkbox will result in "using name from external module" error
|
||||
// ref: https://stackoverflow.com/questions/43900035/ts4023-exported-variable-x-has-or-is-using-name-y-from-external-module-but
|
||||
Checkbox: Checkbox as React.ForwardRefExoticComponent<
|
||||
CheckboxProps & React.RefAttributes<HTMLInputElement>
|
||||
>,
|
||||
RadioButtonControl,
|
||||
};
|
||||
|
||||
export type ControlType = keyof typeof ControlFormItemComponents;
|
||||
|
||||
export type ControlFormValueValidator<V> = (value: V) => string | false;
|
||||
|
||||
export type ControlFormItemSpec<T extends ControlType = ControlType> = {
|
||||
controlType: T;
|
||||
label: ReactNode;
|
||||
description: ReactNode;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
validators?: ControlFormValueValidator<any>[];
|
||||
width?: number | string;
|
||||
/**
|
||||
* Time to delay change propagation.
|
||||
*/
|
||||
debounceDelay?: number;
|
||||
} & (T extends 'Select'
|
||||
? {
|
||||
options: SelectOption<any>[];
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
creatable?: boolean;
|
||||
minWidth?: number | string;
|
||||
validators?: ControlFormValueValidator<string>[];
|
||||
}
|
||||
: T extends 'RadioButtonControl'
|
||||
? {
|
||||
options: RadioButtonOption[];
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
: T extends 'Checkbox'
|
||||
? {
|
||||
value?: boolean;
|
||||
defaultValue?: boolean;
|
||||
}
|
||||
: T extends 'InputNumber' | 'Slider'
|
||||
? {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
value?: number;
|
||||
defaultValue?: number;
|
||||
validators?: ControlFormValueValidator<number>[];
|
||||
}
|
||||
: T extends 'Input'
|
||||
? {
|
||||
controlType: 'Input';
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
validators?: ControlFormValueValidator<string>[];
|
||||
}
|
||||
: {});
|
||||
+7
-18
@@ -16,22 +16,11 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { FeatureFlag, FeatureFlagMap } from '@superset-ui/core';
|
||||
import { styled } from '@superset-ui/core';
|
||||
|
||||
export function initFeatureFlags(featureFlags?: FeatureFlagMap) {
|
||||
if (!window.featureFlags) {
|
||||
window.featureFlags = featureFlags || {};
|
||||
}
|
||||
}
|
||||
|
||||
export function isFeatureEnabled(feature: FeatureFlag) {
|
||||
try {
|
||||
return !!window.featureFlags[feature];
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to query feature flag ${feature} (see error below)`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ControlSubSectionHeader = styled.div`
|
||||
font-weight: ${({ theme }) => theme.typography.weights.bold};
|
||||
font-size: ${({ theme }) => theme.typography.sizes.s};
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit}px;
|
||||
`;
|
||||
export default ControlSubSectionHeader;
|
||||
@@ -17,15 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
t,
|
||||
QueryMode,
|
||||
DTTM_ALIAS,
|
||||
GenericDataType,
|
||||
QueryColumn,
|
||||
DatasourceType,
|
||||
QueryMode,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
|
||||
|
||||
export const DEFAULT_MAX_ROW = 100000;
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const TIME_FILTER_LABELS = {
|
||||
time_range: t('Time Range'),
|
||||
@@ -41,6 +42,7 @@ export const COLUMN_NAME_ALIASES: Record<string, string> = {
|
||||
export const DATASET_TIME_COLUMN_OPTION: ColumnMeta = {
|
||||
verbose_name: COLUMN_NAME_ALIASES[DTTM_ALIAS],
|
||||
column_name: DTTM_ALIAS,
|
||||
type: 'TIMESTAMP',
|
||||
type_generic: GenericDataType.TEMPORAL,
|
||||
description: t(
|
||||
'A reference to the [Time] configuration, taking granularity into account',
|
||||
@@ -49,8 +51,9 @@ export const DATASET_TIME_COLUMN_OPTION: ColumnMeta = {
|
||||
|
||||
export const QUERY_TIME_COLUMN_OPTION: QueryColumn = {
|
||||
column_name: DTTM_ALIAS,
|
||||
type: DatasourceType.Query,
|
||||
is_dttm: false,
|
||||
is_dttm: true,
|
||||
type: 'TIMESTAMP',
|
||||
type_generic: GenericDataType.TEMPORAL,
|
||||
};
|
||||
|
||||
export const QueryModeLabel = {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { DatasourceType } from '@superset-ui/core';
|
||||
import { DatasourceType, GenericDataType } from '@superset-ui/core';
|
||||
import { Dataset } from './types';
|
||||
|
||||
export const TestDataset: Dataset = {
|
||||
@@ -37,7 +37,7 @@ export const TestDataset: Dataset = {
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'BIGINT',
|
||||
type_generic: 0,
|
||||
type_generic: GenericDataType.NUMERIC,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
@@ -55,7 +55,7 @@ export const TestDataset: Dataset = {
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'VARCHAR(16)',
|
||||
type_generic: 1,
|
||||
type_generic: GenericDataType.STRING,
|
||||
verbose_name: '',
|
||||
warning_markdown: null,
|
||||
},
|
||||
@@ -73,7 +73,7 @@ export const TestDataset: Dataset = {
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'VARCHAR(10)',
|
||||
type_generic: 1,
|
||||
type_generic: GenericDataType.STRING,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
@@ -91,7 +91,7 @@ export const TestDataset: Dataset = {
|
||||
is_dttm: true,
|
||||
python_date_format: null,
|
||||
type: 'TIMESTAMP WITHOUT TIME ZONE',
|
||||
type_generic: 2,
|
||||
type_generic: GenericDataType.TEMPORAL,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
@@ -109,7 +109,7 @@ export const TestDataset: Dataset = {
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'VARCHAR(255)',
|
||||
type_generic: 1,
|
||||
type_generic: GenericDataType.STRING,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@ export * from './components/InfoTooltipWithTrigger';
|
||||
export * from './components/ColumnOption';
|
||||
export * from './components/ColumnTypeLabel/ColumnTypeLabel';
|
||||
export * from './components/MetricOption';
|
||||
export * from './components/ControlSubSectionHeader';
|
||||
|
||||
export * from './shared-controls';
|
||||
export * from './types';
|
||||
|
||||
+5
-3
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import { t, RollingType, ComparisonType } from '@superset-ui/core';
|
||||
|
||||
import { ControlSubSectionHeader } from '../components/ControlSubSectionHeader';
|
||||
import { ControlPanelSectionConfig } from '../types';
|
||||
import { formatSelectOptions } from '../utils';
|
||||
|
||||
@@ -30,7 +32,7 @@ export const advancedAnalyticsControls: ControlPanelSectionConfig = {
|
||||
'of query results',
|
||||
),
|
||||
controlSetRows: [
|
||||
[<div className="section-header">{t('Rolling window')}</div>],
|
||||
[<ControlSubSectionHeader>{t('Rolling window')}</ControlSubSectionHeader>],
|
||||
[
|
||||
{
|
||||
name: 'rolling_type',
|
||||
@@ -99,7 +101,7 @@ export const advancedAnalyticsControls: ControlPanelSectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[<div className="section-header">{t('Time comparison')}</div>],
|
||||
[<ControlSubSectionHeader>{t('Time comparison')}</ControlSubSectionHeader>],
|
||||
[
|
||||
{
|
||||
name: 'time_compare',
|
||||
@@ -150,7 +152,7 @@ export const advancedAnalyticsControls: ControlPanelSectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[<div className="section-header">{t('Resample')}</div>],
|
||||
[<ControlSubSectionHeader>{t('Resample')}</ControlSubSectionHeader>],
|
||||
[
|
||||
{
|
||||
name: 'resample_rule',
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
|
||||
import { ControlSubSectionHeader } from '../components/ControlSubSectionHeader';
|
||||
import { ControlPanelSectionConfig } from '../types';
|
||||
import { formatSelectOptions } from '../utils';
|
||||
|
||||
@@ -33,7 +35,7 @@ export const titleControls: ControlPanelSectionConfig = {
|
||||
tabOverride: 'customize',
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[<div className="section-header">{t('X Axis')}</div>],
|
||||
[<ControlSubSectionHeader>{t('X Axis')}</ControlSubSectionHeader>],
|
||||
[
|
||||
{
|
||||
name: 'x_axis_title',
|
||||
@@ -61,7 +63,7 @@ export const titleControls: ControlPanelSectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[<div className="section-header">{t('Y Axis')}</div>],
|
||||
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_title',
|
||||
@@ -81,7 +83,7 @@ export const titleControls: ControlPanelSectionConfig = {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: true,
|
||||
label: t('Y AXIS TITLE MARGIN'),
|
||||
label: t('Y Axis Title Margin'),
|
||||
renderTrigger: true,
|
||||
default: TITLE_MARGIN_OPTIONS[0],
|
||||
choices: formatSelectOptions(TITLE_MARGIN_OPTIONS),
|
||||
@@ -96,7 +98,7 @@ export const titleControls: ControlPanelSectionConfig = {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Y AXIS TITLE POSITION'),
|
||||
label: t('Y Axis Title Position'),
|
||||
renderTrigger: true,
|
||||
default: TITLE_POSITION_OPTIONS[0][0],
|
||||
choices: TITLE_POSITION_OPTIONS,
|
||||
|
||||
+2
@@ -20,6 +20,7 @@ import { hasGenericChartAxes, t } from '@superset-ui/core';
|
||||
import { ControlPanelSectionConfig, ControlSetRow } from '../types';
|
||||
import {
|
||||
contributionModeControl,
|
||||
xAxisForceCategoricalControl,
|
||||
xAxisSortAscControl,
|
||||
xAxisSortControl,
|
||||
xAxisSortSeriesAscendingControl,
|
||||
@@ -55,6 +56,7 @@ export const echartsTimeSeriesQueryWithXAxisSort: ControlPanelSectionConfig = {
|
||||
controlSetRows: [
|
||||
[hasGenericChartAxes ? 'x_axis' : null],
|
||||
[hasGenericChartAxes ? 'time_grain_sqla' : null],
|
||||
[hasGenericChartAxes ? xAxisForceCategoricalControl : null],
|
||||
[hasGenericChartAxes ? xAxisSortControl : null],
|
||||
[hasGenericChartAxes ? xAxisSortAscControl : null],
|
||||
[hasGenericChartAxes ? xAxisSortSeriesControl : null],
|
||||
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
import ControlForm, {
|
||||
ControlFormRow,
|
||||
ControlFormItem,
|
||||
ControlFormItemSpec,
|
||||
} from '../../../components/ControlForm';
|
||||
import {
|
||||
SHARED_COLUMN_CONFIG_PROPS,
|
||||
SharedColumnConfigProp,
|
||||
} from './constants';
|
||||
import {
|
||||
ColumnConfig,
|
||||
ColumnConfigFormLayout,
|
||||
ColumnConfigInfo,
|
||||
} from './types';
|
||||
|
||||
export type ColumnConfigPopoverProps = {
|
||||
column: ColumnConfigInfo;
|
||||
configFormLayout: ColumnConfigFormLayout;
|
||||
onChange: (value: ColumnConfig) => void;
|
||||
};
|
||||
|
||||
export default function ColumnConfigPopover({
|
||||
column,
|
||||
configFormLayout,
|
||||
onChange,
|
||||
}: ColumnConfigPopoverProps) {
|
||||
return (
|
||||
<ControlForm onChange={onChange} value={column.config}>
|
||||
{configFormLayout[
|
||||
column.type === undefined ? GenericDataType.STRING : column.type
|
||||
].map((row, i) => (
|
||||
<ControlFormRow key={i}>
|
||||
{row.map(meta => {
|
||||
const key = typeof meta === 'string' ? meta : meta.name;
|
||||
const override =
|
||||
typeof meta === 'string'
|
||||
? {}
|
||||
: 'override' in meta
|
||||
? meta.override
|
||||
: meta.config;
|
||||
const props = {
|
||||
...(key in SHARED_COLUMN_CONFIG_PROPS
|
||||
? SHARED_COLUMN_CONFIG_PROPS[key as SharedColumnConfigProp]
|
||||
: undefined),
|
||||
...override,
|
||||
} as ControlFormItemSpec;
|
||||
return <ControlFormItem key={key} name={key} {...props} />;
|
||||
})}
|
||||
</ControlFormRow>
|
||||
))}
|
||||
</ControlForm>
|
||||
);
|
||||
}
|
||||
-3
@@ -17,10 +17,8 @@
|
||||
* under the License.
|
||||
*/
|
||||
import RadioButtonControl from './RadioButtonControl';
|
||||
import ColumnConfigControl from './ColumnConfigControl';
|
||||
|
||||
export * from './RadioButtonControl';
|
||||
export * from './ColumnConfigControl';
|
||||
|
||||
/**
|
||||
* Shared chart controls. Can be referred via string shortcuts in chart control
|
||||
@@ -28,5 +26,4 @@ export * from './ColumnConfigControl';
|
||||
*/
|
||||
export default {
|
||||
RadioButtonControl,
|
||||
ColumnConfigControl,
|
||||
};
|
||||
|
||||
+45
-9
@@ -20,9 +20,9 @@
|
||||
import {
|
||||
ContributionType,
|
||||
ensureIsArray,
|
||||
GenericDataType,
|
||||
getColumnLabel,
|
||||
getMetricLabel,
|
||||
isDefined,
|
||||
QueryFormColumn,
|
||||
QueryFormMetric,
|
||||
t,
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
DEFAULT_XAXIS_SORT_SERIES_DATA,
|
||||
SORT_SERIES_CHOICES,
|
||||
} from '../constants';
|
||||
import { checkColumnType } from '../utils/checkColumnType';
|
||||
|
||||
export const contributionModeControl = {
|
||||
name: 'contributionMode',
|
||||
@@ -54,18 +55,29 @@ export const contributionModeControl = {
|
||||
},
|
||||
};
|
||||
|
||||
function isTemporal(controls: ControlStateMapping): boolean {
|
||||
return !(
|
||||
isDefined(controls?.x_axis?.value) &&
|
||||
!isTemporalColumn(
|
||||
function isForcedCategorical(controls: ControlStateMapping): boolean {
|
||||
return (
|
||||
checkColumnType(
|
||||
getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
|
||||
controls?.datasource?.datasource,
|
||||
[GenericDataType.NUMERIC],
|
||||
) && !!controls?.xAxisForceCategorical?.value
|
||||
);
|
||||
}
|
||||
|
||||
function isSortable(controls: ControlStateMapping): boolean {
|
||||
return (
|
||||
isForcedCategorical(controls) ||
|
||||
checkColumnType(
|
||||
getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
|
||||
controls?.datasource?.datasource,
|
||||
[GenericDataType.STRING, GenericDataType.BOOLEAN],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const xAxisSortVisibility = ({ controls }: { controls: ControlStateMapping }) =>
|
||||
!isTemporal(controls) &&
|
||||
isSortable(controls) &&
|
||||
ensureIsArray(controls?.groupby?.value).length === 0 &&
|
||||
ensureIsArray(controls?.metrics?.value).length === 1;
|
||||
|
||||
@@ -74,7 +86,7 @@ const xAxisMultiSortVisibility = ({
|
||||
}: {
|
||||
controls: ControlStateMapping;
|
||||
}) =>
|
||||
!isTemporal(controls) &&
|
||||
isSortable(controls) &&
|
||||
(!!ensureIsArray(controls?.groupby?.value).length ||
|
||||
ensureIsArray(controls?.metrics?.value).length > 1);
|
||||
|
||||
@@ -141,7 +153,29 @@ export const xAxisSortAscControl = {
|
||||
: t('X-Axis Sort Ascending'),
|
||||
default: true,
|
||||
description: t('Whether to sort ascending or descending on the base Axis.'),
|
||||
visibility: xAxisSortVisibility,
|
||||
visibility: ({ controls }: { controls: ControlStateMapping }) =>
|
||||
controls?.x_axis_sort?.value !== undefined &&
|
||||
xAxisSortVisibility({ controls }),
|
||||
},
|
||||
};
|
||||
|
||||
export const xAxisForceCategoricalControl = {
|
||||
name: 'xAxisForceCategorical',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: () => t('Force categorical'),
|
||||
default: false,
|
||||
description: t('Treat values as categorical.'),
|
||||
initialValue: (control: ControlState, state: ControlPanelState | null) =>
|
||||
state?.form_data?.x_axis_sort !== undefined || control.value,
|
||||
renderTrigger: true,
|
||||
visibility: ({ controls }: { controls: ControlStateMapping }) =>
|
||||
checkColumnType(
|
||||
getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
|
||||
controls?.datasource?.datasource,
|
||||
[GenericDataType.NUMERIC],
|
||||
),
|
||||
shouldMapStateToProps: () => true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -173,6 +207,8 @@ export const xAxisSortSeriesAscendingControl = {
|
||||
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_ascending,
|
||||
description: t('Whether to sort ascending or descending on the base Axis.'),
|
||||
renderTrigger: true,
|
||||
visibility: xAxisMultiSortVisibility,
|
||||
visibility: ({ controls }: { controls: ControlStateMapping }) =>
|
||||
controls?.x_axis_sort_series?.value !== undefined &&
|
||||
xAxisMultiSortVisibility({ controls }),
|
||||
},
|
||||
};
|
||||
|
||||
+16
-3
@@ -47,6 +47,7 @@ import {
|
||||
isDefined,
|
||||
hasGenericChartAxes,
|
||||
NO_TIME_RANGE,
|
||||
validateMaxValue,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import {
|
||||
@@ -58,7 +59,7 @@ import {
|
||||
DEFAULT_TIME_FORMAT,
|
||||
DEFAULT_NUMBER_FORMAT,
|
||||
} from '../utils';
|
||||
import { TIME_FILTER_LABELS } from '../constants';
|
||||
import { DEFAULT_MAX_ROW, TIME_FILTER_LABELS } from '../constants';
|
||||
import {
|
||||
SharedControlConfig,
|
||||
Dataset,
|
||||
@@ -208,7 +209,7 @@ const time_grain_sqla: SharedControlConfig<'SelectControl'> = {
|
||||
choices: (datasource as Dataset)?.time_grain_sqla || [],
|
||||
}),
|
||||
visibility: ({ controls }) => {
|
||||
if (!hasGenericChartAxes) {
|
||||
if (!hasGenericChartAxes || !controls?.x_axis) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -245,7 +246,12 @@ const row_limit: SharedControlConfig<'SelectControl'> = {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Row limit'),
|
||||
validators: [legacyValidateInteger],
|
||||
clearable: false,
|
||||
mapStateToProps: state => ({ maxValue: state?.common?.conf?.SQL_MAX_ROW }),
|
||||
validators: [
|
||||
legacyValidateInteger,
|
||||
(v, state) => validateMaxValue(v, state?.maxValue || DEFAULT_MAX_ROW),
|
||||
],
|
||||
default: 10000,
|
||||
choices: formatSelectOptions(ROW_LIMIT_OPTIONS),
|
||||
description: t('Limits the number of rows that get displayed.'),
|
||||
@@ -317,6 +323,12 @@ const y_axis_format: SharedControlConfig<'SelectControl', SelectDefaultOption> =
|
||||
},
|
||||
};
|
||||
|
||||
const currency_format: SharedControlConfig<'CurrencyControl'> = {
|
||||
type: 'CurrencyControl',
|
||||
label: t('Currency format'),
|
||||
renderTrigger: true,
|
||||
};
|
||||
|
||||
const x_axis_time_format: SharedControlConfig<
|
||||
'SelectControl',
|
||||
SelectDefaultOption
|
||||
@@ -406,4 +418,5 @@ export default {
|
||||
x_axis: dndXAxisControl,
|
||||
show_empty_columns,
|
||||
temporal_columns_lookup,
|
||||
currency_format,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,6 @@ import type {
|
||||
import { sharedControls, sharedControlComponents } from './shared-controls';
|
||||
|
||||
export type { Metric } from '@superset-ui/core';
|
||||
export type { ControlFormItemSpec } from './components/ControlForm';
|
||||
export type { ControlComponentProps } from './shared-controls/components/types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -167,6 +166,12 @@ export type InternalControlType =
|
||||
| 'DndColumnSelect'
|
||||
| 'DndFilterSelect'
|
||||
| 'DndMetricSelect'
|
||||
| 'CurrencyControl'
|
||||
| 'InputNumber'
|
||||
| 'Checkbox'
|
||||
| 'Select'
|
||||
| 'Slider'
|
||||
| 'Input'
|
||||
| keyof SharedControlComponents; // expanded in `expandControlConfig`
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -474,13 +479,15 @@ export function isControlPanelSectionConfig(
|
||||
export function isDataset(
|
||||
datasource: Dataset | QueryResponse | null | undefined,
|
||||
): datasource is Dataset {
|
||||
return !!datasource && 'columns' in datasource;
|
||||
return (
|
||||
!!datasource && 'columns' in datasource && !('sqlEditorId' in datasource)
|
||||
);
|
||||
}
|
||||
|
||||
export function isQueryResponse(
|
||||
datasource: Dataset | QueryResponse | null | undefined,
|
||||
): datasource is QueryResponse {
|
||||
return !!datasource && 'results' in datasource && 'sql' in datasource;
|
||||
return !!datasource && 'results' in datasource && 'sqlEditorId' in datasource;
|
||||
}
|
||||
|
||||
export enum SortSeriesType {
|
||||
@@ -495,3 +502,61 @@ export type SortSeriesData = {
|
||||
sort_series_type: SortSeriesType;
|
||||
sort_series_ascending: boolean;
|
||||
};
|
||||
|
||||
export type ControlFormValueValidator<V> = (value: V) => string | false;
|
||||
|
||||
export type ControlFormItemSpec<T extends ControlType = ControlType> = {
|
||||
controlType: T;
|
||||
label: ReactNode;
|
||||
description: ReactNode;
|
||||
placeholder?: string;
|
||||
validators?: ControlFormValueValidator<any>[];
|
||||
width?: number | string;
|
||||
/**
|
||||
* Time to delay change propagation.
|
||||
*/
|
||||
debounceDelay?: number;
|
||||
} & (T extends 'Select'
|
||||
? {
|
||||
allowNewOptions?: boolean;
|
||||
options: any;
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
creatable?: boolean;
|
||||
minWidth?: number | string;
|
||||
validators?: ControlFormValueValidator<string>[];
|
||||
}
|
||||
: T extends 'RadioButtonControl'
|
||||
? {
|
||||
options: [string, ReactNode][];
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
: T extends 'Checkbox'
|
||||
? {
|
||||
value?: boolean;
|
||||
defaultValue?: boolean;
|
||||
}
|
||||
: T extends 'InputNumber' | 'Slider'
|
||||
? {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
value?: number;
|
||||
defaultValue?: number;
|
||||
validators?: ControlFormValueValidator<number>[];
|
||||
}
|
||||
: T extends 'Input'
|
||||
? {
|
||||
controlType: 'Input';
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
validators?: ControlFormValueValidator<string>[];
|
||||
}
|
||||
: T extends 'CurrencyControl'
|
||||
? {
|
||||
controlType: 'CurrencyControl';
|
||||
value?: Currency;
|
||||
defaultValue?: Currency;
|
||||
}
|
||||
: {});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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 { ensureIsArray, GenericDataType, ValueOf } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelState,
|
||||
isDataset,
|
||||
isQueryResponse,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
export function checkColumnType(
|
||||
columnName: string,
|
||||
datasource: ValueOf<Pick<ControlPanelState, 'datasource'>>,
|
||||
columnTypes: GenericDataType[],
|
||||
): boolean {
|
||||
if (isDataset(datasource)) {
|
||||
return ensureIsArray(datasource.columns).some(
|
||||
c =>
|
||||
c.type_generic !== undefined &&
|
||||
columnTypes.includes(c.type_generic) &&
|
||||
columnName === c.column_name,
|
||||
);
|
||||
}
|
||||
if (isQueryResponse(datasource)) {
|
||||
return ensureIsArray(datasource.columns)
|
||||
.filter(
|
||||
c =>
|
||||
c.type_generic !== undefined && columnTypes.includes(c.type_generic),
|
||||
)
|
||||
.map(c => c.column_name)
|
||||
.some(c => columnName === c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { QueryResponse } from '@superset-ui/core';
|
||||
import { Dataset, isColumnMeta, isDataset } from '../types';
|
||||
import { Dataset, isDataset, isQueryResponse } from '../types';
|
||||
|
||||
/**
|
||||
* Convert Datasource columns to column choices
|
||||
@@ -25,11 +25,13 @@ import { Dataset, isColumnMeta, isDataset } from '../types';
|
||||
export default function columnChoices(
|
||||
datasource?: Dataset | QueryResponse | null,
|
||||
): [string, string][] {
|
||||
if (isDataset(datasource) && isColumnMeta(datasource.columns[0])) {
|
||||
if (isDataset(datasource) || isQueryResponse(datasource)) {
|
||||
return datasource.columns
|
||||
.map((col): [string, string] => [
|
||||
col.column_name,
|
||||
col.verbose_name || col.column_name,
|
||||
'verbose_name' in col
|
||||
? col.verbose_name || col.column_name
|
||||
: col.column_name,
|
||||
])
|
||||
.sort((opt1, opt2) =>
|
||||
opt1[1].toLowerCase() > opt2[1].toLowerCase() ? 1 : -1,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
export * from './checkColumnType';
|
||||
export * from './selectOptions';
|
||||
export * from './D3Formatting';
|
||||
export * from './expandControlConfig';
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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 { GenericDataType, testQueryResponse } from '@superset-ui/core';
|
||||
import { checkColumnType, TestDataset } from '../../src';
|
||||
|
||||
test('checkColumnType columns from a Dataset', () => {
|
||||
expect(
|
||||
checkColumnType('num', TestDataset, [GenericDataType.NUMERIC]),
|
||||
).toEqual(true);
|
||||
expect(checkColumnType('num', TestDataset, [GenericDataType.STRING])).toEqual(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
checkColumnType('gender', TestDataset, [GenericDataType.STRING]),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
checkColumnType('gender', TestDataset, [GenericDataType.NUMERIC]),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('checkColumnType from a QueryResponse', () => {
|
||||
expect(
|
||||
checkColumnType('Column 1', testQueryResponse, [GenericDataType.STRING]),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
checkColumnType('Column 1', testQueryResponse, [GenericDataType.NUMERIC]),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('checkColumnType from null', () => {
|
||||
expect(checkColumnType('col', null, [])).toEqual(false);
|
||||
});
|
||||
+13
-3
@@ -16,7 +16,11 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { DatasourceType, testQueryResponse } from '@superset-ui/core';
|
||||
import {
|
||||
DatasourceType,
|
||||
GenericDataType,
|
||||
testQueryResponse,
|
||||
} from '@superset-ui/core';
|
||||
import { columnChoices } from '../../src';
|
||||
|
||||
describe('columnChoices()', () => {
|
||||
@@ -31,14 +35,20 @@ describe('columnChoices()', () => {
|
||||
columns: [
|
||||
{
|
||||
column_name: 'fiz',
|
||||
type: 'INT',
|
||||
type_generic: GenericDataType.NUMERIC,
|
||||
},
|
||||
{
|
||||
column_name: 'about',
|
||||
verbose_name: 'right',
|
||||
type: 'VARCHAR',
|
||||
type_generic: GenericDataType.STRING,
|
||||
},
|
||||
{
|
||||
column_name: 'foo',
|
||||
verbose_name: 'bar',
|
||||
verbose_name: undefined,
|
||||
type: 'TIMESTAMP',
|
||||
type_generic: GenericDataType.TEMPORAL,
|
||||
},
|
||||
],
|
||||
verbose_map: {},
|
||||
@@ -48,8 +58,8 @@ describe('columnChoices()', () => {
|
||||
description: 'this is my datasource',
|
||||
}),
|
||||
).toEqual([
|
||||
['foo', 'bar'],
|
||||
['fiz', 'fiz'],
|
||||
['foo', 'foo'],
|
||||
['about', 'right'],
|
||||
]);
|
||||
});
|
||||
|
||||
+7
-2
@@ -16,7 +16,11 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { testQueryResponse, testQueryResults } from '@superset-ui/core';
|
||||
import {
|
||||
GenericDataType,
|
||||
testQueryResponse,
|
||||
testQueryResults,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
Dataset,
|
||||
getTemporalColumns,
|
||||
@@ -55,8 +59,9 @@ test('get temporal columns from a QueryResponse', () => {
|
||||
temporalColumns: [
|
||||
{
|
||||
column_name: 'Column 2',
|
||||
type: 'TIMESTAMP',
|
||||
is_dttm: true,
|
||||
type: 'TIMESTAMP',
|
||||
type_generic: GenericDataType.TEMPORAL,
|
||||
},
|
||||
],
|
||||
defaultTemporalColumn: 'Column 2',
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"@types/rison": "0.0.6",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"@vx/responsive": "^0.0.199",
|
||||
"csstype": "^2.6.4",
|
||||
"csstype": "^3.1.3",
|
||||
"d3-format": "^1.3.2",
|
||||
"d3-interpolate": "^1.4.0",
|
||||
"d3-scale": "^3.0.0",
|
||||
|
||||
+13
-21
@@ -24,27 +24,19 @@ const schemes = [
|
||||
id: 'bnbColors',
|
||||
label: 'Airbnb Colors',
|
||||
colors: [
|
||||
'#ff5a5f', // rausch
|
||||
'#7b0051', // hackb
|
||||
'#007A87', // kazan
|
||||
'#00d1c1', // babu
|
||||
'#8ce071', // lima
|
||||
'#ffb400', // beach
|
||||
'#b4a76c', // barol
|
||||
'#ff8083',
|
||||
'#cc0086',
|
||||
'#00a1b3',
|
||||
'#00ffeb',
|
||||
'#bbedab',
|
||||
'#ffd266',
|
||||
'#cbc29a',
|
||||
'#ff3339',
|
||||
'#ff1ab1',
|
||||
'#005c66',
|
||||
'#00b3a5',
|
||||
'#55d12e',
|
||||
'#b37e00',
|
||||
'#988b4e',
|
||||
'#29696B',
|
||||
'#5BCACE',
|
||||
'#F4B02A',
|
||||
'#F1826A',
|
||||
'#792EB2',
|
||||
'#C96EC6',
|
||||
'#921E50',
|
||||
'#B27700',
|
||||
'#9C3498',
|
||||
'#9C3498',
|
||||
'#E4679D',
|
||||
'#C32F0E',
|
||||
'#9D63CA',
|
||||
],
|
||||
},
|
||||
].map(s => new CategoricalScheme(s));
|
||||
|
||||
@@ -67,6 +67,7 @@ function SafeMarkdown({
|
||||
rehypePlugins={rehypePlugins}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
skipHtml={false}
|
||||
transformLinkUri={null}
|
||||
>
|
||||
{source}
|
||||
</ReactMarkdown>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ interface CurrencyFormatter {
|
||||
(value: number | null | undefined): string;
|
||||
}
|
||||
|
||||
export const getCurrencySymbol = (currency: Currency) =>
|
||||
export const getCurrencySymbol = (currency: Partial<Currency>) =>
|
||||
new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency.symbol,
|
||||
|
||||
@@ -28,20 +28,24 @@ import {
|
||||
|
||||
export const buildCustomFormatters = (
|
||||
metrics: QueryFormMetric | QueryFormMetric[] | undefined,
|
||||
currencyFormats: Record<string, Currency>,
|
||||
columnFormats: Record<string, string>,
|
||||
savedCurrencyFormats: Record<string, Currency>,
|
||||
savedColumnFormats: Record<string, string>,
|
||||
d3Format: string | undefined,
|
||||
currencyFormat: Currency | undefined,
|
||||
) => {
|
||||
const metricsArray = ensureIsArray(metrics);
|
||||
return metricsArray.reduce((acc, metric) => {
|
||||
if (isSavedMetric(metric)) {
|
||||
const actualD3Format = d3Format ?? columnFormats[metric];
|
||||
return currencyFormats[metric]
|
||||
const actualD3Format = d3Format ?? savedColumnFormats[metric];
|
||||
const actualCurrencyFormat = currencyFormat?.symbol
|
||||
? currencyFormat
|
||||
: savedCurrencyFormats[metric];
|
||||
return actualCurrencyFormat
|
||||
? {
|
||||
...acc,
|
||||
[metric]: new CurrencyFormatter({
|
||||
d3Format: actualD3Format,
|
||||
currency: currencyFormats[metric],
|
||||
currency: actualCurrencyFormat,
|
||||
}),
|
||||
}
|
||||
: {
|
||||
@@ -67,13 +71,29 @@ export const getCustomFormatter = (
|
||||
|
||||
export const getValueFormatter = (
|
||||
metrics: QueryFormMetric | QueryFormMetric[] | undefined,
|
||||
currencyFormats: Record<string, Currency>,
|
||||
columnFormats: Record<string, string>,
|
||||
savedCurrencyFormats: Record<string, Currency>,
|
||||
savedColumnFormats: Record<string, string>,
|
||||
d3Format: string | undefined,
|
||||
currencyFormat: Currency | undefined,
|
||||
key?: string,
|
||||
) =>
|
||||
getCustomFormatter(
|
||||
buildCustomFormatters(metrics, currencyFormats, columnFormats, d3Format),
|
||||
) => {
|
||||
const customFormatter = getCustomFormatter(
|
||||
buildCustomFormatters(
|
||||
metrics,
|
||||
savedCurrencyFormats,
|
||||
savedColumnFormats,
|
||||
d3Format,
|
||||
currencyFormat,
|
||||
),
|
||||
metrics,
|
||||
key,
|
||||
) ?? getNumberFormatter(d3Format);
|
||||
);
|
||||
|
||||
if (customFormatter) {
|
||||
return customFormatter;
|
||||
}
|
||||
if (currencyFormat?.symbol) {
|
||||
return new CurrencyFormatter({ currency: currencyFormat, d3Format });
|
||||
}
|
||||
return getNumberFormatter(d3Format);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { SupersetClient, Method, Endpoint } from '../../../connection';
|
||||
import { QueryFormData } from '../../types/QueryFormData';
|
||||
import { LegacyChartDataResponse } from './types';
|
||||
import { BaseParams } from '../types';
|
||||
|
||||
export interface Params extends BaseParams {
|
||||
method?: Method;
|
||||
endpoint?: Endpoint;
|
||||
formData: QueryFormData;
|
||||
}
|
||||
|
||||
export default async function fetchExploreJson({
|
||||
client = SupersetClient,
|
||||
method = 'POST',
|
||||
requestConfig,
|
||||
endpoint = '/superset/explore_json/',
|
||||
formData,
|
||||
}: Params) {
|
||||
const { json } = await client.request({
|
||||
...requestConfig,
|
||||
method,
|
||||
endpoint,
|
||||
searchParams:
|
||||
method === 'GET'
|
||||
? new URLSearchParams({ form_data: JSON.stringify(formData) })
|
||||
: undefined,
|
||||
postPayload: method === 'POST' ? { form_data: formData } : undefined,
|
||||
});
|
||||
return json as LegacyChartDataResponse;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user