Compare commits

...

106 Commits

Author SHA1 Message Date
Beto Dealmeida
503933756e fix: show only filterable columns on filter dropdown 2025-05-01 17:20:02 -04:00
Beto Dealmeida
60261a5dc6 Fix docstring 2025-05-01 12:33:44 -04:00
Beto Dealmeida
456512c508 Fix test 2025-05-01 11:56:13 -04:00
Beto Dealmeida
c1d9b06649 Remove old method 2025-05-01 09:49:50 -04:00
Beto Dealmeida
7a64a82cd9 fix: improve function detection 2025-04-30 16:58:11 -04:00
JUST.in DO IT
ef14b529b8 fix(echarts): rename time series shifted colnames (#33269) 2025-04-30 14:18:18 -03:00
github-actions[bot]
2a97a6ec1f chore(🦾): bump python importlib-metadata 8.6.1 -> 8.7.0 (#33277)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 10:03:18 -07:00
github-actions[bot]
fa6548939e chore(🦾): bump python mako 1.3.9 -> 1.3.10 (#33280)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 10:02:45 -07:00
github-actions[bot]
418c673699 chore(🦾): bump python pyparsing 3.2.2 -> 3.2.3 (#33281)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 10:01:42 -07:00
github-actions[bot]
13f77a7416 chore(🦾): bump python celery 5.4.0 -> 5.5.2 (#33257)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:28:39 -07:00
github-actions[bot]
303a80a316 chore(🦾): bump python packaging 24.2 -> 25.0 (#33259)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:27:48 -07:00
github-actions[bot]
2392ac6827 chore(🦾): bump python deprecation subpackage(s) (#33260)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:27:25 -07:00
github-actions[bot]
01ce4b987e chore(🦾): bump python python-dotenv 1.0.1 -> 1.1.0 (#33262)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:26:59 -07:00
github-actions[bot]
2f308a85d8 chore(🦾): bump python pandas subpackage(s) (#33263)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:26:40 -07:00
github-actions[bot]
e8d60509a0 chore(🦾): bump python sqlglot 26.11.1 -> 26.16.2 (#33266)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:26:01 -07:00
github-actions[bot]
d6f80eaae7 chore(🦾): bump python gunicorn subpackage(s) (#33265)
Co-authored-by: GitHub Action <action@github.com>
2025-04-29 08:25:23 -07:00
Emad Rad
a5f986fec5 feat: Persian translations (#29580) 2025-04-29 09:01:34 -06:00
Beto Dealmeida
141d0252f2 fix: mask password on DB import (#33267) 2025-04-29 10:27:03 -04:00
Daniel Vaz Gaspar
c029b532d4 fix: LocalProxy is not mapped warning (#33025) 2025-04-28 23:01:26 -06:00
github-actions[bot]
13816443ba chore(🦾): bump python croniter subpackage(s) (#33258)
Co-authored-by: GitHub Action <action@github.com>
2025-04-28 16:52:09 -07:00
Elizabeth Thompson
2c4e22e598 chore: add some utils tests (#33236) 2025-04-28 15:00:32 -07:00
Hamir Mahal
aea776a131 fix: Unexpected input(s) 'depth' CI warnings (#33254) 2025-04-28 11:07:13 -06:00
Evan Rusackas
d2360b533b fix(histogram): remove extra single quotes (#33248) 2025-04-25 16:45:05 -06:00
Vitor Avila
de84a534ac fix(DB update): Gracefully handle querry error during DB update (#33250) 2025-04-25 15:38:59 -03:00
Sam Firke
ac636c73ae fix(heatmap): correctly render int and boolean falsy values on axes (#33238) 2025-04-25 11:25:50 -04:00
Levis Mbote
6a586fe4fd fix(chart): Restore subheader used in bignumber with trendline (#33196) 2025-04-25 09:39:07 -03:00
Vitor Avila
fbd8ae2888 fix(sqllab permalink): Commit SQL Lab permalinks (#33237) 2025-04-24 22:41:15 -03:00
Vitor Avila
7e4fde7a14 fix(standalone): Ensure correct URL param value for standalone mode (#33234) 2025-04-24 16:41:42 -03:00
Evan Rusackas
150b9a0168 feat(maps): Adding Republic of Serbia to country maps (#33208)
Co-authored-by: dykoffi <dykoffi@users.noreply.github.com>
2025-04-23 11:29:35 -06:00
Vitor Avila
f7b7aace38 fix(export): Full CSV/Excel exports respecting SQL_MAX_ROW config (#33214) 2025-04-23 13:13:07 -03:00
Sam Firke
f78c94c988 docs(installation): compare installation methods (#33137) 2025-04-23 11:57:33 -04:00
sha174n
74ff8dc724 docs: Add note on SQL execution security considerations (#33210) 2025-04-23 13:58:33 +01:00
Shao Yu-Lung (Allen)
8aa127eac2 feat(i18n): Frontend add zh_TW Option (#33192)
Co-authored-by: Shao Yu-Lung (Allen) <mis@cendai.com.tw>
2025-04-22 15:36:09 -06:00
Kalai
3729016a0d docs: improve documentation(docs): clarify URL encoding requirement for connection strings (#30047)
Co-authored-by: Evan Rusackas <evan@preset.io>
2025-04-22 15:30:19 -06:00
Elizabeth Thompson
b6628cdfd2 chore: migrate to more db migration utils (#33155) 2025-04-22 11:26:54 -07:00
Evan Rusackas
ae48dba3e1 feat(maps): Adding Ivory Coast / Côte d'Ivoire (#33198)
Co-authored-by: dykoffi <dykoffi@users.noreply.github.com>
2025-04-22 10:04:19 -06:00
dependabot[bot]
09364d182c chore(deps-dev): bump http-proxy-middleware from 2.0.7 to 2.0.9 in /superset-frontend (#33197)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-22 09:31:10 -06:00
Geido
99ed968289 fix(Native Filters): Keep default filter values when configuring creatable behavior (#33205) 2025-04-22 16:32:30 +02:00
Geido
8fa3b8d7e3 fix(Native Filters): Keep default filter values when configuring creatable behavior (#33205) 2025-04-22 16:30:36 +02:00
Maxime Alay-Eddine
7530487760 feat(country-map): fix France Regions IDF region code - Fixes #32627 (#32695)
Co-authored-by: Maxime ALAY-EDDINE <maxime@galeax.com>
2025-04-21 20:15:27 -06:00
Maxime Beauchemin
79afc2b545 docs: add a high-level architecture diagram to the docs (#33173) 2025-04-21 11:15:29 -07:00
JUST.in DO IT
8c94f9c435 fix(sqllab): Invalid SQL Error breaks SQL Lab (#33164) 2025-04-18 13:31:54 -07:00
Evan Rusackas
b589d44dfb fix(deckgl): Update Arc to properly adjust line width (#33154) 2025-04-18 10:07:40 -06:00
Elizabeth Thompson
4140261797 fix: subheader should show as subtitle (#33172) 2025-04-18 13:03:20 +08:00
Jacob Amrany
00f1fdb3c4 fix: os.makedirs race condition (#33161) 2025-04-17 15:09:44 -03:00
JUST.in DO IT
172e5dd095 fix(echart): Thrown errors shown after resized (#33143) 2025-04-17 09:49:49 -07:00
Mehmet Salih Yavuz
a53907a646 feat(Select): Select all and Deselect all that works on visible items while searching (#33043) 2025-04-17 18:04:08 +03:00
amaannawab923
be1b8d6751 feat(Native Filters): Exclude Filter Values (#33054)
Co-authored-by: Amaan Nawab <nelsondrew07@gmail.com>
2025-04-17 17:56:26 +03:00
Elizabeth Thompson
26ff734ef9 fix: add folders to import schema (#33142) 2025-04-15 19:49:44 -07:00
dependabot[bot]
0e18246999 chore(deps): bump @babel/runtime from 7.17.2 to 7.27.0 in /superset-frontend/cypress-base (#33102)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 20:38:56 -06:00
JUST.in DO IT
7333ffd41e fix(echart): Tooltip date format doesn't follow time grain (#33138) 2025-04-15 18:51:53 -07:00
Elizabeth Thompson
7dc5019b9d fix: app icon should not use subdirectory (#33141) 2025-04-15 18:09:06 -07:00
Jillian
93fa39a14f fix(lang): patch FAB's LocaleView to redirect to previous page (#31692) 2025-04-15 09:46:06 -07:00
JUST.in DO IT
342e6f3ab0 fix(dashboard): invalid active tab state (#33106) 2025-04-15 09:14:20 -07:00
Enzo Martellucci
013379eb86 feat(List Users): Migrate List Users FAB to React (#32882) 2025-04-15 17:04:28 +03:00
Michael S. Molina
bc0ffe0d10 fix: Viz migration error handling (#33037) 2025-04-15 08:25:09 -03:00
Elizabeth Thompson
5f62deaa36 chore: use create table util (#33072) 2025-04-14 19:01:11 -07:00
WLCFaro
ff8605b723 feat(lang): update Italian language (#29827) 2025-04-14 16:16:08 -06:00
Felipe Granado
45c77a1976 chore(translations): Update PT-BR language (partial) (#29828)
Co-authored-by: Evan Rusackas <evan@preset.io>
2025-04-14 16:06:54 -06:00
Kamil Gabryjelski
8cb71b8d3b fix(plugin-chart-table): Don't render redundant items in column config when time comparison is enabled (#33126) 2025-04-14 23:08:15 +02:00
Daniel Höxtermann
2233c02720 fix(playwright): allow screenshotting empty dashboards (#33107) 2025-04-14 12:20:39 -07:00
Kamil Gabryjelski
839215148a feat(explore): X-axis sort by specific metric when more than 1 metric is set (#33116) 2025-04-14 20:39:09 +02:00
Maxime Beauchemin
c1eeb63d89 fix: master builds are failing while trying to push report to cypress (#33124) 2025-04-14 10:53:02 -07:00
Elizabeth Thompson
7b9ebbe735 feat(explore): Integrate dataset panel with Folders feature (#33104)
Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
2025-04-14 18:40:31 +02:00
Vitor Avila
a5a91d5e48 fix(OAuth2): Update connection should not fail if connection is missing OAuth2 token (#33100) 2025-04-14 11:19:55 -03:00
Michael S. Molina
e1f5c49df7 fix: Allows configuration of Selenium Webdriver binary (#33103) 2025-04-14 08:11:02 -03:00
Kamil Gabryjelski
3c1fc0b722 fix: Broken menu links to datasets and sql lab (#33114) 2025-04-13 21:31:21 +02:00
Maxime Beauchemin
05faf2f352 fix: resolve recent merge collisio (#33110) 2025-04-12 16:33:00 -07:00
Daniel Höxtermann
347c174099 fix(thumbnails): ensure consistent cache_key (#33109) 2025-04-12 12:15:08 -07:00
Erkka Tahvanainen
5656d69c04 fix(dashboard): Generate screenshot via celery (#32193)
Co-authored-by: Erkka Tahvanainen <erkka.tahvanainen@confidently.fi>
2025-04-12 12:14:16 -07:00
Maxime Beauchemin
ac4df8d06b fix: CI file change detector to handle large PRs (#33092) 2025-04-12 12:08:41 -07:00
Beto Dealmeida
bcd136cee1 feat: catalogs for DuckDB (#28751)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
2025-04-11 12:58:59 -07:00
Beto Dealmeida
7ab8534ef6 feat: dataset folders (backend) (#32520)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
2025-04-11 11:38:08 -07:00
Geido
014b39290b feat(Native Filters): Configure creatable filter behavior (#33096) 2025-04-11 20:38:02 +03:00
Martyn Gigg
4f97b739b1 fix: Broken Python tests on master after merging prefix branch (#33095) 2025-04-11 08:52:35 -07:00
Beto Dealmeida
d88cba92c0 feat: optimize catalog permission sync (#33000) 2025-04-10 17:38:34 -07:00
Pedro-Gato
5304bed4ed chore: Update INTHEWILD.md (#33079) 2025-04-10 11:52:04 -06:00
Johannes
37194a41ec chore: Added Formbricks to INTHEWILD.md (#33074) 2025-04-10 11:51:39 -06:00
Levis Mbote
d75ff9e784 feat(charts): add subtitle option and metric customization controls (#32975) 2025-04-10 17:24:24 +02:00
Hossein Khalilian
164a07e2be fix(docker): fallback to pip if uv is not available (#33087) 2025-04-10 11:10:26 -04:00
Clay Heaton
44bd200885 fix(docs): Update quickstart.mdx to reflect latest version tag (#33063) 2025-04-10 09:58:00 -04:00
dependabot[bot]
8242692541 chore(deps-dev): bump lerna from 8.1.9 to 8.2.1 in /superset-frontend (#32941)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 16:11:41 -06:00
Martyn Gigg
09b92e7d08 feat: Allow superset to be deployed under a prefixed URL (#30134)
Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
2025-04-09 13:43:44 -07:00
Landry Breuil
31ac3898ad fix(list roles): dont send invalid querystrings (#33060) 2025-04-09 23:25:20 +03:00
Michael S. Molina
c1159c53e3 fix: Adds missing __init__ file to commands/logs (#33059) 2025-04-09 16:39:32 -03:00
Maxime Beauchemin
deb6aedddb feat: add a title prop to the dashboard link in CRUD LIST view (#33046) 2025-04-09 12:02:37 -07:00
JUST.in DO IT
ed0cd5e7b0 fix: improve error type on parse error (#33048) 2025-04-09 09:52:15 -07:00
Maxime Beauchemin
9280b4d2a9 docs: clarify docker-compose-image-tag instructions (#33045) 2025-04-09 08:59:07 -07:00
Ville Brofeldt
3a57857707 chore(helm): bump appVersion to 4.1.2 (#33061) 2025-04-09 08:45:48 -07:00
EmmanuelCbd
6b7394e789 fix(export): charts csv export in dashboards (#31720) 2025-04-08 14:02:13 -07:00
dependabot[bot]
5a8eab3b25 chore(deps): bump estree-util-value-to-estree from 3.1.1 to 3.3.3 in /docs (#33028)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 11:21:04 -06:00
Ookong
15969fdf94 docs: add WinWin Network(马上赢) to users list (#33018) 2025-04-08 11:18:47 -06:00
JUST.in DO IT
9b15e04bc4 fix(log): Missing failed query log on async queries (#33024) 2025-04-08 07:12:03 -07:00
Asher Manangan
fd947a097d feat(tags): Export and Import Functionality for Superset Dashboards and Charts (#30833)
Co-authored-by: Asher Manangan <amanangan@powercosts.com>
2025-04-07 15:12:22 -04:00
Sameer ali
e1383d3821 refactor(IconButton): Refactor IconButton to use Ant Design 5 Card (#32890)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
Co-authored-by: Geido <60598000+geido@users.noreply.github.com>
2025-04-07 20:57:32 +03:00
Hugues Verlin
c131205ff1 docs: Update documentation about publishing a dashboard (#32999) 2025-04-07 10:19:39 -07:00
Levis Mbote
b6df88a134 fix: fix bug where dashboard did not enter fullscreen mode. (#32839) 2025-04-07 18:20:49 +03:00
Hugo Lavernhe
629b137bb0 fix(dashboard): chart fullscreen issue when filter pane is collapsed (#28428) 2025-04-04 17:12:14 -06:00
Vitor Avila
db959a6463 chore(Databricks): Display older Databricks driver as legacy (#33001) 2025-04-04 15:09:15 -03:00
Kamil Gabryjelski
4041150660 feat: Add getDataMask function to embedded SDK (#32997) 2025-04-03 21:10:01 +02:00
Hex Café
bcb43327b1 fix: show_filters URL parameter is not working (#29422)
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Vitor Avila <vitor.avila@preset.io>
2025-04-03 15:59:11 -03:00
Trent Lavoie
63c8bbf3eb fix(frontend): add missing antd-5 icon to import (#32990)
Co-authored-by: Trent Lavoie <lavtrent@amazon.com>
2025-04-03 11:18:39 -06:00
Michael S. Molina
24b1666273 fix: Bar Chart (legacy) migration to keep labels layout (#32965) 2025-04-03 08:16:55 -03:00
Mohamed Halat
86b795cd36 feat(embedding-sdk): emit data-mask events through embedded sdk to iframe parent (#31331) 2025-04-03 12:37:52 +02:00
Maxime Beauchemin
bc0bf94680 chore: bump marshmallow-sqlalchemy to 1.4.0 (#32922) 2025-04-02 09:09:08 -07:00
SBIN2010
f5d64176f6 fix: fixed Add Metrics to Tree Chart (#29158) (#30679) 2025-04-02 10:04:36 -06:00
398 changed files with 54042 additions and 26370 deletions

5
.github/labeler.yml vendored
View File

@@ -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
############################################

View File

@@ -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=$!

View File

@@ -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

View File

@@ -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()

View File

@@ -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:

View File

@@ -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}

View File

@@ -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]

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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;
}

View 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;
}
}

View File

@@ -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")

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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 Gunicorns `REMOTE_USER` environment variable:
from the proxy) to Gunicorns `REMOTE_USER` environment variable.

View File

@@ -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:

View File

@@ -1,7 +1,7 @@
---
title: Docker Builds
hide_title: true
sidebar_position: 6
sidebar_position: 7
version: 1
---

View File

@@ -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
```

View 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.

View File

@@ -1,7 +1,7 @@
---
title: Kubernetes
hide_title: true
sidebar_position: 2
sidebar_position: 3
version: 1
---

View File

@@ -1,7 +1,7 @@
---
title: PyPI
hide_title: true
sidebar_position: 3
sidebar_position: 4
version: 1
---

View File

@@ -1,7 +1,7 @@
---
title: Upgrading Superset
hide_title: true
sidebar_position: 5
sidebar_position: 6
version: 1
---

View File

@@ -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

View File

@@ -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,

View File

@@ -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. Well 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! Youve 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:

View File

@@ -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',

View File

@@ -19,9 +19,10 @@
},
"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",

View File

@@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
# superset
![Version: 0.14.1](https://img.shields.io/badge/Version-0.14.1-informational?style=flat-square)
![Version: 0.14.2](https://img.shields.io/badge/Version-0.14.2-informational?style=flat-square)
Apache Superset is a modern, enterprise-ready business intelligence web application

View File

@@ -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 = [

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}")

View File

@@ -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,
};
}

View File

@@ -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(() => {

View File

@@ -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();

View File

@@ -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,
);

View File

@@ -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) {

View File

@@ -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 },

View File

@@ -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);

View File

@@ -55,6 +55,7 @@ export function prepareDashboardFilters(
controlValues: {
enableEmptyFilter: false,
defaultToFirstItem: false,
creatable: true,
multiSelect: true,
searchAllOptions: false,
inverseSelection: false,

View File

@@ -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();

View File

@@ -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',
);
}

View File

@@ -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' });
});

View File

@@ -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', () => {

View File

@@ -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');

View File

@@ -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');
}

View File

@@ -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');
});
});

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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', () => {

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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' };

View File

@@ -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 = {

View File

@@ -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);

View File

@@ -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');

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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",
@@ -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": {
@@ -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"

View File

@@ -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",

View File

@@ -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;

View File

@@ -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,

View File

@@ -121,6 +121,7 @@ export const TestDataset: Dataset = {
main_dttm_col: 'ds',
metrics: [
{
uuid: '123',
certification_details: null,
certified_by: null,
d3format: null,

View File

@@ -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',

View File

@@ -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,
],
};

View File

@@ -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 }),
},
};

View File

@@ -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(

View File

@@ -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

View File

@@ -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');
});
});

View File

@@ -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": "*"

View File

@@ -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
}`;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
});

View File

@@ -36,24 +36,37 @@ export const truncationCSS = css`
* to be displayed, this hook returns a ref to attach to the text
* element and a boolean for whether that element is currently truncated.
*/
const useCSSTextTruncation = <T extends HTMLElement>(): [
RefObject<T>,
boolean,
] => {
const useCSSTextTruncation = <T extends HTMLElement>(
{ isVertical, isHorizontal } = { isVertical: false, isHorizontal: true },
): [RefObject<T>, boolean] => {
const [isTruncated, setIsTruncated] = useState(true);
const ref = useRef<T>(null);
const [offsetWidth, setOffsetWidth] = useState(0);
const [scrollWidth, setScrollWidth] = useState(0);
const [offsetHeight, setOffsetHeight] = useState(0);
const [scrollHeight, setScrollHeight] = useState(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
setOffsetWidth(ref.current?.offsetWidth ?? 0);
setScrollWidth(ref.current?.scrollWidth ?? 0);
setOffsetHeight(ref.current?.offsetHeight ?? 0);
setScrollHeight(ref.current?.scrollHeight ?? 0);
});
useEffect(() => {
setIsTruncated(offsetWidth < scrollWidth);
}, [offsetWidth, scrollWidth]);
setIsTruncated(
(isVertical && offsetHeight < scrollHeight) ||
(isHorizontal && offsetWidth < scrollWidth),
);
}, [
offsetWidth,
scrollWidth,
offsetHeight,
scrollHeight,
isVertical,
isHorizontal,
]);
return [ref, isTruncated];
};

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { nanoid } from 'nanoid';
import { Column } from './Column';
import { Metric } from './Metric';
@@ -58,6 +59,7 @@ export const DEFAULT_METRICS: Metric[] = [
{
metric_name: 'COUNT(*)',
expression: 'COUNT(*)',
uuid: nanoid(),
},
];

View File

@@ -60,6 +60,7 @@ export type SavedMetric = string;
*/
export interface Metric {
id?: number;
uuid: string;
metric_name: string;
expression?: Maybe<string>;
certification_details?: Maybe<string>;

View File

@@ -35,7 +35,8 @@ export type Locale =
| 'pt'
| 'pt_BR'
| 'ru'
| 'zh'; // supported locales in Superset
| 'zh'
| 'zh_TW'; // supported locales in Superset
/**
* Language pack provided to `jed`.

View File

@@ -31,7 +31,8 @@ describe('SupersetClientClass', () => {
describe('new SupersetClientClass()', () => {
it('fallback protocol to https when setting only host', () => {
const client = new SupersetClientClass({ host: 'TEST-HOST' });
expect(client.baseUrl).toEqual('https://test-host');
expect(client.protocol).toEqual('https:');
expect(client.host).toEqual('test-host');
});
});
@@ -72,6 +73,15 @@ describe('SupersetClientClass', () => {
);
});
it('constructs a valid url if url, endpoint, and host are all empty and appRoot is defined', () => {
client = new SupersetClientClass({
protocol: 'https:',
host: 'config_host',
appRoot: '/prefix',
});
expect(client.getUrl()).toBe('https://config_host/prefix/');
});
it('does not throw if url, endpoint, and host are all empty', () => {
client = new SupersetClientClass({ protocol: 'https:', host: '' });
expect(client.getUrl()).toBe('https://localhost/');

View File

@@ -35,13 +35,13 @@ describe('makeApi()', () => {
expect(api.requestType).toEqual('search');
});
it('should allow custom client', async () => {
it('should allow custom path', async () => {
expect.assertions(2);
const api = makeApi({
method: 'GET',
endpoint: '/test-custom-client',
});
const client = new SupersetClientClass({ baseUrl: 'http://foo/' });
const client = new SupersetClientClass({ appRoot: '/foo' });
const mockResponse = { yes: 'ok' };
const mockRequest = jest.fn(() =>
Promise.resolve(

View File

@@ -20,10 +20,10 @@ import { DatasourceType, DEFAULT_METRICS } from '@superset-ui/core';
test('DEFAULT_METRICS', () => {
expect(DEFAULT_METRICS).toEqual([
{
expect.objectContaining({
metric_name: 'COUNT(*)',
expression: 'COUNT(*)',
},
}),
]);
});

View File

@@ -103,6 +103,7 @@ import iran from './countries/iran.geojson';
import israel from './countries/israel.geojson';
import italy from './countries/italy.geojson';
import italy_regions from './countries/italy_regions.geojson';
import ivory_coast from './countries/ivory_coast.geojson';
import japan from './countries/japan.geojson';
import jordan from './countries/jordan.geojson';
import kazakhstan from './countries/kazakhstan.geojson';
@@ -160,6 +161,7 @@ import philippines_regions from './countries/philippines_regions.geojson';
import poland from './countries/poland.geojson';
import portugal from './countries/portugal.geojson';
import qatar from './countries/qatar.geojson';
import republic_of_serbia from './countries/republic_of_serbia.geojson';
import romania from './countries/romania.geojson';
import russia from './countries/russia.geojson';
import rwanda from './countries/rwanda.geojson';
@@ -304,6 +306,7 @@ export const countries = {
israel,
italy,
italy_regions,
ivory_coast,
japan,
jordan,
kazakhstan,
@@ -361,6 +364,7 @@ export const countries = {
poland,
portugal,
qatar,
republic_of_serbia,
romania,
russia,
rwanda,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -77,7 +77,7 @@ export function getLayer(
getTargetColor: (d: any) =>
d.targetColor || d.color || [tc.r, tc.g, tc.b, 255 * tc.a],
id: `path-layer-${fd.slice_id}` as const,
strokeWidth: fd.stroke_width ? fd.stroke_width : 3,
getWidth: fd.stroke_width ? fd.stroke_width : 3,
...commonLayerProps(fd, setTooltip, setTooltipContent(fd)),
});
}

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