mirror of
https://github.com/apache/superset.git
synced 2026-06-14 12:09:14 +00:00
Compare commits
119 Commits
better-db-
...
improve-fu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
503933756e | ||
|
|
60261a5dc6 | ||
|
|
456512c508 | ||
|
|
c1d9b06649 | ||
|
|
7a64a82cd9 | ||
|
|
ef14b529b8 | ||
|
|
2a97a6ec1f | ||
|
|
fa6548939e | ||
|
|
418c673699 | ||
|
|
13f77a7416 | ||
|
|
303a80a316 | ||
|
|
2392ac6827 | ||
|
|
01ce4b987e | ||
|
|
2f308a85d8 | ||
|
|
e8d60509a0 | ||
|
|
d6f80eaae7 | ||
|
|
a5f986fec5 | ||
|
|
141d0252f2 | ||
|
|
c029b532d4 | ||
|
|
13816443ba | ||
|
|
2c4e22e598 | ||
|
|
aea776a131 | ||
|
|
d2360b533b | ||
|
|
de84a534ac | ||
|
|
ac636c73ae | ||
|
|
6a586fe4fd | ||
|
|
fbd8ae2888 | ||
|
|
7e4fde7a14 | ||
|
|
150b9a0168 | ||
|
|
f7b7aace38 | ||
|
|
f78c94c988 | ||
|
|
74ff8dc724 | ||
|
|
8aa127eac2 | ||
|
|
3729016a0d | ||
|
|
b6628cdfd2 | ||
|
|
ae48dba3e1 | ||
|
|
09364d182c | ||
|
|
99ed968289 | ||
|
|
8fa3b8d7e3 | ||
|
|
7530487760 | ||
|
|
79afc2b545 | ||
|
|
8c94f9c435 | ||
|
|
b589d44dfb | ||
|
|
4140261797 | ||
|
|
00f1fdb3c4 | ||
|
|
172e5dd095 | ||
|
|
a53907a646 | ||
|
|
be1b8d6751 | ||
|
|
26ff734ef9 | ||
|
|
0e18246999 | ||
|
|
7333ffd41e | ||
|
|
7dc5019b9d | ||
|
|
93fa39a14f | ||
|
|
342e6f3ab0 | ||
|
|
013379eb86 | ||
|
|
bc0ffe0d10 | ||
|
|
5f62deaa36 | ||
|
|
ff8605b723 | ||
|
|
45c77a1976 | ||
|
|
8cb71b8d3b | ||
|
|
2233c02720 | ||
|
|
839215148a | ||
|
|
c1eeb63d89 | ||
|
|
7b9ebbe735 | ||
|
|
a5a91d5e48 | ||
|
|
e1f5c49df7 | ||
|
|
3c1fc0b722 | ||
|
|
05faf2f352 | ||
|
|
347c174099 | ||
|
|
5656d69c04 | ||
|
|
ac4df8d06b | ||
|
|
bcd136cee1 | ||
|
|
7ab8534ef6 | ||
|
|
014b39290b | ||
|
|
4f97b739b1 | ||
|
|
d88cba92c0 | ||
|
|
5304bed4ed | ||
|
|
37194a41ec | ||
|
|
d75ff9e784 | ||
|
|
164a07e2be | ||
|
|
44bd200885 | ||
|
|
8242692541 | ||
|
|
09b92e7d08 | ||
|
|
31ac3898ad | ||
|
|
c1159c53e3 | ||
|
|
deb6aedddb | ||
|
|
ed0cd5e7b0 | ||
|
|
9280b4d2a9 | ||
|
|
3a57857707 | ||
|
|
6b7394e789 | ||
|
|
5a8eab3b25 | ||
|
|
15969fdf94 | ||
|
|
9b15e04bc4 | ||
|
|
fd947a097d | ||
|
|
e1383d3821 | ||
|
|
c131205ff1 | ||
|
|
b6df88a134 | ||
|
|
629b137bb0 | ||
|
|
db959a6463 | ||
|
|
4041150660 | ||
|
|
bcb43327b1 | ||
|
|
63c8bbf3eb | ||
|
|
24b1666273 | ||
|
|
86b795cd36 | ||
|
|
bc0bf94680 | ||
|
|
f5d64176f6 | ||
|
|
4f0020d0df | ||
|
|
c83eda9551 | ||
|
|
a36e636a58 | ||
|
|
f5d3627468 | ||
|
|
8eeed49547 | ||
|
|
00933a27af | ||
|
|
2bc33beec4 | ||
|
|
e1c1de1b94 | ||
|
|
26743dfcee | ||
|
|
8b0bda3bad | ||
|
|
a8a6254ea2 | ||
|
|
be4bc3dec5 | ||
|
|
6e02d19b0d |
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -127,6 +127,11 @@
|
||||
- any-glob-to-any-file:
|
||||
- 'superset/translations/es/**'
|
||||
|
||||
"i18n:persian":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'superset/translations/fa/**'
|
||||
|
||||
############################################
|
||||
# Sub-projects and monorepo packages
|
||||
############################################
|
||||
|
||||
8
.github/workflows/bashlib.sh
vendored
8
.github/workflows/bashlib.sh
vendored
@@ -145,6 +145,7 @@ cypress-install() {
|
||||
|
||||
cypress-run-all() {
|
||||
local USE_DASHBOARD=$1
|
||||
local APP_ROOT=$2
|
||||
cd "$GITHUB_WORKSPACE/superset-frontend/cypress-base"
|
||||
|
||||
# Start Flask and run it in background
|
||||
@@ -152,7 +153,12 @@ cypress-run-all() {
|
||||
# so errors can print to stderr.
|
||||
local flasklog="${HOME}/flask.log"
|
||||
local port=8081
|
||||
export CYPRESS_BASE_URL="http://localhost:${port}"
|
||||
CYPRESS_BASE_URL="http://localhost:${port}"
|
||||
if [ -n "$APP_ROOT" ]; then
|
||||
export SUPERSET_APP_ROOT=$APP_ROOT
|
||||
CYPRESS_BASE_URL=${CYPRESS_BASE_URL}${APP_ROOT}
|
||||
fi
|
||||
export CYPRESS_BASE_URL
|
||||
|
||||
nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
|
||||
local flaskProcessId=$!
|
||||
|
||||
3
.github/workflows/check-python-deps.yml
vendored
3
.github/workflows/check-python-deps.yml
vendored
@@ -17,13 +17,12 @@ jobs:
|
||||
check-python-deps:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
depth: 1
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Python
|
||||
if: steps.check.outputs.python
|
||||
|
||||
7
.github/workflows/superset-e2e.yml
vendored
7
.github/workflows/superset-e2e.yml
vendored
@@ -42,6 +42,7 @@ jobs:
|
||||
matrix:
|
||||
parallel_id: [0, 1, 2, 3, 4, 5]
|
||||
browser: ["chrome"]
|
||||
app_root: ["", "/app/prefix"]
|
||||
env:
|
||||
SUPERSET_ENV: development
|
||||
SUPERSET_CONFIG: tests.integration_tests.superset_test_config
|
||||
@@ -49,8 +50,8 @@ jobs:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
REDIS_PORT: 16379
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
# use the dashboard feature when running manually OR merging to master
|
||||
USE_DASHBOARD: ${{ github.event.inputs.use_dashboard == 'true'|| (github.ref == 'refs/heads/master' && 'true') || 'false' }}
|
||||
# Only use dashboard when explicitly requested via workflow_dispatch
|
||||
USE_DASHBOARD: ${{ github.event.inputs.use_dashboard == 'true' || 'false' }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
@@ -135,7 +136,7 @@ jobs:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
with:
|
||||
run: cypress-run-all ${{ env.USE_DASHBOARD }}
|
||||
run: cypress-run-all ${{ env.USE_DASHBOARD }} ${{ matrix.app_root }}
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
SUPERSET_TESTENV: true
|
||||
SUPERSET_SECRET_KEY: not-a-secret
|
||||
run: |
|
||||
pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear
|
||||
pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear --maxfail=50
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -107,6 +107,7 @@ ghostdriver.log
|
||||
testCSV.csv
|
||||
.terser-plugin-cache/
|
||||
apache-superset-*.tar.gz*
|
||||
apache_superset-*.tar.gz*
|
||||
release.json
|
||||
|
||||
# Translation-related files
|
||||
|
||||
@@ -208,7 +208,7 @@ RUN rm superset/translations/*/*/*.po
|
||||
COPY --from=superset-node /app/superset/translations superset/translations
|
||||
COPY --from=python-translation-compiler /app/translations_mo superset/translations
|
||||
|
||||
HEALTHCHECK CMD curl -f "http://localhost:${SUPERSET_PORT}/health"
|
||||
HEALTHCHECK CMD /app/docker/docker-healthcheck.sh
|
||||
CMD ["/app/docker/entrypoints/run-server.sh"]
|
||||
EXPOSE ${SUPERSET_PORT}
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -22,9 +22,9 @@ under the License.
|
||||
[](https://opensource.org/license/apache-2-0)
|
||||
[](https://github.com/apache/superset/releases/latest)
|
||||
[](https://github.com/apache/superset/actions)
|
||||
[](https://badge.fury.io/py/apache-superset)
|
||||
[](https://badge.fury.io/py/apache_superset)
|
||||
[](https://codecov.io/github/apache/superset)
|
||||
[](https://pypi.python.org/pypi/apache-superset)
|
||||
[](https://pypi.python.org/pypi/apache_superset)
|
||||
[](http://bit.ly/join-superset-slack)
|
||||
[](https://superset.apache.org)
|
||||
|
||||
@@ -72,9 +72,10 @@ Superset provides:
|
||||
## Screenshots & Gifs
|
||||
|
||||
**Video Overview**
|
||||
<!-- File hosted here https://github.com/apache/superset-site/raw/lfs/superset-video-4k.mp4 -->
|
||||
[superset-video-1080p.webm](https://github.com/user-attachments/assets/b37388f7-a971-409c-96a7-90c4e31322e6)
|
||||
|
||||
<!-- File hosted here https://github.com/apache/superset-site/raw/lfs/superset-video-4k.mp4 -->
|
||||
|
||||
[superset-video-1080p.webm](https://github.com/user-attachments/assets/b37388f7-a971-409c-96a7-90c4e31322e6)
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -156,7 +157,7 @@ Try out Superset's [quickstart](https://superset.apache.org/docs/quickstart/) gu
|
||||
and please read our [Slack Community Guidelines](https://github.com/apache/superset/blob/master/CODE_OF_CONDUCT.md#slack-community-guidelines)
|
||||
- [Join our dev@superset.apache.org Mailing list](https://lists.apache.org/list.html?dev@superset.apache.org). To join, simply send an email to [dev-subscribe@superset.apache.org](mailto:dev-subscribe@superset.apache.org)
|
||||
- If you want to help troubleshoot GitHub Issues involving the numerous database drivers that Superset supports, please consider adding your name and the databases you have access to on the [Superset Database Familiarity Rolodex](https://docs.google.com/spreadsheets/d/1U1qxiLvOX0kBTUGME1AHHi6Ywel6ECF8xk_Qy-V9R8c/edit#gid=0)
|
||||
- Join Superset's Town Hall and [Operational Model](https://preset.io/blog/the-superset-operational-model-wants-you/) recurring meetings. Meeting info is available on the [Superset Community Calendar](https://superset.apache.org/community)
|
||||
- Join Superset's Town Hall and [Operational Model](https://preset.io/blog/the-superset-operational-model-wants-you/) recurring meetings. Meeting info is available on the [Superset Community Calendar](https://superset.apache.org/community)
|
||||
|
||||
## Contributor Guide
|
||||
|
||||
@@ -184,14 +185,16 @@ Understanding the Superset Points of View
|
||||
- [Building New Database Connectors](https://preset.io/blog/building-database-connector/)
|
||||
- [Create Your First Dashboard](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard/)
|
||||
- [Comprehensive Tutorial for Contributing Code to Apache Superset
|
||||
](https://preset.io/blog/tutorial-contributing-code-to-apache-superset/)
|
||||
](https://preset.io/blog/tutorial-contributing-code-to-apache-superset/)
|
||||
- [Resources to master Superset by Preset](https://preset.io/resources/)
|
||||
|
||||
- Deploying Superset
|
||||
|
||||
- [Official Docker image](https://hub.docker.com/r/apache/superset)
|
||||
- [Helm Chart](https://github.com/apache/superset/tree/master/helm/superset)
|
||||
|
||||
- Recordings of Past [Superset Community Events](https://preset.io/events)
|
||||
|
||||
- [Mixed Time Series Charts](https://preset.io/events/mixed-time-series-visualization-in-superset-workshop/)
|
||||
- [How the Bing Team Customized Superset for the Internal Self-Serve Data & Analytics Platform](https://preset.io/events/how-the-bing-team-heavily-customized-superset-for-their-internal-data/)
|
||||
- [Live Demo: Visualizing MongoDB and Pinot Data using Trino](https://preset.io/events/2021-04-13-visualizing-mongodb-and-pinot-data-using-trino/)
|
||||
@@ -199,6 +202,7 @@ Understanding the Superset Points of View
|
||||
- [Building a Database Connector for Superset](https://preset.io/events/2021-02-16-building-a-database-connector-for-superset/)
|
||||
|
||||
- Visualizations
|
||||
|
||||
- [Creating Viz Plugins](https://superset.apache.org/docs/contributing/creating-viz-plugins/)
|
||||
- [Managing and Deploying Custom Viz Plugins](https://medium.com/nmc-techblog/apache-superset-manage-custom-viz-plugins-in-production-9fde1a708e55)
|
||||
- [Why Apache Superset is Betting on Apache ECharts](https://preset.io/blog/2021-4-1-why-echarts/)
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset
|
||||
|
||||
# Configure environment
|
||||
ENV LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
|
||||
RUN apt-get update -y
|
||||
|
||||
@@ -30,14 +30,14 @@ RUN apt-get install -y apt-transport-https apt-utils
|
||||
# Install superset dependencies
|
||||
# https://superset.apache.org/docs/installation/installing-superset-from-scratch
|
||||
RUN apt-get install -y build-essential libssl-dev \
|
||||
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
|
||||
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
|
||||
|
||||
# Install nodejs for custom build
|
||||
# https://nodejs.org/en/download/package-manager/
|
||||
RUN set -eux; \
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
|
||||
apt-get install -y nodejs; \
|
||||
node --version;
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
|
||||
apt-get install -y nodejs; \
|
||||
node --version;
|
||||
RUN if ! which npm; then apt-get install -y npm; fi
|
||||
|
||||
RUN mkdir -p /home/superset
|
||||
@@ -50,21 +50,21 @@ ARG SUPERSET_RELEASE_RC_TARBALL
|
||||
# Can fetch source from svn or copy tarball from local mounted directory
|
||||
COPY $SUPERSET_RELEASE_RC_TARBALL ./
|
||||
RUN tar -xvf *.tar.gz
|
||||
WORKDIR /home/superset/apache-superset-$VERSION/superset-frontend
|
||||
WORKDIR /home/superset/apache_superset-$VERSION/superset-frontend
|
||||
|
||||
RUN npm ci \
|
||||
&& npm run build \
|
||||
&& rm -rf node_modules
|
||||
&& npm run build \
|
||||
&& rm -rf node_modules
|
||||
|
||||
WORKDIR /home/superset/apache-superset-$VERSION
|
||||
WORKDIR /home/superset/apache_superset-$VERSION
|
||||
RUN pip install --upgrade setuptools pip \
|
||||
&& pip install -r requirements/base.txt \
|
||||
&& pip install --no-cache-dir .
|
||||
&& pip install -r requirements/base.txt \
|
||||
&& pip install --no-cache-dir .
|
||||
|
||||
RUN flask fab babel-compile --target superset/translations
|
||||
|
||||
ENV PATH=/home/superset/superset/bin:$PATH \
|
||||
PYTHONPATH=/home/superset/superset/ \
|
||||
SUPERSET_TESTENV=true
|
||||
PYTHONPATH=/home/superset/superset/ \
|
||||
SUPERSET_TESTENV=true
|
||||
COPY from_tarball_entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset
|
||||
|
||||
# Configure environment
|
||||
ENV LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
|
||||
RUN apt-get update -y
|
||||
|
||||
@@ -30,14 +30,14 @@ RUN apt-get install -y apt-transport-https apt-utils
|
||||
# Install superset dependencies
|
||||
# https://superset.apache.org/docs/installation/installing-superset-from-scratch
|
||||
RUN apt-get install -y subversion build-essential libssl-dev \
|
||||
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
|
||||
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
|
||||
|
||||
# Install nodejs for custom build
|
||||
# https://nodejs.org/en/download/package-manager/
|
||||
RUN set -eux; \
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
|
||||
apt-get install -y nodejs; \
|
||||
node --version;
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
|
||||
apt-get install -y nodejs; \
|
||||
node --version;
|
||||
RUN if ! which npm; then apt-get install -y npm; fi
|
||||
|
||||
RUN mkdir -p /home/superset
|
||||
@@ -49,20 +49,20 @@ ARG VERSION
|
||||
# Can fetch source from svn or copy tarball from local mounted directory
|
||||
RUN svn co https://dist.apache.org/repos/dist/dev/superset/$VERSION ./
|
||||
RUN tar -xvf *.tar.gz
|
||||
WORKDIR /home/superset/apache-superset-$VERSION/superset-frontend
|
||||
WORKDIR /home/superset/apache_superset-$VERSION/superset-frontend
|
||||
|
||||
RUN npm ci \
|
||||
&& npm run build \
|
||||
&& rm -rf node_modules
|
||||
&& npm run build \
|
||||
&& rm -rf node_modules
|
||||
|
||||
WORKDIR /home/superset/apache-superset-$VERSION
|
||||
WORKDIR /home/superset/apache_superset-$VERSION
|
||||
RUN pip install --upgrade setuptools pip \
|
||||
&& pip install -r requirements/base.txt \
|
||||
&& pip install --no-cache-dir .
|
||||
&& pip install -r requirements/base.txt \
|
||||
&& pip install --no-cache-dir .
|
||||
|
||||
RUN flask fab babel-compile --target superset/translations
|
||||
|
||||
ENV PATH=/home/superset/superset/bin:$PATH \
|
||||
PYTHONPATH=/home/superset/superset/
|
||||
PYTHONPATH=/home/superset/superset/
|
||||
COPY from_tarball_entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -123,10 +123,10 @@ SUPERSET_RC=1
|
||||
SUPERSET_GITHUB_BRANCH=1.5
|
||||
SUPERSET_PGP_FULLNAME=villebro@apache.org
|
||||
SUPERSET_VERSION_RC=1.5.1rc1
|
||||
SUPERSET_RELEASE=apache-superset-1.5.1
|
||||
SUPERSET_RELEASE_RC=apache-superset-1.5.1rc1
|
||||
SUPERSET_RELEASE_TARBALL=apache-superset-1.5.1-source.tar.gz
|
||||
SUPERSET_RELEASE_RC_TARBALL=apache-superset-1.5.1rc1-source.tar.gz
|
||||
SUPERSET_RELEASE=apache_superset-1.5.1
|
||||
SUPERSET_RELEASE_RC=apache_superset-1.5.1rc1
|
||||
SUPERSET_RELEASE_TARBALL=apache_superset-1.5.1-source.tar.gz
|
||||
SUPERSET_RELEASE_RC_TARBALL=apache_superset-1.5.1rc1-source.tar.gz
|
||||
SUPERSET_TMP_ASF_SITE_PATH=/tmp/incubator-superset-site-1.5.1
|
||||
-------------------------------
|
||||
```
|
||||
@@ -380,7 +380,7 @@ Official instructions:
|
||||
https://www.apache.org/info/verification.html
|
||||
|
||||
We now have a handy script for anyone validating a release to use. The core of it is in this very folder, `verify_release.py`. Just make sure you have all three release files in the same directory (`{some version}.tar.gz`, `{some version}.tar.gz.asc` and `{some version}tar.gz.sha512`). Then you can pass this script the path to the `.gz` file like so:
|
||||
`python verify_release.py ~/path/tp/apache-superset-{version/candidate}-source.tar.gz`
|
||||
`python verify_release.py ~/path/tp/apache_superset-{version/candidate}-source.tar.gz`
|
||||
|
||||
If all goes well, you will see this result in your terminal:
|
||||
|
||||
@@ -470,7 +470,7 @@ while requesting access to push packages.
|
||||
|
||||
```bash
|
||||
twine upload dist/apache_superset-${SUPERSET_VERSION}-py3-none-any.whl
|
||||
twine upload dist/apache-superset-${SUPERSET_VERSION}.tar.gz
|
||||
twine upload dist/apache_superset-${SUPERSET_VERSION}.tar.gz
|
||||
```
|
||||
|
||||
Set your username to `__token__`
|
||||
|
||||
@@ -31,7 +31,7 @@ The official source release:
|
||||
https://downloads.apache.org/{{ project_module }}/{{ version }}
|
||||
|
||||
The PyPI package:
|
||||
https://pypi.org/project/apache-superset/{{ version }}
|
||||
https://pypi.org/project/apache_superset/{{ version }}
|
||||
|
||||
The CHANGELOG for the release:
|
||||
https://github.com/apache/{{ project_module }}/blob/{{ version }}/CHANGELOG/{{ version }}.md
|
||||
|
||||
@@ -32,7 +32,7 @@ else
|
||||
SUPERSET_VERSION="${1}"
|
||||
SUPERSET_RC="${2}"
|
||||
SUPERSET_PGP_FULLNAME="${3}"
|
||||
SUPERSET_RELEASE_RC_TARBALL="apache-superset-${SUPERSET_VERSION_RC}-source.tar.gz"
|
||||
SUPERSET_RELEASE_RC_TARBALL="apache_superset-${SUPERSET_VERSION_RC}-source.tar.gz"
|
||||
fi
|
||||
|
||||
SUPERSET_VERSION_RC="${SUPERSET_VERSION}rc${SUPERSET_RC}"
|
||||
|
||||
@@ -22,7 +22,7 @@ if [ -z "${SUPERSET_VERSION_RC}" ] || [ -z "${SUPERSET_SVN_DEV_PATH}" ] || [ -z
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SUPERSET_RELEASE_RC=apache-superset-"${SUPERSET_VERSION_RC}"
|
||||
SUPERSET_RELEASE_RC=apache_superset-"${SUPERSET_VERSION_RC}"
|
||||
SUPERSET_RELEASE_RC_TARBALL="${SUPERSET_RELEASE_RC}"-source.tar.gz
|
||||
SUPERSET_RELEASE_RC_BASE_PATH="${SUPERSET_SVN_DEV_PATH}"/"${SUPERSET_VERSION_RC}"
|
||||
SUPERSET_RELEASE_RC_TARBALL_PATH="${SUPERSET_RELEASE_RC_BASE_PATH}"/"${SUPERSET_RELEASE_RC_TARBALL}"
|
||||
|
||||
@@ -50,8 +50,8 @@ else
|
||||
export SUPERSET_GITHUB_BRANCH="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
export SUPERSET_PGP_FULLNAME="${2}"
|
||||
export SUPERSET_VERSION_RC="${SUPERSET_VERSION}rc${VERSION_RC}"
|
||||
export SUPERSET_RELEASE=apache-superset-"${SUPERSET_VERSION}"
|
||||
export SUPERSET_RELEASE_RC=apache-superset-"${SUPERSET_VERSION_RC}"
|
||||
export SUPERSET_RELEASE=apache_superset-"${SUPERSET_VERSION}"
|
||||
export SUPERSET_RELEASE_RC=apache_superset-"${SUPERSET_VERSION_RC}"
|
||||
export SUPERSET_RELEASE_TARBALL="${SUPERSET_RELEASE}"-source.tar.gz
|
||||
export SUPERSET_RELEASE_RC_TARBALL="${SUPERSET_RELEASE_RC}"-source.tar.gz
|
||||
export SUPERSET_TMP_ASF_SITE_PATH="/tmp/incubator-superset-site-${SUPERSET_VERSION}"
|
||||
|
||||
@@ -27,7 +27,7 @@ if [ -z "${SUPERSET_SVN_DEV_PATH}" ]; then
|
||||
fi
|
||||
|
||||
if [[ -n ${1} ]] && [[ ${1} == "local" ]]; then
|
||||
SUPERSET_RELEASE_RC=apache-superset-"${SUPERSET_VERSION_RC}"
|
||||
SUPERSET_RELEASE_RC=apache_superset-"${SUPERSET_VERSION_RC}"
|
||||
SUPERSET_RELEASE_RC_TARBALL="${SUPERSET_RELEASE_RC}"-source.tar.gz
|
||||
SUPERSET_TARBALL_PATH="${SUPERSET_SVN_DEV_PATH}"/${SUPERSET_VERSION_RC}/${SUPERSET_RELEASE_RC_TARBALL}
|
||||
SUPERSET_TMP_TARBALL_FILENAME=_tmp_"${SUPERSET_VERSION_RC}".tar.gz
|
||||
|
||||
@@ -38,7 +38,7 @@ get_pip_command() {
|
||||
PYTHON=$(get_python_command)
|
||||
PIP=$(get_pip_command)
|
||||
|
||||
# Get the release directory's path. If you unzip an Apache release and just run the npm script to validate the release, this will be a file name like `apache-superset-x.x.xrcx-source.tar.gz`
|
||||
# Get the release directory's path. If you unzip an Apache release and just run the npm script to validate the release, this will be a file name like `apache_superset-x.x.xrcx-source.tar.gz`
|
||||
RELEASE_ZIP_PATH="../../$(basename "$(dirname "$(pwd)")")-source.tar.gz"
|
||||
|
||||
# Install dependencies from requirements.txt if the file exists
|
||||
|
||||
@@ -101,6 +101,7 @@ Join our growing community!
|
||||
- [ELMO Cloud HR & Payroll](https://elmosoftware.com.au/)
|
||||
- [Endress+Hauser](https://www.endress.com/) [@rumbin]
|
||||
- [FBK - ICT center](https://ict.fbk.eu)
|
||||
- [Formbricks](https://formbricks.com)
|
||||
- [Gavagai](https://gavagai.io) [@gavagai-corp]
|
||||
- [GfK Data Lab](https://www.gfk.com/home) [@mherr]
|
||||
- [Hydrolix](https://www.hydrolix.io/)
|
||||
@@ -137,6 +138,7 @@ Join our growing community!
|
||||
- [Virtuoso QA](https://www.virtuosoqa.com)
|
||||
- [Whale](https://whale.im)
|
||||
- [Windsor.ai](https://www.windsor.ai/) [@octaviancorlade]
|
||||
- [WinWin Network马上赢](https://brandct.cn/) [@wenbinye]
|
||||
- [Zeta](https://www.zeta.tech/) [@shaikidris]
|
||||
|
||||
### Media & Entertainment
|
||||
@@ -215,6 +217,7 @@ Join our growing community!
|
||||
- [Increff](https://www.increff.com/) [@ishansinghania]
|
||||
- [komoot](https://www.komoot.com/) [@christophlingg]
|
||||
- [Let's Roam](https://www.letsroam.com/)
|
||||
- [Machrent SA](https://www.machrent.com/)
|
||||
- [Onebeat](https://1beat.com/) [@GuyAttia]
|
||||
- [X](https://x.com/)
|
||||
- [VLMedia](https://www.vlmedia.com.tr/) [@ibotheperfect]
|
||||
|
||||
@@ -23,7 +23,8 @@ This file documents any backwards-incompatible changes in Superset and
|
||||
assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
|
||||
There's a migration added that can potentially affect a significant number of existing charts.
|
||||
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
|
||||
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
|
||||
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
|
||||
@@ -46,6 +47,7 @@ assists people when migrating to a new version.
|
||||
- [30284](https://github.com/apache/superset/pull/30284) Deprecated GLOBAL_ASYNC_QUERIES_REDIS_CONFIG in favor of the new GLOBAL_ASYNC_QUERIES_CACHE_BACKEND configuration. To leverage Redis Sentinel, set CACHE_TYPE to RedisSentinelCache, or use RedisCache for standalone Redis
|
||||
- [31961](https://github.com/apache/superset/pull/31961) Upgraded React from version 16.13.1 to 17.0.2. If you are using custom frontend extensions or plugins, you may need to update them to be compatible with React 17.
|
||||
- [31260](https://github.com/apache/superset/pull/31260) Docker images now use `uv pip install` instead of `pip install` to manage the python envrionment. Most docker-based deployments will be affected, whether you derive one of the published images, or have custom bootstrap script that install python libraries (drivers)
|
||||
- [32432](https://github.com/apache/superset/pull/31260) Moves the List Roles FAB view to the frontend and requires `FAB_ADD_SECURITY_API` to be enabled in the configuration and `superset init` to be executed.
|
||||
|
||||
### Potential Downtime
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ x-superset-volumes: &superset-volumes
|
||||
- ./superset-frontend:/app/superset-frontend
|
||||
- superset_home:/app/superset_home
|
||||
- ./tests:/app/tests
|
||||
|
||||
x-common-build: &common-build
|
||||
context: .
|
||||
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
|
||||
@@ -43,6 +42,11 @@ x-common-build: &common-build
|
||||
|
||||
services:
|
||||
nginx:
|
||||
env_file:
|
||||
- path: docker/.env # default
|
||||
required: true
|
||||
- path: docker/.env-local # optional override
|
||||
required: false
|
||||
image: nginx:latest
|
||||
container_name: superset_nginx
|
||||
restart: unless-stopped
|
||||
@@ -52,6 +56,8 @@ services:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./docker/nginx/templates:/etc/nginx/templates:ro
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
container_name: superset_cache
|
||||
|
||||
@@ -54,6 +54,7 @@ REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
FLASK_DEBUG=true
|
||||
SUPERSET_APP_ROOT="/"
|
||||
SUPERSET_ENV=development
|
||||
SUPERSET_LOAD_EXAMPLES=yes
|
||||
CYPRESS_CONFIG=false
|
||||
@@ -62,7 +63,6 @@ MAPBOX_API_KEY=''
|
||||
|
||||
# Make sure you set this to a unique secure random value on production
|
||||
SUPERSET_SECRET_KEY=TEST_NON_DEV_SECRET
|
||||
|
||||
ENABLE_PLAYWRIGHT=false
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
BUILD_SUPERSET_FRONTEND_IN_DOCKER=true
|
||||
|
||||
@@ -50,7 +50,11 @@ fi
|
||||
#
|
||||
if [ -f "${REQUIREMENTS_LOCAL}" ]; then
|
||||
echo "Installing local overrides at ${REQUIREMENTS_LOCAL}"
|
||||
uv pip install --no-cache-dir -r "${REQUIREMENTS_LOCAL}"
|
||||
if command -v uv > /dev/null 2>&1; then
|
||||
uv pip install --no-cache-dir -r "${REQUIREMENTS_LOCAL}"
|
||||
else
|
||||
pip install --no-cache-dir -r "${REQUIREMENTS_LOCAL}"
|
||||
fi
|
||||
else
|
||||
echo "Skipping local overrides"
|
||||
fi
|
||||
|
||||
19
docker/docker-healthcheck.sh
Executable file
19
docker/docker-healthcheck.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
curl -f "http://localhost:${SUPERSET_PORT}/${SUPERSET_APP_ROOT/\//}/health" || exit 1
|
||||
@@ -90,44 +90,5 @@ http {
|
||||
|
||||
client_max_body_size 10m;
|
||||
|
||||
upstream superset_app {
|
||||
server host.docker.internal:8088;
|
||||
keepalive 100;
|
||||
}
|
||||
|
||||
upstream superset_websocket {
|
||||
server host.docker.internal:8080;
|
||||
keepalive 100;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
location /ws {
|
||||
proxy_pass http://superset_websocket;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location /static {
|
||||
proxy_pass http://host.docker.internal:9000; # Proxy to superset-node
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://superset_app;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_http_version 1.1;
|
||||
port_in_redirect off;
|
||||
proxy_connect_timeout 300;
|
||||
}
|
||||
}
|
||||
include /etc/nginx/conf.d/superset.conf;
|
||||
}
|
||||
|
||||
57
docker/nginx/templates/superset.conf.template
Normal file
57
docker/nginx/templates/superset.conf.template
Normal file
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
upstream superset_app {
|
||||
server host.docker.internal:8088;
|
||||
keepalive 100;
|
||||
}
|
||||
|
||||
upstream superset_websocket {
|
||||
server host.docker.internal:8080;
|
||||
keepalive 100;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
location /ws {
|
||||
proxy_pass http://superset_websocket;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ${SUPERSET_APP_ROOT}/static {
|
||||
proxy_pass http://host.docker.internal:9000; # Proxy to superset-node
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ${SUPERSET_APP_ROOT} {
|
||||
proxy_pass http://superset_app;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_http_version 1.1;
|
||||
port_in_redirect off;
|
||||
proxy_connect_timeout 300;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,6 +71,7 @@ CACHE_CONFIG = {
|
||||
"CACHE_REDIS_DB": REDIS_RESULTS_DB,
|
||||
}
|
||||
DATA_CACHE_CONFIG = CACHE_CONFIG
|
||||
THUMBNAIL_CACHE_CONFIG = CACHE_CONFIG
|
||||
|
||||
|
||||
class CeleryConfig:
|
||||
@@ -100,9 +101,11 @@ CELERY_CONFIG = CeleryConfig
|
||||
|
||||
FEATURE_FLAGS = {"ALERT_REPORTS": True}
|
||||
ALERT_REPORTS_NOTIFICATION_DRY_RUN = True
|
||||
WEBDRIVER_BASEURL = "http://superset:8088/" # When using docker compose baseurl should be http://superset_app:8088/ # noqa: E501
|
||||
WEBDRIVER_BASEURL = f"http://superset_app{os.environ.get('SUPERSET_APP_ROOT', '/')}/" # When using docker compose baseurl should be http://superset_nginx{ENV{BASEPATH}}/ # noqa: E501
|
||||
# The base URL for the email report hyperlinks.
|
||||
WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL
|
||||
WEBDRIVER_BASEURL_USER_FRIENDLY = (
|
||||
f"http://localhost:8888/{os.environ.get('SUPERSET_APP_ROOT', '/')}/"
|
||||
)
|
||||
SQLLAB_CTAS_NO_LIMIT = True
|
||||
|
||||
log_level_text = os.getenv("SUPERSET_LOG_LEVEL", "INFO")
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"Israel",
|
||||
"Italy",
|
||||
"Italy (regions)",
|
||||
"Ivory Coast",
|
||||
"Japan",
|
||||
"Jordan",
|
||||
"Kazakhstan",
|
||||
@@ -143,6 +144,7 @@
|
||||
"Poland",
|
||||
"Portugal",
|
||||
"Qatar",
|
||||
"Republic Of Serbia",
|
||||
"Romania",
|
||||
"Russia",
|
||||
"Rwanda",
|
||||
|
||||
@@ -215,6 +215,45 @@ In case the reverse proxy is used for providing SSL encryption, an explicit defi
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
```
|
||||
|
||||
## Configuring the application root
|
||||
|
||||
*Please be advised that this feature is in BETA.*
|
||||
|
||||
Superset supports running the application under a non-root path. The root path
|
||||
prefix can be specified in one of two ways:
|
||||
|
||||
- Setting the `SUPERSET_APP_ROOT` environment variable to the desired prefix.
|
||||
- Customizing the [Flask entrypoint](https://github.com/apache/superset/blob/master/superset/app.py#L29)
|
||||
by passing the `superset_app_root` variable.
|
||||
|
||||
Note, the prefix should start with a `/`.
|
||||
|
||||
### Customizing the Flask entrypoint
|
||||
|
||||
To configure a prefix, e.g `/analytics`, pass the `superset_app_root` argument to
|
||||
`create_app` when calling flask run either through the `FLASK_APP`
|
||||
environment variable:
|
||||
|
||||
```sh
|
||||
FLASK_APP="superset:create_app(superset_app_root='/analytics')"
|
||||
```
|
||||
|
||||
or as part of the `--app` argument to `flask run`:
|
||||
|
||||
```sh
|
||||
flask --app "superset.app:create_app(superset_app_root='/analytics')"
|
||||
```
|
||||
|
||||
### Docker builds
|
||||
|
||||
The [docker compose](/docs/installation/docker-compose#configuring-further) developer
|
||||
configuration includes an additional environmental variable,
|
||||
[`SUPERSET_APP_ROOT`](https://github.com/apache/superset/blob/master/docker/.env),
|
||||
to simplify the process of setting up a non-default root path across the services.
|
||||
|
||||
In `docker/.env-local` set `SUPERSET_APP_ROOT` to the desired prefix and then bring the
|
||||
services up with `docker compose up --detach`.
|
||||
|
||||
## Custom OAuth2 Configuration
|
||||
|
||||
Superset is built on Flask-AppBuilder (FAB), which supports many providers out of the box
|
||||
|
||||
@@ -72,7 +72,7 @@ are compatible with Superset.
|
||||
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://{username}:{password}@{hostname}:{port}/{database}` |
|
||||
| [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` |
|
||||
| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache-superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [StarRocks](/docs/configuration/databases#starrocks) | `pip install starrocks` | `starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>` |
|
||||
| [Snowflake](/docs/configuration/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` |
|
||||
@@ -1293,6 +1293,13 @@ The connection string for SQL Server looks like this:
|
||||
mssql+pyodbc:///?odbc_connect=Driver%3D%7BODBC+Driver+17+for+SQL+Server%7D%3BServer%3Dtcp%3A%3Cmy_server%3E%2C1433%3BDatabase%3Dmy_database%3BUid%3Dmy_user_name%3BPwd%3Dmy_password%3BEncrypt%3Dyes%3BConnection+Timeout%3D30
|
||||
```
|
||||
|
||||
:::note
|
||||
You might have noticed that some special charecters are used in the above connection string. For example see the `odbc_connect` parameter. The value is `Driver%3D%7BODBC+Driver+17+for+SQL+Server%7D%3B` which is a URL-encoded form of `Driver={ODBC+Driver+17+for+SQL+Server};`. It's important to give the connection string is URL encoded.
|
||||
|
||||
For more information about this check the [sqlalchemy documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#escaping-special-characters-such-as-signs-in-passwords). Which says `When constructing a fully formed URL string to pass to create_engine(), special characters such as those that may be used in the user and password need to be URL encoded to be parsed correctly. This includes the @ sign.`
|
||||
:::
|
||||
|
||||
|
||||
#### StarRocks
|
||||
|
||||
The [sqlalchemy-starrocks](https://pypi.org/project/starrocks/) library is the recommended
|
||||
|
||||
@@ -11,7 +11,7 @@ version: 1
|
||||
To configure CORS, or cross-origin resource sharing, the following dependency must be installed:
|
||||
|
||||
```python
|
||||
pip install apache-superset[cors]
|
||||
pip install apache_superset[cors]
|
||||
```
|
||||
|
||||
The following keys in `superset_config.py` can be specified to configure CORS:
|
||||
@@ -138,4 +138,4 @@ of your additional middleware classes.
|
||||
|
||||
For example, to use `AUTH_REMOTE_USER` from behind a proxy server like nginx, you have to add a
|
||||
simple middleware class to add the value of `HTTP_X_PROXY_REMOTE_USER` (or any other custom header
|
||||
from the proxy) to Gunicorn’s `REMOTE_USER` environment variable:
|
||||
from the proxy) to Gunicorn’s `REMOTE_USER` environment variable.
|
||||
|
||||
@@ -26,9 +26,9 @@ More references:
|
||||
Here's a list of repositories that contain Superset-related packages:
|
||||
|
||||
- [apache/superset](https://github.com/apache/superset)
|
||||
is the main repository containing the `apache-superset` Python package
|
||||
is the main repository containing the `apache_superset` Python package
|
||||
distributed on
|
||||
[pypi](https://pypi.org/project/apache-superset/). This repository
|
||||
[pypi](https://pypi.org/project/apache_superset/). This repository
|
||||
also includes Superset's main TypeScript/JavaScript bundles and react apps under
|
||||
the [superset-frontend](https://github.com/apache/superset/tree/master/superset-frontend)
|
||||
folder.
|
||||
|
||||
@@ -4,9 +4,95 @@ version: 1
|
||||
---
|
||||
|
||||
import InteractiveSVG from '../../src/components/InteractiveERDSVG';
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# Resources
|
||||
|
||||
## High Level Architecture
|
||||
<div style={{ maxWidth: "600px", margin: "0 auto", marginLeft: 0, marginRight: "auto" }}>
|
||||
```mermaid
|
||||
flowchart TD
|
||||
|
||||
%% Top Level
|
||||
LB["<b>Load Balancer(s)</b><br/>(optional)"]
|
||||
LB -.-> WebServers
|
||||
|
||||
%% Web Servers
|
||||
subgraph WebServers ["<b>Web Server(s)</b>"]
|
||||
WS1["<b>Frontend</b><br/>(React, AntD, ECharts, AGGrid)"]
|
||||
WS2["<b>Backend</b><br/>(Python, Flask, SQLAlchemy, Pandas, ...)"]
|
||||
end
|
||||
|
||||
%% Infra
|
||||
subgraph InfraServices ["<b>Infra</b>"]
|
||||
DB[("<b>Metadata Database</b><br/>(Postgres / MySQL)")]
|
||||
|
||||
subgraph Caching ["<b>Caching Subservices<br/></b>(Redis, memcache, S3, ...)"]
|
||||
direction LR
|
||||
DummySpace[" "]:::invisible
|
||||
QueryCache["<b>Query Results Cache</b><br/>(Accelerated Dashboards)"]
|
||||
CsvCache["<b>CSV Exports Cache</b>"]
|
||||
ThumbnailCache["<b>Thumbnails Cache</b>"]
|
||||
AlertImageCache["<b>Alert/Report Images Cache</b>"]
|
||||
QueryCache -- " " --> CsvCache
|
||||
linkStyle 1 stroke:transparent;
|
||||
ThumbnailCache -- " " --> AlertImageCache
|
||||
linkStyle 2 stroke:transparent;
|
||||
end
|
||||
|
||||
Broker(("<b>Message Queue</b><br/>(Redis / RabbitMQ / SQS)"))
|
||||
end
|
||||
|
||||
AsyncBackend["<b>Async Workers (Celery)</b><br>required for Alerts & Reports, thumbnails, CSV exports, long-running workloads, ..."]
|
||||
|
||||
%% External DBs
|
||||
subgraph ExternalDatabases ["<b>Analytics Databases</b>"]
|
||||
direction LR
|
||||
BigQuery[(BigQuery)]
|
||||
Snowflake[(Snowflake)]
|
||||
Redshift[(Redshift)]
|
||||
Postgres[(Postgres)]
|
||||
Postgres[(... any ...)]
|
||||
end
|
||||
|
||||
%% Connections
|
||||
LB -.-> WebServers
|
||||
WebServers --> DB
|
||||
WebServers -.-> Caching
|
||||
WebServers -.-> Broker
|
||||
WebServers -.-> ExternalDatabases
|
||||
|
||||
Broker -.-> AsyncBackend
|
||||
|
||||
AsyncBackend -.-> ExternalDatabases
|
||||
AsyncBackend -.-> Caching
|
||||
|
||||
|
||||
|
||||
%% Legend styling
|
||||
classDef requiredNode stroke-width:2px,stroke:black;
|
||||
class Required requiredNode;
|
||||
class Optional optionalNode;
|
||||
|
||||
%% Hide real arrow
|
||||
linkStyle 0 stroke:transparent;
|
||||
|
||||
%% Styling
|
||||
classDef optionalNode stroke-dasharray: 5 5, opacity:0.9;
|
||||
class LB optionalNode;
|
||||
class Caching optionalNode;
|
||||
class AsyncBackend optionalNode;
|
||||
class Broker optionalNode;
|
||||
class QueryCache optionalNode;
|
||||
class CsvCache optionalNode;
|
||||
class ThumbnailCache optionalNode;
|
||||
class AlertImageCache optionalNode;
|
||||
class Celery optionalNode;
|
||||
|
||||
classDef invisible fill:transparent,stroke:transparent;
|
||||
```
|
||||
</div>
|
||||
|
||||
## Entity-Relationship Diagram
|
||||
|
||||
Here is our interactive ERD:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Docker Builds
|
||||
hide_title: true
|
||||
sidebar_position: 6
|
||||
sidebar_position: 7
|
||||
version: 1
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Docker Compose
|
||||
hide_title: true
|
||||
sidebar_position: 4
|
||||
sidebar_position: 5
|
||||
version: 1
|
||||
---
|
||||
|
||||
@@ -112,7 +112,15 @@ docker compose -f docker-compose-non-dev.yml up
|
||||
### Option #3 - boot up an official release
|
||||
|
||||
```bash
|
||||
# Set the version you want to run
|
||||
export TAG=3.1.1
|
||||
# Fetch the tag you're about to check out (assuming you shallow-cloned the repo)
|
||||
git fetch --depth=1 origin tag $TAG
|
||||
# Could also fetch all tags too if you've got bandwidth to spare
|
||||
# git fetch --tags
|
||||
# Checkout the corresponding git ref
|
||||
git checkout $TAG
|
||||
# Fire up docker compose
|
||||
docker compose -f docker-compose-image-tag.yml up
|
||||
```
|
||||
|
||||
|
||||
58
docs/docs/installation/installation-methods.mdx
Normal file
58
docs/docs/installation/installation-methods.mdx
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Installation Methods
|
||||
hide_title: true
|
||||
sidebar_position: 2
|
||||
version: 1
|
||||
---
|
||||
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
|
||||
# Installation Methods
|
||||
|
||||
How should you install Superset? Here's a comparison of the different options. It will help if you've first read the [Architecture](/docs/installation/architecture.mdx) page to understand Superset's different components.
|
||||
|
||||
The fundamental trade-off is between you needing to do more of the detail work yourself vs. using a more complex deployment route that handles those details.
|
||||
|
||||
## [Docker Compose](/docs/installation/docker-compose.mdx)
|
||||
|
||||
**Summary:** This takes advantage of containerization while remaining simpler than Kubernetes. This is the best way to try out Superset; it's also useful for developing & contributing back to Superset.
|
||||
|
||||
If you're not just demoing the software, you'll need a moderate understanding of Docker to customize your deployment and avoid a few risks. Even when fully-optimized this is not as robust a method as Kubernetes when it comes to large-scale production deployments.
|
||||
|
||||
You manage a superset-config.py file and a docker-compose.yml file. Docker Compose brings up all the needed services - the Superset application, a Postgres metadata DB, Redis cache, Celery worker and beat. They are automatically connected to each other.
|
||||
|
||||
**Responsibilities**
|
||||
|
||||
You will need to back up your metadata DB. That could mean backing up the service running as a Docker container and its volume; ideally you are running Postgres as a service outside of that container and backing up that service.
|
||||
|
||||
You will also need to extend the Superset docker image. The default `lean` images do not contain drivers needed to access your metadata database (Postgres or MySQL), nor to access your data warehouse, nor the headless browser needed for Alerts & Reports. You could run a `-dev` image while demoing Superset, which has some of this, but you'll still need to install the driver for your data warehouse. The `-dev` images run as root, which is not recommended for production.
|
||||
|
||||
Ideally you will build your own image of Superset that extends `lean`, adding what your deployment needs.
|
||||
|
||||
See [Docker Build Presets](/docs/installation/docker-builds/#build-presets) for more information about the different image versions you can extend.
|
||||
|
||||
## [Kubernetes (K8s)](/docs/installation/kubernetes.mdx)
|
||||
|
||||
**Summary:** This is the best-practice way to deploy a production instance of Superset, but has the steepest skill requirement - someone who knows Kubernetes.
|
||||
|
||||
You will deploy Superset into a K8s cluster. The most common method is using the community-maintained Helm chart, though work is now underway to implement [SIP-149 - a Kubernetes Operator for Superset](https://github.com/apache/superset/issues/31408).
|
||||
|
||||
A K8s deployment can scale up and down based on usage and deploy rolling updates with zero downtime - features that big deployments appreciate.
|
||||
|
||||
**Responsibilities**
|
||||
|
||||
You will need to build your own Docker image, and back up your metadata DB, both as described in Docker Compose above. You'll also need to customize your Helm chart values and deploy and maintain your Kubernetes cluster.
|
||||
|
||||
## [PyPI (Python)](/docs/installation/pypi.mdx)
|
||||
|
||||
**Summary:** This is the only method that requires no knowledge of containers. It requires the most hands-on work to deploy, connect, and maintain each component.
|
||||
|
||||
You install Superset as a Python package and run it that way, providing your own metadata database. Superset has documentation on how to install this way, but it is updated infrequently.
|
||||
|
||||
If you want caching, you'll set up Redis or RabbitMQ. If you want Alerts & Reports, you'll set up Celery.
|
||||
|
||||
**Responsibilities**
|
||||
|
||||
You will need to get the component services running and communicating with each other. You'll need to arrange backups of your metadata database.
|
||||
|
||||
When upgrading, you'll need to manage the system environment and packages and ensure all components have functional dependencies.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Kubernetes
|
||||
hide_title: true
|
||||
sidebar_position: 2
|
||||
sidebar_position: 3
|
||||
version: 1
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: PyPI
|
||||
hide_title: true
|
||||
sidebar_position: 3
|
||||
sidebar_position: 4
|
||||
version: 1
|
||||
---
|
||||
|
||||
@@ -12,7 +12,7 @@ import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
<img src={useBaseUrl("/img/pypi.png" )} width="150" />
|
||||
<br /><br />
|
||||
|
||||
This page describes how to install Superset using the `apache-superset` package [published on PyPI](https://pypi.org/project/apache-superset/).
|
||||
This page describes how to install Superset using the `apache_superset` package [published on PyPI](https://pypi.org/project/apache_superset/).
|
||||
|
||||
## OS Dependencies
|
||||
|
||||
@@ -124,10 +124,10 @@ command line.
|
||||
|
||||
### Installing and Initializing Superset
|
||||
|
||||
First, start by installing `apache-superset`:
|
||||
First, start by installing `apache_superset`:
|
||||
|
||||
```bash
|
||||
pip install apache-superset
|
||||
pip install apache_superset
|
||||
```
|
||||
|
||||
Then, define mandatory configurations, SECRET_KEY and FLASK_APP:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Upgrading Superset
|
||||
hide_title: true
|
||||
sidebar_position: 5
|
||||
sidebar_position: 6
|
||||
version: 1
|
||||
---
|
||||
|
||||
@@ -32,7 +32,7 @@ docker compose up
|
||||
To upgrade superset in a native installation, run the following commands:
|
||||
|
||||
```bash
|
||||
pip install apache-superset --upgrade
|
||||
pip install apache_superset --upgrade
|
||||
```
|
||||
|
||||
## Upgrading the Metadata Database
|
||||
|
||||
@@ -32,7 +32,7 @@ git clone https://github.com/apache/superset
|
||||
$ cd superset
|
||||
|
||||
# Set the repo to the state associated with the latest official version
|
||||
$ git checkout tags/4.1.1
|
||||
$ git checkout tags/4.1.2
|
||||
|
||||
# Fire up Superset using Docker Compose
|
||||
$ docker compose -f docker-compose-image-tag.yml up
|
||||
|
||||
@@ -64,6 +64,26 @@ tables in the **Permissions** dropdown. To select the data sources you want to a
|
||||
You can then confirm with users assigned to the **Gamma** role that they see the
|
||||
objects (dashboards and slices) associated with the tables you just extended them.
|
||||
|
||||
### SQL Execution Security Considerations
|
||||
|
||||
Apache Superset includes features designed to provide safeguards when interacting with connected databases, such as the `DISALLOWED_SQL_FUNCTIONS` configuration setting. This aims to prevent the execution of potentially harmful database functions or system variables directly from Superset interfaces like SQL Lab.
|
||||
|
||||
However, it is crucial to understand the following:
|
||||
|
||||
**Superset is Not a Database Firewall**: Superset's built-in checks, like `DISALLOWED_SQL_FUNCTIONS`, provide a layer of protection but cannot guarantee complete security against all database-level threats or advanced bypass techniques (like specific comment injection methods). They should be viewed as a supplement to, not a replacement for, robust database security.
|
||||
|
||||
**Configuration is Key**: The effectiveness of Superset's safeguards heavily depends on proper configuration by the Superset administrator. This includes maintaining the `DISALLOWED_SQL_FUNCTIONS` list, carefully managing feature flags (like `ENABLE_TEMPLATE_PROCESSING`), and configuring other security settings appropriately.
|
||||
|
||||
**Database Security is Paramount**: The ultimate responsibility for securing database access, controlling permissions, and preventing unauthorized function execution lies with the database administrators (DBAs) and security teams managing the underlying database instance.
|
||||
|
||||
**Recommended Database Practices**: We strongly recommend implementing security best practices at the database level, including:
|
||||
* **Least Privilege**: Connecting Superset using dedicated database user accounts with the minimum permissions required for Superset's operation (typically read-only access to necessary schemas/tables).
|
||||
* **Database Roles & Permissions**: Utilizing database-native roles and permissions to restrict access to sensitive functions, system variables (like `@@hostname`), schemas, or tables.
|
||||
* **Network Security**: Employing network-level controls like database firewalls or proxies to restrict connections.
|
||||
* **Auditing**: Enabling database-level auditing to monitor executed queries and access patterns.
|
||||
|
||||
By combining Superset's configurable safeguards with strong database-level security practices, you can achieve a more robust and layered security posture.
|
||||
|
||||
### REST API for user & role management
|
||||
|
||||
Flask-AppBuilder supports a REST API for user CRUD,
|
||||
|
||||
@@ -12,8 +12,12 @@ import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
This section is focused on documentation for end-users who will be using Superset
|
||||
for the data analysis and exploration workflow
|
||||
(data analysts, business analysts, data
|
||||
scientists, etc). In addition to this site, [Preset.io](http://preset.io/) maintains an updated set of end-user
|
||||
scientists, etc).
|
||||
|
||||
:::tip
|
||||
In addition to this site, [Preset.io](http://preset.io/) maintains an updated set of end-user
|
||||
documentation at [docs.preset.io](https://docs.preset.io/).
|
||||
:::
|
||||
|
||||
This tutorial targets someone who wants to create charts and dashboards in Superset. We’ll show you
|
||||
how to connect Superset to a new database and configure a table in that database for analysis.
|
||||
@@ -175,23 +179,36 @@ into a position you like onto the underlying grid.
|
||||
|
||||
Congrats! You’ve successfully linked, analyzed, and visualized data in Superset. There are a wealth
|
||||
of other table configuration and visualization options, so please start exploring and creating
|
||||
slices and dashboards of your own
|
||||
|
||||
ֿ
|
||||
slices and dashboards of your own.
|
||||
|
||||
### Manage access to Dashboards
|
||||
|
||||
Access to dashboards is managed via owners (users that have edit permissions to the dashboard)
|
||||
Access to dashboards is managed via owners (users that have edit permissions to the dashboard).
|
||||
|
||||
Non-owner users access can be managed two different ways:
|
||||
Non-owner users access can be managed in two different ways. The dashboard needs to be published to be visible to other users.
|
||||
|
||||
1. Dataset permissions - if you add to the relevant role permissions to datasets it automatically grants implicit access to all dashboards that uses those permitted datasets
|
||||
2. Dashboard roles - if you enable **DASHBOARD_RBAC** [feature flag](/docs/configuration/configuring-superset#feature-flags) then you be able to manage which roles can access the dashboard
|
||||
1. Dataset permissions - if you add to the relevant role permissions to datasets it automatically grants implicit access to all dashboards that uses those permitted datasets.
|
||||
2. Dashboard roles - if you enable [**DASHBOARD_RBAC** feature flag](/docs/configuration/configuring-superset#feature-flags) then you will be able to manage which roles can access the dashboard
|
||||
- Granting a role access to a dashboard will bypass dataset level checks. Having dashboard access implicitly grants read access to all the featured charts in the dashboard, and thereby also all the associated datasets.
|
||||
- If no roles are specified for a dashboard, regular **Dataset permissions** will apply.
|
||||
|
||||
<img src={useBaseUrl("/img/tutorial/tutorial_dashboard_access.png" )} />
|
||||
|
||||
### Publishing a Dashboard
|
||||
|
||||
If you would like to make your dashboard available to other users, click on the `Draft` button next to the
|
||||
title of your dashboard.
|
||||
|
||||
<img src={useBaseUrl("/img/tutorial/publish_button_dashboard.png" )} />
|
||||
|
||||
:::warning
|
||||
Draft dashboards are only visible to the dashboard owners and admins. Published dashboards are visible to all users with access to the underlying datasets or if RBAC is enabled, to the roles that have been granted access to the dashboard.
|
||||
:::
|
||||
|
||||
### Mark a Dashboard as Favorite
|
||||
|
||||
You can mark a dashboard as a favorite by clicking on the star icon next to the title of your dashboard. This makes it easier to find it in the list of dashboards or on the home page.
|
||||
|
||||
### Customizing dashboard
|
||||
|
||||
The following URL parameters can be used to modify how the dashboard is rendered:
|
||||
|
||||
@@ -31,10 +31,13 @@ const config: Config = {
|
||||
baseUrl: '/',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'throw',
|
||||
markdown: {
|
||||
mermaid: true,
|
||||
},
|
||||
favicon: '/img/favicon.ico',
|
||||
organizationName: 'apache',
|
||||
projectName: 'superset',
|
||||
themes: ['@saucelabs/theme-github-codeblock'],
|
||||
themes: ['@saucelabs/theme-github-codeblock', '@docusaurus/theme-mermaid'],
|
||||
plugins: [
|
||||
[
|
||||
'docusaurus-plugin-less',
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.5.2",
|
||||
"@docusaurus/core": "^3.5.2",
|
||||
"@docusaurus/plugin-client-redirects": "^3.5.2",
|
||||
"@docusaurus/preset-classic": "^3.5.2",
|
||||
"@docusaurus/core": "3.7.0",
|
||||
"@docusaurus/plugin-client-redirects": "3.7.0",
|
||||
"@docusaurus/preset-classic": "3.7.0",
|
||||
"@docusaurus/theme-mermaid": "3.7.0",
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"@saucelabs/theme-github-codeblock": "^0.3.0",
|
||||
"@superset-ui/style": "^0.14.23",
|
||||
"antd": "^5.24.2",
|
||||
"antd": "^5.24.5",
|
||||
"docusaurus-plugin-less": "^2.0.2",
|
||||
"less": "^4.2.2",
|
||||
"less-loader": "^11.0.0",
|
||||
@@ -34,7 +35,7 @@
|
||||
"react-dom": "^18.3.1",
|
||||
"react-github-btn": "^1.4.0",
|
||||
"react-svg-pan-zoom": "^3.13.1",
|
||||
"swagger-ui-react": "^5.20.0"
|
||||
"swagger-ui-react": "^5.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||
@@ -43,7 +44,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-prettier": "^10.0.2",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.0.0",
|
||||
"prettier": "^2.0.0",
|
||||
|
||||
@@ -111,7 +111,7 @@ const StyledTitleContainer = styled('div')`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Link)`
|
||||
const StyledButton = styled(Link as React.ComponentType<any>)`
|
||||
border-radius: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
|
||||
BIN
docs/static/img/tutorial/publish_button_dashboard.png
vendored
Normal file
BIN
docs/static/img/tutorial/publish_button_dashboard.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
7377
docs/yarn.lock
7377
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
apiVersion: v2
|
||||
appVersion: "4.1.1"
|
||||
appVersion: "4.1.2"
|
||||
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.14.1
|
||||
version: 0.14.2
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 13.4.4
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
|
||||
|
||||
# superset
|
||||
|
||||

|
||||

|
||||
|
||||
Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ requires = ["setuptools>=40.9.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "apache-superset"
|
||||
name = "apache_superset"
|
||||
description = "A modern, enterprise-ready business intelligence web application"
|
||||
readme = "README.md"
|
||||
dynamic = ["version", "scripts", "entry-points"]
|
||||
@@ -125,7 +125,7 @@ denodo = ["denodo-sqlalchemy~=1.0.6"]
|
||||
dremio = ["sqlalchemy-dremio>=1.2.1, <4"]
|
||||
drill = ["sqlalchemy-drill>=1.1.4, <2"]
|
||||
druid = ["pydruid>=0.6.5,<0.7"]
|
||||
duckdb = ["duckdb-engine>=0.10", "duckdb>=1.1.0"]
|
||||
duckdb = ["duckdb-engine>=0.12.1, <0.13"]
|
||||
dynamodb = ["pydynamodb>=0.4.2"]
|
||||
solr = ["sqlalchemy-solr >= 0.2.0"]
|
||||
elasticsearch = ["elasticsearch-dbapi>=0.2.9, <0.3.0"]
|
||||
@@ -146,6 +146,7 @@ hive = [
|
||||
impala = ["impyla>0.16.2, <0.17"]
|
||||
kusto = ["sqlalchemy-kusto>=3.0.0, <4"]
|
||||
kylin = ["kylinpy>=2.8.1, <2.9"]
|
||||
motherduck = ["duckdb==0.10.2", "duckdb-engine>=0.12.1, <0.13"]
|
||||
mssql = ["pymssql>=2.2.8, <3"]
|
||||
mysql = ["mysqlclient>=2.1.0, <3"]
|
||||
ocient = [
|
||||
|
||||
@@ -28,9 +28,8 @@ async_timeout>=4.0.0,<5.0.0
|
||||
# a bit of attention to bump.
|
||||
apispec>=6.0.0,<6.7.0
|
||||
|
||||
# 1.4.0 appears to use much more memory, where the python test suite runs out of memory
|
||||
# causing CI to fail. 1.3.0 is the last version that works.
|
||||
# This is probably related to the changes around PickleType
|
||||
# 1.4.1 appears to use much more memory, where the python test suite runs out of memory
|
||||
# causing CI to fail. 1.4.0 is the last version that works.
|
||||
# https://marshmallow-sqlalchemy.readthedocs.io/en/latest/changelog.html#id3
|
||||
# Opened this issue https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665
|
||||
marshmallow-sqlalchemy>=1.3.0,<1.4.0
|
||||
marshmallow-sqlalchemy>=1.3.0,<1.4.1
|
||||
|
||||
@@ -44,7 +44,7 @@ cachetools==5.5.2
|
||||
# via google-auth
|
||||
cattrs==24.1.2
|
||||
# via requests-cache
|
||||
celery==5.4.0
|
||||
celery==5.5.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
certifi==2025.1.31
|
||||
# via
|
||||
@@ -174,7 +174,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
importlib-metadata==8.6.1
|
||||
importlib-metadata==8.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
isodate==0.7.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -192,13 +192,13 @@ jsonschema==4.23.0
|
||||
# via flask-appbuilder
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via jsonschema
|
||||
kombu==5.5.0
|
||||
kombu==5.5.3
|
||||
# via celery
|
||||
korean-lunar-calendar==0.3.1
|
||||
# via holidays
|
||||
limits==4.4.1
|
||||
limits==5.1.0
|
||||
# via flask-limiter
|
||||
mako==1.3.9
|
||||
mako==1.3.10
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# alembic
|
||||
@@ -216,7 +216,7 @@ marshmallow==3.26.1
|
||||
# via
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
marshmallow-sqlalchemy==1.3.0
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask-appbuilder
|
||||
@@ -245,7 +245,7 @@ ordered-set==4.1.0
|
||||
# via flask-limiter
|
||||
outcome==1.3.0.post0
|
||||
# via trio
|
||||
packaging==24.2
|
||||
packaging==25.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apispec
|
||||
@@ -272,7 +272,7 @@ polyline==2.0.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
prison==0.2.1
|
||||
# via flask-appbuilder
|
||||
prompt-toolkit==3.0.50
|
||||
prompt-toolkit==3.0.51
|
||||
# via click-repl
|
||||
pyarrow==14.0.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -295,7 +295,7 @@ pynacl==1.5.0
|
||||
# via paramiko
|
||||
pyopenssl==25.0.0
|
||||
# via shillelagh
|
||||
pyparsing==3.2.2
|
||||
pyparsing==3.2.3
|
||||
# via apache-superset (pyproject.toml)
|
||||
pysocks==1.7.1
|
||||
# via urllib3
|
||||
@@ -308,11 +308,11 @@ python-dateutil==2.9.0.post0
|
||||
# holidays
|
||||
# pandas
|
||||
# shillelagh
|
||||
python-dotenv==1.0.1
|
||||
python-dotenv==1.1.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
python-geohash==0.8.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
pytz==2025.1
|
||||
pytz==2025.2
|
||||
# via
|
||||
# croniter
|
||||
# flask-babel
|
||||
@@ -374,7 +374,7 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
sqlglot==26.11.1
|
||||
sqlglot==26.16.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
sqlparse==0.5.3
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -399,9 +399,8 @@ typing-extensions==4.12.2
|
||||
# rich
|
||||
# selenium
|
||||
# shillelagh
|
||||
tzdata==2025.1
|
||||
tzdata==2025.2
|
||||
# via
|
||||
# celery
|
||||
# kombu
|
||||
# pandas
|
||||
url-normalize==1.4.3
|
||||
|
||||
@@ -72,7 +72,7 @@ cattrs==24.1.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests-cache
|
||||
celery==5.4.0
|
||||
celery==5.5.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -355,7 +355,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
importlib-metadata==8.6.1
|
||||
importlib-metadata==8.7.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -396,7 +396,7 @@ jsonschema-specifications==2024.10.1
|
||||
# openapi-schema-validator
|
||||
kiwisolver==1.4.7
|
||||
# via matplotlib
|
||||
kombu==5.5.0
|
||||
kombu==5.5.3
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# celery
|
||||
@@ -406,11 +406,11 @@ korean-lunar-calendar==0.3.1
|
||||
# holidays
|
||||
lazy-object-proxy==1.10.0
|
||||
# via openapi-spec-validator
|
||||
limits==4.4.1
|
||||
limits==5.1.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask-limiter
|
||||
mako==1.3.9
|
||||
mako==1.3.10
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# alembic
|
||||
@@ -435,7 +435,7 @@ marshmallow==3.26.1
|
||||
# -c requirements/base.txt
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
marshmallow-sqlalchemy==1.3.0
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask-appbuilder
|
||||
@@ -496,7 +496,7 @@ outcome==1.3.0.post0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# trio
|
||||
packaging==24.2
|
||||
packaging==25.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -566,7 +566,7 @@ prison==0.2.1
|
||||
# flask-appbuilder
|
||||
progress==1.6
|
||||
# via apache-superset
|
||||
prompt-toolkit==3.0.50
|
||||
prompt-toolkit==3.0.51
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# click-repl
|
||||
@@ -636,7 +636,7 @@ pyopenssl==25.0.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# shillelagh
|
||||
pyparsing==3.2.2
|
||||
pyparsing==3.2.3
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -669,7 +669,7 @@ python-dateutil==2.9.0.post0
|
||||
# pyhive
|
||||
# shillelagh
|
||||
# trino
|
||||
python-dotenv==1.0.1
|
||||
python-dotenv==1.1.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -679,7 +679,7 @@ python-geohash==0.8.5
|
||||
# apache-superset
|
||||
python-ldap==3.4.4
|
||||
# via apache-superset
|
||||
pytz==2025.1
|
||||
pytz==2025.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# croniter
|
||||
@@ -800,7 +800,7 @@ sqlalchemy-utils==0.38.3
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
sqlglot==26.11.1
|
||||
sqlglot==26.16.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -851,10 +851,9 @@ typing-extensions==4.12.2
|
||||
# rich
|
||||
# selenium
|
||||
# shillelagh
|
||||
tzdata==2025.1
|
||||
tzdata==2025.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# celery
|
||||
# kombu
|
||||
# pandas
|
||||
tzlocal==5.2
|
||||
|
||||
@@ -63,7 +63,10 @@ def fetch_files_github_api(url: str): # type: ignore
|
||||
|
||||
def fetch_changed_files_pr(repo: str, pr_number: str) -> List[str]:
|
||||
"""Fetches files changed in a PR using the GitHub API."""
|
||||
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/files"
|
||||
|
||||
# NOTE: limited to 100 files ideally should page-through but instead resorting
|
||||
# to assuming we should trigger when 100 files have been touched
|
||||
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/files?per_page=100"
|
||||
files = fetch_files_github_api(url)
|
||||
return [file_info["filename"] for file_info in files]
|
||||
|
||||
@@ -103,7 +106,7 @@ def main(event_type: str, sha: str, repo: str) -> None:
|
||||
"""Main function to check for file changes based on event context."""
|
||||
print("SHA:", sha)
|
||||
print("EVENT_TYPE", event_type)
|
||||
files = None
|
||||
files = []
|
||||
if event_type == "pull_request":
|
||||
pr_number = os.getenv("GITHUB_REF", "").split("/")[-2]
|
||||
if is_int(pr_number):
|
||||
@@ -133,8 +136,11 @@ def main(event_type: str, sha: str, repo: str) -> None:
|
||||
output_path = os.getenv("GITHUB_OUTPUT") or "/tmp/GITHUB_OUTPUT.txt" # noqa: S108
|
||||
with open(output_path, "a") as f:
|
||||
for check, changed in changes_detected.items():
|
||||
if changed:
|
||||
print(f"{check}={str(changed).lower()}", file=f)
|
||||
# NOTE: as noted above, we assume that if 100 files are touched, we should
|
||||
# trigger all checks. This is a workaround for the GitHub API limit of 100
|
||||
# files. Using >= 99 because off-by-one errors are not uncommon
|
||||
if changed or len(files) >= 99:
|
||||
print(f"{check}=true", file=f)
|
||||
print(f"Triggering group: {check}")
|
||||
|
||||
|
||||
|
||||
1
setup.py
1
setup.py
@@ -52,6 +52,7 @@ with open(VERSION_INFO_FILE, "w") as version_file:
|
||||
version_string = version_string.replace("-dev", ".dev0")
|
||||
|
||||
setup(
|
||||
name="apache_superset",
|
||||
version=version_string,
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import {
|
||||
DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY,
|
||||
IFRAME_COMMS_MESSAGE_TYPE
|
||||
IFRAME_COMMS_MESSAGE_TYPE,
|
||||
} from './const';
|
||||
|
||||
// We can swap this out for the actual switchboard package once it gets published
|
||||
@@ -34,50 +34,62 @@ import { getGuestTokenRefreshTiming } from './guestTokenRefresh';
|
||||
export type GuestTokenFetchFn = () => Promise<string>;
|
||||
|
||||
export type UiConfigType = {
|
||||
hideTitle?: boolean
|
||||
hideTab?: boolean
|
||||
hideChartControls?: boolean
|
||||
hideTitle?: boolean;
|
||||
hideTab?: boolean;
|
||||
hideChartControls?: boolean;
|
||||
emitDataMasks?: boolean;
|
||||
filters?: {
|
||||
[key: string]: boolean | undefined
|
||||
visible?: boolean
|
||||
expanded?: boolean
|
||||
}
|
||||
[key: string]: boolean | undefined;
|
||||
visible?: boolean;
|
||||
expanded?: boolean;
|
||||
};
|
||||
urlParams?: {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
|
||||
export type EmbedDashboardParams = {
|
||||
/** The id provided by the embed configuration UI in Superset */
|
||||
id: string
|
||||
id: string;
|
||||
/** The domain where Superset can be located, with protocol, such as: https://superset.example.com */
|
||||
supersetDomain: string
|
||||
supersetDomain: string;
|
||||
/** The html element within which to mount the iframe */
|
||||
mountPoint: HTMLElement
|
||||
mountPoint: HTMLElement;
|
||||
/** A function to fetch a guest token from the Host App's backend server */
|
||||
fetchGuestToken: GuestTokenFetchFn
|
||||
fetchGuestToken: GuestTokenFetchFn;
|
||||
/** The dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded **/
|
||||
dashboardUiConfig?: UiConfigType
|
||||
dashboardUiConfig?: UiConfigType;
|
||||
/** Are we in debug mode? */
|
||||
debug?: boolean
|
||||
debug?: boolean;
|
||||
/** The iframe title attribute */
|
||||
iframeTitle?: string
|
||||
iframeTitle?: string;
|
||||
/** additional iframe sandbox attributes ex (allow-top-navigation, allow-popups-to-escape-sandbox) **/
|
||||
iframeSandboxExtras?: string[]
|
||||
iframeSandboxExtras?: string[];
|
||||
/** force a specific refererPolicy to be used in the iframe request **/
|
||||
referrerPolicy?: ReferrerPolicy
|
||||
}
|
||||
referrerPolicy?: ReferrerPolicy;
|
||||
};
|
||||
|
||||
export type Size = {
|
||||
width: number, height: number
|
||||
}
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type ObserveDataMaskCallbackFn = (
|
||||
dataMask: Record<string, any> & {
|
||||
crossFiltersChanged: boolean;
|
||||
nativeFiltersChanged: boolean;
|
||||
},
|
||||
) => void;
|
||||
export type EmbeddedDashboard = {
|
||||
getScrollSize: () => Promise<Size>
|
||||
unmount: () => void
|
||||
getDashboardPermalink: (anchor: string) => Promise<string>
|
||||
getActiveTabs: () => Promise<string[]>
|
||||
}
|
||||
getScrollSize: () => Promise<Size>;
|
||||
unmount: () => void;
|
||||
getDashboardPermalink: (anchor: string) => Promise<string>;
|
||||
getActiveTabs: () => Promise<string[]>;
|
||||
observeDataMask: (
|
||||
callbackFn: ObserveDataMaskCallbackFn,
|
||||
) => void;
|
||||
getDataMask: () => Record<string, any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Embeds a Superset dashboard into the page using an iframe.
|
||||
@@ -89,7 +101,7 @@ export async function embedDashboard({
|
||||
fetchGuestToken,
|
||||
dashboardUiConfig,
|
||||
debug = false,
|
||||
iframeTitle = "Embedded Dashboard",
|
||||
iframeTitle = 'Embedded Dashboard',
|
||||
iframeSandboxExtras = [],
|
||||
referrerPolicy,
|
||||
}: EmbedDashboardParams): Promise<EmbeddedDashboard> {
|
||||
@@ -101,52 +113,67 @@ export async function embedDashboard({
|
||||
|
||||
log('embedding');
|
||||
|
||||
if (supersetDomain.endsWith("/")) {
|
||||
if (supersetDomain.endsWith('/')) {
|
||||
supersetDomain = supersetDomain.slice(0, -1);
|
||||
}
|
||||
|
||||
function calculateConfig() {
|
||||
let configNumber = 0
|
||||
if(dashboardUiConfig) {
|
||||
if(dashboardUiConfig.hideTitle) {
|
||||
configNumber += 1
|
||||
let configNumber = 0;
|
||||
if (dashboardUiConfig) {
|
||||
if (dashboardUiConfig.hideTitle) {
|
||||
configNumber += 1;
|
||||
}
|
||||
if(dashboardUiConfig.hideTab) {
|
||||
configNumber += 2
|
||||
if (dashboardUiConfig.hideTab) {
|
||||
configNumber += 2;
|
||||
}
|
||||
if(dashboardUiConfig.hideChartControls) {
|
||||
configNumber += 8
|
||||
if (dashboardUiConfig.hideChartControls) {
|
||||
configNumber += 8;
|
||||
}
|
||||
if (dashboardUiConfig.emitDataMasks) {
|
||||
configNumber += 16;
|
||||
}
|
||||
}
|
||||
return configNumber
|
||||
return configNumber;
|
||||
}
|
||||
|
||||
async function mountIframe(): Promise<Switchboard> {
|
||||
return new Promise(resolve => {
|
||||
const iframe = document.createElement('iframe');
|
||||
const dashboardConfigUrlParams = dashboardUiConfig ? {uiConfig: `${calculateConfig()}`} : undefined;
|
||||
const filterConfig = dashboardUiConfig?.filters || {}
|
||||
const filterConfigKeys = Object.keys(filterConfig)
|
||||
const filterConfigUrlParams = Object.fromEntries(filterConfigKeys.map(
|
||||
key => [DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[key], filterConfig[key]]))
|
||||
const dashboardConfigUrlParams = dashboardUiConfig
|
||||
? { uiConfig: `${calculateConfig()}` }
|
||||
: undefined;
|
||||
const filterConfig = dashboardUiConfig?.filters || {};
|
||||
const filterConfigKeys = Object.keys(filterConfig);
|
||||
const filterConfigUrlParams = Object.fromEntries(
|
||||
filterConfigKeys.map(key => [
|
||||
DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[key],
|
||||
filterConfig[key],
|
||||
]),
|
||||
);
|
||||
|
||||
// Allow url query parameters from dashboardUiConfig.urlParams to override the ones from filterConfig
|
||||
const urlParams = {...dashboardConfigUrlParams, ...filterConfigUrlParams, ...dashboardUiConfig?.urlParams}
|
||||
const urlParamsString = Object.keys(urlParams).length ? '?' + new URLSearchParams(urlParams).toString() : ''
|
||||
const urlParams = {
|
||||
...dashboardConfigUrlParams,
|
||||
...filterConfigUrlParams,
|
||||
...dashboardUiConfig?.urlParams,
|
||||
};
|
||||
const urlParamsString = Object.keys(urlParams).length
|
||||
? '?' + new URLSearchParams(urlParams).toString()
|
||||
: '';
|
||||
|
||||
// set up the iframe's sandbox configuration
|
||||
iframe.sandbox.add("allow-same-origin"); // needed for postMessage to work
|
||||
iframe.sandbox.add("allow-scripts"); // obviously the iframe needs scripts
|
||||
iframe.sandbox.add("allow-presentation"); // for fullscreen charts
|
||||
iframe.sandbox.add("allow-downloads"); // for downloading charts as image
|
||||
iframe.sandbox.add("allow-forms"); // for forms to submit
|
||||
iframe.sandbox.add("allow-popups"); // for exporting charts as csv
|
||||
iframe.sandbox.add('allow-same-origin'); // needed for postMessage to work
|
||||
iframe.sandbox.add('allow-scripts'); // obviously the iframe needs scripts
|
||||
iframe.sandbox.add('allow-presentation'); // for fullscreen charts
|
||||
iframe.sandbox.add('allow-downloads'); // for downloading charts as image
|
||||
iframe.sandbox.add('allow-forms'); // for forms to submit
|
||||
iframe.sandbox.add('allow-popups'); // for exporting charts as csv
|
||||
// additional sandbox props
|
||||
iframeSandboxExtras.forEach((key: string) => {
|
||||
iframe.sandbox.add(key);
|
||||
});
|
||||
// force a specific refererPolicy to be used in the iframe request
|
||||
if(referrerPolicy) {
|
||||
if (referrerPolicy) {
|
||||
iframe.referrerPolicy = referrerPolicy;
|
||||
}
|
||||
|
||||
@@ -162,20 +189,26 @@ export async function embedDashboard({
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
// we know the content window isn't null because we are in the load event handler.
|
||||
iframe.contentWindow!.postMessage(
|
||||
{ type: IFRAME_COMMS_MESSAGE_TYPE, handshake: "port transfer" },
|
||||
{ type: IFRAME_COMMS_MESSAGE_TYPE, handshake: 'port transfer' },
|
||||
supersetDomain,
|
||||
[theirPort],
|
||||
)
|
||||
);
|
||||
log('sent message channel to the iframe');
|
||||
|
||||
// return our port from the promise
|
||||
resolve(new Switchboard({ port: ourPort, name: 'superset-embedded-sdk', debug }));
|
||||
resolve(
|
||||
new Switchboard({
|
||||
port: ourPort,
|
||||
name: 'superset-embedded-sdk',
|
||||
debug,
|
||||
}),
|
||||
);
|
||||
});
|
||||
iframe.src = `${supersetDomain}/embedded/${id}${urlParamsString}`;
|
||||
iframe.title = iframeTitle;
|
||||
//@ts-ignore
|
||||
mountPoint.replaceChildren(iframe);
|
||||
log('placed the iframe')
|
||||
log('placed the iframe');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -204,12 +237,21 @@ export async function embedDashboard({
|
||||
const getScrollSize = () => ourPort.get<Size>('getScrollSize');
|
||||
const getDashboardPermalink = (anchor: string) =>
|
||||
ourPort.get<string>('getDashboardPermalink', { anchor });
|
||||
const getActiveTabs = () => ourPort.get<string[]>('getActiveTabs')
|
||||
const getActiveTabs = () => ourPort.get<string[]>('getActiveTabs');
|
||||
const getDataMask = () => ourPort.get<Record<string, any>>('getDataMask');
|
||||
const observeDataMask = (
|
||||
callbackFn: ObserveDataMaskCallbackFn,
|
||||
) => {
|
||||
ourPort.start();
|
||||
ourPort.defineMethod('observeDataMask', callbackFn);
|
||||
};
|
||||
|
||||
return {
|
||||
getScrollSize,
|
||||
unmount,
|
||||
getDashboardPermalink,
|
||||
getActiveTabs,
|
||||
observeDataMask,
|
||||
getDataMask,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
|
||||
describe('explore view', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -184,12 +184,13 @@ describe('Charts list', () => {
|
||||
});
|
||||
|
||||
it('should allow to favorite/unfavorite', () => {
|
||||
cy.intercept({ url: `/api/v1/chart/*/favorites/`, method: 'POST' }).as(
|
||||
cy.intercept({ url: `**/api/v1/chart/*/favorites/`, method: 'POST' }).as(
|
||||
'select',
|
||||
);
|
||||
cy.intercept({ url: `/api/v1/chart/*/favorites/`, method: 'DELETE' }).as(
|
||||
'unselect',
|
||||
);
|
||||
cy.intercept({
|
||||
url: `**/api/v1/chart/*/favorites/`,
|
||||
method: 'DELETE',
|
||||
}).as('unselect');
|
||||
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
|
||||
@@ -27,13 +27,13 @@ describe.skip('Dashboard form data', () => {
|
||||
});
|
||||
|
||||
it('should apply url params to slice requests', () => {
|
||||
cy.intercept('/api/v1/chart/data?*', request => {
|
||||
cy.intercept('**/api/v1/chart/data?*', request => {
|
||||
// TODO: export url params to chart data API
|
||||
request.body.queries.forEach((query: { url_params: JsonObject }) => {
|
||||
expect(query.url_params).deep.eq(urlParams);
|
||||
});
|
||||
});
|
||||
cy.intercept('/superset/explore_json/*', request => {
|
||||
cy.intercept('**/superset/explore_json/*', request => {
|
||||
const requestParams = JSON.parse(
|
||||
parsePostForm(request.body).form_data as string,
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from './utils';
|
||||
|
||||
function interceptSamples() {
|
||||
cy.intercept(`/datasource/samples*`).as('samples');
|
||||
cy.intercept(`**/datasource/samples*`).as('samples');
|
||||
}
|
||||
|
||||
function openModalFromMenu(chartType: string) {
|
||||
@@ -434,7 +434,7 @@ describe('Drill to detail modal', () => {
|
||||
SUPPORTED_TIER2_CHARTS.forEach(waitForChartLoad);
|
||||
});
|
||||
|
||||
describe.only('Modal actions', () => {
|
||||
describe('Modal actions', () => {
|
||||
it('clears filters', () => {
|
||||
interceptSamples();
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ describe('Horizontal FilterBar', () => {
|
||||
});
|
||||
|
||||
it.skip('should spot changes in "more filters" and apply their values', () => {
|
||||
cy.intercept(`/api/v1/chart/data?form_data=**`).as('chart');
|
||||
cy.intercept(`**/api/v1/chart/data?form_data=**`).as('chart');
|
||||
prepareDashboardFilters([
|
||||
{ name: 'test_1', column: 'country_name', datasetId: 2 },
|
||||
{ name: 'test_2', column: 'country_code', datasetId: 2 },
|
||||
|
||||
@@ -170,7 +170,7 @@ describe('Native filters', () => {
|
||||
testItems.datasetForNativeFilter,
|
||||
);
|
||||
saveNativeFilterSettings(WORLD_HEALTH_CHARTS);
|
||||
cy.intercept(`/api/v1/chart/data?form_data=**`).as('chart');
|
||||
cy.intercept(`**/api/v1/chart/data?form_data=**`).as('chart');
|
||||
cy.get(nativeFilters.modal.container).should('not.exist');
|
||||
// assert that native filter is created
|
||||
validateFilterNameOnDashboard(testItems.filterType.timeColumn);
|
||||
|
||||
@@ -55,6 +55,7 @@ export function prepareDashboardFilters(
|
||||
controlValues: {
|
||||
enableEmptyFilter: false,
|
||||
defaultToFirstItem: false,
|
||||
creatable: true,
|
||||
multiSelect: true,
|
||||
searchAllOptions: false,
|
||||
inverseSelection: false,
|
||||
|
||||
@@ -116,7 +116,7 @@ describe('Dashboard tabs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
cy.intercept('/superset/explore_json/?*').as('legacyChartData');
|
||||
cy.intercept('**/superset/explore_json/?*').as('legacyChartData');
|
||||
// click row level tab, send 1 more query
|
||||
cy.get('.ant-tabs-tab').contains('row tab 2').click();
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('Dashboard tabs', () => {
|
||||
expect(requestParams.viz_type).eq(LINE_CHART.viz);
|
||||
});
|
||||
|
||||
cy.intercept('POST', '/api/v1/chart/data?*').as('v1ChartData');
|
||||
cy.intercept('POST', '**/api/v1/chart/data?*').as('v1ChartData');
|
||||
|
||||
// click top level tab, send 1 more query
|
||||
cy.get('.ant-tabs-tab').contains('Tab B').click();
|
||||
|
||||
@@ -125,63 +125,63 @@ export const valueNativeFilterOptions = [
|
||||
];
|
||||
|
||||
export function interceptGet() {
|
||||
cy.intercept('GET', '/api/v1/dashboard/*').as('get');
|
||||
cy.intercept('GET', '**/api/v1/dashboard/*').as('get');
|
||||
}
|
||||
|
||||
export function interceptFiltering() {
|
||||
cy.intercept('GET', `/api/v1/dashboard/?q=*`).as('filtering');
|
||||
cy.intercept('GET', `**/api/v1/dashboard/?q=*`).as('filtering');
|
||||
}
|
||||
|
||||
export function interceptBulkDelete() {
|
||||
cy.intercept('DELETE', `/api/v1/dashboard/?q=*`).as('bulkDelete');
|
||||
cy.intercept('DELETE', `**/api/v1/dashboard/?q=*`).as('bulkDelete');
|
||||
}
|
||||
|
||||
export function interceptDelete() {
|
||||
cy.intercept('DELETE', `/api/v1/dashboard/*`).as('delete');
|
||||
cy.intercept('DELETE', `**/api/v1/dashboard/*`).as('delete');
|
||||
}
|
||||
|
||||
export function interceptUpdate() {
|
||||
cy.intercept('PUT', `/api/v1/dashboard/*`).as('update');
|
||||
cy.intercept('PUT', `**/api/v1/dashboard/*`).as('update');
|
||||
}
|
||||
|
||||
export function interceptExploreUpdate() {
|
||||
cy.intercept('PUT', `/api/v1/chart/*`).as('chartUpdate');
|
||||
cy.intercept('PUT', `**/api/v1/chart/*`).as('chartUpdate');
|
||||
}
|
||||
|
||||
export function interceptPost() {
|
||||
cy.intercept('POST', `/api/v1/dashboard/`).as('post');
|
||||
cy.intercept('POST', `**/api/v1/dashboard/`).as('post');
|
||||
}
|
||||
|
||||
export function interceptLog() {
|
||||
cy.intercept('/superset/log/?explode=events&dashboard_id=*').as('logs');
|
||||
cy.intercept('**/superset/log/?explode=events&dashboard_id=*').as('logs');
|
||||
}
|
||||
|
||||
export function interceptFav() {
|
||||
cy.intercept({ url: `/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
cy.intercept({ url: `**/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
'select',
|
||||
);
|
||||
}
|
||||
|
||||
export function interceptUnfav() {
|
||||
cy.intercept({ url: `/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
cy.intercept({ url: `**/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
'unselect',
|
||||
);
|
||||
}
|
||||
|
||||
export function interceptDataset() {
|
||||
cy.intercept('GET', `/api/v1/dataset/*`).as('getDataset');
|
||||
cy.intercept('GET', `**/api/v1/dataset/*`).as('getDataset');
|
||||
}
|
||||
|
||||
export function interceptCharts() {
|
||||
cy.intercept('GET', `/api/v1/dashboard/*/charts`).as('getCharts');
|
||||
cy.intercept('GET', `**/api/v1/dashboard/*/charts`).as('getCharts');
|
||||
}
|
||||
|
||||
export function interceptDatasets() {
|
||||
cy.intercept('GET', `/api/v1/dashboard/*/datasets`).as('getDatasets');
|
||||
cy.intercept('GET', `**/api/v1/dashboard/*/datasets`).as('getDatasets');
|
||||
}
|
||||
|
||||
export function interceptFilterState() {
|
||||
cy.intercept('POST', `/api/v1/dashboard/*/filter_state*`).as(
|
||||
cy.intercept('POST', `**/api/v1/dashboard/*/filter_state*`).as(
|
||||
'postFilterState',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
*/
|
||||
describe.skip('AdhocFilters', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '/api/v1/datasource/table/*/column/name/values').as(
|
||||
cy.intercept('GET', '**/api/v1/datasource/table/*/column/name/values').as(
|
||||
'filterValues',
|
||||
);
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('postJson');
|
||||
cy.intercept('GET', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('postJson');
|
||||
cy.intercept('GET', '**/superset/explore_json/**').as('getJson');
|
||||
cy.visitChartByName('Boys'); // a table chart
|
||||
cy.verifySliceSuccess({ waitAlias: '@postJson' });
|
||||
});
|
||||
|
||||
@@ -21,8 +21,8 @@ import { interceptV1ChartData } from './utils';
|
||||
describe('Advanced analytics', () => {
|
||||
beforeEach(() => {
|
||||
interceptV1ChartData();
|
||||
cy.intercept('PUT', '/api/v1/explore/**').as('putExplore');
|
||||
cy.intercept('GET', '/explore/**').as('getExplore');
|
||||
cy.intercept('PUT', '**/api/v1/explore/**').as('putExplore');
|
||||
cy.intercept('GET', '**/explore/**').as('getExplore');
|
||||
});
|
||||
|
||||
it('Create custom time compare', () => {
|
||||
|
||||
@@ -146,7 +146,7 @@ describe('Test datatable', () => {
|
||||
});
|
||||
it('Datapane loads view samples', () => {
|
||||
cy.intercept(
|
||||
'datasource/samples?force=false&datasource_type=table&datasource_id=*',
|
||||
'**/datasource/samples?force=false&datasource_type=table&datasource_id=*',
|
||||
).as('Samples');
|
||||
cy.contains('Samples').click();
|
||||
cy.wait('@Samples');
|
||||
|
||||
@@ -20,41 +20,41 @@
|
||||
import { interceptGet as interceptDashboardGet } from '../dashboard/utils';
|
||||
|
||||
export function interceptFiltering() {
|
||||
cy.intercept('GET', `/api/v1/chart/?q=*`).as('filtering');
|
||||
cy.intercept('GET', `**/api/v1/chart/?q=*`).as('filtering');
|
||||
}
|
||||
|
||||
export function interceptBulkDelete() {
|
||||
cy.intercept('DELETE', `/api/v1/chart/?q=*`).as('bulkDelete');
|
||||
cy.intercept('DELETE', `**/api/v1/chart/?q=*`).as('bulkDelete');
|
||||
}
|
||||
|
||||
export function interceptDelete() {
|
||||
cy.intercept('DELETE', `/api/v1/chart/*`).as('delete');
|
||||
cy.intercept('DELETE', `**/api/v1/chart/*`).as('delete');
|
||||
}
|
||||
|
||||
export function interceptFavoriteStatus() {
|
||||
cy.intercept('GET', '/api/v1/chart/favorite_status/*').as('favoriteStatus');
|
||||
cy.intercept('GET', '**/api/v1/chart/favorite_status/*').as('favoriteStatus');
|
||||
}
|
||||
|
||||
export function interceptUpdate() {
|
||||
cy.intercept('PUT', `/api/v1/chart/*`).as('update');
|
||||
cy.intercept('PUT', `**/api/v1/chart/*`).as('update');
|
||||
}
|
||||
|
||||
export const interceptV1ChartData = (alias = 'v1Data') => {
|
||||
cy.intercept('/api/v1/chart/data*').as(alias);
|
||||
cy.intercept('**/api/v1/chart/data*').as(alias);
|
||||
};
|
||||
|
||||
export function interceptExploreJson(alias = 'getJson') {
|
||||
cy.intercept('POST', `/superset/explore_json/**`).as(alias);
|
||||
cy.intercept('POST', `**/superset/explore_json/**`).as(alias);
|
||||
}
|
||||
|
||||
export const interceptFormDataKey = () => {
|
||||
cy.intercept('POST', '/api/v1/explore/form_data').as('formDataKey');
|
||||
cy.intercept('POST', '**/api/v1/explore/form_data').as('formDataKey');
|
||||
};
|
||||
|
||||
export function interceptExploreGet() {
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: /api\/v1\/explore\/\?(form_data_key|dashboard_page_id|slice_id)=.*/,
|
||||
url: /.*\/api\/v1\/explore\/\?(form_data_key|dashboard_page_id|slice_id)=.*/,
|
||||
}).as('getExplore');
|
||||
}
|
||||
|
||||
@@ -96,5 +96,5 @@ export function saveChartToDashboard(dashboardName: string) {
|
||||
|
||||
export function visitSampleChartFromList(chartName: string) {
|
||||
cy.getBySel('table-row').contains(chartName).click();
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ describe('Visualization > Big Number with Trendline', () => {
|
||||
it('should work', () => {
|
||||
verify(BIG_NUMBER_FORM_DATA);
|
||||
cy.get('.chart-container .header-line');
|
||||
cy.get('.chart-container .subheader-line');
|
||||
cy.get('.chart-container canvas');
|
||||
});
|
||||
|
||||
@@ -66,7 +65,7 @@ describe('Visualization > Big Number with Trendline', () => {
|
||||
compare_lag: null,
|
||||
});
|
||||
cy.get('.chart-container .header-line');
|
||||
cy.get('.chart-container .subheader-line').should('not.exist');
|
||||
cy.get('.chart-container .subtitle-line').should('not.exist');
|
||||
cy.get('.chart-container canvas');
|
||||
});
|
||||
|
||||
@@ -76,7 +75,6 @@ describe('Visualization > Big Number with Trendline', () => {
|
||||
show_trend_line: false,
|
||||
});
|
||||
cy.get('[data-test="chart-container"] .header-line');
|
||||
cy.get('[data-test="chart-container"] .subheader-line');
|
||||
cy.get('[data-test="chart-container"] canvas').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Box Plot', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data*').as('getJson');
|
||||
cy.intercept('POST', '**/api/v1/chart/data*').as('getJson');
|
||||
});
|
||||
|
||||
const BOX_PLOT_FORM_DATA = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Bubble', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
const BUBBLE_FORM_DATA = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Compare', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
const COMPARE_FORM_DATA = {
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Download Chart > Bar chart', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
it('download chart with image works', () => {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
describe('Visualization > Gauge', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data*').as('getJson');
|
||||
cy.intercept('POST', '**/api/v1/chart/data*').as('getJson');
|
||||
});
|
||||
|
||||
const GAUGE_FORM_DATA = {
|
||||
|
||||
@@ -28,7 +28,7 @@ type adhocFilter = {
|
||||
|
||||
describe('Visualization > Graph', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data*').as('getJson');
|
||||
cy.intercept('POST', '**/api/v1/chart/data*').as('getJson');
|
||||
});
|
||||
|
||||
const GRAPH_FORM_DATA = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Pie', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data*').as('getJson');
|
||||
cy.intercept('POST', '**/api/v1/chart/data*').as('getJson');
|
||||
});
|
||||
|
||||
const PIE_FORM_DATA = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Pivot Table', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data**').as('chartData');
|
||||
cy.intercept('POST', '**/api/v1/chart/data**').as('chartData');
|
||||
});
|
||||
|
||||
const PIVOT_TABLE_FORM_DATA = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > Sunburst', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/api/v1/chart/data**').as('chartData');
|
||||
cy.intercept('POST', '**/api/v1/chart/data**').as('chartData');
|
||||
});
|
||||
|
||||
const SUNBURST_FORM_DATA = {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper';
|
||||
|
||||
describe('Visualization > Time TableViz', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'time_table' };
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
describe('Visualization > World Map', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
||||
cy.intercept('POST', '**/superset/explore_json/**').as('getJson');
|
||||
});
|
||||
|
||||
const WORLD_MAP_FORM_DATA = {
|
||||
|
||||
@@ -26,7 +26,7 @@ describe.skip('SqlLab datasource panel', () => {
|
||||
// TODO the test below is flaky, and has been disabled for the time being
|
||||
// (notice the `it.skip`)
|
||||
it('creates a table preview when a database, schema, and table are selected', () => {
|
||||
cy.intercept('/superset/table/**').as('tableMetadata');
|
||||
cy.intercept('**/superset/table/**').as('tableMetadata');
|
||||
|
||||
// it should have dropdowns to select database, schema, and table
|
||||
cy.get('.sql-toolbar .Select').should('have.length', 3);
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('SqlLab query panel', () => {
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/api/v1/sqllab/execute/',
|
||||
url: '**/api/v1/sqllab/execute/',
|
||||
}).as('mockSQLResponse');
|
||||
|
||||
cy.get('.TableSelector .Select:eq(0)').click();
|
||||
@@ -79,7 +79,7 @@ describe('SqlLab query panel', () => {
|
||||
});
|
||||
|
||||
it.skip('successfully saves a query', () => {
|
||||
cy.intercept('api/v1/database/**/tables/**').as('getTables');
|
||||
cy.intercept('**/api/v1/database/**/tables/**').as('getTables');
|
||||
|
||||
const query =
|
||||
'SELECT ds, gender, name, num FROM main.birth_names ORDER BY name LIMIT 3';
|
||||
@@ -142,7 +142,7 @@ describe('SqlLab query panel', () => {
|
||||
});
|
||||
|
||||
it.skip('Create a chart from a query', () => {
|
||||
cy.intercept('/api/v1/sqllab/execute/').as('queryFinished');
|
||||
cy.intercept('**/api/v1/sqllab/execute/').as('queryFinished');
|
||||
cy.intercept('**/api/v1/explore/**').as('explore');
|
||||
cy.intercept('**/api/v1/chart/**').as('chart');
|
||||
cy.intercept('**/tabstateview/**').as('tabstateview');
|
||||
|
||||
@@ -65,9 +65,9 @@ export function getChartDataRouteForSlice(slice: Slice) {
|
||||
const isLegacy = isLegacyChart(vizType);
|
||||
const formData = encodeURIComponent(`{"slice_id":${slice.slice_id}}`);
|
||||
if (isLegacy) {
|
||||
return `/superset/explore_json/?*${formData}*`;
|
||||
return `**/superset/explore_json/?*${formData}*`;
|
||||
}
|
||||
return `/api/v1/chart/data?*${formData}*`;
|
||||
return `**/api/v1/chart/data?*${formData}*`;
|
||||
}
|
||||
|
||||
export function getChartAlias(slice: Slice): string {
|
||||
|
||||
30
superset-frontend/cypress-base/package-lock.json
generated
30
superset-frontend/cypress-base/package-lock.json
generated
@@ -2581,11 +2581,12 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
|
||||
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -9244,9 +9245,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.15.1",
|
||||
@@ -12806,11 +12808,11 @@
|
||||
"peer": true
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
|
||||
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
@@ -17857,9 +17859,9 @@
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.15.1",
|
||||
|
||||
373
superset-frontend/package-lock.json
generated
373
superset-frontend/package-lock.json
generated
@@ -152,7 +152,7 @@
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-storybook": "^3.50.9",
|
||||
"@babel/cli": "^7.22.6",
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
"@babel/compat-data": "^7.26.8",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/eslint-parser": "^7.25.9",
|
||||
"@babel/node": "^7.22.6",
|
||||
@@ -234,7 +234,7 @@
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-matchers": "^7.1.2",
|
||||
"eslint": "^8.56.0",
|
||||
@@ -267,7 +267,7 @@
|
||||
"jest-html-reporter": "^3.10.2",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"lerna": "^8.1.7",
|
||||
"lerna": "^8.2.1",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
@@ -1160,9 +1160,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz",
|
||||
"integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==",
|
||||
"version": "7.26.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
|
||||
"integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3747,9 +3747,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz",
|
||||
"integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz",
|
||||
"integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3758,9 +3758,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||
"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz",
|
||||
"integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5581,9 +5581,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lerna/create": {
|
||||
"version": "8.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.9.tgz",
|
||||
"integrity": "sha512-DPnl5lPX4v49eVxEbJnAizrpMdMTBz1qykZrAbBul9rfgk531v8oAt+Pm6O/rpAleRombNM7FJb5rYGzBJatOQ==",
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.2.1.tgz",
|
||||
"integrity": "sha512-Cz2u/fwc03D1EE6VFZCLMmI8FIUtGmxHQ3ECeNblsxv9i0YSKWe4Xm18sjO1xltG/K5ByiH8/HMeY9dlyAv22A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5592,7 +5592,7 @@
|
||||
"@npmcli/run-script": "8.1.0",
|
||||
"@nx/devkit": ">=17.1.2 < 21",
|
||||
"@octokit/plugin-enterprise-rest": "6.0.1",
|
||||
"@octokit/rest": "19.0.11",
|
||||
"@octokit/rest": "20.1.2",
|
||||
"aproba": "2.0.0",
|
||||
"byte-size": "8.1.1",
|
||||
"chalk": "4.1.0",
|
||||
@@ -5645,7 +5645,6 @@
|
||||
"slash": "^3.0.0",
|
||||
"ssri": "^10.0.6",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strong-log-transformer": "2.1.0",
|
||||
"tar": "6.2.1",
|
||||
"temp-dir": "1.0.0",
|
||||
@@ -6097,19 +6096,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@lerna/create/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@lerna/create/node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
@@ -7119,9 +7105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/devkit": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.3.2.tgz",
|
||||
"integrity": "sha512-VhbxEsSTCZlOVgjuQC+6HQmb9Oz9VoHUeo4001Pw6BFBcSXZUi5q37C/lxbAgQPnMKLkFcLva3WKZ+fOLwhGIg==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.7.0.tgz",
|
||||
"integrity": "sha512-BzgeF7sVM6eLVVZIkhqwkSu8hzKx+Wa/bLyMithiU+aLJVGALWFQriDS68MBMA+MHyodKwV3QHQH9/9/UO6uyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7178,9 +7164,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-darwin-arm64": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.3.2.tgz",
|
||||
"integrity": "sha512-lQOXMIPmE9o36TuZ+SX6iq7PPWa3s1fjNRqCujlviExX69245NNCMxd754gXlLrsxC1onrx/zmJciKmmEWDIiw==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.7.0.tgz",
|
||||
"integrity": "sha512-SuPP4AlBGuQjfkgFaGAO66GEUll0Isir5HKa0w0LcehHmxncE9LT4YagEJONPDp6SVDIjpoFIRw71jf9toFvPQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -7195,9 +7181,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-darwin-x64": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.3.2.tgz",
|
||||
"integrity": "sha512-RvvSz4QYVOYOfC8sUE63b6dy8iHk2AEI0r1FF5FCQuqE1DdTeTjPETY2sY35tRqF+mO/6oLGp2+m9ti/ysRoTg==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.7.0.tgz",
|
||||
"integrity": "sha512-eJnGtZmXzhRqitBBSIU7CjUjEGyo7QMZlvTQsqIpkZDay3v3JyQ9tikdGe3L6BwnTgeFQsBxNfK3ddmwTyK19g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7212,9 +7198,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-freebsd-x64": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.3.2.tgz",
|
||||
"integrity": "sha512-KBDTyGn1evlZ17pupwRUDh2wrCMuHhP2j8cOCdgF5cl7vRki8BOK9yyL6jD11d/d/6DgXzy1jmQEX4Xx+AGCug==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.7.0.tgz",
|
||||
"integrity": "sha512-WhuK5XGy2c7WNMrVW2+mWMb0MCvgoOaSUxvxpl8OORdENYmgsUjYUDVnndjqRdSzcQTB/WfQX5G7bJQhySzaww==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7229,9 +7215,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-linux-arm-gnueabihf": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.3.2.tgz",
|
||||
"integrity": "sha512-mW+OcOnJEMvs7zD3aSwEG3z5M9bI4CuUU5Q/ePmnNzWIucRHpoAMNt/Sd+yu6L4+QttvoUf967uwcMsX8l4nrw==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.7.0.tgz",
|
||||
"integrity": "sha512-TZKDjO/YyOT/Zq449kIpkw9OUD4EQlmMk+aM8WW29VirJnnIfGxhjfCBkSRmVIOvwSlIf7GKLQjDuWwcObIZRg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -7246,9 +7232,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-linux-arm64-gnu": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.3.2.tgz",
|
||||
"integrity": "sha512-hbXpZqUvGY5aeEWvh0SNsiYjP1ytSM30XOT6qN6faLO2CL/7j9D2UB69SKOqF3TJOvuNU6cweFgZCxyGfXBYIQ==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.7.0.tgz",
|
||||
"integrity": "sha512-wEkE/MRcPQHZK10SgocPGCEyqFksLDdsSeE2fyVZb+N+vUGSp5YBu7OE6mUKobIs3uUXgftqP4d7QXTlehb7gw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -7263,9 +7249,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-linux-arm64-musl": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.3.2.tgz",
|
||||
"integrity": "sha512-HXthtN7adXCNVWs2F4wIqq2f7BcKTjsEnqg2LWV5lm4hRYvMfEvPftb0tECsEhcSQQYcvIJnLfv3vtu9HZSfVA==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.7.0.tgz",
|
||||
"integrity": "sha512-je7OmX7d41ihMzq4/q6AOsYGfiH5B3xIsAKBfXHSmlGcaijnxMCzSHzjR3znxhmOS0bMcfICXCCwm1AVcyUAUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -7280,9 +7266,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-linux-x64-gnu": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.3.2.tgz",
|
||||
"integrity": "sha512-HhgHqOUT05H45zuQL+XPywQbRNFttd7Rkkr7dZnpCRdp4W8GDjfyKCoCS5qVyowAyNh9Vc7VEq9qmiLMlvf6Zg==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.7.0.tgz",
|
||||
"integrity": "sha512-dF5/VJgtbydSgu9WjL1CrzNIXZR/9Z92b4f7lrvd/KO/LwBB3EVavgV0tSq0TAFuu4n5eSo66SA1j0miIBA1dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7297,9 +7283,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-linux-x64-musl": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.3.2.tgz",
|
||||
"integrity": "sha512-NrZ8L9of2GmYEM8GMJX6QRrLJlAwM+ds2rhdY1bxwpiyCNcD3IO/gzJlBs+kG4ly05F1u/X4k/FI5dXPpjUSgw==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.7.0.tgz",
|
||||
"integrity": "sha512-Jy760sdTDgzplpWerrUpfugmHnjQoNbCdcrNiBm1HaL2+P/6Wl2eZ2zLW3Uw5/D/wf7z8qsokMrPO9XdvnZ3cA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7314,9 +7300,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-win32-arm64-msvc": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.3.2.tgz",
|
||||
"integrity": "sha512-yLjacZND7C1XmsC0jfRLSgeLWZUw2Oz+u3nXNvj5JX6YHtYTVLFnRbTAcI+pG2Y6v0Otf2GKb3VT5d1mQb8JvA==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.7.0.tgz",
|
||||
"integrity": "sha512-5UvJg1RfOeSwcMr/7ghQA5yH3kpU8wye81sCLtyKPfBeH312Ntm52+ckc1IiCFAIkVyo2rmWF9ogRzOTR1O9sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -7331,9 +7317,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nx/nx-win32-x64-msvc": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.3.2.tgz",
|
||||
"integrity": "sha512-oDhcctfk0UB1V+Otp1161VKNMobzkFQxGyiEIjp0CjCBa2eRHC1r35L695F1Hj0bvLQPSni9XIe9evh2taeAkg==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.7.0.tgz",
|
||||
"integrity": "sha512-Ddtk/owLLj1wRHB5MUuUjEfxn9h6sQntOpCC1qDYJLCLRGe3jCEx7MHxKWo5MlmtShXeHRrS27CrA5AGzSS+6w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7514,19 +7500,170 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "19.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.11.tgz",
|
||||
"integrity": "sha512-m2a9VhaP5/tUw8FwfnW2ICXlXpLPIqxtg3XcAiGMLj/Xhw3RSBfZ8le/466ktO1Gcjr8oXudGnHhxV1TXJgFxw==",
|
||||
"version": "20.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz",
|
||||
"integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^4.2.1",
|
||||
"@octokit/plugin-paginate-rest": "^6.1.2",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^7.1.2"
|
||||
"@octokit/core": "^5.0.2",
|
||||
"@octokit/plugin-paginate-rest": "11.4.4-cjs.2",
|
||||
"@octokit/plugin-request-log": "^4.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/auth-token": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
|
||||
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/core": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
|
||||
"integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
||||
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/graphql": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
|
||||
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/openapi-types": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "11.4.4-cjs.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz",
|
||||
"integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
|
||||
"integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "13.3.2-cjs.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz",
|
||||
"integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "^5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/request": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
|
||||
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^9.0.6",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/request-error": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
|
||||
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/tsconfig": {
|
||||
@@ -15857,9 +15994,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
||||
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
||||
"version": "1.8.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
|
||||
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -18988,16 +19125,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-minimizer-webpack-plugin": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.0.tgz",
|
||||
"integrity": "sha512-niy66jxsQHqO+EYbhPuIhqRQ1mNcNVUHrMnkzzir9kFOERJUaQDDRhh7dKDz33kBpkWMF9M8Vx0QlDbc5AHOsw==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.2.tgz",
|
||||
"integrity": "sha512-nBRWZtI77PBZQgcXMNqiIXVshiQOVLGSf2qX/WZfG8IQfMbeHUMXaBWQmiiSTmPJUflQxHjZjzAmuyO7tpL2Jg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"cssnano": "^7.0.1",
|
||||
"cssnano": "^7.0.4",
|
||||
"jest-worker": "^29.7.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss": "^8.4.40",
|
||||
"schema-utils": "^4.2.0",
|
||||
"serialize-javascript": "^6.0.2"
|
||||
},
|
||||
@@ -22706,9 +22843,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/exponential-backoff": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
|
||||
"integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
|
||||
"integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
@@ -26545,9 +26682,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-middleware": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
|
||||
"integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
|
||||
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -27840,9 +27977,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-ssh": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz",
|
||||
"integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz",
|
||||
"integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -30570,19 +30707,19 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/lerna": {
|
||||
"version": "8.1.9",
|
||||
"resolved": "https://registry.npmjs.org/lerna/-/lerna-8.1.9.tgz",
|
||||
"integrity": "sha512-ZRFlRUBB2obm+GkbTR7EbgTMuAdni6iwtTQTMy7LIrQ4UInG44LyfRepljtgUxh4HA0ltzsvWfPkd5J1DKGCeQ==",
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/lerna/-/lerna-8.2.1.tgz",
|
||||
"integrity": "sha512-Xwjv9/4ixp7fpBWhtvp7dz4NoQT8DEf7hzibHKCgu/8kmZUHeXsTn+TKspHqhI+p4YDmdkDnkg8xmymz73kVOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lerna/create": "8.1.9",
|
||||
"@lerna/create": "8.2.1",
|
||||
"@npmcli/arborist": "7.5.4",
|
||||
"@npmcli/package-json": "5.2.0",
|
||||
"@npmcli/run-script": "8.1.0",
|
||||
"@nx/devkit": ">=17.1.2 < 21",
|
||||
"@octokit/plugin-enterprise-rest": "6.0.1",
|
||||
"@octokit/rest": "19.0.11",
|
||||
"@octokit/rest": "20.1.2",
|
||||
"aproba": "2.0.0",
|
||||
"byte-size": "8.1.1",
|
||||
"chalk": "4.1.0",
|
||||
@@ -30643,7 +30780,6 @@
|
||||
"slash": "3.0.0",
|
||||
"ssri": "^10.0.6",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strong-log-transformer": "2.1.0",
|
||||
"tar": "6.2.1",
|
||||
"temp-dir": "1.0.0",
|
||||
@@ -34669,9 +34805,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nx": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nx/-/nx-20.3.2.tgz",
|
||||
"integrity": "sha512-VWUHX0uCn8ACFbpBTpgucDzwe4q/a/UU3AYOhzKCvTzb3kQiyvoxLjORSze93ZNEqgor0PMkCQgcoMBUjxJfzQ==",
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nx/-/nx-20.7.0.tgz",
|
||||
"integrity": "sha512-IcrQr6alrSJTl5pb80Y/ytwK5Bsx7zC0LbJj5Ck5K+dctFKO2sEAvB2hKz5GiZx92NJzcfCVc5Lu44UeZST1bw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -34680,7 +34816,7 @@
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"@yarnpkg/parsers": "3.0.2",
|
||||
"@zkochan/js-yaml": "0.0.7",
|
||||
"axios": "^1.7.4",
|
||||
"axios": "^1.8.3",
|
||||
"chalk": "^4.1.0",
|
||||
"cli-cursor": "3.1.0",
|
||||
"cli-spinners": "2.6.1",
|
||||
@@ -34716,16 +34852,16 @@
|
||||
"nx-cloud": "bin/nx-cloud.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@nx/nx-darwin-arm64": "20.3.2",
|
||||
"@nx/nx-darwin-x64": "20.3.2",
|
||||
"@nx/nx-freebsd-x64": "20.3.2",
|
||||
"@nx/nx-linux-arm-gnueabihf": "20.3.2",
|
||||
"@nx/nx-linux-arm64-gnu": "20.3.2",
|
||||
"@nx/nx-linux-arm64-musl": "20.3.2",
|
||||
"@nx/nx-linux-x64-gnu": "20.3.2",
|
||||
"@nx/nx-linux-x64-musl": "20.3.2",
|
||||
"@nx/nx-win32-arm64-msvc": "20.3.2",
|
||||
"@nx/nx-win32-x64-msvc": "20.3.2"
|
||||
"@nx/nx-darwin-arm64": "20.7.0",
|
||||
"@nx/nx-darwin-x64": "20.7.0",
|
||||
"@nx/nx-freebsd-x64": "20.7.0",
|
||||
"@nx/nx-linux-arm-gnueabihf": "20.7.0",
|
||||
"@nx/nx-linux-arm64-gnu": "20.7.0",
|
||||
"@nx/nx-linux-arm64-musl": "20.7.0",
|
||||
"@nx/nx-linux-x64-gnu": "20.7.0",
|
||||
"@nx/nx-linux-x64-musl": "20.7.0",
|
||||
"@nx/nx-win32-arm64-msvc": "20.7.0",
|
||||
"@nx/nx-win32-x64-msvc": "20.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc-node/register": "^1.8.0",
|
||||
@@ -36437,9 +36573,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/parse-path": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz",
|
||||
"integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.1.tgz",
|
||||
"integrity": "sha512-6ReLMptznuuOEzLoGEa+I1oWRSj2Zna5jLWC+l6zlfAI4dbbSaIES29ThzuPkbhNahT65dWzfoZEO6cfJw2Ksg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -37828,9 +37964,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/protocols": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz",
|
||||
"integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz",
|
||||
"integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -47920,9 +48056,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
|
||||
"integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -48839,6 +48975,7 @@
|
||||
"@types/react": "*",
|
||||
"@types/react-loadable": "*",
|
||||
"@types/tinycolor2": "*",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^17.0.2",
|
||||
"react-loadable": "^5.5.0",
|
||||
"tinycolor2": "*"
|
||||
@@ -50727,7 +50864,7 @@
|
||||
"version": "0.20.3",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/react-redux": "^7.1.10",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"d3-array": "^1.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"lodash": "^4.17.21"
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-storybook": "^3.50.9",
|
||||
"@babel/cli": "^7.22.6",
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
"@babel/compat-data": "^7.26.8",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/eslint-parser": "^7.25.9",
|
||||
"@babel/node": "^7.22.6",
|
||||
@@ -301,7 +301,7 @@
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-matchers": "^7.1.2",
|
||||
"eslint": "^8.56.0",
|
||||
@@ -334,7 +334,7 @@
|
||||
"jest-html-reporter": "^3.10.2",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"lerna": "^8.1.7",
|
||||
"lerna": "^8.2.1",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
|
||||
@@ -42,7 +42,7 @@ const FlexRowContainer = styled.div`
|
||||
`;
|
||||
|
||||
export interface MetricOptionProps {
|
||||
metric: Omit<Metric, 'id'> & { label?: string };
|
||||
metric: Omit<Metric, 'id' | 'uuid'> & { label?: string };
|
||||
openInNewWindow?: boolean;
|
||||
showFormula?: boolean;
|
||||
showType?: boolean;
|
||||
|
||||
@@ -97,7 +97,7 @@ export const getColumnTooltipNode = (
|
||||
);
|
||||
};
|
||||
|
||||
type MetricType = Omit<Metric, 'id'> & { label?: string };
|
||||
type MetricType = Omit<Metric, 'id' | 'uuid'> & { label?: string };
|
||||
|
||||
export const getMetricTooltipNode = (
|
||||
metric: MetricType,
|
||||
|
||||
@@ -121,6 +121,7 @@ export const TestDataset: Dataset = {
|
||||
main_dttm_col: 'ds',
|
||||
metrics: [
|
||||
{
|
||||
uuid: '123',
|
||||
certification_details: null,
|
||||
certified_by: null,
|
||||
d3format: null,
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from '@superset-ui/core';
|
||||
import { PostProcessingFactory } from './types';
|
||||
import { getMetricOffsetsMap, isTimeComparison } from './utils';
|
||||
import { TIME_COMPARISON_SEPARATOR } from './utils/constants';
|
||||
|
||||
export const renameOperator: PostProcessingFactory<PostProcessingRename> = (
|
||||
formData,
|
||||
@@ -37,50 +38,60 @@ export const renameOperator: PostProcessingFactory<PostProcessingRename> = (
|
||||
);
|
||||
const { truncate_metric } = formData;
|
||||
const xAxisLabel = getXAxisLabel(formData);
|
||||
const isTimeComparisonValue = isTimeComparison(formData, queryObject);
|
||||
|
||||
// remove or rename top level of column name(metric name) in the MultiIndex when
|
||||
// 1) only 1 metric
|
||||
// 1) at least 1 metric
|
||||
// 2) dimension exist
|
||||
// 3) xAxis exist
|
||||
// 4) time comparison exist, and comparison type is "actual values"
|
||||
// 5) truncate_metric in form_data and truncate_metric is true
|
||||
// 4) truncate_metric in form_data and truncate_metric is true
|
||||
if (
|
||||
metrics.length === 1 &&
|
||||
metrics.length > 0 &&
|
||||
columns.length > 0 &&
|
||||
xAxisLabel &&
|
||||
!(
|
||||
// todo: we should provide an approach to handle derived metrics
|
||||
(
|
||||
isTimeComparison(formData, queryObject) &&
|
||||
[
|
||||
ComparisonType.Difference,
|
||||
ComparisonType.Ratio,
|
||||
ComparisonType.Percentage,
|
||||
].includes(formData.comparison_type)
|
||||
)
|
||||
) &&
|
||||
truncate_metric !== undefined &&
|
||||
!!truncate_metric
|
||||
) {
|
||||
const renamePairs: [string, string | null][] = [];
|
||||
|
||||
if (
|
||||
// "actual values" will add derived metric.
|
||||
// we will rename the "metric" from the metricWithOffset label
|
||||
// for example: "count__1 year ago" => "1 year ago"
|
||||
isTimeComparison(formData, queryObject) &&
|
||||
formData.comparison_type === ComparisonType.Values
|
||||
isTimeComparisonValue
|
||||
) {
|
||||
const metricOffsetMap = getMetricOffsetsMap(formData, queryObject);
|
||||
const timeOffsets = ensureIsArray(formData.time_compare);
|
||||
[...metricOffsetMap.keys()].forEach(metricWithOffset => {
|
||||
const offsetLabel = timeOffsets.find(offset =>
|
||||
metricWithOffset.includes(offset),
|
||||
);
|
||||
renamePairs.push([metricWithOffset, offsetLabel]);
|
||||
});
|
||||
[...metricOffsetMap.entries()].forEach(
|
||||
([metricWithOffset, metricOnly]) => {
|
||||
const offsetLabel = timeOffsets.find(offset =>
|
||||
metricWithOffset.includes(offset),
|
||||
);
|
||||
renamePairs.push([
|
||||
formData.comparison_type === ComparisonType.Values
|
||||
? metricWithOffset
|
||||
: [formData.comparison_type, metricOnly, metricWithOffset].join(
|
||||
TIME_COMPARISON_SEPARATOR,
|
||||
),
|
||||
metrics.length > 1 ? `${metricOnly}, ${offsetLabel}` : offsetLabel,
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
renamePairs.push([getMetricLabel(metrics[0]), null]);
|
||||
if (
|
||||
![
|
||||
ComparisonType.Difference,
|
||||
ComparisonType.Percentage,
|
||||
ComparisonType.Ratio,
|
||||
].includes(formData.comparison_type) &&
|
||||
metrics.length === 1
|
||||
) {
|
||||
renamePairs.push([getMetricLabel(metrics[0]), null]);
|
||||
}
|
||||
|
||||
if (renamePairs.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
operation: 'rename',
|
||||
|
||||
@@ -23,8 +23,6 @@ import {
|
||||
xAxisForceCategoricalControl,
|
||||
xAxisSortAscControl,
|
||||
xAxisSortControl,
|
||||
xAxisSortSeriesAscendingControl,
|
||||
xAxisSortSeriesControl,
|
||||
} from '../shared-controls';
|
||||
|
||||
const controlsWithoutXAxis: ControlSetRow[] = [
|
||||
@@ -55,8 +53,6 @@ export const echartsTimeSeriesQueryWithXAxisSort: ControlPanelSectionConfig = {
|
||||
[xAxisForceCategoricalControl],
|
||||
[xAxisSortControl],
|
||||
[xAxisSortAscControl],
|
||||
[xAxisSortSeriesControl],
|
||||
[xAxisSortSeriesAscendingControl],
|
||||
...controlsWithoutXAxis,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -57,9 +57,7 @@ export const contributionModeControl = {
|
||||
};
|
||||
|
||||
const xAxisSortVisibility = ({ controls }: { controls: ControlStateMapping }) =>
|
||||
isSortable(controls) &&
|
||||
ensureIsArray(controls?.groupby?.value).length === 0 &&
|
||||
ensureIsArray(controls?.metrics?.value).length === 1;
|
||||
isSortable(controls);
|
||||
|
||||
// TODO: Expand this aggregation options list to include all backend-supported aggregations.
|
||||
// TODO: Migrate existing chart types (Pivot Table, etc.) to use this shared control.
|
||||
@@ -87,15 +85,6 @@ export const aggregationControl = {
|
||||
},
|
||||
};
|
||||
|
||||
const xAxisMultiSortVisibility = ({
|
||||
controls,
|
||||
}: {
|
||||
controls: ControlStateMapping;
|
||||
}) =>
|
||||
isSortable(controls) &&
|
||||
(!!ensureIsArray(controls?.groupby?.value).length ||
|
||||
ensureIsArray(controls?.metrics?.value).length > 1);
|
||||
|
||||
export const xAxisSortControl = {
|
||||
name: 'x_axis_sort',
|
||||
config: {
|
||||
@@ -104,7 +93,7 @@ export const xAxisSortControl = {
|
||||
state.form_data?.orientation === 'horizontal'
|
||||
? t('Y-Axis Sort By')
|
||||
: t('X-Axis Sort By'),
|
||||
description: t('Decides which column to sort the base axis by.'),
|
||||
description: t('Decides which column or measure to sort the base axis by.'),
|
||||
shouldMapStateToProps: () => true,
|
||||
mapStateToProps: (state: ControlPanelState, controlState: ControlState) => {
|
||||
const { controls, datasource } = state;
|
||||
@@ -112,23 +101,35 @@ export const xAxisSortControl = {
|
||||
const columns = [controls?.x_axis?.value as QueryFormColumn].filter(
|
||||
Boolean,
|
||||
);
|
||||
const isSingleSortAvailable =
|
||||
ensureIsArray(controls?.groupby?.value).length === 0;
|
||||
const isMultiSortAvailable =
|
||||
!!ensureIsArray(controls?.groupby?.value).length ||
|
||||
ensureIsArray(controls?.metrics?.value).length > 1;
|
||||
const metrics = [
|
||||
...ensureIsArray(controls?.metrics?.value as QueryFormMetric),
|
||||
controls?.timeseries_limit_metric?.value as QueryFormMetric,
|
||||
].filter(Boolean);
|
||||
const metricLabels = [...new Set(metrics.map(getMetricLabel))];
|
||||
const options = [
|
||||
...columns.map(column => {
|
||||
const value = getColumnLabel(column);
|
||||
return {
|
||||
value,
|
||||
label: dataset?.verbose_map?.[value] || value,
|
||||
};
|
||||
}),
|
||||
...metricLabels.map(value => ({
|
||||
value,
|
||||
label: dataset?.verbose_map?.[value] || value,
|
||||
})),
|
||||
...(isSingleSortAvailable
|
||||
? [
|
||||
...columns.map(column => {
|
||||
const value = getColumnLabel(column);
|
||||
return { value, label: dataset?.verbose_map?.[value] || value };
|
||||
}),
|
||||
...metricLabels.map(value => ({
|
||||
value,
|
||||
label: dataset?.verbose_map?.[value] || value,
|
||||
})),
|
||||
]
|
||||
: []),
|
||||
...(isMultiSortAvailable
|
||||
? SORT_SERIES_CHOICES.map(choice => ({
|
||||
value: choice[0],
|
||||
label: choice[1],
|
||||
}))
|
||||
: []),
|
||||
];
|
||||
|
||||
const shouldReset = !(
|
||||
@@ -157,7 +158,7 @@ export const xAxisSortAscControl = {
|
||||
state.form_data?.orientation === 'horizontal'
|
||||
? t('Y-Axis Sort Ascending')
|
||||
: t('X-Axis Sort Ascending'),
|
||||
default: true,
|
||||
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_ascending,
|
||||
description: t('Whether to sort ascending or descending on the base Axis.'),
|
||||
visibility: ({ controls }: { controls: ControlStateMapping }) =>
|
||||
controls?.x_axis_sort?.value !== undefined &&
|
||||
@@ -184,37 +185,3 @@ export const xAxisForceCategoricalControl = {
|
||||
shouldMapStateToProps: () => true,
|
||||
},
|
||||
};
|
||||
|
||||
export const xAxisSortSeriesControl = {
|
||||
name: 'x_axis_sort_series',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: false,
|
||||
label: (state: ControlPanelState) =>
|
||||
state.form_data?.orientation === 'horizontal'
|
||||
? t('Y-Axis Sort By')
|
||||
: t('X-Axis Sort By'),
|
||||
choices: SORT_SERIES_CHOICES,
|
||||
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_type,
|
||||
renderTrigger: true,
|
||||
description: t('Decides which measure to sort the base axis by.'),
|
||||
visibility: xAxisMultiSortVisibility,
|
||||
},
|
||||
};
|
||||
|
||||
export const xAxisSortSeriesAscendingControl = {
|
||||
name: 'x_axis_sort_series_ascending',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: (state: ControlPanelState) =>
|
||||
state.form_data?.orientation === 'horizontal'
|
||||
? t('Y-Axis Sort Ascending')
|
||||
: t('X-Axis Sort Ascending'),
|
||||
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_ascending,
|
||||
description: t('Whether to sort ascending or descending on the base Axis.'),
|
||||
renderTrigger: true,
|
||||
visibility: ({ controls }: { controls: ControlStateMapping }) =>
|
||||
controls?.x_axis_sort_series?.value !== undefined &&
|
||||
xAxisMultiSortVisibility({ controls }),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,12 +43,12 @@ const queryObject: QueryObject = {
|
||||
post_processing: [],
|
||||
};
|
||||
|
||||
test('should skip renameOperator if exists multiple metrics', () => {
|
||||
test('should skip renameOperator for empty metrics', () => {
|
||||
expect(
|
||||
renameOperator(formData, {
|
||||
...queryObject,
|
||||
...{
|
||||
metrics: ['count(*)', 'sum(sales)'],
|
||||
metrics: [],
|
||||
},
|
||||
}),
|
||||
).toEqual(undefined);
|
||||
@@ -77,7 +77,23 @@ test('should skip renameOperator if does not exist x_axis and is_timeseries', ()
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('should skip renameOperator if exists derived metrics', () => {
|
||||
test('should skip renameOperator if not is_timeseries and multi metrics', () => {
|
||||
expect(
|
||||
renameOperator(formData, {
|
||||
...queryObject,
|
||||
...{ is_timeseries: false, metrics: ['count(*)', 'sum(val)'] },
|
||||
}),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('should add renameOperator', () => {
|
||||
expect(renameOperator(formData, queryObject)).toEqual({
|
||||
operation: 'rename',
|
||||
options: { columns: { 'count(*)': null }, inplace: true, level: 0 },
|
||||
});
|
||||
});
|
||||
|
||||
test('should add renameOperator if exists derived metrics', () => {
|
||||
[
|
||||
ComparisonType.Difference,
|
||||
ComparisonType.Ratio,
|
||||
@@ -99,14 +115,14 @@ test('should skip renameOperator if exists derived metrics', () => {
|
||||
},
|
||||
},
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('should add renameOperator', () => {
|
||||
expect(renameOperator(formData, queryObject)).toEqual({
|
||||
operation: 'rename',
|
||||
options: { columns: { 'count(*)': null }, inplace: true, level: 0 },
|
||||
).toEqual({
|
||||
operation: 'rename',
|
||||
options: {
|
||||
columns: { [`${type}__count(*)__count(*)__1 year ago`]: '1 year ago' },
|
||||
inplace: true,
|
||||
level: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,6 +186,61 @@ test('should add renameOperator if exist "actual value" time comparison', () =>
|
||||
});
|
||||
});
|
||||
|
||||
test('should add renameOperator if derived time comparison exists', () => {
|
||||
expect(
|
||||
renameOperator(
|
||||
{
|
||||
...formData,
|
||||
...{
|
||||
comparison_type: ComparisonType.Ratio,
|
||||
time_compare: ['1 year ago', '1 year later'],
|
||||
},
|
||||
},
|
||||
queryObject,
|
||||
),
|
||||
).toEqual({
|
||||
operation: 'rename',
|
||||
options: {
|
||||
columns: {
|
||||
'ratio__count(*)__count(*)__1 year ago': '1 year ago',
|
||||
'ratio__count(*)__count(*)__1 year later': '1 year later',
|
||||
},
|
||||
inplace: true,
|
||||
level: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should add renameOperator if multiple metrics exist', () => {
|
||||
expect(
|
||||
renameOperator(
|
||||
{
|
||||
...formData,
|
||||
...{
|
||||
comparison_type: ComparisonType.Values,
|
||||
time_compare: ['1 year ago'],
|
||||
},
|
||||
},
|
||||
{
|
||||
...queryObject,
|
||||
...{
|
||||
metrics: ['count(*)', 'sum(sales)'],
|
||||
},
|
||||
},
|
||||
),
|
||||
).toEqual({
|
||||
operation: 'rename',
|
||||
options: {
|
||||
columns: {
|
||||
'count(*)__1 year ago': 'count(*), 1 year ago',
|
||||
'sum(sales)__1 year ago': 'sum(sales), 1 year ago',
|
||||
},
|
||||
inplace: true,
|
||||
level: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should remove renameOperator', () => {
|
||||
expect(
|
||||
renameOperator(
|
||||
|
||||
@@ -32,6 +32,7 @@ describe('defineSavedMetrics', () => {
|
||||
{
|
||||
metric_name: 'COUNT(*) non-default-dataset-metric',
|
||||
expression: 'COUNT(*) non-default-dataset-metric',
|
||||
uuid: '1',
|
||||
},
|
||||
],
|
||||
type: DatasourceType.Table,
|
||||
@@ -48,6 +49,7 @@ describe('defineSavedMetrics', () => {
|
||||
{
|
||||
metric_name: 'COUNT(*) non-default-dataset-metric',
|
||||
expression: 'COUNT(*) non-default-dataset-metric',
|
||||
uuid: '1',
|
||||
},
|
||||
]);
|
||||
// @ts-ignore
|
||||
|
||||
@@ -24,15 +24,24 @@ describe('mainMetric', () => {
|
||||
expect(mainMetric(null)).toBeUndefined();
|
||||
});
|
||||
it('prefers the "count" metric when first', () => {
|
||||
const metrics = [{ metric_name: 'count' }, { metric_name: 'foo' }];
|
||||
const metrics = [
|
||||
{ metric_name: 'count', uuid: '1' },
|
||||
{ metric_name: 'foo', uuid: '2' },
|
||||
];
|
||||
expect(mainMetric(metrics)).toBe('count');
|
||||
});
|
||||
it('prefers the "count" metric when not first', () => {
|
||||
const metrics = [{ metric_name: 'foo' }, { metric_name: 'count' }];
|
||||
const metrics = [
|
||||
{ metric_name: 'foo', uuid: '1' },
|
||||
{ metric_name: 'count', uuid: '2' },
|
||||
];
|
||||
expect(mainMetric(metrics)).toBe('count');
|
||||
});
|
||||
it('selects the first metric when "count" is not an option', () => {
|
||||
const metrics = [{ metric_name: 'foo' }, { metric_name: 'not_count' }];
|
||||
const metrics = [
|
||||
{ metric_name: 'foo', uuid: '2' },
|
||||
{ metric_name: 'not_count', uuid: '2' },
|
||||
];
|
||||
expect(mainMetric(metrics)).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"@types/react": "*",
|
||||
"@types/react-loadable": "*",
|
||||
"@types/tinycolor2": "*",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^17.0.2",
|
||||
"react-loadable": "^5.5.0",
|
||||
"tinycolor2": "*"
|
||||
|
||||
@@ -31,11 +31,11 @@ import {
|
||||
RequestConfig,
|
||||
ParseMethod,
|
||||
} from './types';
|
||||
import { DEFAULT_FETCH_RETRY_OPTIONS, DEFAULT_BASE_URL } from './constants';
|
||||
import { DEFAULT_FETCH_RETRY_OPTIONS, DEFAULT_APP_ROOT } from './constants';
|
||||
|
||||
const defaultUnauthorizedHandler = () => {
|
||||
if (!window.location.pathname.startsWith('/login')) {
|
||||
window.location.href = `/login?next=${window.location.href}`;
|
||||
const defaultUnauthorizedHandlerForPrefix = (appRoot: string) => () => {
|
||||
if (!window.location.pathname.startsWith(`${appRoot}/login`)) {
|
||||
window.location.href = `${appRoot}/login?next=${window.location.href}`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ export default class SupersetClientClass {
|
||||
|
||||
fetchRetryOptions?: FetchRetryOptions;
|
||||
|
||||
baseUrl: string;
|
||||
appRoot?: string;
|
||||
|
||||
protocol: Protocol;
|
||||
|
||||
@@ -67,9 +67,9 @@ export default class SupersetClientClass {
|
||||
handleUnauthorized: () => void;
|
||||
|
||||
constructor({
|
||||
baseUrl = DEFAULT_BASE_URL,
|
||||
host,
|
||||
protocol,
|
||||
appRoot = DEFAULT_APP_ROOT,
|
||||
headers = {},
|
||||
fetchRetryOptions = {},
|
||||
mode = 'same-origin',
|
||||
@@ -78,17 +78,10 @@ export default class SupersetClientClass {
|
||||
csrfToken = undefined,
|
||||
guestToken = undefined,
|
||||
guestTokenHeaderName = 'X-GuestToken',
|
||||
unauthorizedHandler = defaultUnauthorizedHandler,
|
||||
unauthorizedHandler = undefined,
|
||||
}: ClientConfig = {}) {
|
||||
const url = new URL(
|
||||
host || protocol
|
||||
? `${protocol || 'https:'}//${host || 'localhost'}`
|
||||
: baseUrl,
|
||||
// baseUrl for API could also be relative, so we provide current location.href
|
||||
// as the base of baseUrl
|
||||
window.location.href,
|
||||
);
|
||||
this.baseUrl = url.href.replace(/\/+$/, ''); // always strip trailing slash
|
||||
const url = new URL(`${protocol || 'https:'}//${host || 'localhost'}`);
|
||||
this.appRoot = appRoot;
|
||||
this.host = url.host;
|
||||
this.protocol = url.protocol as Protocol;
|
||||
this.headers = { Accept: 'application/json', ...headers }; // defaulting accept to json
|
||||
@@ -109,7 +102,10 @@ export default class SupersetClientClass {
|
||||
if (guestToken) {
|
||||
this.headers[guestTokenHeaderName] = guestToken;
|
||||
}
|
||||
this.handleUnauthorized = unauthorizedHandler;
|
||||
this.handleUnauthorized =
|
||||
unauthorizedHandler !== undefined
|
||||
? unauthorizedHandler
|
||||
: defaultUnauthorizedHandlerForPrefix(this.appRoot);
|
||||
}
|
||||
|
||||
async init(force = false): CsrfPromise {
|
||||
@@ -239,7 +235,7 @@ export default class SupersetClientClass {
|
||||
method: 'GET',
|
||||
mode: this.mode,
|
||||
timeout: this.timeout,
|
||||
url: this.getUrl({ endpoint: 'api/v1/security/csrf_token/' }),
|
||||
url: this.getUrl({ endpoint: '/api/v1/security/csrf_token/' }),
|
||||
parseMethod: 'json',
|
||||
}).then(({ json }) => {
|
||||
if (typeof json === 'object') {
|
||||
@@ -271,7 +267,7 @@ export default class SupersetClientClass {
|
||||
const host = inputHost ?? this.host;
|
||||
const cleanHost = host.slice(-1) === '/' ? host.slice(0, -1) : host; // no backslash
|
||||
|
||||
return `${this.protocol}//${cleanHost}/${
|
||||
return `${this.protocol}//${cleanHost}${this.appRoot}/${
|
||||
endpoint[0] === '/' ? endpoint.slice(1) : endpoint
|
||||
}`;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import { FetchRetryOptions } from './types';
|
||||
|
||||
export const DEFAULT_BASE_URL = 'http://localhost';
|
||||
export const DEFAULT_APP_ROOT = '';
|
||||
|
||||
// HTTP status codes
|
||||
export const HTTP_STATUS_OK = 200;
|
||||
|
||||
@@ -132,9 +132,9 @@ export type CsrfPromise = Promise<string | undefined>;
|
||||
export type Protocol = 'http:' | 'https:';
|
||||
|
||||
export interface ClientConfig {
|
||||
baseUrl?: string;
|
||||
host?: Host;
|
||||
protocol?: Protocol;
|
||||
appRoot?: string;
|
||||
credentials?: Credentials;
|
||||
csrfToken?: CsrfToken;
|
||||
guestToken?: string;
|
||||
|
||||
@@ -59,3 +59,37 @@ test('should truncate', () => {
|
||||
|
||||
expect(isTruncated).toBe(true);
|
||||
});
|
||||
|
||||
test('should not truncate with vertical orientation', () => {
|
||||
const ref = { current: document.createElement('p') };
|
||||
Object.defineProperty(ref.current, 'offsetHeight', { get: () => 100 });
|
||||
Object.defineProperty(ref.current, 'scrollHeight', { get: () => 50 });
|
||||
jest.spyOn(global.React, 'useRef').mockReturnValue({ current: ref.current });
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useCSSTextTruncation<HTMLParagraphElement>({
|
||||
isVertical: true,
|
||||
isHorizontal: false,
|
||||
}),
|
||||
);
|
||||
const [, isTruncated] = result.current;
|
||||
|
||||
expect(isTruncated).toBe(false);
|
||||
});
|
||||
|
||||
test('should truncate with vertical orientation', () => {
|
||||
const ref = { current: document.createElement('p') };
|
||||
Object.defineProperty(ref.current, 'offsetHeight', { get: () => 50 });
|
||||
Object.defineProperty(ref.current, 'scrollHeight', { get: () => 100 });
|
||||
jest.spyOn(global.React, 'useRef').mockReturnValue({ current: ref.current });
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useCSSTextTruncation<HTMLParagraphElement>({
|
||||
isVertical: true,
|
||||
isHorizontal: false,
|
||||
}),
|
||||
);
|
||||
const [, isTruncated] = result.current;
|
||||
|
||||
expect(isTruncated).toBe(true);
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user