Compare commits

...

16 Commits

Author SHA1 Message Date
Ville Brofeldt
d9ec47bc63 chore: add cherries to CHANGELOG.md 2020-07-16 11:53:01 +03:00
Ville Brofeldt
3f284eaa09 fix: make __time an ok column name in SQL Lab (#10336) 2020-07-16 11:20:10 +03:00
Jesse Yang
08d43b1515 fix(table-viz): value "undefined" for column.name (#10325)
Bump table viz plugin to fix a bug: apache-superset/superset-ui#686
2020-07-16 09:15:25 +03:00
Daniel Vaz Gaspar
c57df314d7 fix: humanised changed on UTC on dashboards and charts (#10321)
* fix: API marshmallow3 drop utc for naive datetime fields

* fix: API marshmallow3 drop utc for naive datetime fields

* fix, tests

* isort and test

* black

* add and fix test

* fix comment
2020-07-16 09:15:12 +03:00
Erik Ritter
2f2fb47721 chore: type welcome (#10317) 2020-07-16 09:14:55 +03:00
Erik Ritter
9f0d456b6a chore: add typing to more sqllab components (#10278) 2020-07-16 09:14:17 +03:00
Erik Ritter
2631b3882e chore: add typing to profile (#10282) 2020-07-16 09:12:40 +03:00
Daniel Vaz Gaspar
791d787256 fix: Bump FAB to 3.0.1 fix superset init (#10310) 2020-07-14 19:13:25 +03:00
Ville Brofeldt
8d0e6676ef fix: leave null timestamp unformatted in view results table (#10313) 2020-07-14 19:12:51 +03:00
Ville Brofeldt
522ed20a20 fix: fix csv and query result type and QueryObject schema (#10312) 2020-07-14 16:37:56 +03:00
Ville Brofeldt
38bc62db4b fix(chart-data-api): improve chart data endpoint errors (#10300)
* fix: improve chart data error response

* Populate error_message in QueryResult

* add tests

* Lint + fix incorrect raise

* add more tests
2020-07-14 15:49:20 +03:00
Jesse Yang
1bc26797ad fix(table-viz): table chart time column should use default (#10293) 2020-07-14 15:48:50 +03:00
Ville Brofeldt
1217cb05b3 fix(chart-data-api): case insensitive evaluation of filter op (#10299)
* fix(chart-data-api): case insensitive evaluation of filter op

* fix(chart-data-api): case insensitive evaluation of filter op

* mypy

* remove print statement

* add test
2020-07-14 15:48:32 +03:00
Ville Brofeldt
6f41b6ef5f chore: improve release documentation (#10292)
* fix: releasing readme.md

* fix: improvements to release documentation
2020-07-14 15:48:12 +03:00
Ville Brofeldt
5693580dae fix: add updating headers for 0.36.0 and 0.37.0 2020-07-11 12:14:39 +03:00
Ville Brofeldt
727c1b8ce1 chore: update changelog and bump version 2020-07-11 11:15:35 +03:00
68 changed files with 1295 additions and 453 deletions

View File

@@ -18,6 +18,419 @@ under the License.
-->
## Change Log
### 0.37.0 (2020/07/11 08:07 +00:00)
- [#10336](https://github.com/apache/incubator-superset/pull/10336) fix: make __time an ok column name in SQL Lab (#10336) (@villebro)
- [#10325](https://github.com/apache/incubator-superset/pull/10325) fix(table-viz): value "undefined" for column.name (#10325) (@ktmud)
- [#10321](https://github.com/apache/incubator-superset/pull/10321) fix: humanised changed on UTC on dashboards and charts (#10321) (@dpgaspar)
- [#10317](https://github.com/apache/incubator-superset/pull/10317) chore: type welcome (#10317) (@etr2460)
- [#10278](https://github.com/apache/incubator-superset/pull/10278) chore: add typing to more sqllab components (#10278) (@etr2460)
- [#10282](https://github.com/apache/incubator-superset/pull/10282) chore: add typing to profile (#10282) (@etr2460)
- [#10310](https://github.com/apache/incubator-superset/pull/10310) fix: Bump FAB to 3.0.1 fix superset init (#10310) (@dpgaspar)
- [#10313](https://github.com/apache/incubator-superset/pull/10313) fix: leave null timestamp unformatted in view results table (#10313) (@villebro)
- [#10312](https://github.com/apache/incubator-superset/pull/10312) fix: fix csv and query result type and QueryObject schema (#10312) (@villebro)
- [#10300](https://github.com/apache/incubator-superset/pull/10300) fix(chart-data-api): improve chart data endpoint errors (#10300) (@villebro)
- [#10293](https://github.com/apache/incubator-superset/pull/10293) fix(table-viz): table chart time column should use default (#10293) (@ktmud)
- [#10299](https://github.com/apache/incubator-superset/pull/10299) fix(chart-data-api): case insensitive evaluation of filter op (#10299) (@villebro)
- [#10292](https://github.com/apache/incubator-superset/pull/10292) chore: improve release documentation (#10292) (@villebro)
- [#10281](https://github.com/apache/incubator-superset/pull/10281) logo gets the vertically-centered flexbox treatment in jinja template (#10281) (@rusackas)
- [#10178](https://github.com/apache/incubator-superset/pull/10178) style: new toast design closer to SIP-34 (#10178) (@lilykuang)
- [#10258](https://github.com/apache/incubator-superset/pull/10258) feat: update delete modal for dataset (#10258) (@lilykuang)
- [#10284](https://github.com/apache/incubator-superset/pull/10284) fix(explore): edit datasource does not update control states (#10284) (@ktmud)
- [#10279](https://github.com/apache/incubator-superset/pull/10279) chore: Releasing SQL Lab Celery results (#10279) (@john-bodley)
- [#10280](https://github.com/apache/incubator-superset/pull/10280) chore: Bumping Celery (#10280) (@john-bodley)
- [#10286](https://github.com/apache/incubator-superset/pull/10286) feat: add contribution operation and fix cache_key bug (#10286) (@villebro)
- [#10283](https://github.com/apache/incubator-superset/pull/10283) fix(explore): 'Edit Datasource' is missing from btn-dropdown (#10283) (@mistercrunch)
- [#10277](https://github.com/apache/incubator-superset/pull/10277) fix: deckgl dimension select missing options (#10277) (@ktmud)
- [#10276](https://github.com/apache/incubator-superset/pull/10276) docs(fix): /swaggerview/v1 has moved to /swagger/v1 (#10276) (@mistercrunch)
- [#10273](https://github.com/apache/incubator-superset/pull/10273) fix(csv upload): hive params typo (#10273) (@serenajiang)
- [#10268](https://github.com/apache/incubator-superset/pull/10268) feat: show formatted dates instead of epoch on results (#10268) (@villebro)
- [#10269](https://github.com/apache/incubator-superset/pull/10269) fix: Fixed bug for issue #9967 (#10269) (@Nj-kol)
- [#10220](https://github.com/apache/incubator-superset/pull/10220) fix: dashboard endpoint sig changed (#10220) (@dpgaspar)
- [#10264](https://github.com/apache/incubator-superset/pull/10264) fix: optimize mapStateToProps for chart controls (#10264) (@ktmud)
- [#10263](https://github.com/apache/incubator-superset/pull/10263) feat: revised icons (#10263) (@rusackas)
- [#10262](https://github.com/apache/incubator-superset/pull/10262) fix: issue in getting filter_box config (#10262) (@graceguo-supercat)
- [#10259](https://github.com/apache/incubator-superset/pull/10259) chore: nixing requiresTime (#10259) (@rusackas)
- [#10260](https://github.com/apache/incubator-superset/pull/10260) [fix] disable search all options by default (#10260) (@graceguo-supercat)
- [#10257](https://github.com/apache/incubator-superset/pull/10257) feat: minor reorder SQL Lab Tab controls (#10257) (@graceguo-supercat)
- [#10255](https://github.com/apache/incubator-superset/pull/10255) feat(chart-data-api): make pivoted columns flattenable (#10255) (@villebro)
- [#10253](https://github.com/apache/incubator-superset/pull/10253) docs: update upgrading for FAB3 OAuth change (#10253) (@dpgaspar)
- [#10252](https://github.com/apache/incubator-superset/pull/10252) fix: datasets related objects, apply filter and openapi spec (#10252) (@dpgaspar)
- [#10250](https://github.com/apache/incubator-superset/pull/10250) fix(table-viz): JS error when sort time column (#10250) (@ktmud)
- [#10210](https://github.com/apache/incubator-superset/pull/10210) feat: Typeahead searchable filter_box for dashboard (#10210) (@graceguo-supercat)
- [#9964](https://github.com/apache/incubator-superset/pull/9964) feat(api): bump marshmallow and FAB to version 3 (#9964) (@dpgaspar)
- [#10248](https://github.com/apache/incubator-superset/pull/10248) chore: Cleaning up types and names for SQLA models (#10248) (@john-bodley)
- [#10237](https://github.com/apache/incubator-superset/pull/10237) chore: Revised logo (new color), w/CSS for vert alignment (#10237) (@rusackas)
- [#10235](https://github.com/apache/incubator-superset/pull/10235) feat: dataset api endpoint for charts and dashboards count (#10235) (@lilykuang)
- [#10208](https://github.com/apache/incubator-superset/pull/10208) feat: support nulls in the csv uploads (#10208) (@bkyryliuk)
- [#9944](https://github.com/apache/incubator-superset/pull/9944) feat: Alerts! allowing users to set SQL-based email alerts with screenshots (#9944) (@mistercrunch)
- [#10165](https://github.com/apache/incubator-superset/pull/10165) fix: downgrade sqlparse and add unit test (#10165) (@bkyryliuk)
- [#10243](https://github.com/apache/incubator-superset/pull/10243) feat: support new errors payload in SQL Lab (#10243) (@etr2460)
- [#10118](https://github.com/apache/incubator-superset/pull/10118) feat: add database dropdown to dashboard import (#10118) (@mistercrunch)
- [#10146](https://github.com/apache/incubator-superset/pull/10146) refactor: Using self.get_session in security manager (#10146) (@john-bodley)
- [#10197](https://github.com/apache/incubator-superset/pull/10197) [log] Updating form-data logic (#10197) (@john-bodley)
- [#10233](https://github.com/apache/incubator-superset/pull/10233) chore: Deprecating ENABLE_FLASK_COMPRESS (#10233) (@john-bodley)
- [#10236](https://github.com/apache/incubator-superset/pull/10236) Removing node_modules volume in docker-compose.yml (#10236) (@craig-rueda)
- [#9825](https://github.com/apache/incubator-superset/pull/9825) Upload excel (#9825) (@blcksrx)
- [#10222](https://github.com/apache/incubator-superset/pull/10222) fix: Remove double pickling for cached payloads (#10222) (@john-bodley)
- [#10234](https://github.com/apache/incubator-superset/pull/10234) fix(table-viz): format non-numeric metrics (#10234) (@ktmud)
- [#10226](https://github.com/apache/incubator-superset/pull/10226) chore: type ResultSet.tsx (#10226) (@etr2460)
- [#10223](https://github.com/apache/incubator-superset/pull/10223) fix(listview): use disableSortBy to disable sorting in table columns (#10223) (@nytai)
- [#10221](https://github.com/apache/incubator-superset/pull/10221) Update README.md (#10221) (@ceohockey60)
- [#10225](https://github.com/apache/incubator-superset/pull/10225) fix(table-vis): bump to v0.14.6 to fix missing anchor links (#10225) (@ktmud)
- [#10224](https://github.com/apache/incubator-superset/pull/10224) fix: chart validation error not cleared on control value update (#10224) (@ktmud)
- [#10219](https://github.com/apache/incubator-superset/pull/10219) fix: better backward compatibility for table viz (#10219) (@ktmud)
- [#10192](https://github.com/apache/incubator-superset/pull/10192) docs: update release instructions for Github releases (#10192) (@etr2460)
- [#10209](https://github.com/apache/incubator-superset/pull/10209) docs: added slack community guidelines (#10209) (@srinify)
- [#10211](https://github.com/apache/incubator-superset/pull/10211) fix: tooltips on nvd3 charts rendering in the wrong location (#10211) (@etr2460)
- [#10206](https://github.com/apache/incubator-superset/pull/10206) build: enable Cypress tests for visualizations (#10206) (@ktmud)
- [#10205](https://github.com/apache/incubator-superset/pull/10205) fix: datatable dependencies for pivot-table in dev (#10205) (@ktmud)
- [#10191](https://github.com/apache/incubator-superset/pull/10191) feat: add configuration for Presto cursor poll interval (#10191) (@etr2460)
- [#10195](https://github.com/apache/incubator-superset/pull/10195) s/assertEqual/assertEquals (#10195) (@bkyryliuk)
- [#10177](https://github.com/apache/incubator-superset/pull/10177) feat: Switch from nosetest to pytest (#10177) (@bkyryliuk)
- [#10174](https://github.com/apache/incubator-superset/pull/10174) added explore database for ctas/cvas (#10174) (@JasonD28)
- [#10170](https://github.com/apache/incubator-superset/pull/10170) build: enable typescript for cypress (#10170) (@ktmud)
- [#10188](https://github.com/apache/incubator-superset/pull/10188) update to last pyathena >= 1.10.8, to fix athena CSV upload (#10188) (@thibault-ketterer)
- [#10187](https://github.com/apache/incubator-superset/pull/10187) fix: make time grain nullable in chart data endpoint (#10187) (@villebro)
- [#10172](https://github.com/apache/incubator-superset/pull/10172) fix: make auth for hive optional (#10172) (@0xBADBAC0N)
- [#10113](https://github.com/apache/incubator-superset/pull/10113) feat(viz): add query mode switch to table chart (#10113) (@ktmud)
- [#10180](https://github.com/apache/incubator-superset/pull/10180) refactor: better naming for config flags (#10180) (@nytai)
- [#10179](https://github.com/apache/incubator-superset/pull/10179) fix: row count container alignment (#10179) (@etr2460)
- [#10175](https://github.com/apache/incubator-superset/pull/10175) Break some static methods out of superset.views.core.Superset (#10175) (@willbarrett)
- [#10155](https://github.com/apache/incubator-superset/pull/10155) chore: Updating UPDATING.md (#10155) (@john-bodley)
- [#10121](https://github.com/apache/incubator-superset/pull/10121) style: bring new SIP-34 palettes in controls (#10121) (@mistercrunch)
- [#10164](https://github.com/apache/incubator-superset/pull/10164) feat: Added configuration to SQL Lab results "Explore" button (#10164) (@JasonD28)
- [#9210](https://github.com/apache/incubator-superset/pull/9210) Add maximize and minimize feature to charts (#9210) (@oashton)
- [#10120](https://github.com/apache/incubator-superset/pull/10120) refactor: Fix lint on superset/utils/core.py (#10120) (@willbarrett)
- [#10171](https://github.com/apache/incubator-superset/pull/10171) fix: return slice owners for /user_slices ep (#10171) (@villebro)
- [#10161](https://github.com/apache/incubator-superset/pull/10161) build: dont prefer ts for cypress tests (#10161) (@ktmud)
- [#10158](https://github.com/apache/incubator-superset/pull/10158) build: upgrade Cypress and re-enable visualization tests (#10158) (@ktmud)
- [#10128](https://github.com/apache/incubator-superset/pull/10128) fix: [search query view] edit link is broken (#10128) (@mistercrunch)
- [#10106](https://github.com/apache/incubator-superset/pull/10106) refactor: Re-enable pylint on 5 files (#10106) (@willbarrett)
- [#10135](https://github.com/apache/incubator-superset/pull/10135) fix: implement legacy druid dashboard import (#10135) (@villebro)
- [#10160](https://github.com/apache/incubator-superset/pull/10160) feat: Add new timegrains and convert_dttm to Druid engine spec (#10160) (@villebro)
- [#10157](https://github.com/apache/incubator-superset/pull/10157) refactor: import emotion-theming methods from @superset-ui/style (#10157) (@ktmud)
- [#10124](https://github.com/apache/incubator-superset/pull/10124) refactor: Re-enable lint for 3 files (#10124) (@willbarrett)
- [#10105](https://github.com/apache/incubator-superset/pull/10105) fix: Loading overlay bugfix and cleanup (#10105) (@rusackas)
- [#10154](https://github.com/apache/incubator-superset/pull/10154) fix: Fixed Attribute Error when running core_tests.py (#10154) (@JasonD28)
- [#10156](https://github.com/apache/incubator-superset/pull/10156) Fix alembic migrations order (#10156) (@bkyryliuk)
- [#10130](https://github.com/apache/incubator-superset/pull/10130) fix(security): dbs/clusters perm (#10130) (@john-bodley)
- [#9794](https://github.com/apache/incubator-superset/pull/9794) Implement create view as functionality (#9794) (@bkyryliuk)
- [#10148](https://github.com/apache/incubator-superset/pull/10148) fix: refine shouldUseLegacyApi and add tests (#10148) (@villebro)
- [#10143](https://github.com/apache/incubator-superset/pull/10143) fix: FilterBox JS when no results (#10143) (@ktmud)
- [#10147](https://github.com/apache/incubator-superset/pull/10147) Fix CTAS explore flow (#10147) (@bkyryliuk)
- [#10034](https://github.com/apache/incubator-superset/pull/10034) chore(security): Updating assert logic (#10034) (@john-bodley)
- [#10094](https://github.com/apache/incubator-superset/pull/10094) style: listviews closer to SIP-34 (#10094) (@nytai)
- [#10104](https://github.com/apache/incubator-superset/pull/10104) feat: dataset add modal (#10104) (@lilykuang)
- [#10138](https://github.com/apache/incubator-superset/pull/10138) Break down Superset 101 Getting Started content (#10138) (@ceohockey60)
- [#10112](https://github.com/apache/incubator-superset/pull/10112) chore: bump superset-ui to v0.14.1 (#10112) (@ktmud)
- [#10114](https://github.com/apache/incubator-superset/pull/10114) fix: dashboard filter scope bug (#10114) (@graceguo-supercat)
- [#10080](https://github.com/apache/incubator-superset/pull/10080) [logging] add selected tab name into perf logging (#10080) (@graceguo-supercat)
- [#10127](https://github.com/apache/incubator-superset/pull/10127) chore: type src/addSlice (#10127) (@etr2460)
- [#10129](https://github.com/apache/incubator-superset/pull/10129) chore: fix add datasource help string (#10129) (@etr2460)
- [#10131](https://github.com/apache/incubator-superset/pull/10131) chore: extract translateable strings (#10131) (@etr2460)
- [#10117](https://github.com/apache/incubator-superset/pull/10117) chore: Bump sqlalchemy-utils and cachelib and refresh requirements.txt (#10117) (@villebro)
- [#9751](https://github.com/apache/incubator-superset/pull/9751) feat: Add multiple table filters for Row Level Security (#9751) (@axelet)
- [#10111](https://github.com/apache/incubator-superset/pull/10111) perf(datasets): improve datasets API performance for attr kind (#10111) (@dpgaspar)
- [#10123](https://github.com/apache/incubator-superset/pull/10123) style: replace broken glyphs with font-awesome (#10123) (@mistercrunch)
- [#10122](https://github.com/apache/incubator-superset/pull/10122) Add Nielsen to users list (#10122) (@amitNielsen)
- [#10119](https://github.com/apache/incubator-superset/pull/10119) chore(mypy): Fixing can_access_database types (#10119) (@john-bodley)
- [#10115](https://github.com/apache/incubator-superset/pull/10115) fix: dashboard should not add extra_filters onto chart annotation (#10115) (@graceguo-supercat)
- [#10092](https://github.com/apache/incubator-superset/pull/10092) fix: Visualization settings were lost when editing a datasource from Explore (#10092) (@willbarrett)
- [#10073](https://github.com/apache/incubator-superset/pull/10073) chore: type FilterableTable (#10073) (@etr2460)
- [#10056](https://github.com/apache/incubator-superset/pull/10056) style: push bootstrap theme towards SIP-34 styles (#10056) (@mistercrunch)
- [#10101](https://github.com/apache/incubator-superset/pull/10101) chore: Upgrade pylint to 2.5.3 and fix most new rules (#10101) (@willbarrett)
- [#10077](https://github.com/apache/incubator-superset/pull/10077) chore(metricoption): remove metricoption and columntypelable dependency (#10077) (@pkdotson)
- [#10090](https://github.com/apache/incubator-superset/pull/10090) style: Change logo color to new branding (#10090) (@willbarrett)
- [#10035](https://github.com/apache/incubator-superset/pull/10035) fix: adds pagination/infinite scroll to owners select in DashboardList and ChartList (#10035) (@nytai)
- [#9444](https://github.com/apache/incubator-superset/pull/9444) feat: implement dttm column configuration through db extra config (#9444) (@bkyryliuk)
- [#10078](https://github.com/apache/incubator-superset/pull/10078) refactor: Break up superset/views/core.py (#10078) (@willbarrett)
- [#10068](https://github.com/apache/incubator-superset/pull/10068) chore: type src/dashboard/util/charts (#10068) (@etr2460)
- [#10076](https://github.com/apache/incubator-superset/pull/10076) fix(sqllab): table refresh API endpoint (#10076) (@ktmud)
- [#9810](https://github.com/apache/incubator-superset/pull/9810) feat: superset report slack integration (#9810) (@bkyryliuk)
- [#10071](https://github.com/apache/incubator-superset/pull/10071) docs: FAQ entry for 'Does Superset have an API' (#10071) (@mistercrunch)
- [#10079](https://github.com/apache/incubator-superset/pull/10079) fix: Ensuring queries route accepts float or int (#10079) (@john-bodley)
- [#10075](https://github.com/apache/incubator-superset/pull/10075) add GrowthSimple to list of users on readme (#10075) (@AnimeshAgrawal)
- [#10074](https://github.com/apache/incubator-superset/pull/10074) other: add TypeScript tracker generation script (#10074) (@etr2460)
- [#10043](https://github.com/apache/incubator-superset/pull/10043) feat: add more columns and icons to dataset listview (#10043) (@nytai)
- [#10059](https://github.com/apache/incubator-superset/pull/10059) chore: bumping superset-ui theme version (#10059) (@rusackas)
- [#10070](https://github.com/apache/incubator-superset/pull/10070) fix: use custom int parsing over flask int parsing in sqllab queries endpoint (#10070) (@nytai)
- [#10069](https://github.com/apache/incubator-superset/pull/10069) Update prefer_typescript.yml (#10069) (@etr2460)
- [#10062](https://github.com/apache/incubator-superset/pull/10062) fix: chart export fails when buildQuery not present (#10062) (@villebro)
- [#10061](https://github.com/apache/incubator-superset/pull/10061) fix: caching on viz with relative time ranges and time compare (#10061) (@etr2460)
- [#10065](https://github.com/apache/incubator-superset/pull/10065) docs: Add DouroECI to list of users on readme (#10065) (@nunohelibeires)
- [#10060](https://github.com/apache/incubator-superset/pull/10060) fix: dashboard export raises schema issues (#10060) (@mistercrunch)
- [#10041](https://github.com/apache/incubator-superset/pull/10041) feat: owners profile icon on dataset list view (#10041) (@lilykuang)
- [#10037](https://github.com/apache/incubator-superset/pull/10037) fix: native annotations (#10037) (@villebro)
- [#10046](https://github.com/apache/incubator-superset/pull/10046) fix: bump pydruid version (#10046) (@villebro)
- [#10048](https://github.com/apache/incubator-superset/pull/10048) feat: add deafult buildQuery for V1 chart data requests (#10048) (@villebro)
- [#10042](https://github.com/apache/incubator-superset/pull/10042) other: [logging] add dashboard is_published into perf logging (#10042) (@graceguo-supercat)
- [#10031](https://github.com/apache/incubator-superset/pull/10031) chore(security): Renaming access methods (#10031) (@john-bodley)
- [#10030](https://github.com/apache/incubator-superset/pull/10030) chore(security): Renaming schemas_accessible_by_user (#10030) (@john-bodley)
- [#10039](https://github.com/apache/incubator-superset/pull/10039) fix(security): can_access with None crashes on builtin roles (#10039) (@dpgaspar)
- [#9971](https://github.com/apache/incubator-superset/pull/9971) [csv upload][hive] support other delimiters (#9971) (@serenajiang)
- [#10036](https://github.com/apache/incubator-superset/pull/10036) Fix chart annotation source type showing perpetual loading when re-selecting the same value. (#10036) (@nruhe)
- [#10033](https://github.com/apache/incubator-superset/pull/10033) fix: Resolves a regression from #9939 (#10033) (@john-bodley)
- [#10014](https://github.com/apache/incubator-superset/pull/10014) fix: bump pydruid to 0.6.0 (#10014) (@karen-pal)
- [#10025](https://github.com/apache/incubator-superset/pull/10025) feat: Add owners and physical/virtual status to Datasets API list endpoint (#10025) (@willbarrett)
- [#9982](https://github.com/apache/incubator-superset/pull/9982) feat: implement secondary navigation for datasets (#9982) (@lilykuang)
- [#9764](https://github.com/apache/incubator-superset/pull/9764) feat: add replace option to hive csv upload (#9764) (@etr2460)
- [#9859](https://github.com/apache/incubator-superset/pull/9859) fix: raise error in sqllab when using reserved column name (#9859) (@villebro)
- [#10018](https://github.com/apache/incubator-superset/pull/10018) chore: bump superset-ui to 0.13.27 (#10018) (@villebro)
- [#9673](https://github.com/apache/incubator-superset/pull/9673) refactor(sql): optimize sql query parser (#9673) (@lilykuang)
- [#9997](https://github.com/apache/incubator-superset/pull/9997) chore: remove markup from viz.py (#9997) (@villebro)
- [#10003](https://github.com/apache/incubator-superset/pull/10003) fix(SankeyViz): enforce source/target order (#10003) (@serenajiang)
- [#10021](https://github.com/apache/incubator-superset/pull/10021) fix: flaky test_copy_dash test in dashboard_tests.py (#10021) (@villebro)
- [#10010](https://github.com/apache/incubator-superset/pull/10010) feat: add support for query offset (#10010) (@villebro)
- [#10015](https://github.com/apache/incubator-superset/pull/10015) Quick fix for bad regex in GH Workflow (#10015) (@craig-rueda)
- [#10001](https://github.com/apache/incubator-superset/pull/10001) style(mypy): Spit-and-polish pass (#10001) (@john-bodley)
- [#10000](https://github.com/apache/incubator-superset/pull/10000) fix: [logging] fix render chart error type (#10000) (@graceguo-supercat)
- [#9939](https://github.com/apache/incubator-superset/pull/9939) style(mypy): Enforcing typing for superset.views (#9939) (@john-bodley)
- [#9975](https://github.com/apache/incubator-superset/pull/9975) feat: finalize Word Cloud move to new chart data endpoint (#9975) (@villebro)
- [#9989](https://github.com/apache/incubator-superset/pull/9989) fix: remove -1,-1 from owners request in charts, dashboards list and propertiesModal (#9989) (@nytai)
- [#9965](https://github.com/apache/incubator-superset/pull/9965) fix(api): Wrong parameter name on database openapi spec (#9965) (@dpgaspar)
- [#9960](https://github.com/apache/incubator-superset/pull/9960) docs: add ELMO Cloud HR & Payroll to list of users on readme (#9960) (@rayzor65)
- [#9934](https://github.com/apache/incubator-superset/pull/9934) fix: display python_date_format in react views as well (#9934) (@bkyryliuk)
- [#9784](https://github.com/apache/incubator-superset/pull/9784) fix: fetch all owners for dashboard, chart listview filters and properties modal (#9784) (@nytai)
- [#9943](https://github.com/apache/incubator-superset/pull/9943) style(mypy): Enforcing typing for superset (#9943) (@john-bodley)
- [#9886](https://github.com/apache/incubator-superset/pull/9886) feat: [dashboard] notification and warning for auto force refresh (#9886) (@graceguo-supercat)
- [#9973](https://github.com/apache/incubator-superset/pull/9973) fix(mypy): Resolves regression introducted in #9824 (#9973) (@john-bodley)
- [#9456](https://github.com/apache/incubator-superset/pull/9456) fix #8302, disabling save button when can_add: false, can_overwrite: false (#9456) (@micimize)
- [#9954](https://github.com/apache/incubator-superset/pull/9954) fix: pinot select query logic (#9954) (@fx19880617)
- [#9888](https://github.com/apache/incubator-superset/pull/9888) feat: make CRUD annotations inline (#9888) (@mistercrunch)
- [#9969](https://github.com/apache/incubator-superset/pull/9969) Revert "[caching] Using request context rather than globals" (#9969) (@john-bodley)
- [#9970](https://github.com/apache/incubator-superset/pull/9970) style(mypy): Fix memoize watch type (#9970) (@john-bodley)
- [#9962](https://github.com/apache/incubator-superset/pull/9962) feat: implement new version of word cloud (#9962) (@villebro)
- [#9903](https://github.com/apache/incubator-superset/pull/9903) feat: expand new chart data endpoint coverage (#9903) (@villebro)
- [#9901](https://github.com/apache/incubator-superset/pull/9901) build: add workflow preferring TypeScript files (#9901) (@etr2460)
- [#9952](https://github.com/apache/incubator-superset/pull/9952) [filter_box] disable instant_filtering by defult (#9952) (@graceguo-supercat)
- [#9940](https://github.com/apache/incubator-superset/pull/9940) fix: FilterBox Select should be Creatable (#9940) (@ktmud)
- [#9905](https://github.com/apache/incubator-superset/pull/9905) [mypy] Enforcing typing for superset.utils (#9905) (@john-bodley)
- [#9912](https://github.com/apache/incubator-superset/pull/9912) style(mypy): Enforcing mypy typing for views.chart (#9912) (@john-bodley)
- [#9920](https://github.com/apache/incubator-superset/pull/9920) style(mypy): Enforcing typing for views.database (#9920) (@john-bodley)
- [#9921](https://github.com/apache/incubator-superset/pull/9921) style(mypy): Enforcing typing for views.dashboard (#9921) (@john-bodley)
- [#9933](https://github.com/apache/incubator-superset/pull/9933) fix(react-select): FilterBox focus event and adhoc filter popup height (#9933) (@ktmud)
- [#9908](https://github.com/apache/incubator-superset/pull/9908) Revert "feat: bump superset-ui and implement queryFields in formData (#9908)" (#9931) (@villebro)
- [#9926](https://github.com/apache/incubator-superset/pull/9926) feat: bump sinon to latest (#9926) (@villebro)
- [#9915](https://github.com/apache/incubator-superset/pull/9915) fix: annotation layer json (#9915) (@etr2460)
- [#9890](https://github.com/apache/incubator-superset/pull/9890) css: beautify roles CRUD (#9890) (@mistercrunch)
- [#9919](https://github.com/apache/incubator-superset/pull/9919) fix: bump nvd3 plugin for annotation styles (#9919) (@etr2460)
- [#9916](https://github.com/apache/incubator-superset/pull/9916) feat: paired t testchart control migration (#9916) (@pkdotson)
- [#9851](https://github.com/apache/incubator-superset/pull/9851) feat: remove para controls (#9851) (@pkdotson)
- [#9908](https://github.com/apache/incubator-superset/pull/9908) feat: bump superset-ui and implement queryFields in formData (#9908) (@villebro)
- [#9891](https://github.com/apache/incubator-superset/pull/9891) fix: [dashboard] should not trigger chart refresh when filter not applicable (#9891) (@graceguo-supercat)
- [#9824](https://github.com/apache/incubator-superset/pull/9824) style: enforcing mypy typing for connectors (#9824) (@john-bodley)
- [#9826](https://github.com/apache/incubator-superset/pull/9826) chore(ts): convert base js files to typescript (#9826) (@etr2460)
- [#9895](https://github.com/apache/incubator-superset/pull/9895) Fix missing line break in Database 'extra' instructions (#9895) (@tooptoop4)
- [#9883](https://github.com/apache/incubator-superset/pull/9883) [mypy] Enforcing typing for superset.models (#9883) (@john-bodley)
- [#9884](https://github.com/apache/incubator-superset/pull/9884) fix: reenable table chart page length (#9884) (@ktmud)
- [#9881](https://github.com/apache/incubator-superset/pull/9881) fix: don't create examples db on 'superset init' (#9881) (@mistercrunch)
- [#9734](https://github.com/apache/incubator-superset/pull/9734) feat: implement csv upload configuration func for the schema enforcement (#9734) (@bkyryliuk)
- [#9847](https://github.com/apache/incubator-superset/pull/9847) feat: tablechart plugin controls migration (#9847) (@pkdotson)
- [#9864](https://github.com/apache/incubator-superset/pull/9864) Update README Resources section (#9864) (@ceohockey60)
- [#9839](https://github.com/apache/incubator-superset/pull/9839) fix: chart datasource explore URL showing datasource name for druid (#9839) (@dpgaspar)
- [#9820](https://github.com/apache/incubator-superset/pull/9820) refactor(frontend): move utils to TypeScript (#9820) (@ChristianMurphy)
- [#9865](https://github.com/apache/incubator-superset/pull/9865) only skip fossa on PRs (#9865) (@nytai)
- [#9811](https://github.com/apache/incubator-superset/pull/9811) fix: Emit a warning message rather than an exception on query failure (#9811) (@willbarrett)
- [#9853](https://github.com/apache/incubator-superset/pull/9853) feat: Adding Emotion ThemeProviders for all React apps (#9853) (@rusackas)
- [#9848](https://github.com/apache/incubator-superset/pull/9848) upgrade plugins, remove unnecessary code (#9848) (@suddjian)
- [#9841](https://github.com/apache/incubator-superset/pull/9841) feat: Add new result formats and types to chart data API (#9841) (@villebro)
- [#9832](https://github.com/apache/incubator-superset/pull/9832) fix: Removing the logic to add timeseries_limit_metric to the data for table (#9832) (@michellethomas)
- [#9862](https://github.com/apache/incubator-superset/pull/9862) Revert "[sqllab] fix, strip comments before parsing statements" (#9862) (@john-bodley)
- [#9850](https://github.com/apache/incubator-superset/pull/9850) fix: Fix for updated cache dependency (#9850) (@craig-rueda)
- [#9816](https://github.com/apache/incubator-superset/pull/9816) fixes: limit no authorization error for sentry (#9816) (@lilykuang)
- [#9846](https://github.com/apache/incubator-superset/pull/9846) docs: Disable sphinx-autodoc-typehints (#9846) (@john-bodley)
- [#9806](https://github.com/apache/incubator-superset/pull/9806) feat: markupchart plugin controls migration (#9806) (@pkdotson)
- [#9628](https://github.com/apache/incubator-superset/pull/9628) feat: upgrade react-select and make multi-select sortable (#9628) (@ktmud)
- [#9814](https://github.com/apache/incubator-superset/pull/9814) build: conditionally run fossa check if dependency files change (#9814) (@nytai)
- [#9837](https://github.com/apache/incubator-superset/pull/9837) fix: issue with duplicated dependencies (#9837) (@craig-rueda)
- [#9817](https://github.com/apache/incubator-superset/pull/9817) [explore view] inline edit slice name should not overwrite (#9817) (@graceguo-supercat)
- [#9833](https://github.com/apache/incubator-superset/pull/9833) build: Updating the sphinx configuration (#9833) (@john-bodley)
- [#9829](https://github.com/apache/incubator-superset/pull/9829) fix: [filter_box] fix 2 issues in single value filter_box (#9829) (@graceguo-supercat)
- [#9834](https://github.com/apache/incubator-superset/pull/9834) Update the link to ClickHouse official website (#9834) (@blinkov)
- [#9768](https://github.com/apache/incubator-superset/pull/9768) feat: partitionchart controls migration (#9768) (@pkdotson)
- [#9808](https://github.com/apache/incubator-superset/pull/9808) feat: bump superset-ui/time-format and big-number (#9808) (@ktmud)
- [#9835](https://github.com/apache/incubator-superset/pull/9835) fix: use snake_case for error_type (#9835) (@etr2460)
- [#9831](https://github.com/apache/incubator-superset/pull/9831) refactor: simplify getExploreUrl functions (#9831) (@villebro)
- [#9828](https://github.com/apache/incubator-superset/pull/9828) build: tox leveraging conditional factors (#9828) (@john-bodley)
- [#9819](https://github.com/apache/incubator-superset/pull/9819) feat(frontend): add ability to download dashboard and chart as image (#9819) (@ChristianMurphy)
- [#9827](https://github.com/apache/incubator-superset/pull/9827) build: Add python 3.8 to the build matrix (#9827) (@etr2460)
- [#9800](https://github.com/apache/incubator-superset/pull/9800) [mypy] Enforcing typing for translations (#9800) (@john-bodley)
- [#9789](https://github.com/apache/incubator-superset/pull/9789) FilterBox,BigNumber,WorldMap: Handle empty results - second attempt (#9789) (@elukey)
- [#9790](https://github.com/apache/incubator-superset/pull/9790) fix(babel): broken babel extract (#9790) (@dpgaspar)
- [#9774](https://github.com/apache/incubator-superset/pull/9774) upgrade migrated plugins, remove controlPanel configs (#9774) (@suddjian)
- [#9803](https://github.com/apache/incubator-superset/pull/9803) fix: invalid JSON in FR and DE translations (#9803) (@villebro)
- [#9752](https://github.com/apache/incubator-superset/pull/9752) fix(mssql): reverts #9644 and displays a better error msg (#9752) (@dpgaspar)
- [#9775](https://github.com/apache/incubator-superset/pull/9775) tests(celery): improve celery tests infra (#9775) (@dpgaspar)
- [#9796](https://github.com/apache/incubator-superset/pull/9796) feat: return security errors in the SIP-40 format (#9796) (@etr2460)
- [#9799](https://github.com/apache/incubator-superset/pull/9799) fix: construct SupersetErrors properly (#9799) (@etr2460)
- [#9798](https://github.com/apache/incubator-superset/pull/9798) fix: add flag for time series table (#9798) (@kristw)
- [#9342](https://github.com/apache/incubator-superset/pull/9342) Add extra configration to the email reports (#9342) (@bkyryliuk)
- [#9786](https://github.com/apache/incubator-superset/pull/9786) chore: bump python dependencies (#9786) (@villebro)
- [#9795](https://github.com/apache/incubator-superset/pull/9795) docs: add whale.im to list of users (#9795) (@Fullstop000)
- [#9753](https://github.com/apache/incubator-superset/pull/9753) feat: convert backend chart errors to the new error type (#9753) (@etr2460)
- [#9791](https://github.com/apache/incubator-superset/pull/9791) build: disable webpack progress for CI (#9791) (@ktmud)
- [#9655](https://github.com/apache/incubator-superset/pull/9655) chore: add support for prlint: a commit msg linter (#9655) (@mistercrunch)
- [#9715](https://github.com/apache/incubator-superset/pull/9715) [caching] Using request context rather than globals (#9715) (@john-bodley)
- [#9773](https://github.com/apache/incubator-superset/pull/9773) chore: move bullet string manipulation to frontend (#9773) (@villebro)
- [#9782](https://github.com/apache/incubator-superset/pull/9782) fix: make tests work individually (#9782) (@villebro)
- [#9779](https://github.com/apache/incubator-superset/pull/9779) tests: Fix, script to help run single tests (#9779) (@dpgaspar)
- [#9778](https://github.com/apache/incubator-superset/pull/9778) Add QueryContext deserialization test (#9778) (@villebro)
- [#9767](https://github.com/apache/incubator-superset/pull/9767) [docs] Add SSL config options for postgres (#9767) (@nytai)
- [#9756](https://github.com/apache/incubator-superset/pull/9756) Fix SQL Lab schema permission checks (#9756) (@bkyryliuk)
- [#9691](https://github.com/apache/incubator-superset/pull/9691) fix: Catch db_engine_spec.get_function_names exceptions (#9691) (@bkyryliuk)
- [#9714](https://github.com/apache/incubator-superset/pull/9714) fix bug where error at import dashboard fails to show toast in "welcome" app (#9714) (@pkdotson)
- [#9771](https://github.com/apache/incubator-superset/pull/9771) use the builtin calendar control panel (#9771) (@suddjian)
- [#9770](https://github.com/apache/incubator-superset/pull/9770) use word cloud's built in control panel (#9770) (@suddjian)
- [#9761](https://github.com/apache/incubator-superset/pull/9761) test(frontend): use absolute path for src imports (#9761) (@ktmud)
- [#9766](https://github.com/apache/incubator-superset/pull/9766) [docs] add postgres documentation (#9766) (@nytai)
- [#9762](https://github.com/apache/incubator-superset/pull/9762) fix: implement force refresh in chart data request (#9762) (@villebro)
- [#9710](https://github.com/apache/incubator-superset/pull/9710) Query the new chart data api for charts that support it (#9710) (@suddjian)
- [#9758](https://github.com/apache/incubator-superset/pull/9758) fix: removing controls from incubator-superset that are now in the plugin (#9758) (@rusackas)
- [#9757](https://github.com/apache/incubator-superset/pull/9757) build: bump superset-ui to 0.13 (#9757) (@kristw)
- [#9731](https://github.com/apache/incubator-superset/pull/9731) Update README.md (#9731) (@ceohockey60)
- [#9671](https://github.com/apache/incubator-superset/pull/9671) Revert "FilterBox,BigNumber,WorldMap: Handle empty results (#9671)" (#9755) (@etr2460)
- [#9745](https://github.com/apache/incubator-superset/pull/9745) [perf logging] Add extra logging for new/editMode dash (#9745) (@graceguo-supercat)
- [#9652](https://github.com/apache/incubator-superset/pull/9652) fix: adhoc filter 'equals' doesn't let you save (#9652) (@mistercrunch)
- [#9709](https://github.com/apache/incubator-superset/pull/9709) Use monospace (#9709) (@bkyryliuk)
- [#9740](https://github.com/apache/incubator-superset/pull/9740) feat: Add controlGroups to formData (#9740) (@villebro)
- [#9739](https://github.com/apache/incubator-superset/pull/9739) perf(dataset): improve performance on get list (#9739) (@dpgaspar)
- [#9749](https://github.com/apache/incubator-superset/pull/9749) fix: Add force flag to QueryContext schema (#9749) (@villebro)
- [#9654](https://github.com/apache/incubator-superset/pull/9654) fix: autocomplete search in AdhocFilter operator dropdown (#9654) (@mistercrunch)
- [#9744](https://github.com/apache/incubator-superset/pull/9744) fix: re-add all time grain options to the new chart API (#9744) (@etr2460)
- [#9728](https://github.com/apache/incubator-superset/pull/9728) chore: update README screenshots (#9728) (@etr2460)
- [#9733](https://github.com/apache/incubator-superset/pull/9733) [perf logging] Add timing event when browser tab is hidden (#9733) (@graceguo-supercat)
- [#9742](https://github.com/apache/incubator-superset/pull/9742) fix: css prop is now overwhelmed with emotion (#9742) (@suddjian)
- [#9724](https://github.com/apache/incubator-superset/pull/9724) docs(api): improve openapi documentation for dash, charts and queries (#9724) (@dpgaspar)
- [#9703](https://github.com/apache/incubator-superset/pull/9703) fix(dependencies): Bump FAB to 2.3.4 (#9703) (@dpgaspar)
- [#9704](https://github.com/apache/incubator-superset/pull/9704) perf(dashboards): improve API performance for dashboards (#9704) (@dpgaspar)
- [#9735](https://github.com/apache/incubator-superset/pull/9735) Fix bad test (#9735) (@bkyryliuk)
- [#9671](https://github.com/apache/incubator-superset/pull/9671) FilterBox,BigNumber,WorldMap: Handle empty results (#9671) (@elukey)
- [#9692](https://github.com/apache/incubator-superset/pull/9692) [sqllab] fix, strip comments before parsing statements (#9692) (@nytai)
- [#9716](https://github.com/apache/incubator-superset/pull/9716) [hive][csv upload] make INTs BIGINTs (#9716) (@serenajiang)
- [#9712](https://github.com/apache/incubator-superset/pull/9712) feat: add logging to warm_up_cache endpoint (#9712) (@etr2460)
- [#9695](https://github.com/apache/incubator-superset/pull/9695) Fix email reports (#9695) (@bkyryliuk)
- [#9707](https://github.com/apache/incubator-superset/pull/9707) migrate bignumber to use builtin controls (#9707) (@suddjian)
- [#9706](https://github.com/apache/incubator-superset/pull/9706) [table editor] hide Edit Datasource option when no onDatasourceSave (#9706) (@graceguo-supercat)
- [#9693](https://github.com/apache/incubator-superset/pull/9693) chore(ts): type getClientErrorObject (#9693) (@etr2460)
- [#9696](https://github.com/apache/incubator-superset/pull/9696) chore: Bump PyArrow to latest stable version (#9696) (@villebro)
- [#9694](https://github.com/apache/incubator-superset/pull/9694) [Helm] - Allow for customization of release name (#9694) (@craig-rueda)
- [#9702](https://github.com/apache/incubator-superset/pull/9702) tests(engine_specs): full bigquery engine coverage (#9702) (@dpgaspar)
- [#9705](https://github.com/apache/incubator-superset/pull/9705) fix(security) Fixing regression in #9689 (#9705) (@john-bodley)
- [#9619](https://github.com/apache/incubator-superset/pull/9619) perf(charts): improve performance on GET list (#9619) (@dpgaspar)
- [#9684](https://github.com/apache/incubator-superset/pull/9684) fix: migrate all slices off the old time grain format (#9684) (@etr2460)
- [#9649](https://github.com/apache/incubator-superset/pull/9649) [sql] Adding lighweight Table class (#9649) (@john-bodley)
- [#9651](https://github.com/apache/incubator-superset/pull/9651) [Chart & Dashboard] improve listview filter ui and add expandable list support (#9651) (@nytai)
- [#9677](https://github.com/apache/incubator-superset/pull/9677) feat(errors): add client scaffolding for custom error messages (#9677) (@etr2460)
- [#9688](https://github.com/apache/incubator-superset/pull/9688) Make cache work again for annotations (#9688) (@bkyryliuk)
- [#9689](https://github.com/apache/incubator-superset/pull/9689) Fix ENABLE_ROW_LEVEL_SECURITY flag usage (#9689) (@bkyryliuk)
- [#9665](https://github.com/apache/incubator-superset/pull/9665) [debug] Debugging caching issue (#9665) (@john-bodley)
- [#9685](https://github.com/apache/incubator-superset/pull/9685) [fix] reduce table metadata fetch for latest_partition check (#9685) (@graceguo-supercat)
- [#9682](https://github.com/apache/incubator-superset/pull/9682) tests(engine_specs): full postgres engine coverage (#9682) (@dpgaspar)
- [#9679](https://github.com/apache/incubator-superset/pull/9679) fix(tests): custom filter flaky tests on dash and charts (#9679) (@dpgaspar)
- [#9637](https://github.com/apache/incubator-superset/pull/9637) [explore view] add partition as adhoc filter option (#9637) (@graceguo-supercat)
- [#9669](https://github.com/apache/incubator-superset/pull/9669) fix: catch viz exceptions (#9669) (@villebro)
- [#9662](https://github.com/apache/incubator-superset/pull/9662) [logging] deprecation notices for SQLite (#9662) (@lilykuang)
- [#9676](https://github.com/apache/incubator-superset/pull/9676) gotta catch 'em all (#9676) (@rusackas)
- [#9666](https://github.com/apache/incubator-superset/pull/9666) [fix] Support APP_ICON_WIDTH configuration parameter in SPA menu (#9666) (@nruhe)
- [#9661](https://github.com/apache/incubator-superset/pull/9661) feat: Add geospatial post processing operations (#9661) (@villebro)
- [#9624](https://github.com/apache/incubator-superset/pull/9624) [fix] Push browser history on pagination in react listviews (#9624) (@lilykuang)
- [#9663](https://github.com/apache/incubator-superset/pull/9663) chore(ts): refactor and migrate StackTraceMessage to TypeScript (#9663) (@etr2460)
- [#9634](https://github.com/apache/incubator-superset/pull/9634) fix(database): test connection error message for module not found (#9634) (@dpgaspar)
- [#9644](https://github.com/apache/incubator-superset/pull/9644) fix(mssql): apply limit and set alias for functions (#9644) (@dpgaspar)
- [#9578](https://github.com/apache/incubator-superset/pull/9578) Controls cleanup pass (#9578) (@rusackas)
- [#9640](https://github.com/apache/incubator-superset/pull/9640) [dashboard] increase tab count limit (#9640) (@graceguo-supercat)
- [#9586](https://github.com/apache/incubator-superset/pull/9586) [mypy] Enforcing typing for a number of modules (#9586) (@john-bodley)
- [#9639](https://github.com/apache/incubator-superset/pull/9639) docs: remove references to superset-ui-plugins (#9639) (@ktmud)
- [#9577](https://github.com/apache/incubator-superset/pull/9577) [datesets] feat: add statsd to datasets api (#9577) (@lilykuang)
- [#9572](https://github.com/apache/incubator-superset/pull/9572) [fix] Automatically add relevant Jinja methods to cache key if present (#9572) (@john-bodley)
- [#9643](https://github.com/apache/incubator-superset/pull/9643) Cast raw 'fetch_values_predicate' to Sqlalchemy Text (#9643) (@vnnw)
- [#9641](https://github.com/apache/incubator-superset/pull/9641) [fix] Fixing regression from #9161 (#9641) (@john-bodley)
- [#9599](https://github.com/apache/incubator-superset/pull/9599) chore: Improve chart data API + schemas + tests (#9599) (@villebro)
- [#9612](https://github.com/apache/incubator-superset/pull/9612) [sql_lab] Improve performance, only use slow func when needed (#9612) (@dpgaspar)
- [#9605](https://github.com/apache/incubator-superset/pull/9605) use new @superset-ui/style package for theming (#9605) (@suddjian)
- [#9608](https://github.com/apache/incubator-superset/pull/9608) fix: change number format to original value to "~g" (#9608) (@ktmud)
- [#9613](https://github.com/apache/incubator-superset/pull/9613) fix: broken IS NULL and IS NOT NULL operator (#9613) (@villebro)
- [#9592](https://github.com/apache/incubator-superset/pull/9592) fix: move docs image symlink to _static (#9592) (@villebro)
- [#9614](https://github.com/apache/incubator-superset/pull/9614) [statsd] Send time metrics in ms not seconds (#9614) (@dpgaspar)
- [#9346](https://github.com/apache/incubator-superset/pull/9346) improve helm chart (#9346) (@fbalicchia)
- [#9587](https://github.com/apache/incubator-superset/pull/9587) [SQL Lab] Lock result set controls to be always visible (#9587) (@etr2460)
- [#9530](https://github.com/apache/incubator-superset/pull/9530) [charts] adds new filters ui (#9530) (@nytai)
- [#9547](https://github.com/apache/incubator-superset/pull/9547) [tests] Helper script to run single tests (#9547) (@dpgaspar)
- [#9571](https://github.com/apache/incubator-superset/pull/9571) [charts] feat: add statsd to charts api (#9571) (@lilykuang)
- [#9602](https://github.com/apache/incubator-superset/pull/9602) Bump superset-ui-connection to 0.12.22 (#9602) (@etr2460)
- [#9562](https://github.com/apache/incubator-superset/pull/9562) Add raw number/integer option to d3 number formats (#9562) (@ktmud)
- [#9569](https://github.com/apache/incubator-superset/pull/9569) Add documentation build to Github Actions (#9569) (@willbarrett)
- [#9591](https://github.com/apache/incubator-superset/pull/9591) [Build] moves prettier check to separate script (#9591) (@nytai)
- [#9582](https://github.com/apache/incubator-superset/pull/9582) [fix] Fixing issue with Jinja filter_value (#9582) (@john-bodley)
- [#9598](https://github.com/apache/incubator-superset/pull/9598) [docs] fix, elastic.co elasticsearch product location (#9598) (@dpgaspar)
- [#9585](https://github.com/apache/incubator-superset/pull/9585) [config] Enable dashboard bootstrap payload reduction by default (#9585) (@etr2460)
- [#9443](https://github.com/apache/incubator-superset/pull/9443) Re-enable the AnnotationLayerModelView read API (#9443) (@etr2460)
- [#9583](https://github.com/apache/incubator-superset/pull/9583) [mypy] Enforcing typing for superset.migrations (#9583) (@john-bodley)
- [#9579](https://github.com/apache/incubator-superset/pull/9579) fix: Add deprecated fields to QueryObject schema (#9579) (@villebro)
- [#9525](https://github.com/apache/incubator-superset/pull/9525) Migrating shared NVD3 controls to new module (#9525) (@rusackas)
- [#9570](https://github.com/apache/incubator-superset/pull/9570) doc: Add changelog for 0.36.0 (#9570) (@villebro)
- [#9567](https://github.com/apache/incubator-superset/pull/9567) [pypi] Include compiled translations on Pypi pkg (#9567) (@dpgaspar)
- [#9556](https://github.com/apache/incubator-superset/pull/9556) chore: Add OpenAPI docs to /api/v1/chart/data EP (#9556) (@villebro)
- [#9566](https://github.com/apache/incubator-superset/pull/9566) chore: Migrate unique FilterBox controls from controls.jsx (#9566) (@villebro)
- [#9555](https://github.com/apache/incubator-superset/pull/9555) [Build] Collect frontend code coverage from Cypress tests (#9555) (@ktmud)
- [#9550](https://github.com/apache/incubator-superset/pull/9550) [fix] dashboard filter scope bug (#9550) (@graceguo-supercat)
- [#9560](https://github.com/apache/incubator-superset/pull/9560) [fix] warm up cache error handling (#9560) (@john-bodley)
- [#9519](https://github.com/apache/incubator-superset/pull/9519) [dashboard] New, add statsd metrics to the API (#9519) (@dpgaspar)
- [#9549](https://github.com/apache/incubator-superset/pull/9549) Bump @superset-ui/legacy-plugin-chart-table to 0.12.14 (#9549) (@ktmud)
- [#9548](https://github.com/apache/incubator-superset/pull/9548) Ditching travis config in favor of Github Actions (#9548) (@craig-rueda)
- [#9539](https://github.com/apache/incubator-superset/pull/9539) Bump copyright notice (#9539) (@villebro)
- [#9536](https://github.com/apache/incubator-superset/pull/9536) docs: point our README the maintained Docker image (#9536) (@mistercrunch)
- [#9523](https://github.com/apache/incubator-superset/pull/9523) Make email parsing more robust (#9523) (@bkyryliuk)
- [#9541](https://github.com/apache/incubator-superset/pull/9541) [copy] fix: Row Level Security get_rls_filters func SELECT statement (#9541) (@axelet)
- [#8947](https://github.com/apache/incubator-superset/pull/8947) [thumbnails] API and celery task for dashboards and charts (#8947) (@dpgaspar)
- [#9537](https://github.com/apache/incubator-superset/pull/9537) [list views] add work-break css for table layouts (#9537) (@nytai)
- [#9538](https://github.com/apache/incubator-superset/pull/9538) Run CI on all pushes / PR's (#9538) (@craig-rueda)
- [#9535](https://github.com/apache/incubator-superset/pull/9535) Disabling recording in Cypress tests (#9535) (@craig-rueda)
- [#9517](https://github.com/apache/incubator-superset/pull/9517) [Build] Add Github workflows (#9517) (@ktmud)
- [#9533](https://github.com/apache/incubator-superset/pull/9533) Fix typo in viz.py (#9533) (@willbarrett)
- [#9465](https://github.com/apache/incubator-superset/pull/9465) Importing validators module from superset-ui (#9465) (@rusackas)
- [#9520](https://github.com/apache/incubator-superset/pull/9520) [api] refactor, remove unnecessary code, using command pattern now (#9520) (@dpgaspar)
- [#9521](https://github.com/apache/incubator-superset/pull/9521) [dashboard] Fix, improve test for custom filter (#9521) (@dpgaspar)
- [#9366](https://github.com/apache/incubator-superset/pull/9366) deprecate groupby controls in query_obj (#9366) (@villebro)
- [#9522](https://github.com/apache/incubator-superset/pull/9522) Another attempt to fix a viz.py bug (#9522) (@willbarrett)
- [#9500](https://github.com/apache/incubator-superset/pull/9500) control to turn off table bar-chart backgrounds (#9500) (@rusackas)
- [#9507](https://github.com/apache/incubator-superset/pull/9507) chore: auto label issues based on the template chosen (#9507) (@mistercrunch)
- [#9462](https://github.com/apache/incubator-superset/pull/9462) [Dashboard] new listview filters & emotion infra (#9462) (@nytai)
- [#9509](https://github.com/apache/incubator-superset/pull/9509) docker node_modules in its own volume (#9509) (@octaviancorlade)
- [#9492](https://github.com/apache/incubator-superset/pull/9492) [charts] New, custom filter for name OR description (#9492) (@dpgaspar)
- [#9503](https://github.com/apache/incubator-superset/pull/9503) Bump sqlalchemy and dremio deps (#9503) (@villebro)
- [#9370](https://github.com/apache/incubator-superset/pull/9370) Add visualization flow to the CTA queries (#9370) (@bkyryliuk)
- [#9427](https://github.com/apache/incubator-superset/pull/9427) feat: Add post processing to QueryObject (#9427) (@villebro)
- [#9496](https://github.com/apache/incubator-superset/pull/9496) fix: add lineWidth to Shared_DeckGL.jsx (#9496) (@villebro)
- [#9435](https://github.com/apache/incubator-superset/pull/9435) [dashboards] New, tittle and slug OR filter (#9435) (@dpgaspar)
- [#9484](https://github.com/apache/incubator-superset/pull/9484) [dashboards] Fix, update dashboard owners not propagating to charts o… (#9484) (@dpgaspar)
- [#9491](https://github.com/apache/incubator-superset/pull/9491) Bump FAB to 2.3.2 (#9491) (@dpgaspar)
- [#9479](https://github.com/apache/incubator-superset/pull/9479) [query] Migrate api v1 query to new location (#9479) (@dpgaspar)
- [#9495](https://github.com/apache/incubator-superset/pull/9495) fix: add explore control tabOverride at the section level (#9495) (@ktmud)
- [#9486](https://github.com/apache/incubator-superset/pull/9486) feat: change default time range in sql lab explore (#9486) (@ktmud)
- [#9493](https://github.com/apache/incubator-superset/pull/9493) Upgrade table chart plugin to 0.12.13 (#9493) (@ktmud)
- [#9460](https://github.com/apache/incubator-superset/pull/9460) pylint: accept specific 2 character names by default (#9460) (@villebro)
- [#9487](https://github.com/apache/incubator-superset/pull/9487) [tests] refactor, change datasets and charts to it's own folder (#9487) (@dpgaspar)
- [#9376](https://github.com/apache/incubator-superset/pull/9376) [sqllab] Add CUSTOM_TEMPLATE_PROCESSOR config (#9376) (@dandanhub)
- [#9480](https://github.com/apache/incubator-superset/pull/9480) Handle empty dataframes in TableViz (#9480) (@elukey)
- [#9337](https://github.com/apache/incubator-superset/pull/9337) Filter owners select by text input (#9337) (@suddjian)
- [#9437](https://github.com/apache/incubator-superset/pull/9437) [datasets] Add strict type annotation (#9437) (@dpgaspar)
- [#9418](https://github.com/apache/incubator-superset/pull/9418) [mypy] Enforcing typing for superset.dashboards (#9418) (@dpgaspar)
- [#9464](https://github.com/apache/incubator-superset/pull/9464) [Doc] Update installation doc for Dremio (#9464) (@narendrans)
- [#9455](https://github.com/apache/incubator-superset/pull/9455) Migrating shared DeckGL controls (#9455) (@rusackas)
- [#9469](https://github.com/apache/incubator-superset/pull/9469) [mypy] Enforcing typing for superset.examples (#9469) (@john-bodley)
- [#9403](https://github.com/apache/incubator-superset/pull/9403) [query] New, readonly API (#9403) (@dpgaspar)
- [#9472](https://github.com/apache/incubator-superset/pull/9472) Added dremio (#9472) (@narendrans)
- [#9451](https://github.com/apache/incubator-superset/pull/9451) release: Add support for ZSH in RELEASING and add tagging instructions (#9451) (@villebro)
- [#9378](https://github.com/apache/incubator-superset/pull/9378) chore: bump black to 19.10b0 and mypy to 0.770 (#9378) (@ktmud)
- [#9416](https://github.com/apache/incubator-superset/pull/9416) [mypy] Enforcing typing for some modules (#9416) (@john-bodley)
- [#9466](https://github.com/apache/incubator-superset/pull/9466) Eslint prefer-object-spread (#9466) (@rusackas)
- [#9454](https://github.com/apache/incubator-superset/pull/9454) Migrating NVD3 Area's stacked_style config (#9454) (@rusackas)
- [#9445](https://github.com/apache/incubator-superset/pull/9445) Migrating unique NVD3 viz controls (#9445) (@rusackas)
- [#9440](https://github.com/apache/incubator-superset/pull/9440) Migrating unique bigNumber(total) controls (#9440) (@rusackas)
- [#9356](https://github.com/apache/incubator-superset/pull/9356) Enforcing linting of LESS (#9356) (@rusackas)
- [#9446](https://github.com/apache/incubator-superset/pull/9446) migrating unique controls (#9446) (@rusackas)
### 0.36.0 (2020/04/02 07:57 +00:00)
- [#9436](https://github.com/apache/incubator-superset/pull/9436) Add check for SSL certificate and add form validators (#9436) (@villebro)
- [#9428](https://github.com/apache/incubator-superset/pull/9428) [fix]some translation not work better (#9428) (@venter-zhu)

View File

@@ -63,44 +63,50 @@ final release. Therefore, it's a good idea to do the following every time you
work on a new phase of the release process to make sure you aren't releasing
the wrong files/using wrong names. There's a script to help you set correctly all the
necessary environment variables. Change your current directory to `superset/RELEASING`
and execute the `set_release_env.sh` script with the relevant parameters:
```bash
# usage (BASH): . set_release_env.sh <SUPERSET_RC_VERSION> <PGP_KEY_FULLNAME>
# usage (ZSH): source set_release_env.sh <SUPERSET_RC_VERSION> <PGP_KEY_FULLNAME>
#
# example: source set_relese_env.sh 0.36.0rc3 myid@apache.org
# example: source set_release_env.sh 0.37.0rc1 myid@apache.org
```
The script will output the exported variables. Here's example for 0.36.0rc3:
The script will output the exported variables. Here's example for 0.37.0rc1:
```
-------------------------------
Set Release env variables
SUPERSET_VERSION=0.36.0
SUPERSET_RC=3
SUPERSET_GITHUB_BRANCH=0.36
SUPERSET_VERSION=0.37.0
SUPERSET_RC=1
SUPERSET_GITHUB_BRANCH=0.37
SUPERSET_PGP_FULLNAME=myid@apache.org
SUPERSET_VERSION_RC=0.36.0rc3
SUPERSET_RELEASE=apache-superset-incubating-0.36.0
SUPERSET_RELEASE_RC=apache-superset-incubating-0.36.0rc3
SUPERSET_RELEASE_TARBALL=apache-superset-incubating-0.36.0-source.tar.gz
SUPERSET_RELEASE_RC_TARBALL=apache-superset-incubating-0.36.0rc3-source.tar.gz
SUPERSET_TMP_ASF_SITE_PATH=/tmp/incubator-superset-site-0.36.0
-------------------------------
SUPERSET_VERSION_RC=0.37.0rc1
SUPERSET_RELEASE=apache-superset-incubating-0.37.0
SUPERSET_RELEASE_RC=apache-superset-incubating-0.37.0rc1
SUPERSET_RELEASE_TARBALL=apache-superset-incubating-0.37.0-source.tar.gz
SUPERSET_RELEASE_RC_TARBALL=apache-superset-incubating-0.37.0rc1-source.tar.gz
SUPERSET_TMP_ASF_SITE_PATH=/tmp/incubator-superset-site-0.37.0
```
## Crafting a source release
When crafting a new minor or major release we create
a branch named with the release MAJOR.MINOR version (on this example 0.35).
a branch named with the release MAJOR.MINOR version (on this example 0.37).
This new branch will hold all PATCH and release candidates
that belong to the MAJOR.MINOR version.
The MAJOR.MINOR branch is normally a "cut" from a specific point in time from the master branch.
Then (if needed) apply all cherries that will make the PATCH
Then (if needed) apply all cherries that will make the PATCH.
Next update the `CHANGELOG.md` with all the changes that are included in the release. Make sure you have
set your GITHUB_TOKEN environment variable.
```bash
git checkout -b $SUPERSET_GITHUB_BRANCH
git push upstream $SUPERSET_GITHUB_BRANCH
```
Next, update the `CHANGELOG.md` with all the changes that are included in the release.
Make sure the branch has been pushed to `upstream` to ensure the changelog generator
can pick up changes since the previous release (otherwise `github-changes` will raise
an `Error: Not Found` exception).
```bash
# will overwrites the local CHANGELOG.md, somehow you need to merge it in
@@ -293,7 +299,7 @@ with the changes on `CHANGELOG.md` and `UPDATING.md`.
### Publishing a Convenience Release to PyPI
Using the final release tarball, unpack it and run `./pypi_push.sh`.
This script will build the Javascript bundle and echo the twine command
This script will build the Javascript bundle and echo the twine command
allowing you to publish to PyPI. You may need to ask a fellow committer to grant
you access to it if you don't have access already. Make sure to create
an account first if you don't have one, and reference your username

View File

@@ -34,6 +34,9 @@ https://github.com/apache/incubator-{{ project_module }}/tree/{{ version_rc }}
The Change Log for the release:
https://github.com/apache/incubator-{{ project_module }}/blob/{{ version_rc }}/CHANGELOG.md
The Updating instructions for the release:
https://github.com/apache/incubator-{{ project_module }}/blob/{{ version_rc }}/UPDATING.md
public keys are available at:
https://www.apache.org/dist/incubator/{{ project_module }}/KEYS

View File

@@ -19,7 +19,7 @@ usage() {
echo "usage (BASH): . set_release_env.sh <SUPERSET_RC_VERSION> <PGP_KEY_FULLNAME>"
echo "usage (ZSH): source set_release_env.sh <SUPERSET_RC_VERSION> <PGP_KEY_FULLNAME>"
echo
echo "example: source set_relese_env.sh 0.36.0rc3 myid@apache.org"
echo "example: source set_release_env.sh 0.37.0rc1 myid@apache.org"
}
if [ -z "$1" ] || [ -z "$2" ]; then

View File

@@ -21,7 +21,7 @@ under the License.
This file documents any backwards-incompatible changes in Superset and
assists people when migrating to a new version.
## Next
## 0.37.0
* [9964](https://github.com/apache/incubator-superset/pull/9964): Breaking change on Flask-AppBuilder 3. If you're using OAuth, find out what needs to be changed [here](https://github.com/dpgaspar/Flask-AppBuilder/blob/master/README.rst#change-log).
@@ -44,6 +44,8 @@ assists people when migrating to a new version.
* [9572](https://github.com/apache/incubator-superset/pull/9572): a change which by default means that the Jinja `current_user_id`, `current_username`, and `url_param` context calls no longer need to be wrapped via `cache_key_wrapper` in order to be included in the cache key. The `cache_key_wrapper` function should only be required for Jinja add-ons.
## 0.36.0
* [8867](https://github.com/apache/incubator-superset/pull/8867): a change which adds the `tmp_schema_name` column to the `query` table which requires locking the table. Given the `query` table is heavily used performance may be degraded during the migration. Scheduled downtime may be advised.
* [9238](https://github.com/apache/incubator-superset/pull/9238): the config option `TIME_GRAIN_FUNCTIONS` has been renamed to `TIME_GRAIN_EXPRESSIONS` to better reflect the content of the dictionary.

View File

@@ -29,7 +29,7 @@ decorator==4.4.2 # via retry
defusedxml==0.6.0 # via python3-openid
dnspython==1.16.0 # via email-validator
email-validator==1.1.0 # via flask-appbuilder
flask-appbuilder==3.0.0 # via apache-superset (setup.py)
flask-appbuilder==3.0.1 # via apache-superset (setup.py)
flask-babel==1.0.0 # via flask-appbuilder
flask-caching==1.8.0 # via apache-superset (setup.py)
flask-compress==1.5.0 # via apache-superset (setup.py)

View File

@@ -78,7 +78,7 @@ setup(
"cryptography>=2.4.2",
"dataclasses<0.7",
"flask>=1.1.0, <2.0.0",
"flask-appbuilder>=3.0.0, <4.0.0",
"flask-appbuilder>=3.0.1, <4.0.0",
"flask-caching",
"flask-compress",
"flask-talisman",

View File

@@ -45,6 +45,13 @@ describe('Visualization > Line', () => {
cy.get('.alert-warning').should('not.exist');
});
it('should allow negative values in Y bounds', () => {
cy.get('#controlSections-tab-display').click();
cy.get('span').contains('Y Axis Bounds').scrollIntoView();
cy.get('input[placeholder="Min"]').type('-0.1', { delay: 100 });
cy.get('.alert-warning').should('not.exist');
});
it('should work with adhoc metric', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
cy.visitChartByParams(JSON.stringify(formData));
@@ -54,9 +61,7 @@ describe('Visualization > Line', () => {
it('should work with groupby', () => {
const metrics = ['count'];
const groupby = ['gender'];
const formData = { ...LINE_CHART_DEFAULTS, metrics, groupby };
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
@@ -64,13 +69,11 @@ describe('Visualization > Line', () => {
it('should work with simple filter', () => {
const metrics = ['count'];
const filters = [SIMPLE_FILTER];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
adhoc_filters: filters,
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
@@ -83,7 +86,6 @@ describe('Visualization > Line', () => {
groupby: ['name'],
timeseries_limit_metric: NUM_METRIC,
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
@@ -97,28 +99,24 @@ describe('Visualization > Line', () => {
timeseries_limit_metric: NUM_METRIC,
order_desc: true,
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with rolling avg', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
rolling_type: 'mean',
rolling_periods: 10,
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with time shift 1 year', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
@@ -162,28 +160,24 @@ describe('Visualization > Line', () => {
it('should work with time shift yoy', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
time_compare: ['1+year'],
comparison_type: 'ratio',
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with time shift percentage change', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
time_compare: ['1+year'],
comparison_type: 'percentage',
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
@@ -193,7 +187,6 @@ describe('Visualization > Line', () => {
...LINE_CHART_DEFAULTS,
metrics: ['count'],
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('text.nv-legend-text').contains('COUNT(*)');
@@ -224,7 +217,6 @@ describe('Visualization > Line', () => {
},
],
};
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('.slice_container').within(() => {
@@ -263,7 +255,6 @@ describe('Visualization > Line', () => {
cy.visitChartByParams(JSON.stringify(formData));
},
);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('.slice_container').within(() => {
cy.get('.nv-event-annotation-layer-0')

View File

@@ -35,6 +35,15 @@ describe('Visualization > Table', () => {
cy.route('POST', '/superset/explore_json/**').as('getJson');
});
it('Use default time column', () => {
cy.visitChartByParams({
...VIZ_DEFAULTS,
granularity_sqla: undefined,
metrics: ['count'],
});
cy.get('input[name="select-granularity_sqla"]').should('have.value', 'ds');
});
it('Format non-numeric metrics correctly', () => {
cy.visitChartByParams({
...VIZ_DEFAULTS,

View File

@@ -6158,15 +6158,14 @@
}
},
"@superset-ui/plugin-chart-table": {
"version": "0.14.9",
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.14.9.tgz",
"integrity": "sha512-A65LdejI4Vy99LLCVT20WrUGNIdp/FQBK2NsRIAMKNyhnZ1rp0R2F5xX0MG5AjxBUDQYlMQY4fptt1oB8V1fqA==",
"version": "0.14.11",
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.14.11.tgz",
"integrity": "sha512-YEnmDojbO4JK4niLsLs7akTJAD0mnmWwmPHM+uIej6Q0ByJnNF4hHtnpCoMrBzmfRShZH04xfaF5AtaNsaItsw==",
"requires": {
"@emotion/core": "^10.0.28",
"@types/d3-array": "^2.0.0",
"@types/match-sorter": "^4.0.0",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"@types/react": "^16.9.43",
"@types/react-table": "^7.0.19",
"d3-array": "^2.4.0",
"match-sorter": "^4.1.0",
@@ -7658,6 +7657,11 @@
"resolved": "https://registry.npmjs.org/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ=="
},
"@types/fetch-mock": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.3.2.tgz",
"integrity": "sha512-NCEfv49jmDsBAixjMjEHKVgmVQlJ+uK56FOc+2roYPExnXCZDpi6mJOHQ3v23BiO84hBDStND9R2itJr7PNoow=="
},
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
@@ -7678,21 +7682,10 @@
"@types/node": "*"
}
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dev": true,
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
},
"dependencies": {
"react-is": {
"version": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz",
"integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA=="
}
}
"@types/history": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.6.tgz",
"integrity": "sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w=="
},
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
@@ -7794,9 +7787,9 @@
"dev": true
},
"@types/react": {
"version": "16.9.38",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.38.tgz",
"integrity": "sha512-pHAeZbjjNRa/hxyNuLrvbxhhnKyKNiLC6I5fRF2Zr/t/S6zS41MiyzH4+c+1I9vVfvuRt1VS2Lodjr4ZWnxrdA==",
"version": "16.9.43",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.43.tgz",
"integrity": "sha512-PxshAFcnJqIWYpJbLPriClH53Z2WlJcVZE+NP2etUtWQs2s7yIMj3/LDKZT/5CHJ/F62iyjVCDu2H3jHEXIxSg==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
@@ -7818,6 +7811,14 @@
"@types/react": "*"
}
},
"@types/react-gravatar": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/@types/react-gravatar/-/react-gravatar-2.6.8.tgz",
"integrity": "sha512-VMk0bF0w72l+opBm+EqLs0JqUG+hPowMBWCVGrbTwUWm/oDncvwNrf7P/ImwYwkTCKiLnU8Rc+/lyhehaIE/Rw==",
"requires": {
"@types/react": "*"
}
},
"@types/react-json-tree": {
"version": "0.6.11",
"resolved": "https://registry.npmjs.org/@types/react-json-tree/-/react-json-tree-0.6.11.tgz",
@@ -7836,31 +7837,32 @@
}
},
"@types/react-redux": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.7.tgz",
"integrity": "sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg==",
"version": "5.0.21",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-5.0.21.tgz",
"integrity": "sha512-ewkOW4GjnyXq5L++T31utI8yRmwj8iCIahZohYi1Ef7Xkrw0V/q92ao7x20rm38FKgImDaCCsaRGWfCJmF/Ukg==",
"dev": true,
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
},
"dependencies": {
"react-is": {
"version": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz",
"integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA=="
},
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
"integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
"dev": true,
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
}
}
"redux": "^3.6.0"
}
},
"@types/react-router": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
"integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==",
"requires": {
"@types/history": "*",
"@types/react": "*"
}
},
"@types/react-router-dom": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.5.tgz",
"integrity": "sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==",
"requires": {
"@types/history": "*",
"@types/react": "*",
"@types/react-router": "*"
}
},
"@types/react-select": {
@@ -7923,6 +7925,25 @@
"redux": "^3.6.0"
}
},
"@types/redux-mock-store": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.2.tgz",
"integrity": "sha512-6LBtAQBN34i7SI5X+Qs4zpTEZO1tTDZ6sZ9fzFjYwTl3nLQXaBtwYdoV44CzNnyKu438xJ1lSIYyw0YMvunESw==",
"requires": {
"redux": "^4.0.5"
},
"dependencies": {
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
"integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
}
}
}
},
"@types/rison": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/rison/-/rison-0.0.6.tgz",
@@ -7933,6 +7954,19 @@
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
"integrity": "sha1-gJPuBBam4r8qpjOBCRFLP7/6Dps="
},
"@types/sinon": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz",
"integrity": "sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==",
"requires": {
"@types/sinonjs__fake-timers": "*"
}
},
"@types/sinonjs__fake-timers": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz",
"integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA=="
},
"@types/sizzle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
@@ -19746,18 +19780,18 @@
"integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg=="
},
"match-sorter": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-4.1.0.tgz",
"integrity": "sha512-DCzT9JVO2FWVOTfsKqIqVhu/skFa3bK0lQom70j6Co9yKX9bPn2gRtn9BFD9ykkM8F/USjTQeId+nlFfTVvz+w==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-4.2.0.tgz",
"integrity": "sha512-oEvLn8R+a30YZ9l5XdCTkYQuLsOs8frxEqQTAuxoqkQx/qV5pQpx/NqAWvJ5xbYecqfXoF/ZevaIS1+NkbRymg==",
"requires": {
"@babel/runtime": "^7.9.2",
"@babel/runtime": "^7.10.5",
"remove-accents": "0.4.2"
},
"dependencies": {
"@babel/runtime": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz",
"integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==",
"version": "7.10.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz",
"integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==",
"requires": {
"regenerator-runtime": "^0.13.4"
}

View File

@@ -1,6 +1,6 @@
{
"name": "superset",
"version": "0.999.0dev",
"version": "0.37.0",
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
"license": "Apache-2.0",
"directories": {
@@ -85,13 +85,13 @@
"@superset-ui/legacy-plugin-chart-sankey": "^0.14.9",
"@superset-ui/legacy-plugin-chart-sankey-loop": "^0.14.9",
"@superset-ui/legacy-plugin-chart-sunburst": "^0.14.9",
"@superset-ui/plugin-chart-table": "^0.14.9",
"@superset-ui/legacy-plugin-chart-treemap": "^0.14.9",
"@superset-ui/legacy-plugin-chart-world-map": "^0.14.9",
"@superset-ui/legacy-preset-chart-big-number": "^0.14.9",
"@superset-ui/legacy-preset-chart-deckgl": "^0.2.4",
"@superset-ui/legacy-preset-chart-nvd3": "^0.14.9",
"@superset-ui/number-format": "^0.14.9",
"@superset-ui/plugin-chart-table": "^0.14.11",
"@superset-ui/plugin-chart-word-cloud": "^0.14.9",
"@superset-ui/preset-chart-xy": "^0.14.9",
"@superset-ui/query": "^0.14.9",
@@ -102,15 +102,19 @@
"@superset-ui/validator": "^0.14.9",
"@types/classnames": "^2.2.9",
"@types/enzyme": "^3.10.5",
"@types/fetch-mock": "^7.3.2",
"@types/react-bootstrap": "^0.32.21",
"@types/react-gravatar": "^2.6.8",
"@types/react-json-tree": "^0.6.11",
"@types/react-router-dom": "^5.1.5",
"@types/react-select": "^3.0.12",
"@types/react-virtualized": "^9.21.10",
"@types/react-window": "^1.8.2",
"@types/redux-localstorage": "^1.0.8",
"@types/redux-mock-store": "^1.0.2",
"@types/rison": "0.0.6",
"@types/sinon": "^9.0.4",
"@vx/responsive": "^0.0.195",
"memoize-one": "^5.1.1",
"abortcontroller-polyfill": "^1.1.9",
"aphrodite": "^2.3.1",
"array-move": "^2.2.1",
@@ -134,6 +138,7 @@
"lodash": "^4.17.15",
"lodash-es": "^4.17.14",
"mathjs": "^3.20.2",
"memoize-one": "^5.1.1",
"moment": "^2.20.1",
"mousetrap": "^1.6.1",
"mustache": "^2.2.1",
@@ -205,10 +210,10 @@
"@types/dom-to-image": "^2.6.0",
"@types/jest": "^26.0.3",
"@types/jquery": "^3.3.32",
"@types/react": "^16.9.38",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-json-tree": "^0.6.11",
"@types/react-redux": "^7.1.7",
"@types/react-redux": "^5.0.2",
"@types/react-table": "^7.0.19",
"@types/react-ultimate-pagination": "^1.2.0",
"@types/yargs": "12 - 15",

View File

@@ -35,6 +35,7 @@ describe('controlUtils', () => {
columns: ['a', 'b', 'c'],
metrics: [{ metric_name: 'first' }, { metric_name: 'second' }],
},
controls: {},
};
beforeAll(() => {
@@ -250,9 +251,10 @@ describe('controlUtils', () => {
it('should not apply mapStateToProps when initializing', () => {
const control = getControlState('metrics', 'table', {
...state,
isInitializing: true,
controls: undefined,
});
expect(control.default).toEqual(null);
expect(typeof control.default).toBe('function');
expect(control.value).toBe(undefined);
});
});
@@ -261,6 +263,15 @@ describe('controlUtils', () => {
const control = getControlState('metric', 'table', state, null);
expect(control.validationErrors).toEqual(['cannot be empty']);
});
it('should not validate if control panel is initializing', () => {
const control = getControlState(
'metric',
'table',
{ ...state, controls: undefined },
undefined,
);
expect(control.validationErrors).toBeUndefined();
});
});
describe('queryFields', () => {

View File

@@ -18,6 +18,7 @@
*/
import sinon from 'sinon';
import * as actions from 'src/SqlLab/actions/sqlLab';
import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement';
export const mockedActions = sinon.stub({ ...actions });
@@ -68,7 +69,7 @@ export const table = {
keys: [
{
column_names: ['id'],
type: 'pk',
type: 'pk' as ColumnKeyTypeType,
name: null,
},
],
@@ -84,14 +85,14 @@ export const table = {
name: 'slices_ibfk_1',
referred_columns: ['id'],
referred_table: 'datasources',
type: 'fk',
type: 'fk' as ColumnKeyTypeType,
referred_schema: 'carapal',
options: {},
},
{
unique: false,
column_names: ['druid_datasource_id'],
type: 'index',
type: 'index' as ColumnKeyTypeType,
name: 'druid_datasource_id',
},
],
@@ -283,7 +284,7 @@ export const queries = [
export const queryWithBadColumns = {
...queries[0],
results: {
data: queries[0].results.data,
data: queries[0].results?.data,
selected_columns: [
{
is_date: true,
@@ -320,6 +321,11 @@ export const queryWithBadColumns = {
name: '_TIMESTAMP',
type: 'TIMESTAMP',
},
{
is_date: true,
name: '__TIME',
type: 'TIMESTAMP',
},
{
is_date: true,
name: '__TIMESTAMP',

View File

@@ -72,12 +72,16 @@ describe('utils/common', () => {
});
it('changes formatting of temporal column', () => {
const originalData = [
{ __timestamp: 1594285437771, column1: 'lorem' },
{ __timestamp: 1594285441675, column1: 'ipsum' },
{ __timestamp: null, column1: 'lorem' },
{ __timestamp: 0, column1: 'ipsum' },
{ __timestamp: 1594285437771, column1: 'dolor' },
{ __timestamp: 1594285441675, column1: 'sit' },
];
const expectedData = [
{ __timestamp: '2020-07-09 09:03:57', column1: 'lorem' },
{ __timestamp: '2020-07-09 09:04:01', column1: 'ipsum' },
{ __timestamp: null, column1: 'lorem' },
{ __timestamp: '1970-01-01 00:00:00', column1: 'ipsum' },
{ __timestamp: '2020-07-09 09:03:57', column1: 'dolor' },
{ __timestamp: '2020-07-09 09:04:01', column1: 'sit' },
];
expect(applyFormattingToTabularData(originalData)).toEqual(expectedData);
});

View File

@@ -105,7 +105,7 @@ describe('ChartList', () => {
const callsD = fetchMock.calls(/chart\/\?q/);
expect(callsD).toHaveLength(1);
expect(callsD[0][0]).toMatchInlineSnapshot(
`"http://localhost/api/v1/chart/?q=(order_column:changed_on,order_direction:desc,page:0,page_size:25)"`,
`"http://localhost/api/v1/chart/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
);
});
});

View File

@@ -43,7 +43,8 @@ const mockDashboards = [...new Array(3)].map((_, i) => ({
changed_by_url: 'changed_by_url',
changed_by_fk: 1,
published: true,
changed_on: new Date().toISOString(),
changed_on_utc: new Date().toISOString(),
changed_on_delta_humanized: '5 minutes ago',
owners: [{ first_name: 'admin', last_name: 'admin_user' }],
}));
@@ -95,7 +96,7 @@ describe('DashboardList', () => {
const callsD = fetchMock.calls(/dashboard\/\?q/);
expect(callsD).toHaveLength(1);
expect(callsD[0][0]).toMatchInlineSnapshot(
`"http://localhost/api/v1/dashboard/?q=(order_column:changed_on,order_direction:desc,page:0,page_size:25)"`,
`"http://localhost/api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
);
});
it('edits', () => {

View File

@@ -55,7 +55,7 @@ describe('DashboardTable', () => {
expect(fetchMock.calls(dashboardsEndpoint)).toHaveLength(1);
// there's a delay between response and updating state, so manually set it
// rather than adding a timeout which could introduce flakiness
wrapper.setState({ dashaboards: mockDashboards });
wrapper.setState({ dashboards: mockDashboards });
expect(wrapper.find(ListView)).toHaveLength(1);
done();
});

View File

@@ -23,11 +23,21 @@ import { shallow } from 'enzyme';
import Welcome from 'src/welcome/Welcome';
describe('Welcome', () => {
const mockedProps = {};
const mockedProps = {
user: {
username: 'alpha',
firstName: 'alpha',
lastName: 'alpha',
createdOn: '2016-11-11T12:34:17',
userId: 5,
email: 'alpha@alpha.com',
isActive: true,
},
};
it('is valid', () => {
expect(React.isValidElement(<Welcome {...mockedProps} />)).toBe(true);
});
it('renders 4 Tab, Panel, and Row components', () => {
it('renders 3 Tab, Panel, and Row components', () => {
const wrapper = shallow(<Welcome {...mockedProps} />);
expect(wrapper.find(Tab)).toHaveLength(3);
expect(wrapper.find(Panel)).toHaveLength(3);

View File

@@ -36,13 +36,22 @@ const tooltipTitleMap = {
index: 'Index',
};
export default function ColumnElement(props) {
const col = props.column;
let name = col.name;
export type ColumnKeyTypeType = keyof typeof tooltipTitleMap;
interface ColumnElementProps {
column: {
name: string;
keys?: { type: ColumnKeyTypeType }[];
type: string;
};
}
export default function ColumnElement({ column }: ColumnElementProps) {
let name: React.ReactNode = column.name;
let icons;
if (col.keys && col.keys.length > 0) {
name = <strong>{col.name}</strong>;
icons = col.keys.map((key, i) => (
if (column.keys && column.keys.length > 0) {
name = <strong>{column.name}</strong>;
icons = column.keys.map((key, i) => (
<span key={i} className="ColumnElement">
<OverlayTrigger
placement="right"
@@ -68,7 +77,7 @@ export default function ColumnElement(props) {
{icons}
</div>
<div className="pull-right text-muted">
<small> {col.type}</small>
<small> {column.type}</small>
</div>
</div>
);

View File

@@ -104,7 +104,7 @@ class ExploreResultsButton extends React.PureComponent {
getInvalidColumns() {
const re1 = /^[A-Za-z_]\w*$/; // starts with char or _, then only alphanum
const re2 = /__\d+$/; // does not finish with __ and then a number which screams dup col name
const re3 = /^__/; // is not a reserved column name e.g. __timestamp
const re3 = /^__timestamp/i; // is not a reserved temporal column alias
return this.props.query.results.selected_columns
.map(col => col.name)
@@ -199,9 +199,10 @@ class ExploreResultsButton extends React.PureComponent {
<strong>AS my_alias</strong>
</code>
){' '}
{t(`limited to alphanumeric characters and underscores. Column aliases starting
with double underscores or ending with double underscores followed by a
numeric value are not allowed for reasons discussed in Github issue #5739.
{t(`limited to alphanumeric characters and underscores. The alias "__timestamp"
used as for the temporal expression and column aliases ending with
double underscores followed by a numeric value are not allowed for reasons
discussed in Github issue #5739.
`)}
</div>
);

View File

@@ -158,6 +158,7 @@ export default class ResultSet extends React.PureComponent<
this.props.database &&
this.props.database.allows_virtual_table_explore && (
<ExploreResultsButton
// @ts-ignore Redux types are difficult to work with, ignoring for now
query={this.props.query}
database={this.props.database}
actions={this.props.actions}
@@ -246,6 +247,7 @@ export default class ResultSet extends React.PureComponent<
{t('Query in a new tab')}
</Button>
<ExploreCtasResultsButton
// @ts-ignore Redux types are difficult to work with, ignoring for now
table={tempTable}
schema={tempSchema}
dbId={exploreDBId}

View File

@@ -17,58 +17,48 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import SyntaxHighlighter, {
registerLanguage,
// @ts-ignore
} from 'react-syntax-highlighter/dist/light';
// @ts-ignore
import sql from 'react-syntax-highlighter/dist/languages/hljs/sql';
// @ts-ignore
import github from 'react-syntax-highlighter/dist/styles/hljs/github';
import { t } from '@superset-ui/translation';
import Link from '../../components/Link';
import ModalTrigger from '../../components/ModalTrigger';
registerLanguage('sql', sql);
const propTypes = {
tooltipText: PropTypes.string,
title: PropTypes.string,
sql: PropTypes.string,
};
const defaultProps = {
tooltipText: t('Show SQL'),
title: t('SQL statement'),
sql: '',
};
export default class ShowSQL extends React.PureComponent {
renderModalBody() {
return (
<div>
<SyntaxHighlighter language="sql" style={github}>
{this.props.sql}
</SyntaxHighlighter>
</div>
);
}
render() {
return (
<ModalTrigger
modalTitle={this.props.title}
triggerNode={
<Link
className="fa fa-eye pull-left m-l-2"
tooltip={this.props.tooltipText}
href="#"
/>
}
modalBody={this.renderModalBody()}
/>
);
}
interface ShowSQLProps {
sql: string;
title: string;
tooltipText: string;
}
ShowSQL.propTypes = propTypes;
ShowSQL.defaultProps = defaultProps;
export default function ShowSQL({
tooltipText,
title,
sql: sqlString,
}: ShowSQLProps) {
return (
<ModalTrigger
modalTitle={title}
triggerNode={
<Link
className="fa fa-eye pull-left m-l-2"
tooltip={tooltipText}
href="#"
/>
}
modalBody={
<div>
<SyntaxHighlighter language="sql" style={github}>
{sqlString}
</SyntaxHighlighter>
</div>
}
/>
);
}

View File

@@ -143,7 +143,7 @@ class Chart extends React.PureComponent {
return (
<ErrorMessageWithStackTrace
error={queryResponse?.errors?.[0]}
message={chartAlert}
message={chartAlert || queryResponse?.message}
link={queryResponse ? queryResponse.link : null}
stackTrace={chartStackTrace}
/>

View File

@@ -66,15 +66,14 @@ class ControlPanelsContainer extends React.Component {
renderControl({ name, config }) {
const { actions, controls, exploreState, form_data: formData } = this.props;
const { visibility } = config;
// If the control item is not an object, we have to look up the control data from
// the centralized controls file.
// When it is an object we read control data straight from `config` instead
const controlData = {
...controls[name],
...config,
...controls[name],
name,
// apply current value in formData
value: formData[name],
};
const {
validationErrors,

View File

@@ -36,18 +36,17 @@ export function getFormDataFromControls(controlsState) {
export function validateControl(control, processedState) {
const validators = control.validators;
const validationErrors = [];
if (validators && validators.length > 0) {
const validatedControl = { ...control };
const validationErrors = [];
validators.forEach(f => {
const v = f.call(control, control.value, processedState);
if (v) {
validationErrors.push(v);
}
});
return { ...validatedControl, validationErrors };
}
return control;
// always reset validation errors even when there is no validator
return { ...control, validationErrors };
}
/**
@@ -88,17 +87,6 @@ export const getControlConfig = memoizeOne(function getControlConfig(
return control?.config || control;
});
export function applyMapStateToPropsToControl(controlState, controlPanelState) {
const { mapStateToProps } = controlState;
if (mapStateToProps && controlPanelState) {
return {
...controlState,
...mapStateToProps(controlPanelState, controlState),
};
}
return controlState;
}
function handleMissingChoice(control) {
// If the value is not valid anymore based on choices, clear it
const value = control.value;
@@ -121,6 +109,36 @@ function handleMissingChoice(control) {
return control;
}
export function applyMapStateToPropsToControl(controlState, controlPanelState) {
const { mapStateToProps } = controlState;
let { value } = controlState;
let state = { ...controlState };
if (mapStateToProps && controlPanelState) {
state = {
...controlState,
...mapStateToProps(controlPanelState, controlState),
};
}
// If default is a function, evaluate it
if (typeof state.default === 'function') {
state.default = state.default(state, controlPanelState);
// if default is still a function, discard
if (typeof state.default === 'function') {
delete state.default;
}
}
// If no current value, set it as default
if (state.default && value === undefined) {
value = state.default;
}
// If a choice control went from multi=false to true, wrap value in array
if (value && state.multi && !Array.isArray(value)) {
value = [value];
}
state.value = value;
return validateControl(handleMissingChoice(state), state);
}
export function getControlStateFromControlConfig(
controlConfig,
controlPanelState,
@@ -130,38 +148,16 @@ export function getControlStateFromControlConfig(
if (!controlConfig) {
return null;
}
let controlState = { ...controlConfig };
const controlState = { ...controlConfig, value };
// only apply mapStateToProps when control states have been initialized
// or when explicitly didn't provide control panel state (mostly for testing)
if (
controlPanelState &&
(controlPanelState.controls || !controlPanelState.isInitializing)
(controlPanelState && controlPanelState.controls) ||
controlPanelState === null
) {
controlState = applyMapStateToPropsToControl(
controlState,
controlPanelState,
);
return applyMapStateToPropsToControl(controlState, controlPanelState);
}
// If default is a function, evaluate it
if (typeof controlState.default === 'function') {
controlState.default = controlState.default(
controlState,
controlPanelState,
);
// if default is still a function, discard
if (typeof controlState.default === 'function') {
delete controlState.default;
}
}
// If a choice control went from multi=false to true, wrap value in array
const controlValue =
controlConfig.multi && value && !Array.isArray(value) ? [value] : value;
controlState.value =
typeof controlValue === 'undefined' ? controlState.default : controlValue;
return validateControl(handleMissingChoice(controlState), controlState);
return controlState;
}
export function getControlState(controlKey, vizType, state, value) {

View File

@@ -41,7 +41,6 @@ export default function getInitialState(bootstrapData) {
filterColumnOpts: [],
isDatasourceMetaLoading: false,
isStarred: false,
isInitializing: true,
};
const controls = getControlsState(bootstrappedState, rawFormData);
bootstrappedState.controls = controls;
@@ -55,7 +54,6 @@ export default function getInitialState(bootstrapData) {
bootstrappedState,
);
});
bootstrappedState.isInitializing = false;
const sliceFormData = slice
? getFormDataFromControls(getControlsState(bootstrapData, slice.form_data))

View File

@@ -29,7 +29,7 @@ import {
} from '../actions';
// To work properly the redux state must have a `messageToasts` subtree
export default function withToasts(BaseComponent: ComponentType) {
export default function withToasts(BaseComponent: ComponentType<any>) {
return connect(null, dispatch =>
bindActionCreators(
{
@@ -41,6 +41,6 @@ export default function withToasts(BaseComponent: ComponentType) {
dispatch,
),
)(BaseComponent) as any;
// Rsedux has some confusing typings that cause problems for consumers of this function.
// Redux has some confusing typings that cause problems for consumers of this function.
// If someone can fix the types, great, but for now it's just any.
}

View File

@@ -33,7 +33,7 @@ setupApp();
const profileViewContainer = document.getElementById('app');
const bootstrap = JSON.parse(
profileViewContainer.getAttribute('data-bootstrap'),
profileViewContainer?.getAttribute('data-bootstrap') ?? '{}',
);
const store = createStore(

View File

@@ -17,7 +17,6 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Col, Row, Tabs, Tab, Panel } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
@@ -26,17 +25,18 @@ import UserInfo from './UserInfo';
import Security from './Security';
import RecentActivity from './RecentActivity';
import CreatedContent from './CreatedContent';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
const propTypes = {
user: PropTypes.object.isRequired,
};
interface AppProps {
user: UserWithPermissionsAndRoles;
}
export default function App(props) {
export default function App({ user }: AppProps) {
return (
<div className="container app">
<Row>
<Col md={3}>
<UserInfo user={props.user} />
<UserInfo user={user} />
</Col>
<Col md={9}>
<Tabs id="options">
@@ -50,7 +50,7 @@ export default function App(props) {
>
<Panel>
<Panel.Body>
<Favorites user={props.user} />
<Favorites user={user} />
</Panel.Body>
</Panel>
</Tab>
@@ -64,7 +64,7 @@ export default function App(props) {
>
<Panel>
<Panel.Body>
<CreatedContent user={props.user} />
<CreatedContent user={user} />
</Panel.Body>
</Panel>
</Tab>
@@ -78,7 +78,7 @@ export default function App(props) {
>
<Panel>
<Panel.Body>
<RecentActivity user={props.user} />
<RecentActivity user={user} />
</Panel.Body>
</Panel>
</Tab>
@@ -92,7 +92,7 @@ export default function App(props) {
>
<Panel>
<Panel.Body>
<Security user={props.user} />
<Security user={user} />
</Panel.Body>
</Panel>
</Tab>
@@ -102,4 +102,3 @@ export default function App(props) {
</div>
);
}
App.propTypes = propTypes;

View File

@@ -17,28 +17,20 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { t } from '@superset-ui/translation';
import TableLoader from '../../components/TableLoader';
import { Slice } from '../types';
import { User, Dashboard } from '../../types/bootstrapTypes';
const propTypes = {
user: PropTypes.object.isRequired,
};
interface CreatedContentProps {
user: User;
}
class CreatedContent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
dashboardsLoading: true,
slicesLoading: true,
dashboards: [],
slices: [],
};
}
class CreatedContent extends React.PureComponent<CreatedContentProps> {
renderSliceTable() {
const mutator = data =>
const mutator = (data: Slice[]) =>
data.map(slice => ({
slice: <a href={slice.url}>{slice.title}</a>,
favorited: moment.utc(slice.dttm).fromNow(),
@@ -56,7 +48,7 @@ class CreatedContent extends React.PureComponent {
);
}
renderDashboardTable() {
const mutator = data =>
const mutator = (data: Dashboard[]) =>
data.map(dash => ({
dashboard: <a href={dash.url}>{dash.title}</a>,
favorited: moment.utc(dash.dttm).fromNow(),
@@ -85,6 +77,5 @@ class CreatedContent extends React.PureComponent {
);
}
}
CreatedContent.propTypes = propTypes;
export default CreatedContent;

View File

@@ -17,28 +17,20 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { t } from '@superset-ui/translation';
import TableLoader from '../../components/TableLoader';
import { Slice } from '../types';
import { User, Dashboard } from '../../types/bootstrapTypes';
const propTypes = {
user: PropTypes.object.isRequired,
};
interface FavoritesProps {
user: User;
}
export default class Favorites extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
dashboardsLoading: true,
slicesLoading: true,
dashboards: [],
slices: [],
};
}
export default class Favorites extends React.PureComponent<FavoritesProps> {
renderSliceTable() {
const mutator = data =>
const mutator = (data: Slice[]) =>
data.map(slice => ({
slice: <a href={slice.url}>{slice.title}</a>,
creator: <a href={slice.creator_url}>{slice.creator}</a>,
@@ -57,7 +49,7 @@ export default class Favorites extends React.PureComponent {
);
}
renderDashboardTable() {
const mutator = data =>
const mutator = (data: Dashboard[]) =>
data.map(dash => ({
dashboard: <a href={dash.url}>{dash.title}</a>,
creator: <a href={dash.creator_url}>{dash.creator}</a>,
@@ -86,4 +78,3 @@ export default class Favorites extends React.PureComponent {
);
}
}
Favorites.propTypes = propTypes;

View File

@@ -17,39 +17,36 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import TableLoader from '../../components/TableLoader';
import { Activity } from '../types';
import { User } from '../../types/bootstrapTypes';
const propTypes = {
user: PropTypes.object,
};
export default class RecentActivity extends React.PureComponent {
render() {
const rowLimit = 50;
const mutator = function (data) {
return data
.filter(row => row.action === 'dashboard' || row.action === 'explore')
.map(row => ({
name: <a href={row.item_url}>{row.item_title}</a>,
type: row.action,
time: moment.utc(row.time).fromNow(),
_time: row.time,
}));
};
return (
<div>
<TableLoader
className="table table-condensed"
mutator={mutator}
sortable
dataEndpoint={`/superset/recent_activity/${this.props.user.userId}/?limit=${rowLimit}`}
/>
</div>
);
}
interface RecentActivityProps {
user: User;
}
RecentActivity.propTypes = propTypes;
export default function RecentActivity({ user }: RecentActivityProps) {
const rowLimit = 50;
const mutator = function (data: Activity[]) {
return data
.filter(row => row.action === 'dashboard' || row.action === 'explore')
.map(row => ({
name: <a href={row.item_url}>{row.item_title}</a>,
type: row.action,
time: moment.utc(row.time).fromNow(),
_time: row.time,
}));
};
return (
<div>
<TableLoader
className="table table-condensed"
mutator={mutator}
sortable
dataEndpoint={`/superset/recent_activity/${user.userId}/?limit=${rowLimit}`}
/>
</div>
);
}

View File

@@ -17,14 +17,15 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Badge, Label } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
const propTypes = {
user: PropTypes.object.isRequired,
};
export default function Security({ user }) {
interface SecurityProps {
user: UserWithPermissionsAndRoles;
}
export default function Security({ user }: SecurityProps) {
return (
<div>
<div className="roles">
@@ -66,4 +67,3 @@ export default function Security({ user }) {
</div>
);
}
Security.propTypes = propTypes;

View File

@@ -1,75 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import Gravatar from 'react-gravatar';
import moment from 'moment';
import { Panel } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
const propTypes = {
user: PropTypes.object.isRequired,
};
const UserInfo = ({ user }) => (
<div>
<a href="https://en.gravatar.com/">
<Gravatar
email={user.email}
width="100%"
height=""
size={220}
alt={t('Profile picture provided by Gravatar')}
className="img-rounded"
style={{ borderRadius: 15 }}
/>
</a>
<hr />
<Panel>
<Panel.Body>
<h3>
<strong>
{user.firstName} {user.lastName}
</strong>
</h3>
<h4 className="username">
<i className="fa fa-user-o" /> {user.username}
</h4>
<hr />
<p>
<i className="fa fa-clock-o" /> {t('joined')}{' '}
{moment(user.createdOn, 'YYYYMMDD').fromNow()}
</p>
<p className="email">
<i className="fa fa-envelope-o" /> {user.email}
</p>
<p className="roles">
<i className="fa fa-lock" /> {Object.keys(user.roles).join(', ')}
</p>
<p>
<i className="fa fa-key" />
&nbsp;
<span className="text-muted">{t('id:')}</span>&nbsp;
<span className="user-id">{user.userId}</span>
</p>
</Panel.Body>
</Panel>
</div>
);
UserInfo.propTypes = propTypes;
export default UserInfo;

View File

@@ -0,0 +1,76 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import Gravatar from 'react-gravatar';
import moment from 'moment';
import { Panel } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
interface UserInfoProps {
user: UserWithPermissionsAndRoles;
}
export default function UserInfo({ user }: UserInfoProps) {
return (
<div>
<a href="https://en.gravatar.com/">
<Gravatar
email={user.email}
width="100%"
height=""
size={220}
alt={t('Profile picture provided by Gravatar')}
className="img-rounded"
style={{ borderRadius: 15 }}
/>
</a>
<hr />
<Panel>
<Panel.Body>
<h3>
<strong>
{user.firstName} {user.lastName}
</strong>
</h3>
<h4 className="username">
<i className="fa fa-user-o" /> {user.username}
</h4>
<hr />
<p>
<i className="fa fa-clock-o" /> {t('joined')}{' '}
{moment(user.createdOn, 'YYYYMMDD').fromNow()}
</p>
<p className="email">
<i className="fa fa-envelope-o" /> {user.email}
</p>
<p className="roles">
<i className="fa fa-lock" /> {Object.keys(user.roles).join(', ')}
</p>
<p>
<i className="fa fa-key" />
&nbsp;
<span className="text-muted">{t('id:')}</span>&nbsp;
<span className="user-id">{user.userId}</span>
</p>
</Panel.Body>
</Panel>
</div>
);
}

View File

@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export type Slice = {
dttm: number;
id: number;
url: string;
title: string;
creator?: string;
creator_url?: string;
viz_type: string;
};
export type Activity = {
action: string;
item_title: string;
item_url: string;
time: number;
};

View File

@@ -0,0 +1,44 @@
/**
* 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 type User = {
createdOn: string;
email: string;
firstName: string;
isActive: boolean;
lastName: string;
userId: number;
username: string;
};
export interface UserWithPermissionsAndRoles extends User {
permissions: {
database_access?: string[];
datasource_access?: string[];
};
roles: Record<string, string[][]>;
}
export type Dashboard = {
dttm: number;
id: number;
url: string;
title: string;
creator?: string;
creator_url?: string;
};

View File

@@ -128,7 +128,11 @@ export function applyFormattingToTabularData(data) {
}
return data.map(row => ({
...row,
// eslint-disable-next-line no-underscore-dangle
__timestamp: DATETIME_FORMATTER(new Date(row.__timestamp)),
/* eslint-disable no-underscore-dangle */
__timestamp:
row.__timestamp === 0 || row.__timestamp
? DATETIME_FORMATTER(new Date(row.__timestamp))
: row.__timestamp,
/* eslint-enable no-underscore-dangle */
}));
}

View File

@@ -19,7 +19,6 @@
import { SupersetClient } from '@superset-ui/connection';
import { t } from '@superset-ui/translation';
import { getChartMetadataRegistry } from '@superset-ui/chart';
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import rison from 'rison';
@@ -108,7 +107,7 @@ class ChartList extends React.PureComponent<Props, State> {
return isFeatureEnabled(FeatureFlag.LIST_VIEWS_SIP34_FILTER_UI);
}
initialSort = [{ id: 'changed_on', desc: true }];
initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
columns = [
{
@@ -153,11 +152,11 @@ class ChartList extends React.PureComponent<Props, State> {
{
Cell: ({
row: {
original: { changed_on: changedOn },
original: { changed_on_delta_humanized: changedOn },
},
}: any) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>,
}: any) => <span className="no-wrap">{changedOn}</span>,
Header: t('Last Modified'),
accessor: 'changed_on',
accessor: 'changed_on_delta_humanized',
},
{
accessor: 'description',

View File

@@ -18,7 +18,6 @@
*/
import { SupersetClient } from '@superset-ui/connection';
import { t } from '@superset-ui/translation';
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import rison from 'rison';
@@ -60,7 +59,7 @@ interface Dashboard {
changed_by: string;
changed_by_name: string;
changed_by_url: string;
changed_on: string;
changed_on_delta_humanized: string;
dashboard_title: string;
published: boolean;
url: string;
@@ -123,7 +122,7 @@ class DashboardList extends React.PureComponent<Props, State> {
return isFeatureEnabled(FeatureFlag.LIST_VIEWS_SIP34_FILTER_UI);
}
initialSort = [{ id: 'changed_on', desc: true }];
initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
columns = [
{
@@ -181,11 +180,11 @@ class DashboardList extends React.PureComponent<Props, State> {
{
Cell: ({
row: {
original: { changed_on: changedOn },
original: { changed_on_delta_humanized: changedOn },
},
}: any) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>,
}: any) => <span className="no-wrap">{changedOn}</span>,
Header: t('Modified'),
accessor: 'changed_on',
accessor: 'changed_on_delta_humanized',
},
{
accessor: 'slug',

View File

@@ -44,7 +44,7 @@ setupApp();
setupPlugins();
const container = document.getElementById('app');
const bootstrap = JSON.parse(container.getAttribute('data-bootstrap'));
const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}');
const user = { ...bootstrap.user };
const menu = { ...bootstrap.common.menu_data };
const common = { ...bootstrap.common };

View File

@@ -17,35 +17,44 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { t } from '@superset-ui/translation';
import { SupersetClient } from '@superset-ui/connection';
import moment from 'moment';
import { debounce } from 'lodash';
import ListView from 'src/components/ListView/ListView';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import { Dashboard } from 'src/types/bootstrapTypes';
import { FetchDataConfig } from 'src/components/ListView/types';
const PAGE_SIZE = 25;
class DashboardTable extends React.PureComponent {
static propTypes = {
addDangerToast: PropTypes.func.isRequired,
search: PropTypes.string,
};
interface DashboardTableProps {
addDangerToast: (message: string) => void;
search?: string;
}
interface DashboardTableState {
dashboards: Dashboard[];
dashboard_count: number;
loading: boolean;
}
class DashboardTable extends React.PureComponent<
DashboardTableProps,
DashboardTableState
> {
state = {
dashboards: [],
dashboard_count: 0,
loading: false,
};
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: DashboardTableProps) {
if (prevProps.search !== this.props.search) {
this.fetchDataDebounced({
pageSize: PAGE_SIZE,
pageIndex: 0,
sortBy: this.initialSort,
filters: {},
filters: [],
});
}
}
@@ -58,6 +67,13 @@ class DashboardTable extends React.PureComponent {
row: {
original: { url, dashboard_title: dashboardTitle },
},
}: {
row: {
original: {
url: string;
dashboard_title: string;
};
};
}) => <a href={url}>{dashboardTitle}</a>,
},
{
@@ -67,22 +83,35 @@ class DashboardTable extends React.PureComponent {
row: {
original: { changed_by_name: changedByName, changedByUrl },
},
}: {
row: {
original: {
changed_by_name: string;
changedByUrl: string;
};
};
}) => <a href={changedByUrl}>{changedByName}</a>,
},
{
accessor: 'changed_on',
accessor: 'changed_on_delta_humanized',
Header: 'Modified',
Cell: ({
row: {
original: { changed_on: changedOn },
original: { changed_on_delta_humanized: changedOn },
},
}) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>,
}: {
row: {
original: {
changed_on_delta_humanized: string;
};
};
}) => <span className="no-wrap">{changedOn}</span>,
},
];
initialSort = [{ id: 'changed_on', desc: true }];
initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
fetchData = ({ pageIndex, pageSize, sortBy, filters }) => {
fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => {
this.setState({ loading: true });
const filterExps = Object.keys(filters)
.map(fk => ({

View File

@@ -17,23 +17,30 @@
* under the License.
*/
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Panel, Row, Col, Tabs, Tab, FormControl } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { useQueryParam, StringParam } from 'use-query-params';
import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
import { User } from 'src/types/bootstrapTypes';
import RecentActivity from '../profile/components/RecentActivity';
import Favorites from '../profile/components/Favorites';
import DashboardTable from './DashboardTable';
const propTypes = {
user: PropTypes.object.isRequired,
};
interface WelcomeProps {
user: User;
}
function useSyncQueryState(queryParam, queryParamType, defaultState) {
function useSyncQueryState(
queryParam: string,
queryParamType: QueryParamConfig<
string | null | undefined,
string | undefined
>,
defaultState: string,
): [string, (val: string) => void] {
const [queryState, setQueryState] = useQueryParam(queryParam, queryParamType);
const [state, setState] = useState(queryState || defaultState);
const setQueryStateAndState = val => {
const setQueryStateAndState = (val: string) => {
setQueryState(val);
setState(val);
};
@@ -41,7 +48,7 @@ function useSyncQueryState(queryParam, queryParamType, defaultState) {
return [state, setQueryStateAndState];
}
export default function Welcome({ user }) {
export default function Welcome({ user }: WelcomeProps) {
const [activeTab, setActiveTab] = useSyncQueryState(
'activeTab',
StringParam,
@@ -58,6 +65,7 @@ export default function Welcome({ user }) {
<div className="container welcome">
<Tabs
activeKey={activeTab}
// @ts-ignore React bootstrap types aren't quite right here
onSelect={setActiveTab}
id="uncontrolled-tab-example"
>
@@ -75,6 +83,7 @@ export default function Welcome({ user }) {
style={{ marginTop: '25px' }}
placeholder="Search"
value={searchQuery}
// @ts-ignore React bootstrap types aren't quite right here
onChange={e => setSearchQuery(e.currentTarget.value)}
/>
</Col>
@@ -114,5 +123,3 @@ export default function Welcome({ user }) {
</div>
);
}
Welcome.propTypes = propTypes;

View File

@@ -180,9 +180,9 @@ const config = {
addSlice: addPreamble('/src/addSlice/index.tsx'),
explore: addPreamble('/src/explore/index.jsx'),
dashboard: addPreamble('/src/dashboard/index.jsx'),
sqllab: addPreamble('/src/SqlLab/index.jsx'),
welcome: addPreamble('/src/welcome/index.jsx'),
profile: addPreamble('/src/profile/index.jsx'),
sqllab: addPreamble('/src/SqlLab/index.tsx'),
welcome: addPreamble('/src/welcome/index.tsx'),
profile: addPreamble('/src/profile/index.tsx'),
showSavedQuery: [path.join(APP_DIR, '/src/showSavedQuery/index.jsx')],
},
output,

View File

@@ -101,7 +101,6 @@ class ChartRestApi(BaseSupersetModelRestApi):
"cache_timeout",
]
show_select_columns = show_columns + ["table.id"]
list_columns = [
"id",
"slice_name",
@@ -113,7 +112,8 @@ class ChartRestApi(BaseSupersetModelRestApi):
"changed_by_url",
"changed_by.first_name",
"changed_by.last_name",
"changed_on",
"changed_on_utc",
"changed_on_delta_humanized",
"datasource_id",
"datasource_type",
"datasource_name_text",
@@ -124,13 +124,13 @@ class ChartRestApi(BaseSupersetModelRestApi):
"params",
"cache_timeout",
]
list_select_columns = list_columns + ["changed_on"]
order_columns = [
"slice_name",
"viz_type",
"datasource_name",
"changed_by_fk",
"changed_on",
"changed_on_delta_humanized",
]
search_columns = (
"slice_name",
@@ -419,7 +419,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
@protect()
@safe
@statsd_metrics
def data(self) -> Response:
def data(self) -> Response: # pylint: disable=too-many-return-statements
"""
Takes a query context constructed in the client and returns payload
data response for the given query.
@@ -462,13 +462,16 @@ class ChartRestApi(BaseSupersetModelRestApi):
return self.response_400(message="Request is incorrect")
except ValidationError as error:
return self.response_400(
_("Request is incorrect: %(error)s", error=error.messages)
message=_("Request is incorrect: %(error)s", error=error.messages)
)
try:
query_context.raise_for_access()
except SupersetSecurityException:
return self.response_401()
payload = query_context.get_payload()
for query in payload:
if query.get("error"):
return self.response_400(message=f"Error: {query['error']}")
result_format = query_context.result_format
if result_format == ChartDataResultFormat.CSV:
# return the first result
@@ -488,7 +491,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
resp.headers["Content-Type"] = "application/json; charset=utf-8"
return resp
raise self.response_400(message=f"Unsupported result_format: {result_format}")
return self.response_400(message=f"Unsupported result_format: {result_format}")
@expose("/<pk>/cache_screenshot/", methods=["GET"])
@protect()

View File

@@ -14,15 +14,15 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Any, Dict, Union
from typing import Any, Dict
from flask_babel import gettext as _
from marshmallow import fields, post_load, Schema, validate, ValidationError
from marshmallow import fields, post_load, Schema, validate
from marshmallow.validate import Length, Range
from superset.common.query_context import QueryContext
from superset.exceptions import SupersetException
from superset.utils import core as utils
from superset.utils import schema as utils
from superset.utils.core import FilterOperator
#
# RISON/JSON schemas for query parameters
@@ -100,13 +100,6 @@ openapi_spec_methods_override = {
}
def validate_json(value: Union[bytes, bytearray, str]) -> None:
try:
utils.validate_json(value)
except SupersetException:
raise ValidationError("JSON not valid")
class ChartPostSchema(Schema):
"""
Schema to add a new chart.
@@ -123,7 +116,7 @@ class ChartPostSchema(Schema):
)
owners = fields.List(fields.Integer(description=owners_description))
params = fields.String(
description=params_description, allow_none=True, validate=validate_json
description=params_description, allow_none=True, validate=utils.validate_json
)
cache_timeout = fields.Integer(
description=cache_timeout_description, allow_none=True
@@ -551,8 +544,8 @@ class ChartDataFilterSchema(Schema):
)
op = fields.String( # pylint: disable=invalid-name
description="The comparison operator.",
validate=validate.OneOf(
choices=[filter_op.value for filter_op in utils.FilterOperator]
validate=utils.OneOfCaseInsensitive(
choices=[filter_op.value for filter_op in FilterOperator]
),
required=True,
example="IN",
@@ -687,7 +680,7 @@ class ChartDataQueryObjectSchema(Schema):
timeseries_limit = fields.Integer(
description="Maximum row count for timeseries queries. Default: `0`",
)
timeseries_limit_metric = fields.Integer(
timeseries_limit_metric = fields.Raw(
description="Metric used to limit timeseries queries by.", allow_none=True,
)
row_limit = fields.Integer(

View File

@@ -111,8 +111,7 @@ class QueryContext:
self.df_metrics_to_num(df, query_object)
df.replace([np.inf, -np.inf], np.nan)
df = query_object.exec_post_processing(df)
df = query_object.exec_post_processing(df)
return {
"query": result.query,
@@ -160,10 +159,7 @@ class QueryContext:
df = payload["df"]
status = payload["status"]
if status != utils.QueryStatus.FAILED:
if df.empty:
payload["error"] = "No data"
else:
payload["data"] = self.get_data(df)
payload["data"] = self.get_data(df)
del payload["df"]
if self.result_type == utils.ChartDataResultType.RESULTS:
return {"data": payload["data"]}

View File

@@ -1133,6 +1133,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
sql = query_str_ext.sql
status = utils.QueryStatus.SUCCESS
errors = None
error_message = None
def mutator(df: pd.DataFrame) -> None:
"""
@@ -1163,6 +1164,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
)
db_engine_spec = self.database.db_engine_spec
errors = db_engine_spec.extract_errors(ex)
error_message = utils.error_msg_from_exception(ex)
return QueryResult(
status=status,
@@ -1170,6 +1172,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
duration=datetime.now() - qry_start_dttm,
query=sql,
errors=errors,
error_message=error_message,
)
def get_sqla_table_object(self) -> Table:

View File

@@ -96,7 +96,6 @@ class DashboardRestApi(BaseSupersetModelRestApi):
"table_names",
"thumbnail_url",
]
order_columns = ["dashboard_title", "changed_on", "published", "changed_by_fk"]
list_columns = [
"id",
"published",
@@ -112,13 +111,22 @@ class DashboardRestApi(BaseSupersetModelRestApi):
"changed_by.id",
"changed_by_name",
"changed_by_url",
"changed_on",
"changed_on_utc",
"changed_on_delta_humanized",
"dashboard_title",
"owners.id",
"owners.username",
"owners.first_name",
"owners.last_name",
]
list_select_columns = list_columns + ["changed_on"]
order_columns = [
"dashboard_title",
"changed_on_delta_humanized",
"published",
"changed_by_fk",
]
add_columns = [
"dashboard_title",
"slug",

View File

@@ -25,6 +25,7 @@ from typing import Any, Dict, List, Optional, Set, Union
# pylint: disable=ungrouped-imports
import humanize
import pandas as pd
import pytz
import sqlalchemy as sa
import yaml
from flask import escape, g, Markup
@@ -381,6 +382,15 @@ class AuditMixinNullable(AuditMixin):
def changed_on_(self) -> Markup:
return Markup(f'<span class="no-wrap">{self.changed_on}</span>')
@renders("changed_on")
def changed_on_delta_humanized(self) -> str:
return self.changed_on_humanized
@renders("changed_on")
def changed_on_utc(self) -> str:
# Convert naive datetime to UTC
return self.changed_on.astimezone(pytz.utc).strftime("%Y-%m-%dT%H:%M:%S.%f%z")
@property
def changed_on_humanized(self) -> str:
return humanize.naturaltime(datetime.now() - self.changed_on)

54
superset/utils/schema.py Normal file
View File

@@ -0,0 +1,54 @@
# 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.
from typing import Any, Union
from marshmallow import validate, ValidationError
from superset.exceptions import SupersetException
from superset.utils import core as utils
class OneOfCaseInsensitive(validate.OneOf):
"""
Marshmallow validator that's based on the built-in `OneOf`, but performs
validation case insensitively.
"""
def __call__(self, value: Any) -> str:
try:
if (value.lower() if isinstance(value, str) else value) not in [
choice.lower() if isinstance(choice, str) else choice
for choice in self.choices
]:
raise ValidationError(self._format_error(value))
except TypeError as error:
raise ValidationError(self._format_error(value)) from error
return value
def validate_json(value: Union[bytes, bytearray, str]) -> None:
"""
JSON Validator that can be passed to a Marshmallow `Field`'s validate argument.
:raises ValidationError: if value is not serializable to JSON
:param value: an object that should be parseable to JSON
"""
try:
utils.validate_json(value)
except SupersetException:
raise ValidationError("JSON not valid")

View File

@@ -18,9 +18,11 @@
"""Unit tests for Superset"""
import json
from typing import List, Optional
from datetime import datetime
from unittest import mock
import prison
import humanize
from sqlalchemy.sql import func
from tests.test_app import app
@@ -543,6 +545,34 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin):
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 33)
def test_get_charts_changed_on(self):
"""
Dashboard API: Test get charts changed on
"""
admin = self.get_user("admin")
start_changed_on = datetime.now()
chart = self.insert_chart("foo_a", [admin.id], 1, description="ZY_bar")
self.login(username="admin")
arguments = {
"order_column": "changed_on_delta_humanized",
"order_direction": "desc",
}
uri = f"api/v1/chart/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(
data["result"][0]["changed_on_delta_humanized"],
humanize.naturaltime(datetime.now() - start_changed_on),
)
# rollback changes
db.session.delete(chart)
db.session.commit()
def test_get_charts_filter(self):
"""
Chart API: Test get charts filter
@@ -708,6 +738,94 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin):
result = response_payload["result"][0]
self.assertEqual(result["rowcount"], 5)
def test_chart_data_incorrect_result_type(self):
"""
Chart data API: Test chart data with unsupported result type
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["result_type"] = "qwerty"
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 400)
def test_chart_data_incorrect_result_format(self):
"""
Chart data API: Test chart data with unsupported result format
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["result_format"] = "qwerty"
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 400)
def test_chart_data_query_result_type(self):
"""
Chart data API: Test chart data with query result format
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["result_type"] = "query"
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 200)
def test_chart_data_csv_result_format(self):
"""
Chart data API: Test chart data with CSV result format
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["result_format"] = "csv"
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 200)
def test_chart_data_mixed_case_filter_op(self):
"""
Chart data API: Ensure mixed case filter operator generates valid result
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["queries"][0]["filters"][0]["op"] = "In"
request_payload["queries"][0]["row_limit"] = 10
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
response_payload = json.loads(rv.data.decode("utf-8"))
result = response_payload["result"][0]
self.assertEqual(result["rowcount"], 10)
def test_chart_data_no_data(self):
"""
Chart data API: Test chart data with empty result
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["queries"][0]["filters"] = [
{"col": "gender", "op": "==", "val": "foo"}
]
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 200)
response_payload = json.loads(rv.data.decode("utf-8"))
result = response_payload["result"][0]
self.assertEqual(result["rowcount"], 0)
self.assertEqual(result["data"], [])
def test_chart_data_incorrect_request(self):
"""
Chart data API: Test chart data with invalid SQL
"""
self.login(username="admin")
table = self.get_table_by_name("birth_names")
request_payload = get_query_context(table.name, table.id, table.type)
request_payload["queries"][0]["filters"] = []
# erroneus WHERE-clause
request_payload["queries"][0]["extras"]["where"] = "(gender abc def)"
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
self.assertEqual(rv.status_code, 400)
def test_chart_data_with_invalid_datasource(self):
"""Chart data API: Test chart data query with invalid schema
"""

View File

@@ -26,10 +26,6 @@ from tests.base_tests import SupersetTestCase
from tests.fixtures.query_context import get_query_context
def load_query_context(payload: Dict[str, Any]) -> Tuple[QueryContext, Dict[str, Any]]:
return ChartDataQueryContextSchema().load(payload)
class TestSchema(SupersetTestCase):
def test_query_context_limit_and_offset(self):
self.login(username="admin")
@@ -40,7 +36,7 @@ class TestSchema(SupersetTestCase):
# Use defaults
payload["queries"][0].pop("row_limit", None)
payload["queries"][0].pop("row_offset", None)
query_context = load_query_context(payload)
query_context = ChartDataQueryContextSchema().load(payload)
query_object = query_context.queries[0]
self.assertEqual(query_object.row_limit, app.config["ROW_LIMIT"])
self.assertEqual(query_object.row_offset, 0)
@@ -66,10 +62,32 @@ class TestSchema(SupersetTestCase):
table_name = "birth_names"
table = self.get_table_by_name(table_name)
payload = get_query_context(table.name, table.id, table.type)
payload["queries"][0]["extras"]["time_grain_sqla"] = None
_ = ChartDataQueryContextSchema().load(payload)
def test_query_context_series_limit(self):
self.login(username="admin")
table_name = "birth_names"
table = self.get_table_by_name(table_name)
payload = get_query_context(table.name, table.id, table.type)
payload["queries"][0]["timeseries_limit"] = 2
payload["queries"][0]["timeseries_limit_metric"] = {
"expressionType": "SIMPLE",
"column": {
"id": 334,
"column_name": "gender",
"filterable": True,
"groupby": True,
"is_dttm": False,
"type": "VARCHAR(16)",
"optionName": "_col_gender",
},
"aggregate": "COUNT_DISTINCT",
"label": "COUNT_DISTINCT(gender)",
}
_ = ChartDataQueryContextSchema().load(payload)
def test_query_context_null_post_processing_op(self):
self.login(username="admin")
table_name = "birth_names"

View File

@@ -18,8 +18,10 @@
"""Unit tests for Superset"""
import json
from typing import List, Optional
from datetime import datetime
import prison
import humanize
from sqlalchemy.sql import func
import tests.test_app
@@ -111,7 +113,7 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin):
data = json.loads(rv.data.decode("utf-8"))
self.assertIn("changed_on", data["result"])
for key, value in data["result"].items():
# We can't assert timestamp
# We can't assert timestamp values
if key != "changed_on":
self.assertEqual(value, expected_result[key])
# rollback changes
@@ -152,6 +154,37 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin):
db.session.delete(dashboard)
db.session.commit()
def test_get_dashboards_changed_on(self):
"""
Dashboard API: Test get dashboards changed on
"""
from datetime import datetime
import humanize
admin = self.get_user("admin")
start_changed_on = datetime.now()
dashboard = self.insert_dashboard("title", "slug1", [admin.id])
self.login(username="admin")
arguments = {
"order_column": "changed_on_delta_humanized",
"order_direction": "desc",
}
uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(
data["result"][0]["changed_on_delta_humanized"],
humanize.naturaltime(datetime.now() - start_changed_on),
)
# rollback changes
db.session.delete(dashboard)
db.session.commit()
def test_get_dashboards_filter(self):
"""
Dashboard API: Test get dashboards filter
@@ -214,9 +247,9 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin):
self.assertEqual(data["count"], 3)
expected_response = [
{"slug": "ZY_bar", "dashboard_title": "foo_a",},
{"slug": "slug1zy_", "dashboard_title": "foo_b",},
{"slug": "slug1", "dashboard_title": "zy_foo",},
{"slug": "ZY_bar", "dashboard_title": "foo_a"},
{"slug": "slug1zy_", "dashboard_title": "foo_b"},
{"slug": "slug1", "dashboard_title": "zy_foo"},
]
for index, item in enumerate(data["result"]):
self.assertEqual(item["slug"], expected_response[index]["slug"])

View File

@@ -27,6 +27,7 @@ from unittest.mock import Mock, patch
import numpy
from flask import Flask, g
from flask_caching import Cache
import marshmallow
from sqlalchemy.exc import ArgumentError
import tests.test_app
@@ -60,6 +61,7 @@ from superset.utils.core import (
zlib_compress,
zlib_decompress,
)
from superset.utils import schema
from superset.views.utils import (
build_extra_filters,
get_form_data,
@@ -582,6 +584,8 @@ class TestUtils(SupersetTestCase):
self.assertEqual(jsonObj.process_result_value(val, "dialect"), obj)
def test_validate_json(self):
valid = '{"a": 5, "b": [1, 5, ["g", "h"]]}'
self.assertIsNone(validate_json(valid))
invalid = '{"a": 5, "b": [1, 5, ["g", "h]]}'
with self.assertRaises(SupersetException):
validate_json(invalid)
@@ -1344,3 +1348,20 @@ class TestUtils(SupersetTestCase):
json.loads(record.json)["form_data"]["viz_type"],
slc.viz.form_data["viz_type"],
)
def test_schema_validate_json(self):
valid = '{"a": 5, "b": [1, 5, ["g", "h"]]}'
self.assertIsNone(schema.validate_json(valid))
invalid = '{"a": 5, "b": [1, 5, ["g", "h]]}'
self.assertRaises(marshmallow.ValidationError, schema.validate_json, invalid)
def test_schema_one_of_case_insensitive(self):
validator = schema.OneOfCaseInsensitive(choices=[1, 2, 3, "FoO", "BAR", "baz"])
self.assertEqual(1, validator(1))
self.assertEqual(2, validator(2))
self.assertEqual("FoO", validator("FoO"))
self.assertEqual("FOO", validator("FOO"))
self.assertEqual("bar", validator("bar"))
self.assertEqual("BaZ", validator("BaZ"))
self.assertRaises(marshmallow.ValidationError, validator, "qwerty")
self.assertRaises(marshmallow.ValidationError, validator, 4)