diff --git a/.github/workflows/superset-python-presto-hive.yml b/.github/workflows/superset-python-presto-hive.yml index 34d23e8833cc7..b7e05b3995a20 100644 --- a/.github/workflows/superset-python-presto-hive.yml +++ b/.github/workflows/superset-python-presto-hive.yml @@ -73,7 +73,7 @@ jobs: setup-postgres - name: Run celery if: steps.check.outcome == 'failure' - run: celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 & + run: celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 & - name: Python unit tests (PostgreSQL) if: steps.check.outcome == 'failure' run: | @@ -148,7 +148,7 @@ jobs: setup-postgres - name: Run celery if: steps.check.outcome == 'failure' - run: celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 & + run: celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 & - name: Python unit tests (PostgreSQL) if: steps.check.outcome == 'failure' run: | diff --git a/.github/workflows/superset-python-unittest.yml b/.github/workflows/superset-python-unittest.yml index 8d03fa8b443fa..3a95556647bdd 100644 --- a/.github/workflows/superset-python-unittest.yml +++ b/.github/workflows/superset-python-unittest.yml @@ -62,7 +62,7 @@ jobs: setup-mysql - name: Run celery if: steps.check.outcome == 'failure' - run: celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 & + run: celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 & - name: Python unit tests (MySQL) if: steps.check.outcome == 'failure' run: | @@ -126,7 +126,7 @@ jobs: setup-postgres - name: Run celery if: steps.check.outcome == 'failure' - run: celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 & + run: celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 & - name: Python unit tests (PostgreSQL) if: steps.check.outcome == 'failure' run: | @@ -182,7 +182,7 @@ jobs: mkdir ${{ github.workspace }}/.temp - name: Run celery if: steps.check.outcome == 'failure' - run: celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 & + run: celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 & - name: Python unit tests (SQLite) if: steps.check.outcome == 'failure' run: | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8a20d1c1e042..e9b82c0748f26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1264,7 +1264,7 @@ To do this, you'll need to: - Start up a celery worker ```shell script - celery worker --app=superset.tasks.celery_app:app -Ofair + celery --app=superset.tasks.celery_app:app worker -Ofair ``` Note that: diff --git a/docker/docker-bootstrap.sh b/docker/docker-bootstrap.sh index 57163ccfe4c4c..e4468b8ad0315 100755 --- a/docker/docker-bootstrap.sh +++ b/docker/docker-bootstrap.sh @@ -38,10 +38,10 @@ fi if [[ "${1}" == "worker" ]]; then echo "Starting Celery worker..." - celery worker --app=superset.tasks.celery_app:app -Ofair -l INFO + celery --app=superset.tasks.celery_app:app worker -Ofair -l INFO elif [[ "${1}" == "beat" ]]; then echo "Starting Celery beat..." - celery beat --app=superset.tasks.celery_app:app --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule + celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule elif [[ "${1}" == "app" ]]; then echo "Starting web app..." flask run -p 8088 --with-threads --reload --debugger --host=0.0.0.0 diff --git a/docs/installation.rst b/docs/installation.rst index edfedce43965d..f35b8c5b87f74 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1080,11 +1080,11 @@ have the same configuration. * To start a Celery worker to leverage the configuration run: :: - celery worker --app=superset.tasks.celery_app:app --pool=prefork -O fair -c 4 + celery --app=superset.tasks.celery_app:app worker --pool=prefork -O fair -c 4 * To start a job which schedules periodic background jobs, run :: - celery beat --app=superset.tasks.celery_app:app + celery --app=superset.tasks.celery_app:app beat To setup a result backend, you need to pass an instance of a derivative of ``from cachelib.base.BaseCache`` to the ``RESULTS_BACKEND`` diff --git a/docs/src/pages/docs/installation/alerts_reports.mdx b/docs/src/pages/docs/installation/alerts_reports.mdx index 6ac292b1a3faa..0b25cbb8b09ef 100644 --- a/docs/src/pages/docs/installation/alerts_reports.mdx +++ b/docs/src/pages/docs/installation/alerts_reports.mdx @@ -246,7 +246,7 @@ services: - superset - postgres - redis - command: "celery worker --app=superset.tasks.celery_app:app --pool=gevent --concurrency=500" + command: "celery --app=superset.tasks.celery_app:app worker --pool=gevent --concurrency=500" volumes: - ./config/:/app/pythonpath/ beat: @@ -258,7 +258,7 @@ services: - superset - postgres - redis - command: "celery beat --app=superset.tasks.celery_app:app --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule" + command: "celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule" volumes: - ./config/:/app/pythonpath/ superset: diff --git a/docs/src/pages/docs/installation/async_queries_celery.mdx b/docs/src/pages/docs/installation/async_queries_celery.mdx index a95fcf0bdcfa4..58c1f1aa0c969 100644 --- a/docs/src/pages/docs/installation/async_queries_celery.mdx +++ b/docs/src/pages/docs/installation/async_queries_celery.mdx @@ -57,13 +57,13 @@ CELERY_CONFIG = CeleryConfig To start a Celery worker to leverage the configuration, run the following command: ``` -celery worker --app=superset.tasks.celery_app:app --pool=prefork -O fair -c 4 +celery --app=superset.tasks.celery_app:app worker --pool=prefork -O fair -c 4 ``` To start a job which schedules periodic background jobs, run the following command: ``` -celery beat --app=superset.tasks.celery_app:app +celery --app=superset.tasks.celery_app:app beat ``` To setup a result backend, you need to pass an instance of a derivative of from diff --git a/docs/src/pages/docs/installation/sql_templating.mdx b/docs/src/pages/docs/installation/sql_templating.mdx index 76f6f94c857e4..68d1ca682d125 100644 --- a/docs/src/pages/docs/installation/sql_templating.mdx +++ b/docs/src/pages/docs/installation/sql_templating.mdx @@ -87,3 +87,167 @@ FEATURE_FLAGS = { The available validators and names can be found in [sql_validators](https://github.com/apache/superset/tree/master/superset/sql_validators). + +### Available Macros + +In this section, we'll walkthrough the pre-defined Jinja macros in Superset. + +**Current Username** + +The `{{ current_username() }}` macro returns the username of the currently logged in user. + +If you have caching enabled in your Superset configuration, then by defaul the the `username` value will be used +by Superset when calculating the cache key. A cache key is a unique identifer that determines if there's a +cache hit in the future and Superset can retrieve cached data. + +You can disable the inclusion of the `username` value in the calculation of the +cache key by adding the following parameter to your Jinja code: + +``` +{{ current_username(add_to_cache_keys=False) }} +``` + +**Current User ID** + +The `{{ current_user_id()}}` macro returns the user_id of the currently logged in user. + +If you have caching enabled in your Superset configuration, then by defaul the the `user_id` value will be used +by Superset when calculating the cache key. A cache key is a unique identifer that determines if there's a +cache hit in the future and Superset can retrieve cached data. + +You can disable the inclusion of the `user_id` value in the calculation of the +cache key by adding the following parameter to your Jinja code: + +``` +{{ current_user_id(add_to_cache_keys=False) }} +``` + +**Custom URL Parameters** + +The `{{ url_param('custom_variable') }}` macro lets you define arbitrary URL +parameters and reference them in your SQL code. + +Here's a concrete example: + +- You write the following query in SQL Lab: + + ``` + SELECT count(*) + FROM ORDERS + WHERE country_code = '{{ url_param('countrycode') }}' + ``` + +- You're hosting Superset at the domain www.example.com and you send your +coworker in Spain the following SQL Lab URL `www.example.com/superset/sqllab?countrycode=ES` +and your coworker in the USA the following SQL Lab URL `www.example.com/superset/sqllab?countrycode=US` +- For your coworker in Spain, the SQL Lab query will be rendered as: + + ``` + SELECT count(*) + FROM ORDERS + WHERE country_code = 'ES' + ``` + +- For your coworker in the USA, the SQL Lab query will be rendered as: + + ``` + SELECT count(*) + FROM ORDERS + WHERE country_code = 'US' + ``` + +**Explicitly Including Values in Cache Key** + +The `{{ cache_key_wrapper() }}` function explicitly instructs Superset to add a value to the +accumulated list of values used in the the calculation of the cache key. + +This function is only needed when you want to wrap your own custom function return values +in the cache key. You can gain more context +[here](https://github.com/apache/superset/blob/efd70077014cbed62e493372d33a2af5237eaadf/superset/jinja_context.py#L133-L148). + +Note that this function powers the caching of the `user_id` and `username` values +in the `current_user_id()` and `current_username()` function calls (if you have caching enabled). + +**Filter Values** + +You can retrieve the value for a specific filter as a list using `{{ filter_values() }}`. + +This is useful if: +- you want to use a filter component to filter a query where the name of filter component column doesn't match the one in the select statement +- you want to have the ability for filter inside the main query for speed purposes + +Here's a concrete example: + +``` +SELECT action, count(*) as times +FROM logs +WHERE + action in ({{ "'" + "','".join(filter_values('action_type')) + "'" }}) +GROUP BY action +``` + +You can use thisfeature to reference the start & end datetimes from a time filter using: + +- `{{ from_dttm }}`: start datetime value +- `{{ to_dttm }}`: end datetime value + +**Filters for a Specific Column** + +The `{{ get_filters() }}` macro returns the filters applied to a given column. In addition to +returning the values (similar to how `filter_values()` does), the `get_filters()` macro +returns the operator specified in the Explore UI. + + This is useful if: +- you want to handle more than the IN operator in your SQL clause +- you want to handle generating custom SQL conditions for a filter +- you want to have the ability to filter inside the main query for speed purposes + +Here's a concrete example: + +``` + WITH RECURSIVE + superiors(employee_id, manager_id, full_name, level, lineage) AS ( + SELECT + employee_id, + manager_id, + full_name, + 1 as level, + employee_id as lineage + FROM + employees + WHERE + 1=1 + + {# Render a blank line #} + {%- for filter in get_filters('full_name', remove_filter=True) -%} + + {%- if filter.get('op') == 'IN' -%} + AND + full_name IN ( {{ "'" + "', '".join(filter.get('val')) + "'" }} ) + {%- endif -%} + + {%- if filter.get('op') == 'LIKE' -%} + AND + full_name LIKE {{ "'" + filter.get('val') + "'" }} + {%- endif -%} + + {%- endfor -%} + UNION ALL + SELECT + e.employee_id, + e.manager_id, + e.full_name, + s.level + 1 as level, + s.lineage + FROM + employees e, + superiors s + WHERE s.manager_id = e.employee_id + ) + + SELECT + employee_id, manager_id, full_name, level, lineage + FROM + superiors + order by lineage, level +``` diff --git a/helm/superset/Chart.yaml b/helm/superset/Chart.yaml index d06eff1859fda..7f446525b68ec 100644 --- a/helm/superset/Chart.yaml +++ b/helm/superset/Chart.yaml @@ -22,7 +22,7 @@ maintainers: - name: craig-rueda email: craig@craigrueda.com url: https://github.com/craig-rueda -version: 0.1.3 +version: 0.1.4 dependencies: - name: postgresql version: 10.2.0 diff --git a/helm/superset/values.yaml b/helm/superset/values.yaml index 58e9faaef5fab..45e956e43971c 100644 --- a/helm/superset/values.yaml +++ b/helm/superset/values.yaml @@ -203,7 +203,7 @@ supersetCeleryBeat: command: - "/bin/sh" - "-c" - - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery beat --app=superset.tasks.celery_app:app --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule" forceReload: false # If true, forces deployment to reload on each upgrade initContainers: - name: wait-for-postgres diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 2649abee8eb27..a8bcfc7f3352b 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -15,35 +15,35 @@ "@emotion/babel-preset-css-prop": "^11.2.0", "@emotion/cache": "^11.1.3", "@emotion/react": "^11.1.5", - "@superset-ui/chart-controls": "^0.17.53", - "@superset-ui/core": "^0.17.53", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.53", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.53", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.53", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.53", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.53", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.53", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.53", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.53", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.53", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.53", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.53", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.53", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.53", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.53", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.53", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.53", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.53", + "@superset-ui/chart-controls": "^0.17.55", + "@superset-ui/core": "^0.17.55", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.55", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.55", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.55", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.55", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.55", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.55", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.55", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.55", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.55", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.55", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.55", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.55", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.55", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.55", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.55", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.55", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.55", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.7", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.53", - "@superset-ui/plugin-chart-echarts": "^0.17.53", - "@superset-ui/plugin-chart-pivot-table": "^0.17.53", - "@superset-ui/plugin-chart-table": "^0.17.53", - "@superset-ui/plugin-chart-word-cloud": "^0.17.53", - "@superset-ui/preset-chart-xy": "^0.17.53", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.55", + "@superset-ui/plugin-chart-echarts": "^0.17.55", + "@superset-ui/plugin-chart-pivot-table": "^0.17.55", + "@superset-ui/plugin-chart-table": "^0.17.55", + "@superset-ui/plugin-chart-word-cloud": "^0.17.55", + "@superset-ui/preset-chart-xy": "^0.17.55", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", @@ -14103,11 +14103,11 @@ } }, "node_modules/@superset-ui/chart-controls": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.53.tgz", - "integrity": "sha512-PjIDka4/lUwXUNEGjkQOIMwVWF2WfknqM6pKFNDPO0/nG4S4faQk96z/ABOXp8GYwIbBshnmmbmW4TCrCQ10Xw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.55.tgz", + "integrity": "sha512-pfw4Uzzr+OvLx/tczyvjpGjZBIBAEK1/dzH66KknDYdhCP60cRN5++OEc23/r9e4Ten/n8YL90NnrDVW/+GtBw==", "dependencies": { - "@superset-ui/core": "0.17.53", + "@superset-ui/core": "0.17.55", "lodash": "^4.17.15", "prop-types": "^15.7.2" }, @@ -14119,9 +14119,9 @@ } }, "node_modules/@superset-ui/core": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.53.tgz", - "integrity": "sha512-2bIRrK3Y+4ZSNu6drc1EzHTq6fO3aWfdjCh43ytju88nlADHheQXgwxEKnmjzI141qxiVL2+oSL2kC6pSTkW8A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.55.tgz", + "integrity": "sha512-z78NBeR9cMpM2os+nDA6+eh9n/gY0HWteg39ZiOxWoxwz4zc+XHY8itGrWLr+VlweuwEAcs5DTUwrCAQ6ZD6gw==", "dependencies": { "@babel/runtime": "^7.1.2", "@emotion/cache": "^11.1.3", @@ -14248,12 +14248,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.53.tgz", - "integrity": "sha512-NLevYzzhQyRgP+vdEfhJyDxJIBbGM/bJTJfFw1iRllny3WQax6iU/X5hUw/iWZqruVNkwSnUA39+EGcjU1aIjg==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.55.tgz", + "integrity": "sha512-zwKD6Q+zrIAoHTff7XoAjeI9IlB2I7TubCyCi+ibV43nQjct773wbM0AtU9Xa7eXQR1fInQR7mksF2BiLGSCNw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", @@ -14272,24 +14272,24 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.53.tgz", - "integrity": "sha512-a3Y8b/1nSuFvzEzUDTVVmad5/YjTBhz0qU2rcVGrdKp2kzuSVXVVljdN7KVisDUNHhYqrttLM8RQrqGw9f7x1A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.55.tgz", + "integrity": "sha512-19Mv8nP4t/jLJYQYr4tSXvmekTQId2nxr7vMSJMejcejWdPaXXBOH985imTcGd/EzBF7s+SbR/waA5ypXnk1PQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.53.tgz", - "integrity": "sha512-zTImQdeBT8raXnxafBIHvaVqOqKoECfyDwgFlPKhs4M7EXPG7U8/VLg0Oi2dCA7/SFZA/ASrJwc/KxW399vJhw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.55.tgz", + "integrity": "sha512-1uEw7eidN5NDZXprcbzGxLChhkN9TUkZrSXPnaIpLQ0gQ4Wd8NSHViy0Oh5D9qHxX40bE6nXIUH2VtmVpycEOA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -14304,13 +14304,13 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.53.tgz", - "integrity": "sha512-QYL0Feyfu7ZH1GeQ9sfEaEgnW2IQG93sJnM29NO53CjSvdbbZItfU9v6xVnAo6jMwcam7JLNYRtuIPgJevNThw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.55.tgz", + "integrity": "sha512-7BcWITPc6piX6GHQ7V9YWamSoyJzcLL7S0MPR9zUW41kGb2YMTV6QYj67UZn8zrHF56X5Cb2xX/XKW+MvdNe/A==", "dependencies": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "prop-types": "^15.6.2" }, "peerDependencies": { @@ -14318,12 +14318,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.53.tgz", - "integrity": "sha512-F7hkrBxC7EWrClQ1jb7anzj1SmIjqXVMz2JKhzwEUk++Tafnn0mrB7Yo51u3twFFOY5bwn+KcI1NObzBRkXguQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.55.tgz", + "integrity": "sha512-8jTDis27tuyOlspuIdMuAhoYRlzovURNi+tkt/N/T1DS54HXlutmK70rhL+6nVGVXGYNT1nzZuMgYbyinKWBbw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.7.2" }, @@ -14332,12 +14332,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.53.tgz", - "integrity": "sha512-NXx/E3AiTxkL+qwaj8B0IDrhWo6P5u5EuXXx1xaWqMTH18YomyeA9l4NBPwsjCfhAMqrEeT0hzeY2/WSoPq5KQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.55.tgz", + "integrity": "sha512-MUKzENLBnpBs0umUxAeWEqmb5snX71EH2+uxElH3kClf7meqv7eAyMpR49O7R7wzPXsChQ9fiAYD9azA6gDW/g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -14345,14 +14345,14 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.53.tgz", - "integrity": "sha512-EQ/VvG+qCec+IqnwYHA90iHAjkhnPNGkKbTuKlsRyL3ONfxg3n6L4EQOlAA0HvELKkFAZXBxh8TA8Qc3j+g4Fw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.55.tgz", + "integrity": "sha512-/g57p3Ux0GzmFSd6qPNmC5msifag1ex+UmHscfH9y0O00YU2TK/boAC1fB9uNH7XhA/E+7GEeeRo8t7ExE70yA==", "dependencies": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", @@ -14421,12 +14421,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.53.tgz", - "integrity": "sha512-LsM4HOuOkiabRNxMUjjietbFx99admne59Mm5zQdsRPNEpN/EKEWu8R4G4crSSqxxzD9KVnveRPE7OD0n91k/A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.55.tgz", + "integrity": "sha512-ngXI77ILZivz99mbcZsUcKfdMJEvyQe58+8EkM93IBAPdyCzIHay8UOooicCZzhLizGIoQ42SkApBsAhzJ+TXg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" @@ -14456,12 +14456,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.53.tgz", - "integrity": "sha512-JuM77arnxECuSiHkdLMry4JruuVTAfTKTtR8F4qGOpiYiXzGEv4K+y12eqBe1o94ckJF43Esz9e1fdPLDkjqTw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.55.tgz", + "integrity": "sha512-Nzi6AhYNbJA/7m/VMa4Cs3qLnO+Eay/dKzaWR4mD5hg7LqajMF5OJXbHA1rp2YmYsUZkC3e19pXzEFsmyQ64dA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", @@ -14482,12 +14482,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.53.tgz", - "integrity": "sha512-QkRVm0XGoOxqOX0nRvHnGon2gG8MmV+dbBBpmPkmspxCWKrn183Wzq5SiMlM4vgo2HaroWUIPuBgLBd7rYZtGw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.55.tgz", + "integrity": "sha512-r96LZqVTKncE5F43D+Ze2Ylqu3Sgz95z95STXEthARJ8XBvCCbobTG4QgVN5Eg2CUCkZTprsyUQZqfwuclxugQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" @@ -14497,12 +14497,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.53.tgz", - "integrity": "sha512-NcwuEd+rXfmwPshPby0jEgnJnbYfKruM7l0Hb3lIw6iMTc1IV21d1CMftQPvYYdwagam0FapBO2YcSvnvj2rDw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.55.tgz", + "integrity": "sha512-X6PEW3Y1k/gDFBuatAC7ns8AAWR/y1PyXtPBReByF7mBOwFxXldbu6WreIqcoj2275VgAtz0/557b/IUo4zwcg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.7.2" }, @@ -14511,12 +14511,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.53.tgz", - "integrity": "sha512-CTzKjaKCdT/+bFlXUDD4nXC2CO7mXmIPJ2K/M94rY2G2gdAWRZJ1i2HlcvTP+RY/AItzZm3C+E7hYdAQ6toBkA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.55.tgz", + "integrity": "sha512-cJKSumvjxHOYe9JtmsesdE0aKaDYqp5vDbBQgn+kycCVaQRqbddwbP2zmPe/I4/z3MUrJjab3d+OtObqV78O4w==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" @@ -14526,24 +14526,24 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.53.tgz", - "integrity": "sha512-bk7mttnZFGgGmWCfj0kO++65XsMNyQJch0dgfRRnLVTlSnY89/kGqszTKybbCZhsbx4T5bJ+bn6hZKAGH+FnUA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.55.tgz", + "integrity": "sha512-dXVVTulMpFAjRtckwbCKadWLtpaWN5W1jO0mKYJF9TktjdzcHyEs9B8x++EFPdVOVzk5dTPAjiQ9qjWg4B8/Gg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.53.tgz", - "integrity": "sha512-ppvQuKAS0rMhniKenLXSKczmAsHX4igYc0bVZAvfFDmLNW3tnlmivL+zYSw/sQ9PAhjMGDbTBlSio1oJ+91wiA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.55.tgz", + "integrity": "sha512-NnL7BAXdevk758JJDXB0PqnYr67cynxVUuGQn9BI5KOGwd1YfMaRiQWE+iVhTjUI4QObP43pzqlXiq2LdpWjIQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" @@ -14553,12 +14553,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.53.tgz", - "integrity": "sha512-3tvMghg5WUAq40su8cZrjJHoc/TsK1WWx6UFu+j2mPOh/BJJZb8wh7A63X82ubLdyzEqdjxsEs9pzZWzs7kUHw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.55.tgz", + "integrity": "sha512-GbkunzrGCNVEiZ+yz1claWIsPMFXlooxaAl6JgZGRvKW8hGRzNALBbRLLJZ1o2pSlbmaj/+ZdGcnuhgMLvn8LQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" @@ -14568,47 +14568,47 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.53.tgz", - "integrity": "sha512-60aGflqOi5+XDE3BR/p+Pw0xVp7OHsjwroX77CwkwBtFkw1AFVWczaTJH6CYeeCJZXCLYjrbc5OFMuaxIJ+j+Q==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.55.tgz", + "integrity": "sha512-5K2XntaMLHpKNs8txfgX7Mvy7Q8+NPSpW4WD6VZxMVEndJ73dfRpn1R7xxXiHPmwoAvrZvXIglPyo1WzIhS0Rw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.53.tgz", - "integrity": "sha512-t0z7XPsDtDpnZ+fIpn57w9Vi3oWQ7ximDdjmag1WGhC6+dwR3XxEpNcicI6P6xfNX078RT8Iz89PZQBtagAAkA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.55.tgz", + "integrity": "sha512-EWT/oldmjLPmO8rkJPMCFr3P2LE1gK7SlO8acvy1JJ/bbBOovTlbtQmLDAlybtzU2oSBk2CRbjXnZlML11pMqg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.53.tgz", - "integrity": "sha512-LV16Qwiz7ahfhCmuWIGk6f54KpdRJDAyLtr/ifFi8a2AcoG27Lf7hZZ3mCI9Jl5X6c7LLBmvAHfxdbBnLGa8+g==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.55.tgz", + "integrity": "sha512-ZDkBFfDzJKTE6zDnD30DWbuFKe3vTordo13zYPTCpOUqaoClcvAL72FAcsMWifvq+M5Fv9w8b3nrPfAtc8VbtQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.53.tgz", - "integrity": "sha512-gnDBTyWPctqucyQzAObH6N+3f9GUQq9qpQ4cNbtvpIoVgXowYA5Q5dIfXBPnq525t78o3eiWqclTYf2Xcd62Kw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.55.tgz", + "integrity": "sha512-qQuc93ePtoERqpPEeDJIxbWIEY+4WwW6nl3ETJTC7nuoePcITZeJ0AVqwZh85YbJWttvQJxFOTMPRqQDBau37w==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", @@ -14633,13 +14633,13 @@ "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, "node_modules/@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.53.tgz", - "integrity": "sha512-HUlE6IZUjFvPMiXCj1cdRiR4avFLVhT5qwIQZk1l30kycl8/73rTm37Y/syBfZMPrfCrIW3nyReqfcnAaNqw9g==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.55.tgz", + "integrity": "sha512-TY5/s8J56yF4Fs7tj9uD5l20R7hXU/+QxvBIRXq5bJqQxXqEH0+skmPhSd3wkRc/fd79IC61vY5AVWDisAcZdQ==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", @@ -14680,13 +14680,13 @@ } }, "node_modules/@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.53.tgz", - "integrity": "sha512-wTbQRCZDrnb16tLJzXYbIiCFbHddRJ3fo5DKsbv6MFNrfOLWWx1SjAZ5C60e57u33XpKdTE5jGpEuGdq7BZ55w==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.55.tgz", + "integrity": "sha512-oJs/n8ZUS3in5XrJa4wd2mR1tw9IB3f5We7Hur3twnDlGB/7aDedyd0A4jJ9JTTBm5O6uYDytvAPxzI1CPXiFg==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -14703,12 +14703,12 @@ } }, "node_modules/@superset-ui/plugin-chart-echarts": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.53.tgz", - "integrity": "sha512-XXKqhr2CwZfi02qW55d9SQnNmdewTsAJT6xePBjci0SXAZRmi/T8vRbq2OCDJ7mQ0de7kjVBydAuOEEU/Y554A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.55.tgz", + "integrity": "sha512-dKLLYCey/ODrJBoTTvoFQxbJMIx2rq5jirHh+0vyu3qMXwo1a+GGO+QKBA2y4n6M95mhNMb/SSk1sd8uh+NuNw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.1.1", @@ -14720,12 +14720,12 @@ } }, "node_modules/@superset-ui/plugin-chart-pivot-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.53.tgz", - "integrity": "sha512-18CTaM1sRgK5laFwHlKV+1A7+l9YWwPAvb7XrMjS8CQq0T2aEqNSQm7KWByG+LEj2x86idM8gaWghNes27yVtQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.55.tgz", + "integrity": "sha512-oVdFjQNxMT4X9lq8ztFt0zBKGqerDYHpZYfyVH8gxk6hZeMFjPEYioJcJFwUn+Y4Z0yWSxWuj0ZHbwSccfV0/Q==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@superset-ui/react-pivottable": "^0.12.8" }, "peerDependencies": { @@ -14733,12 +14733,12 @@ } }, "node_modules/@superset-ui/plugin-chart-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.53.tgz", - "integrity": "sha512-PSeL/zQSTvQyztjUMMm4U4G6oEM3xk3wkC4HTpuLEpjQ7qyGme39M1JeCGvNG4pPZRm0nO4pU+0U/36oR0lAjw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.55.tgz", + "integrity": "sha512-rDEtzDd7qEtZi/iYQ5kI+N6lU39+Rn19Dm9Gtoq3H0f5UZ0UC5mTaoN5/WtRk+e16pGgGJ4mgB0aRzS1C8psSA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-array": "^2.9.0", "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", @@ -14764,12 +14764,12 @@ } }, "node_modules/@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.53.tgz", - "integrity": "sha512-lQTr9UpeoGgX1B0SkjrvtL0zjgYVoJbm6RVv8ELG+efCG1oYAoIVgw2sahJI4zLqNiHcNeWqHUcu7NK06uc4mA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.55.tgz", + "integrity": "sha512-tJDmL1ahNr8ukRkjKRaAhryjLCkOKubqRSiWhKjmndGqT6nxoxheAyMu7gW/heLV6GGYHD4MHpy3tMVuodtlQw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", @@ -14802,14 +14802,14 @@ } }, "node_modules/@superset-ui/preset-chart-xy": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.53.tgz", - "integrity": "sha512-nmqif4Zd7Tdx4hLoDiiRiNFUFn1kliumjp9RQK68eMaefWcl1vTMT7nPmyFvgUH5390HJygpC3up50+j5Bngkg==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.55.tgz", + "integrity": "sha512-neJFrZXIj3EOXapyrYLWvz3q1gwKphrx5UAAjBYWBG0wRXBqSVlud0SaROj7XRNpxMEkak4ZCVcYinrVSDvkfA==", "dependencies": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", @@ -66285,19 +66285,19 @@ } }, "@superset-ui/chart-controls": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.53.tgz", - "integrity": "sha512-PjIDka4/lUwXUNEGjkQOIMwVWF2WfknqM6pKFNDPO0/nG4S4faQk96z/ABOXp8GYwIbBshnmmbmW4TCrCQ10Xw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.55.tgz", + "integrity": "sha512-pfw4Uzzr+OvLx/tczyvjpGjZBIBAEK1/dzH66KknDYdhCP60cRN5++OEc23/r9e4Ten/n8YL90NnrDVW/+GtBw==", "requires": { - "@superset-ui/core": "0.17.53", + "@superset-ui/core": "0.17.55", "lodash": "^4.17.15", "prop-types": "^15.7.2" } }, "@superset-ui/core": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.53.tgz", - "integrity": "sha512-2bIRrK3Y+4ZSNu6drc1EzHTq6fO3aWfdjCh43ytju88nlADHheQXgwxEKnmjzI141qxiVL2+oSL2kC6pSTkW8A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.55.tgz", + "integrity": "sha512-z78NBeR9cMpM2os+nDA6+eh9n/gY0HWteg39ZiOxWoxwz4zc+XHY8itGrWLr+VlweuwEAcs5DTUwrCAQ6ZD6gw==", "requires": { "@babel/runtime": "^7.1.2", "@emotion/cache": "^11.1.3", @@ -66409,12 +66409,12 @@ } }, "@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.53.tgz", - "integrity": "sha512-NLevYzzhQyRgP+vdEfhJyDxJIBbGM/bJTJfFw1iRllny3WQax6iU/X5hUw/iWZqruVNkwSnUA39+EGcjU1aIjg==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.55.tgz", + "integrity": "sha512-zwKD6Q+zrIAoHTff7XoAjeI9IlB2I7TubCyCi+ibV43nQjct773wbM0AtU9Xa7eXQR1fInQR7mksF2BiLGSCNw==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", @@ -66432,24 +66432,24 @@ } }, "@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.53.tgz", - "integrity": "sha512-a3Y8b/1nSuFvzEzUDTVVmad5/YjTBhz0qU2rcVGrdKp2kzuSVXVVljdN7KVisDUNHhYqrttLM8RQrqGw9f7x1A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.55.tgz", + "integrity": "sha512-19Mv8nP4t/jLJYQYr4tSXvmekTQId2nxr7vMSJMejcejWdPaXXBOH985imTcGd/EzBF7s+SbR/waA5ypXnk1PQ==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.53.tgz", - "integrity": "sha512-zTImQdeBT8raXnxafBIHvaVqOqKoECfyDwgFlPKhs4M7EXPG7U8/VLg0Oi2dCA7/SFZA/ASrJwc/KxW399vJhw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.55.tgz", + "integrity": "sha512-1uEw7eidN5NDZXprcbzGxLChhkN9TUkZrSXPnaIpLQ0gQ4Wd8NSHViy0Oh5D9qHxX40bE6nXIUH2VtmVpycEOA==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -66466,34 +66466,34 @@ } }, "@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.53.tgz", - "integrity": "sha512-QYL0Feyfu7ZH1GeQ9sfEaEgnW2IQG93sJnM29NO53CjSvdbbZItfU9v6xVnAo6jMwcam7JLNYRtuIPgJevNThw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.55.tgz", + "integrity": "sha512-7BcWITPc6piX6GHQ7V9YWamSoyJzcLL7S0MPR9zUW41kGb2YMTV6QYj67UZn8zrHF56X5Cb2xX/XKW+MvdNe/A==", "requires": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.53.tgz", - "integrity": "sha512-F7hkrBxC7EWrClQ1jb7anzj1SmIjqXVMz2JKhzwEUk++Tafnn0mrB7Yo51u3twFFOY5bwn+KcI1NObzBRkXguQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.55.tgz", + "integrity": "sha512-8jTDis27tuyOlspuIdMuAhoYRlzovURNi+tkt/N/T1DS54HXlutmK70rhL+6nVGVXGYNT1nzZuMgYbyinKWBbw==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.53.tgz", - "integrity": "sha512-NXx/E3AiTxkL+qwaj8B0IDrhWo6P5u5EuXXx1xaWqMTH18YomyeA9l4NBPwsjCfhAMqrEeT0hzeY2/WSoPq5KQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.55.tgz", + "integrity": "sha512-MUKzENLBnpBs0umUxAeWEqmb5snX71EH2+uxElH3kClf7meqv7eAyMpR49O7R7wzPXsChQ9fiAYD9azA6gDW/g==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -66501,14 +66501,14 @@ } }, "@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.53.tgz", - "integrity": "sha512-EQ/VvG+qCec+IqnwYHA90iHAjkhnPNGkKbTuKlsRyL3ONfxg3n6L4EQOlAA0HvELKkFAZXBxh8TA8Qc3j+g4Fw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.55.tgz", + "integrity": "sha512-/g57p3Ux0GzmFSd6qPNmC5msifag1ex+UmHscfH9y0O00YU2TK/boAC1fB9uNH7XhA/E+7GEeeRo8t7ExE70yA==", "requires": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", @@ -66576,12 +66576,12 @@ } }, "@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.53.tgz", - "integrity": "sha512-LsM4HOuOkiabRNxMUjjietbFx99admne59Mm5zQdsRPNEpN/EKEWu8R4G4crSSqxxzD9KVnveRPE7OD0n91k/A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.55.tgz", + "integrity": "sha512-ngXI77ILZivz99mbcZsUcKfdMJEvyQe58+8EkM93IBAPdyCzIHay8UOooicCZzhLizGIoQ42SkApBsAhzJ+TXg==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" @@ -66610,12 +66610,12 @@ } }, "@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.53.tgz", - "integrity": "sha512-JuM77arnxECuSiHkdLMry4JruuVTAfTKTtR8F4qGOpiYiXzGEv4K+y12eqBe1o94ckJF43Esz9e1fdPLDkjqTw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.55.tgz", + "integrity": "sha512-Nzi6AhYNbJA/7m/VMa4Cs3qLnO+Eay/dKzaWR4mD5hg7LqajMF5OJXbHA1rp2YmYsUZkC3e19pXzEFsmyQ64dA==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", @@ -66632,118 +66632,118 @@ } }, "@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.53.tgz", - "integrity": "sha512-QkRVm0XGoOxqOX0nRvHnGon2gG8MmV+dbBBpmPkmspxCWKrn183Wzq5SiMlM4vgo2HaroWUIPuBgLBd7rYZtGw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.55.tgz", + "integrity": "sha512-r96LZqVTKncE5F43D+Ze2Ylqu3Sgz95z95STXEthARJ8XBvCCbobTG4QgVN5Eg2CUCkZTprsyUQZqfwuclxugQ==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" } }, "@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.53.tgz", - "integrity": "sha512-NcwuEd+rXfmwPshPby0jEgnJnbYfKruM7l0Hb3lIw6iMTc1IV21d1CMftQPvYYdwagam0FapBO2YcSvnvj2rDw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.55.tgz", + "integrity": "sha512-X6PEW3Y1k/gDFBuatAC7ns8AAWR/y1PyXtPBReByF7mBOwFxXldbu6WreIqcoj2275VgAtz0/557b/IUo4zwcg==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.53.tgz", - "integrity": "sha512-CTzKjaKCdT/+bFlXUDD4nXC2CO7mXmIPJ2K/M94rY2G2gdAWRZJ1i2HlcvTP+RY/AItzZm3C+E7hYdAQ6toBkA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.55.tgz", + "integrity": "sha512-cJKSumvjxHOYe9JtmsesdE0aKaDYqp5vDbBQgn+kycCVaQRqbddwbP2zmPe/I4/z3MUrJjab3d+OtObqV78O4w==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.53.tgz", - "integrity": "sha512-bk7mttnZFGgGmWCfj0kO++65XsMNyQJch0dgfRRnLVTlSnY89/kGqszTKybbCZhsbx4T5bJ+bn6hZKAGH+FnUA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.55.tgz", + "integrity": "sha512-dXVVTulMpFAjRtckwbCKadWLtpaWN5W1jO0mKYJF9TktjdzcHyEs9B8x++EFPdVOVzk5dTPAjiQ9qjWg4B8/Gg==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.53.tgz", - "integrity": "sha512-ppvQuKAS0rMhniKenLXSKczmAsHX4igYc0bVZAvfFDmLNW3tnlmivL+zYSw/sQ9PAhjMGDbTBlSio1oJ+91wiA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.55.tgz", + "integrity": "sha512-NnL7BAXdevk758JJDXB0PqnYr67cynxVUuGQn9BI5KOGwd1YfMaRiQWE+iVhTjUI4QObP43pzqlXiq2LdpWjIQ==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.53.tgz", - "integrity": "sha512-3tvMghg5WUAq40su8cZrjJHoc/TsK1WWx6UFu+j2mPOh/BJJZb8wh7A63X82ubLdyzEqdjxsEs9pzZWzs7kUHw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.55.tgz", + "integrity": "sha512-GbkunzrGCNVEiZ+yz1claWIsPMFXlooxaAl6JgZGRvKW8hGRzNALBbRLLJZ1o2pSlbmaj/+ZdGcnuhgMLvn8LQ==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.53.tgz", - "integrity": "sha512-60aGflqOi5+XDE3BR/p+Pw0xVp7OHsjwroX77CwkwBtFkw1AFVWczaTJH6CYeeCJZXCLYjrbc5OFMuaxIJ+j+Q==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.55.tgz", + "integrity": "sha512-5K2XntaMLHpKNs8txfgX7Mvy7Q8+NPSpW4WD6VZxMVEndJ73dfRpn1R7xxXiHPmwoAvrZvXIglPyo1WzIhS0Rw==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.53.tgz", - "integrity": "sha512-t0z7XPsDtDpnZ+fIpn57w9Vi3oWQ7ximDdjmag1WGhC6+dwR3XxEpNcicI6P6xfNX078RT8Iz89PZQBtagAAkA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.55.tgz", + "integrity": "sha512-EWT/oldmjLPmO8rkJPMCFr3P2LE1gK7SlO8acvy1JJ/bbBOovTlbtQmLDAlybtzU2oSBk2CRbjXnZlML11pMqg==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.53.tgz", - "integrity": "sha512-LV16Qwiz7ahfhCmuWIGk6f54KpdRJDAyLtr/ifFi8a2AcoG27Lf7hZZ3mCI9Jl5X6c7LLBmvAHfxdbBnLGa8+g==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.55.tgz", + "integrity": "sha512-ZDkBFfDzJKTE6zDnD30DWbuFKe3vTordo13zYPTCpOUqaoClcvAL72FAcsMWifvq+M5Fv9w8b3nrPfAtc8VbtQ==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.53.tgz", - "integrity": "sha512-gnDBTyWPctqucyQzAObH6N+3f9GUQq9qpQ4cNbtvpIoVgXowYA5Q5dIfXBPnq525t78o3eiWqclTYf2Xcd62Kw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.55.tgz", + "integrity": "sha512-qQuc93ePtoERqpPEeDJIxbWIEY+4WwW6nl3ETJTC7nuoePcITZeJ0AVqwZh85YbJWttvQJxFOTMPRqQDBau37w==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", @@ -66767,13 +66767,13 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.53.tgz", - "integrity": "sha512-HUlE6IZUjFvPMiXCj1cdRiR4avFLVhT5qwIQZk1l30kycl8/73rTm37Y/syBfZMPrfCrIW3nyReqfcnAaNqw9g==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.55.tgz", + "integrity": "sha512-TY5/s8J56yF4Fs7tj9uD5l20R7hXU/+QxvBIRXq5bJqQxXqEH0+skmPhSd3wkRc/fd79IC61vY5AVWDisAcZdQ==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", @@ -66806,13 +66806,13 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.53.tgz", - "integrity": "sha512-wTbQRCZDrnb16tLJzXYbIiCFbHddRJ3fo5DKsbv6MFNrfOLWWx1SjAZ5C60e57u33XpKdTE5jGpEuGdq7BZ55w==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.55.tgz", + "integrity": "sha512-oJs/n8ZUS3in5XrJa4wd2mR1tw9IB3f5We7Hur3twnDlGB/7aDedyd0A4jJ9JTTBm5O6uYDytvAPxzI1CPXiFg==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -66826,12 +66826,12 @@ } }, "@superset-ui/plugin-chart-echarts": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.53.tgz", - "integrity": "sha512-XXKqhr2CwZfi02qW55d9SQnNmdewTsAJT6xePBjci0SXAZRmi/T8vRbq2OCDJ7mQ0de7kjVBydAuOEEU/Y554A==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.55.tgz", + "integrity": "sha512-dKLLYCey/ODrJBoTTvoFQxbJMIx2rq5jirHh+0vyu3qMXwo1a+GGO+QKBA2y4n6M95mhNMb/SSk1sd8uh+NuNw==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.1.1", @@ -66840,22 +66840,22 @@ } }, "@superset-ui/plugin-chart-pivot-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.53.tgz", - "integrity": "sha512-18CTaM1sRgK5laFwHlKV+1A7+l9YWwPAvb7XrMjS8CQq0T2aEqNSQm7KWByG+LEj2x86idM8gaWghNes27yVtQ==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.55.tgz", + "integrity": "sha512-oVdFjQNxMT4X9lq8ztFt0zBKGqerDYHpZYfyVH8gxk6hZeMFjPEYioJcJFwUn+Y4Z0yWSxWuj0ZHbwSccfV0/Q==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@superset-ui/react-pivottable": "^0.12.8" } }, "@superset-ui/plugin-chart-table": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.53.tgz", - "integrity": "sha512-PSeL/zQSTvQyztjUMMm4U4G6oEM3xk3wkC4HTpuLEpjQ7qyGme39M1JeCGvNG4pPZRm0nO4pU+0U/36oR0lAjw==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.55.tgz", + "integrity": "sha512-rDEtzDd7qEtZi/iYQ5kI+N6lU39+Rn19Dm9Gtoq3H0f5UZ0UC5mTaoN5/WtRk+e16pGgGJ4mgB0aRzS1C8psSA==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-array": "^2.9.0", "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", @@ -66877,12 +66877,12 @@ } }, "@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.53.tgz", - "integrity": "sha512-lQTr9UpeoGgX1B0SkjrvtL0zjgYVoJbm6RVv8ELG+efCG1oYAoIVgw2sahJI4zLqNiHcNeWqHUcu7NK06uc4mA==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.55.tgz", + "integrity": "sha512-tJDmL1ahNr8ukRkjKRaAhryjLCkOKubqRSiWhKjmndGqT6nxoxheAyMu7gW/heLV6GGYHD4MHpy3tMVuodtlQw==", "requires": { - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", @@ -66913,14 +66913,14 @@ } }, "@superset-ui/preset-chart-xy": { - "version": "0.17.53", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.53.tgz", - "integrity": "sha512-nmqif4Zd7Tdx4hLoDiiRiNFUFn1kliumjp9RQK68eMaefWcl1vTMT7nPmyFvgUH5390HJygpC3up50+j5Bngkg==", + "version": "0.17.55", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.55.tgz", + "integrity": "sha512-neJFrZXIj3EOXapyrYLWvz3q1gwKphrx5UAAjBYWBG0wRXBqSVlud0SaROj7XRNpxMEkak4ZCVcYinrVSDvkfA==", "requires": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.53", - "@superset-ui/core": "0.17.53", + "@superset-ui/chart-controls": "0.17.55", + "@superset-ui/core": "0.17.55", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 120644c2b81f0..b1f860dc097f3 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -67,35 +67,35 @@ "@emotion/babel-preset-css-prop": "^11.2.0", "@emotion/cache": "^11.1.3", "@emotion/react": "^11.1.5", - "@superset-ui/chart-controls": "^0.17.53", - "@superset-ui/core": "^0.17.53", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.53", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.53", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.53", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.53", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.53", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.53", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.53", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.53", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.53", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.53", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.53", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.53", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.53", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.53", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.53", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.53", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.53", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.53", + "@superset-ui/chart-controls": "^0.17.55", + "@superset-ui/core": "^0.17.55", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.55", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.55", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.55", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.55", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.55", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.55", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.55", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.55", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.55", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.55", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.55", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.55", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.55", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.55", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.55", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.55", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.55", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.55", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.7", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.53", - "@superset-ui/plugin-chart-echarts": "^0.17.53", - "@superset-ui/plugin-chart-pivot-table": "^0.17.53", - "@superset-ui/plugin-chart-table": "^0.17.53", - "@superset-ui/plugin-chart-word-cloud": "^0.17.53", - "@superset-ui/preset-chart-xy": "^0.17.53", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.55", + "@superset-ui/plugin-chart-echarts": "^0.17.55", + "@superset-ui/plugin-chart-pivot-table": "^0.17.55", + "@superset-ui/plugin-chart-table": "^0.17.55", + "@superset-ui/plugin-chart-word-cloud": "^0.17.55", + "@superset-ui/preset-chart-xy": "^0.17.55", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", diff --git a/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx b/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx index 4e7e9346f8826..77a35eba895a5 100644 --- a/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx +++ b/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx @@ -48,7 +48,7 @@ const datasourceData = { }; const DATASOURCES_ENDPOINT = - 'glob:*/api/v1/dataset/?q=(order_column:changed_on_delta_humanized,order_direction:asc,page:0,page_size:20)'; + 'glob:*/api/v1/dataset/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)'; const DATASOURCE_ENDPOINT = `glob:*/datasource/get/${datasourceData.type}/${datasourceData.id}`; const DATASOURCE_PAYLOAD = { new: 'data' }; diff --git a/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx b/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx index 39bcfad8658ec..96d23eb63da1f 100644 --- a/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx +++ b/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx @@ -233,4 +233,21 @@ describe('DatasourceEditor RTL', () => { ); expect(warningMarkdown.value).toEqual('someone'); }); + it('properly updates the metric information', async () => { + render(, { + useRedux: true, + }); + const metricButton = screen.getByTestId('collection-tab-Metrics'); + userEvent.click(metricButton); + const expandToggle = await screen.findAllByLabelText(/toggle expand/i); + userEvent.click(expandToggle[1]); + const certifiedBy = await screen.findByPlaceholderText(/certified by/i); + userEvent.type(certifiedBy, 'I am typing a new name'); + const certificationDetails = await screen.findByPlaceholderText( + /certification details/i, + ); + expect(certifiedBy.value).toEqual('I am typing a new name'); + userEvent.type(certificationDetails, 'I am typing something new'); + expect(certificationDetails.value).toEqual('I am typing something new'); + }); }); diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.jsx b/superset-frontend/src/SqlLab/components/QueryTable/index.jsx index d23fd49ca6d24..d53c9cff2892b 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.jsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.jsx @@ -120,6 +120,14 @@ const statusAttributes = { status: 'queued', }, }, + error: { + color: ({ theme }) => theme.colors.error.base, + config: { + name: 'error', + label: t('Unknown Status'), + status: 'unknown status!', + }, + }, }; const StatusIcon = styled(Icon, { @@ -172,6 +180,8 @@ const QueryTable = props => { return props.queries .map(query => { const q = { ...query }; + const status = statusAttributes[q.state] || statusAttributes.error; + if (q.endDttm) { q.duration = fDuration(q.startDttm, q.endDttm); } @@ -268,14 +278,11 @@ const QueryTable = props => { /> ); q.state = ( - + diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 593695919c58b..a50d664019710 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -30,6 +30,7 @@ export type QueryState = | 'running' | 'scheduled' | 'success' + | 'fetching' | 'timed_out'; export type Query = { diff --git a/superset-frontend/src/common/components/index.tsx b/superset-frontend/src/common/components/index.tsx index c98099645a7bc..230a9e183c61d 100644 --- a/superset-frontend/src/common/components/index.tsx +++ b/superset-frontend/src/common/components/index.tsx @@ -54,6 +54,7 @@ export { Tag, Tabs, Tooltip, + Upload, Input as AntdInput, } from 'antd'; export { Card as AntdCard } from 'antd'; diff --git a/superset-frontend/src/components/Button/index.tsx b/superset-frontend/src/components/Button/index.tsx index 1f9d737836b0d..62e1214d3dffc 100644 --- a/superset-frontend/src/components/Button/index.tsx +++ b/superset-frontend/src/components/Button/index.tsx @@ -62,6 +62,7 @@ export interface ButtonProps { href?: string; htmlType?: 'button' | 'submit' | 'reset'; cta?: boolean; + loading?: boolean | { delay?: number | undefined } | undefined; } export default function Button(props: ButtonProps) { diff --git a/superset-frontend/src/components/ImportModal/ImportModal.test.tsx b/superset-frontend/src/components/ImportModal/ImportModal.test.tsx index 478fd599b3779..d4e3723b44957 100644 --- a/superset-frontend/src/components/ImportModal/ImportModal.test.tsx +++ b/superset-frontend/src/components/ImportModal/ImportModal.test.tsx @@ -17,10 +17,12 @@ * under the License. */ import React from 'react'; +import { act } from 'react-dom/test-utils'; import thunk from 'redux-thunk'; import configureStore from 'redux-mock-store'; import { styledMount as mount } from 'spec/helpers/theming'; import { ReactWrapper } from 'enzyme'; +import { Upload } from 'src/common/components'; import Button from 'src/components/Button'; import { ImportResourceName } from 'src/views/CRUD/types'; import ImportModelsModal from 'src/components/ImportModal'; @@ -66,29 +68,37 @@ describe('ImportModelsModal', () => { expect(wrapper.find('h4').text()).toEqual('Import database'); }); - it('renders a label and a file input field', () => { + it('renders a file input field', () => { expect(wrapper.find('input[type="file"]')).toExist(); - expect(wrapper.find('label')).toExist(); }); - it('should attach the label to the input field', () => { - const id = 'modelFile'; - expect(wrapper.find('label').prop('htmlFor')).toBe(id); - expect(wrapper.find('input').prop('id')).toBe(id); - }); - - it('should render the close, import and cancel buttons', () => { - expect(wrapper.find('button')).toHaveLength(3); + it('should render the close, file, import and cancel buttons', () => { + expect(wrapper.find('button')).toHaveLength(4); }); it('should render the import button initially disabled', () => { - expect(wrapper.find(Button).at(1).prop('disabled')).toBe(true); + expect(wrapper.find(Button).at(2).prop('disabled')).toBe(true); }); it('should render the import button enabled when a file is selected', () => { const file = new File([new ArrayBuffer(1)], 'model_export.zip'); - wrapper.find('input').simulate('change', { target: { files: [file] } }); - expect(wrapper.find(Button).at(1).prop('disabled')).toBe(false); + act(() => { + const handler = wrapper.find(Upload).prop('onChange'); + if (handler) { + handler({ + fileList: [], + file: { + name: 'model_export.zip', + originFileObj: file, + uid: '-1', + size: 0, + type: 'zip', + }, + }); + } + }); + wrapper.update(); + expect(wrapper.find(Button).at(2).prop('disabled')).toBe(false); }); it('should render password fields when needed for import', () => { diff --git a/superset-frontend/src/components/ImportModal/index.tsx b/superset-frontend/src/components/ImportModal/index.tsx index f9dffcbab9ec6..193d3c568e21a 100644 --- a/superset-frontend/src/components/ImportModal/index.tsx +++ b/superset-frontend/src/components/ImportModal/index.tsx @@ -16,11 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; +import React, { FunctionComponent, useEffect, useState } from 'react'; +import { UploadChangeParam, UploadFile } from 'antd/lib/upload/interface'; import { styled, t } from '@superset-ui/core'; -import Icon from 'src//components/Icon'; +import Button from 'src/components/Button'; +import Icon from 'src/components/Icon'; import Modal from 'src/components/Modal'; +import { Upload } from 'src/common/components'; import { useImportResource } from 'src/views/CRUD/hooks'; import { ImportResourceName } from 'src/views/CRUD/types'; @@ -127,24 +130,21 @@ const ImportModelsModal: FunctionComponent = ({ setPasswordFields = () => {}, }) => { const [isHidden, setIsHidden] = useState(true); - const [uploadFile, setUploadFile] = useState(null); const [passwords, setPasswords] = useState>({}); const [needsOverwriteConfirm, setNeedsOverwriteConfirm] = useState( false, ); const [confirmedOverwrite, setConfirmedOverwrite] = useState(false); - - const fileInputRef = useRef(null); + const [fileList, setFileList] = useState([]); + const [importingModel, setImportingModel] = useState(false); const clearModal = () => { - setUploadFile(null); + setFileList([]); setPasswordFields([]); setPasswords({}); setNeedsOverwriteConfirm(false); setConfirmedOverwrite(false); - if (fileInputRef && fileInputRef.current) { - fileInputRef.current.value = ''; - } + setImportingModel(false); }; const handleErrorMsg = (msg: string) => { @@ -159,10 +159,16 @@ const ImportModelsModal: FunctionComponent = ({ useEffect(() => { setPasswordFields(passwordsNeeded); + if (passwordsNeeded.length > 0) { + setImportingModel(false); + } }, [passwordsNeeded, setPasswordFields]); useEffect(() => { setNeedsOverwriteConfirm(alreadyExists.length > 0); + if (alreadyExists.length > 0) { + setImportingModel(false); + } }, [alreadyExists, setNeedsOverwriteConfirm]); // Functions @@ -173,11 +179,16 @@ const ImportModelsModal: FunctionComponent = ({ }; const onUpload = () => { - if (uploadFile === null) { + if (!(fileList[0]?.originFileObj instanceof File)) { return; } - importResource(uploadFile, passwords, confirmedOverwrite).then(result => { + setImportingModel(true); + importResource( + fileList[0].originFileObj, + passwords, + confirmedOverwrite, + ).then(result => { if (result) { addSuccessToast(t('The import was successful')); clearModal(); @@ -186,9 +197,18 @@ const ImportModelsModal: FunctionComponent = ({ }); }; - const changeFile = (event: React.ChangeEvent) => { - const { files } = event.target as HTMLInputElement; - setUploadFile((files && files[0]) || null); + const changeFile = (info: UploadChangeParam) => { + setFileList([ + { + ...info.file, + status: 'done', + }, + ]); + }; + + const removeFile = (removedFile: UploadFile) => { + setFileList(fileList.filter(file => file.uid !== removedFile.uid)); + return false; }; const confirmOverwrite = (event: React.ChangeEvent) => { @@ -259,7 +279,9 @@ const ImportModelsModal: FunctionComponent = ({ name="model" className="import-model-modal" disablePrimaryButton={ - uploadFile === null || (needsOverwriteConfirm && !confirmedOverwrite) + fileList.length === 0 || + (needsOverwriteConfirm && !confirmedOverwrite) || + importingModel } onHandledPrimaryAction={onUpload} onHide={hide} @@ -270,21 +292,19 @@ const ImportModelsModal: FunctionComponent = ({ title={

{t('Import %s', resourceLabel)}

} > -
- -
- + onRemove={removeFile} + // upload is handled by hook + customRequest={() => {}} + > + +
{renderPasswordFields()} {renderOverwriteConfirmation()} diff --git a/superset-frontend/src/components/Select/NativeSelect.tsx b/superset-frontend/src/components/Select/NativeSelect.tsx index 604496701eb3b..2f690b1ba6d8d 100644 --- a/superset-frontend/src/components/Select/NativeSelect.tsx +++ b/superset-frontend/src/components/Select/NativeSelect.tsx @@ -20,6 +20,11 @@ import React from 'react'; import { styled } from '@superset-ui/core'; import Select, { SelectProps } from 'antd/lib/select'; +export { + OptionType as NativeSelectOptionType, + SelectProps as NativeSelectProps, +} from 'antd/lib/select'; + const StyledNativeSelect = styled((props: SelectProps) => ( ({ - marginTop: theme.gridUnit * 4, - marginBottom: theme.gridUnit * 4, - })} - {...this.selectProps} - {...subjectSelectProps} - name="filter-column" - getPopupContainer={triggerNode => triggerNode.parentNode} - > - {columns.map(column => ( - - {this.renderSubjectOptionLabel(column)} - - ))} - - - {MULTI_OPERATORS.has(operator) || this.state.suggestions.length > 0 ? ( - triggerNode.parentNode} - onSearch={val => this.setState({ currentSuggestionSearch: val })} - onSelect={this.clearSuggestionSearch} - onBlur={this.clearSuggestionSearch} - menuItemSelectedIcon={} - > - {this.state.suggestions.map(suggestion => ( - - {suggestion} - - ))} - - {/* enable selecting an option not included in suggestions */} - {currentSuggestionSearch && - !this.state.suggestions.some( - suggestion => suggestion === currentSuggestionSearch, - ) && ( - - {`${t('Create "%s"', currentSuggestionSearch)}`} - - )} - - ) : ( - this.focusComparator(ref, focusComparator)} - onChange={this.onInputComparatorChange} - value={comparator} - placeholder={t('Filter value (case sensitive)')} - disabled={DISABLE_INPUT_OPERATORS.includes(operator)} - /> - )} - - ); - } -} -AdhocFilterEditPopoverSimpleTabContent.propTypes = propTypes; -AdhocFilterEditPopoverSimpleTabContent.defaultProps = defaultProps; diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx new file mode 100644 index 0000000000000..4d08e843f910b --- /dev/null +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx @@ -0,0 +1,463 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useEffect, useState } from 'react'; +import { NativeSelect as Select } from 'src/components/Select'; +import { t, SupersetClient, styled } from '@superset-ui/core'; +import { + Operators, + OPERATORS_OPTIONS, + TABLE_ONLY_OPERATORS, + DRUID_ONLY_OPERATORS, + HAVING_OPERATORS, + MULTI_OPERATORS, + CUSTOM_OPERATORS, + DISABLE_INPUT_OPERATORS, + AGGREGATES, + OPERATOR_ENUM_TO_OPERATOR_TYPE, +} from 'src/explore/constants'; +import FilterDefinitionOption from 'src/explore/components/controls/MetricControl/FilterDefinitionOption'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from 'src/explore/components/controls/FilterControl/AdhocFilter'; +import { Input, SelectProps } from 'src/common/components'; + +const SelectWithLabel = styled(Select)` + .ant-select-selector { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } + + .ant-select-selector::after { + content: '${( + pr: SelectProps & { + labelText: string | boolean; + }, + ) => pr.labelText || '\\A0'}'; + display: inline-block; + white-space: nowrap; + color: ${({ theme }) => theme.colors.grayscale.light1}; + width: max-content; + } +`; + +export interface SimpleColumnType { + id: number; + column_name: string; + expression?: string; + type: string; + optionName?: string; + filterBy?: string; + value?: string; +} + +export interface SimpleExpressionType { + expressionType: keyof typeof EXPRESSION_TYPES; + column: SimpleColumnType; + aggregate: keyof typeof AGGREGATES; + label: string; +} +export interface SQLExpressionType { + expressionType: keyof typeof EXPRESSION_TYPES; + sqlExpression: string; + label: string; +} + +export interface MetricColumnType { + saved_metric_name: string; +} + +export type ColumnType = + | SimpleColumnType + | SimpleExpressionType + | SQLExpressionType + | MetricColumnType; + +export interface Props { + adhocFilter: AdhocFilter; + onChange: (filter: AdhocFilter) => void; + options: ColumnType[]; + datasource: { + id: string; + columns: SimpleColumnType[]; + type: string; + filter_select: boolean; + }; + partitionColumn: string; +} +export const useSimpleTabFilterProps = (props: Props) => { + const isOperatorRelevant = (operator: Operators, subject: string) => { + const column = props.datasource.columns?.find( + col => col.column_name === subject, + ); + const isColumnBoolean = + !!column && (column.type === 'BOOL' || column.type === 'BOOLEAN'); + const isColumnNumber = + !!column && (column.type === 'INT' || column.type === 'INTEGER'); + const isColumnFunction = !!column && !!column.expression; + + if (operator && CUSTOM_OPERATORS.has(operator)) { + const { partitionColumn } = props; + return partitionColumn && subject && subject === partitionColumn; + } + if (operator === Operators.IS_TRUE || operator === Operators.IS_FALSE) { + return isColumnBoolean || isColumnNumber || isColumnFunction; + } + if (isColumnBoolean) { + return ( + operator === Operators.IS_NULL || operator === Operators.IS_NOT_NULL + ); + } + return !( + (props.datasource.type === 'druid' && + TABLE_ONLY_OPERATORS.indexOf(operator) >= 0) || + (props.datasource.type === 'table' && + DRUID_ONLY_OPERATORS.indexOf(operator) >= 0) || + (props.adhocFilter.clause === CLAUSES.HAVING && + HAVING_OPERATORS.indexOf(operator) === -1) + ); + }; + const onSubjectChange = (id: string | number) => { + const option = props.options.find( + option => + ('id' in option && option.id === id) || + ('optionName' in option && option.optionName === id), + ); + + let subject = ''; + let clause; + // infer the new clause based on what subject was selected. + if (option && 'column_name' in option) { + subject = option.column_name; + clause = CLAUSES.WHERE; + } else if (option && 'saved_metric_name' in option) { + subject = option.saved_metric_name; + clause = CLAUSES.HAVING; + } else if (option && option.label) { + subject = option.label; + clause = CLAUSES.HAVING; + } + const { operator, operatorId } = props.adhocFilter; + props.onChange( + props.adhocFilter.duplicateWith({ + subject, + clause, + operator: + operator && isOperatorRelevant(operatorId, subject) + ? OPERATOR_ENUM_TO_OPERATOR_TYPE[operatorId].operation + : null, + expressionType: EXPRESSION_TYPES.SIMPLE, + operatorId, + }), + ); + }; + const onOperatorChange = (operatorId: Operators) => { + const currentComparator = props.adhocFilter.comparator; + let newComparator; + // convert between list of comparators and individual comparators + // (e.g. `in ('North America', 'Africa')` to `== 'North America'`) + if (MULTI_OPERATORS.has(operatorId)) { + newComparator = Array.isArray(currentComparator) + ? currentComparator + : [currentComparator].filter(element => element); + } else { + newComparator = Array.isArray(currentComparator) + ? currentComparator[0] + : currentComparator; + } + if (operatorId === Operators.IS_TRUE || operatorId === Operators.IS_FALSE) { + newComparator = Operators.IS_TRUE === operatorId; + } + if (operatorId && CUSTOM_OPERATORS.has(operatorId)) { + props.onChange( + props.adhocFilter.duplicateWith({ + subject: props.adhocFilter.subject, + clause: CLAUSES.WHERE, + operatorId, + operator: OPERATOR_ENUM_TO_OPERATOR_TYPE[operatorId].operation, + expressionType: EXPRESSION_TYPES.SQL, + datasource: props.datasource, + }), + ); + } else { + props.onChange( + props.adhocFilter.duplicateWith({ + operatorId, + operator: OPERATOR_ENUM_TO_OPERATOR_TYPE[operatorId].operation, + comparator: newComparator, + expressionType: EXPRESSION_TYPES.SIMPLE, + }), + ); + } + }; + const onComparatorChange = (comparator: string) => { + props.onChange( + props.adhocFilter.duplicateWith({ + comparator, + expressionType: EXPRESSION_TYPES.SIMPLE, + }), + ); + }; + return { + onSubjectChange, + onOperatorChange, + onComparatorChange, + isOperatorRelevant, + }; +}; + +const AdhocFilterEditPopoverSimpleTabContent: React.FC = props => { + const selectProps = { + name: 'select-column', + showSearch: true, + }; + const { + onSubjectChange, + onOperatorChange, + isOperatorRelevant, + onComparatorChange, + } = useSimpleTabFilterProps(props); + const [suggestions, setSuggestions] = useState>([]); + const [currentSuggestionSearch, setCurrentSuggestionSearch] = useState(''); + const [ + loadingComparatorSuggestions, + setLoadingComparatorSuggestions, + ] = useState(false); + + useEffect(() => { + const refreshComparatorSuggestions = () => { + const { datasource } = props; + const col = props.adhocFilter.subject; + const having = props.adhocFilter.clause === CLAUSES.HAVING; + + if (col && datasource && datasource.filter_select && !having) { + const controller = new AbortController(); + const { signal } = controller; + if (loadingComparatorSuggestions) { + controller.abort(); + } + setLoadingComparatorSuggestions(true); + SupersetClient.get({ + signal, + endpoint: `/superset/filter/${datasource.type}/${datasource.id}/${col}/`, + }) + .then(({ json }) => { + setSuggestions(json); + setLoadingComparatorSuggestions(false); + }) + .catch(() => { + setSuggestions([]); + setLoadingComparatorSuggestions(false); + }); + } + }; + refreshComparatorSuggestions(); + }, [props.adhocFilter.subject]); + + const onInputComparatorChange = ( + event: React.ChangeEvent, + ) => { + onComparatorChange(event.target.value); + }; + + const renderSubjectOptionLabel = (option: ColumnType) => ( + + ); + + const clearSuggestionSearch = () => { + setCurrentSuggestionSearch(''); + }; + + const getOptionsRemaining = () => { + const { comparator } = props.adhocFilter; + // if select is multi/value is array, we show the options not selected + const valuesFromSuggestionsLength = Array.isArray(comparator) + ? comparator.filter(v => suggestions.includes(v)).length + : 0; + return suggestions?.length - valuesFromSuggestionsLength ?? 0; + }; + const createSuggestionsPlaceholder = () => { + const optionsRemaining = getOptionsRemaining(); + const placeholder = t('%s option(s)', optionsRemaining); + return optionsRemaining ? placeholder : ''; + }; + + let columns = props.options; + const { subject, operator, comparator, operatorId } = props.adhocFilter; + const subjectSelectProps = { + value: subject ?? undefined, + onChange: onSubjectChange, + notFoundContent: t( + 'No such column found. To filter on a metric, try the Custom SQL tab.', + ), + autoFocus: !subject, + placeholder: '', + }; + + if (props.datasource.type === 'druid') { + subjectSelectProps.placeholder = t( + '%s column(s) and metric(s)', + columns.length, + ); + } else { + // we cannot support simple ad-hoc filters for metrics because we don't know what type + // the value should be cast to (without knowing the output type of the aggregate, which + // becomes a rather complicated problem) + subjectSelectProps.placeholder = + props.adhocFilter.clause === CLAUSES.WHERE + ? t('%s column(s)', columns.length) + : t('To filter on a metric, use Custom SQL tab.'); + columns = props.options.filter( + option => 'column_name' in option && option.column_name, + ); + } + + const operatorSelectProps = { + placeholder: t( + '%s operator(s)', + OPERATORS_OPTIONS.filter(op => isOperatorRelevant(op, subject)).length, + ), + value: OPERATOR_ENUM_TO_OPERATOR_TYPE[operatorId]?.display, + onChange: onOperatorChange, + autoFocus: !!subjectSelectProps.value && !operator, + name: 'select-operator', + }; + + const shouldFocusComparator = + !!subjectSelectProps.value && !!operatorSelectProps.value; + + const comparatorSelectProps: SelectProps & { + labelText: string | boolean; + } = { + allowClear: true, + showSearch: true, + mode: MULTI_OPERATORS.has(operatorId) ? 'tags' : undefined, + tokenSeparators: [',', '\n', '\t', ';'], + loading: loadingComparatorSuggestions, + value: comparator, + onChange: onComparatorChange, + notFoundContent: t('Type a value here'), + disabled: DISABLE_INPUT_OPERATORS.includes(operatorId), + placeholder: createSuggestionsPlaceholder(), + labelText: + comparator && comparator.length > 0 && createSuggestionsPlaceholder(), + autoFocus: shouldFocusComparator, + }; + + return ( + <> + + + {MULTI_OPERATORS.has(operatorId) || suggestions.length > 0 ? ( + triggerNode.parentNode} + onSearch={val => setCurrentSuggestionSearch(val)} + onSelect={clearSuggestionSearch} + onBlur={clearSuggestionSearch} + > + {suggestions.map((suggestion: string) => ( + + {suggestion} + + ))} + + {/* enable selecting an option not included in suggestions */} + {currentSuggestionSearch && + !suggestions.some( + (suggestion: string) => suggestion === currentSuggestionSearch, + ) && ( + + {currentSuggestionSearch} + + )} + + ) : ( + { + if (ref && shouldFocusComparator) { + ref.blur(); + } + }} + onChange={onInputComparatorChange} + value={comparator} + placeholder={t('Filter value (case sensitive)')} + disabled={DISABLE_INPUT_OPERATORS.includes(operatorId)} + /> + )} + + ); +}; + +export default AdhocFilterEditPopoverSimpleTabContent; diff --git a/superset-frontend/src/explore/components/controls/FilterControl/adhocFilterType.js b/superset-frontend/src/explore/components/controls/FilterControl/adhocFilterType.js index f036fc0fca7a6..c9eef05926fc9 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/adhocFilterType.js +++ b/superset-frontend/src/explore/components/controls/FilterControl/adhocFilterType.js @@ -17,8 +17,6 @@ * under the License. */ import PropTypes from 'prop-types'; - -import { OPERATORS } from 'src/explore/constants'; import { EXPRESSION_TYPES, CLAUSES } from './AdhocFilter'; export default PropTypes.oneOfType([ @@ -26,7 +24,6 @@ export default PropTypes.oneOfType([ expressionType: PropTypes.oneOf([EXPRESSION_TYPES.SIMPLE]).isRequired, clause: PropTypes.oneOf([CLAUSES.HAVING, CLAUSES.WHERE]).isRequired, subject: PropTypes.string.isRequired, - operator: PropTypes.oneOf(Object.keys(OPERATORS)).isRequired, comparator: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf(PropTypes.string), diff --git a/superset-frontend/src/explore/components/controls/MetricControl/FilterDefinitionOption.jsx b/superset-frontend/src/explore/components/controls/MetricControl/FilterDefinitionOption.jsx index 0491090ed90af..7063c4c69452a 100644 --- a/superset-frontend/src/explore/components/controls/MetricControl/FilterDefinitionOption.jsx +++ b/superset-frontend/src/explore/components/controls/MetricControl/FilterDefinitionOption.jsx @@ -51,5 +51,6 @@ export default function FilterDefinitionOption({ option }) { /> ); } + return null; } FilterDefinitionOption.propTypes = propTypes; diff --git a/superset-frontend/src/explore/constants.ts b/superset-frontend/src/explore/constants.ts index b3af092eb9fe0..c3b31f1de568a 100644 --- a/superset-frontend/src/explore/constants.ts +++ b/superset-frontend/src/explore/constants.ts @@ -28,49 +28,78 @@ export const AGGREGATES = { }; export const AGGREGATES_OPTIONS = Object.values(AGGREGATES); -export const OPERATORS = { - '==': '==', - '!=': '!=', - '>': '>', - '<': '<', - '>=': '>=', - '<=': '<=', - IN: 'IN', - 'NOT IN': 'NOT IN', - ILIKE: 'ILIKE', - LIKE: 'LIKE', - REGEX: 'REGEX', - 'IS NOT NULL': 'IS NOT NULL', - 'IS NULL': 'IS NULL', - 'LATEST PARTITION': 'LATEST PARTITION', - 'IS TRUE': 'IS TRUE', - 'IS FALSE': 'IS FALSE', +export enum Operators { + EQUALS = 'EQUALS', + NOT_EQUALS = 'NOT_EQUALS', + LESS_THAN = 'LESS_THAN', + GREATER_THAN = 'GREATER_THAN', + LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL', + GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL', + IN = 'IN', + NOT_IN = 'NOT_IN', + ILIKE = 'ILIKE', + LIKE = 'LIKE', + REGEX = 'REGEX', + IS_NOT_NULL = 'IS_NOT_NULL', + IS_NULL = 'IS_NULL', + LATEST_PARTITION = 'LATEST_PARTITION', + IS_TRUE = 'IS_TRUE', + IS_FALSE = 'IS_FALSE', +} + +export interface OperatorType { + display: string; + operation: string; +} + +export const OPERATOR_ENUM_TO_OPERATOR_TYPE: { + [key in Operators]: OperatorType; +} = { + [Operators.EQUALS]: { display: 'equals', operation: '==' }, + [Operators.NOT_EQUALS]: { display: 'not equals', operation: '!=' }, + [Operators.GREATER_THAN]: { display: '>', operation: '>' }, + [Operators.LESS_THAN]: { display: '<', operation: '<' }, + [Operators.GREATER_THAN_OR_EQUAL]: { display: '>=', operation: '>=' }, + [Operators.LESS_THAN_OR_EQUAL]: { display: '<=', operation: '<=' }, + [Operators.IN]: { display: 'IN', operation: 'IN' }, + [Operators.NOT_IN]: { display: 'NOT IN', operation: 'NOT IN' }, + [Operators.LIKE]: { display: 'LIKE', operation: 'LIKE' }, + [Operators.ILIKE]: { display: 'LIKE (case insensitive)', operation: 'ILIKE' }, + [Operators.REGEX]: { display: 'REGEX', operation: 'REGEX' }, + [Operators.IS_NOT_NULL]: { display: 'IS NOT NULL', operation: 'IS NOT NULL' }, + [Operators.IS_NULL]: { display: 'IS NULL', operation: 'IS NULL' }, + [Operators.LATEST_PARTITION]: { + display: 'use latest_partition template', + operation: 'LATEST PARTITION', + }, + [Operators.IS_TRUE]: { display: 'IS TRUE', operation: '==' }, + [Operators.IS_FALSE]: { display: 'IS FALSE', operation: '==' }, }; -export const OPERATORS_OPTIONS = Object.values(OPERATORS); +export const OPERATORS_OPTIONS = Object.values(Operators) as Operators[]; -export const TABLE_ONLY_OPERATORS = [OPERATORS.LIKE, OPERATORS.ILIKE]; -export const DRUID_ONLY_OPERATORS = [OPERATORS.REGEX]; +export const TABLE_ONLY_OPERATORS = [Operators.LIKE, Operators.ILIKE]; +export const DRUID_ONLY_OPERATORS = [Operators.REGEX]; export const HAVING_OPERATORS = [ - OPERATORS['=='], - OPERATORS['!='], - OPERATORS['>'], - OPERATORS['<'], - OPERATORS['>='], - OPERATORS['<='], + Operators.EQUALS, + Operators.NOT_EQUALS, + Operators.GREATER_THAN, + Operators.LESS_THAN, + Operators.GREATER_THAN_OR_EQUAL, + Operators.LESS_THAN_OR_EQUAL, ]; -export const MULTI_OPERATORS = new Set([OPERATORS.IN, OPERATORS['NOT IN']]); +export const MULTI_OPERATORS = new Set([Operators.IN, Operators.NOT_IN]); // CUSTOM_OPERATORS will show operator in simple mode, // but will generate customized sqlExpression -export const CUSTOM_OPERATORS = new Set([OPERATORS['LATEST PARTITION']]); +export const CUSTOM_OPERATORS = new Set([Operators.LATEST_PARTITION]); // DISABLE_INPUT_OPERATORS will disable filter value input // in adhocFilter control export const DISABLE_INPUT_OPERATORS = [ - OPERATORS['IS NOT NULL'], - OPERATORS['IS NULL'], - OPERATORS['LATEST PARTITION'], - OPERATORS['IS TRUE'], - OPERATORS['IS FALSE'], + Operators.IS_NOT_NULL, + Operators.IS_NULL, + Operators.LATEST_PARTITION, + Operators.IS_TRUE, + Operators.IS_FALSE, ]; export const sqlaAutoGeneratedMetricNameRegex = /^(sum|min|max|avg|count|count_distinct)__.*$/i; @@ -107,3 +136,4 @@ export const TIME_FILTER_MAP = { // TODO: make this configurable per Superset installation export const DEFAULT_TIME_RANGE = 'No filter'; +export const NO_TIME_RANGE = 'No filter'; diff --git a/superset-frontend/src/explore/exploreUtils/index.js b/superset-frontend/src/explore/exploreUtils/index.js index c936590db095f..d5dca456cb2dd 100644 --- a/superset-frontend/src/explore/exploreUtils/index.js +++ b/superset-frontend/src/explore/exploreUtils/index.js @@ -29,7 +29,10 @@ import { import { availableDomains } from 'src/utils/hostNamesConfig'; import { safeStringify } from 'src/utils/safeStringify'; import { URL_PARAMS } from 'src/constants'; -import { MULTI_OPERATORS } from 'src/explore/constants'; +import { + MULTI_OPERATORS, + OPERATOR_ENUM_TO_OPERATOR_TYPE, +} from 'src/explore/constants'; import { DashboardStandaloneMode } from 'src/dashboard/util/constants'; const MAX_URL_LENGTH = 8000; @@ -319,7 +322,10 @@ export const useDebouncedEffect = (effect, delay, deps) => { }; export const getSimpleSQLExpression = (subject, operator, comparator) => { - const isMulti = MULTI_OPERATORS.has(operator); + const isMulti = + [...MULTI_OPERATORS] + .map(op => OPERATOR_ENUM_TO_OPERATOR_TYPE[op].operation) + .indexOf(operator) >= 0; let expression = subject ?? ''; if (subject && operator) { expression += ` ${operator}`; diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx index e2ed21c90bc9b..549217619b78a 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx @@ -150,7 +150,7 @@ describe('SelectFilterPlugin', () => { ], }, filterState: { - label: '', + label: undefined, value: null, }, }); @@ -165,7 +165,7 @@ describe('SelectFilterPlugin', () => { }, extraFormData: {}, filterState: { - label: '', + label: undefined, value: null, }, }); diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 305667c2947b5..3051e3fffc8f1 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -138,7 +138,9 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { inverseSelection, ), filterState: { - label: `${(values || []).join(', ')}${suffix}`, + label: values?.length + ? `${(values || []).join(', ')}${suffix}` + : undefined, value: appSection === AppSection.FILTER_CONFIG_MODAL && defaultToFirstItem ? undefined diff --git a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx index 9f6f42df1ee9c..73d04064ff996 100644 --- a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx @@ -17,15 +17,14 @@ * under the License. */ import { styled } from '@superset-ui/core'; -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; import DateFilterControl from 'src/explore/components/controls/DateFilterControl'; import { PluginFilterTimeProps } from './types'; import { Styles } from '../common'; - -const DEFAULT_VALUE = 'Last week'; +import { NO_TIME_RANGE } from '../../../explore/constants'; const TimeFilterStyles = styled(Styles)` - overflow-x: scroll; + overflow-x: auto; `; const ControlContainer = styled.div` @@ -34,36 +33,31 @@ const ControlContainer = styled.div` export default function TimeFilterPlugin(props: PluginFilterTimeProps) { const { - formData, setDataMask, setFocusedFilter, unsetFocusedFilter, width, filterState, } = props; - const { defaultValue } = formData; - - const [value, setValue] = useState(defaultValue ?? DEFAULT_VALUE); - - const handleTimeRangeChange = (timeRange: string): void => { - setValue(timeRange); + const handleTimeRangeChange = (timeRange?: string): void => { + const isSet = timeRange && timeRange !== NO_TIME_RANGE; setDataMask({ - extraFormData: { - time_range: timeRange, + extraFormData: isSet + ? { + time_range: timeRange, + } + : {}, + filterState: { + value: isSet ? timeRange : undefined, }, - filterState: { value: timeRange }, }); }; useEffect(() => { - handleTimeRangeChange(filterState.value ?? DEFAULT_VALUE); + handleTimeRangeChange(filterState.value); }, [filterState.value]); - useEffect(() => { - handleTimeRangeChange(defaultValue ?? DEFAULT_VALUE); - }, [defaultValue]); - return ( // @ts-ignore @@ -72,7 +66,7 @@ export default function TimeFilterPlugin(props: PluginFilterTimeProps) { onMouseLeave={unsetFocusedFilter} > diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx index 7b1da36214720..3f3ab8d39714b 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx @@ -53,20 +53,12 @@ import ImportModelsModal from 'src/components/ImportModal/index'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; import AddDatasetModal from './AddDatasetModal'; - -const PAGE_SIZE = 25; -const PASSWORDS_NEEDED_MESSAGE = t( - 'The passwords for the databases below are needed in order to ' + - 'import them together with the datasets. Please note that the ' + - '"Secure Extra" and "Certificate" sections of ' + - 'the database configuration are not present in export files, and ' + - 'should be added manually after the import if they are needed.', -); -const CONFIRM_OVERWRITE_MESSAGE = t( - 'You are importing one or more datasets that already exist. ' + - 'Overwriting might cause you to lose some of your work. Are you ' + - 'sure you want to overwrite?', -); +import { + PAGE_SIZE, + SORT_BY, + PASSWORDS_NEEDED_MESSAGE, + CONFIRM_OVERWRITE_MESSAGE, +} from './constants'; const FlexRowContainer = styled.div` align-items: center; @@ -158,7 +150,7 @@ const DatasetList: FunctionComponent = ({ const canCreate = hasPerm('can_write'); const canExport = hasPerm('can_read'); - const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }]; + const initialSort = SORT_BY; const openDatasetEditModal = useCallback( ({ id }: Dataset) => { diff --git a/superset-frontend/src/views/CRUD/data/dataset/constants.ts b/superset-frontend/src/views/CRUD/data/dataset/constants.ts new file mode 100644 index 0000000000000..cb0b5d3c0adb7 --- /dev/null +++ b/superset-frontend/src/views/CRUD/data/dataset/constants.ts @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { t } from '@superset-ui/core'; + +export const PAGE_SIZE = 25; +export const SORT_BY = [{ id: 'changed_on_delta_humanized', desc: true }]; +export const PASSWORDS_NEEDED_MESSAGE = t( + 'The passwords for the databases below are needed in order to ' + + 'import them together with the datasets. Please note that the ' + + '"Secure Extra" and "Certificate" sections of ' + + 'the database configuration are not present in export files, and ' + + 'should be added manually after the import if they are needed.', +); +export const CONFIRM_OVERWRITE_MESSAGE = t( + 'You are importing one or more datasets that already exist. ' + + 'Overwriting might cause you to lose some of your work. Are you ' + + 'sure you want to overwrite?', +); diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.test.jsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.test.jsx index ac6f8c293e598..38ecb4aadc065 100644 --- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.test.jsx +++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.test.jsx @@ -330,7 +330,7 @@ describe('RTL', () => { userEvent.click(importButton); // Grab "Choose File" input from import modal - const chooseFileInput = screen.getByLabelText(/file\*/i); + const chooseFileInput = screen.getByTestId('model-file-input'); // Upload mocked import file userEvent.upload(chooseFileInput, mockImportFile); diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts index 6e6a1dddd1591..64bec4770df2a 100644 --- a/superset-frontend/src/views/CRUD/hooks.ts +++ b/superset-frontend/src/views/CRUD/hooks.ts @@ -20,7 +20,12 @@ import rison from 'rison'; import { useState, useEffect, useCallback } from 'react'; import { makeApi, SupersetClient, t, JsonObject } from '@superset-ui/core'; -import { createErrorHandler } from 'src/views/CRUD/utils'; +import { + createErrorHandler, + getAlreadyExists, + getPasswordsNeeded, + hasTerminalValidation, +} from 'src/views/CRUD/utils'; import { FetchDataConfig } from 'src/components/ListView'; import { FilterValue } from 'src/components/ListView/types'; import Chart, { Slice } from 'src/types/Chart'; @@ -385,40 +390,6 @@ export function useImportResource( setState(currentState => ({ ...currentState, ...update })); } - /* eslint-disable no-underscore-dangle */ - const isNeedsPassword = (payload: any) => - typeof payload === 'object' && - Array.isArray(payload._schema) && - payload._schema.length === 1 && - payload._schema[0] === 'Must provide a password for the database'; - - const isAlreadyExists = (payload: any) => - typeof payload === 'string' && - payload.includes('already exists and `overwrite=true` was not passed'); - - const getPasswordsNeeded = ( - errMsg: Record>, - ) => - Object.entries(errMsg) - .filter(([, validationErrors]) => isNeedsPassword(validationErrors)) - .map(([fileName]) => fileName); - - const getAlreadyExists = ( - errMsg: Record>, - ) => - Object.entries(errMsg) - .filter(([, validationErrors]) => isAlreadyExists(validationErrors)) - .map(([fileName]) => fileName); - - const hasTerminalValidation = ( - errMsg: Record>, - ) => - Object.values(errMsg).some( - validationErrors => - !isNeedsPassword(validationErrors) && - !isAlreadyExists(validationErrors), - ); - const importResource = useCallback( ( bundle: File, @@ -453,29 +424,28 @@ export function useImportResource( .then(() => true) .catch(response => getClientErrorObject(response).then(error => { - const errMsg = error.message || error.error; - if (typeof errMsg === 'string') { + if (!error.errors) { handleErrorMsg( t( 'An error occurred while importing %s: %s', resourceLabel, - parsedErrorMessage(errMsg), + error.message || error.error, ), ); return false; } - if (hasTerminalValidation(errMsg)) { + if (hasTerminalValidation(error.errors)) { handleErrorMsg( t( 'An error occurred while importing %s: %s', resourceLabel, - parsedErrorMessage(errMsg), + error.errors.map(payload => payload.message).join('\n'), ), ); } else { updateState({ - passwordsNeeded: getPasswordsNeeded(errMsg), - alreadyExists: getAlreadyExists(errMsg), + passwordsNeeded: getPasswordsNeeded(error.errors), + alreadyExists: getAlreadyExists(error.errors), }); } return false; diff --git a/superset-frontend/src/views/CRUD/utils.test.tsx b/superset-frontend/src/views/CRUD/utils.test.tsx new file mode 100644 index 0000000000000..24cb0ce97fc23 --- /dev/null +++ b/superset-frontend/src/views/CRUD/utils.test.tsx @@ -0,0 +1,145 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + isNeedsPassword, + isAlreadyExists, + getPasswordsNeeded, + getAlreadyExists, + hasTerminalValidation, +} from 'src/views/CRUD/utils'; + +const terminalErrors = { + errors: [ + { + message: 'Error importing database', + error_type: 'GENERIC_COMMAND_ERROR', + level: 'warning', + extra: { + 'metadata.yaml': { type: ['Must be equal to Database.'] }, + issue_codes: [ + { + code: 1010, + message: + 'Issue 1010 - Superset encountered an error while running a command.', + }, + ], + }, + }, + ], +}; + +const overwriteNeededErrors = { + errors: [ + { + message: 'Error importing database', + error_type: 'GENERIC_COMMAND_ERROR', + level: 'warning', + extra: { + 'databases/imported_database.yaml': + 'Database already exists and `overwrite=true` was not passed', + issue_codes: [ + { + code: 1010, + message: + 'Issue 1010 - Superset encountered an error while running a command.', + }, + ], + }, + }, + ], +}; + +const passwordNeededErrors = { + errors: [ + { + message: 'Error importing database', + error_type: 'GENERIC_COMMAND_ERROR', + level: 'warning', + extra: { + 'databases/imported_database.yaml': { + _schema: ['Must provide a password for the database'], + }, + issue_codes: [ + { + code: 1010, + message: + 'Issue 1010 - Superset encountered an error while running a command.', + }, + ], + }, + }, + ], +}; + +test('identifies error payloads indicating that password is needed', () => { + let needsPassword; + + needsPassword = isNeedsPassword({ + _schema: ['Must provide a password for the database'], + }); + expect(needsPassword).toBe(true); + + needsPassword = isNeedsPassword( + 'Database already exists and `overwrite=true` was not passed', + ); + expect(needsPassword).toBe(false); + + needsPassword = isNeedsPassword({ type: ['Must be equal to Database.'] }); + expect(needsPassword).toBe(false); +}); + +test('identifies error payloads indicating that overwrite confirmation is needed', () => { + let alreadyExists; + + alreadyExists = isAlreadyExists( + 'Database already exists and `overwrite=true` was not passed', + ); + expect(alreadyExists).toBe(true); + + alreadyExists = isAlreadyExists({ + _schema: ['Must provide a password for the database'], + }); + expect(alreadyExists).toBe(false); + + alreadyExists = isAlreadyExists({ type: ['Must be equal to Database.'] }); + expect(alreadyExists).toBe(false); +}); + +test('extracts DB configuration files that need passwords', () => { + const passwordsNeeded = getPasswordsNeeded(passwordNeededErrors.errors); + expect(passwordsNeeded).toEqual(['databases/imported_database.yaml']); +}); + +test('extracts files that need overwrite confirmation', () => { + const alreadyExists = getAlreadyExists(overwriteNeededErrors.errors); + expect(alreadyExists).toEqual(['databases/imported_database.yaml']); +}); + +test('detects if the error message is terminal or if it requires uses intervention', () => { + let isTerminal; + + isTerminal = hasTerminalValidation(terminalErrors.errors); + expect(isTerminal).toBe(true); + + isTerminal = hasTerminalValidation(overwriteNeededErrors.errors); + expect(isTerminal).toBe(false); + + isTerminal = hasTerminalValidation(passwordNeededErrors.errors); + expect(isTerminal).toBe(false); +}); diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx index 716282f7806a1..7e575c425aa8b 100644 --- a/superset-frontend/src/views/CRUD/utils.tsx +++ b/superset-frontend/src/views/CRUD/utils.tsx @@ -322,3 +322,40 @@ export const CardStyles = styled.div` text-decoration: none; } `; + +export /* eslint-disable no-underscore-dangle */ +const isNeedsPassword = (payload: any) => + typeof payload === 'object' && + Array.isArray(payload._schema) && + payload._schema.length === 1 && + payload._schema[0] === 'Must provide a password for the database'; + +export const isAlreadyExists = (payload: any) => + typeof payload === 'string' && + payload.includes('already exists and `overwrite=true` was not passed'); + +export const getPasswordsNeeded = (errors: Record[]) => + errors + .map(error => + Object.entries(error.extra) + .filter(([, payload]) => isNeedsPassword(payload)) + .map(([fileName]) => fileName), + ) + .flat(); + +export const getAlreadyExists = (errors: Record[]) => + errors + .map(error => + Object.entries(error.extra) + .filter(([, payload]) => isAlreadyExists(payload)) + .map(([fileName]) => fileName), + ) + .flat(); + +export const hasTerminalValidation = (errors: Record[]) => + errors.some( + error => + !Object.values(error.extra).some( + payload => isNeedsPassword(payload) || isAlreadyExists(payload), + ), + ); diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js index b5460f5a9f5b4..9df9943a78d42 100644 --- a/superset-frontend/webpack.config.js +++ b/superset-frontend/webpack.config.js @@ -59,11 +59,11 @@ if (isDevMode) { output.filename = '[name].[hash:8].entry.js'; output.chunkFilename = '[name].[hash:8].chunk.js'; } else if (nameChunks) { - output.filename = '[name].[chunkhash].entry.js'; - output.chunkFilename = '[name].[chunkhash].chunk.js'; + output.filename = '[name].[contenthash].entry.js'; + output.chunkFilename = '[name].[contenthash].chunk.js'; } else { - output.filename = '[name].[chunkhash].entry.js'; - output.chunkFilename = '[chunkhash].chunk.js'; + output.filename = '[name].[contenthash].entry.js'; + output.chunkFilename = '[contenthash].chunk.js'; } const plugins = [ @@ -155,8 +155,8 @@ if (!isDevMode) { // text loading (webpack 4+) plugins.push( new MiniCssExtractPlugin({ - filename: '[name].[chunkhash].entry.css', - chunkFilename: '[name].[chunkhash].chunk.css', + filename: '[name].[contenthash].entry.css', + chunkFilename: '[name].[contenthash].chunk.css', }), ); plugins.push(new OptimizeCSSAssetsPlugin()); diff --git a/superset/utils/core.py b/superset/utils/core.py index 5ece0aeef6d67..c8c352d368287 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -218,7 +218,7 @@ class FilterOperator(str, Enum): class PostProcessingBoxplotWhiskerType(str, Enum): """ - Calculate cell contibution to row/column total + Calculate cell contribution to row/column total """ TUKEY = "tukey" @@ -228,7 +228,7 @@ class PostProcessingBoxplotWhiskerType(str, Enum): class PostProcessingContributionOrientation(str, Enum): """ - Calculate cell contibution to row/column total + Calculate cell contribution to row/column total """ ROW = "row" @@ -263,6 +263,7 @@ class QueryStatus(str, Enum): # pylint: disable=too-few-public-methods RUNNING: str = "running" SCHEDULED: str = "scheduled" SUCCESS: str = "success" + FETCHING: str = "fetching" TIMED_OUT: str = "timed_out" diff --git a/tox.ini b/tox.ini index 970930b27880d..39bc8e534fc86 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ # # Remember to start celery workers to run celery tests, e.g. -# celery worker --app=superset.tasks.celery_app:app -Ofair -c 2 +# celery --app=superset.tasks.celery_app:app worker -Ofair -c 2 [testenv] basepython = python3.8 ignore_basepython_conflict = true