Compare commits

...

116 Commits

Author SHA1 Message Date
AAfghahi
5b12703b98 changelog 2022-12-09 15:48:28 -05:00
Michael S. Molina
0f5216b7c6 feat: Improves SafeMarkdown HTML sanitization 2022-12-09 14:00:45 -05:00
AAfghahi
d319b2b56f pylint 2022-12-07 11:37:35 -05:00
AAfghahi
8237558d62 changelog 2022-11-22 17:13:17 -05:00
Eric Briscoe
c2f6256e90 fix(CustomFrame): Resolves issue #21731 where date range in explore throws runtime error (#21776) 2022-11-21 17:49:48 -05:00
AAfghahi
5667078d89 changelog 2022-11-18 16:25:58 -05:00
Mayur
095a22dd5d fix: respect chart cache timeout setting (#21637) 2022-11-18 16:25:58 -05:00
Mayur
11a40a417e fix: allow adhoc columns in non-aggregate query (#21729) 2022-11-18 16:25:58 -05:00
Ville Brofeldt
cdf03dfbcb chore(sqla): refactor query utils (#21811)
Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
2022-11-18 16:25:58 -05:00
AAfghahi
9321bc6ba2 add fixture 2022-11-18 16:25:58 -05:00
Mayur
44f19a5c99 pylint 2022-11-18 16:25:58 -05:00
AAfghahi
1c77f3f8f0 added fixtures 2022-11-18 16:25:58 -05:00
Ville Brofeldt
4327fbc794 fix(cache): respect default cache timeout on v1 chart data requests (#21441) 2022-11-18 16:25:58 -05:00
AAfghahi
149ad56aa8 add physical dataset 2022-11-18 16:25:58 -05:00
AAfghahi
e682bae27f integration test 2022-11-18 16:25:58 -05:00
Daniel Vaz Gaspar
80a306eaad fix: datasource save, improve data validation (#22038) 2022-11-18 16:25:58 -05:00
Daniel Vaz Gaspar
176995eac9 fix: deprecate approve and request_access endpoint (#22022)
Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
2022-11-18 16:25:58 -05:00
AAfghahi
dff5be1a58 added test fix 2022-11-18 16:25:58 -05:00
Elizabeth Thompson
52cc90d741 fix tests 2022-11-18 16:25:57 -05:00
Daniel Vaz Gaspar
bf40df2f81 fix: dashboard api cache decorator (#21964) 2022-11-18 16:25:57 -05:00
Beto Dealmeida
a8a92d7a69 fix: check that imports are ZIPs (#21875) 2022-11-18 16:25:57 -05:00
Ville Brofeldt
d779789652 chore(sqla): refactor query utils (#21811)
Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
2022-11-18 16:25:57 -05:00
Michael S. Molina
aa11e5486b feat: Adds a Content Security Policy (CSP) check for production environments (#21874)
(cherry picked from commit f4da74ce8d)
2022-11-18 16:25:57 -05:00
Michael S. Molina
68a389642c feat: Disables HTML rendering in Toast by default (#21853)
(cherry picked from commit 47b1e0ca9d)
2022-11-18 16:25:57 -05:00
Daniel Vaz Gaspar
b238d04f29 fix: flash message on database data upload forms (#21761)
(cherry picked from commit ba3275a4d0)
2022-11-18 16:25:57 -05:00
Daniel Vaz Gaspar
6e325ac73e fix: database schema selector on import data (#21759)
(cherry picked from commit 91f0de0c5d)
2022-11-18 16:25:57 -05:00
Elizabeth Thompson
cceb9f1794 update changelog 2022-11-18 16:25:57 -05:00
dependabot[bot]
c13fbd4f5d chore(deps): bump moment from 2.29.2 to 2.29.4 in /superset-frontend (#20644)
Bumps [moment](https://github.com/moment/moment) from 2.29.2 to 2.29.4.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.29.2...2.29.4)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 16:25:57 -05:00
Elizabeth Thompson
a3694f6ea1 fix test 2022-11-18 16:25:57 -05:00
Elizabeth Thompson
5b686bc6de update changelog 2022-11-18 16:25:57 -05:00
Elizabeth Thompson
9f33519312 xit cypress test 2022-11-18 16:25:57 -05:00
Mayur
b43c047887 fix: allow adhoc columns in non-aggregate query (#21729) 2022-11-18 16:25:57 -05:00
Mayur
930148dec6 fix(dashboard): show correct roles for dashboard access dropdown (#21549) 2022-10-18 14:00:56 -07:00
Elizabeth Thompson
f7e664bd68 fix: remove deprecated ETagResponseMixin (#21773)
(cherry picked from commit 75e6a04269)
2022-10-18 13:55:45 -07:00
Rui Zhao
b1c9adb52b fix(report): Fix permission check for set up email report on charts/dashboards. Fixes #21559 (#21561)
Co-authored-by: Rui Zhao <zhaorui@dropbox.com>
(cherry picked from commit 7f971b4103)
2022-10-18 13:55:41 -07:00
Yongjie Zhao
9e907be7a8 fix: annotation broken (#20651)
* fix: annotation broken

* fix UT

* add annotation data to mixed timeseries chart

(cherry picked from commit 7f918a4ec0)
2022-10-18 13:55:37 -07:00
Elizabeth Thompson
61b64492a2 bump package-lock version 2022-10-06 15:45:55 -07:00
AAfghahi
2c65ef967e build: changelog for 2.0.1 (#21721) 2022-10-06 14:32:46 -07:00
AAfghahi
ad52469a04 test fix 2022-10-06 14:45:06 -04:00
AAfghahi
548311c424 linting issues 2022-10-06 14:45:06 -04:00
Hugh A. Miles II
40c5b2a688 remove eleement reference (#20830)
(cherry picked from commit 2263a76f4d)
2022-10-06 14:45:06 -04:00
aehanno
511cafa790 fix: Add locale for DatePicker component (#20063)
Co-authored-by: Kevin Dethelot <kevin.dethelot@kosmos.fr>
Co-authored-by: Yongjie Zhao <yongjie.zhao@gmail.com>
2022-10-06 14:45:06 -04:00
Daniel Vaz Gaspar
dd919bc176 fix: disallow users from viewing other user's profile on config (#21302) 2022-10-06 14:45:06 -04:00
Cody Leff
143c5f1ecc fix(explore): Prevent unnecessary series limit subquery (#21154)
* Prevent series limit when no series limit columns specified.

* Add timeseries check for legacy charts.

* Apply fix to helpers.py.

* Skip Cypress color consistency tests.
2022-10-06 14:45:06 -04:00
Mayur
dfcb66a098 fix: set correct favicon from config for login and FAB list views (#21498)
(cherry picked from commit b29e7e7d9e)
2022-10-06 14:45:06 -04:00
MichaelHintz
440ab64c53 fix(sqllab): Fix cursor alignment in SQL lab editor by avoiding Lucida Console font on Windows (#21380)
(cherry picked from commit 3098e657e5)
2022-10-06 14:45:06 -04:00
aehanno
5b662f7874 fix: Add french translation missing (#20061)
(cherry picked from commit 944808a0ce)
2022-10-06 14:45:06 -04:00
JUST.in DO IT
08052a7db3 fix(plugin-chart-echarts): missing value format in mixed timeseries (#21044) 2022-10-06 14:45:06 -04:00
Daniel Vaz Gaspar
2583f7fde3 fix: cached common bootstrap Revert (#21018) (#21419)
(cherry picked from commit 094400c308)
2022-10-06 14:45:05 -04:00
Ville Brofeldt
db661ec17f fix(plugin-chart-echarts): show zero value in tooltip (#21296)
Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
(cherry picked from commit 1aeb8fd6b7)
2022-10-06 14:45:05 -04:00
Kamil Gabryjelski
7f8d6b3400 fix(explore): Time column label not formatted when GENERIC_X_AXES enabled (#21294)
(cherry picked from commit c3a00d43d0)
2022-10-06 14:45:05 -04:00
Daniel Vaz Gaspar
2b760d0775 feat: adds TLS certificate validation option for SMTP (#21272)
(cherry picked from commit 9fd752057e)
2022-09-14 11:38:28 -07:00
ʈᵃᵢ
4298690e16 fix(celery cache warmup): add auth and use warm_up_cache endpoint (#21076)
(cherry picked from commit 04dd8d414d)
2022-09-14 11:38:27 -07:00
Stephen Liu
32736680da fix(database-list): hidden upload file button if no permission (#21216)
(cherry picked from commit 0c43190e04)
2022-09-14 11:38:27 -07:00
JUST.in DO IT
9337dec038 fix(sqllab): missing zero values while copy-to-clipboard (#21153)
(cherry picked from commit 4e23d62d4f)
2022-09-14 11:38:26 -07:00
stevetracvc
05d7c3d74d fix(native filters): groupby filter issue (#21084)
(cherry picked from commit d79b0bfc74)
2022-09-14 11:38:25 -07:00
Stephen Liu
c3e04d7ebf fix(plugin-chart-handlebars): order by control not work (#21005)
(cherry picked from commit e70699fb43)
2022-09-14 11:38:25 -07:00
EugeneTorap
47c3cd10bd fix(dashboard): Fix scroll behaviour in DashboardBuilderSidepane (#20969)
(cherry picked from commit 6f3a555e58)
2022-09-14 11:38:24 -07:00
Bogdan
b4df82591e Memoize the common_bootstrap_payload (#21018)
Try patch

Co-authored-by: Bogdan Kyryliuk <bogdankyryliuk@dropbox.com>
(cherry picked from commit 495a205dec)
2022-09-14 11:38:24 -07:00
Stephen Liu
884e2f1ca7 fix(plugin-chart-echarts): gauge chart enhancements and fixes (#21007)
* fix(plugin-chart-echarts): gauge chart enhancements and fixes

* fix lint

(cherry picked from commit b303d1e156)
2022-09-14 11:38:24 -07:00
Erik Cederstrand
e91222eb65 chore(deps): unpin holidays dependency version (#21091)
The blocking issue has been fixed upstream

Co-authored-by: Erik Cederstrand <erik@adamatics.com>
(cherry picked from commit d817a1dc87)
2022-09-14 11:38:23 -07:00
JUST.in DO IT
2db82d578c fix(plugin-chart-echarts): invalid total label location for negative values in stacked bar chart (#21032)
(cherry picked from commit a8ba544e60)
2022-09-14 11:38:23 -07:00
Kamil Gabryjelski
346c035690 fix: Explore scrolled down when navigating from dashboard (#20962)
(cherry picked from commit e4fc5564ce)
2022-09-14 11:38:22 -07:00
Antonio Rivero Martinez
6a5b12ec8c Big Number Viz: (#20946)
- When the value is zero we still render the percent change and suffix if present

(cherry picked from commit aa53c10312)
2022-09-14 11:38:22 -07:00
Diego Medina
8c2ca2d8d8 Temporal X Axis values are not properly displayed if the time column has a custom label defined (#20819)
(cherry picked from commit 51869f32ac)
2022-09-14 11:38:21 -07:00
Yongjie Zhao
43b8f18a21 fix: getting default value in run-server.sh (#20736)
(cherry picked from commit 5990ea639e)
2022-09-14 11:38:21 -07:00
Multazim Deshmukh
5efee17def fix: make max-requests and max-requests-jitter adjustable (#20733)
Co-authored-by: Multazim Deshmukh <multazim.deshmukh@morningstar.com>
(cherry picked from commit 883241070f)
2022-09-14 11:38:21 -07:00
Beto Dealmeida
56137ebbe5 fix: logger message (#20714)
(cherry picked from commit c70d102b73)
2022-09-14 11:38:21 -07:00
Michael S. Molina
067495d954 Updates CHANGELOG.md 2022-07-06 13:48:18 -03:00
Reese
bba486b08c fix: Allow dataset owners to explore their datasets (#20382)
* fix: Allow dataset owners to explore their datasets

* Re-order imports

* Give owners security manager permissions to their datasets

* Update test suite

* Add SqlaTable to is_owner types

* Add owners to datasource mock

* Fix VSCode import error

* Fix merge error
2022-07-06 11:43:19 -03:00
Daniel Vaz Gaspar
6e74f3e82c docs: fix link for Apache Superset source code (#20620)
(cherry picked from commit b39a3d8f78)
2022-07-06 11:40:43 -03:00
Daniel Vaz Gaspar
3e97c60c8d chore: bump FAB to 4.1.3 (#20621)
(cherry picked from commit 183871b002)
2022-07-06 11:40:43 -03:00
Rui Zhao
747b011bb7 fix(embedded): Retry when executing alert queries to avoid sending transient errors to users as alert failure notifications (#20419)
Co-authored-by: Rui Zhao <zhaorui@dropbox.com>
(cherry picked from commit 818962cc89)
2022-07-06 11:40:43 -03:00
Evan Rusackas
dc71454416 fix: Respecting max/min opacities, and adding tests. (#20555)
* Respecting max/min opacities, and adding tests.

* revising tests

* Adding missing test case for maximum coverage :)

* removing unnecessary logic and test

* adding another unit test for (hopefully) full coverage.

* no more ternary operator

* New approach with Math.min  - take THAT codecov.

* one more stab at making codecov happy... ignoring the file next.

* lint fixes

(cherry picked from commit ac8e502228)
2022-07-06 11:38:33 -03:00
Evan Rusackas
4dcc805dee Revert "feat(plugin-chart-echarts): Support stacking negative and positive values (#20408)" (#20571)
This reverts commit c959d92dd1.

(cherry picked from commit f5f8ddec3e)
2022-07-06 11:38:33 -03:00
Stephen Liu
3b5513be36 fix(database-modal): forms in database modal will be effected by external form values (#20487)
(cherry picked from commit 932e304ffb)
2022-07-06 11:38:33 -03:00
Stephen Liu
4b2397f0c7 fix(big-number): big number gets cut off on a Dashboard (#20488)
(cherry picked from commit 24a53c38c6)
2022-07-06 11:38:32 -03:00
yourssvk
9a26a211d4 fix: SQL Lab cancel query in Redshift database connection does not wo… (#16326)
* fix: SQL Lab cancel query in Redshift database connection does not work #16325

Co-authored-by: Venkata Krishnan Somasundaram <venkata_cred@Venkatas-MacBook-Pro.local>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
(cherry picked from commit 90d486a643)
2022-07-06 11:38:32 -03:00
Diego Medina
eedcefc64a fix: Unable to download the Dashboard as image in case there's an image added through Markdown (#20362)
* fix: Unable to download the Dashboard as image in case there's an image added through Markdown

* licence

(cherry picked from commit c5d3678a31)
2022-07-06 11:38:32 -03:00
Alex Lauderbaugh
eba63b4add Updated copy in chart drop down to "View as table" (#20486)
(cherry picked from commit 93fbfe9d28)
2022-07-06 11:38:32 -03:00
Michael S. Molina
edbbf886af Updates CHANGELOG.md 2022-06-29 09:27:48 -03:00
Michael S. Molina
80e2f1abe7 fix: Removes psycopg2 as a required dependency (#20543)
* fix: Removes psycopg2 as a required dependency

* Disables lint warning

(cherry picked from commit cb3cd41dcd)
2022-06-29 09:25:30 -03:00
Michael S. Molina
b5a4c06d82 Updates CHANGELOG.md, UPDATING.md and package.json 2022-06-28 15:55:58 -03:00
smileydev
789f99341b fix(db): Show the only db install guide when the db is already installed and error is existed while importing file. (#20442)
* fix(db): make to show the db error msg when db installed and error is exist

* fix(db): make to replace dbinstall str into showDbInstallInstructions

(cherry picked from commit 23e62d3782)
2022-06-28 14:22:22 -03:00
Daniel Vaz Gaspar
63229dcf56 fix: bump FAB to 4.1.2 (#20483) 2022-06-28 14:22:12 -03:00
Multazim Deshmukh
92038db579 fix: correction from mmsql to mssql in setup.py (#20493)
Co-authored-by: Multazim Deshmukh <multazim.deshmukh@morningstar.com>
(cherry picked from commit 5a2abfab65)
2022-06-28 14:21:25 -03:00
Yongjie Zhao
b8d9208b6e feat(standardized form data): keep all columns and metrics (#20377)
(cherry picked from commit bbbe102887)
2022-06-28 14:21:25 -03:00
Elizabeth Thompson
885bbdde95 remove autoflush for queries during dual write (#20460)
(cherry picked from commit 44f0b511dd)
2022-06-28 14:21:25 -03:00
John Bodley
25a6f02cd6 fix: Re-add filter-box time granularity/column (#20485)
Co-authored-by: John Bodley <john.bodley@airbnb.com>
(cherry picked from commit 661ab35bd0)
2022-06-28 14:21:25 -03:00
Stephen Liu
bbca109ea3 fix(docs): prevent some symbols from being copied with (#20480)
(cherry picked from commit aa4068048a)
2022-06-28 14:19:23 -03:00
stevetracvc
bcc23bbacd fix: issue with sorting by multiple columns in a table (#19920)
Recent commit to sort alphanumeric columns via case insensitive
comparison broke the multi-column sort option. React-table only sorts
by the second (or third...) column if the first column matches.
Since the alphanumeric sort only returned -1 or 1, it never would move
to the subsequent columns when the earlier column values matched.

(cherry picked from commit a45d011e74)
2022-06-28 14:19:23 -03:00
John Bodley
23061d6822 fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL (#20385)
* fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL

* Update 2022-06-14_15-28_e09b4ae78457_resize_key_value_blob.py

Co-authored-by: John Bodley <john.bodley@airbnb.com>
(cherry picked from commit f5cb23e0a3)
2022-06-28 14:19:23 -03:00
Diego Medina
40a9257311 fix: alert & reports active toggle optimistic update (#20402)
(cherry picked from commit 4dc30441b7)
2022-06-28 14:19:23 -03:00
Michael S. Molina
ca0544a573 fix: Changes the return type of get_permissions to be JSON friendly (#20472)
* fix: Changes the return type of get_permissions to be JSON friendly

* Removes dangling comma

* Removes unused import

* Fixes typing errors

(cherry picked from commit a169b60712)
2022-06-28 14:19:23 -03:00
AAfghahi
d789f376b3 async queries limit bug (#20468)
(cherry picked from commit 2c16be42e1)
2022-06-28 14:19:23 -03:00
smileydev
3d850426ff fix(home): Show home page tabs as pills instead of links (#20257)
* fix(home): make to update the css style of links

* fix(home): make to fix lint issue

* fix(home): make to remove underline when tab is active

* fix(homes): make to fix the issue of tab

* fix(home): make to move styles to a tag

(cherry picked from commit a833674a8d)
2022-06-28 14:19:23 -03:00
Beto Dealmeida
4728d8f49b fix: ensure column name in description is string (#20340)
* fix: ensure column name in description is string

* Add unit test

(cherry picked from commit f3b289d3c3)
2022-06-28 14:19:23 -03:00
Diego Medina
9e6a3e1a4e fix(viz): BigQuery time grain 'minute'/'second' throws an error (#20350)
(cherry picked from commit 5afeba34bd)
2022-06-28 14:19:22 -03:00
smileydev
2d551faaf4 fix(chart & table): make to prevent dates from wrapping (#20384)
(cherry picked from commit 1ae935379f)
2022-06-28 14:19:22 -03:00
Yongjie Zhao
f58ad259ac fix: suppress translation warning in jest (#20404)
(cherry picked from commit 9fad26fa19)
2022-06-28 14:19:22 -03:00
Yongjie Zhao
4e93690e19 fix: should raise exception when apply a categorical axis (#20451)
(cherry picked from commit 8bbbd6f03f)
2022-06-28 14:19:22 -03:00
Diego Medina
d1ac6e5db4 fix: table viz sort icon bottom aligned (#20447)
(cherry picked from commit 93774d1860)
2022-06-28 14:19:22 -03:00
Samira El Aabidi
59fbf2a202 feat(chart): Enable caching per user when user impersonation is enabled (#20114)
* add username to extra cache keys when impersonation is enabled.

* don't put effective_user in extra_cache_key

* get_impersonation_key method in engine_spec class  to construct an impersonation key

* pass datasource when creating query objects

* adding an impersonation key when construction cache key

* add feature flag to control caching per user

* revert changes

* make precommit and pylint happy

* pass a User instance

* remove unnecessary import

(cherry picked from commit 68af5980ea)
2022-06-23 09:12:14 -03:00
Sam Firke
4841e8fb9c style(typo): occured -> occurred (#20116)
* Occured -> Occurred

* Occured -> Occurred

* Occured -> Occurred

* Occured - > Occurred

* Update FallbackComponent.tsx

* Update FallbackComponent.tsx

Co-authored-by: John Bodley <4567245+john-bodley@users.noreply.github.com>
(cherry picked from commit b7eb235440)
2022-06-23 09:12:14 -03:00
John Bodley
06592180ea [fbprophet] Fix frequencies (#20326)
(cherry picked from commit 8b0bee5e8b)
2022-06-23 09:12:14 -03:00
Simon Thelin
cb270034f3 fix(20428): Address-Presto/Trino-Poll-Issue-Refactor (#20434)
* fix(20428)-Address-Presto/Trino-Poll-Issue-Refacto
r

Update linter

* Update to only use BaseEngineSpec handle_cursor

* Fix CI

Co-authored-by: John Bodley <4567245+john-bodley@users.noreply.github.com>
(cherry picked from commit 8b7262fa90)
2022-06-23 09:12:14 -03:00
Diego Medina
7e48de484a fix(dashboard): new created chart did not have high lighted effect when using the permalink of chart share in dashboard (#20411)
(cherry picked from commit c2f01a676c)
2022-06-23 09:12:14 -03:00
Lily Kuang
a08499d88d fix(embedded): CSV download for chart (#20261)
* move postForm to superset client

* lint

* fix lint

* fix type

* update tests

* add tests

* add test for form submit

* add test for request form

* lint

* fix test

* fix tests

* more tests

* more tests

* test

* lint

* more test for postForm

* lint

* Update superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts

Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com>

* update tests

* remove useless test

* make test cover happy

* make test cover happy

* make test cover happy

* make codecov happy

* make codecov happy

Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com>
(cherry picked from commit ab9f72f1a1)
2022-06-23 09:12:14 -03:00
jiAng
bd721cd86c fix(cosmetic): cannot find m-r-10 class in superset.less (#20276)
* fix(cosmetic): cannot find m-r-10 class in superset.less

* fix: remove .m-r-10 class and use emotion instead

* Update superset-frontend/src/components/Datasource/CollectionTable.tsx

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

* Update superset-frontend/src/components/Datasource/DatasourceEditor.jsx

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
(cherry picked from commit f6f93aad37)
2022-06-23 09:12:14 -03:00
Stephen Liu
67c853790d fix: rm eslint-plugin-translation-vars engine requirement (#20420)
(cherry picked from commit fa7f144a68)
2022-06-23 09:12:14 -03:00
Stephen Liu
3b3c3be9b2 fix(bar-chart-v2): remove marker from bar chart V2 (#20409)
(cherry picked from commit b32288fddf)
2022-06-23 09:12:13 -03:00
mohittt8
2f07a88c32 fix(presto): use correct timespec for presto (#20333)
(cherry picked from commit 41bbf62e58)
2022-06-23 09:12:13 -03:00
Elizabeth Thompson
128722085c fix key error on permalink fetch for old permalinks (#20414)
(cherry picked from commit 12436e47c9)
2022-06-23 09:12:13 -03:00
Smart-Codi
78c577b515 adding extra metrics after chart configuration (#20410)
(cherry picked from commit a8a6b732e9)
2022-06-23 09:12:13 -03:00
chuancy
9aa047cf12 Chinese translation and English translation do not match (#20405)
(cherry picked from commit 11d94ce56c)
2022-06-23 09:12:13 -03:00
Kamil Gabryjelski
ce9807941b feat(plugin-chart-echarts): Support stacking negative and positive values (#20408)
(cherry picked from commit c959d92dd1)
2022-06-16 09:03:57 -03:00
206 changed files with 12840 additions and 21449 deletions

View File

@@ -37,7 +37,7 @@ jobs:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: 'requirements/testing.txt'
# TODO: separated requiermentes.txt file just for unit tests
# TODO: separated requirements.txt file just for unit tests
- name: Install dependencies
if: steps.check.outcome == 'failure'
uses: ./.github/actions/cached-dependencies

View File

@@ -18,6 +18,558 @@ under the License.
-->
## Change Log
### 2.0.1 (Thu Oct 13 09:52:35 2022 -0700)
**Database Migrations**
**Features**
**Fixes**
- [#21895](https://github.com/apache/superset/pull/21895) feat: Improves SafeMarkdown HTML sanitization (@michael-s-molina) (security fix)
- [#21874](https://github.com/apache/superset/pull/21874) feat: Adds a Content Security Policy (CSP) check for production environments (@michael-s-molina) (security fix)
- [#21853](https://github.com/apache/superset/pull/21853) feat: Disables HTML rendering in Toast by default (@michael-s-molina) (security fix)
- [#21776](https://github.com/apache/superset/pull/21776) fix(CustomFrame): Resolves issue #21731 where date range in explore throws runtime error (@eric-briscoe)
- [#21637](https://github.com/apache/superset/pull/21637) fix: respect chart cache timeout setting (@mayurnewase)
- [#21729](https://github.com/apache/superset/pull/21729) fix: allow adhoc columns in non-aggregate query (@mayurnewase)
- [#21441](https://github.com/apache/superset/pull/21441) fix(cache): respect default cache timeout on v1 chart data requests (@villebro)
- [#22038](https://github.com/apache/superset/pull/22038) fix: datasource save, improve data validation (@dpgaspar)
- [#22022](https://github.com/apache/superset/pull/22022) fix: deprecate approve and request_access endpoint (@dpgaspar)
- [#21964](https://github.com/apache/superset/pull/21964) fix: dashboard api cache decorator (@dpgaspar)
- [#21875](https://github.com/apache/superset/pull/21875) fix: check that imports are ZIPs (@betodealmeida)
- [#21761](https://github.com/apache/superset/pull/21761) fix: flash message on database data upload forms (@dpgaspar)
- [#21759](https://github.com/apache/superset/pull/21759) fix: database schema selector on import data (@dpgaspar)
- [#21729](https://github.com/apache/superset/pull/21729) fix: allow adhoc columns in non-aggregate query (@mayurnewase)
- [#21549](https://github.com/apache/superset/pull/21549) fix(dashboard): show correct roles for dashboard access dropdown (@mayurnewase)
- [#21773](https://github.com/apache/superset/pull/21773) fix: remove deprecated ETagResponseMixin (@eschutho)
- [#21561](https://github.com/apache/superset/pull/21561) fix(report): Fix permission check for set up email report on charts/dashboards. Fixes #21559 (@zhaorui2022)
- [#20651](https://github.com/apache/superset/pull/20651) fix: annotation broken (@zhaoyongjie)
- [#20830](https://github.com/apache/superset/pull/20830) fix: remove element reference in alerts report fetchs (@hughhhh)
- [#20063](https://github.com/apache/superset/pull/20063) fix: Add locale for DatePicker component (@aehanno)
- [#21302](https://github.com/apache/superset/pull/21302) fix: disallow users from viewing other user's profile on config (@dpgaspar)
- [#21154](https://github.com/apache/superset/pull/21154) fix(explore): Prevent unnecessary series limit subquery (@codyml)
- [#21498](https://github.com/apache/superset/pull/21498) fix: set correct favicon from config for login and FAB list views (@mayurnewase)
- [#21380](https://github.com/apache/superset/pull/21380) fix(sqllab): Fix cursor alignment in SQL lab editor by avoiding Lucida Console font on Windows (@MichaelHintz)
- [#20061](https://github.com/apache/superset/pull/20061) fix: Add french translation missing (@aehanno)
- [#21044](https://github.com/apache/superset/pull/21044) fix(plugin-chart-echarts): missing value format in mixed timeseries (@justinpark)
- [#21419](https://github.com/apache/superset/pull/21419) fix: cached common bootstrap Revert (#21018) (@dpgaspar)
- [#21296](https://github.com/apache/superset/pull/21296) fix(plugin-chart-echarts): show zero value in tooltip (@villebro)
- [#21294](https://github.com/apache/superset/pull/21294) fix(explore): Time column label not formatted when GENERIC_X_AXES enabled (@kgabryje)
- [#21272](https://github.com/apache/superset/pull/21272) fix: adds TLS certificate validation option for SMTP (@dpgaspar)
- [#21076](https://github.com/apache/superset/pull/21076) fix(celery cache warmup): add auth and use warm_up_cache endpoint (@nytai)
- [#21216](https://github.com/apache/superset/pull/21216) fix(database-list): hide upload file button if no permission (@stephenLYZ)
- [#21153](https://github.com/apache/superset/pull/21153) fix(sqllab): missing zero values while copy-to-clipboard (@justinpark)
- [#21084](https://github.com/apache/superset/pull/21084) fix(native filters): groupby filter issue (@stevetracvc)
- [#21005](https://github.com/apache/superset/pull/21005) fix(plugin-chart-handlebars): Sort-By and Sort-By-Descending control not work (@stephenLYZ)
- [#20969](https://github.com/apache/superset/pull/20969) fix(dashboard): Fix scroll behaviour in DashboardBuilderSidepane (@EugeneTorap)
- [#21007](https://github.com/apache/superset/pull/21007) fix(plugin-chart-echarts): gauge chart enhancements and fixes (@stephenLYZ)
- [#21032](https://github.com/apache/superset/pull/21032) fix(plugin-chart-echarts): invalid total label location for negative values in stacked bar chart (@justinpark)
- [#20962](https://github.com/apache/superset/pull/20962) fix: Explore scrolled down when navigating from dashboard (@kgabryje)
- [#20946](https://github.com/apache/superset/pull/20946) fix(viz): Show zero percent changes in Big Number Viz (@Antonio-RiveroMartnez)
- [#20819](https://github.com/apache/superset/pull/20819) fix: Temporal X Axis values are not properly displayed if the time column has a custom label defined (@diegomedina248)
- [#20736](https://github.com/apache/superset/pull/20736) fix: getting default value in run-server.sh (@zhaoyongjie)
- [#20733](https://github.com/apache/superset/pull/20733) fix(docker): Make Gunicorn max_requests and max_requests_jitter adjustable (@mdeshmu)
- [#20714](https://github.com/apache/superset/pull/20714) fix: logger message (@betodealmeida)
**Others**
- [#21811](https://github.com/apache/superset/pull/21811) chore(sqla): refactor query utils (@villebro)
- [#21811](https://github.com/apache/superset/pull/21811) chore(sqla): refactor query utils (@villebro)
- [#20644](https://github.com/apache/superset/pull/20644) chore(deps): bump moment from 2.29.2 to 2.29.4 in /superset-frontend (@dependabot[bot])
- [#21721](https://github.com/apache/superset/pull/21721) build: changelog for 2.0.1 (@AAfghahi)
- [#21018](https://github.com/apache/superset/pull/21018) perf: Memoize the common_bootstrap_payload (@bkyryliuk)
- [#21091](https://github.com/apache/superset/pull/21091) chore(deps): unpin holidays dependency version (@ecederstrand)
### 2.0 (Tue Jun 28 08:53:02 2022 -0400)
**Database Migrations**
- [#20385](https://github.com/apache/superset/pull/20385) fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL (@john-bodley)
- [#20284](https://github.com/apache/superset/pull/20284) chore(migrations): Renaming migration files so that they're easier to keep track of (@craig-rueda)
- [#20108](https://github.com/apache/superset/pull/20108) fix: None dataset and schema permissions (@dpgaspar)
- [#18794](https://github.com/apache/superset/pull/18794) feat(business-types): initial implementation of SIP-78 (@cccs-RyanS)
- [#20073](https://github.com/apache/superset/pull/20073) fix(dataset): handle missing sqla uri in migration (@villebro)
- [#19941](https://github.com/apache/superset/pull/19941) fix(reports): Clear last value when state is WORKING (@john-bodley)
- [#19675](https://github.com/apache/superset/pull/19675) chore(docs): Spelling (@jsoref)
- [#19793](https://github.com/apache/superset/pull/19793) fix(SIP-68): handle empty table name during migration (@ktmud)
- [#19786](https://github.com/apache/superset/pull/19786) fix(migrations): coalesce is_temporal when inserting into sl_columns (@cemremengu)
- [#19421](https://github.com/apache/superset/pull/19421) perf: refactor SIP-68 db migrations with INSERT SELECT FROM (@ktmud)
- [#19767](https://github.com/apache/superset/pull/19767) fix: Fix migration for removing time_range_endpoints 3 (@hughhhh)
- [#19728](https://github.com/apache/superset/pull/19728) fix: Removetime_range_endpoints from query context object pt 2 (@hughhhh)
- [#19630](https://github.com/apache/superset/pull/19630) chore: clean up unused imports in db migration scripts (@ktmud)
- [#19577](https://github.com/apache/superset/pull/19577) fix: merge multiple db heads (@eschutho)
- [#19243](https://github.com/apache/superset/pull/19243) fix: cannot delete a database if team member has SQL editor tab that uses that db (@diegomedina248)
- [#19537](https://github.com/apache/superset/pull/19537) chore: block unsafe functions (@betodealmeida)
- [#19513](https://github.com/apache/superset/pull/19513) chore: postpone timerange endpoint removal (@villebro)
- [#19495](https://github.com/apache/superset/pull/19495) perf: speed up db migration for deprecating time_range_endpoints (@ktmud)
- [#19474](https://github.com/apache/superset/pull/19474) fix: handle null params in #18936 migration (@serenajiang)
- [#19423](https://github.com/apache/superset/pull/19423) fix: Remove`time_range_endpoints` from query context object (@hughhhh)
- [#18936](https://github.com/apache/superset/pull/18936) chore: Remove legacy SIP-15 interim logic/flags (@john-bodley)
**Features**
- [#20377](https://github.com/apache/superset/pull/20377) feat(standardized form data): keep all columns and metrics (@zhaoyongjie)
- [#20114](https://github.com/apache/superset/pull/20114) feat(chart): Enable caching per user when user impersonation is enabled (@Samira-El)
- [#20408](https://github.com/apache/superset/pull/20408) feat(plugin-chart-echarts): Support stacking negative and positive values (@kgabryje)
- [#20278](https://github.com/apache/superset/pull/20278) feat: Prevent dataset edit modal closing on click-away in edit mode (@reesercollins)
- [#20392](https://github.com/apache/superset/pull/20392) feat: setting limit value when Pie chart switches (@zhaoyongjie)
- [#20373](https://github.com/apache/superset/pull/20373) feat: adding truncate metric control on timeseries charts (@zhaoyongjie)
- [#20248](https://github.com/apache/superset/pull/20248) feat(explore): Implement viz switcher redesign (@kgabryje)
- [#20113](https://github.com/apache/superset/pull/20113) feat(api): Added "kind" to dataset/<pk> endpoint (@reesercollins)
- [#20299](https://github.com/apache/superset/pull/20299) feat(explore): Dataset Panel Options when Source = Query II (@lyndsiWilliams)
- [#20320](https://github.com/apache/superset/pull/20320) feat: Databricks native driver (@betodealmeida)
- [#20313](https://github.com/apache/superset/pull/20313) feat(explore): Denormalize form data in echarts, world map and nvd3 bar and line charts (@kgabryje)
- [#20277](https://github.com/apache/superset/pull/20277) feat: multiple results pane on explore and dashboard (@zhaoyongjie)
- [#19898](https://github.com/apache/superset/pull/19898) feat: When editing the label/title in the Metrics popover, hitting Enter should save what you've typed (@diegomedina248)
- [#16493](https://github.com/apache/superset/pull/16493) feat(plugin-chart-echarts): [feature-parity] support extra control for the area chart V2 (@stephenLYZ)
- [#19855](https://github.com/apache/superset/pull/19855) feat(explore): Frontend implementation of dataset creation from infobox (@lyndsiWilliams)
- [#20165](https://github.com/apache/superset/pull/20165) feat: add modfied col and timezone info to schedule col (@pkdotson)
- [#20144](https://github.com/apache/superset/pull/20144) feat: showing results pane in dashboard (@zhaoyongjie)
- [#20242](https://github.com/apache/superset/pull/20242) feat: derived metrics use different line style (@zhaoyongjie)
- [#20010](https://github.com/apache/superset/pull/20010) feat: standardized form_data (@zhaoyongjie)
- [#19987](https://github.com/apache/superset/pull/19987) feat(superset-ui-core): add feature flag for the analogous colors (@stephenLYZ)
- [#19881](https://github.com/apache/superset/pull/19881) feat(world-map): support color by metric or country column (@stephenLYZ)
- [#19981](https://github.com/apache/superset/pull/19981) feat!: pass datasource_type and datasource_id to form_data (@eschutho)
- [#15241](https://github.com/apache/superset/pull/15241) feat: query datasets from SQL Lab (@betodealmeida)
- [#20129](https://github.com/apache/superset/pull/20129) feat(explore): Fill dashboard name when adding new chart from dashboard view (@kgabryje)
- [#20160](https://github.com/apache/superset/pull/20160) feat(explore): Add empty state to annotations (@kgabryje)
- [#20134](https://github.com/apache/superset/pull/20134) feat: add Query.columns for bootstrap_data (@hughhhh)
- [#20158](https://github.com/apache/superset/pull/20158) feat: add statsd metrics for notifications (@dpgaspar)
- [#20052](https://github.com/apache/superset/pull/20052) feat(Helm Chart): Support resource limits and requests for each component (@rathberm)
- [#20170](https://github.com/apache/superset/pull/20170) feat: add samples endpoint (@zhaoyongjie)
- [#19381](https://github.com/apache/superset/pull/19381) feat: add drag and drop column rearrangement for table viz (@stevetracvc)
- [#20136](https://github.com/apache/superset/pull/20136) feat: Add Certified filter to Datasets (@hughhhh)
- [#20111](https://github.com/apache/superset/pull/20111) feat(dashboard): Chart title click redirects to Explore in new tab (@kgabryje)
- [#20097](https://github.com/apache/superset/pull/20097) feat(plugin-chart-echarts): add support for generic axis to mixed chart (@villebro)
- [#20126](https://github.com/apache/superset/pull/20126) feat(dashboard): Add create chart button in dashboard edit mode (@kgabryje)
- [#20059](https://github.com/apache/superset/pull/20059) feat: Save column data into json_metadata for all Query executions (@hughhhh)
- [#19918](https://github.com/apache/superset/pull/19918) feat(plugin-chart-echarts): support horizontal bar chart (@stephenLYZ)
- [#19902](https://github.com/apache/superset/pull/19902) feat: Explore popovers should close on escape (@diegomedina248)
- [#20049](https://github.com/apache/superset/pull/20049) feat(dashboard): Rearrange items in chart header controls dropdown (@kgabryje)
- [#20030](https://github.com/apache/superset/pull/20030) feat(sip-68): Add DatasourceDAO class to manage querying different datasources easier (@hughhhh)
- [#19581](https://github.com/apache/superset/pull/19581) feat(viz-gallery): add search weight for viz-name (@stephenLYZ)
- [#19999](https://github.com/apache/superset/pull/19999) feat: RLS for SQL Lab (@betodealmeida)
- [#19993](https://github.com/apache/superset/pull/19993) feat(explore): Show confirmation modal if user exits Explore without saving changes (@kgabryje)
- [#19873](https://github.com/apache/superset/pull/19873) feat(css): adds `chartId`-based class to dashboard chart holder (@rusackas)
- [#20002](https://github.com/apache/superset/pull/20002) feat: deprecate /superset/testconn and migrate to api v1 (@zephyring)
- [#19935](https://github.com/apache/superset/pull/19935) feat: deprecate /superset/validate_sql_json migrate to api v1 (@dpgaspar)
- [#20015](https://github.com/apache/superset/pull/20015) feat: add new enums for datasource types (@hughhhh)
- [#19956](https://github.com/apache/superset/pull/19956) feat: Applitools Cypress workflow (@geido)
- [#19852](https://github.com/apache/superset/pull/19852) feat: Run Applitools on public Storybook (@geido)
- [#19963](https://github.com/apache/superset/pull/19963) feat: Add cypress test for downloading chart as image (@codemaster08240328)
- [#19957](https://github.com/apache/superset/pull/19957) feat: switch from `sqlalchemy-trino` to `trino-python-client` (@dungdm93)
- [#19921](https://github.com/apache/superset/pull/19921) feat: deprecate /superset/extra_table_metadata migrate to api v1 (@dpgaspar)
- [#19745](https://github.com/apache/superset/pull/19745) feat: simplify SQLite time grain (@betodealmeida)
- [#19927](https://github.com/apache/superset/pull/19927) feat(chart & legend): make to enable show legend by default (@prosdev0107)
- [#19754](https://github.com/apache/superset/pull/19754) feat: deprecate old API on core superset fave_dashboards (@dpgaspar)
- [#19905](https://github.com/apache/superset/pull/19905) feat: simplify `memoized_func` (@betodealmeida)
- [#19871](https://github.com/apache/superset/pull/19871) feat(filter): make to hide sort filter when time range (@prosdev0107)
- [#19851](https://github.com/apache/superset/pull/19851) feat: add Advanced Analytics into mixed time series chart (@zhaoyongjie)
- [#19692](https://github.com/apache/superset/pull/19692) feat: Update ShortKey for stop query running in SqlLab editor (@codemaster08240328)
- [#17903](https://github.com/apache/superset/pull/17903) feat: Adds plugin-chart-handlebars (@jdbranham)
- [#19748](https://github.com/apache/superset/pull/19748) feat(explore): improve UI in the control panel (@stephenLYZ)
- [#19724](https://github.com/apache/superset/pull/19724) feat: 10/15/30 min grain to Pinot (@hughhhh)
- [#19696](https://github.com/apache/superset/pull/19696) feat(explore): Replace overlay with alert banner when chart controls change (@kgabryje)
- [#19751](https://github.com/apache/superset/pull/19751) feat(explore): Implement data panel redesign (@kgabryje)
- [#19598](https://github.com/apache/superset/pull/19598) feat: add empty states to sqlab editor and select (@pkdotson)
- [#19450](https://github.com/apache/superset/pull/19450) feat: Remove legacy sql alchemy db connection link from G Sheet connection (@codemaster08240328)
- [#19710](https://github.com/apache/superset/pull/19710) feat: Enabling source maps full time (@rusackas)
- [#19671](https://github.com/apache/superset/pull/19671) feat: UI override registry (@suddjian)
- [#19691](https://github.com/apache/superset/pull/19691) feat(explore): More explicit labels of adhoc filter operators (@kgabryje)
- [#19558](https://github.com/apache/superset/pull/19558) feat(explore): Redesign of Run/Save buttons (@kgabryje)
- [#19650](https://github.com/apache/superset/pull/19650) feat(embedded): API get embedded dashboard config by uuid (@lilykuang)
- [#19310](https://github.com/apache/superset/pull/19310) feat(CRUD): add new empty state (@stephenLYZ)
- [#19622](https://github.com/apache/superset/pull/19622) feat(plugin-chart-echarts): add aggregate total for the Pie/Donuct chart (@stephenLYZ)
- [#19314](https://github.com/apache/superset/pull/19314) feat: Move Database Import option into DB Connection modal (@lyndsiWilliams)
- [#19434](https://github.com/apache/superset/pull/19434) feat: deprecate old API and create new API for dashes created by me (@dpgaspar)
- [#19482](https://github.com/apache/superset/pull/19482) feat: add success toast to alerts and reports (@pkdotson)
- [#19574](https://github.com/apache/superset/pull/19574) feat: add a `where_in` filter for Jinja2 (@betodealmeida)
- [#19458](https://github.com/apache/superset/pull/19458) feat(explore): Move timer, row counter and cached pills to chart container (@kgabryje)
- [#19529](https://github.com/apache/superset/pull/19529) feat(explore): Move chart header to top of the page (@kgabryje)
- [#19489](https://github.com/apache/superset/pull/19489) feat(CI): clean up Python tests output (@ktmud)
- [#19308](https://github.com/apache/superset/pull/19308) feat(explore): SQL popover in datasource panel (@kgabryje)
- [#19325](https://github.com/apache/superset/pull/19325) feat(color): support analogous colors to prevent color conflict (@stephenLYZ)
- [#19408](https://github.com/apache/superset/pull/19408) feat(dashboard): Implement empty states for empty tabs (@kgabryje)
- [#19446](https://github.com/apache/superset/pull/19446) feat(explore): Move chart actions into dropdown (@kgabryje)
- [#19394](https://github.com/apache/superset/pull/19394) feat(explore): UI changes in dataset panel on Explore page (@kgabryje)
**Fixes**
- [#20382](https://github.com/apache/superset/pull/20382) fix: Allow dataset owners to explore their datasets (@reesercollins)
- [#20419](https://github.com/apache/superset/pull/20419) fix(embedded): Retry when executing alert queries to avoid sending transient errors to users as alert failure notifications (@zhaorui2022)
- [#20555](https://github.com/apache/superset/pull/20555) fix: Respecting max/min opacities, and adding tests. (@rusackas)
- [#20571](https://github.com/apache/superset/pull/20571) fix: Revert #20408 (stacking negative values in echarts bar chart) (@rusackas)
- [#20487](https://github.com/apache/superset/pull/20487) fix(database-modal): form in database model effects results of the database list (@stephenLYZ)
- [#20488](https://github.com/apache/superset/pull/20488) fix(big-number): big number gets cut off on a Dashboard (@stephenLYZ)
- [#16326](https://github.com/apache/superset/pull/16326) fix: SQL Lab cancel query in Redshift database connection does not wo… (@yourssvk)
- [#20362](https://github.com/apache/superset/pull/20362) fix: Unable to download the Dashboard as image in case there's an image added through Markdown (@diegomedina248)
- [#20543](https://github.com/apache/superset/pull/20543) fix: Removes psycopg2 as a required dependency
- [#20442](https://github.com/apache/superset/pull/20442) fix(db): Show the only db install guide when the db is already installed and error is existed while importing file. (@prosdev0107)
- [#20483](https://github.com/apache/superset/pull/20483) fix: bump FAB to 4.1.2 (@dpgaspar)
- [#20493](https://github.com/apache/superset/pull/20493) fix: correction from mmsql to mssql in setup.py (@mdeshmu)
- [#20460](https://github.com/apache/superset/pull/20460) fix: new column UUID conflicts in dual write (@eschutho)
- [#20485](https://github.com/apache/superset/pull/20485) fix: Re-add filter-box time granularity/column (@john-bodley)
- [#20480](https://github.com/apache/superset/pull/20480) fix(docs): prevent some symbols from being copied in docs (@stephenLYZ)
- [#19920](https://github.com/apache/superset/pull/19920) fix(table viz): correctly sort by multiple columns in a table (@stevetracvc)
- [#20402](https://github.com/apache/superset/pull/20402) fix: alert & reports active toggle optimistic update (@diegomedina248)
- [#20472](https://github.com/apache/superset/pull/20472) fix: Changes the return type of get_permissions to be JSON friendly (@michael-s-molina)
- [#20468](https://github.com/apache/superset/pull/20468) fix: async queries limit bug (@AAfghahi)
- [#20257](https://github.com/apache/superset/pull/20257) fix(home): Show home page tabs as pills instead of links (@prosdev0107)
- [#20340](https://github.com/apache/superset/pull/20340) fix: ensure column name in description is string (@betodealmeida)
- [#20350](https://github.com/apache/superset/pull/20350) fix(viz): BigQuery time grain 'minute'/'second' throws an error (@diegomedina248)
- [#20384](https://github.com/apache/superset/pull/20384) fix(chart & table): Prevent the dates from wrapping in table chart (@prosdev0107)
- [#20404](https://github.com/apache/superset/pull/20404) fix: suppress translation warning in jest (@zhaoyongjie)
- [#20451](https://github.com/apache/superset/pull/20451) fix: should raise exception when apply a categorical axis (@zhaoyongjie)
- [#20447](https://github.com/apache/superset/pull/20447) fix: table viz sort icon bottom aligned (@diegomedina248)
- [#20326](https://github.com/apache/superset/pull/20326) fix(fbprophet): Fix weekly frequencies (@john-bodley)
- [#20434](https://github.com/apache/superset/pull/20434) fix(20428): Address-Presto/Trino-Poll-Issue-Refactor (@Thelin90)
- [#20411](https://github.com/apache/superset/pull/20411) fix(dashboard): new created chart did not have high lighted effect when using the permalink of chart share in dashboard (@diegomedina248)
- [#20261](https://github.com/apache/superset/pull/20261) fix(embedded): CSV download for chart (@lilykuang)
- [#20276](https://github.com/apache/superset/pull/20276) fix(cosmetic): cannot find m-r-10 class in superset.less (@Renderz)
- [#20420](https://github.com/apache/superset/pull/20420) fix: rm eslint-plugin-translation-vars engines requirement (@stephenLYZ)
- [#20409](https://github.com/apache/superset/pull/20409) fix(bar-chart-v2): remove marker control from bar chart V2 (@stephenLYZ)
- [#20333](https://github.com/apache/superset/pull/20333) fix(presto): use milliseconds timespec for presto (@mohittt8)
- [#20414](https://github.com/apache/superset/pull/20414) fix: key error on permalink fetch for old permalinks (@eschutho)
- [#20410](https://github.com/apache/superset/pull/20410) fix: Adding extra metrics issue after chart configuration (@codemaster08240328)
- [#20405](https://github.com/apache/superset/pull/20405) fix: Incorrect translations in Chinese in messages.po (@chuancyzhang)
- [#20396](https://github.com/apache/superset/pull/20396) fix(plugin-chart-pivot-table): color weight of Conditional formatting metrics not work (@stephenLYZ)
- [#20361](https://github.com/apache/superset/pull/20361) fix(fonts): Show the all the A's in our workspace correctly, not funky (@prosdev0107)
- [#20383](https://github.com/apache/superset/pull/20383) fix: Unable to export multiple Dashboards with the same name (@diegomedina248)
- [#20363](https://github.com/apache/superset/pull/20363) fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button (@diegomedina248)
- [#20372](https://github.com/apache/superset/pull/20372) fix: update connection modal to use existing catalog (@pkdotson)
- [#20368](https://github.com/apache/superset/pull/20368) fix(VERSIONED_EXPORTS): Ensure dashboards and charts adhere to the VERSIONED_EXPORTS feature flag (@john-bodley)
- [#20351](https://github.com/apache/superset/pull/20351) fix: catch some potential errors on dual write (@eschutho)
- [#20364](https://github.com/apache/superset/pull/20364) fix: query execution time is not fully displayed in bubble icon (@diegomedina248)
- [#20365](https://github.com/apache/superset/pull/20365) fix: Fix typo in Error handling message (@codemaster08240328)
- [#19967](https://github.com/apache/superset/pull/19967) fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button (@diegomedina248)
- [#20348](https://github.com/apache/superset/pull/20348) fix(docker): Make Gunicorn Keepalive Adjustable (@mdeshmu)
- [#19670](https://github.com/apache/superset/pull/19670) fix: Add serviceAccountName to celerybeat pods (@paulinjo)
- [#20315](https://github.com/apache/superset/pull/20315) fix(chart): chart gets cut off on the dashboard (@stephenLYZ)
- [#20324](https://github.com/apache/superset/pull/20324) fix: superset-ui/core coverage (@zhaoyongjie)
- [#20282](https://github.com/apache/superset/pull/20282) fix(explore): Make that see more/see less works correctly with scrolling when error msg is long text. (@prosdev0107)
- [#20296](https://github.com/apache/superset/pull/20296) fix: Alpha are unable to perform a second modification to a Dataset when in Explore (@hughhhh)
- [#20290](https://github.com/apache/superset/pull/20290) fix: Faulty datetime parser regex (@reesercollins)
- [#19761](https://github.com/apache/superset/pull/19761) fix(plugin-chart-echarts): [feature-parity] apply button of annotation layer doesn't work as expected (@stephenLYZ)
- [#20263](https://github.com/apache/superset/pull/20263) fix(embedded): accessing variable response before initialization (@zhaorui2022)
- [#20274](https://github.com/apache/superset/pull/20274) fix(codecov): improve core code coverage (@stephenLYZ)
- [#20187](https://github.com/apache/superset/pull/20187) fix: Database import with cancel_query.. extra field (@codemaster08240328)
- [#20237](https://github.com/apache/superset/pull/20237) fix(cosmetic): Fix Datasource Modal Out Of Box (@Renderz)
- [#20058](https://github.com/apache/superset/pull/20058) fix: Support the Clipboard API in modern browsers (@diegomedina248)
- [#20164](https://github.com/apache/superset/pull/20164) fix(sql lab): View result button is not showing consistently (@diegomedina248)
- [#20171](https://github.com/apache/superset/pull/20171) fix(charts list): do not trigger ListViewError exception for anonymous user (@trepmag)
- [#20178](https://github.com/apache/superset/pull/20178) fix: While exporting CSV , only the entries on first page are getting downloaded even when user is on other pages #17861 (@LahmerIlyas)
- [#20204](https://github.com/apache/superset/pull/20204) fix: Fixes issue where results panel height was incorrect [sc-49045] (@eric-briscoe)
- [#20235](https://github.com/apache/superset/pull/20235) fix: Box Plot Chart throws an error when the average (AVG) / SUM is being calculated on the Metrics (@diegomedina248)
- [#20088](https://github.com/apache/superset/pull/20088) fix: datatype tracking issue on virtual dataset (@codemaster08240328)
- [#20220](https://github.com/apache/superset/pull/20220) fix: dashbaord unable to refresh (@zhaoyongjie)
- [#20228](https://github.com/apache/superset/pull/20228) fix: failed samples should throw exception (@zhaoyongjie)
- [#20203](https://github.com/apache/superset/pull/20203) fix: move columns to datasource object for bootstrap data (@hughhhh)
- [#20151](https://github.com/apache/superset/pull/20151) fix(csv): Ensure df_to_escaped_csv does not coerce integer columns to float (@john-bodley)
- [#20221](https://github.com/apache/superset/pull/20221) fix(legacy-plugin-chart-sunburst): linear color scheme not work when secondary metric is provided (@stephenLYZ)
- [#20223](https://github.com/apache/superset/pull/20223) fix(legacy-plugin-chart-sunburst): chart broken when secondary metric is removed (@stephenLYZ)
- [#20147](https://github.com/apache/superset/pull/20147) fix(cosmetic): Limiting modal height (@rusackas)
- [#20206](https://github.com/apache/superset/pull/20206) fix(sql lab): SQL Lab Compile Query Delay (@diegomedina248)
- [#20201](https://github.com/apache/superset/pull/20201) fix: unable to set destroyOnClose on ModalTrigger (@zhaoyongjie)
- [#20186](https://github.com/apache/superset/pull/20186) fix(db): make to allow to show/hide the password when only creating (@prosdev0107)
- [#20127](https://github.com/apache/superset/pull/20127) fix(database): retrival of tables and views from schema for exasol backend (@Nicoretti)
- [#19899](https://github.com/apache/superset/pull/19899) fix: always create parameter json field (@pkdotson)
- [#20173](https://github.com/apache/superset/pull/20173) fix: avoid while cycle in computeMaxFontSize for big Number run forever when css rule applied (@diegomedina248)
- [#20086](https://github.com/apache/superset/pull/20086) fix(css): transparent linear gradient not working in safari (@stephenLYZ)
- [#19102](https://github.com/apache/superset/pull/19102) fix: string aggregation is incorrect in PivotTableV2 (@diegomedina248)
- [#20011](https://github.com/apache/superset/pull/20011) fix(chart & heatmap): make to fix that y label is rendering out of bounds (@prosdev0107)
- [#20142](https://github.com/apache/superset/pull/20142) fix(explore): handle null control sections (@villebro)
- [#20128](https://github.com/apache/superset/pull/20128) fix: advanced data type API spec and permission name (@dpgaspar)
- [#20107](https://github.com/apache/superset/pull/20107) fix(generic-chart-axes): set x-axis if unset and ff is enabled (@villebro)
- [#20018](https://github.com/apache/superset/pull/20018) fix(modal): add primary button loading state to modals (@kgopal492)
- [#20099](https://github.com/apache/superset/pull/20099) fix: Add cypress test for report page direct link issue (@codemaster08240328)
- [#20068](https://github.com/apache/superset/pull/20068) fix: dbmodal test connection error timeout (@pkdotson)
- [#20092](https://github.com/apache/superset/pull/20092) fix: Revert "feat(explore): Show confirmation modal if user exits Explore without saving changes (#19993) (@kgabryje)
- [#19939](https://github.com/apache/superset/pull/19939) fix(chart & alert): make to show metrics properly (@prosdev0107)
- [#20085](https://github.com/apache/superset/pull/20085) fix: typo in `importexport/api.py` OpenAPI (@betodealmeida)
- [#20051](https://github.com/apache/superset/pull/20051) fix(CRUD): make to fix the dancing when crud view is on hover (@prosdev0107)
- [#20064](https://github.com/apache/superset/pull/20064) fix(chart & gallery): make to add mixed time-series into recommended charts (@prosdev0107)
- [#20013](https://github.com/apache/superset/pull/20013) fix: The dynamic form to connect to Snowflake DB is not returning any errors (@diegomedina248)
- [#20029](https://github.com/apache/superset/pull/20029) fix(plugin-chart-echarts): tooltip of big number truncated at then bottom (@stephenLYZ)
- [#19914](https://github.com/apache/superset/pull/19914) fix: Refactor SQL engine username logic (@john-bodley)
- [#20050](https://github.com/apache/superset/pull/20050) fix: Fixes Tabs style (@michael-s-molina)
- [#20048](https://github.com/apache/superset/pull/20048) fix(homepage): make to show indicator when tab is chosen (@prosdev0107)
- [#20026](https://github.com/apache/superset/pull/20026) fix(chart & filters): make to padding between textarea and buttons (@prosdev0107)
- [#20019](https://github.com/apache/superset/pull/20019) fix(embedded): third party cookies (@lilykuang)
- [#20033](https://github.com/apache/superset/pull/20033) fix: Direct Linking issue on report list: 404 status code. (@codemaster08240328)
- [#19977](https://github.com/apache/superset/pull/19977) fix(word-cloud): fix randomness of each word's rotation (@ebaratte)
- [#20021](https://github.com/apache/superset/pull/20021) fix: native filter truncation rerendering loop on hover (@diegomedina248)
- [#20004](https://github.com/apache/superset/pull/20004) fix: URI form is blank when trying to connect from sql lab (@diegomedina248)
- [#19841](https://github.com/apache/superset/pull/19841) fix: Table chart column config issue (@codemaster08240328)
- [#19877](https://github.com/apache/superset/pull/19877) fix: Making chart update more truthful (@Gwitchr)
- [#19996](https://github.com/apache/superset/pull/19996) fix: Use pull_request_target in Cypress Applitools workflow (@geido)
- [#19972](https://github.com/apache/superset/pull/19972) fix: revert chore(deps): bump d3-svg-legend in /superset-frontend (#19846) (@villebro)
- [#19889](https://github.com/apache/superset/pull/19889) fix: Fix auto-reversion of label/title in the Metrics popover (@diegomedina248)
- [#19903](https://github.com/apache/superset/pull/19903) fix(explore): Explore data table tooltip (@Gwitchr)
- [#19938](https://github.com/apache/superset/pull/19938) fix(chart & table): make to allow highlight in case of numeric column (@prosdev0107)
- [#19839](https://github.com/apache/superset/pull/19839) fix(dashboard): allow users to resize the markdown widget easier (@cccs-Dustin)
- [#19887](https://github.com/apache/superset/pull/19887) fix(hive): Workaround for Python 3.9 s3 transfer issue (@john-bodley)
- [#19936](https://github.com/apache/superset/pull/19936) fix: OpenAPI docs small fixes (@dpgaspar)
- [#19932](https://github.com/apache/superset/pull/19932) fix: can not correctly set force in store (@zhaoyongjie)
- [#19930](https://github.com/apache/superset/pull/19930) fix: memoize primitives (@betodealmeida)
- [#19926](https://github.com/apache/superset/pull/19926) fix(dataset): DAO update (@betodealmeida)
- [#19826](https://github.com/apache/superset/pull/19826) fix: Missing `f` prefix on f-strings (@code-review-doctor)
- [#18988](https://github.com/apache/superset/pull/18988) fix(column-header-tooltip): make that hide the tooltip when the cloum… (@prosdev0107)
- [#19782](https://github.com/apache/superset/pull/19782) fix: chart import error with virtual dataset (@codemaster08240328)
- [#19485](https://github.com/apache/superset/pull/19485) fix: Set fixed maxWidth of the cron schedule modal (@codemaster08240328)
- [#19885](https://github.com/apache/superset/pull/19885) fix: Chart download as image issue (@codemaster08240328)
- [#19883](https://github.com/apache/superset/pull/19883) fix(allow-db-explore): make to check the allow virtual table explore option by default (@prosdev0107)
- [#19835](https://github.com/apache/superset/pull/19835) fix(helm): fix postgresql values (@benjamin-texier)
- [#19758](https://github.com/apache/superset/pull/19758) fix(plugin-chart-echarts): [feature parity] annotation line chart color does not work (@stephenLYZ)
- [#19879](https://github.com/apache/superset/pull/19879) fix(plugin-chart-handlebars): fix overflow, debounce and control reset (@villebro)
- [#19668](https://github.com/apache/superset/pull/19668) fix: Dates alignment in Table viz (@geido)
- [#19876](https://github.com/apache/superset/pull/19876) fix: Cannot re-order metrics by drag and drop (@diegomedina248)
- [#19840](https://github.com/apache/superset/pull/19840) fix(dashboard-css): make to load saved css template (@prosdev0107)
- [#19859](https://github.com/apache/superset/pull/19859) fix: Dashboard report creation error handling (@etr2460)
- [#19857](https://github.com/apache/superset/pull/19857) fix: Update eslint error message to reflect location of antd components (@etr2460)
- [#19605](https://github.com/apache/superset/pull/19605) fix: Query execution time is displayed as invalid date (@diegomedina248)
- [#19694](https://github.com/apache/superset/pull/19694) fix(db & connection): make to show/hide the password when only creating db connection (@prosdev0107)
- [#19778](https://github.com/apache/superset/pull/19778) fix: deck.gl GeoJsonLayer Autozoom & fill/stroke options (@diegomedina248)
- [#19850](https://github.com/apache/superset/pull/19850) fix: Regression on Data and Alerts & Reports Headers (@diegomedina248)
- [#19842](https://github.com/apache/superset/pull/19842) fix: count(distinct column_name) in metrics (@zhaoyongjie)
- [#19843](https://github.com/apache/superset/pull/19843) fix(explore): ignore temporary controls in altered pill (@villebro)
- [#19800](https://github.com/apache/superset/pull/19800) fix: Cypress tests reliability improvements (@diegomedina248)
- [#19575](https://github.com/apache/superset/pull/19575) fix: Show full long number in text email report for table chart. (@codemaster08240328)
- [#19429](https://github.com/apache/superset/pull/19429) fix(dashboard): make to filter the correct certified or non-certified… (@prosdev0107)
- [#13082](https://github.com/apache/superset/pull/13082) fix(sql_lab): Add custom timestamp type for literal casting for presto timestamps (@kekwan)
- [#19797](https://github.com/apache/superset/pull/19797) fix: add missing init files (@suddjian)
- [#19672](https://github.com/apache/superset/pull/19672) fix: trap SQLAlchemy common exceptions & throw 422 error instead (@diegomedina248)
- [#19288](https://github.com/apache/superset/pull/19288) fix: AlertReportCronScheduler tests (@diegomedina248)
- [#19781](https://github.com/apache/superset/pull/19781) fix(world-map): remove categorical color control (@serenajiang)
- [#19792](https://github.com/apache/superset/pull/19792) fix(plugin-chart-table): Resetting controls when switching query mode (@kgabryje)
- [#19755](https://github.com/apache/superset/pull/19755) fix: small cleanup for created by me dashboards API (@dpgaspar)
- [#19784](https://github.com/apache/superset/pull/19784) fix(readme): Remove broken link to legacy gallery (@drluckyspin)
- [#19722](https://github.com/apache/superset/pull/19722) fix: dashboard top level tabs edit (@diegomedina248)
- [#19777](https://github.com/apache/superset/pull/19777) fix(explore): Double divider if no permissions for adding reports (@kgabryje)
- [#19673](https://github.com/apache/superset/pull/19673) fix(import): Add the error alert on failed database import (@prosdev0107)
- [#19518](https://github.com/apache/superset/pull/19518) fix: alert/report created by filter inconsistency with table display (@diegomedina248)
- [#19700](https://github.com/apache/superset/pull/19700) fix: remove expose (@AAfghahi)
- [#19626](https://github.com/apache/superset/pull/19626) fix: deactivate embedding on a dashboard (@suddjian)
- [#19472](https://github.com/apache/superset/pull/19472) fix: Dashboard Edit View Tab Headers Hidden when Dashboard Name is Long (@diegomedina248)
- [#19311](https://github.com/apache/superset/pull/19311) fix(sql lab): add quotes when autocompleting table names with spaces in the editor (@diegomedina248)
- [#19290](https://github.com/apache/superset/pull/19290) fix(sql lab): select edit on query from history doesn't upload editor properly (@diegomedina248)
- [#19420](https://github.com/apache/superset/pull/19420) fix: sql lab ctrl t behaved differently from clicking (@Gwitchr)
- [#19357](https://github.com/apache/superset/pull/19357) fix: Redirect to full url on 401 (@geido)
- [#19001](https://github.com/apache/superset/pull/19001) fix: Line Chart Annotation Info Update (@codemaster08240328)
- [#19714](https://github.com/apache/superset/pull/19714) fix: create virtual table with exotic type (@villebro)
- [#19708](https://github.com/apache/superset/pull/19708) fix(nav): infinite redirect and upload dataset nav permissions (@ktmud)
- [#19430](https://github.com/apache/superset/pull/19430) fix(data-upload): make to change err message (@prosdev0107)
- [#19419](https://github.com/apache/superset/pull/19419) fix(alert & report): make to fix the issue when recreate report (@prosdev0107)
- [#19371](https://github.com/apache/superset/pull/19371) fix: Reset sorting bar issue in Barchart (@codemaster08240328)
- [#19362](https://github.com/apache/superset/pull/19362) fix(sql lab): display the 'View Results' button consistently in the history tab on sync mode (@diegomedina248)
- [#19294](https://github.com/apache/superset/pull/19294) fix: improve alerts & reports modal on small devices (@diegomedina248)
- [#19257](https://github.com/apache/superset/pull/19257) fix(sql lab): table selector should display all the selected tables (@diegomedina248)
- [#19686](https://github.com/apache/superset/pull/19686) fix(plugin-chart-echarts): xAxis scale is not correct when time grain is quarter (@stephenLYZ)
- [#19646](https://github.com/apache/superset/pull/19646) fix(explore): Change copy of cross filters checkbox (@kgabryje)
- [#19586](https://github.com/apache/superset/pull/19586) fix: Navbar styles and Welcome page text (@geido)
- [#19662](https://github.com/apache/superset/pull/19662) fix(database-api): allow search for all columns (@villebro)
- [#19656](https://github.com/apache/superset/pull/19656) fix: allow_browser_login in import/export API (@betodealmeida)
- [#19628](https://github.com/apache/superset/pull/19628) fix: Table Autosizing Has Unnecessary Horizontal Scroll Bars (@diegomedina248)
- [#19573](https://github.com/apache/superset/pull/19573) fix(chart & polygon): make to fix the issue the polygon chart (@prosdev0107)
- [#19051](https://github.com/apache/superset/pull/19051) fix: update Permissions for right nav (@AAfghahi)
- [#19625](https://github.com/apache/superset/pull/19625) fix(test): make test_clean_requests_after_schema_grant more idempotent (@ktmud)
- [#19571](https://github.com/apache/superset/pull/19571) fix: Catch literal colors when theme top level (@geido)
- [#19594](https://github.com/apache/superset/pull/19594) fix: spelling of following (@lzm0)
- [#19569](https://github.com/apache/superset/pull/19569) fix: check type of url before performing string actions (@eschutho)
- [#19570](https://github.com/apache/superset/pull/19570) fix: sqloxide optional (@betodealmeida)
- [#19397](https://github.com/apache/superset/pull/19397) fix: weight tooltip issue (@codemaster08240328)
- [#19313](https://github.com/apache/superset/pull/19313) fix(sql lab): increase the size of the action icons in the history tab (@diegomedina248)
- [#19039](https://github.com/apache/superset/pull/19039) fix(explore): clean data when hidding control (@stephenLYZ)
- [#19444](https://github.com/apache/superset/pull/19444) fix: Error Message is cut off in alerts & reports log page (@codemaster08240328)
- [#19312](https://github.com/apache/superset/pull/19312) fix: adaptive formatting typo in explore dropdowns (@diegomedina248)
- [#19534](https://github.com/apache/superset/pull/19534) fix(explore): Chart header icon paddings (@kgabryje)
- [#19399](https://github.com/apache/superset/pull/19399) fix: native filter dropdown not attached to parent node (@diegomedina248)
- [#19112](https://github.com/apache/superset/pull/19112) fix: Dashboard import holding issue (@codemaster08240328)
- [#19342](https://github.com/apache/superset/pull/19342) fix: Clean up custom css when dashboard unmounted (@codemaster08240328)
- [#19491](https://github.com/apache/superset/pull/19491) fix: Dynamic form to connect to Snowflake DB is not displaying authentication errors (@diegomedina248)
- [#19528](https://github.com/apache/superset/pull/19528) fix: Correct Ukraine map (@wacken89)
- [#19522](https://github.com/apache/superset/pull/19522) fix: add back view for report reload error (@pkdotson)
- [#19519](https://github.com/apache/superset/pull/19519) fix: GSheets rendering from global nav (@hughhhh)
- [#19358](https://github.com/apache/superset/pull/19358) fix(sqllab): make to hide the delete button of most recent query history (@prosdev0107)
- [#19307](https://github.com/apache/superset/pull/19307) fix: Logo resizing on page load (@geido)
- [#19166](https://github.com/apache/superset/pull/19166) fix: time filter should be [start, end) (@zhaoyongjie)
**Others**
- [#20620](https://github.com/apache/superset/pull/20620) docs: fix link for Apache Superset source code (@dpgaspar)
- [#20621](https://github.com/apache/superset/pull/20621) chore: bump FAB to 4.1.3 (@dpgaspar)
- [#20486](https://github.com/apache/superset/pull/20486) chore: Updated copy in chart drop down to "View as table" (@lauderbaugh)
- [#20116](https://github.com/apache/superset/pull/20116) style(typo): occured -> occurred (@sfirke)
- [#20401](https://github.com/apache/superset/pull/20401) chore: add action to welcome new users (@eschutho)
- [#20269](https://github.com/apache/superset/pull/20269) chore(docs): Remove cache warming documentation (@ajwhite)
- [#20194](https://github.com/apache/superset/pull/20194) chore: Removes unused vars (@michael-s-molina)
- [#20321](https://github.com/apache/superset/pull/20321) chore: add breaking change information about form_data datasource_type (@eschutho)
- [#20298](https://github.com/apache/superset/pull/20298) chore: Removes no-use-before-define warnings (@michael-s-molina)
- [#20337](https://github.com/apache/superset/pull/20337) chore(dashboard): update Edit Dashboard side panel tabs (@codyml)
- [#20318](https://github.com/apache/superset/pull/20318) chore: Updates the final steps of the release README (@michael-s-molina)
- [#20307](https://github.com/apache/superset/pull/20307) docs: Updates CHANGELOG.md with 1.5.1 fixes (@michael-s-molina)
- [#20308](https://github.com/apache/superset/pull/20308) docs(jinja): Detail how to use Jinja parameters (@EBoisseauSierra)
- [#20304](https://github.com/apache/superset/pull/20304) chore: superset-ui/core code coverage (@zhaoyongjie)
- [#20297](https://github.com/apache/superset/pull/20297) chore(deps): pinning pyjwt to 2.4.0 (@sadpandajoe)
- [#20287](https://github.com/apache/superset/pull/20287) chore(deps): bump numpy 1.22.1 and PyJWT to 2.4.0 (@sadpandajoe)
- [#20272](https://github.com/apache/superset/pull/20272) chore: remove unused codes for samples (@zhaoyongjie)
- [#20289](https://github.com/apache/superset/pull/20289) chore: Adjusts release emails (@michael-s-molina)
- [#20180](https://github.com/apache/superset/pull/20180) docs: facelift the docs (@mistercrunch)
- [#20249](https://github.com/apache/superset/pull/20249) chore: add event logger to reports/alerts CRUD (@AAfghahi)
- [#20273](https://github.com/apache/superset/pull/20273) chore: adjust the psycopg2 version of k8s installation guide (@ensky)
- [#20152](https://github.com/apache/superset/pull/20152) refactor(trino): Handful of updates for the Trino engine (@john-bodley)
- [#20252](https://github.com/apache/superset/pull/20252) chore: use exc_info to pass errors to log warnings (@eschutho)
- [#20154](https://github.com/apache/superset/pull/20154) chore(requirements): Cleanup of Python requirements (@john-bodley)
- [#20226](https://github.com/apache/superset/pull/20226) refactor: decouple DataTableControl (@zhaoyongjie)
- [#20243](https://github.com/apache/superset/pull/20243) docs: Add beans to users list (@kakoni)
- [#20231](https://github.com/apache/superset/pull/20231) docs: Updates release scripts and docs (@michael-s-molina)
- [#20196](https://github.com/apache/superset/pull/20196) chore: bumping min version of shillelagh (@AAfghahi)
- [#20192](https://github.com/apache/superset/pull/20192) chore: Moves date utils to utils folder (@michael-s-molina)
- [#20210](https://github.com/apache/superset/pull/20210) docs: update release instructions (@villebro)
- [#20205](https://github.com/apache/superset/pull/20205) chore(deps): bump swagger-ui-react from 4.1.2 to 4.1.3 in /docs (@dependabot[bot])
- [#20195](https://github.com/apache/superset/pull/20195) docs: correct case of ClickHouse (@DanRoscigno)
- [#20109](https://github.com/apache/superset/pull/20109) refactor: decouple DataTablesPane (@zhaoyongjie)
- [#20193](https://github.com/apache/superset/pull/20193) refactor: Removes embedded/index.tsx warnings (@michael-s-molina)
- [#20185](https://github.com/apache/superset/pull/20185) docs(security): a typo: Gamma should be in quotes (@jimmytheneutrino)
- [#20146](https://github.com/apache/superset/pull/20146) chore: Implement global header in Dashboard (@geido)
- [#20174](https://github.com/apache/superset/pull/20174) chore: Disable flaky assert in reports cypress test (@kgabryje)
- [#20163](https://github.com/apache/superset/pull/20163) chore: change button name in Sql Lab (@AAfghahi)
- [#20157](https://github.com/apache/superset/pull/20157) chore: filter undefined operators (@zhaoyongjie)
- [#20140](https://github.com/apache/superset/pull/20140) chore(data-table): make formatted dttm the default (@villebro)
- [#20104](https://github.com/apache/superset/pull/20104) chore: fix INTHEWILD sort order and indentation (@villebro)
- [#20093](https://github.com/apache/superset/pull/20093) chore: Add the tnum font property to Table components (@geido)
- [#20103](https://github.com/apache/superset/pull/20103) docs: Update INTHEWILD.md (@fccoelho)
- [#20102](https://github.com/apache/superset/pull/20102) chore: Update aiohttp to 3.8.1 (@diegomedina248)
- [#20066](https://github.com/apache/superset/pull/20066) chore: Set limit for a query in execute_sql_statement (@AAfghahi)
- [#20032](https://github.com/apache/superset/pull/20032) chore: Change copy to Edit chart in Dashboard dropdown (@geido)
- [#20071](https://github.com/apache/superset/pull/20071) chore: Fix and enhance Applitools workflows (@geido)
- [#19966](https://github.com/apache/superset/pull/19966) test: make tabbed dashboard a little more complex (@ktmud)
- [#19976](https://github.com/apache/superset/pull/19976) perf(plugin-chart-table): Add memoization to avoid rerenders (@kgabryje)
- [#20044](https://github.com/apache/superset/pull/20044) chore: Create a generic header component for Explore and Dashboard (@kgabryje)
- [#20046](https://github.com/apache/superset/pull/20046) docs: add changelog and updating entries for 1.5.0 (@villebro)
- [#19962](https://github.com/apache/superset/pull/19962) chore: add doc link for db migration conflict warning (@ktmud)
- [#20034](https://github.com/apache/superset/pull/20034) chore: Changes the no-literal-colors lint rule to throw errors instead of warnings (@michael-s-molina)
- [#20031](https://github.com/apache/superset/pull/20031) chore: Run Applitools + Cypress nightly (@geido)
- [#20006](https://github.com/apache/superset/pull/20006) chore: Removes hard-coded colors from the plugins - iteration 2 (@michael-s-molina)
- [#19130](https://github.com/apache/superset/pull/19130) refactor: Refactor reports for Charts and Dashboards (@AAfghahi)
- [#20016](https://github.com/apache/superset/pull/20016) chore: Removes hard-coded colors - iteration 3 (@michael-s-molina)
- [#19870](https://github.com/apache/superset/pull/19870) docs: Detail front-end development instructions (@EBoisseauSierra)
- [#19971](https://github.com/apache/superset/pull/19971) docs: Add config for running on a WSGI HTTP server (@thinhnd2104)
- [#20008](https://github.com/apache/superset/pull/20008) chore: Upgrades Storybook from 6.4.19 to 6.4.22 (@michael-s-molina)
- [#20009](https://github.com/apache/superset/pull/20009) docs: typo in chart-params markdown file (@JakobMiksch)
- [#19923](https://github.com/apache/superset/pull/19923) chore: Removes hard-coded colors from the plugins - iteration 1 (@michael-s-molina)
- [#19954](https://github.com/apache/superset/pull/19954) chore: convert URLShortLinkButton to typescript (@ktmud)
- [#19929](https://github.com/apache/superset/pull/19929) chore: change subject name from no_name to named for PNGs in (@AAfghahi)
- [#19942](https://github.com/apache/superset/pull/19942) refactor(ReportModal): simplify state reducer and improve error handling (@ktmud)
- [#19770](https://github.com/apache/superset/pull/19770) chore: remove druid datasource from the config (@eschutho)
- [#19911](https://github.com/apache/superset/pull/19911) chore: Fix broken link for DouroECI (@mavimo)
- [#19951](https://github.com/apache/superset/pull/19951) chore: Adds the theme object to chart properties (@michael-s-molina)
- [#19813](https://github.com/apache/superset/pull/19813) chore: get embedded user with roles and permissions (@suddjian)
- [#19897](https://github.com/apache/superset/pull/19897) chore: Adds a storybook to FilterableTable (@michael-s-molina)
- [#19924](https://github.com/apache/superset/pull/19924) chore(reports): Improving logging around failed scheduled reports (@craig-rueda)
- [#19906](https://github.com/apache/superset/pull/19906) revert: "fix(sql lab): display the 'View Results' button consistently in the history tab on sync mode" (@Gwitchr)
- [#19916](https://github.com/apache/superset/pull/19916) chore(deps): bump react-virtualized-auto-sizer from 1.0.2 to 1.0.6 in /superset-frontend (@dependabot[bot])
- [#19888](https://github.com/apache/superset/pull/19888) chore(deps): bump cross-fetch from 3.1.4 to 3.1.5 in /docs (@dependabot[bot])
- [#19894](https://github.com/apache/superset/pull/19894) chore(deps-dev): bump eslint-plugin-prettier from 3.3.1 to 4.0.0 in /superset-frontend (@dependabot[bot])
- [#19602](https://github.com/apache/superset/pull/19602) docs: Added gtag to docusaurus (@AAfghahi)
- [#19878](https://github.com/apache/superset/pull/19878) chore(deps-dev): bump @storybook/client-api from 6.4.19 to 6.4.22 in /superset-frontend (@dependabot[bot])
- [#19821](https://github.com/apache/superset/pull/19821) test(native filter): refactor and add new test (@jinghua-qa)
- [#19613](https://github.com/apache/superset/pull/19613) chore: Update line-height in SliceHeaderControl (@geido)
- [#19616](https://github.com/apache/superset/pull/19616) chore: Update font-sizes in DatabaseModal (@geido)
- [#19866](https://github.com/apache/superset/pull/19866) chore: fix explore pills (@villebro)
- [#19872](https://github.com/apache/superset/pull/19872) chore: Update aiohttp>=3.7.4 in requirements (@hughhhh)
- [#19874](https://github.com/apache/superset/pull/19874) chore: bump rockset>=0.8.10, <0.9 (@hughhhh)
- [#19864](https://github.com/apache/superset/pull/19864) chore(deps): bump react-syntax-highlighter from 15.4.5 to 15.5.0 in /superset-frontend (@dependabot[bot])
- [#19828](https://github.com/apache/superset/pull/19828) chore: add custom eslint plugin to prevent translation variables (@stephenLYZ)
- [#19845](https://github.com/apache/superset/pull/19845) chore(deps): bump react-split from 2.0.9 to 2.0.14 in /superset-frontend (@dependabot[bot])
- [#19846](https://github.com/apache/superset/pull/19846) chore(deps): bump d3-svg-legend from 1.13.0 to 2.25.6 in /superset-frontend (@dependabot[bot])
- [#19847](https://github.com/apache/superset/pull/19847) chore(deps-dev): bump eslint-plugin-jsx-a11y from 6.4.1 to 6.5.1 in /superset-frontend (@dependabot[bot])
- [#19853](https://github.com/apache/superset/pull/19853) chore(frontend-tests): Spelling (@jsoref)
- [#19823](https://github.com/apache/superset/pull/19823) docs: updated links for country map scripts (@ktmud)
- [#19829](https://github.com/apache/superset/pull/19829) chore(deps-dev): bump babel-loader from 8.2.4 to 8.2.5 in /superset-frontend (@dependabot[bot])
- [#19830](https://github.com/apache/superset/pull/19830) chore(deps): bump react-hot-loader from 4.12.20 to 4.13.0 in /superset-frontend (@dependabot[bot])
- [#19403](https://github.com/apache/superset/pull/19403) chore(deps-dev): bump babel-loader from 8.2.2 to 8.2.4 in /superset-frontend (@dependabot[bot])
- [#19637](https://github.com/apache/superset/pull/19637) chore(deps): bump moment from 2.29.1 to 2.29.2 in /superset-frontend (@dependabot[bot])
- [#19681](https://github.com/apache/superset/pull/19681) chore(deps): bump async from 3.2.0 to 3.2.3 in /superset-frontend/cypress-base (@dependabot[bot])
- [#19680](https://github.com/apache/superset/pull/19680) chore(deps): bump async from 3.2.0 to 3.2.3 in /superset-websocket (@dependabot[bot])
- [#19020](https://github.com/apache/superset/pull/19020) chore(deps): bump url-parse from 1.5.7 to 1.5.10 in /superset-frontend (@dependabot[bot])
- [#17978](https://github.com/apache/superset/pull/17978) chore(deps): bump @types/d3-time from 1.1.1 to 3.0.0 in /superset-frontend (@dependabot[bot])
- [#19727](https://github.com/apache/superset/pull/19727) chore(deps): bump async from 2.6.3 to 2.6.4 in /docs (@dependabot[bot])
- [#19551](https://github.com/apache/superset/pull/19551) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-websocket (@dependabot[bot])
- [#19165](https://github.com/apache/superset/pull/19165) chore: simplify error messaging in database modal (@pkdotson)
- [#19790](https://github.com/apache/superset/pull/19790) chore: bump postgres from 10 to 14 (@dpgaspar)
- [#19480](https://github.com/apache/superset/pull/19480) chore: Update UPDATING.md (@john-bodley)
- [#19740](https://github.com/apache/superset/pull/19740) chore: fix grammar error (@zhaoyongjie)
- [#19703](https://github.com/apache/superset/pull/19703) chore(build): upgrade less-loader (@ktmud)
- [#19736](https://github.com/apache/superset/pull/19736) chore: Updates the Select code owners (@michael-s-molina)
- [#19715](https://github.com/apache/superset/pull/19715) docs(install): ubuntu default-libmysqlclient-dev (@cemremengu)
- [#19726](https://github.com/apache/superset/pull/19726) chore: bumping shillelagh (@AAfghahi)
- [#19699](https://github.com/apache/superset/pull/19699) chore: fix typo (@betodealmeida)
- [#19674](https://github.com/apache/superset/pull/19674) chore: upgrade Pillow (@betodealmeida)
- [#19647](https://github.com/apache/superset/pull/19647) chore(explore): Change labels "Group by"/"Series" to "Dimensions" (@kgabryje)
- [#19679](https://github.com/apache/superset/pull/19679) chore(deps): bump urijs from 1.19.8 to 1.19.11 in /superset-frontend (@dependabot[bot])
- [#19638](https://github.com/apache/superset/pull/19638) chore(deps): bump moment from 2.29.1 to 2.29.2 in /docs (@dependabot[bot])
- [#19617](https://github.com/apache/superset/pull/19617) chore: updated two github issue templates (@srinify)
- [#19666](https://github.com/apache/superset/pull/19666) chore: Remove TwoTone icons (@geido)
- [#19614](https://github.com/apache/superset/pull/19614) chore: Remove wrong usage of font-size in ExploreViewContainer (@geido)
- [#19593](https://github.com/apache/superset/pull/19593) chore: Update font-sizes in ReportModal (@geido)
- [#19611](https://github.com/apache/superset/pull/19611) chore: Update font-sizes in ImportModal (@geido)
- [#19615](https://github.com/apache/superset/pull/19615) chore: Update font-sizes in AlertReportModal (@geido)
- [#19620](https://github.com/apache/superset/pull/19620) chore: Update font-sizes in QueryPreviewModal (@geido)
- [#19641](https://github.com/apache/superset/pull/19641) chore: clean up dynamic translation strings (@villebro)
- [#19635](https://github.com/apache/superset/pull/19635) refactor: consistent migration tests organization (@ktmud)
- [#19634](https://github.com/apache/superset/pull/19634) test: freeze time for dashboard export test (@ktmud)
- [#19606](https://github.com/apache/superset/pull/19606) test(jinja): refactor to functional tests (@villebro)
- [#19587](https://github.com/apache/superset/pull/19587) chore: cleanup as unknown conversion (@zhaoyongjie)
- [#19562](https://github.com/apache/superset/pull/19562) refactor: Removes the CSS files from the Horizon plugin (@michael-s-molina)
- [#19563](https://github.com/apache/superset/pull/19563) refactor: Removes the CSS files from the Paired T-Test plugin (@michael-s-molina)
- [#19539](https://github.com/apache/superset/pull/19539) refactor: Removes the CSS files from the Parallel Coordinates plugin (@michael-s-molina)
- [#19521](https://github.com/apache/superset/pull/19521) refactor: Removes the CSS files from the Partition plugin (@michael-s-molina)
- [#19493](https://github.com/apache/superset/pull/19493) chore: Removes hard-coded colors from legacy-plugin-chart-sankey (@michael-s-molina)
- [#19462](https://github.com/apache/superset/pull/19462) chore: Remove FilterBox.less (@geido)
- [#19438](https://github.com/apache/superset/pull/19438) chore: Remove crud.less from Datasource (@geido)
- [#19517](https://github.com/apache/superset/pull/19517) chore: Enhance ReactChord style with theme vars (@geido)
- [#19463](https://github.com/apache/superset/pull/19463) chore: Remove TimeTable.less (@geido)
- [#19550](https://github.com/apache/superset/pull/19550) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-embedded-sdk (@dependabot[bot])
- [#19566](https://github.com/apache/superset/pull/19566) chore(deps): bump node-forge from 1.2.1 to 1.3.1 in /docs (@dependabot[bot])
- [#19552](https://github.com/apache/superset/pull/19552) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /docs (@dependabot[bot])
- [#19549](https://github.com/apache/superset/pull/19549) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-frontend/cypress-base (@dependabot[bot])
- [#19559](https://github.com/apache/superset/pull/19559) docs: update the typo in the documentation (@fatosmorina)
- [#19538](https://github.com/apache/superset/pull/19538) refactor: Removes the CSS files from the Country Map plugin (@michael-s-molina)
- [#19536](https://github.com/apache/superset/pull/19536) chore: Removes hard-coded opacity and spacing from the BigNumber plugin (@michael-s-molina)
- [#19494](https://github.com/apache/superset/pull/19494) refactor: Removes the CSS files from the Sankey Loop plugin (@michael-s-molina)
- [#19492](https://github.com/apache/superset/pull/19492) chore: Remove Legacy Force Directed viz plugin (@geido)
- [#19524](https://github.com/apache/superset/pull/19524) chore: Deprecating /my_queries endpoint (@AAfghahi)
- [#19467](https://github.com/apache/superset/pull/19467) chore(Explore): Change text when saving a chart in a new dashboard (@geido)
- [#19526](https://github.com/apache/superset/pull/19526) chore(database): Creating helper make_url_safe to wrap potential errors (@craig-rueda)
- [#19415](https://github.com/apache/superset/pull/19415) chore: Remove Control.less in Explore (@geido)
- [#19413](https://github.com/apache/superset/pull/19413) chore: Remove unused less file from profile (@geido)
- [#19460](https://github.com/apache/superset/pull/19460) chore: Switch to gender neutral terms (@inclusive-coding-bot)
- [#19486](https://github.com/apache/superset/pull/19486) refactor: Removes the CSS files from the Treemap plugin (@michael-s-molina)
- [#19488](https://github.com/apache/superset/pull/19488) refactor: Removes the CSS files from the Sunburst plugin (@michael-s-molina)
- [#19490](https://github.com/apache/superset/pull/19490) chore: Add theme color to ParallelCoordinates (@geido)
- [#19442](https://github.com/apache/superset/pull/19442) chore: Remove FilterbaleTableStyles.less (@geido)
- [#19441](https://github.com/apache/superset/pull/19441) chore: Remove StyledQueryButton.less (@geido)
- [#19473](https://github.com/apache/superset/pull/19473) refactor: Removes the CSS files from the Rose plugin (@michael-s-molina)
- [#19466](https://github.com/apache/superset/pull/19466) chore: Removes hard-coded colors from legacy-plugin-chart-world-map (@michael-s-molina)
- [#19465](https://github.com/apache/superset/pull/19465) refactor: Removes the CSS files from the DeckGL plugin (@michael-s-molina)
- [#19440](https://github.com/apache/superset/pull/19440) chore: Remove index.less from showSavedQuery (@geido)
- [#19230](https://github.com/apache/superset/pull/19230) chore!: remove `ROW_LEVEL_SECURITY` feature flag (permanently enable) (@suddjian)
- [#19361](https://github.com/apache/superset/pull/19361) chore: remove deprecated config keys and endpoints code 2.0 (@pkdotson)
- [#19261](https://github.com/apache/superset/pull/19261) chore: remove old alerts and configs keys (@pkdotson)
- [#19168](https://github.com/apache/superset/pull/19168) chore: bump celery and Flask (@dpgaspar)
- [#19049](https://github.com/apache/superset/pull/19049) chore: Remove logo forced width (@geido)
- [#19274](https://github.com/apache/superset/pull/19274) chore: remove PUBLIC_ROLE_LIKE_GAMMA deprecated config key (@dpgaspar)
- [#19273](https://github.com/apache/superset/pull/19273) chore: remove deprecated celery cli (@dpgaspar)
- [#19262](https://github.com/apache/superset/pull/19262) chore: update updating with druid no sql deprecation (@eschutho)
- [#19083](https://github.com/apache/superset/pull/19083) chore!: update mutator to take kwargs (@eschutho)
- [#19231](https://github.com/apache/superset/pull/19231) chore!: remove `ENABLE_REACT_CRUD_VIEWS` feature flag (permanently enable) (@suddjian)
- [#19241](https://github.com/apache/superset/pull/19241) chore(superset 2.0): remove front-end deprecated code (@graceguo-supercat)
- [#19107](https://github.com/apache/superset/pull/19107) chore: turn on SQLLAB_BACKEND_PERSISTENCE by default (@ktmud)
- [#19142](https://github.com/apache/superset/pull/19142) chore!: turn on Versioned Export in config.py (@AAfghahi)
- [#19108](https://github.com/apache/superset/pull/19108) chore: Update UPDATING.md with info about flipping dnd feature flag (@kgabryje)
- [#19146](https://github.com/apache/superset/pull/19146) chore!: Remove remove SQLALCHEMY_DOCS_URL and SQLALCHEMY_DISPLAY_TEXT from the config from the config (@hughhhh)
- [#19017](https://github.com/apache/superset/pull/19017) chore: Deprecate Python 3.7 (@john-bodley)
- [#19113](https://github.com/apache/superset/pull/19113) chore(config): Migrating `ENABLE_JAVASCRIPT_CONTROLS` from app config to a feature flag (@rusackas)
- [#19046](https://github.com/apache/superset/pull/19046) chore(explore): Set Drag&Drop feature flags to True by default (@kgabryje)
- [#19016](https://github.com/apache/superset/pull/19016) chore: Adding PR to Updating.md (@AAfghahi)
- [#18970](https://github.com/apache/superset/pull/18970) chore: Change Dataset legacy editor flag to true (@AAfghahi)
### 1.5.1 (Thu May 26 14:45:20 2022 +0300)

View File

@@ -24,6 +24,24 @@ assists people when migrating to a new version.
## Next
- [22022](https://github.com/apache/superset/pull/22022): HTTP API endpoints `/superset/approve` and `/superset/request_access` have been deprecated and their HTTP methods were changed from GET to POST
- [21895](https://github.com/apache/superset/pull/21895): Markdown components had their security increased by adhering to the same sanitization process enforced by Github. This means that some HTML elements found in markdowns are not allowed anymore due to the security risks they impose. If you're deploying Superset in a trusted environment and wish to use some of the blocked elements, then you can use the HTML_SANITIZATION_SCHEMA_EXTENSIONS configuration to extend the default sanitization schema. There's also the option to disable HTML sanitization using the HTML_SANITIZATION configuration but we do not recommend this approach because of the security risks. Given the provided configurations, we don't view the improved sanitization as a breaking change but as a security patch.
- [20606](https://github.com/apache/superset/pull/20606): When user clicks on chart title or "Edit chart" button in Dashboard page, Explore opens in the same tab. Clicking while holding cmd/ctrl opens Explore in a new tab. To bring back the old behaviour (always opening Explore in a new tab), flip feature flag `DASHBOARD_EDIT_CHART_IN_NEW_TAB` to `True`.
- [20799](https://github.com/apache/superset/pull/20799): Presto and Trino engine will now display tracking URL for running queries in SQL Lab. If for some reason you don't want to show the tracking URL (for example, when your data warehouse hasn't enable access for to Presto or Trino UI), update `TRACKING_URL_TRANSFORMER` in `config.py` to return `None`.
- [21002](https://github.com/apache/superset/pull/21002): Support Python 3.10 and bump pandas 1.4 and pyarrow 6.
- [21163](https://github.com/apache/superset/pull/21163): When `GENERIC_CHART_AXES` feature flags set to `True`, the Time Grain control will move below the X-Axis control.
- [21284](https://github.com/apache/superset/pull/21284): The non-functional `MAX_TABLE_NAMES` config key has been removed.
### Breaking Changes
- [21765](https://github.com/apache/superset/pull/21765): For deployments that have enabled the "ALERT_REPORTS" feature flag, Gamma users will no longer have read and write access to Alerts & Reports by default. To give Gamma users the ability to schedule reports from the Dashboard and Explore view like before, create an additional role with "can read on ReportSchedule" and "can write on ReportSchedule" permissions. To further give Gamma users access to the "Alerts & Reports" menu and CRUD view, add "menu access on Manage" and "menu access on Alerts & Report" permissions to the role.
### Potential Downtime
### Other
## 2.0.0
- [19046](https://github.com/apache/superset/pull/19046): Enables the drag and drop interface in Explore control panel by default. Flips `ENABLE_EXPLORE_DRAG_AND_DROP` and `ENABLE_DND_WITH_CLICK_UX` feature flags to `True`.
- [18936](https://github.com/apache/superset/pull/18936): Removes legacy SIP-15 interim logic/flags—specifically the `SIP_15_ENABLED`, `SIP_15_GRACE_PERIOD_END`, `SIP_15_DEFAULT_TIME_RANGE_ENDPOINTS`, and `SIP_15_TOAST_MESSAGE` flags. Time range endpoints are no longer configurable and strictly adhere to the `[start, end)` paradigm, i.e., inclusive of the start and exclusive of the end. Additionally this change removes the now obsolete `time_range_endpoints` from the form-data and resulting in the cache being busted.
- [19570](https://github.com/apache/superset/pull/19570): makes [sqloxide](https://pypi.org/project/sqloxide/) optional so the SIP-68 migration can be run on aarch64. If the migration is taking too long installing sqloxide manually should improve the performance.
@@ -46,10 +64,6 @@ assists people when migrating to a new version.
- [19017](https://github.com/apache/superset/pull/19017): Removes Python 3.7 support.
- [18970](https://github.com/apache/superset/pull/18970): The `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag is now `True` by default which disables the legacy datasource editor from being shown in the client.
### Potential Downtime
### Other
## 1.5.0
### Breaking Changes
@@ -82,6 +96,7 @@ assists people when migrating to a new version.
## 1.4.1
### Breaking Changes
- [17984](https://github.com/apache/superset/pull/17984): Default Flask SECRET_KEY has changed for security reasons. You should always override with your own secret. Set `PREVIOUS_SECRET_KEY` (ex: PREVIOUS_SECRET_KEY = "\2\1thisismyscretkey\1\2\\e\\y\\y\\h") with your previous key and use `superset re-encrypt-secrets` to rotate you current secrets
### Potential Downtime

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
x-superset-image: &superset-image apache/superset:${TAG:-latest-dev}
x-superset-image: &superset-image apache/superset:2.0.0
x-superset-user: &superset-user root
x-superset-depends-on: &superset-depends-on
- db

View File

@@ -69,6 +69,16 @@ REDIS_RESULTS_DB = get_env_variable("REDIS_RESULTS_DB", "1")
RESULTS_BACKEND = FileSystemCache("/app/superset_home/sqllab")
CACHE_CONFIG = {
"CACHE_TYPE": "redis",
"CACHE_DEFAULT_TIMEOUT": 300,
"CACHE_KEY_PREFIX": "superset_",
"CACHE_REDIS_HOST": REDIS_HOST,
"CACHE_REDIS_PORT": REDIS_PORT,
"CACHE_REDIS_DB": REDIS_RESULTS_DB,
}
DATA_CACHE_CONFIG = CACHE_CONFIG
class CeleryConfig(object):
BROKER_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_CELERY_DB}"

View File

@@ -28,6 +28,8 @@ gunicorn \
--threads ${SERVER_THREADS_AMOUNT:-20} \
--timeout ${GUNICORN_TIMEOUT:-60} \
--keep-alive ${GUNICORN_KEEPALIVE:-2} \
--max-requests ${WORKER_MAX_REQUESTS:-0} \
--max-requests-jitter ${WORKER_MAX_REQUESTS_JITTER:-0} \
--limit-request-line ${SERVER_LIMIT_REQUEST_LINE:-0} \
--limit-request-field_size ${SERVER_LIMIT_REQUEST_FIELD_SIZE:-0} \
"${FLASK_APP}"

View File

@@ -14,7 +14,7 @@ You also need to install MySQL or [MariaDB](https://mariadb.com/downloads).
Ensure that you are using Python version 3.8 or 3.9, then proceed with:
````bash
```bash
# Create a virtual environment and activate it (recommended)
python3 -m venv venv # setup a python3 virtualenv
source venv/bin/activate
@@ -47,18 +47,18 @@ Or you can install via our Makefile
```bash
# Create a virtual environment and activate it (recommended)
$ python3 -m venv venv # setup a python3 virtualenv
$ source venv/bin/activate
python3 -m venv venv # setup a python3 virtualenv
source venv/bin/activate
# install pip packages + pre-commit
$ make install
make install
# Install superset pip packages and setup env only
$ make superset
make superset
# Setup pre-commit only
$ make pre-commit
````
make pre-commit
```
**Note: the FLASK_APP env var should not need to be set, as it's currently controlled
via `.flaskenv`, however if needed, it should be set to `superset.app:create_app()`**
@@ -103,4 +103,5 @@ app.logger.info(form_data)
```
### Frontend Assets
See [Building Frontend Assets Locally](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#frontend)

View File

@@ -126,6 +126,7 @@ SLACK_API_TOKEN = "xoxb-"
# Email configuration
SMTP_HOST = "smtp.sendgrid.net" #change to your host
SMTP_STARTTLS = True
SMTP_SSL_SERVER_AUTH = True # If your using an SMTP server with a valid certificate
SMTP_SSL = False
SMTP_USER = "your_user"
SMTP_PORT = 2525 # your port eg. 587

View File

@@ -129,7 +129,7 @@ Finish installing by running through the following commands:
```
# Create an admin user in your metadata database (use `admin` as username to be able to load the examples)
$ export FLASK_APP=superset
export FLASK_APP=superset
superset fab create-admin
# Load some data to play with

View File

@@ -44,7 +44,7 @@ all of the required dependencies. Docker Desktop [recently added support for Win
following command:
```bash
$ git clone https://github.com/apache/superset.git
git clone https://github.com/apache/superset.git
```
Once that command completes successfully, you should see a new `superset` folder in your
@@ -55,14 +55,14 @@ current directory.
Navigate to the folder you created in step 1:
```bash
$ cd superset
cd superset
```
When working on master branch, run the following commands:
```bash
$ docker-compose -f docker-compose-non-dev.yml pull
$ docker-compose -f docker-compose-non-dev.yml up
docker-compose -f docker-compose-non-dev.yml pull
docker-compose -f docker-compose-non-dev.yml up
```
Alternatively, you can also run a specific version of Superset by first checking out
@@ -70,9 +70,9 @@ the branch/tag, and then starting `docker-compose` with the `TAG` variable.
For example, to run the 1.4.0 version, run the following commands:
```bash
% git checkout 1.4.0
$ TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml pull
$ TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml up
git checkout 1.4.0
TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml pull
TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml up
```
You should see a wall of logging output from the containers being launched on your machine. Once

View File

@@ -13,7 +13,7 @@ geospatial charts.
Here are a **few different ways you can get started with Superset**:
- Download the [source from Apache Foundation's website](https://dist.apache.org/repos/dist/release/superset/1.4.1/)
- Download the [source from Apache Foundation's website](https://dist.apache.org/repos/dist/release/superset/)
- Download the latest Superset version from [Pypi here](https://pypi.org/project/apache-superset/)
- Setup Superset locally with one command
using [Docker Compose](installation/installing-superset-using-docker-compose)

View File

@@ -131,6 +131,28 @@ For example, the filters `client_id=4` and `client_id=5`, applied to a role,
will result in users of that role having `client_id=4` AND `client_id=5`
added to their query, which can never be true.
### Content Security Policiy (CSP)
[Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is an added
layer of security that helps to detect and mitigate certain types of attacks, including
Cross-Site Scripting (XSS) and data injection attacks.
CSP makes it possible for server administrators to reduce or eliminate the vectors by which XSS can
occur by specifying the domains that the browser should consider to be valid sources of executable scripts.
A CSP compatible browser will then only execute scripts loaded in source files received from those allowed domains,
ignoring all other scripts (including inline scripts and event-handling HTML attributes).
A policy is described using a series of policy directives, each of which describes the policy for
a certain resource type or policy area. You can check possible directives
[here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy).
It's extremely important to correclty configure a Content Security Policy when deploying Superset to
prevent many types of attacks. For that matter, Superset provides the ` TALISMAN_CONFIG` key in `config.py`
where admnistrators can define the policy. When running in production mode, Superset will check for the presence
of a policy and if it's not able to find one, it will issue a warning with the security risks. For environments
where CSP policies are defined outside of Superset using other software, administrators can disable
the warning using the `CONTENT_SECURITY_POLICY_WARNING` key in `config.py`.
### Reporting Security Vulnerabilities
Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its

View File

@@ -60,7 +60,7 @@ colorama==0.4.4
# via
# apache-superset
# flask-appbuilder
convertdate==2.3.2
convertdate==2.4.0
# via holidays
cron-descriptor==1.2.24
# via apache-superset
@@ -86,7 +86,7 @@ flask==2.0.3
# flask-migrate
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.0.0
flask-appbuilder==4.1.3
# via apache-superset
flask-babel==1.0.0
# via flask-appbuilder
@@ -126,7 +126,7 @@ gunicorn==20.1.0
# via apache-superset
hashids==1.3.1
# via apache-superset
holidays==0.10.3
holidays==0.14.2
# via apache-superset
humanize==3.11.0
# via apache-superset

View File

@@ -32,4 +32,4 @@ superset init
echo "Running tests"
pytest --durations-min=2 --maxfail=1 --cov-report= --cov=superset "$@"
pytest --durations-min=2 --maxfail=1 --cov-report= --cov=superset ./tests/integration_tests "$@"

View File

@@ -77,7 +77,7 @@ setup(
"cryptography>=3.3.2",
"deprecation>=2.1.0, <2.2.0",
"flask>=2.0.0, <3.0.0",
"flask-appbuilder>=4.0.0, <5.0.0",
"flask-appbuilder>=4.1.3, <5.0.0",
"flask-caching>=1.10.0",
"flask-compress",
"flask-talisman",
@@ -88,7 +88,7 @@ setup(
"graphlib-backport",
"gunicorn>=20.1.0",
"hashids>=1.3.1, <2",
"holidays==0.10.3", # PINNED! https://github.com/dr-prodigy/python-holidays/issues/406
"holidays==0.14.2",
"humanize",
"isodate",
"markdown>=3.0",
@@ -149,7 +149,7 @@ setup(
"impala": ["impyla>0.16.2, <0.17"],
"kusto": ["sqlalchemy-kusto>=1.0.1, <2"],
"kylin": ["kylinpy>=2.8.1, <2.9"],
"mmsql": ["pymssql>=2.1.4, <2.2"],
"mssql": ["pymssql>=2.1.4, <2.2"],
"mysql": ["mysqlclient>=2.1.0, <3"],
"oracle": ["cx-Oracle>8.0.0, <8.1"],
"pinot": ["pinotdb>=0.3.3, <0.4"],

View File

@@ -46,7 +46,7 @@ describe('Dashboard edit markdown', () => {
cy.get('[data-test="dashboard-markdown-editor"]')
.should(
'have.text',
'✨Markdown✨Markdown✨MarkdownClick here to edit markdown',
'✨Markdown\n✨Markdown\n✨Markdown\n\nClick here to edit markdown',
)
.click();

View File

@@ -204,7 +204,7 @@ describe('Time range filter', () => {
});
});
it('Custom time_range params', () => {
xit('Custom time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "superset",
"version": "0.0.0dev",
"version": "2.0.1",
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
"keywords": [
"big",
@@ -126,7 +126,7 @@
"d3-array": "^1.2.4",
"d3-color": "^1.2.0",
"d3-scale": "^2.1.2",
"dom-to-image": "^2.6.0",
"dom-to-image-more": "^2.10.1",
"emotion-rgba": "0.0.9",
"fast-glob": "^3.2.7",
"fontsource-fira-code": "^4.0.0",
@@ -170,7 +170,6 @@
"react-jsonschema-form": "^1.2.0",
"react-lines-ellipsis": "^0.15.0",
"react-loadable": "^5.5.0",
"react-markdown": "^4.3.1",
"react-redux": "^7.2.0",
"react-resize-detector": "^6.7.6",
"react-reverse-portal": "^2.0.1",
@@ -237,7 +236,6 @@
"@testing-library/react-hooks": "^5.0.3",
"@testing-library/user-event": "^12.7.0",
"@types/classnames": "^2.2.10",
"@types/dom-to-image": "^2.6.0",
"@types/enzyme": "^3.10.5",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/fetch-mock": "^7.3.2",

View File

@@ -34,7 +34,7 @@ export const annotationsAndLayersControls: ControlPanelSectionConfig = {
label: '',
default: annotationLayers,
description: t('Annotation Layers'),
renderTrigger: true,
renderTrigger: false,
},
},
],

View File

@@ -363,6 +363,10 @@ export interface ControlPanelConfig {
standardizedFormData: StandardizedFormDataInterface;
},
) => QueryFormData;
updateStandardizedState?: (
prevState: StandardizedState,
currState: StandardizedState,
) => StandardizedState;
}
export type ControlOverrides = {

View File

@@ -37,16 +37,21 @@ export const getOpacity = (
extremeValue: number,
minOpacity = MIN_OPACITY_BOUNDED,
maxOpacity = MAX_OPACITY,
) =>
extremeValue === cutoffPoint
? maxOpacity
: round(
Math.abs(
((maxOpacity - minOpacity) / (extremeValue - cutoffPoint)) *
(value - cutoffPoint),
) + minOpacity,
2,
);
) => {
if (extremeValue === cutoffPoint) {
return maxOpacity;
}
return Math.min(
maxOpacity,
round(
Math.abs(
((maxOpacity - minOpacity) / (extremeValue - cutoffPoint)) *
(value - cutoffPoint),
) + minOpacity,
2,
),
);
};
export const getColorFunction = (
{

View File

@@ -50,6 +50,9 @@ describe('getOpacity', () => {
expect(getOpacity(100, 100, 50)).toEqual(0.05);
expect(getOpacity(100, 100, 100, 0, 0.8)).toEqual(0.8);
expect(getOpacity(100, 100, 50, 0, 1)).toEqual(0);
expect(getOpacity(999, 100, 50, 0, 1)).toEqual(1);
expect(getOpacity(100, 100, 50, 0.99, 1)).toEqual(0.99);
expect(getOpacity(99, 100, 50, 0, 1)).toEqual(0.02);
});
});

View File

@@ -2,6 +2,19 @@
"name": "@superset-ui/core",
"version": "0.18.25",
"description": "Superset UI core",
"keywords": [
"superset"
],
"homepage": "https://github.com/apache-superset/superset-ui#readme",
"bugs": {
"url": "https://github.com/apache-superset/superset-ui/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/apache-superset/superset-ui.git"
},
"license": "Apache-2.0",
"author": "Superset",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
@@ -9,28 +22,6 @@
"esm",
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/apache-superset/superset-ui.git"
},
"keywords": [
"superset"
],
"author": "Superset",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/apache-superset/superset-ui/issues"
},
"homepage": "https://github.com/apache-superset/superset-ui#readme",
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@emotion/styled": "^11.3.0",
"resize-observer-polyfill": "1.5.1",
"fetch-mock": "^6.5.2",
"jest-mock-console": "^1.0.0"
},
"dependencies": {
"@babel/runtime": "^7.1.2",
"@types/d3-format": "^1.3.0",
@@ -38,13 +29,14 @@
"@types/d3-scale": "^2.1.1",
"@types/d3-time": "^3.0.0",
"@types/d3-time-format": "^2.1.0",
"@types/enzyme": "^3.10.5",
"@types/fetch-mock": "^7.3.3",
"@types/lodash": "^4.14.149",
"@types/math-expression-evaluator": "^1.2.1",
"@types/node": "^18.0.0",
"@types/prop-types": "^15.7.2",
"@types/rison": "0.0.6",
"@types/seedrandom": "^2.4.28",
"@types/fetch-mock": "^7.3.3",
"@types/enzyme": "^3.10.5",
"@types/prop-types": "^15.7.2",
"@vx/responsive": "^0.0.199",
"csstype": "^2.6.4",
"d3-format": "^1.3.2",
@@ -58,12 +50,20 @@
"math-expression-evaluator": "^1.3.8",
"pretty-ms": "^7.0.0",
"react-error-boundary": "^1.2.5",
"react-markdown": "^4.3.1",
"react-markdown": "^8.0.3",
"rehype-raw": "^6.1.1",
"rehype-sanitize": "^5.0.1",
"reselect": "^4.0.0",
"rison": "^0.1.1",
"seedrandom": "^3.0.5",
"whatwg-fetch": "^3.0.0"
},
"devDependencies": {
"@emotion/styled": "^11.3.0",
"fetch-mock": "^6.5.2",
"jest-mock-console": "^1.0.0",
"resize-observer-polyfill": "1.5.1"
},
"peerDependencies": {
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.4.1",
@@ -71,8 +71,11 @@
"@types/react": "*",
"@types/react-loadable": "*",
"@types/tinycolor2": "*",
"tinycolor2": "*",
"react": "^16.13.1",
"react-loadable": "^5.5.0"
"react-loadable": "^5.5.0",
"tinycolor2": "*"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -41,7 +41,7 @@ export default function FallbackComponent({
>
<div>
<div>
<b>Oops! An error occured!</b>
<b>Oops! An error occurred!</b>
</div>
<code>{error ? error.toString() : 'Unknown Error'}</code>
</div>

View File

@@ -16,38 +16,44 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import ReactMarkdown, { MarkdownAbstractSyntaxTree } from 'react-markdown';
// @ts-ignore no types available
import htmlParser from 'react-markdown/plugins/html-parser';
import React, { useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
import rehypeRaw from 'rehype-raw';
import { merge } from 'lodash';
import { FeatureFlag, isFeatureEnabled } from '../utils';
interface SafeMarkdownProps {
source: string;
htmlSanitization?: boolean;
htmlSchemaOverrides?: typeof defaultSchema;
}
function isSafeMarkup(node: MarkdownAbstractSyntaxTree) {
return node.type === 'html' && node.value
? /href="(javascript|vbscript|file):.*"/gim.test(node.value) === false
: true;
}
function SafeMarkdown({
source,
htmlSanitization = true,
htmlSchemaOverrides = {},
}: SafeMarkdownProps) {
const displayHtml = isFeatureEnabled(FeatureFlag.DISPLAY_MARKDOWN_HTML);
const escapeHtml = isFeatureEnabled(FeatureFlag.ESCAPE_MARKDOWN_HTML);
function SafeMarkdown({ source }: SafeMarkdownProps) {
const rehypePlugins = useMemo(() => {
const rehypePlugins: any = [];
if (displayHtml && !escapeHtml) {
rehypePlugins.push(rehypeRaw);
if (htmlSanitization) {
const schema = merge(defaultSchema, htmlSchemaOverrides);
rehypePlugins.push([rehypeSanitize, schema]);
}
}
return rehypePlugins;
}, [displayHtml, escapeHtml, htmlSanitization, htmlSchemaOverrides]);
// React Markdown escapes HTML by default
return (
<ReactMarkdown
source={source}
escapeHtml={isFeatureEnabled(FeatureFlag.ESCAPE_MARKDOWN_HTML)}
skipHtml={!isFeatureEnabled(FeatureFlag.DISPLAY_MARKDOWN_HTML)}
allowNode={isSafeMarkup}
astPlugins={[
htmlParser({
isValidNode: (node: MarkdownAbstractSyntaxTree) =>
node.type !== 'script',
}),
]}
/>
<ReactMarkdown rehypePlugins={rehypePlugins} skipHtml={!displayHtml}>
{source}
</ReactMarkdown>
);
}

View File

@@ -45,6 +45,7 @@ const SupersetClient: SupersetClientInterface = {
init: force => getInstance().init(force),
isAuthenticated: () => getInstance().isAuthenticated(),
post: request => getInstance().post(request),
postForm: (...args) => getInstance().postForm(...args),
put: request => getInstance().put(request),
reAuthenticate: () => getInstance().reAuthenticate(),
request: request => getInstance().request(request),

View File

@@ -119,6 +119,36 @@ export default class SupersetClientClass {
return this.getCSRFToken();
}
async postForm(url: string, payload: Record<string, any>, target = '_blank') {
if (url) {
await this.ensureAuth();
const hiddenForm = document.createElement('form');
hiddenForm.action = url;
hiddenForm.method = 'POST';
hiddenForm.target = target;
const payloadWithToken: Record<string, any> = {
...payload,
csrf_token: this.csrfToken!,
};
if (this.guestToken) {
payloadWithToken.guest_token = this.guestToken;
}
Object.entries(payloadWithToken).forEach(([key, value]) => {
const data = document.createElement('input');
data.type = 'hidden';
data.name = key;
data.value = value;
hiddenForm.appendChild(data);
});
document.body.appendChild(hiddenForm);
hiddenForm.submit();
document.body.removeChild(hiddenForm);
}
}
async reAuthenticate() {
return this.init(true);
}

View File

@@ -146,6 +146,7 @@ export interface SupersetClientInterface
| 'delete'
| 'get'
| 'post'
| 'postForm'
| 'put'
| 'request'
| 'init'

View File

@@ -178,9 +178,9 @@ export function isTimeseriesAnnotationResult(
}
export function isRecordAnnotationResult(
result: AnnotationResult,
result: any,
): result is RecordAnnotationResult {
return 'columns' in result && 'records' in result;
return Array.isArray(result?.columns) && Array.isArray(result?.records);
}
export type AnnotationData = { [key: string]: AnnotationResult };

View File

@@ -47,7 +47,7 @@ export interface ChartDataResponseResult {
/**
* Data for the annotation layer.
*/
annotation_data: AnnotationData[] | null;
annotation_data: AnnotationData | null;
cache_key: string | null;
cache_timeout: number | null;
cached_dttm: string | null;

View File

@@ -56,7 +56,7 @@ export default class Translator {
*/
addTranslation(key: string, texts: ReadonlyArray<string>) {
const translations = this.i18n.options.locale_data.superset;
if (key in translations) {
if (process.env.WEBPACK_MODE !== 'test' && key in translations) {
logging.warn(`Duplicate translation key "${key}", will override.`);
}
translations[key] = texts;

View File

@@ -121,7 +121,7 @@ describe('SuperChart', () => {
);
await new Promise(resolve => setImmediate(resolve));
wrapper.update();
expect(wrapper.text()).toContain('Oops! An error occured!');
expect(wrapper.text()).toContain('Oops! An error occurred!');
});
it('renders custom FallbackComponent', () => {
expectedErrors = 1;

View File

@@ -30,21 +30,23 @@ describe('SupersetClient', () => {
afterEach(SupersetClient.reset);
it('exposes reset, configure, init, get, post, isAuthenticated, and reAuthenticate methods', () => {
it('exposes reset, configure, init, get, post, postForm, isAuthenticated, and reAuthenticate methods', () => {
expect(typeof SupersetClient.configure).toBe('function');
expect(typeof SupersetClient.init).toBe('function');
expect(typeof SupersetClient.get).toBe('function');
expect(typeof SupersetClient.post).toBe('function');
expect(typeof SupersetClient.postForm).toBe('function');
expect(typeof SupersetClient.isAuthenticated).toBe('function');
expect(typeof SupersetClient.reAuthenticate).toBe('function');
expect(typeof SupersetClient.request).toBe('function');
expect(typeof SupersetClient.reset).toBe('function');
});
it('throws if you call init, get, post, isAuthenticated, or reAuthenticate before configure', () => {
it('throws if you call init, get, post, postForm, isAuthenticated, or reAuthenticate before configure', () => {
expect(SupersetClient.init).toThrow();
expect(SupersetClient.get).toThrow();
expect(SupersetClient.post).toThrow();
expect(SupersetClient.postForm).toThrow();
expect(SupersetClient.isAuthenticated).toThrow();
expect(SupersetClient.reAuthenticate).toThrow();
expect(SupersetClient.request).toThrow();

View File

@@ -605,4 +605,107 @@ describe('SupersetClientClass', () => {
}
});
});
describe('.postForm()', () => {
const protocol = 'https:';
const host = 'host';
const mockPostFormEndpoint = '/post_form/url';
const mockPostFormUrl = `${protocol}//${host}${mockPostFormEndpoint}`;
const guestToken = 'test-guest-token';
const postFormPayload = { number: 123, array: [1, 2, 3] };
let authSpy: jest.SpyInstance;
let client: SupersetClientClass;
let appendChild: any;
let removeChild: any;
let submit: any;
let createElement: any;
beforeEach(async () => {
client = new SupersetClientClass({ protocol, host });
authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth');
await client.init();
appendChild = jest.fn();
removeChild = jest.fn();
submit = jest.fn();
createElement = jest.fn(() => ({
appendChild: jest.fn(),
submit,
}));
document.createElement = createElement as any;
document.body.appendChild = appendChild;
document.body.removeChild = removeChild;
});
afterEach(() => {
jest.restoreAllMocks();
});
it('makes postForm request', async () => {
await client.postForm(mockPostFormUrl, {});
const hiddenForm = createElement.mock.results[0].value;
const csrfTokenInput = createElement.mock.results[1].value;
expect(createElement.mock.calls).toHaveLength(2);
expect(hiddenForm.action).toBe(mockPostFormUrl);
expect(hiddenForm.method).toBe('POST');
expect(hiddenForm.target).toBe('_blank');
expect(csrfTokenInput.type).toBe('hidden');
expect(csrfTokenInput.name).toBe('csrf_token');
expect(csrfTokenInput.value).toBe(1234);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('makes postForm request with guest token', async () => {
client = new SupersetClientClass({ protocol, host, guestToken });
await client.init();
await client.postForm(mockPostFormUrl, {});
const guestTokenInput = createElement.mock.results[2].value;
expect(createElement.mock.calls).toHaveLength(3);
expect(guestTokenInput.type).toBe('hidden');
expect(guestTokenInput.name).toBe('guest_token');
expect(guestTokenInput.value).toBe(guestToken);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('makes postForm request with payload', async () => {
await client.postForm(mockPostFormUrl, { form_data: postFormPayload });
const postFormPayloadInput = createElement.mock.results[1].value;
expect(createElement.mock.calls).toHaveLength(3);
expect(postFormPayloadInput.type).toBe('hidden');
expect(postFormPayloadInput.name).toBe('form_data');
expect(postFormPayloadInput.value).toBe(postFormPayload);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(submit.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('should do nothing when url is empty string', async () => {
const result = await client.postForm('', {});
expect(result).toBeUndefined();
expect(createElement.mock.calls).toHaveLength(0);
expect(appendChild.mock.calls).toHaveLength(0);
expect(removeChild.mock.calls).toHaveLength(0);
expect(authSpy).toHaveBeenCalledTimes(0);
});
});
});

View File

@@ -41,10 +41,12 @@ describe('Translator', () => {
spy.mockImplementation((info: any) => {
throw new Error(info);
});
process.env.WEBPACK_MODE = 'production';
});
afterAll(() => {
spy.mockRestore();
process.env.WEBPACK_MODE = 'test';
});
describe('new Translator(config)', () => {

View File

@@ -31,6 +31,7 @@ import {
MixedTimeseriesTransformProps,
} from '@superset-ui/plugin-chart-echarts';
import data from '../Timeseries/data';
import negativeNumData from './negativeData';
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
new EchartsTimeseriesChartPlugin()
@@ -57,6 +58,8 @@ export const Timeseries = ({ width, height }) => {
Boston: row.Boston,
}))
.filter(row => !!row.Boston),
colnames: ['__timestamp'],
coltypes: [2],
},
{
data: data
@@ -82,8 +85,13 @@ export const Timeseries = ({ width, height }) => {
logAxis: boolean('Log axis', false),
xAxisTimeFormat: 'smart_date',
tooltipTimeFormat: 'smart_date',
yAxisFormat: 'SMART_NUMBER',
yAxisFormat: select(
'y-axis format',
['$,.2f', 'SMART_NUMBER'],
'$,.2f',
),
yAxisTitle: text('Y Axis title', ''),
yAxisIndexB: select('yAxisIndexB', [0, 1], 1),
minorSplitLine: boolean('Query 1: Minor splitline', false),
seriesType: select(
'Query 1: Line type',
@@ -105,7 +113,61 @@ export const Timeseries = ({ width, height }) => {
markerEnabledB: boolean('Query 2: Enable markers', false),
markerSizeB: number('Query 2: Marker Size', 6),
opacityB: number('Query 2: Opacity', 0.2),
showValue: true,
}}
/>
);
};
export const WithNegativeNumbers = ({ width, height }) => (
<SuperChart
chartType="mixed-timeseries"
width={width}
height={height}
queriesData={[
{
data: negativeNumData,
colnames: ['__timestamp'],
coltypes: [2],
},
{
data: negativeNumData.map(({ __timestamp, Boston }) => ({
__timestamp,
avgRate: Boston / 100,
})),
},
]}
formData={{
contributionMode: undefined,
colorScheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
xAxisTimeFormat: 'smart_date',
yAxisFormat: select(
'y-axis format',
{
'Original value': '~g',
'Smart number': 'SMART_NUMBER',
'(12345.432 => $12,345.43)': '$,.2f',
},
'$,.2f',
),
stack: true,
showValue: boolean('Query 1: Show Value', true),
showValueB: boolean('Query 2: Show Value', false),
showLegend: true,
markerEnabledB: true,
yAxisIndexB: select(
'Query 2: Y Axis',
{
Primary: 0,
Secondary: 1,
},
1,
),
}}
/>
);

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
export default [
{
__timestamp: 1619827200000,
Boston: 10.8812312312,
Washington: -45.3089432023,
JerseyCity: -23.0509234029834,
},
{
__timestamp: 1622505600000,
Boston: 80.81029340234,
Washington: -10.299023489023,
JerseyCity: 53.54239402349,
},
{
__timestamp: 1625097600000,
Boston: 30.9129034924,
Washington: 100.25234902349,
JerseyCity: 27.17239402394,
},
{
__timestamp: 1627776000000,
Boston: 42.6129034924,
Washington: 90.23234902349,
JerseyCity: -32.23239402394,
},
];

View File

@@ -25,6 +25,7 @@ import {
TimeseriesTransformProps,
} from '@superset-ui/plugin-chart-echarts';
import data from './data';
import negativeNumData from './negativeNumData';
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
new EchartsTimeseriesChartPlugin()
@@ -61,7 +62,9 @@ export const Timeseries = ({ width, height }) => {
chartType="echarts-timeseries"
width={width}
height={height}
queriesData={[{ data: queryData }]}
queriesData={[
{ data: queryData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
contributionMode: undefined,
forecastEnabled,
@@ -87,3 +90,33 @@ export const Timeseries = ({ width, height }) => {
/>
);
};
export const WithNegativeNumbers = ({ width, height }) => (
<SuperChart
chartType="echarts-timeseries"
width={width}
height={height}
queriesData={[
{ data: negativeNumData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
contributionMode: undefined,
colorScheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
yAxisFormat: '$,.2f',
stack: boolean('Stack', true),
showValue: true,
showLegend: true,
onlyTotal: boolean('Only Total', true),
orientation: select(
'Orientation',
['vertical', 'horizontal'],
'vertical',
),
}}
/>
);

View File

@@ -0,0 +1,111 @@
/*
* 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.
*/
export default [
{
__timestamp: 1619827200000,
Boston: -0.88,
NewYork: null,
Washington: -0.3,
JerseyCity: -3.05,
Denver: -8.25,
SF: -0.13,
},
{
__timestamp: 1622505600000,
Boston: -0.81,
NewYork: null,
Washington: -0.29,
JerseyCity: -3.54,
Denver: -13.4,
SF: -0.12,
},
{
__timestamp: 1625097600000,
Boston: 0.91,
NewYork: null,
Washington: 0.25,
JerseyCity: 7.17,
Denver: 7.69,
SF: 0.05,
},
{
__timestamp: 1627776000000,
Boston: -1.05,
NewYork: -1.04,
Washington: -0.19,
JerseyCity: -8.99,
Denver: -7.99,
SF: -0.01,
},
{
__timestamp: 1630454400000,
Boston: -0.92,
NewYork: -1.09,
Washington: -0.17,
JerseyCity: -8.75,
Denver: -7.55,
SF: -0.01,
},
{
__timestamp: 1633046400000,
Boston: 0.79,
NewYork: -0.85,
Washington: 0.13,
JerseyCity: 12.59,
Denver: 3.34,
SF: -0.05,
},
{
__timestamp: 1635724800000,
Boston: 0.72,
NewYork: 0.54,
Washington: 0.15,
JerseyCity: 11.03,
Denver: 7.24,
SF: -0.14,
},
{
__timestamp: 1638316800000,
Boston: 0.61,
NewYork: 0.73,
Washington: 0.15,
JerseyCity: 13.45,
Denver: 5.98,
SF: -0.22,
},
{
__timestamp: 1640995200000,
Boston: 0.51,
NewYork: 1.8,
Washington: 0.15,
JerseyCity: 12.96,
Denver: 3.22,
SF: -0.02,
},
{
__timestamp: 1643673600000,
Boston: -0.47,
NewYork: null,
Washington: -0.18,
JerseyCity: -14.27,
Denver: -6.24,
SF: -0.04,
},
];

View File

@@ -100,4 +100,8 @@ export default {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
} as ControlPanelConfig;

View File

@@ -152,7 +152,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
document.body.append(container);
const fontSize = computeMaxFontSize({
text,
maxWidth: width,
maxWidth: width - 8, // Decrease 8px for more precise font size
maxHeight,
className: 'header-line',
container,

View File

@@ -274,6 +274,10 @@ const config: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -125,8 +125,10 @@ export default function transformProps(
if (compareIndex < sortedData.length) {
const compareValue = sortedData[compareIndex][1];
// compare values must both be non-nulls
if (bigNumber !== null && compareValue !== null && compareValue !== 0) {
percentChange = (bigNumber - compareValue) / Math.abs(compareValue);
if (bigNumber !== null && compareValue !== null) {
percentChange = compareValue
? (bigNumber - compareValue) / Math.abs(compareValue)
: 0;
formattedSubheader = `${formatPercentChange(
percentChange,
)} ${compareSuffix}`;

View File

@@ -148,6 +148,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { t, validateNonEmpty, validateInteger } from '@superset-ui/core';
import { t } from '@superset-ui/core';
import {
sharedControls,
ControlPanelConfig,
@@ -81,8 +81,7 @@ const config: ControlPanelConfig = {
config: {
type: 'TextControl',
isInt: true,
default: String(DEFAULT_FORM_DATA.minVal),
validators: [validateNonEmpty, validateInteger],
default: DEFAULT_FORM_DATA.minVal,
renderTrigger: true,
label: t('Min'),
description: t('Minimum value on the gauge axis'),
@@ -94,7 +93,6 @@ const config: ControlPanelConfig = {
type: 'TextControl',
isInt: true,
default: DEFAULT_FORM_DATA.maxVal,
validators: [validateNonEmpty, validateInteger],
renderTrigger: true,
label: t('Max'),
description: t('Maximum value on the gauge axis'),
@@ -313,6 +311,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -28,6 +28,7 @@ import {
} from '@superset-ui/core';
import { EChartsCoreOption, GaugeSeriesOption } from 'echarts';
import { GaugeDataItemOption } from 'echarts/types/src/chart/gauge/GaugeSeries';
import { CallbackDataParams } from 'echarts/types/src/util/types';
import range from 'lodash/range';
import { parseNumbersList } from '../utils/controls';
import {
@@ -80,6 +81,12 @@ const calculateAxisLineWidth = (
overlap: boolean,
): number => (overlap ? fontSize : data.length * fontSize);
const calculateMin = (data: GaugeDataItemOption[]) =>
2 * Math.min(...data.map(d => d.value as number).concat([0]));
const calculateMax = (data: GaugeDataItemOption[]) =>
2 * Math.max(...data.map(d => d.value as number).concat([0]));
export default function transformProps(
chartProps: EchartsGaugeChartProps,
): GaugeChartTransformedProps {
@@ -115,12 +122,7 @@ export default function transformProps(
const data = (queriesData[0]?.data || []) as DataRecord[];
const numberFormatter = getNumberFormatter(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const normalizer = maxVal;
const axisLineWidth = calculateAxisLineWidth(data, fontSize, overlap);
const axisLabels = range(minVal, maxVal, (maxVal - minVal) / splitNumber);
const axisLabelLength = Math.max(
...axisLabels.map(label => numberFormatter(label).length).concat([1]),
);
const groupbyLabels = groupby.map(getColumnLabel);
const formatValue = (value: number) =>
valueFormatter.replace('{value}', numberFormatter(value));
@@ -130,12 +132,6 @@ export default function transformProps(
FONT_SIZE_MULTIPLIERS.titleOffsetFromTitle * fontSize;
const detailOffsetFromTitle =
FONT_SIZE_MULTIPLIERS.detailOffsetFromTitle * fontSize;
const intervalBoundsAndColors = setIntervalBoundsAndColors(
intervals,
intervalColorIndices,
colorFn,
normalizer,
);
const columnsLabelMap = new Map<string, DataRecordValue[]>();
const transformedData: GaugeDataItemOption[] = data.map(
@@ -196,6 +192,33 @@ export default function transformProps(
const { setDataMask = () => {} } = hooks;
const min = minVal ?? calculateMin(transformedData);
const max = maxVal ?? calculateMax(transformedData);
const axisLabels = range(min, max, (max - min) / splitNumber);
const axisLabelLength = Math.max(
...axisLabels.map(label => numberFormatter(label).length).concat([1]),
);
const normalizer = max;
const intervalBoundsAndColors = setIntervalBoundsAndColors(
intervals,
intervalColorIndices,
colorFn,
normalizer,
);
const splitLineDistance =
axisLineWidth + splitLineLength + OFFSETS.ticksFromLine;
const axisLabelDistance =
FONT_SIZE_MULTIPLIERS.axisLabelDistance *
fontSize *
FONT_SIZE_MULTIPLIERS.axisLabelLength *
axisLabelLength +
(showSplitLine ? splitLineLength : 0) +
(showAxisTick ? axisTickLength : 0) +
OFFSETS.ticksFromLine -
axisLineWidth;
const axisTickDistance =
axisLineWidth + axisTickLength + OFFSETS.ticksFromLine;
const progress = {
show: showProgress,
overlap,
@@ -204,7 +227,7 @@ export default function transformProps(
};
const splitLine = {
show: showSplitLine,
distance: -axisLineWidth - splitLineLength - OFFSETS.ticksFromLine,
distance: -splitLineDistance,
length: splitLineLength,
lineStyle: {
width: FONT_SIZE_MULTIPLIERS.splitLineWidth * fontSize,
@@ -219,22 +242,14 @@ export default function transformProps(
},
};
const axisLabel = {
distance:
axisLineWidth -
FONT_SIZE_MULTIPLIERS.axisLabelDistance *
fontSize *
FONT_SIZE_MULTIPLIERS.axisLabelLength *
axisLabelLength -
(showSplitLine ? splitLineLength : 0) -
(showAxisTick ? axisTickLength : 0) -
OFFSETS.ticksFromLine,
distance: -axisLabelDistance,
fontSize,
formatter: numberFormatter,
color: gaugeSeriesOptions.axisLabel?.color,
};
const axisTick = {
show: showAxisTick,
distance: -axisLineWidth - axisTickLength - OFFSETS.ticksFromLine,
distance: -axisTickDistance,
length: axisTickLength,
lineStyle: gaugeSeriesOptions.axisTick?.lineStyle as AxisTickLineStyle,
};
@@ -243,8 +258,14 @@ export default function transformProps(
formatter: (value: number) => formatValue(value),
color: gaugeSeriesOptions.detail?.color,
};
let pointer;
const tooltip = {
formatter: (params: CallbackDataParams) => {
const { name, value } = params;
return `${name} : ${formatValue(value as number)}`;
},
};
let pointer;
if (intervalBoundsAndColors.length) {
splitLine.lineStyle.color =
INTERVAL_GAUGE_SERIES_OPTION.splitLine?.lineStyle?.color;
@@ -269,8 +290,8 @@ export default function transformProps(
type: 'gauge',
startAngle,
endAngle,
min: minVal,
max: maxVal,
min,
max,
progress,
animation,
axisLine: axisLine as GaugeSeriesOption['axisLine'],
@@ -280,11 +301,19 @@ export default function transformProps(
axisTick,
pointer,
detail,
tooltip,
radius:
Math.min(width, height) / 2 - axisLabelDistance - axisTickDistance,
center: ['50%', '55%'],
data: transformedData,
},
];
const echartOptions: EChartsCoreOption = {
tooltip: {
appendToBody: true,
trigger: 'item',
},
series,
};

View File

@@ -34,8 +34,8 @@ export type EchartsGaugeFormData = QueryFormData & {
groupby: QueryFormColumn[];
metric?: string;
rowLimit: number;
minVal: number;
maxVal: number;
minVal: number | null;
maxVal: number | null;
fontSize: number;
numberFormat: string;
animation: boolean;
@@ -58,8 +58,8 @@ export const DEFAULT_FORM_DATA: Partial<EchartsGaugeFormData> = {
...DEFAULT_LEGEND_FORM_DATA,
groupby: [],
rowLimit: 10,
minVal: 0,
maxVal: 100,
minVal: null,
maxVal: null,
fontSize: 15,
numberFormat: 'SMART_NUMBER',
animation: true,

View File

@@ -324,6 +324,10 @@ const controlPanel: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default controlPanel;

View File

@@ -47,8 +47,13 @@ import {
getAxisType,
getColtypesMapping,
getLegendProps,
extractDataTotalValues,
extractShowValueIndexes,
} from '../utils/series';
import { extractAnnotationLabels } from '../utils/annotation';
import {
extractAnnotationLabels,
getAnnotationData,
} from '../utils/annotation';
import {
extractForecastSeriesContext,
extractForecastValuesFromTooltipParams,
@@ -81,11 +86,11 @@ export default function transformProps(
filterState,
datasource,
theme,
annotationData = {},
} = chartProps;
const { verboseMap = {} } = datasource;
const data1 = (queriesData[0].data || []) as TimeseriesDataRecord[];
const data2 = (queriesData[1].data || []) as TimeseriesDataRecord[];
const annotationData = getAnnotationData(chartProps);
const {
area,
@@ -136,6 +141,7 @@ export default function transformProps(
yAxisTitlePosition,
sliceId,
timeGrainSqla,
percentageThreshold,
}: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
@@ -155,7 +161,7 @@ export default function transformProps(
});
const dataTypes = getColtypesMapping(queriesData[0]);
const xAxisDataType = dataTypes?.[xAxisCol];
const xAxisDataType = dataTypes?.[xAxisCol] ?? dataTypes?.[xAxisOrig];
const xAxisType = getAxisType(xAxisDataType);
const series: SeriesOption[] = [];
const formatter = getNumberFormatter(contributionMode ? ',.0%' : yAxisFormat);
@@ -181,7 +187,28 @@ export default function transformProps(
rawSeriesB.forEach(seriesOption =>
mapSeriesIdToAxis(seriesOption, yAxisIndexB),
);
const showValueIndexesA = extractShowValueIndexes(rawSeriesA, {
stack,
});
const showValueIndexesB = extractShowValueIndexes(rawSeriesB, {
stack,
});
const { totalStackedValues, thresholdValues } = extractDataTotalValues(
rebasedDataA,
{
stack,
percentageThreshold,
xAxisCol,
},
);
const {
totalStackedValues: totalStackedValuesB,
thresholdValues: thresholdValuesB,
} = extractDataTotalValues(rebasedDataB, {
stack: Boolean(stackB),
percentageThreshold,
xAxisCol,
});
rawSeriesA.forEach(entry => {
const transformedSeries = transformSeries(entry, colorScale, {
area,
@@ -195,6 +222,10 @@ export default function transformProps(
filterState,
seriesKey: entry.name,
sliceId,
formatter,
showValueIndexes: showValueIndexesA,
totalStackedValues,
thresholdValues,
});
if (transformedSeries) series.push(transformedSeries);
});
@@ -214,6 +245,10 @@ export default function transformProps(
? `${entry.name} (1)`
: entry.name,
sliceId,
formatter: formatterSecondary,
showValueIndexes: showValueIndexesB,
totalStackedValues: totalStackedValuesB,
thresholdValues: thresholdValuesB,
});
if (transformedSeries) series.push(transformedSeries);
});

View File

@@ -260,6 +260,10 @@ const config: ControlPanelConfig = {
row_limit:
ensureIsInt(formData.row_limit, 100) >= 100 ? 100 : formData.row_limit,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -45,8 +45,6 @@ import {
const {
contributionMode,
logAxis,
markerEnabled,
markerSize,
minorSplitLine,
rowLimit,
truncateYAxis,
@@ -341,38 +339,6 @@ const config: ControlPanelConfig = {
controlSetRows: [
['color_scheme'],
...showValueSection,
[
{
name: 'markerEnabled',
config: {
type: 'CheckboxControl',
label: t('Marker'),
renderTrigger: true,
default: markerEnabled,
description: t(
'Draw a marker on data points. Only applicable for line types.',
),
},
},
],
[
{
name: 'markerSize',
config: {
type: 'SliderControl',
label: t('Marker Size'),
renderTrigger: true,
min: 0,
max: 20,
default: markerSize,
description: t(
'Size of marker. Also applies to forecast observations.',
),
visibility: ({ controls }: ControlPanelsContainerProps) =>
Boolean(controls?.markerEnabled?.value),
},
},
],
[
{
name: 'zoomable',

View File

@@ -55,7 +55,10 @@ import {
extractDataTotalValues,
extractShowValueIndexes,
} from '../utils/series';
import { extractAnnotationLabels } from '../utils/annotation';
import {
extractAnnotationLabels,
getAnnotationData,
} from '../utils/annotation';
import {
extractForecastSeriesContext,
extractForecastSeriesContexts,
@@ -93,12 +96,12 @@ export default function transformProps(
queriesData,
datasource,
theme,
annotationData = {},
} = chartProps;
const { verboseMap = {} } = datasource;
const [queryData] = queriesData;
const { data = [] } = queryData as TimeseriesChartDataResponseResult;
const dataTypes = getColtypesMapping(queryData);
const annotationData = getAnnotationData(chartProps);
const {
area,
@@ -165,12 +168,15 @@ export default function transformProps(
});
const showValueIndexes = extractShowValueIndexes(rawSeries, {
stack,
onlyTotal,
isHorizontal,
});
const seriesContexts = extractForecastSeriesContexts(
Object.values(rawSeries).map(series => series.name as string),
);
const isAreaExpand = stack === AreaChartExtraControlsValue.Expand;
const xAxisDataType = dataTypes?.[xAxisCol];
const xAxisDataType = dataTypes?.[xAxisCol] ?? dataTypes?.[xAxisOrig];
const xAxisType = getAxisType(xAxisDataType);
const series: SeriesOption[] = [];
const formatter = getNumberFormatter(

View File

@@ -233,7 +233,10 @@ export function transformSeries(
if (!formatter) return numericValue;
if (!stack || isSelectedLegend) return formatter(numericValue);
if (!onlyTotal) {
if (numericValue >= thresholdValues[dataIndex]) {
if (
numericValue >=
(thresholdValues[dataIndex] || Number.MIN_SAFE_INTEGER)
) {
return formatter(numericValue);
}
return '';

View File

@@ -289,6 +289,10 @@ const controlPanel: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default controlPanel;

View File

@@ -142,6 +142,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -17,6 +17,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isEmpty } from 'lodash';
import {
Annotation,
AnnotationData,
@@ -30,6 +32,8 @@ import {
isTimeseriesAnnotationResult,
TimeseriesDataRecord,
} from '@superset-ui/core';
import { EchartsTimeseriesChartProps } from '../types';
import { EchartsMixedTimeseriesProps } from '../MixedTimeseries/types';
export function evalFormula(
formula: FormulaAnnotationLayer,
@@ -130,3 +134,13 @@ export function extractAnnotationLabels(
return formulaAnnotationLabels.concat(timeseriesAnnotationLabels);
}
export function getAnnotationData(
chartProps: EchartsTimeseriesChartProps | EchartsMixedTimeseriesProps,
): AnnotationData {
const data = chartProps?.queriesData[0]?.annotation_data as AnnotationData;
if (!isEmpty(data)) {
return data;
}
return {};
}

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isNumber } from 'lodash';
import { DataRecord, DTTM_ALIAS, NumberFormatter } from '@superset-ui/core';
import { OptionName } from 'echarts/types/src/util/types';
import { TooltipMarker } from 'echarts/types/src/util/format';
@@ -60,7 +61,7 @@ export const extractForecastValuesFromTooltipParams = (
const { marker, seriesId, value } = param;
const context = extractForecastSeriesContext(seriesId);
const numericValue = isHorizontal ? value[0] : value[1];
if (numericValue) {
if (isNumber(numericValue)) {
if (!(context.name in values))
values[context.name] = {
marker: marker || '',
@@ -94,7 +95,7 @@ export const formatForecastTooltipSeries = ({
}): string => {
let row = `${marker}${sanitizeHtml(seriesName)}: `;
let isObservation = false;
if (observation) {
if (isNumber(observation)) {
isObservation = true;
row += `${formatter(observation)}`;
}

View File

@@ -77,6 +77,8 @@ export function extractShowValueIndexes(
series: SeriesOption[],
opts: {
stack: StackType;
onlyTotal?: boolean;
isHorizontal?: boolean;
},
): number[] {
const showValueIndexes: number[] = [];
@@ -84,9 +86,20 @@ export function extractShowValueIndexes(
series.forEach((entry, seriesIndex) => {
const { data = [] } = entry;
(data as [any, number][]).forEach((datum, dataIndex) => {
if (datum[1] !== null) {
if (!opts.onlyTotal && datum[opts.isHorizontal ? 0 : 1] !== null) {
showValueIndexes[dataIndex] = seriesIndex;
}
if (opts.onlyTotal) {
if (datum[opts.isHorizontal ? 0 : 1] > 0) {
showValueIndexes[dataIndex] = seriesIndex;
}
if (
!showValueIndexes[dataIndex] &&
datum[opts.isHorizontal ? 0 : 1] !== null
) {
showValueIndexes[dataIndex] = seriesIndex;
}
}
});
});
}

View File

@@ -174,60 +174,62 @@ describe('EchartsTimeseries transformProps', () => {
titleColumn: '',
value: 3,
};
const annotationData = {
'My Event': {
columns: [
'start_dttm',
'end_dttm',
'short_descr',
'long_descr',
'json_metadata',
],
records: [
{
start_dttm: 0,
end_dttm: 1000,
short_descr: '',
long_descr: '',
json_metadata: null,
},
],
},
'My Interval': {
columns: ['start', 'end', 'title'],
records: [
{
start: 2000,
end: 3000,
title: 'My Title',
},
],
},
'My Timeseries': [
{
key: 'My Line',
values: [
{
x: 10000,
y: 11000,
},
{
x: 20000,
y: 21000,
},
],
},
],
};
const chartProps = new ChartProps({
...chartPropsConfig,
formData: {
...formData,
annotationLayers: [event, interval, timeseries],
},
annotationData: {
'My Event': {
columns: [
'start_dttm',
'end_dttm',
'short_descr',
'long_descr',
'json_metadata',
],
records: [
{
start_dttm: 0,
end_dttm: 1000,
short_descr: '',
long_descr: '',
json_metadata: null,
},
],
},
'My Interval': {
columns: ['start', 'end', 'title'],
records: [
{
start: 2000,
end: 3000,
title: 'My Title',
},
],
},
'My Timeseries': [
{
key: 'My Line',
values: [
{
x: 10000,
y: 11000,
},
{
x: 20000,
y: 21000,
},
],
},
],
},
annotationData,
queriesData: [
{
...queriesData[0],
annotation_data: annotationData,
},
],
});

View File

@@ -154,103 +154,148 @@ describe('rebaseForecastDatum', () => {
});
});
describe('extractForecastValuesFromTooltipParams', () => {
it('should extract the proper data from tooltip params', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
seriesId: 'abc',
value: [new Date(0), 10],
},
{
marker: '<img>',
seriesId: 'abc__yhat',
value: [new Date(0), 1],
},
{
marker: '<img>',
seriesId: 'abc__yhat_lower',
value: [new Date(0), 5],
},
{
marker: '<img>',
seriesId: 'abc__yhat_upper',
value: [new Date(0), 6],
},
{
marker: '<img>',
seriesId: 'qwerty',
value: [new Date(0), 2],
},
]),
).toEqual({
abc: {
test('extractForecastValuesFromTooltipParams should extract the proper data from tooltip params', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
observation: 10,
forecastTrend: 1,
forecastLower: 5,
forecastUpper: 6,
seriesId: 'abc',
value: [new Date(0), 10],
},
qwerty: {
{
marker: '<img>',
observation: 2,
seriesId: 'abc__yhat',
value: [new Date(0), 1],
},
});
{
marker: '<img>',
seriesId: 'abc__yhat_lower',
value: [new Date(0), 5],
},
{
marker: '<img>',
seriesId: 'abc__yhat_upper',
value: [new Date(0), 6],
},
{
marker: '<img>',
seriesId: 'qwerty',
value: [new Date(0), 2],
},
]),
).toEqual({
abc: {
marker: '<img>',
observation: 10,
forecastTrend: 1,
forecastLower: 5,
forecastUpper: 6,
},
qwerty: {
marker: '<img>',
observation: 2,
},
});
});
test('extractForecastValuesFromTooltipParams should extract valid values', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
seriesId: 'foo',
value: [0, 10],
},
{
marker: '<img>',
seriesId: 'bar',
value: [100, 0],
},
]),
).toEqual({
foo: {
marker: '<img>',
observation: 10,
},
bar: {
marker: '<img>',
observation: 0,
},
});
});
const formatter = getNumberFormatter(NumberFormats.INTEGER);
describe('formatForecastTooltipSeries', () => {
it('should generate a proper series tooltip', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 10.1,
formatter,
}),
).toEqual('<img>abc: 10');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastTrend: 20.1,
forecastLower: 5.1,
forecastUpper: 7.1,
formatter,
}),
).toEqual('<img>qwerty: 10, ŷ = 20 (5, 12)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastTrend: 20,
forecastLower: 5,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: ŷ = 20 (5, 12)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastLower: 6,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: 10 (6, 13)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastLower: 7,
forecastUpper: 8,
formatter,
}),
).toEqual('<img>qwerty: (7, 15)');
});
test('formatForecastTooltipSeries should apply format to value', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 10.1,
formatter,
}),
).toEqual('<img>abc: 10');
});
test('formatForecastTooltipSeries should show falsy value', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 0,
formatter,
}),
).toEqual('<img>abc: 0');
});
test('formatForecastTooltipSeries should format full forecast', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastTrend: 20.1,
forecastLower: 5.1,
forecastUpper: 7.1,
formatter,
}),
).toEqual('<img>qwerty: 10, ŷ = 20 (5, 12)');
});
test('formatForecastTooltipSeries should format forecast without observation', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastTrend: 20,
forecastLower: 5,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: ŷ = 20 (5, 12)');
});
test('formatForecastTooltipSeries should format forecast without point estimate', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastLower: 6,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: 10 (6, 13)');
});
test('formatForecastTooltipSeries should format forecast with only confidence band', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastLower: 7,
forecastUpper: 8,
formatter,
}),
).toEqual('<img>qwerty: (7, 15)');
});

View File

@@ -25,6 +25,7 @@ import {
getChartPadding,
getLegendProps,
sanitizeHtml,
extractShowValueIndexes,
} from '../../src/utils/series';
import { LegendOrientation, LegendType } from '../../src/types';
import { defaultLegendPadding } from '../../src/defaults';
@@ -206,6 +207,124 @@ describe('extractGroupbyLabel', () => {
});
});
describe('extractShowValueIndexes', () => {
it('should return the latest index for stack', () => {
expect(
extractShowValueIndexes(
[
{
id: 'abc',
name: 'abc',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', 1],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', null],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', null],
['2000-03-01', null],
['2000-04-01', null],
['2000-05-01', null],
['2000-06-01', 3],
['2000-07-01', null],
['2000-08-01', null],
['2000-09-01', null],
['2000-10-01', null],
],
},
],
{ stack: true, onlyTotal: false, isHorizontal: false },
),
).toEqual([undefined, 1, 0, 1, undefined, 2, 1, 1, undefined, 1]);
});
it('should handle the negative numbers for total only', () => {
expect(
extractShowValueIndexes(
[
{
id: 'abc',
name: 'abc',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', -1],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', -2],
['2000-08-01', -3],
['2000-09-01', null],
['2000-10-01', null],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', -3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 1],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', -2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
],
{ stack: true, onlyTotal: true, isHorizontal: false },
),
).toEqual([undefined, 1, 0, 2, undefined, 1, 1, 2, undefined, 1]);
});
});
describe('formatSeriesName', () => {
const numberFormatter = getNumberFormatter();
const timeFormatter = getTimeFormatter();

View File

@@ -16,15 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
import { buildQueryContext, QueryFormData } from '@superset-ui/core';
import {
buildQueryContext,
normalizeOrderBy,
QueryFormData,
} from '@superset-ui/core';
export default function buildQuery(formData: QueryFormData) {
const { metric, sort_by_metric, groupby } = formData;
const { groupby } = formData;
return buildQueryContext(formData, baseQueryObject => [
{
...baseQueryObject,
...(sort_by_metric && { orderby: [[metric, false]] }),
orderby: normalizeOrderBy(baseQueryObject).orderby,
...(groupby && { groupby }),
},
]);

View File

@@ -61,9 +61,10 @@ const config: ControlPanelConfig = {
[metricsControlSetItem, allColumnsControlSetItem],
[percentMetricsControlSetItem],
[timeSeriesLimitMetricControlSetItem, orderByControlSetItem],
[orderDescendingControlSetItem],
serverPaginationControlSetRow,
[rowLimitControlSetItem, serverPageLengthControlSetItem],
[includeTimeControlSetItem, orderDescendingControlSetItem],
[includeTimeControlSetItem],
[showTotalsControlSetItem],
['adhoc_filters'],
emitFilterControl,

View File

@@ -18,6 +18,7 @@
*/
import { ControlSetItem, Dataset } from '@superset-ui/chart-controls';
import { t } from '@superset-ui/core';
import { isEmpty } from 'lodash';
import { isAggMode, isRawMode } from './shared';
export const orderByControlSetItem: ControlSetItem = {
@@ -45,7 +46,12 @@ export const orderDescendingControlSetItem: ControlSetItem = {
label: t('Sort descending'),
default: true,
description: t('Whether to sort descending or ascending'),
visibility: isAggMode,
visibility: ({ controls }) =>
!!(
isAggMode({ controls }) &&
controls?.timeseries_limit_metric.value &&
!isEmpty(controls?.timeseries_limit_metric.value)
),
resetOnHide: false,
},
};

View File

@@ -33,5 +33,5 @@ export const sortAlphanumericCaseInsensitive = <D extends {}>(
if (!valueB || typeof valueB !== 'string') {
return 1;
}
return valueA.localeCompare(valueB) > 0 ? 1 : -1;
return valueA.localeCompare(valueB);
};

View File

@@ -394,6 +394,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
colorPositiveNegative,
})
: undefined)};
white-space: ${value instanceof Date ? 'nowrap' : undefined};
`;
const cellProps = {
@@ -449,7 +450,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
data-column-name={col.id}
css={{
display: 'inline-flex',
alignItems: 'center',
alignItems: 'flex-end',
}}
>
<span data-column-name={col.id}>{label}</span>

View File

@@ -17,8 +17,13 @@
* under the License.
*/
import { defaultOrderByFn, Row } from 'react-table';
import { sortAlphanumericCaseInsensitive } from '../src/DataTable/utils/sortAlphanumericCaseInsensitive';
type RecursivePartial<T> = {
[P in keyof T]?: T[P] | RecursivePartial<T[P]>;
};
const testData = [
{
values: {
@@ -133,3 +138,106 @@ describe('sortAlphanumericCaseInsensitive', () => {
]);
});
});
const testDataMulti: Array<RecursivePartial<Row<object>>> = [
{
values: {
colA: 'group 1',
colB: '10',
},
},
{
values: {
colA: 'group 1',
colB: '15',
},
},
{
values: {
colA: 'group 1',
colB: '20',
},
},
{
values: {
colA: 'group 2',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '15',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
];
describe('sortAlphanumericCaseInsensitiveMulti', () => {
it('Sort rows', () => {
const sorted = defaultOrderByFn(
[...testDataMulti] as Array<Row<object>>,
[
(a, b) => sortAlphanumericCaseInsensitive(a, b, 'colA'),
(a, b) => sortAlphanumericCaseInsensitive(a, b, 'colB'),
],
[true, false],
);
expect(sorted).toEqual([
{
values: {
colA: 'group 1',
colB: '20',
},
},
{
values: {
colA: 'group 1',
colB: '15',
},
},
{
values: {
colA: 'group 1',
colB: '10',
},
},
{
values: {
colA: 'group 2',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '15',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
]);
});
});

View File

@@ -35,6 +35,11 @@ export default {
sliceEntities: sliceEntitiesForChart,
charts: chartQueries,
nativeFilters: nativeFiltersInfo,
common: {
conf: {
SAMPLES_ROW_LIMIT: 10,
},
},
dataMask: mockDataMaskInfo,
dashboardInfo,
dashboardFilters: emptyFilters,

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
@@ -81,3 +82,10 @@ setupSupersetClient();
jest.mock('src/hooks/useTabId', () => ({
useTabId: () => 1,
}));
// Check https://github.com/remarkjs/react-markdown/issues/635
jest.mock('react-markdown', () => (props: any) => <>{props.children}</>);
jest.mock('rehype-sanitize', () => () => jest.fn());
jest.mock('rehype-raw', () => () => jest.fn());
process.env.WEBPACK_MODE = 'test';

View File

@@ -370,8 +370,8 @@ div.tablePopover {
border: 1px solid @gray-light;
font-feature-settings: @font-feature-settings;
// Fira Code causes problem with Ace under Firefox
font-family: 'Menlo', 'Lucida Console', 'Courier New', 'Ubuntu Mono',
'Consolas', 'source-code-pro', monospace;
font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono',
'source-code-pro', 'Lucida Console', monospace;
&.ace_autocomplete {
// Use !important because Ace Editor applies extra CSS at the last second

View File

@@ -27,7 +27,6 @@ import {
getExploreUrl,
getLegacyEndpointType,
buildV1ChartDataPayload,
postForm,
shouldUseLegacyApi,
getChartDataUri,
} from 'src/explore/exploreUtils';
@@ -40,6 +39,7 @@ import { addDangerToast } from 'src/components/MessageToasts/actions';
import { logEvent } from 'src/logger/actions';
import { Logger, LOG_ACTIONS_LOAD_CHART } from 'src/logger/LogUtils';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { safeStringify } from 'src/utils/safeStringify';
import { allowCrossDomain as domainShardingEnabled } from 'src/utils/hostNamesConfig';
import { updateDataMask } from 'src/dataMask/actions';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
@@ -563,7 +563,9 @@ export function redirectSQLLab(formData) {
datasourceKey: formData.datasource,
sql: json.result[0].query,
};
postForm(redirectUrl, payload);
SupersetClient.postForm(redirectUrl, {
form_data: safeStringify(payload),
});
})
.catch(() =>
dispatch(addDangerToast(t('An error occurred while loading the SQL'))),

View File

@@ -130,6 +130,13 @@ const CrudButtonWrapper = styled.div`
${({ theme }) => `margin-bottom: ${theme.gridUnit * 2}px`}
`;
const StyledButtonWrapper = styled.span`
${({ theme }) => `
margin-top: ${theme.gridUnit * 3}px;
margin-left: ${theme.gridUnit * 3}px;
`}
`;
export default class CRUDCollection extends React.PureComponent<
CRUDCollectionProps,
CRUDCollectionState
@@ -424,7 +431,7 @@ export default class CRUDCollection extends React.PureComponent<
<>
<CrudButtonWrapper>
{this.props.allowAddItem && (
<span className="m-t-10 m-r-10">
<StyledButtonWrapper>
<Button
buttonSize="small"
buttonStyle="tertiary"
@@ -434,7 +441,7 @@ export default class CRUDCollection extends React.PureComponent<
<i data-test="crud-add-table-item" className="fa fa-plus" />{' '}
{t('Add item')}
</Button>
</span>
</StyledButtonWrapper>
)}
</CrudButtonWrapper>
<CrudTableWrapper

View File

@@ -123,6 +123,13 @@ const StyledColumnsTabWrapper = styled.div`
}
`;
const StyledButtonWrapper = styled.span`
${({ theme }) => `
margin-top: ${theme.gridUnit * 3}px;
margin-left: ${theme.gridUnit * 3}px;
`}
`;
const checkboxGenerator = (d, onChange) => (
<CheckboxControl value={d} onChange={onChange} />
);
@@ -1361,7 +1368,7 @@ class DatasourceEditor extends React.PureComponent {
>
<StyledColumnsTabWrapper>
<ColumnButtonWrapper>
<span className="m-t-10 m-r-10">
<StyledButtonWrapper>
<Button
buttonSize="small"
buttonStyle="tertiary"
@@ -1372,7 +1379,7 @@ class DatasourceEditor extends React.PureComponent {
<i className="fa fa-database" />{' '}
{t('Sync columns from source')}
</Button>
</span>
</StyledButtonWrapper>
</ColumnButtonWrapper>
<ColumnCollectionTable
className="columns-table"

View File

@@ -31,9 +31,13 @@ export const DOCUMENTATION_LINK = supersetTextDocs
export interface IProps {
errorMessage: string;
showDbInstallInstructions: boolean;
}
const ErrorAlert: FunctionComponent<IProps> = ({ errorMessage }) => (
const ErrorAlert: FunctionComponent<IProps> = ({
errorMessage,
showDbInstallInstructions,
}) => (
<Alert
closable={false}
css={(theme: SupersetTheme) => antdWarningAlertStyles(theme)}
@@ -41,21 +45,25 @@ const ErrorAlert: FunctionComponent<IProps> = ({ errorMessage }) => (
showIcon
message={errorMessage}
description={
<>
<br />
{t(
'Database driver for importing maybe not installed. Visit the Superset documentation page for installation instructions:',
)}
<a
href={DOCUMENTATION_LINK}
target="_blank"
rel="noopener noreferrer"
className="additional-fields-alert-description"
>
{t('here')}
</a>
.
</>
showDbInstallInstructions ? (
<>
<br />
{t(
'Database driver for importing maybe not installed. Visit the Superset documentation page for installation instructions:',
)}
<a
href={DOCUMENTATION_LINK}
target="_blank"
rel="noopener noreferrer"
className="additional-fields-alert-description"
>
{t('here')}
</a>
.
</>
) : (
''
)
}
/>
);

View File

@@ -300,7 +300,12 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
<Button loading={importingModel}>Select file</Button>
</Upload>
</StyledInputContainer>
{errorMessage && <ErrorAlert errorMessage={errorMessage} />}
{errorMessage && (
<ErrorAlert
errorMessage={errorMessage}
showDbInstallInstructions={passwordFields.length > 0}
/>
)}
{renderPasswordFields()}
{renderOverwriteConfirmation()}
</Modal>

View File

@@ -25,8 +25,8 @@ const MenuItem = styled(AntdMenu.Item)`
}
&.ant-menu-item {
height: ${({ theme }) => theme.gridUnit * 7}px;
line-height: ${({ theme }) => theme.gridUnit * 7}px;
height: ${({ theme }) => theme.gridUnit * 8}px;
line-height: ${({ theme }) => theme.gridUnit * 8}px;
a {
border-bottom: none;
transition: background-color ${({ theme }) => theme.transitionTiming}s;

View File

@@ -97,7 +97,7 @@ export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
role="alert"
>
{icon}
<Interweave content={toast.text} />
<Interweave content={toast.text} noHtml={!toast.allowHtml} />
<i
className="fa fa-close pull-right pointer"
role="button"

View File

@@ -31,4 +31,6 @@ export interface ToastMeta {
/** Whether to skip displaying this message if there are another toast
* with the same message. */
noDuplicate?: boolean;
/** For security reasons, HTML rendering is disabled by default. Use this property to enable it. */
allowHtml?: boolean;
}

View File

@@ -49,6 +49,43 @@ const stateWithOnlyUser = {
reports: {},
};
const stateWithNonAdminUser = {
explore: {
user: {
email: 'nonadmin@test.com',
firstName: 'nonadmin',
isActive: true,
lastName: 'nonadmin',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: {
Gamme: [['no_menu_access', 'Manage']],
OtherRole: [['menu_access', 'Manage']],
},
userId: 1,
username: 'nonadmin',
},
},
reports: {},
};
const stateWithNonMenuAccessOnManage = {
explore: {
user: {
email: 'nonaccess@test.com',
firstName: 'nonaccess',
isActive: true,
lastName: 'nonaccess',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: { Gamma: [['no_menu_access', 'Manage']] },
userId: 1,
username: 'nonaccess',
},
},
reports: {},
};
const stateWithUserAndReport = {
explore: {
user: {
@@ -195,4 +232,32 @@ describe('Header Report Dropdown', () => {
});
expect(screen.getByText('Set up an email report')).toBeInTheDocument();
});
it('renders Schedule Email Reports as long as user has permission through any role', () => {
let mockedProps = createProps();
mockedProps = {
...mockedProps,
useTextMenu: true,
isDropdownVisible: true,
};
act(() => {
setup(mockedProps, stateWithNonAdminUser);
});
expect(screen.getByText('Set up an email report')).toBeInTheDocument();
});
it('do not render Schedule Email Reports if user no permission', () => {
let mockedProps = createProps();
mockedProps = {
...mockedProps,
useTextMenu: true,
isDropdownVisible: true,
};
act(() => {
setup(mockedProps, stateWithNonMenuAccessOnManage);
});
expect(
screen.queryByText('Set up an email report'),
).not.toBeInTheDocument();
});
});

View File

@@ -81,7 +81,6 @@ export interface HeaderReportProps {
setShowReportSubMenu?: (show: boolean) => void;
setIsDropdownVisible?: (visible: boolean) => void;
isDropdownVisible?: boolean;
showReportSubMenu?: boolean;
}
export default function HeaderReportDropDown({
@@ -120,7 +119,7 @@ export default function HeaderReportDropDown({
perms => perms[0] === 'menu_access' && perms[1] === 'Manage',
),
);
return permissions[0].length > 0;
return permissions.some(permission => permission.length > 0);
};
const [currentReportDeleting, setCurrentReportDeleting] =
@@ -156,10 +155,8 @@ export default function HeaderReportDropDown({
}
}, []);
const showReportSubMenu = report && setShowReportSubMenu && canAddReports();
useEffect(() => {
if (showReportSubMenu) {
if (report && setShowReportSubMenu && canAddReports()) {
setShowReportSubMenu(true);
} else if (!report && setShowReportSubMenu) {
setShowReportSubMenu(false);

View File

@@ -325,7 +325,7 @@ export function saveDashboardRequest(data, id, saveType) {
const onError = async response => {
const { error, message } = await getClientErrorObject(response);
let errorText = t('Sorry, an unknown error occured');
let errorText = t('Sorry, an unknown error occurred');
if (error) {
errorText = t(

View File

@@ -127,6 +127,8 @@ export const hydrateDashboard =
const dashboardFilters = {};
const slices = {};
const sliceIds = new Set();
const slicesFromExploreCount = new Map();
chartData.forEach(slice => {
const key = slice.slice_id;
const form_data = {
@@ -182,6 +184,10 @@ export const hydrateDashboard =
(newSlicesContainer.parents || []).slice(),
);
const count = (slicesFromExploreCount.get(slice.slice_id) ?? 0) + 1;
chartHolder.id = `${CHART_TYPE}-explore-${slice.slice_id}-${count}`;
slicesFromExploreCount.set(slice.slice_id, count);
layout[chartHolder.id] = chartHolder;
newSlicesContainer.children.push(chartHolder.id);
chartIdToLayoutId[chartHolder.meta.chartId] = chartHolder.id;

View File

@@ -41,8 +41,7 @@ export interface BCPProps {
const SUPERSET_HEADER_HEIGHT = 59;
const SIDEPANE_ADJUST_OFFSET = 4;
const SIDEPANE_HEADER_HEIGHT = 64; // including margins
const SIDEPANE_FILTERBAR_HEIGHT = 56;
const TOP_PANEL_OFFSET = 210;
const BuilderComponentPaneTabs = styled(Tabs)`
line-height: inherit;
@@ -52,20 +51,10 @@ const BuilderComponentPaneTabs = styled(Tabs)`
const DashboardBuilderSidepane = styled.div<{
topOffset: number;
}>`
height: 100%;
height: calc(100% - ${TOP_PANEL_OFFSET}px);
position: fixed;
right: 0;
top: 0;
.ReactVirtualized__List {
padding-bottom: ${({ topOffset }) =>
`${
SIDEPANE_HEADER_HEIGHT +
SIDEPANE_FILTERBAR_HEIGHT +
SIDEPANE_ADJUST_OFFSET +
topOffset
}px`};
}
`;
const BuilderComponentPane: React.FC<BCPProps> = ({

View File

@@ -174,7 +174,6 @@ class HeaderActionsDropdown extends React.PureComponent {
downloadAsImage(
SCREENSHOT_NODE_SELECTOR,
this.props.dashboardTitle,
{},
true,
)(domEvent).then(() => {
menu.style.visibility = 'visible';

File diff suppressed because one or more lines are too long

View File

@@ -129,12 +129,14 @@ const PropertiesModal = ({
return SupersetClient.get({
endpoint: `/api/v1/dashboard/related/${accessType}?q=${query}`,
}).then(response => ({
data: response.json.result.map(
(item: { value: number; text: string }) => ({
data: response.json.result
.filter((item: { extra: { active: boolean } }) =>
item.extra.active !== undefined ? item.extra.active : true,
)
.map((item: { value: number; text: string }) => ({
value: item.value,
label: item.text,
}),
),
})),
totalCount: response.json.count,
}));
},

View File

@@ -334,7 +334,7 @@ class SliceHeaderControls extends React.PureComponent<
<ModalTrigger
triggerNode={
<span data-test="view-query-menu-item">
{t('Drill to detail')}
{t('View as table')}
</span>
}
modalTitle={t('Chart Data: %s', slice.slice_name)}

View File

@@ -65,6 +65,10 @@ const propTypes = {
deleteComponent: PropTypes.func.isRequired,
handleComponentDrop: PropTypes.func.isRequired,
updateComponents: PropTypes.func.isRequired,
// HTML sanitization
htmlSanitization: PropTypes.bool,
htmlSchemaOverrides: PropTypes.object,
};
const defaultProps = {};
@@ -265,6 +269,8 @@ class Markdown extends React.PureComponent {
? MARKDOWN_ERROR_MESSAGE
: this.state.markdownSource || MARKDOWN_PLACE_HOLDER
}
htmlSanitization={this.props.htmlSanitization}
htmlSchemaOverrides={this.props.htmlSchemaOverrides}
/>
);
}
@@ -373,6 +379,8 @@ function mapStateToProps(state) {
return {
undoLength: state.dashboardLayout.past.length,
redoLength: state.dashboardLayout.future.length,
htmlSanitization: state.common.conf.HTML_SANITIZATION,
htmlSchemaOverrides: state.common.conf.HTML_SANITIZATION_SCHEMA_EXTENSIONS,
};
}
export default connect(mapStateToProps)(Markdown);

View File

@@ -20,9 +20,9 @@ import { Provider } from 'react-redux';
import React from 'react';
import { styledMount as mount } from 'spec/helpers/theming';
import sinon from 'sinon';
import ReactMarkdown from 'react-markdown';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { SafeMarkdown } from '@superset-ui/core';
import { act } from 'react-dom/test-utils';
import { MarkdownEditor } from 'src/components/AsyncAceEditor';
@@ -112,26 +112,26 @@ describe('Markdown', () => {
it('should render an Markdown when NOT focused', () => {
const wrapper = setup();
expect(wrapper.find(MarkdownEditor)).not.toExist();
expect(wrapper.find(ReactMarkdown)).toExist();
expect(wrapper.find(SafeMarkdown)).toExist();
});
it('should render an AceEditor when focused and editMode=true and editorMode=edit', async () => {
const wrapper = setup({ editMode: true });
expect(wrapper.find(MarkdownEditor)).not.toExist();
expect(wrapper.find(ReactMarkdown)).toExist();
expect(wrapper.find(SafeMarkdown)).toExist();
act(() => {
wrapper.find(WithPopoverMenu).simulate('click'); // focus + edit
});
await waitForComponentToPaint(wrapper);
expect(wrapper.find(MarkdownEditor)).toExist();
expect(wrapper.find(ReactMarkdown)).not.toExist();
expect(wrapper.find(SafeMarkdown)).not.toExist();
});
it('should render a ReactMarkdown when focused and editMode=true and editorMode=preview', () => {
it('should render a SafeMarkdown when focused and editMode=true and editorMode=preview', () => {
const wrapper = setup({ editMode: true });
wrapper.find(WithPopoverMenu).simulate('click'); // focus + edit
expect(wrapper.find(MarkdownEditor)).toExist();
expect(wrapper.find(ReactMarkdown)).not.toExist();
expect(wrapper.find(SafeMarkdown)).not.toExist();
// we can't call setState on Markdown bc it's not the root component, so call
// the mode dropdown onchange instead
@@ -139,7 +139,7 @@ describe('Markdown', () => {
dropdown.prop('onChange')('preview');
wrapper.update();
expect(wrapper.find(ReactMarkdown)).toExist();
expect(wrapper.find(SafeMarkdown)).toExist();
expect(wrapper.find(MarkdownEditor)).not.toExist();
});

View File

@@ -22,7 +22,6 @@ import { Input } from 'src/components/Input';
import { Form, FormItem } from 'src/components/Form';
import Alert from 'src/components/Alert';
import { JsonObject, t, styled } from '@superset-ui/core';
import ReactMarkdown from 'react-markdown';
import Modal from 'src/components/Modal';
import { Radio } from 'src/components/Radio';
import Button from 'src/components/Button';
@@ -32,7 +31,6 @@ import { connect } from 'react-redux';
// Session storage key for recent dashboard
const SK_DASHBOARD_ID = 'save_chart_recent_dashboard';
const SELECT_PLACEHOLDER = t('**Select** a dashboard OR **create** a new one');
type SaveModalProps = {
onHide: () => void;
@@ -282,11 +280,12 @@ class SaveModal extends React.Component<SaveModalProps, SaveModalState> {
onChange={this.onDashboardSelectChange}
value={dashboardSelectValue || undefined}
placeholder={
// Using markdown to allow for good i18n
<ReactMarkdown
source={SELECT_PLACEHOLDER}
renderers={{ paragraph: 'span' }}
/>
<div>
<b>{t('Select')}</b>
{t(' a dashboard OR ')}
<b>{t('create')}</b>
{t(' a new one')}
</div>
}
/>
</FormItem>

View File

@@ -21,7 +21,6 @@ import React from 'react';
import { render, screen, act } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { SupersetClient, DatasourceType } from '@superset-ui/core';
import * as Utils from 'src/explore/exploreUtils';
import DatasourceControl from '.';
const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
@@ -142,7 +141,7 @@ test('Click on Edit dataset', async () => {
test('Click on View in SQL Lab', async () => {
const props = createProps();
const postFormSpy = jest.spyOn(Utils, 'postForm');
const postFormSpy = jest.spyOn(SupersetClient, 'postForm');
postFormSpy.mockImplementation(jest.fn());
render(<DatasourceControl {...props} />, {

View File

@@ -19,7 +19,13 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { t, styled, withTheme, DatasourceType } from '@superset-ui/core';
import {
DatasourceType,
SupersetClient,
styled,
t,
withTheme,
} from '@superset-ui/core';
import { getUrlParam } from 'src/utils/urlUtils';
import { AntdDropdown } from 'src/components';
@@ -30,13 +36,13 @@ import {
ChangeDatasourceModal,
DatasourceModal,
} from 'src/components/Datasource';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
import { postForm } from 'src/explore/exploreUtils';
import Button from 'src/components/Button';
import ErrorAlert from 'src/components/ErrorMessage/ErrorAlert';
import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import { URL_PARAMS } from 'src/constants';
import { isUserAdmin } from 'src/dashboard/util/findPermission';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
import { safeStringify } from 'src/utils/safeStringify';
const propTypes = {
actions: PropTypes.object.isRequired,
@@ -193,7 +199,9 @@ class DatasourceControl extends React.PureComponent {
datasourceKey: `${datasource.id}__${datasource.type}`,
sql: datasource.sql,
};
postForm('/superset/sqllab/', payload);
SupersetClient.postForm('/superset/sqllab/', {
form_data: safeStringify(payload),
});
}
break;

View File

@@ -17,6 +17,9 @@
* under the License.
*/
import React from 'react';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { render, screen } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { CustomFrame } from '.';
@@ -29,8 +32,23 @@ const specificValue = '2021-03-16T00:00:00 : 2021-03-17T00:00:00';
const relativeNowValue = `DATEADD(DATETIME("now"), -7, day) : DATEADD(DATETIME("now"), 7, day)`;
const relativeTodayValue = `DATEADD(DATETIME("today"), -7, day) : DATEADD(DATETIME("today"), 7, day)`;
const mockStore = configureStore([thunk]);
const store = mockStore({
common: { locale: 'en' },
});
// case when common.locale is not populated
const emptyStore = mockStore({});
// case when common.locale is populated with invalid locale
const invalidStore = mockStore({ common: { locale: 'invalid_locale' } });
test('renders with default props', () => {
render(<CustomFrame onChange={jest.fn()} value={emptyValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={emptyValue} />
</Provider>,
);
expect(screen.getByText('Configure custom time range')).toBeInTheDocument();
expect(screen.getByText('Relative Date/Time')).toBeInTheDocument();
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
@@ -39,14 +57,70 @@ test('renders with default props', () => {
expect(screen.getByRole('img', { name: 'calendar' })).toBeInTheDocument();
});
test('renders with empty store', () => {
render(
<Provider store={emptyStore}>
<CustomFrame onChange={jest.fn()} value={emptyValue} />
</Provider>,
);
expect(screen.getByText('Configure custom time range')).toBeInTheDocument();
expect(screen.getByText('Relative Date/Time')).toBeInTheDocument();
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
expect(screen.getByText('Days Before')).toBeInTheDocument();
expect(screen.getByText('Specific Date/Time')).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'calendar' })).toBeInTheDocument();
});
test('renders since and until with specific date/time with default locale', () => {
render(
<Provider store={emptyStore}>
<CustomFrame onChange={jest.fn()} value={specificValue} />
</Provider>,
);
expect(screen.getAllByText('Specific Date/Time').length).toBe(2);
expect(screen.getAllByRole('img', { name: 'calendar' }).length).toBe(2);
});
test('renders with invalid locale', () => {
render(
<Provider store={invalidStore}>
<CustomFrame onChange={jest.fn()} value={emptyValue} />
</Provider>,
);
expect(screen.getByText('Configure custom time range')).toBeInTheDocument();
expect(screen.getByText('Relative Date/Time')).toBeInTheDocument();
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
expect(screen.getByText('Days Before')).toBeInTheDocument();
expect(screen.getByText('Specific Date/Time')).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'calendar' })).toBeInTheDocument();
});
test('renders since and until with specific date/time with invalid locale', () => {
render(
<Provider store={invalidStore}>
<CustomFrame onChange={jest.fn()} value={specificValue} />
</Provider>,
);
expect(screen.getAllByText('Specific Date/Time').length).toBe(2);
expect(screen.getAllByRole('img', { name: 'calendar' }).length).toBe(2);
});
test('renders since and until with specific date/time', () => {
render(<CustomFrame onChange={jest.fn()} value={specificValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={specificValue} />
</Provider>,
);
expect(screen.getAllByText('Specific Date/Time').length).toBe(2);
expect(screen.getAllByRole('img', { name: 'calendar' }).length).toBe(2);
});
test('renders since and until with relative date/time', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeNowValue} />
</Provider>,
);
expect(screen.getAllByText('Relative Date/Time').length).toBe(2);
expect(screen.getAllByRole('spinbutton').length).toBe(2);
expect(screen.getByText('Days Before')).toBeInTheDocument();
@@ -54,17 +128,29 @@ test('renders since and until with relative date/time', () => {
});
test('renders since and until with Now option', () => {
render(<CustomFrame onChange={jest.fn()} value={nowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={nowValue} />
</Provider>,
);
expect(screen.getAllByText('Now').length).toBe(2);
});
test('renders since and until with Midnight option', () => {
render(<CustomFrame onChange={jest.fn()} value={todayValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={todayValue} />
</Provider>,
);
expect(screen.getAllByText('Midnight').length).toBe(2);
});
test('renders anchor with now option', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeNowValue} />
</Provider>,
);
expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument();
@@ -72,7 +158,11 @@ test('renders anchor with now option', () => {
});
test('renders anchor with date/time option', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeTodayValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeTodayValue} />
</Provider>,
);
expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument();
@@ -81,21 +171,33 @@ test('renders anchor with date/time option', () => {
test('triggers onChange when the anchor changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={relativeNowValue} />
</Provider>,
);
userEvent.click(screen.getByRole('radio', { name: 'Date/Time' }));
expect(onChange).toHaveBeenCalled();
});
test('triggers onChange when the value changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={emptyValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={emptyValue} />
</Provider>,
);
userEvent.click(screen.getByRole('img', { name: 'up' }));
expect(onChange).toHaveBeenCalled();
});
test('triggers onChange when the mode changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={todayNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={todayNowValue} />
</Provider>,
);
userEvent.click(screen.getByTitle('Midnight'));
userEvent.click(screen.getByTitle('Relative Date/Time'));
userEvent.click(screen.getAllByTitle('Now')[1]);
@@ -105,7 +207,11 @@ test('triggers onChange when the mode changes', () => {
test('triggers onChange when the grain changes', async () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={relativeNowValue} />
</Provider>,
);
userEvent.click(screen.getByText('Days Before'));
userEvent.click(screen.getByText('Weeks Before'));
userEvent.click(screen.getByText('Days After'));
@@ -115,7 +221,11 @@ test('triggers onChange when the grain changes', async () => {
test('triggers onChange when the date changes', async () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={specificValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={specificValue} />
</Provider>,
);
const inputs = screen.getAllByPlaceholderText('Select date');
userEvent.click(inputs[0]);
userEvent.click(screen.getAllByText('Now')[0]);
@@ -123,3 +233,24 @@ test('triggers onChange when the date changes', async () => {
userEvent.click(screen.getAllByText('Now')[1]);
expect(onChange).toHaveBeenCalledTimes(2);
});
test('should translate Date Picker', () => {
const onChange = jest.fn();
const store = mockStore({
common: { locale: 'fr' },
});
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={specificValue} />
</Provider>,
);
userEvent.click(screen.getAllByRole('img', { name: 'calendar' })[0]);
expect(screen.getByText('2021')).toBeInTheDocument();
expect(screen.getByText('lu')).toBeInTheDocument();
expect(screen.getByText('ma')).toBeInTheDocument();
expect(screen.getByText('me')).toBeInTheDocument();
expect(screen.getByText('je')).toBeInTheDocument();
expect(screen.getByText('ve')).toBeInTheDocument();
expect(screen.getByText('sa')).toBeInTheDocument();
expect(screen.getByText('di')).toBeInTheDocument();
});

View File

@@ -17,9 +17,12 @@
* under the License.
*/
import React from 'react';
import { useSelector } from 'react-redux';
import { t } from '@superset-ui/core';
import { Moment } from 'moment';
import { isInteger } from 'lodash';
// @ts-ignore
import { locales } from 'antd/dist/antd-with-locales';
import { Col, Row } from 'src/components';
import { InputNumber } from 'src/components/Input';
import { DatePicker } from 'src/components/DatePicker';
@@ -36,11 +39,13 @@ import {
customTimeRangeDecode,
customTimeRangeEncode,
dttmToMoment,
LOCALE_MAPPING,
} from 'src/explore/components/controls/DateFilterControl/utils';
import {
CustomRangeKey,
FrameComponentProps,
} from 'src/explore/components/controls/DateFilterControl/types';
import { ExplorePageState } from 'src/explore/types';
export function CustomFrame(props: FrameComponentProps) {
const { customRange, matchedFlag } = customTimeRangeDecode(props.value);
@@ -105,6 +110,16 @@ export function CustomFrame(props: FrameComponentProps) {
}
}
// check if there is a locale defined for explore
const localFromFlaskBabel = useSelector(
(state: ExplorePageState) => state?.common?.locale,
);
// An undefined datePickerLocale is acceptable if no match is found in the LOCALE_MAPPING[localFromFlaskBabel] lookup
// and will fall back to antd's default locale when the antd DataPicker's prop locale === undefined
// This also protects us from the case where state is populated with a locale that antd locales does not recognize
const datePickerLocale =
locales[LOCALE_MAPPING[localFromFlaskBabel]]?.DatePicker;
return (
<div data-test="custom-frame">
<div className="section-title">{t('Configure custom time range')}</div>
@@ -132,6 +147,7 @@ export function CustomFrame(props: FrameComponentProps) {
onChange('sinceDatetime', datetime.format(MOMENT_FORMAT))
}
allowClear={false}
locale={datePickerLocale}
/>
</Row>
)}
@@ -184,6 +200,7 @@ export function CustomFrame(props: FrameComponentProps) {
onChange('untilDatetime', datetime.format(MOMENT_FORMAT))
}
allowClear={false}
locale={datePickerLocale}
/>
</Row>
)}
@@ -241,6 +258,7 @@ export function CustomFrame(props: FrameComponentProps) {
}
allowClear={false}
className="control-anchor-to-datetime"
locale={datePickerLocale}
/>
</Col>
)}

View File

@@ -114,3 +114,20 @@ export const SEVEN_DAYS_AGO = moment()
.subtract(7, 'days')
.format(MOMENT_FORMAT);
export const MIDNIGHT = moment().utc().startOf('day').format(MOMENT_FORMAT);
export const LOCALE_MAPPING = {
en: 'en_US',
fr: 'fr_FR',
es: 'es_ES',
it: 'it_IT',
zh: 'zh_CN',
ja: 'ja_JP',
de: 'de_DE',
pt: 'pt_PT',
pt_BR: 'pt_BR',
ru: 'ru_RU',
ko: 'ko_KR',
sk: 'sk_SK',
sl: 'sl_SI',
nl: 'nl_NL',
};

View File

@@ -203,7 +203,6 @@ export const useExploreAdditionalActionsMenu = (
'.panel-body .chart-container',
// eslint-disable-next-line camelcase
slice?.slice_name ?? t('New chart'),
{},
true,
)(domEvent);
setIsDropdownVisible(false);

View File

@@ -28,15 +28,46 @@ import {
import { xAxisControl } from '../../../plugins/plugin-chart-echarts/src/controls';
describe('should collect control values and create SFD', () => {
const sharedControlsFormData = {};
Object.entries(sharedControls).forEach(([, names]) => {
names.forEach(name => {
sharedControlsFormData[name] = name;
});
});
const publicControlsFormData = Object.fromEntries(
publicControls.map((name, idx) => [[name], idx]),
);
const sharedControlsFormData = {
// metrics
metric: 'm1',
metrics: ['m2'],
metric_2: 'm3',
// columns
groupby: ['c1'],
columns: ['c2'],
groupbyColumns: ['c3'],
groupbyRows: ['c4'],
};
const publicControlsFormData = {
// time section
granularity_sqla: 'time_column',
time_grain_sqla: 'P1D',
time_range: '2000 : today',
// filters
adhoc_filters: [],
// subquery limit(series limit)
limit: 5,
// order by clause
timeseries_limit_metric: 'orderby_metric',
series_limit_metric: 'orderby_metric',
// desc or asc in order by clause
order_desc: true,
// outer query limit
row_limit: 100,
// x asxs column
x_axis: 'x_axis_column',
// advanced analytics - rolling window
rolling_type: 'sum',
rolling_periods: 1,
min_periods: 0,
// advanced analytics - time comparison
time_compare: '1 year ago',
comparison_type: 'values',
// advanced analytics - resample
resample_rule: '1D',
resample_method: 'zerofill',
};
const sourceMockFormData: QueryFormData = {
...sharedControlsFormData,
...publicControlsFormData,
@@ -90,26 +121,45 @@ describe('should collect control values and create SFD', () => {
});
});
test('collect sharedControls', () => {
const sfd = new StandardizedFormData(sourceMockFormData);
expect(sfd.dumpSFD().standardizedState.metrics).toEqual(
sharedControls.metrics.map(controlName => controlName),
);
expect(sfd.dumpSFD().standardizedState.columns).toEqual(
sharedControls.columns.map(controlName => controlName),
);
test('should avoid to overlap', () => {
const sharedControlsSet = new Set(Object.keys(sharedControls));
const publicControlsSet = new Set(publicControls);
expect(
[...sharedControlsSet].filter((x: string) => publicControlsSet.has(x)),
).toEqual([]);
});
test('should transform all publicControls', () => {
test('should collect all sharedControls', () => {
expect(Object.entries(sharedControlsFormData).length).toBe(
Object.entries(sharedControls).length,
);
const sfd = new StandardizedFormData(sourceMockFormData);
expect(sfd.serialize().standardizedState.metrics).toEqual([
'm1',
'm2',
'm3',
]);
expect(sfd.serialize().standardizedState.columns).toEqual([
'c1',
'c2',
'c3',
'c4',
]);
});
test('should transform all publicControls and sharedControls', () => {
expect(Object.entries(publicControlsFormData).length).toBe(
publicControls.length,
);
const sfd = new StandardizedFormData(sourceMockFormData);
const { formData } = sfd.transform('target_viz', sourceMockStore);
Object.entries(publicControlsFormData).forEach(([key]) => {
Object.entries(publicControlsFormData).forEach(([key, value]) => {
expect(formData).toHaveProperty(key);
expect(value).toEqual(publicControlsFormData[key]);
});
Object.entries(sharedControls).forEach(([key, value]) => {
expect(formData[key]).toEqual(value);
});
expect(formData.columns).toEqual(['c1', 'c2', 'c3', 'c4']);
expect(formData.metrics).toEqual(['m1', 'm2', 'm3']);
});
test('should inherit standardizedFormData and memorizedFormData is LIFO', () => {
@@ -157,6 +207,7 @@ describe('should transform form_data between table and bigNumberTotal', () => {
const tableVizFormData = {
datasource: '30__table',
viz_type: 'table',
granularity_sqla: 'ds',
time_grain_sqla: 'P1D',
time_range: 'No filter',
query_mode: 'aggregate',
@@ -172,7 +223,6 @@ describe('should transform form_data between table and bigNumberTotal', () => {
table_timestamp_format: 'smart_date',
show_cell_bars: true,
color_pn: true,
applied_time_extras: {},
url_params: {
form_data_key:
'p3No_sqDW7k-kMTzlBPAPd9vwp1IXTf6stbyzjlrPPa0ninvdYUUiMC6F1iKit3Y',
@@ -197,7 +247,9 @@ describe('should transform form_data between table and bigNumberTotal', () => {
dataset_id: '30',
},
},
granularity_sqla: {},
granularity_sqla: {
value: 'ds',
},
time_grain_sqla: {
value: 'P1D',
},
@@ -271,6 +323,22 @@ describe('should transform form_data between table and bigNumberTotal', () => {
);
});
test('get and has', () => {
// table -> bigNumberTotal
const sfd = new StandardizedFormData(tableVizFormData);
const { formData: bntFormData } = sfd.transform(
'big_number_total',
tableVizStore,
);
// bigNumberTotal -> table
const sfd2 = new StandardizedFormData(bntFormData);
expect(sfd2.has('big_number_total')).toBeTruthy();
expect(sfd2.has('table')).toBeTruthy();
expect(sfd2.get('big_number_total').viz_type).toBe('big_number_total');
expect(sfd2.get('table').viz_type).toBe('table');
});
test('transform', () => {
// table -> bigNumberTotal
const sfd = new StandardizedFormData(tableVizFormData);
@@ -301,7 +369,7 @@ describe('should transform form_data between table and bigNumberTotal', () => {
);
expect(tblFormData.viz_type).toBe('table');
expect(tblFormData.metrics).toEqual(['sum(sales)']);
expect(tblFormData.groupby).toEqual([]);
expect(tblFormData.groupby).toEqual(['name']);
expect(tblFormData.time_range).toBe('2021 : 2022');
});
});

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isEmpty, intersection } from 'lodash';
import {
ensureIsArray,
getChartControlPanelRegistry,
@@ -29,38 +30,52 @@ import {
import { getControlsState } from 'src/explore/store';
import { getFormDataFromControls } from './getFormDataFromControls';
export const sharedControls: Record<keyof StandardizedState, string[]> = {
metrics: ['metric', 'metrics', 'metric_2'],
columns: ['groupby', 'columns', 'groupbyColumns', 'groupbyRows'],
export const sharedControls: Record<string, keyof StandardizedState> = {
// metrics
metric: 'metrics', // via sharedControls, scalar
metrics: 'metrics', // via sharedControls, array
metric_2: 'metrics', // via sharedControls, scalar
// columns
groupby: 'columns', // via sharedControls, array
columns: 'columns', // via sharedControls, array
groupbyColumns: 'columns', // via pivot table v2, array
groupbyRows: 'columns', // via pivot table v2, array
};
const sharedControlsMap: Record<keyof StandardizedState, string[]> = {
metrics: [],
columns: [],
};
Object.entries(sharedControls).forEach(([key, value]) =>
sharedControlsMap[value].push(key),
);
export const publicControls = [
// time section
'granularity_sqla',
'time_grain_sqla',
'time_range',
'granularity_sqla', // via sharedControls
'time_grain_sqla', // via sharedControls
'time_range', // via sharedControls
// filters
'adhoc_filters',
'adhoc_filters', // via sharedControls
// subquery limit(series limit)
'limit',
'limit', // via sharedControls
// order by clause
'timeseries_limit_metric',
'series_limit_metric',
'timeseries_limit_metric', // via sharedControls
'series_limit_metric', // via sharedControls
// desc or asc in order by clause
'order_desc',
'order_desc', // via sharedControls
// outer query limit
'row_limit',
'row_limit', // via sharedControls
// x asxs column
'x_axis',
'x_axis', // via sharedControls
// advanced analytics - rolling window
'rolling_type',
'rolling_periods',
'min_periods',
'rolling_type', // via sections.advancedAnalytics
'rolling_periods', // via sections.advancedAnalytics
'min_periods', // via sections.advancedAnalytics
// advanced analytics - time comparison
'time_compare',
'comparison_type',
'time_compare', // via sections.advancedAnalytics
'comparison_type', // via sections.advancedAnalytics
// advanced analytics - resample
'resample_rule',
'resample_method',
'resample_rule', // via sections.advancedAnalytics
'resample_method', // via sections.advancedAnalytics
];
export class StandardizedFormData {
@@ -70,20 +85,10 @@ export class StandardizedFormData {
/*
* Support form_data for smooth switching between different viz
* */
const standardizedState = {
metrics: [],
columns: [],
};
const formData = Object.freeze(sourceFormData);
const reversedMap = StandardizedFormData.getReversedMap();
Object.entries(formData).forEach(([key, value]) => {
if (reversedMap.has(key)) {
standardizedState[reversedMap.get(key)].push(...ensureIsArray(value));
}
});
const memorizedFormData = Array.isArray(
// generates an ordered map, the key is viz_type and the value is form_data. the last item is current viz
const memorizedFormData: Map<string, QueryFormData> = Array.isArray(
formData?.standardizedFormData?.memorizedFormData,
)
? new Map(formData.standardizedFormData.memorizedFormData)
@@ -93,25 +98,72 @@ export class StandardizedFormData {
memorizedFormData.delete(vizType);
}
memorizedFormData.set(vizType, formData);
// calculate sharedControls
const standardizedState =
StandardizedFormData.getStandardizedState(formData);
this.sfd = {
standardizedState,
memorizedFormData,
};
}
static getReversedMap() {
const reversedMap = new Map();
Object.entries(sharedControls).forEach(([key, names]) => {
names.forEach(name => {
reversedMap.set(name, key);
});
static getStandardizedState(formData: QueryFormData): StandardizedState {
// 1. collect current sharedControls
let currState: StandardizedState = {
metrics: [],
columns: [],
};
Object.entries(formData).forEach(([key, value]) => {
if (key in sharedControls) {
currState[sharedControls[key]].push(...ensureIsArray(value));
}
});
return reversedMap;
// 2. get previous StandardizedState
let prevState: StandardizedState = {
metrics: [],
columns: [],
};
if (
formData?.standardizedFormData?.standardizedState &&
Array.isArray(formData.standardizedFormData.standardizedState.metrics) &&
Array.isArray(formData.standardizedFormData.standardizedState.columns)
) {
prevState = formData.standardizedFormData.standardizedState;
}
// the initial prevState should equal to currentState
if (isEmpty(prevState.metrics) && isEmpty(prevState.columns)) {
prevState = currState;
}
// 3. inherit SS from previous state if current viz hasn't columns-like controls or metrics-like controls
Object.keys(sharedControlsMap).forEach(key => {
if (
isEmpty(intersection(Object.keys(formData), sharedControlsMap[key]))
) {
currState[key] = prevState[key];
}
});
// 4. update hook
const controlPanel = getChartControlPanelRegistry().get(formData.viz_type);
if (controlPanel?.updateStandardizedState) {
currState = controlPanel.updateStandardizedState(prevState, currState);
}
// 5. clear up
Object.entries(currState).forEach(([key, value]) => {
currState[key] = value.filter(Boolean);
});
return currState;
}
private getLatestFormData(vizType: string): QueryFormData {
if (this.sfd.memorizedFormData.has(vizType)) {
return this.sfd.memorizedFormData.get(vizType) as QueryFormData;
if (this.has(vizType)) {
return this.get(vizType);
}
return this.memorizedFormData.slice(-1)[0][1];
@@ -125,13 +177,21 @@ export class StandardizedFormData {
return Array.from(this.sfd.memorizedFormData.entries());
}
dumpSFD() {
serialize() {
return {
standardizedState: this.standardizedState,
memorizedFormData: this.memorizedFormData,
};
}
has(vizType: string): boolean {
return this.sfd.memorizedFormData.has(vizType);
}
get(vizType: string): QueryFormData {
return this.sfd.memorizedFormData.get(vizType) as QueryFormData;
}
transform(
targetVizType: string,
exploreState: Record<string, any>,
@@ -162,7 +222,7 @@ export class StandardizedFormData {
});
const targetFormData = {
...getFormDataFromControls(targetControlsState),
standardizedFormData: this.dumpSFD(),
standardizedFormData: this.serialize(),
};
const controlPanel = getChartControlPanelRegistry().get(targetVizType);

View File

@@ -21,13 +21,14 @@ import sinon from 'sinon';
import URI from 'urijs';
import {
buildV1ChartDataPayload,
exploreChart,
getExploreUrl,
shouldUseLegacyApi,
getSimpleSQLExpression,
shouldUseLegacyApi,
} from 'src/explore/exploreUtils';
import { DashboardStandaloneMode } from 'src/dashboard/util/constants';
import * as hostNamesConfig from 'src/utils/hostNamesConfig';
import { getChartMetadataRegistry } from '@superset-ui/core';
import { getChartMetadataRegistry, SupersetClient } from '@superset-ui/core';
describe('exploreUtils', () => {
const { location } = window;
@@ -275,4 +276,16 @@ describe('exploreUtils', () => {
);
});
});
describe('.exploreChart()', () => {
it('postForm', () => {
const postFormSpy = jest.spyOn(SupersetClient, 'postForm');
postFormSpy.mockImplementation(jest.fn());
exploreChart({
formData: { ...formData, viz_type: 'my_custom_viz' },
});
expect(postFormSpy).toBeCalledTimes(1);
});
});
});

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