mirror of
https://github.com/apache/superset.git
synced 2026-05-13 11:55:16 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9ec47bc63 | ||
|
|
3f284eaa09 | ||
|
|
08d43b1515 | ||
|
|
c57df314d7 | ||
|
|
2f2fb47721 | ||
|
|
9f0d456b6a | ||
|
|
2631b3882e | ||
|
|
791d787256 | ||
|
|
8d0e6676ef | ||
|
|
522ed20a20 | ||
|
|
38bc62db4b | ||
|
|
1bc26797ad | ||
|
|
1217cb05b3 | ||
|
|
6f41b6ef5f | ||
|
|
5693580dae | ||
|
|
727c1b8ce1 |
413
CHANGELOG.md
413
CHANGELOG.md
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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",
|
||||
|
||||
@@ -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')
|
||||
@@ -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,
|
||||
|
||||
138
superset-frontend/package-lock.json
generated
138
superset-frontend/package-lock.json
generated
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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',
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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)"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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" />
|
||||
|
||||
<span className="text-muted">{t('id:')}</span>
|
||||
<span className="user-id">{user.userId}</span>
|
||||
</p>
|
||||
</Panel.Body>
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
UserInfo.propTypes = propTypes;
|
||||
export default UserInfo;
|
||||
76
superset-frontend/src/profile/components/UserInfo.tsx
Normal file
76
superset-frontend/src/profile/components/UserInfo.tsx
Normal 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" />
|
||||
|
||||
<span className="text-muted">{t('id:')}</span>
|
||||
<span className="user-id">{user.userId}</span>
|
||||
</p>
|
||||
</Panel.Body>
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
superset-frontend/src/profile/types.ts
Normal file
34
superset-frontend/src/profile/types.ts
Normal 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;
|
||||
};
|
||||
44
superset-frontend/src/types/bootstrapTypes.ts
Normal file
44
superset-frontend/src/types/bootstrapTypes.ts
Normal 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;
|
||||
};
|
||||
@@ -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 */
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 };
|
||||
@@ -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 => ({
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"]}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
54
superset/utils/schema.py
Normal 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")
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user