mirror of
https://github.com/apache/superset.git
synced 2026-06-27 18:35:32 +00:00
Compare commits
65 Commits
chore/ci-c
...
0.37.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb8c87e62 | ||
|
|
2fd965c6b3 | ||
|
|
315acf481f | ||
|
|
1f3a93b2c9 | ||
|
|
465572325b | ||
|
|
98329fbd6c | ||
|
|
c30c3b20d6 | ||
|
|
71fc7cbfcf | ||
|
|
30efcc964d | ||
|
|
698bad187f | ||
|
|
8ecfb18ac7 | ||
|
|
8fb6c8361c | ||
|
|
4d73490fa3 | ||
|
|
fa9a558354 | ||
|
|
421694bfa8 | ||
|
|
091c6ae316 | ||
|
|
25d9fb7544 | ||
|
|
029a70b8b9 | ||
|
|
fc94d74aed | ||
|
|
d06e023d88 | ||
|
|
f3bf6cebb7 | ||
|
|
92b3f88b1a | ||
|
|
820d07d6cf | ||
|
|
3aadbc4fde | ||
|
|
3293ef4b6b | ||
|
|
ebb3307c1d | ||
|
|
3b4882d836 | ||
|
|
bc80029f15 | ||
|
|
fb60d302c2 | ||
|
|
13928fcf2d | ||
|
|
1fb14e81f7 | ||
|
|
ddac4b8838 | ||
|
|
024ef9a9e3 | ||
|
|
9c62eb2a7b | ||
|
|
6a85a341ad | ||
|
|
fa787d2de4 | ||
|
|
a72903cb92 | ||
|
|
0dbc1dbb9a | ||
|
|
6d07273360 | ||
|
|
0f3670e1af | ||
|
|
bde453be79 | ||
|
|
7b6f88a272 | ||
|
|
f908770dbc | ||
|
|
1fc06a7443 | ||
|
|
070dd5359c | ||
|
|
f13629abd5 | ||
|
|
c74acdc381 | ||
|
|
5b4277e4fc | ||
|
|
e2690f0802 | ||
|
|
d9ec47bc63 | ||
|
|
3f284eaa09 | ||
|
|
08d43b1515 | ||
|
|
c57df314d7 | ||
|
|
2f2fb47721 | ||
|
|
9f0d456b6a | ||
|
|
2631b3882e | ||
|
|
791d787256 | ||
|
|
8d0e6676ef | ||
|
|
522ed20a20 | ||
|
|
38bc62db4b | ||
|
|
1bc26797ad | ||
|
|
1217cb05b3 | ||
|
|
6f41b6ef5f | ||
|
|
5693580dae | ||
|
|
727c1b8ce1 |
460
CHANGELOG.md
460
CHANGELOG.md
@@ -18,6 +18,466 @@ under the License.
|
||||
-->
|
||||
## Change Log
|
||||
|
||||
### 0.37.2 (2020/09/21 11:45 +00:00)
|
||||
- [#10964](https://github.com/apache/incubator-superset/pull/10964) fix: simply is_adhoc_metric (#10964) (@villebro)
|
||||
- [#10934](https://github.com/apache/incubator-superset/pull/10934) fix(jinja): make context attrs private on SQL templates (#10934) (@dpgaspar)
|
||||
- [#10931](https://github.com/apache/incubator-superset/pull/10931) fix(legacy-druid): undefined filter key (#10931) (@villebro)
|
||||
- [#10451](https://github.com/apache/incubator-superset/pull/10451) fix(chart-data-api): assert referenced columns are present in datasource (#10451) (@villebro)
|
||||
- [#10816](https://github.com/apache/incubator-superset/pull/10816) fix(sql-lab): relax column name restrictions (#10816) (@villebro)
|
||||
- [#10814](https://github.com/apache/incubator-superset/pull/10814) fix: ColorSchemeControl should not use CreatableSelect (#10814) (@ktmud)
|
||||
- [#10805](https://github.com/apache/incubator-superset/pull/10805) Fix: Include RLS filters for cache keys (#10805) (@gtg472b)
|
||||
- [#10774](https://github.com/apache/incubator-superset/pull/10774) fix: pivot table timestamp grouping (#10774) (@villebro)
|
||||
- [#10706](https://github.com/apache/incubator-superset/pull/10706) fix(db-engine-spec): execute oracle DML statement bug in sqllab (#10706) (@chuancyzhang)
|
||||
- [#10684](https://github.com/apache/incubator-superset/pull/10684) fix(jinja): extract form_data from json body (#10684) (@villebro)
|
||||
- [#10707](https://github.com/apache/incubator-superset/pull/10707) refactor(database): use SupersetResultSet on SqlaTable.get_df() (#10707) (@villebro)
|
||||
- [#10687](https://github.com/apache/incubator-superset/pull/10687) fix(filter-box): don't add empty filter to filtersChoices (#10687) (@villebro)
|
||||
- [#10683](https://github.com/apache/incubator-superset/pull/10683) feat(row-level-security): add hook for customizing form dropdowns (#10683) (@villebro)
|
||||
- [#10633](https://github.com/apache/incubator-superset/pull/10633) fix: dedup groupby in viz.py while preserving order (#10633) (@mistercrunch)
|
||||
- [#10637](https://github.com/apache/incubator-superset/pull/10637) feat(viz-plugins): add date formatting to pivot-table (#10637) (@villebro)
|
||||
- [#10621](https://github.com/apache/incubator-superset/pull/10621) improve documentation for country maps (#10621) (@czue)
|
||||
- [#10572](https://github.com/apache/incubator-superset/pull/10572) fix: show error if rolling window returns empty df (#10572) (@villebro)
|
||||
- [#10578](https://github.com/apache/incubator-superset/pull/10578) fix: dataset delete and perm delete (#10578) (@dpgaspar)
|
||||
- [#10576](https://github.com/apache/incubator-superset/pull/10576) fix: disable false positive error (#10576) (@dpgaspar)
|
||||
- [#10475](https://github.com/apache/incubator-superset/pull/10475) fix(dashboard): add animation state to fix tab switch re-renders (#10475) (@ktmud)
|
||||
- [#10552](https://github.com/apache/incubator-superset/pull/10552) fix: table viz query mode switch not working (#10552) (@ktmud)
|
||||
- [#10551](https://github.com/apache/incubator-superset/pull/10551) fix: embedded chart height (#10551) (@etr2460)
|
||||
- [#10548](https://github.com/apache/incubator-superset/pull/10548) fix: handle query exceptions gracefully (#10548) (@villebro)
|
||||
- [#10544](https://github.com/apache/incubator-superset/pull/10544) bugfix: table chart query mode initial value (#10544) (@ktmud)
|
||||
- [#10538](https://github.com/apache/incubator-superset/pull/10538) fix: remove unnecessary exception when exploring non-legacy viz plugins (#10538) (@villebro)
|
||||
- [#10522](https://github.com/apache/incubator-superset/pull/10522) fix(log): don't log exceptions on test connection (#10522) (@dpgaspar)
|
||||
- [#10517](https://github.com/apache/incubator-superset/pull/10517) feat: make screenshot timeout configurable (#10517) (@JasonD28)
|
||||
- [#10458](https://github.com/apache/incubator-superset/pull/10458) fix: update time range select tooltip (#10458) (@riahk)
|
||||
- [#10493](https://github.com/apache/incubator-superset/pull/10493) fix: support non-string groupbys for pie chart (#10493) (@villebro)
|
||||
- [#10435](https://github.com/apache/incubator-superset/pull/10435) fix(log): log endpoint authentication (#10435) (@dpgaspar)
|
||||
- [#10430](https://github.com/apache/incubator-superset/pull/10430) update code (#10430) (@stuarthu)
|
||||
- [#10391](https://github.com/apache/incubator-superset/pull/10391) fix: pie chart multiple groupbys (#10391) (@villebro)
|
||||
|
||||
### 0.37.1 (2020/09/05 17:28 +00:00)
|
||||
- [#10794](https://github.com/apache/incubator-superset/pull/10794) security: disallow uuid package on jinja1 (#10794) (@dpgaspar)
|
||||
|
||||
### 0.37.0 (2020/07/11 08:07 +00:00)
|
||||
- [#10450](https://github.com/apache/incubator-superset/pull/10450) fix: excel sheet upload is not working (#10450) (@pphszx)
|
||||
- [#10389](https://github.com/apache/incubator-superset/pull/10389) feat: support non-numeric columns in pivot table (#10389) (@villebro)
|
||||
- [#10432](https://github.com/apache/incubator-superset/pull/10432) fix(dashboard): chart rerender when switching tabs (#10432) (@ktmud)
|
||||
- [#10421](https://github.com/apache/incubator-superset/pull/10421) fix: incorrect filter operator emitted by Filter Box (#10421) (@villebro)
|
||||
- [#10400](https://github.com/apache/incubator-superset/pull/10400) fix: bump pivot-table and rose (#10400) (@villebro)
|
||||
- [#10382](https://github.com/apache/incubator-superset/pull/10382) fix: treemap template literal (#10382) (@villebro)
|
||||
- [#10344](https://github.com/apache/incubator-superset/pull/10344) fix: group by with timestamp granularity (#10344) (@dpgaspar)
|
||||
- [#10340](https://github.com/apache/incubator-superset/pull/10340) fix: modified by column on charts and dashboards (#10340) (@dpgaspar)
|
||||
- [#10359](https://github.com/apache/incubator-superset/pull/10359) fix: extra filters for chart data endpoint (#10359) (@villebro)
|
||||
- [#10345](https://github.com/apache/incubator-superset/pull/10345) Pin slack client, breaking change (#10345) (@bkyryliuk)
|
||||
- [#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,11 @@ under the License.
|
||||
This file documents any backwards-incompatible changes in Superset and
|
||||
assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
## 0.37.1
|
||||
|
||||
* [10794](https://github.com/apache/incubator-superset/pull/10794): Breaking change: `uuid` python package is not supported on Jinja2 anymore, only uuid functions are exposed eg: `uuid1`, `uuid3`, `uuid4`, `uuid5`.
|
||||
|
||||
## 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 +48,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.
|
||||
|
||||
@@ -73,7 +73,10 @@ Superset's Jinja context:
|
||||
|
||||
- ``time``: ``time``
|
||||
- ``datetime``: ``datetime.datetime``
|
||||
- ``uuid``: ``uuid``
|
||||
- ``uuid1``: ``uuid1``
|
||||
- ``uuid3``: ``uuid3``
|
||||
- ``uuid4``: ``uuid4``
|
||||
- ``uuid5``: ``uuid5``
|
||||
- ``random``: ``random``
|
||||
- ``relativedelta``: ``dateutil.relativedelta.relativedelta``
|
||||
|
||||
|
||||
@@ -1959,7 +1959,9 @@ List of Countries
|
||||
Need to add a new Country?
|
||||
-------------------------------
|
||||
|
||||
To add a new country in country map tools, we need to follow the following steps :
|
||||
Warning: adding a new country is not easy and requires building superset from source!
|
||||
|
||||
To add a new country in country map tools, you need to follow the following steps :
|
||||
|
||||
1. You need shapefiles which contain data of your map.
|
||||
You can get this file on this site: https://www.diva-gis.org/gdata
|
||||
@@ -1970,13 +1972,17 @@ To add a new country in country map tools, we need to follow the following steps
|
||||
3. You need to convert shapefile to geojson file.
|
||||
This action can make with ogr2ogr tools: https://www.gdal.org/ogr2ogr.html
|
||||
|
||||
4. Put your geojson file in next folder : superset-frontend/src/visualizations/CountryMap/countries with the next name : nameofyourcountries.geojson
|
||||
4. You can to reduce size of geojson file on this site: https://mapshaper.org/
|
||||
|
||||
5. You can to reduce size of geojson file on this site: https://mapshaper.org/
|
||||
5. You will need to put your geojson file in the right place in the @superset-ui npm package. This is the "countries" folder in
|
||||
./superset-frontend/node_modules/@superset-ui/legacy-plugin-chart-country-map/esm/.
|
||||
The .geojson files for other countries are already in this folder.
|
||||
|
||||
6. Go in file superset-frontend/src/explore/controls.jsx
|
||||
6. You will also need to edit the "countries.js" file in the same directory, following the pattern of the other countries.
|
||||
|
||||
7. Add your country in component 'select_country'
|
||||
7. Then go to the file superset-frontend/src/explore/controls.jsx
|
||||
|
||||
8. Add your country in component 'select_country'
|
||||
Example :
|
||||
|
||||
.. code-block:: javascript
|
||||
@@ -2005,3 +2011,6 @@ To add a new country in country map tools, we need to follow the following steps
|
||||
].map(s => [s, s]),
|
||||
description: 'The name of country that Superset should display',
|
||||
},
|
||||
|
||||
|
||||
9. Rebuild the front end from source and restart superset.
|
||||
|
||||
@@ -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)
|
||||
@@ -86,7 +86,7 @@ retry==0.9.2 # via apache-superset (setup.py)
|
||||
selenium==3.141.0 # via apache-superset (setup.py)
|
||||
simplejson==3.17.0 # via apache-superset (setup.py)
|
||||
six==1.14.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, packaging, pathlib2, polyline, prison, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json
|
||||
slackclient==2.6.2 # via apache-superset (setup.py)
|
||||
slackclient==2.5.0 # via apache-superset (setup.py)
|
||||
sqlalchemy-utils==0.36.6 # via apache-superset (setup.py), flask-appbuilder
|
||||
sqlalchemy==1.3.16 # via alembic, apache-superset (setup.py), flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
|
||||
sqlparse==0.3.0 # via apache-superset (setup.py)
|
||||
|
||||
@@ -38,7 +38,7 @@ combine_as_imports = true
|
||||
include_trailing_comma = true
|
||||
line_length = 88
|
||||
known_first_party = superset
|
||||
known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,croniter,cryptography,dataclasses,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,geohash,geopy,humanize,isodate,jinja2,markdown,markupsafe,marshmallow,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,polyline,prison,pyarrow,pyhive,pytest,pytz,retry,selenium,setuptools,simplejson,slack,sphinx_rtd_theme,sqlalchemy,sqlalchemy_utils,sqlparse,werkzeug,wtforms,wtforms_json,yaml
|
||||
known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,croniter,cryptography,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,geohash,geopy,humanize,isodate,jinja2,markdown,markupsafe,marshmallow,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,polyline,prison,pyarrow,pyhive,pytest,pytz,retry,selenium,setuptools,simplejson,slack,sphinx_rtd_theme,sqlalchemy,sqlalchemy_utils,sqlparse,werkzeug,wtforms,wtforms_json,yaml
|
||||
multi_line_output = 3
|
||||
order_by_type = false
|
||||
|
||||
|
||||
4
setup.py
4
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",
|
||||
@@ -102,7 +102,7 @@ setup(
|
||||
"retry>=0.9.2",
|
||||
"selenium>=3.141.0",
|
||||
"simplejson>=3.15.0",
|
||||
"slackclient>=2.6.2",
|
||||
"slackclient==2.5.0", # PINNED! slack changes file upload api in the future versions
|
||||
"sqlalchemy>=1.3.16, <2.0",
|
||||
"sqlalchemy-utils>=0.36.6,<0.37",
|
||||
"sqlparse==0.3.0", # PINNED! see https://github.com/andialbrecht/sqlparse/issues/562
|
||||
|
||||
@@ -45,6 +45,25 @@ 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 allow type to search color schemes', () => {
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="text"]')
|
||||
.focus()
|
||||
.type('air{enter}');
|
||||
cy.get('input[name="select-color_scheme"]').should(
|
||||
'have.value',
|
||||
'bnbColors',
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with adhoc metric', () => {
|
||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
@@ -54,9 +73,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 +81,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 +98,6 @@ describe('Visualization > Line', () => {
|
||||
groupby: ['name'],
|
||||
timeseries_limit_metric: NUM_METRIC,
|
||||
};
|
||||
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
||||
});
|
||||
@@ -97,28 +111,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 +172,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 +199,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 +229,6 @@ describe('Visualization > Line', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
||||
cy.get('.slice_container').within(() => {
|
||||
@@ -263,7 +267,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')
|
||||
@@ -29,12 +29,31 @@ import readResponseBlob from '../../../utils/readResponseBlob';
|
||||
describe('Visualization > Table', () => {
|
||||
const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'table' };
|
||||
|
||||
const PERCENT_METRIC = {
|
||||
expressionType: 'SQL',
|
||||
sqlExpression: 'CAST(SUM(sum_girls)+AS+FLOAT)/SUM(num)',
|
||||
column: null,
|
||||
aggregate: null,
|
||||
hasCustomLabel: true,
|
||||
label: 'Girls',
|
||||
optionName: 'metric_6qwzgc8bh2v_zox7hil1mzs',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.server();
|
||||
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,
|
||||
@@ -110,7 +129,7 @@ describe('Visualization > Table', () => {
|
||||
it('Test table with percent metrics and groupby', () => {
|
||||
const formData = {
|
||||
...VIZ_DEFAULTS,
|
||||
percent_metrics: NUM_METRIC,
|
||||
percent_metrics: PERCENT_METRIC,
|
||||
metrics: [],
|
||||
groupby: ['name'],
|
||||
};
|
||||
@@ -156,9 +175,20 @@ describe('Visualization > Table', () => {
|
||||
metrics: [],
|
||||
row_limit: 10,
|
||||
};
|
||||
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
|
||||
// should display in raw records mode
|
||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Raw Records');
|
||||
cy.get('div[data-test="all_columns"]').should('be.visible');
|
||||
cy.get('div[data-test="groupby"]').should('not.be.visible');
|
||||
|
||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' });
|
||||
|
||||
// should allow switch to aggregate mode
|
||||
cy.get('div[data-test="query_mode"] .btn').contains('Aggregate').click();
|
||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Aggregate');
|
||||
cy.get('div[data-test="all_columns"]').should('not.be.visible');
|
||||
cy.get('div[data-test="groupby"]').should('be.visible');
|
||||
});
|
||||
|
||||
it('Test table with columns, ordering, and row limit', () => {
|
||||
@@ -194,15 +224,6 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
it('Tests table number formatting with % in metric name', () => {
|
||||
const PERCENT_METRIC = {
|
||||
expressionType: 'SQL',
|
||||
sqlExpression: 'CAST(SUM(sum_girls)+AS+FLOAT)/SUM(num)',
|
||||
column: null,
|
||||
aggregate: null,
|
||||
hasCustomLabel: true,
|
||||
label: 'Girls',
|
||||
optionName: 'metric_6qwzgc8bh2v_zox7hil1mzs',
|
||||
};
|
||||
const formData = {
|
||||
...VIZ_DEFAULTS,
|
||||
percent_metrics: PERCENT_METRIC,
|
||||
|
||||
187
superset-frontend/package-lock.json
generated
187
superset-frontend/package-lock.json
generated
@@ -6024,9 +6024,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/legacy-plugin-chart-pivot-table": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.14.9.tgz",
|
||||
"integrity": "sha512-czjbOIew4gDSeZ2yUkB3JHBf3FdwrNMj1Q96IpLLYcoJvWsIw8H6wSFXblCwNkoT8tiPVARZ2ltw4Qkjx0xUBg==",
|
||||
"version": "0.14.21",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.14.21.tgz",
|
||||
"integrity": "sha512-gmj3iu+ibkXwshcSna1V9Tmbh+wBCHi3HKTuy6R9KrB+0585U0dsHro3xe0o14Uamhld6PIeWbZBSl3axXK+SQ==",
|
||||
"requires": {
|
||||
"d3": "^3.5.17",
|
||||
"datatables.net-bs": "^1.10.15",
|
||||
@@ -6034,9 +6034,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/legacy-plugin-chart-rose": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.14.9.tgz",
|
||||
"integrity": "sha512-sylNIX2PcICKEF3Xr0VzgivviFRJHLFq6akY0F/Mk3+GUctUGd8+xmSbKzMKLQlH+E9/9OLNqZ+tiODGI8bdQw==",
|
||||
"version": "0.14.14",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.14.14.tgz",
|
||||
"integrity": "sha512-gln7uwUu6RzpoIs4HYxonUQ4Mn9ZtRl9TLzak1cI+ZznUIZqAlSvtrU39mx0+fQ/b69qpG3Qgq8C2wivCqgL3w==",
|
||||
"requires": {
|
||||
"d3": "^3.5.17",
|
||||
"nvd3": "1.8.6",
|
||||
@@ -6073,9 +6073,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/legacy-plugin-chart-treemap": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.14.9.tgz",
|
||||
"integrity": "sha512-hHxySbtxK8BdDNR790UtdV676h9OpTO4F99qiW9nKCzCYiSEXPl6mBPrQtyPjX8i4lzwpm9GsB91hIj8zeZUSQ==",
|
||||
"version": "0.14.13",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.14.13.tgz",
|
||||
"integrity": "sha512-/5nYtjmOO+CyYx+TMOoM8swu7NFmFQbiQFy1EYLF7gNx1DdboOxbAV8pxqlSrpwX5qw+Fz2mDITclCc03I2czA==",
|
||||
"requires": {
|
||||
"d3-hierarchy": "^1.1.8",
|
||||
"d3-selection": "^1.4.0",
|
||||
@@ -6083,9 +6083,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/legacy-plugin-chart-world-map": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.14.9.tgz",
|
||||
"integrity": "sha512-8QR5tUvA7D5fQOgVON0viTlbYSHpV1sQhW3igvL6uWrF3u4xc+WVIeyn4ggw4601P4UhE4E5EXyLuHHFvWvDXg==",
|
||||
"version": "0.14.16",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.14.16.tgz",
|
||||
"integrity": "sha512-9vQOk+jRs4UxezXUh6Fg9x2QKRMmTiFxDzndMvq5HEo34mqZvjiYNy4D1HUTCNXtHKeJUfjNp5+uDJqD5q9CYg==",
|
||||
"requires": {
|
||||
"d3": "^3.5.17",
|
||||
"datamaps": "^0.5.8",
|
||||
@@ -6130,9 +6130,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/legacy-preset-chart-nvd3": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.14.9.tgz",
|
||||
"integrity": "sha512-VUSOxpRXoTU49OofNzPoXTNpr2MzRRMRfbaPlbQQOwtY5/+9dFa2SfbsD0fVDMtWiX2jPBQeIunAhlrtNUhrBg==",
|
||||
"version": "0.14.21",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.14.21.tgz",
|
||||
"integrity": "sha512-BbsVZnkkAL2a44XFYQtc24VNINGM5JwXAA9HbygdspumYTUu6cpH2nFVPwc06NREUeeN+EV/zF/AVW2O1IJ1tg==",
|
||||
"requires": {
|
||||
"@data-ui/xy-chart": "^0.0.84",
|
||||
"d3": "^3.5.17",
|
||||
@@ -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.16",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.14.16.tgz",
|
||||
"integrity": "sha512-N95ptdDNQatU2CmznsVbFWGumj9Z59oF694NXZkbqJB/Bp+4OPkvjybjOrlEMmay2zj2sXFHAxO0b3ZKKcCucg==",
|
||||
"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",
|
||||
@@ -6337,9 +6336,9 @@
|
||||
}
|
||||
},
|
||||
"@superset-ui/query": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/query/-/query-0.14.9.tgz",
|
||||
"integrity": "sha512-tldCA8YzqG02oCz/mSY5xA2g3AAqdF5f2p755tPimUHCqtn5axnE38AaRDz+0PyKNyStx0JkYkOVgqvX2ggpzA=="
|
||||
"version": "0.14.15",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/query/-/query-0.14.15.tgz",
|
||||
"integrity": "sha512-k89EuCkXp3LmbBSm8yYpmykeoJNy1HvMj3jNRwYS0kvV7nNd267oAdXl8UnFzl+htxqwLUIidcXN9vzydB4Whw=="
|
||||
},
|
||||
"@superset-ui/style": {
|
||||
"version": "0.14.9",
|
||||
@@ -6369,6 +6368,13 @@
|
||||
"@superset-ui/time-format": "0.14.9",
|
||||
"@superset-ui/translation": "0.14.9",
|
||||
"@superset-ui/validator": "0.14.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@superset-ui/query": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@superset-ui/query/-/query-0.14.9.tgz",
|
||||
"integrity": "sha512-tldCA8YzqG02oCz/mSY5xA2g3AAqdF5f2p755tPimUHCqtn5axnE38AaRDz+0PyKNyStx0JkYkOVgqvX2ggpzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@superset-ui/time-format": {
|
||||
@@ -7658,6 +7664,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 +7689,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 +7794,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 +7818,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 +7844,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 +7932,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 +7961,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",
|
||||
@@ -12774,9 +12815,9 @@
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.7.tgz",
|
||||
"integrity": "sha512-S3O0lk6rFJtO01ZTzMollCOGg+WAtCwS3U5E2WSDY/x/sy7q70RjEC4Dmrih5/UqzLLB9XoKJ8KqwBxaNvBu4A=="
|
||||
"version": "2.0.12",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.12.tgz",
|
||||
"integrity": "sha512-Fl8KseK1imyhErHypFPA8qpq9gPzlsJ/EukA6yk9o0gX23p1TzC+rh9LqNg1qvErRTc0UNMYlKxEGSfSh43NDg=="
|
||||
},
|
||||
"domutils": {
|
||||
"version": "1.5.1",
|
||||
@@ -19746,18 +19787,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.2",
|
||||
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
||||
"license": "Apache-2.0",
|
||||
"directories": {
|
||||
@@ -80,21 +80,21 @@
|
||||
"@superset-ui/legacy-plugin-chart-paired-t-test": "^0.14.9",
|
||||
"@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.14.9",
|
||||
"@superset-ui/legacy-plugin-chart-partition": "^0.14.9",
|
||||
"@superset-ui/legacy-plugin-chart-pivot-table": "^0.14.9",
|
||||
"@superset-ui/legacy-plugin-chart-rose": "^0.14.9",
|
||||
"@superset-ui/legacy-plugin-chart-pivot-table": "^0.14.21",
|
||||
"@superset-ui/legacy-plugin-chart-rose": "^0.14.14",
|
||||
"@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-plugin-chart-treemap": "^0.14.13",
|
||||
"@superset-ui/legacy-plugin-chart-world-map": "^0.14.16",
|
||||
"@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/legacy-preset-chart-nvd3": "^0.14.21",
|
||||
"@superset-ui/number-format": "^0.14.9",
|
||||
"@superset-ui/plugin-chart-table": "^0.14.16",
|
||||
"@superset-ui/plugin-chart-word-cloud": "^0.14.9",
|
||||
"@superset-ui/preset-chart-xy": "^0.14.9",
|
||||
"@superset-ui/query": "^0.14.9",
|
||||
"@superset-ui/query": "^0.14.15",
|
||||
"@superset-ui/style": "^0.14.9",
|
||||
"@superset-ui/superset-ui": "^0.14.9",
|
||||
"@superset-ui/time-format": "^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",
|
||||
|
||||
@@ -20,7 +20,7 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Chart from 'src/dashboard/components/gridComponents/Chart';
|
||||
import { ChartUnconnected as Chart } from 'src/dashboard/components/gridComponents/Chart';
|
||||
import SliceHeader from 'src/dashboard/components/SliceHeader';
|
||||
import ChartContainer from 'src/chart/ChartContainer';
|
||||
|
||||
@@ -44,6 +44,7 @@ describe('Chart', () => {
|
||||
slice: {
|
||||
...sliceEntities.slices[queryId],
|
||||
description_markeddown: 'markdown',
|
||||
owners: [],
|
||||
},
|
||||
sliceName: sliceEntities.slices[queryId].slice_name,
|
||||
timeout: 60,
|
||||
@@ -52,6 +53,13 @@ describe('Chart', () => {
|
||||
toggleExpandSlice() {},
|
||||
addFilter() {},
|
||||
logEvent() {},
|
||||
handleToggleFullSize() {},
|
||||
changeFilter() {},
|
||||
setFocusedFilterField() {},
|
||||
unsetFocusedFilterField() {},
|
||||
addDangerToast() {},
|
||||
componentId: 'test',
|
||||
dashboardId: 111,
|
||||
editMode: false,
|
||||
isExpanded: false,
|
||||
supersetCanExplore: false,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 getEffectiveExtraFilters from 'src/dashboard/util/charts/getEffectiveExtraFilters';
|
||||
|
||||
describe('getEffectiveExtraFilters', () => {
|
||||
it('should create valid filters', () => {
|
||||
const result = getEffectiveExtraFilters({
|
||||
gender: ['girl'],
|
||||
name: null,
|
||||
__time_range: ' : 2020-07-17T00:00:00',
|
||||
});
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
col: 'gender',
|
||||
op: 'in',
|
||||
val: ['girl'],
|
||||
},
|
||||
{
|
||||
col: '__time_range',
|
||||
op: '==',
|
||||
val: ' : 2020-07-17T00:00:00',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -19,12 +19,14 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Creatable from 'react-select/creatable';
|
||||
import { Select } from 'src/components/Select';
|
||||
import { getCategoricalSchemeRegistry } from '@superset-ui/color';
|
||||
|
||||
import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeControl';
|
||||
|
||||
const defaultProps = {
|
||||
name: 'color_scheme',
|
||||
label: 'Color Scheme',
|
||||
options: getCategoricalSchemeRegistry()
|
||||
.keys()
|
||||
.map(s => [s, s]),
|
||||
@@ -37,6 +39,6 @@ describe('ColorSchemeControl', () => {
|
||||
});
|
||||
|
||||
it('renders a Creatable', () => {
|
||||
expect(wrapper.find(Creatable)).toHaveLength(1);
|
||||
expect(wrapper.find(Select)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 { shallow } from 'enzyme';
|
||||
|
||||
import FilterBox from 'src/visualizations/FilterBox/FilterBox';
|
||||
|
||||
describe('FilterBox', () => {
|
||||
it('should only add defined non-predefined options to filtersChoices', () => {
|
||||
const wrapper = shallow(
|
||||
<FilterBox
|
||||
chartId={1001}
|
||||
datasource={{ id: 1 }}
|
||||
filtersChoices={{
|
||||
name: [
|
||||
{ id: 'John', text: 'John', metric: 1234 },
|
||||
{ id: 'Jane', text: 'Jane', metric: 345678 },
|
||||
],
|
||||
}}
|
||||
filtersFields={[
|
||||
{
|
||||
asc: false,
|
||||
clearable: true,
|
||||
column: 'name',
|
||||
key: 'name',
|
||||
label: 'name',
|
||||
metric: 'sum__COUNT',
|
||||
multiple: true,
|
||||
},
|
||||
]}
|
||||
origSelectedValues={{}}
|
||||
/>,
|
||||
);
|
||||
const inst = wrapper.instance();
|
||||
// choose a predefined value
|
||||
inst.setState({ selectedValues: { name: ['John'] } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(2);
|
||||
// reset selection
|
||||
inst.setState({ selectedValues: { name: null } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(2);
|
||||
// Add a new name
|
||||
inst.setState({ selectedValues: { name: 'James' } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -94,13 +94,7 @@ describe('ExploreResultsButton', () => {
|
||||
});
|
||||
|
||||
const badCols = wrapper.instance().getInvalidColumns();
|
||||
expect(badCols).toEqual([
|
||||
'COUNT(*)',
|
||||
'1',
|
||||
'123',
|
||||
'CASE WHEN 1=1 THEN 1 ELSE 0 END',
|
||||
'__TIMESTAMP',
|
||||
]);
|
||||
expect(badCols).toEqual(['my_dupe_col__2', '__timestamp', '__TIMESTAMP']);
|
||||
|
||||
const msgWrapper = shallow(wrapper.instance().renderInvalidColumnMessage());
|
||||
expect(msgWrapper.find('div')).toHaveLength(1);
|
||||
|
||||
@@ -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,21 @@ export const queryWithBadColumns = {
|
||||
name: '_TIMESTAMP',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '__TIME',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'my_dupe_col__2',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '__timestamp',
|
||||
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>
|
||||
);
|
||||
@@ -102,13 +102,12 @@ class ExploreResultsButton extends React.PureComponent {
|
||||
.asSeconds();
|
||||
}
|
||||
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 re1 = /__\d+$/; // duplicate column name pattern
|
||||
const re2 = /^__timestamp/i; // reserved temporal column alias
|
||||
|
||||
return this.props.query.results.selected_columns
|
||||
.map(col => col.name)
|
||||
.filter(col => !re1.test(col) || re2.test(col) || re3.test(col));
|
||||
.filter(col => re1.test(col) || re2.test(col));
|
||||
}
|
||||
datasourceName() {
|
||||
const { query } = this.props;
|
||||
@@ -193,16 +192,11 @@ class ExploreResultsButton extends React.PureComponent {
|
||||
<code>
|
||||
<strong>{invalidColumns.join(', ')} </strong>
|
||||
</code>
|
||||
{t('cannot be used as a column name. Please use aliases (as in ')}
|
||||
<code>
|
||||
SELECT count(*)
|
||||
<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(`cannot be used as a column name. The column name/alias "__timestamp"
|
||||
is reserved for the main temporal expression, and column aliases ending with
|
||||
double underscores followed by a numeric value (e.g. "my_col__1") are reserved
|
||||
for deduplicating duplicate column names. Please use aliases to rename the
|
||||
invalid column names.`)}
|
||||
</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>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -62,6 +62,8 @@ const propTypes = {
|
||||
onQuery: PropTypes.func,
|
||||
onFilterMenuOpen: PropTypes.func,
|
||||
onFilterMenuClose: PropTypes.func,
|
||||
// id of the last mounted parent tab
|
||||
mountedParent: PropTypes.string,
|
||||
};
|
||||
|
||||
const BLANK = {};
|
||||
@@ -143,7 +145,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}
|
||||
/>
|
||||
|
||||
@@ -87,8 +87,7 @@ class ChartRenderer extends React.Component {
|
||||
if (resultsReady) {
|
||||
this.hasQueryResponseChange =
|
||||
nextProps.queryResponse !== this.props.queryResponse;
|
||||
|
||||
if (
|
||||
return (
|
||||
this.hasQueryResponseChange ||
|
||||
nextProps.annotationData !== this.props.annotationData ||
|
||||
nextProps.height !== this.props.height ||
|
||||
@@ -96,9 +95,7 @@ class ChartRenderer extends React.Component {
|
||||
nextProps.triggerRender ||
|
||||
nextProps.formData.color_scheme !== this.props.formData.color_scheme ||
|
||||
nextProps.cacheBusterProp !== this.props.cacheBusterProp
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -321,6 +321,14 @@ export function setDirectPathToChild(path) {
|
||||
return { type: SET_DIRECT_PATH, path };
|
||||
}
|
||||
|
||||
export const SET_MOUNTED_TAB = 'SET_MOUNTED_TAB';
|
||||
/**
|
||||
* Set if tab switch animation is in progress
|
||||
*/
|
||||
export function setMountedTab(mountedTab) {
|
||||
return { type: SET_MOUNTED_TAB, mountedTab };
|
||||
}
|
||||
|
||||
export const SET_FOCUSED_FILTER_FIELD = 'SET_FOCUSED_FILTER_FIELD';
|
||||
export function setFocusedFilterField(chartId, column) {
|
||||
return { type: SET_FOCUSED_FILTER_FIELD, chartId, column };
|
||||
|
||||
@@ -62,6 +62,7 @@ const propTypes = {
|
||||
handleComponentDrop: PropTypes.func.isRequired,
|
||||
directPathToChild: PropTypes.arrayOf(PropTypes.string),
|
||||
setDirectPathToChild: PropTypes.func.isRequired,
|
||||
setMountedTab: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@@ -250,6 +251,12 @@ class DashboardBuilder extends React.Component {
|
||||
<TabPane
|
||||
key={index === 0 ? DASHBOARD_GRID_ID : id}
|
||||
eventKey={index}
|
||||
mountOnEnter
|
||||
unmountOnExit={false}
|
||||
onEntering={() => {
|
||||
// Entering current tab, DOM is visible and has dimension
|
||||
this.props.setMountedTab(id);
|
||||
}}
|
||||
>
|
||||
<DashboardGrid
|
||||
gridComponent={dashboardLayout[id]}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
import cx from 'classnames';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { exploreChart, exportChart } from '../../../explore/exploreUtils';
|
||||
import SliceHeader from '../SliceHeader';
|
||||
@@ -41,6 +42,8 @@ const propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
updateSliceName: PropTypes.func.isRequired,
|
||||
isComponentVisible: PropTypes.bool,
|
||||
// last switched tab
|
||||
mountedParent: PropTypes.string,
|
||||
handleToggleFullSize: PropTypes.func.isRequired,
|
||||
|
||||
// from redux
|
||||
@@ -70,6 +73,7 @@ const propTypes = {
|
||||
const defaultProps = {
|
||||
isCached: false,
|
||||
isComponentVisible: true,
|
||||
mountedParent: 'ROOT',
|
||||
};
|
||||
|
||||
// we use state + shouldComponentUpdate() logic to prevent perf-wrecking
|
||||
@@ -114,6 +118,9 @@ class Chart extends React.Component {
|
||||
// allow chart update/re-render only if visible:
|
||||
// under selected tab or no tab layout
|
||||
if (nextProps.isComponentVisible) {
|
||||
if (nextProps.mountedParent === null) {
|
||||
return false;
|
||||
}
|
||||
if (nextProps.chart.triggerQuery) {
|
||||
return true;
|
||||
}
|
||||
@@ -140,7 +147,7 @@ class Chart extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
// `cacheBusterProp` is nnjected by react-hot-loader
|
||||
// `cacheBusterProp` is jected by react-hot-loader
|
||||
return this.props.cacheBusterProp !== nextProps.cacheBusterProp;
|
||||
}
|
||||
|
||||
@@ -346,4 +353,20 @@ class Chart extends React.Component {
|
||||
Chart.propTypes = propTypes;
|
||||
Chart.defaultProps = defaultProps;
|
||||
|
||||
export default Chart;
|
||||
function mapStateToProps({ dashboardState }) {
|
||||
return {
|
||||
// needed to prevent chart from rendering while tab switch animation in progress
|
||||
// when undefined, default to have mounted the root tab
|
||||
mountedParent: dashboardState?.mountedTab,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The original Chart component not connected to state.
|
||||
*/
|
||||
export const ChartUnconnected = Chart;
|
||||
|
||||
/**
|
||||
* Redux connected Chart component.
|
||||
*/
|
||||
export default connect(mapStateToProps, null)(Chart);
|
||||
|
||||
@@ -47,9 +47,12 @@ const propTypes = {
|
||||
renderTabContent: PropTypes.bool, // whether to render tabs + content or just tabs
|
||||
editMode: PropTypes.bool.isRequired,
|
||||
renderHoverMenu: PropTypes.bool,
|
||||
logEvent: PropTypes.func.isRequired,
|
||||
directPathToChild: PropTypes.arrayOf(PropTypes.string),
|
||||
|
||||
// actions (from DashboardComponent.jsx)
|
||||
logEvent: PropTypes.func.isRequired,
|
||||
setMountedTab: PropTypes.func.isRequired,
|
||||
|
||||
// grid related
|
||||
availableColumnCount: PropTypes.number,
|
||||
columnWidth: PropTypes.number,
|
||||
@@ -260,6 +263,12 @@ class Tabs extends React.PureComponent {
|
||||
onDeleteTab={this.handleDeleteTab}
|
||||
/>
|
||||
}
|
||||
onEntering={() => {
|
||||
// Entering current tab, DOM is visible and has dimension
|
||||
if (renderTabContent) {
|
||||
this.props.setMountedTab(tabId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{renderTabContent && (
|
||||
<DashboardComponent
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
showBuilderPane,
|
||||
setDirectPathToChild,
|
||||
setMountedTab,
|
||||
} from '../actions/dashboardState';
|
||||
import {
|
||||
deleteTopLevelTabs,
|
||||
@@ -49,6 +50,7 @@ function mapDispatchToProps(dispatch) {
|
||||
showBuilderPane,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
setDirectPathToChild,
|
||||
setMountedTab,
|
||||
},
|
||||
dispatch,
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
updateComponents,
|
||||
handleComponentDrop,
|
||||
} from '../actions/dashboardLayout';
|
||||
import { setDirectPathToChild } from '../actions/dashboardState';
|
||||
import { setDirectPathToChild, setMountedTab } from '../actions/dashboardState';
|
||||
import { logEvent } from '../../logger/actions';
|
||||
import { addDangerToast } from '../../messageToasts/actions';
|
||||
|
||||
@@ -106,6 +106,7 @@ function mapDispatchToProps(dispatch) {
|
||||
updateComponents,
|
||||
handleComponentDrop,
|
||||
setDirectPathToChild,
|
||||
setMountedTab,
|
||||
logEvent,
|
||||
},
|
||||
dispatch,
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
UPDATE_CSS,
|
||||
SET_REFRESH_FREQUENCY,
|
||||
SET_DIRECT_PATH,
|
||||
SET_MOUNTED_TAB,
|
||||
SET_FOCUSED_FILTER_FIELD,
|
||||
} from '../actions/dashboardState';
|
||||
import { BUILDER_PANE_TYPE } from '../util/constants';
|
||||
@@ -127,10 +128,19 @@ export default function dashboardStateReducer(state = {}, action) {
|
||||
[SET_DIRECT_PATH]() {
|
||||
return {
|
||||
...state,
|
||||
// change of direct path (tabs) will reset current mounted tab
|
||||
mountedTab: null,
|
||||
directPathToChild: action.path,
|
||||
directPathLastUpdated: Date.now(),
|
||||
};
|
||||
},
|
||||
[SET_MOUNTED_TAB]() {
|
||||
// set current mounted tab after tab is really mounted to DOM
|
||||
return {
|
||||
...state,
|
||||
mountedTab: action.mountedTab,
|
||||
};
|
||||
},
|
||||
[SET_FOCUSED_FILTER_FIELD]() {
|
||||
const { focusedFilterField } = state;
|
||||
if (action.chartId && action.column) {
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
import { DataRecordFilters } from '@superset-ui/chart';
|
||||
|
||||
export default function getEffectiveExtraFilters(filters: DataRecordFilters) {
|
||||
return Object.entries(filters).map(([column, values]) => ({
|
||||
col: column,
|
||||
op: 'in',
|
||||
val: values,
|
||||
}));
|
||||
return Object.entries(filters)
|
||||
.map(([column, values]) => ({
|
||||
col: column,
|
||||
op: Array.isArray(values) ? 'in' : '==',
|
||||
val: values,
|
||||
}))
|
||||
.filter(filter => filter.val !== null);
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ setupPlugins();
|
||||
const App = ({ store }) => (
|
||||
<Provider store={store}>
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<div>
|
||||
<>
|
||||
<ExploreViewContainer />
|
||||
<ToastPresenter />
|
||||
</div>
|
||||
</>
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isFunction } from 'lodash';
|
||||
import { CreatableSelect } from 'src/components/Select';
|
||||
import { Select } from 'src/components/Select';
|
||||
import ControlHeader from '../ControlHeader';
|
||||
import TooltipWrapper from '../../../components/TooltipWrapper';
|
||||
|
||||
@@ -50,7 +50,6 @@ export default class ColorSchemeControl extends React.PureComponent {
|
||||
this.state = {
|
||||
scheme: this.props.value,
|
||||
};
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.renderOption = this.renderOption.bind(this);
|
||||
}
|
||||
@@ -62,9 +61,8 @@ export default class ColorSchemeControl extends React.PureComponent {
|
||||
}
|
||||
|
||||
renderOption(key) {
|
||||
const { isLinear, schemes } = this.props;
|
||||
const schemeLookup = isFunction(schemes) ? schemes() : schemes;
|
||||
const currentScheme = schemeLookup[key.value || defaultProps.value];
|
||||
const { isLinear } = this.props;
|
||||
const currentScheme = this.schemes[key.value];
|
||||
|
||||
// For categorical scheme, display all the colors
|
||||
// For sequential scheme, show 10 or interpolate to 10.
|
||||
@@ -97,12 +95,16 @@ export default class ColorSchemeControl extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { choices } = this.props;
|
||||
const options = (isFunction(choices) ? choices() : choices).map(choice => ({
|
||||
value: choice[0],
|
||||
label: choice[1],
|
||||
}));
|
||||
|
||||
const { schemes, choices } = this.props;
|
||||
// save parsed schemes for later
|
||||
this.schemes = isFunction(schemes) ? schemes() : schemes;
|
||||
const options = (isFunction(choices) ? choices() : choices).map(
|
||||
([value, label]) => ({
|
||||
value,
|
||||
// use scheme label if available
|
||||
label: this.schemes[value]?.label || label,
|
||||
}),
|
||||
);
|
||||
const selectProps = {
|
||||
multi: false,
|
||||
name: `select-${this.props.name}`,
|
||||
@@ -119,7 +121,7 @@ export default class ColorSchemeControl extends React.PureComponent {
|
||||
return (
|
||||
<div>
|
||||
<ControlHeader {...this.props} />
|
||||
<CreatableSelect {...selectProps} />
|
||||
<Select {...selectProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ const DEFAULT_SINCE = moment()
|
||||
const DEFAULT_UNTIL = moment().utc().startOf('day').format(MOMENT_FORMAT);
|
||||
const SEPARATOR = ' : ';
|
||||
const FREEFORM_TOOLTIP = t(
|
||||
'Superset supports smart date parsing. Strings like `last sunday` or ' +
|
||||
'`last october` can be used.',
|
||||
'Superset supports smart date parsing. Strings like `3 weeks ago`, `last sunday`, or ' +
|
||||
'`2 weeks from now` can be used.',
|
||||
);
|
||||
|
||||
const DATE_FILTER_POPOVER_STYLE = { width: '250px' };
|
||||
|
||||
@@ -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,38 @@ function handleMissingChoice(control) {
|
||||
return control;
|
||||
}
|
||||
|
||||
export function applyMapStateToPropsToControl(controlState, controlPanelState) {
|
||||
const { mapStateToProps } = controlState;
|
||||
let state = { ...controlState };
|
||||
let { value } = state; // value is current user-input value
|
||||
if (mapStateToProps && controlPanelState) {
|
||||
state = {
|
||||
...controlState,
|
||||
...mapStateToProps(controlPanelState, controlState),
|
||||
};
|
||||
// `mapStateToProps` may also provide a value
|
||||
value = value || state.value;
|
||||
}
|
||||
// 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 +150,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 = [
|
||||
{
|
||||
@@ -147,17 +146,17 @@ class ChartList extends React.PureComponent<Props, State> {
|
||||
},
|
||||
},
|
||||
}: any) => <a href={changedByUrl}>{changedByName}</a>,
|
||||
Header: t('Creator'),
|
||||
accessor: 'changed_by_fk',
|
||||
Header: t('Modified By'),
|
||||
accessor: 'changed_by.first_name',
|
||||
},
|
||||
{
|
||||
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 = [
|
||||
{
|
||||
@@ -162,8 +161,8 @@ class DashboardList extends React.PureComponent<Props, State> {
|
||||
},
|
||||
},
|
||||
}: any) => <a href={changedByUrl}>{changedByName}</a>,
|
||||
Header: t('Creator'),
|
||||
accessor: 'changed_by_fk',
|
||||
Header: t('Modified By'),
|
||||
accessor: 'changed_by.first_name',
|
||||
},
|
||||
{
|
||||
Cell: ({
|
||||
@@ -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',
|
||||
|
||||
@@ -331,7 +331,7 @@ class FilterBox extends React.Component {
|
||||
? selectedValues[key]
|
||||
: [selectedValues[key]];
|
||||
selectedValuesForKey
|
||||
.filter(value => !choiceIds.has(value))
|
||||
.filter(value => value !== null && !choiceIds.has(value))
|
||||
.forEach(value => {
|
||||
choices.unshift({
|
||||
filter: key,
|
||||
|
||||
@@ -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,31 +67,51 @@ class DashboardTable extends React.PureComponent {
|
||||
row: {
|
||||
original: { url, dashboard_title: dashboardTitle },
|
||||
},
|
||||
}: {
|
||||
row: {
|
||||
original: {
|
||||
url: string;
|
||||
dashboard_title: string;
|
||||
};
|
||||
};
|
||||
}) => <a href={url}>{dashboardTitle}</a>,
|
||||
},
|
||||
{
|
||||
accessor: 'changed_by_fk',
|
||||
Header: 'Creator',
|
||||
accessor: 'changed_by.first_name',
|
||||
Header: 'Modified By',
|
||||
Cell: ({
|
||||
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,19 +101,17 @@ class ChartRestApi(BaseSupersetModelRestApi):
|
||||
"cache_timeout",
|
||||
]
|
||||
show_select_columns = show_columns + ["table.id"]
|
||||
|
||||
list_columns = [
|
||||
"id",
|
||||
"slice_name",
|
||||
"url",
|
||||
"description",
|
||||
"changed_by_fk",
|
||||
"created_by_fk",
|
||||
"changed_by_name",
|
||||
"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 +122,13 @@ class ChartRestApi(BaseSupersetModelRestApi):
|
||||
"params",
|
||||
"cache_timeout",
|
||||
]
|
||||
|
||||
list_select_columns = list_columns + ["changed_on", "changed_by_fk"]
|
||||
order_columns = [
|
||||
"slice_name",
|
||||
"viz_type",
|
||||
"datasource_name",
|
||||
"changed_by_fk",
|
||||
"changed_on",
|
||||
"changed_by.first_name",
|
||||
"changed_on_delta_humanized",
|
||||
]
|
||||
search_columns = (
|
||||
"slice_name",
|
||||
@@ -419,7 +417,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 +460,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 +489,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",
|
||||
@@ -568,7 +561,7 @@ class ChartDataExtrasSchema(Schema):
|
||||
|
||||
time_range_endpoints = fields.List(
|
||||
fields.String(
|
||||
validate=validate.OneOf(choices=("INCLUSIVE", "EXCLUSIVE")),
|
||||
validate=validate.OneOf(choices=("unknown", "inclusive", "exclusive")),
|
||||
description="A list with two values, stating if start/end should be "
|
||||
"inclusive/exclusive.",
|
||||
)
|
||||
@@ -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(
|
||||
@@ -725,10 +718,10 @@ class ChartDataQueryObjectSchema(Schema):
|
||||
deprecated=True,
|
||||
)
|
||||
having_filters = fields.List(
|
||||
fields.Dict(),
|
||||
fields.Nested(ChartDataFilterSchema),
|
||||
description="HAVING filters to be added to legacy Druid datasource queries. "
|
||||
"This field is deprecated and should be passed to `extras` "
|
||||
"as `filters_druid`.",
|
||||
"as `having_druid`.",
|
||||
deprecated=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ from typing import Any, ClassVar, Dict, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from flask_babel import gettext as _
|
||||
|
||||
from superset import app, cache, db, security_manager
|
||||
from superset.common.query_object import QueryObject
|
||||
from superset.connectors.base.models import BaseDatasource
|
||||
from superset.connectors.connector_registry import ConnectorRegistry
|
||||
from superset.exceptions import QueryObjectValidationError
|
||||
from superset.stats_logger import BaseStatsLogger
|
||||
from superset.utils import core as utils
|
||||
from superset.utils.core import DTTM_ALIAS
|
||||
@@ -111,8 +113,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 +161,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"]}
|
||||
@@ -238,6 +236,21 @@ class QueryContext:
|
||||
|
||||
if query_obj and not is_loaded:
|
||||
try:
|
||||
invalid_columns = [
|
||||
col
|
||||
for col in query_obj.columns
|
||||
+ query_obj.groupby
|
||||
+ [flt["col"] for flt in query_obj.filter]
|
||||
+ utils.get_column_names_from_metrics(query_obj.metrics)
|
||||
if col not in self.datasource.column_names
|
||||
]
|
||||
if invalid_columns:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Columns missing in datasource: %(invalid_columns)s",
|
||||
invalid_columns=invalid_columns,
|
||||
)
|
||||
)
|
||||
query_result = self.get_query_result(query_obj)
|
||||
status = query_result["status"]
|
||||
query = query_result["query"]
|
||||
@@ -248,10 +261,13 @@ class QueryContext:
|
||||
if not self.force:
|
||||
stats_logger.incr("loaded_from_source_without_force")
|
||||
is_loaded = True
|
||||
except QueryObjectValidationError as ex:
|
||||
error_message = str(ex)
|
||||
status = utils.QueryStatus.FAILED
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
logger.exception(ex)
|
||||
if not error_message:
|
||||
error_message = "{}".format(ex)
|
||||
error_message = str(ex)
|
||||
status = utils.QueryStatus.FAILED
|
||||
stacktrace = utils.get_stacktrace()
|
||||
|
||||
|
||||
@@ -335,6 +335,12 @@ GET_FEATURE_FLAGS_FUNC: Optional[Callable[[Dict[str, bool]], Dict[str, bool]]] =
|
||||
THUMBNAIL_SELENIUM_USER = "Admin"
|
||||
THUMBNAIL_CACHE_CONFIG: CacheConfig = {"CACHE_TYPE": "null"}
|
||||
|
||||
# Used for thumbnails and other api: Time in seconds before selenium
|
||||
# times out after trying to locate an element on the page and wait
|
||||
# for that element to load for an alert screenshot.
|
||||
SCREENSHOT_LOCATE_WAIT = 10
|
||||
SCREENSHOT_LOAD_WAIT = 60
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Image and file configuration
|
||||
# ---------------------------------------------------
|
||||
@@ -840,6 +846,16 @@ TALISMAN_CONFIG = {
|
||||
# a custom security config could potentially give access to setting filters on
|
||||
# tables that users do not have access to.
|
||||
ENABLE_ROW_LEVEL_SECURITY = False
|
||||
# It is possible to customize which tables and roles are featured in the RLS
|
||||
# dropdown. When set, this dict is assigned to `add_form_query_rel_fields` and
|
||||
# `edit_form_query_rel_fields` on `RowLevelSecurityFiltersModelView`. Example:
|
||||
#
|
||||
# from flask_appbuilder.models.sqla import filters
|
||||
# RLS_FORM_QUERY_REL_FIELDS = {
|
||||
# "roles": [["name", filters.FilterStartsWith, "RlsRole"]]
|
||||
# "tables": [["table_name", filters.FilterContains, "rls"]]
|
||||
# }
|
||||
RLS_FORM_QUERY_REL_FIELDS: Optional[Dict[str, List[List[Any]]]] = None
|
||||
|
||||
#
|
||||
# Flask session cookie options
|
||||
|
||||
@@ -1396,7 +1396,7 @@ class DruidDatasource(Model, BaseDatasource):
|
||||
if df is None:
|
||||
df = pd.DataFrame()
|
||||
qry["filter"] = self._add_filter_from_pre_query_data(
|
||||
df, pre_qry["dimensions"], qry["filter"]
|
||||
df, pre_qry["dimensions"], filters
|
||||
)
|
||||
qry["limit_spec"] = None
|
||||
if row_limit:
|
||||
|
||||
@@ -25,6 +25,7 @@ import sqlparse
|
||||
from flask import escape, Markup
|
||||
from flask_appbuilder import Model
|
||||
from flask_babel import lazy_gettext as _
|
||||
from jinja2.exceptions import TemplateError
|
||||
from sqlalchemy import (
|
||||
and_,
|
||||
asc,
|
||||
@@ -40,7 +41,7 @@ from sqlalchemy import (
|
||||
Table,
|
||||
Text,
|
||||
)
|
||||
from sqlalchemy.exc import CompileError
|
||||
from sqlalchemy.exc import CompileError, SQLAlchemyError
|
||||
from sqlalchemy.orm import backref, Query, relationship, RelationshipProperty, Session
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from sqlalchemy.schema import UniqueConstraint
|
||||
@@ -51,7 +52,7 @@ from superset import app, db, is_feature_enabled, security_manager
|
||||
from superset.connectors.base.models import BaseColumn, BaseDatasource, BaseMetric
|
||||
from superset.constants import NULL_STRING
|
||||
from superset.db_engine_specs.base import TimestampExpression
|
||||
from superset.exceptions import DatabaseNotFound
|
||||
from superset.exceptions import DatabaseNotFound, QueryObjectValidationError
|
||||
from superset.jinja_context import (
|
||||
BaseTemplateProcessor,
|
||||
ExtraCache,
|
||||
@@ -89,6 +90,19 @@ class AnnotationDatasource(BaseDatasource):
|
||||
cache_timeout = 0
|
||||
changed_on = None
|
||||
type = "annotation"
|
||||
column_names = [
|
||||
"created_on",
|
||||
"changed_on",
|
||||
"id",
|
||||
"start_dttm",
|
||||
"end_dttm",
|
||||
"layer_id",
|
||||
"short_descr",
|
||||
"long_descr",
|
||||
"json_metadata",
|
||||
"created_by_fk",
|
||||
"changed_by_fk",
|
||||
]
|
||||
|
||||
def query(self, query_obj: QueryObjectDict) -> QueryResult:
|
||||
error_message = None
|
||||
@@ -217,15 +231,16 @@ class TableColumn(Model, BaseColumn):
|
||||
return and_(*l)
|
||||
|
||||
def get_timestamp_expression(
|
||||
self, time_grain: Optional[str]
|
||||
self, time_grain: Optional[str], label: Optional[str] = None
|
||||
) -> Union[TimestampExpression, Label]:
|
||||
"""
|
||||
Return a SQLAlchemy Core element representation of self to be used in a query.
|
||||
|
||||
:param time_grain: Optional time grain, e.g. P1Y
|
||||
:param label: alias/label that column is expected to have
|
||||
:return: A TimeExpression object wrapped in a Label if supported by db
|
||||
"""
|
||||
label = utils.DTTM_ALIAS
|
||||
label = label or utils.DTTM_ALIAS
|
||||
|
||||
db_ = self.table.database
|
||||
pdf = self.python_date_format
|
||||
@@ -629,7 +644,15 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
|
||||
if self.fetch_values_predicate:
|
||||
tp = self.get_template_processor()
|
||||
qry = qry.where(text(tp.process_template(self.fetch_values_predicate)))
|
||||
try:
|
||||
qry = qry.where(text(tp.process_template(self.fetch_values_predicate)))
|
||||
except TemplateError as ex:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Error in jinja expression in fetch values predicate: %(msg)s",
|
||||
msg=ex.message,
|
||||
)
|
||||
)
|
||||
|
||||
engine = self.database.get_sqla_engine()
|
||||
sql = "{}".format(qry.compile(engine, compile_kwargs={"literal_binds": True}))
|
||||
@@ -679,7 +702,16 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
if self.sql:
|
||||
from_sql = self.sql
|
||||
if template_processor:
|
||||
from_sql = template_processor.process_template(from_sql)
|
||||
try:
|
||||
from_sql = template_processor.process_template(from_sql)
|
||||
except TemplateError as ex:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Error in jinja expression in FROM clause: %(msg)s",
|
||||
msg=ex.message,
|
||||
)
|
||||
)
|
||||
|
||||
from_sql = sqlparse.format(from_sql, strip_comments=True)
|
||||
return TextAsFrom(sa.text(from_sql), []).alias("expr_qry")
|
||||
return self.get_sqla_table()
|
||||
@@ -698,7 +730,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
expression_type = metric.get("expressionType")
|
||||
label = utils.get_metric_name(metric)
|
||||
|
||||
if expression_type == utils.ADHOC_METRIC_EXPRESSION_TYPES["SIMPLE"]:
|
||||
if expression_type == utils.AdhocMetricExpressionType.SIMPLE:
|
||||
column_name = metric["column"].get("column_name")
|
||||
table_column = columns_by_name.get(column_name)
|
||||
if table_column:
|
||||
@@ -706,7 +738,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
else:
|
||||
sqla_column = column(column_name)
|
||||
sqla_metric = self.sqla_aggregations[metric["aggregate"]](sqla_column)
|
||||
elif expression_type == utils.ADHOC_METRIC_EXPRESSION_TYPES["SQL"]:
|
||||
elif expression_type == utils.AdhocMetricExpressionType.SQL:
|
||||
sqla_metric = literal_column(metric.get("sqlExpression"))
|
||||
else:
|
||||
return None
|
||||
@@ -725,10 +757,15 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
:returns: A list of SQL clauses to be ANDed together.
|
||||
:rtype: List[str]
|
||||
"""
|
||||
return [
|
||||
text("({})".format(template_processor.process_template(f.clause)))
|
||||
for f in security_manager.get_rls_filters(self)
|
||||
]
|
||||
try:
|
||||
return [
|
||||
text("({})".format(template_processor.process_template(f.clause)))
|
||||
for f in security_manager.get_rls_filters(self)
|
||||
]
|
||||
except TemplateError as ex:
|
||||
raise QueryObjectValidationError(
|
||||
_("Error in jinja expression in RLS filters: %(msg)s", msg=ex.message,)
|
||||
)
|
||||
|
||||
def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
|
||||
self,
|
||||
@@ -786,7 +823,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
metrics_by_name: Dict[str, SqlMetric] = {m.metric_name: m for m in self.metrics}
|
||||
|
||||
if not granularity and is_timeseries:
|
||||
raise Exception(
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Datetime column not provided as part table configuration "
|
||||
"and is required by this type of chart"
|
||||
@@ -797,7 +834,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
and not columns
|
||||
and (is_sip_38 or (not is_sip_38 and not groupby))
|
||||
):
|
||||
raise Exception(_("Empty query?"))
|
||||
raise QueryObjectValidationError(_("Empty query?"))
|
||||
metrics_exprs: List[ColumnElement] = []
|
||||
for metric in metrics:
|
||||
if utils.is_adhoc_metric(metric):
|
||||
@@ -806,7 +843,9 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
elif isinstance(metric, str) and metric in metrics_by_name:
|
||||
metrics_exprs.append(metrics_by_name[metric].get_sqla_col())
|
||||
else:
|
||||
raise Exception(_("Metric '%(metric)s' does not exist", metric=metric))
|
||||
raise QueryObjectValidationError(
|
||||
_("Metric '%(metric)s' does not exist", metric=metric)
|
||||
)
|
||||
if metrics_exprs:
|
||||
main_metric_expr = metrics_exprs[0]
|
||||
else:
|
||||
@@ -816,6 +855,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
select_exprs: List[Column] = []
|
||||
groupby_exprs_sans_timestamp = OrderedDict()
|
||||
|
||||
assert extras is not None
|
||||
if (is_sip_38 and metrics and columns) or (not is_sip_38 and groupby):
|
||||
# dedup columns while preserving order
|
||||
columns_ = columns if is_sip_38 else groupby
|
||||
@@ -824,7 +864,13 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
|
||||
select_exprs = []
|
||||
for selected in groupby:
|
||||
if selected in columns_by_name:
|
||||
# if groupby field/expr equals granularity field/expr
|
||||
if selected == granularity:
|
||||
time_grain = extras.get("time_grain_sqla")
|
||||
sqla_col = columns_by_name[selected]
|
||||
outer = sqla_col.get_timestamp_expression(time_grain, selected)
|
||||
# if groupby field equals a selected column
|
||||
elif selected in columns_by_name:
|
||||
outer = columns_by_name[selected].get_sqla_col()
|
||||
else:
|
||||
outer = literal_column(f"({selected})")
|
||||
@@ -841,7 +887,6 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
)
|
||||
metrics_exprs = []
|
||||
|
||||
assert extras is not None
|
||||
time_range_endpoints = extras.get("time_range_endpoints")
|
||||
groupby_exprs_with_timestamp = OrderedDict(groupby_exprs_sans_timestamp.items())
|
||||
if granularity:
|
||||
@@ -947,7 +992,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
!= None
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
raise QueryObjectValidationError(
|
||||
_("Invalid filter operation type: %(op)s", op=op)
|
||||
)
|
||||
if config["ENABLE_ROW_LEVEL_SECURITY"]:
|
||||
@@ -955,11 +1000,27 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
if extras:
|
||||
where = extras.get("where")
|
||||
if where:
|
||||
where = template_processor.process_template(where)
|
||||
try:
|
||||
where = template_processor.process_template(where)
|
||||
except TemplateError as ex:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Error in jinja expression in WHERE clause: %(msg)s",
|
||||
msg=ex.message,
|
||||
)
|
||||
)
|
||||
where_clause_and += [sa.text("({})".format(where))]
|
||||
having = extras.get("having")
|
||||
if having:
|
||||
having = template_processor.process_template(having)
|
||||
try:
|
||||
having = template_processor.process_template(having)
|
||||
except TemplateError as ex:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Error in jinja expression in HAVING clause: %(msg)s",
|
||||
msg=ex.message,
|
||||
)
|
||||
)
|
||||
having_clause_and += [sa.text("({})".format(having))]
|
||||
if granularity:
|
||||
qry = qry.where(and_(*(time_filters + where_clause_and)))
|
||||
@@ -1106,7 +1167,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
):
|
||||
ob = metrics_by_name[timeseries_limit_metric].get_sqla_col()
|
||||
else:
|
||||
raise Exception(
|
||||
raise QueryObjectValidationError(
|
||||
_("Metric '%(metric)s' does not exist", metric=timeseries_limit_metric)
|
||||
)
|
||||
|
||||
@@ -1133,6 +1194,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:
|
||||
"""
|
||||
@@ -1147,7 +1209,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
labels_expected = query_str_ext.labels_expected
|
||||
if df is not None and not df.empty:
|
||||
if len(df.columns) != len(labels_expected):
|
||||
raise Exception(
|
||||
raise QueryObjectValidationError(
|
||||
f"For {sql}, df.columns: {df.columns}"
|
||||
f" differs from {labels_expected}"
|
||||
)
|
||||
@@ -1163,6 +1225,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 +1233,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:
|
||||
@@ -1179,13 +1243,13 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
"""Fetches the metadata for the table and merges it in"""
|
||||
try:
|
||||
table_ = self.get_sqla_table_object()
|
||||
except Exception as ex:
|
||||
logger.exception(ex)
|
||||
raise Exception(
|
||||
except SQLAlchemyError:
|
||||
raise QueryObjectValidationError(
|
||||
_(
|
||||
"Table [{}] doesn't seem to exist in the specified database, "
|
||||
"couldn't fetch column information"
|
||||
).format(self.table_name)
|
||||
"Table %(table)s doesn't seem to exist in the specified database, "
|
||||
"couldn't fetch column information",
|
||||
table=self.table_name,
|
||||
)
|
||||
)
|
||||
|
||||
metrics = []
|
||||
@@ -1332,6 +1396,10 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
|
||||
templatable_statements.append(extras["where"])
|
||||
if "having" in extras:
|
||||
templatable_statements.append(extras["having"])
|
||||
if config["ENABLE_ROW_LEVEL_SECURITY"] and self.is_rls_supported:
|
||||
templatable_statements += [
|
||||
f.clause for f in security_manager.get_rls_filters(self)
|
||||
]
|
||||
for statement in templatable_statements:
|
||||
if ExtraCache.regex.search(statement):
|
||||
return True
|
||||
|
||||
@@ -263,6 +263,9 @@ class RowLevelSecurityFiltersModelView( # pylint: disable=too-many-ancestors
|
||||
"creator": _("Creator"),
|
||||
"modified": _("Modified"),
|
||||
}
|
||||
if app.config["RLS_FORM_QUERY_REL_FIELDS"]:
|
||||
add_form_query_rel_fields = app.config["RLS_FORM_QUERY_REL_FIELDS"]
|
||||
edit_form_query_rel_fields = add_form_query_rel_fields
|
||||
|
||||
|
||||
class TableModelView( # pylint: disable=too-many-ancestors
|
||||
|
||||
@@ -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", "changed_by_fk"]
|
||||
order_columns = [
|
||||
"dashboard_title",
|
||||
"changed_on_delta_humanized",
|
||||
"published",
|
||||
"changed_by.first_name",
|
||||
]
|
||||
|
||||
add_columns = [
|
||||
"dashboard_title",
|
||||
"slug",
|
||||
|
||||
@@ -46,10 +46,27 @@ class DeleteDatasetCommand(BaseCommand):
|
||||
def run(self) -> Model:
|
||||
self.validate()
|
||||
try:
|
||||
dataset = DatasetDAO.delete(self._model, commit=False)
|
||||
security_manager.del_permission_view_menu(
|
||||
"datasource_access", dataset.get_perm()
|
||||
view_menu = (
|
||||
security_manager.find_view_menu(self._model.get_perm())
|
||||
if self._model
|
||||
else None
|
||||
)
|
||||
if not view_menu:
|
||||
logger.error(
|
||||
"Could not find the data access permission for the dataset"
|
||||
)
|
||||
raise DatasetDeleteFailedError()
|
||||
permission_views = (
|
||||
db.session.query(security_manager.permissionview_model)
|
||||
.filter_by(view_menu=view_menu)
|
||||
.all()
|
||||
)
|
||||
dataset = DatasetDAO.delete(self._model, commit=False)
|
||||
|
||||
for permission_view in permission_views:
|
||||
db.session.delete(permission_view)
|
||||
if view_menu:
|
||||
db.session.delete(view_menu)
|
||||
db.session.commit()
|
||||
except (SQLAlchemyError, DAODeleteFailedError) as ex:
|
||||
logger.exception(ex)
|
||||
|
||||
@@ -296,7 +296,9 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
|
||||
return select_exprs
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
"""
|
||||
|
||||
:param cursor: Cursor instance
|
||||
@@ -305,7 +307,7 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
if cls.arraysize:
|
||||
cursor.arraysize = cls.arraysize
|
||||
if cls.limit_method == LimitMethod.FETCH_MANY:
|
||||
if cls.limit_method == LimitMethod.FETCH_MANY and limit:
|
||||
return cursor.fetchmany(limit)
|
||||
return cursor.fetchall()
|
||||
|
||||
@@ -438,10 +440,7 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
kwargs["encoding"] = "utf-8"
|
||||
kwargs["iterator"] = True
|
||||
chunks = pd.io.excel.read_excel(
|
||||
io=kwargs["filepath_or_buffer"], sheet_name=kwargs["sheet_name"]
|
||||
)
|
||||
df = pd.concat(chunk for chunk in chunks.values())
|
||||
df = pd.read_excel(**kwargs)
|
||||
return df
|
||||
|
||||
@staticmethod
|
||||
@@ -513,7 +512,7 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
|
||||
Create table from contents of a excel. Note: this method does not create
|
||||
metadata for the table.
|
||||
"""
|
||||
df = cls.excel_to_df(filepath_or_buffer=filename, **excel_to_df_kwargs,)
|
||||
df = cls.excel_to_df(io=filename, **excel_to_df_kwargs,)
|
||||
engine = cls.get_engine(database)
|
||||
if table.schema:
|
||||
# only add schema when it is preset and non empty
|
||||
|
||||
@@ -84,7 +84,9 @@ class BigQueryEngineSpec(BaseEngineSpec):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
data = super().fetch_data(cursor, limit)
|
||||
# Support type BigQuery Row, introduced here PR #4071
|
||||
# google.cloud.bigquery.table.Row
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Any, List, Tuple
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from superset.db_engine_specs.base import BaseEngineSpec
|
||||
|
||||
@@ -39,7 +39,9 @@ class ExasolEngineSpec(BaseEngineSpec): # pylint: disable=abstract-method
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
data = super().fetch_data(cursor, limit)
|
||||
# Lists of `pyodbc.Row` need to be unpacked further
|
||||
return cls.pyodbc_rows_to_tuples(data)
|
||||
|
||||
@@ -108,7 +108,9 @@ class HiveEngineSpec(PrestoEngineSpec):
|
||||
return BaseEngineSpec.get_all_datasource_names(database, datasource_type)
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
import pyhive
|
||||
from TCLIService import ttypes
|
||||
|
||||
@@ -116,7 +118,7 @@ class HiveEngineSpec(PrestoEngineSpec):
|
||||
if state.operationState == ttypes.TOperationState.ERROR_STATE:
|
||||
raise Exception("Query error", state.errorMessage)
|
||||
try:
|
||||
return super(HiveEngineSpec, cls).fetch_data(cursor, limit)
|
||||
return super().fetch_data(cursor, limit)
|
||||
except pyhive.exc.ProgrammingError:
|
||||
return []
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@ class MssqlEngineSpec(BaseEngineSpec):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
data = super().fetch_data(cursor, limit)
|
||||
# Lists of `pyodbc.Row` need to be unpacked further
|
||||
return cls.pyodbc_rows_to_tuples(data)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from superset.db_engine_specs.base import BaseEngineSpec, LimitMethod
|
||||
from superset.utils import core as utils
|
||||
@@ -57,3 +57,16 @@ class OracleEngineSpec(BaseEngineSpec):
|
||||
@classmethod
|
||||
def epoch_ms_to_dttm(cls) -> str:
|
||||
return "TO_DATE('1970-01-01','YYYY-MM-DD')+(1/24/60/60/1000)*{col}"
|
||||
|
||||
@classmethod
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
"""
|
||||
:param cursor: Cursor instance
|
||||
:param limit: Maximum number of rows to be returned by the cursor
|
||||
:return: Result of query
|
||||
"""
|
||||
if not cursor.description:
|
||||
return []
|
||||
return super().fetch_data(cursor, limit)
|
||||
|
||||
@@ -52,7 +52,9 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def fetch_data(cls, cursor: Any, limit: int) -> List[Tuple[Any, ...]]:
|
||||
def fetch_data(
|
||||
cls, cursor: Any, limit: Optional[int] = None
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
cursor.tzinfo_factory = FixedOffsetTimezone
|
||||
if not cursor.description:
|
||||
return []
|
||||
|
||||
@@ -48,7 +48,10 @@ class JinjaContextManager:
|
||||
"relativedelta": relativedelta,
|
||||
"time": time,
|
||||
"timedelta": timedelta,
|
||||
"uuid": uuid,
|
||||
"uuid1": uuid.uuid1,
|
||||
"uuid3": uuid.uuid3,
|
||||
"uuid4": uuid.uuid4,
|
||||
"uuid5": uuid.uuid5,
|
||||
}
|
||||
self._template_processors: Dict[str, Type["BaseTemplateProcessor"]] = {}
|
||||
|
||||
|
||||
@@ -213,17 +213,17 @@ class BaseTemplateProcessor: # pylint: disable=too-few-public-methods
|
||||
extra_cache_keys: Optional[List[Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self.database = database
|
||||
self.query = query
|
||||
self.schema = None
|
||||
self._database = database
|
||||
self._query = query
|
||||
self._schema = None
|
||||
if query and query.schema:
|
||||
self.schema = query.schema
|
||||
self._schema = query.schema
|
||||
elif table:
|
||||
self.schema = table.schema
|
||||
self._schema = table.schema
|
||||
|
||||
extra_cache = ExtraCache(extra_cache_keys)
|
||||
|
||||
self.context = {
|
||||
self._context = {
|
||||
"url_param": extra_cache.url_param,
|
||||
"current_user_id": extra_cache.current_user_id,
|
||||
"current_username": extra_cache.current_username,
|
||||
@@ -231,11 +231,11 @@ class BaseTemplateProcessor: # pylint: disable=too-few-public-methods
|
||||
"filter_values": filter_values,
|
||||
"form_data": {},
|
||||
}
|
||||
self.context.update(kwargs)
|
||||
self.context.update(jinja_base_context)
|
||||
self._context.update(kwargs)
|
||||
self._context.update(jinja_base_context)
|
||||
if self.engine:
|
||||
self.context[self.engine] = self
|
||||
self.env = SandboxedEnvironment()
|
||||
self._context[self.engine] = self
|
||||
self._env = SandboxedEnvironment()
|
||||
|
||||
def process_template(self, sql: str, **kwargs: Any) -> str:
|
||||
"""Processes a sql template
|
||||
@@ -244,8 +244,8 @@ class BaseTemplateProcessor: # pylint: disable=too-few-public-methods
|
||||
>>> process_template(sql)
|
||||
"SELECT '2017-01-01T00:00:00'"
|
||||
"""
|
||||
template = self.env.from_string(sql)
|
||||
kwargs.update(self.context)
|
||||
template = self._env.from_string(sql)
|
||||
kwargs.update(self._context)
|
||||
return template.render(kwargs)
|
||||
|
||||
|
||||
@@ -288,20 +288,20 @@ class PrestoTemplateProcessor(BaseTemplateProcessor):
|
||||
|
||||
from superset.db_engine_specs.presto import PrestoEngineSpec
|
||||
|
||||
table_name, schema = self._schema_table(table_name, self.schema)
|
||||
return cast(PrestoEngineSpec, self.database.db_engine_spec).latest_partition(
|
||||
table_name, schema, self.database
|
||||
table_name, schema = self._schema_table(table_name, self._schema)
|
||||
return cast(PrestoEngineSpec, self._database.db_engine_spec).latest_partition(
|
||||
table_name, schema, self._database
|
||||
)[1]
|
||||
|
||||
def latest_sub_partition(self, table_name: str, **kwargs: Any) -> Any:
|
||||
table_name, schema = self._schema_table(table_name, self.schema)
|
||||
table_name, schema = self._schema_table(table_name, self._schema)
|
||||
|
||||
from superset.db_engine_specs.presto import PrestoEngineSpec
|
||||
|
||||
return cast(
|
||||
PrestoEngineSpec, self.database.db_engine_spec
|
||||
PrestoEngineSpec, self._database.db_engine_spec
|
||||
).latest_sub_partition(
|
||||
table_name=table_name, schema=schema, database=self.database, **kwargs
|
||||
table_name=table_name, schema=schema, database=self._database, **kwargs
|
||||
)
|
||||
|
||||
latest_partition = first_latest_partition
|
||||
|
||||
@@ -57,6 +57,7 @@ from superset.db_engine_specs.base import TimeGrain
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.models.helpers import AuditMixinNullable, ImportMixin
|
||||
from superset.models.tags import DashboardUpdater, FavStarUpdater
|
||||
from superset.result_set import SupersetResultSet
|
||||
from superset.utils import cache as cache_util, core as utils
|
||||
|
||||
config = app.config
|
||||
@@ -392,21 +393,18 @@ class Database(
|
||||
_log_query(sqls[-1])
|
||||
self.db_engine_spec.execute(cursor, sqls[-1])
|
||||
|
||||
if cursor.description is not None:
|
||||
columns = [col_desc[0] for col_desc in cursor.description]
|
||||
else:
|
||||
columns = []
|
||||
|
||||
df = pd.DataFrame.from_records(
|
||||
data=list(cursor.fetchall()), columns=columns, coerce_float=True
|
||||
data = self.db_engine_spec.fetch_data(cursor)
|
||||
result_set = SupersetResultSet(
|
||||
data, cursor.description, self.db_engine_spec
|
||||
)
|
||||
|
||||
df = result_set.to_pandas_df()
|
||||
if mutator:
|
||||
mutator(df)
|
||||
|
||||
for k, v in df.dtypes.items():
|
||||
if v.type == numpy.object_ and needs_conversion(df[k]):
|
||||
df[k] = df[k].apply(utils.json_dumps_w_dates)
|
||||
|
||||
return df
|
||||
|
||||
def compile_sqla_query(self, qry: Select, schema: Optional[str] = None) -> str:
|
||||
|
||||
@@ -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
|
||||
@@ -365,8 +366,8 @@ class AuditMixinNullable(AuditMixin):
|
||||
|
||||
@property
|
||||
def changed_by_name(self) -> str:
|
||||
if self.created_by:
|
||||
return escape("{}".format(self.created_by))
|
||||
if self.changed_by:
|
||||
return escape("{}".format(self.changed_by))
|
||||
return ""
|
||||
|
||||
@renders("created_by")
|
||||
@@ -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)
|
||||
|
||||
@@ -155,10 +155,12 @@ class Slice(
|
||||
|
||||
@property # type: ignore
|
||||
@utils.memoized
|
||||
def viz(self) -> BaseViz:
|
||||
def viz(self) -> Optional[BaseViz]:
|
||||
form_data = json.loads(self.params)
|
||||
viz_class = viz_types[self.viz_type]
|
||||
return viz_class(datasource=self.datasource, form_data=form_data)
|
||||
viz_class = viz_types.get(self.viz_type)
|
||||
if viz_class:
|
||||
return viz_class(datasource=self.datasource, form_data=form_data)
|
||||
return None
|
||||
|
||||
@property
|
||||
def description_markeddown(self) -> str:
|
||||
@@ -170,8 +172,9 @@ class Slice(
|
||||
data: Dict[str, Any] = {}
|
||||
self.token = ""
|
||||
try:
|
||||
data = self.viz.data
|
||||
self.token = data.get("token") # type: ignore
|
||||
viz = self.viz
|
||||
data = viz.data if viz else self.form_data
|
||||
self.token = utils.get_form_data_token(data)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
logger.exception(ex)
|
||||
data["error"] = str(ex)
|
||||
|
||||
@@ -18,15 +18,13 @@ import logging
|
||||
from io import IOBase
|
||||
from typing import cast, Union
|
||||
|
||||
from flask import current_app
|
||||
from retry.api import retry
|
||||
from slack import WebClient
|
||||
from slack.errors import SlackApiError
|
||||
from slack.web.slack_response import SlackResponse
|
||||
|
||||
from superset import app
|
||||
|
||||
# Globals
|
||||
config = app.config # type: ignore
|
||||
logger = logging.getLogger("tasks.slack_util")
|
||||
|
||||
|
||||
@@ -34,6 +32,7 @@ logger = logging.getLogger("tasks.slack_util")
|
||||
def deliver_slack_msg(
|
||||
slack_channel: str, subject: str, body: str, file: Union[str, IOBase]
|
||||
) -> None:
|
||||
config = current_app.config
|
||||
client = WebClient(token=config["SLACK_API_TOKEN"], proxy=config["SLACK_PROXY"])
|
||||
# files_upload returns SlackResponse as we run it in sync mode.
|
||||
response = cast(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user